@shipstatic/ship 2.0.0-beta.1 → 2.0.0-beta.3

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.
@@ -1 +1 @@
1
- {"version":3,"sources":["../node_modules/.pnpm/@shipstatic+types@2.2.1-beta.0/node_modules/@shipstatic/types/dist/index.js","../node_modules/.pnpm/spark-md5@3.0.2/node_modules/spark-md5/spark-md5.js","../src/shared/lib/md5.ts","../src/shared/lib/path.ts","../src/shared/lib/deploy-paths.ts","../src/shared/lib/env.ts","../src/shared/lib/file-validation.ts","../node_modules/.pnpm/junk@4.0.1/node_modules/junk/index.js","../src/shared/lib/junk.ts","../src/shared/lib/security.ts","../src/browser/core/browser-files.ts","../src/browser/index.ts","../src/shared/base-ship.ts","../src/shared/api/http.ts","../src/shared/events.ts","../src/shared/lib/validation.ts","../src/shared/resources.ts","../src/shared/core/config.ts","../src/shared/lib/spa.ts","../src/browser/core/deploy-body.ts","../src/shared/index.ts","../src/shared/core/constants.ts","../src/shared/lib/text.ts"],"sourcesContent":["/**\n * @file Shared TypeScript types, constants, and utilities for the ShipStatic platform.\n * This package is the single source of truth for all shared data structures.\n */\n// =============================================================================\n// I. CORE ENTITIES\n// =============================================================================\n/**\n * Deployment status constants\n */\nexport const DeploymentStatus = {\n PENDING: 'pending',\n SUCCESS: 'success',\n FAILED: 'failed',\n DELETING: 'deleting',\n};\n// =============================================================================\n// DOMAIN TYPES\n// =============================================================================\n/**\n * Domain status constants\n *\n * - PENDING: DNS not configured\n * - PARTIAL: DNS partially configured\n * - SUCCESS: DNS fully verified\n * - PAUSED: Domain paused due to plan enforcement (billing)\n */\nexport const DomainStatus = {\n PENDING: 'pending',\n PARTIAL: 'partial',\n SUCCESS: 'success',\n PAUSED: 'paused',\n};\n// =============================================================================\n// ACCOUNT TYPES\n// =============================================================================\n/**\n * Account plan constants\n */\nexport const AccountPlan = {\n FREE: 'free',\n STANDARD: 'standard',\n SPONSORED: 'sponsored',\n ENTERPRISE: 'enterprise',\n SUSPENDED: 'suspended',\n TERMINATING: 'terminating',\n TERMINATED: 'terminated',\n};\n// =============================================================================\n// ERROR SYSTEM\n// =============================================================================\n/**\n * All possible error types in the ShipStatic platform.\n *\n * Developer-friendly key names map to stable wire-format string values.\n * Both the value and the type are exported under the same name so callers\n * can use `ErrorType.Validation` (value comparison) and `: ErrorType` (type\n * annotation) without ceremony — matching the pattern other status objects\n * (`DeploymentStatus`, `DomainStatus`, `AccountPlan`, `AuthMethod`) follow.\n */\nexport const ErrorType = {\n /** Validation failed (400). Input shape is wrong. */\n Validation: 'validation_failed',\n /** Resource not found (404). */\n NotFound: 'not_found',\n /** Authenticated but not allowed (403). User lacks permission for this action. */\n Forbidden: 'forbidden',\n /** Rate limit exceeded (429). */\n RateLimit: 'rate_limit_exceeded',\n /** Authentication required or failed (401). Missing/invalid credentials. */\n Authentication: 'authentication_failed',\n /** Business rule violation. Catch-all for 4xx state-rule errors that aren't more specific. */\n Business: 'business_logic_error',\n /** API server error (500). Generic server-side fault. */\n Api: 'internal_server_error',\n /** Network/connection error. Client-side only — set by HTTP clients on fetch failure; never produced server-side. */\n Network: 'network_error',\n /** Operation was cancelled. Client-side only — set on `AbortSignal` abort; never produced server-side. */\n Cancelled: 'operation_cancelled',\n /** File operation error. Client-side only — set by SDK during local file processing; never produced server-side. */\n File: 'file_error',\n /** Configuration error. Client-side only — set by SDK during config parsing/validation; never produced server-side. */\n Config: 'config_error',\n};\n/**\n * Error types that originate exclusively on the client (HTTP clients, SDK\n * file processing, local config parsing). These never appear on the wire\n * from the server, so `fromHttpResponse` will not trust them even if a\n * misbehaving server claims one in `body.error`.\n */\nconst CLIENT_ONLY_ERROR_TYPES = new Set([\n ErrorType.Network,\n ErrorType.Cancelled,\n ErrorType.File,\n ErrorType.Config,\n]);\n/**\n * Categorizes error types for the `isClientError` / `isNetworkError` /\n * `isAuthError` helpers. Each `Set` is typed against the wider `ErrorType`\n * union so `.has(error.type)` accepts any value from the union.\n */\nconst ERROR_CATEGORIES = {\n client: new Set([\n ErrorType.Business,\n ErrorType.Config,\n ErrorType.File,\n ErrorType.Forbidden,\n ErrorType.Validation,\n ]),\n network: new Set([ErrorType.Network]),\n auth: new Set([ErrorType.Authentication]),\n};\n/**\n * Error types the server can legitimately produce on the wire. Used by\n * `ShipError.fromHttpResponse` to validate the body's `error` field before\n * trusting it as `ShipError.type`. Derived by exclusion from\n * `CLIENT_ONLY_ERROR_TYPES` so adding a new server-producible type to\n * `ErrorType` is automatically picked up.\n */\nconst SERVER_PRODUCIBLE_ERROR_TYPES = new Set(Object.values(ErrorType).filter((t) => !CLIENT_ONLY_ERROR_TYPES.has(t)));\n/**\n * Simple unified error class for both API and SDK\n */\nexport class ShipError extends Error {\n type;\n status;\n details;\n constructor(type, message, status, details) {\n super(message);\n this.type = type;\n this.status = status;\n this.details = details;\n this.name = 'ShipError';\n }\n /** Convert to wire format */\n toResponse() {\n // Strip authentication details when they carry an `internal` telemetry\n // tag (see `ShipError.authentication` JSDoc) — these are server-side\n // diagnostics like 'session_invalid' that must not leak to clients.\n const authDetails = this.details;\n const details = this.type === ErrorType.Authentication && authDetails?.internal ? undefined : this.details;\n return {\n error: this.type,\n message: this.message,\n status: this.status,\n details,\n };\n }\n /**\n * Construct a `ShipError` from an HTTP error response.\n *\n * Best-effort body parse for `{ message, error?, details? }`. Message\n * resolution: `body.message` → `body.error` → `\"<operationName> failed with\n * status <N>\"`.\n *\n * Type resolution: trusts `body.error` when it's a known server-producible\n * `ErrorType` (preserves the wire's intent — server's\n * `ShipError.validation(...)` round-trips back to `ErrorType.Validation`\n * on the client). Falls back to status-derived (401 → Authentication,\n * 403 → Forbidden, 429 → RateLimit, else → Api) for non-API responses\n * (CDN errors, intermediaries) or malformed bodies. Client-only types\n * (`Network`, `Cancelled`, `File`, `Config`) are filtered out of the\n * trusted set — a misbehaving server claiming one of those is ignored.\n *\n * `operationName` (e.g. `\"Get account\"`) is used to compose the fallback\n * message. Defaults to `\"Request\"`. Same convention as `fromFetchError`.\n *\n * Async because it reads the response body. Returns rather than throws so\n * callers can compose; most will `throw await ShipError.fromHttpResponse(...)`.\n */\n static async fromHttpResponse(response, operationName) {\n let message;\n let details;\n let bodyType;\n try {\n const contentType = response.headers.get('content-type');\n if (contentType?.includes('application/json')) {\n const json = await response.json();\n if (json && typeof json === 'object') {\n const obj = json;\n if (typeof obj.message === 'string')\n message = obj.message;\n else if (typeof obj.error === 'string')\n message = obj.error;\n details = obj.details;\n if (typeof obj.error === 'string' && SERVER_PRODUCIBLE_ERROR_TYPES.has(obj.error)) {\n bodyType = obj.error;\n }\n }\n }\n else {\n const text = await response.text();\n if (text)\n message = text;\n }\n }\n catch {\n // Body unreadable; fall through to operationName-derived message.\n }\n message = message || `${operationName || 'Request'} failed with status ${response.status}`;\n const type = bodyType ??\n (response.status === 401\n ? ErrorType.Authentication\n : response.status === 403\n ? ErrorType.Forbidden\n : response.status === 429\n ? ErrorType.RateLimit\n : ErrorType.Api);\n return new ShipError(type, message, response.status, details);\n }\n /**\n * Construct a `ShipError` from an error caught around a `fetch()` call.\n *\n * The mirror of `fromHttpResponse` for the *other* side of the HTTP error\n * story — the network layer failing (offline, CORS, abort) rather than the\n * server returning a non-OK response.\n *\n * Routing:\n * - Already a `ShipError` → returned as-is (caller's intent preserved)\n * - `AbortError` → `ShipError.cancelled(...)`\n * - `TypeError` whose message mentions \"fetch\" → `ShipError.network(...)`\n * - Any other `Error` → `ShipError(Api, ...)` (no HTTP status — fetch never reached the server)\n * - Anything else (string, undefined, etc.) → `ShipError(Api, ...)`\n *\n * The optional `operationName` is composed into the message for context:\n * `\"Get account was cancelled\"`, `\"Get account failed: ...\"`. Defaults to\n * `\"Request\"` when omitted.\n */\n static fromFetchError(cause, operationName) {\n if (isShipError(cause))\n return cause;\n const op = operationName || 'Request';\n if (cause instanceof Error) {\n if (cause.name === 'AbortError') {\n return ShipError.cancelled(`${op} was cancelled`);\n }\n if (cause instanceof TypeError && cause.message.includes('fetch')) {\n return ShipError.network(`${op} failed: ${cause.message}`, { cause });\n }\n return new ShipError(ErrorType.Api, `${op} failed: ${cause.message}`);\n }\n return new ShipError(ErrorType.Api, `${op} failed: Unknown error`);\n }\n // Factory methods. Uniform shape `(message, details?)` with two principled\n // exceptions: `notFound` composes its message from (resource, id?), and\n // `business` / `api` accept an optional status because they're the\n // multi-status fallbacks.\n static validation(message, details) {\n return new ShipError(ErrorType.Validation, message, 400, details);\n }\n static notFound(resource, id) {\n const message = id ? `${resource} ${id} not found` : `${resource} not found`;\n return new ShipError(ErrorType.NotFound, message, 404);\n }\n static forbidden(message, details) {\n return new ShipError(ErrorType.Forbidden, message, 403, details);\n }\n static rateLimit(message = 'Too many requests', details) {\n return new ShipError(ErrorType.RateLimit, message, 429, details);\n }\n /**\n * Construct an Authentication (401) error.\n *\n * **Telemetry pattern — `details: { internal: '<tag>' }`.** When the\n * server creates an auth error with an `internal` key in `details`\n * (e.g. `{ internal: 'session_invalid' }`), `toResponse()` strips the\n * entire `details` object before serialization. This keeps the wire\n * response a clean \"Authentication failed\" while preserving granular\n * server-side telemetry (which strategy/check failed) for logs and tests.\n *\n * Use this pattern in API auth code; do not put client-visible info under\n * `internal`. Other `details` keys round-trip normally.\n */\n static authentication(message = 'Authentication required', details) {\n return new ShipError(ErrorType.Authentication, message, 401, details);\n }\n static business(message, status = 400, details) {\n return new ShipError(ErrorType.Business, message, status, details);\n }\n static network(message, details) {\n return new ShipError(ErrorType.Network, message, undefined, details);\n }\n static cancelled(message, details) {\n return new ShipError(ErrorType.Cancelled, message, undefined, details);\n }\n static file(message, details) {\n return new ShipError(ErrorType.File, message, undefined, details);\n }\n static config(message, details) {\n return new ShipError(ErrorType.Config, message, undefined, details);\n }\n static api(message, status = 500, details) {\n return new ShipError(ErrorType.Api, message, status, details);\n }\n // Semantic-category type guards. For specific-type checks, use\n // `error.type === ErrorType.X` directly or the generic `isType(t)`.\n isClientError() {\n return ERROR_CATEGORIES.client.has(this.type);\n }\n isNetworkError() {\n return ERROR_CATEGORIES.network.has(this.type);\n }\n isAuthError() {\n return ERROR_CATEGORIES.auth.has(this.type);\n }\n isType(errorType) {\n return this.type === errorType;\n }\n}\n/**\n * Type guard to check if an unknown value is a ShipError.\n *\n * Uses structural checking instead of instanceof to handle module duplication\n * in bundled applications where multiple copies of the ShipError class may exist.\n *\n * @example\n * if (isShipError(error)) {\n * console.log(error.status, error.message);\n * }\n */\nexport function isShipError(error) {\n return (error !== null &&\n typeof error === 'object' &&\n 'name' in error &&\n error.name === 'ShipError' &&\n 'status' in error);\n}\n// =============================================================================\n// EXTENSION BLOCKLIST\n// =============================================================================\n/**\n * Blocked file extensions — files that cannot be uploaded.\n *\n * We accept any file type by default and derive Content-Type from the\n * extension at serve time (via mime-db in the API worker). Unknown extensions\n * are served as `application/octet-stream` with `X-Content-Type-Options: nosniff`.\n *\n * The blocklist targets file types that pose direct security risks when hosted:\n * executables, disk images, malware vectors, dangerous scripts, and shortcuts.\n */\nexport const BLOCKED_EXTENSIONS = new Set([\n // Executables\n 'exe',\n 'msi',\n 'dll',\n 'scr',\n 'bat',\n 'cmd',\n 'com',\n 'pif',\n 'app',\n 'deb',\n 'rpm',\n // Installers\n 'pkg',\n 'mpkg',\n // Disk images\n 'dmg',\n 'iso',\n 'img',\n // Malware vectors\n 'cab',\n 'cpl',\n 'chm',\n // Dangerous scripts\n 'ps1',\n 'vbs',\n 'vbe',\n 'ws',\n 'wsf',\n 'wsc',\n 'wsh',\n 'reg',\n // Java\n 'jar',\n 'jnlp',\n // Mobile/browser packages\n 'apk',\n 'crx',\n // Shortcut/link\n 'lnk',\n 'inf',\n 'hta',\n]);\n/**\n * Check if a filename has a blocked extension.\n * Extracts the extension from the filename and checks against the blocklist.\n * Case-insensitive. Returns false for files without extensions.\n *\n * @example\n * isBlockedExtension('virus.exe') // true\n * isBlockedExtension('app.dmg') // true\n * isBlockedExtension('style.css') // false\n * isBlockedExtension('data.custom') // false\n * isBlockedExtension('README') // false\n */\nexport function isBlockedExtension(filename) {\n const dotIndex = filename.lastIndexOf('.');\n if (dotIndex === -1 || dotIndex === filename.length - 1)\n return false;\n const ext = filename.slice(dotIndex + 1).toLowerCase();\n return BLOCKED_EXTENSIONS.has(ext);\n}\n// =============================================================================\n// FILENAME CHARACTER VALIDATION\n// =============================================================================\n/**\n * Characters that are unsafe in filenames for static hosting.\n *\n * Blocks only characters that genuinely break the upload→serve round-trip:\n * - # ? % URL round-trip breakers (fragment, query, encoding ambiguity)\n * - \\ Path separator confusion (upload splits on backslash)\n * - < > \" XSS vectors with zero legitimate use in filenames\n * - \\x00-\\x1f \\x7f Control characters (header injection, display corruption)\n *\n * Everything else is allowed — browser percent-encodes, Worker decodes, R2 matches.\n */\n// biome-ignore lint/suspicious/noControlCharactersInRegex: blocking control characters is this regex's purpose\nexport const UNSAFE_FILENAME_CHARS = /[\\x00-\\x1f\\x7f#?%\\\\<>\"]/;\n/**\n * Check if a filename contains unsafe characters.\n *\n * @example\n * hasUnsafeChars('saved_resource(1).html') // false — parentheses are safe\n * hasUnsafeChars('page[slug].js') // false — brackets are safe\n * hasUnsafeChars('file#anchor.html') // true — # breaks URL resolution\n * hasUnsafeChars('file<tag>.html') // true — < is an XSS vector\n */\nexport function hasUnsafeChars(filename) {\n return UNSAFE_FILENAME_CHARS.test(filename);\n}\n// =============================================================================\n// UNBUILT PROJECT MARKERS\n// =============================================================================\n/**\n * Path segment names that indicate an unbuilt project was uploaded instead of build output.\n * Used for early detection in CLI, browser, and server validation.\n */\nexport const UNBUILT_PROJECT_MARKERS = new Set([\n 'node_modules',\n 'package.json',\n]);\n/**\n * Check if a file path contains an unbuilt project marker.\n *\n * @example\n * hasUnbuiltMarker('node_modules/react/index.js') // true\n * hasUnbuiltMarker('package.json') // true\n * hasUnbuiltMarker('dist/index.html') // false\n */\nexport function hasUnbuiltMarker(filePath) {\n const segments = filePath.replace(/\\\\/g, '/').split('/').filter(Boolean);\n return segments.some((s) => UNBUILT_PROJECT_MARKERS.has(s));\n}\n// =============================================================================\n// CREDENTIAL SHAPES\n// =============================================================================\n// The one address for credential vocabulary: how a request is authorized\n// (AuthMethod), the shapes that distinguish populations on the wire\n// (API_KEY, DEPLOY_TOKEN, CALLER), the single dispatch over them (TokenKind,\n// classifyToken), and the delegated-access scopes (OAuthScope).\n/**\n * How a request (or recorded activity) was authorized.\n *\n * Client populations: `SESSION` (first-party cookie), `API_KEY` (`ship-`\n * key), `TOKEN` (`deploy-` deploy token), `AGENT` (anonymous public deploy —\n * no credential; the platform grants the public-account identity per\n * request), `OAUTH` (delegated access token). Server populations: `WEBHOOK`\n * (signed webhook processing), `SYSTEM` (scheduled/background jobs).\n */\nexport const AuthMethod = {\n SESSION: 'session',\n API_KEY: 'apiKey',\n TOKEN: 'token',\n AGENT: 'agent',\n OAUTH: 'oauth',\n WEBHOOK: 'webhook',\n SYSTEM: 'system',\n};\n/**\n * Shape constants for API keys (`ship-{64 hex chars}`).\n * Single source of truth used by validation utilities and auth middleware.\n */\nexport const API_KEY = {\n /** Prefix that identifies an API key. */\n PREFIX: 'ship-',\n /** Number of hex characters following the prefix. */\n HEX_LENGTH: 64,\n /** Total length of an API key including prefix (`PREFIX.length + HEX_LENGTH = 69`). */\n TOTAL_LENGTH: 69,\n /** Number of trailing characters used to display a redacted hint (e.g. last 4). */\n HINT_LENGTH: 4,\n};\n/**\n * Shape constants for deploy tokens (`deploy-{64 hex chars}`).\n * Single source of truth used by validation utilities and auth middleware.\n */\nexport const DEPLOY_TOKEN = {\n /** Prefix that identifies a deploy token. */\n PREFIX: 'deploy-',\n /** Number of hex characters following the prefix. */\n HEX_LENGTH: 64,\n /** Total length of a deploy token including prefix (`PREFIX.length + HEX_LENGTH = 71`). */\n TOTAL_LENGTH: 71,\n};\n/**\n * Shape constants for caller identifiers (the `X-Caller` instance-identity\n * header — rate-limit bucketing for multi-tenant orchestrators). The API\n * normalizes case and silently ignores malformed values (the header is\n * unauthenticated); clients validate at the boundary via `validateCaller`,\n * so a value the server would drop fails fast instead.\n */\nexport const CALLER = {\n /** HTTP header name. */\n HEADER: 'X-Caller',\n /** Maximum identifier length. */\n MAX_LENGTH: 128,\n /** Allowed characters: alphanumeric, dot, underscore, hyphen. */\n PATTERN: /^[a-zA-Z0-9._-]+$/,\n};\n/**\n * Token populations distinguishable by shape. The platform carries every\n * client token in one wire slot (`Authorization: Bearer <value>`) and\n * classifies by value, never by a side channel — this is the classifier.\n *\n * `API_KEY` and `DEPLOY_TOKEN` *are* `AuthMethod.API_KEY` and\n * `AuthMethod.TOKEN` — the equality is structural, so a classification flows\n * straight into an auth method and the pair can never drift. `OPAQUE` is any\n * other value — shape says nothing about it, so only a lookup can. Today the\n * server refuses every opaque bearer; the OAuth access-token population\n * resolves there when the authorization server ships.\n */\nexport const TokenKind = {\n API_KEY: AuthMethod.API_KEY,\n DEPLOY_TOKEN: AuthMethod.TOKEN,\n OPAQUE: 'opaque',\n};\n/**\n * Classify a client token by shape. The single dispatch used by both sides\n * of the wire: API auth middleware (which population is this credential?)\n * and SDK validation (which format rules apply before sending?). Sharing it\n * is what guarantees client and server can never disagree on dispatch.\n */\nexport function classifyToken(token) {\n if (token.startsWith(API_KEY.PREFIX))\n return TokenKind.API_KEY;\n if (token.startsWith(DEPLOY_TOKEN.PREFIX))\n return TokenKind.DEPLOY_TOKEN;\n return TokenKind.OPAQUE;\n}\n/**\n * OAuth scope vocabulary for delegated third-party access tokens.\n * Single source of truth used by the authorization server (advertised in\n * `scopes_supported`), the API's scope-enforcement middleware, and consent UI\n * copy. The standard `offline_access` scope (refresh tokens) is not platform\n * vocabulary and is deliberately absent — the middleware never checks it.\n *\n * Deliberately absent by design: any `tokens:*` scope, `account:write`, or\n * admin scope — a delegated app must never mint credentials, delete the\n * account, or act as admin.\n */\nexport const OAuthScope = {\n ACCOUNT_READ: 'account:read',\n DEPLOYMENTS_READ: 'deployments:read',\n DEPLOYMENTS_WRITE: 'deployments:write',\n DOMAINS_READ: 'domains:read',\n DOMAINS_WRITE: 'domains:write',\n};\n// =============================================================================\n// DEPLOYMENT CONFIGURATION CONSTANTS\n// =============================================================================\nexport const DEPLOYMENT_CONFIG_FILENAME = 'ship.json';\n/** Default ship.json config for SPA routing. Single source of truth — used by both API and SDK. */\nexport const SPA_DEFAULT_CONFIG = {\n rewrites: [{ source: '/(.*)', destination: '/index.html' }],\n};\n// =============================================================================\n// VALIDATION UTILITIES\n// =============================================================================\n/**\n * Shared rule for prefixed credentials: `{PREFIX}{HEX_LENGTH hex chars}`.\n * The regex derives from the shape constants, so the validators can never\n * drift from the shapes `classifyToken` dispatches on.\n */\nfunction validatePrefixedCredential(value, shape, label) {\n if (!value.startsWith(shape.PREFIX)) {\n throw ShipError.validation(`${label} must start with \"${shape.PREFIX}\"`);\n }\n if (value.length !== shape.TOTAL_LENGTH) {\n throw ShipError.validation(`${label} must be ${shape.TOTAL_LENGTH} characters total (${shape.PREFIX} + ${shape.HEX_LENGTH} hex chars)`);\n }\n const hexPart = value.slice(shape.PREFIX.length);\n if (!new RegExp(`^[a-f0-9]{${shape.HEX_LENGTH}}$`, 'i').test(hexPart)) {\n throw ShipError.validation(`${label} must contain ${shape.HEX_LENGTH} hexadecimal characters after \"${shape.PREFIX}\" prefix`);\n }\n}\n/**\n * Validate API key format\n */\nexport function validateApiKey(apiKey) {\n validatePrefixedCredential(apiKey, API_KEY, 'API key');\n}\n/**\n * Validate deploy token format\n */\nexport function validateDeployToken(deployToken) {\n validatePrefixedCredential(deployToken, DEPLOY_TOKEN, 'Deploy token');\n}\n/**\n * Validate a client token of any population. Classifies by shape and applies\n * the matching format rules: `ship-` keys and `deploy-` deploy tokens are\n * validated strictly; opaque tokens (OAuth access tokens, future populations)\n * only need to be non-empty — their validity is the server's to decide.\n */\nexport function validateToken(token) {\n switch (classifyToken(token)) {\n case TokenKind.API_KEY:\n validateApiKey(token);\n return;\n case TokenKind.DEPLOY_TOKEN:\n validateDeployToken(token);\n return;\n case TokenKind.OPAQUE:\n if (!token)\n throw ShipError.validation('Token must be a non-empty string');\n }\n}\n/**\n * Validate a caller identifier against the `CALLER` shape. The server\n * silently ignores malformed values (the header is unauthenticated); clients\n * call this at configuration time so the drop never silently happens.\n */\nexport function validateCaller(caller) {\n if (!caller || caller.length > CALLER.MAX_LENGTH || !CALLER.PATTERN.test(caller)) {\n throw ShipError.validation(`Caller must be 1-${CALLER.MAX_LENGTH} characters: letters, digits, dots, underscores, or hyphens`);\n }\n}\n/**\n * Validate API URL format\n */\nexport function validateApiUrl(apiUrl) {\n try {\n const url = new URL(apiUrl);\n if (!['http:', 'https:'].includes(url.protocol)) {\n throw ShipError.validation('API URL must use http:// or https:// protocol');\n }\n if (url.pathname !== '/' && url.pathname !== '') {\n throw ShipError.validation('API URL must not contain a path');\n }\n if (url.search || url.hash) {\n throw ShipError.validation('API URL must not contain query parameters or fragments');\n }\n }\n catch (error) {\n if (isShipError(error)) {\n throw error;\n }\n throw ShipError.validation('API URL must be a valid URL');\n }\n}\n/**\n * Check if a string matches the deployment identifier pattern (word-word-alphanumeric7).\n * Example: \"happy-cat-abc1234.shipstatic.com\"\n */\nexport function isDeployment(input) {\n return /^[a-z]+-[a-z]+-[a-z0-9]{7}(\\.[a-z0-9.-]+)?$/i.test(input);\n}\n// =============================================================================\n// PLATFORM CONSTANTS\n// =============================================================================\n/** Default API URL if not otherwise configured. */\nexport const DEFAULT_API = 'https://api.shipstatic.com';\n// =============================================================================\n// FILE UPLOAD TYPES\n// =============================================================================\n/**\n * File status constants for validation state tracking\n */\nexport const FileValidationStatus = {\n /** File is pending validation */\n PENDING: 'pending',\n /** File failed during processing (before validation) */\n PROCESSING_ERROR: 'processing_error',\n /** File was excluded by validation warning (not an error) */\n EXCLUDED: 'excluded',\n /** File failed validation (blocks deployment) */\n VALIDATION_FAILED: 'validation_failed',\n /** File passed validation and is ready for deployment */\n READY: 'ready',\n};\n// =============================================================================\n// DOMAIN UTILITIES\n// =============================================================================\n/**\n * Check if a domain is a platform domain (subdomain of our platform).\n * Platform domains are free and don't require DNS verification.\n *\n * @example isPlatformDomain(\"www.shipstatic.com\", \"shipstatic.com\") → true\n * @example isPlatformDomain(\"example.com\", \"shipstatic.com\") → false\n */\nexport function isPlatformDomain(domain, platformDomain) {\n return domain.endsWith(`.${platformDomain}`);\n}\n/**\n * Check if a domain is a custom domain (not a platform subdomain).\n * Custom domains are billable and require DNS verification.\n *\n * @example isCustomDomain(\"example.com\", \"shipstatic.com\") → true\n * @example isCustomDomain(\"www.shipstatic.com\", \"shipstatic.com\") → false\n */\nexport function isCustomDomain(domain, platformDomain) {\n return !isPlatformDomain(domain, platformDomain);\n}\n/**\n * Extract subdomain from a platform domain.\n * Returns null if not a platform domain.\n *\n * @example extractSubdomain(\"www.shipstatic.com\", \"shipstatic.com\") → \"www\"\n * @example extractSubdomain(\"example.com\", \"shipstatic.com\") → null\n */\nexport function extractSubdomain(domain, platformDomain) {\n if (!isPlatformDomain(domain, platformDomain)) {\n return null;\n }\n return domain.slice(0, -(platformDomain.length + 1)); // +1 for the dot\n}\n/**\n * Generate HTTPS URL for a deployment hostname.\n */\nexport function generateDeploymentUrl(deployment) {\n return `https://${deployment}`;\n}\n/**\n * Generate HTTPS URL for a domain.\n */\nexport function generateDomainUrl(domain) {\n return `https://${domain}`;\n}\n// =============================================================================\n// LABEL UTILITIES\n// =============================================================================\n/**\n * Label validation constraints shared across UI and API.\n * These rules define the single source of truth for label validation.\n */\nexport const LABEL_CONSTRAINTS = {\n /** Minimum label length in characters */\n MIN_LENGTH: 3,\n /** Maximum label length in characters (concise labels, matches Stack Overflow's original limit) */\n MAX_LENGTH: 25,\n /** Maximum number of labels allowed per resource */\n MAX_COUNT: 10,\n /** Allowed separator characters between label segments */\n SEPARATORS: '._-',\n};\n/**\n * Label validation pattern.\n * Must start and end with alphanumeric (a-z, 0-9).\n * Can contain separators (. _ -) between segments, but not consecutive.\n *\n * Valid examples: 'production', 'v1.2.3', 'api_v2', 'us-east-1'\n * Invalid examples: 'ab' (too short), '-prod' (starts with separator), 'foo--bar' (consecutive separators)\n */\nexport const LABEL_PATTERN = /^[a-z0-9]+(?:[._-][a-z0-9]+)*$/;\n/**\n * Serialize labels array to JSON string for database storage.\n * Returns null for empty or undefined arrays.\n *\n * @example serializeLabels(['web', 'production']) → '[\"web\",\"production\"]'\n * @example serializeLabels([]) → null\n * @example serializeLabels(undefined) → null\n */\nexport function serializeLabels(labels) {\n if (!labels || labels.length === 0)\n return null;\n return JSON.stringify(labels);\n}\n/**\n * Deserialize labels from JSON string to array.\n * Always returns an array — empty array for null/empty/invalid input.\n *\n * @example deserializeLabels('[\"web\",\"production\"]') → ['web', 'production']\n * @example deserializeLabels(null) → []\n * @example deserializeLabels('') → []\n */\nexport function deserializeLabels(labelsJson) {\n if (!labelsJson)\n return [];\n try {\n const parsed = JSON.parse(labelsJson);\n return Array.isArray(parsed) ? parsed : [];\n }\n catch {\n return [];\n }\n}\n// =============================================================================\n// PASSWORD UTILITIES\n// =============================================================================\n/**\n * Length constraints for the optional deployment password\n * (`DeploymentUploadOptions.password`). Single source of truth shared across\n * platform consumers.\n */\nexport const PASSWORD_CONSTRAINTS = {\n /** Minimum password length in characters */\n MIN_LENGTH: 6,\n /** Maximum password length in characters */\n MAX_LENGTH: 128,\n};\n/**\n * Validate an optional deployment password and return it normalized.\n *\n * Absent (`undefined` / `null`) → returns `undefined`. Present → trim\n * leading/trailing whitespace, then validate against `PASSWORD_CONSTRAINTS`\n * length bounds (internal whitespace is significant and counts toward\n * length). Throws `ShipError.validation` on breach; returns the trimmed\n * value.\n *\n * The trim is canonical: at upload, the API hashes the trimmed value; at\n * unlock, the router trims submissions before hashing. Submission and storage\n * agree byte-for-byte. Length validation runs on the trimmed value because\n * that's the user's actual intent — and it disarms a class of invisible\n * foot-guns (trailing newlines from copy/paste, mobile auto-spacing,\n * password-manager artifacts).\n *\n * Single source of truth shared by SDK (client-side validation, return\n * ignored) and API (server-side enforcement, return threaded into config).\n * Length is part of the wire-format contract; strength rules, if added later,\n * stay server-side. See `CLAUDE.md` \"Validation: format vs policy\".\n */\nexport function validatePassword(value) {\n if (value === undefined || value === null)\n return undefined;\n if (typeof value !== 'string') {\n throw ShipError.validation('Password must be a string');\n }\n const trimmed = value.trim();\n if (trimmed.length < PASSWORD_CONSTRAINTS.MIN_LENGTH ||\n trimmed.length > PASSWORD_CONSTRAINTS.MAX_LENGTH) {\n throw ShipError.validation(`Password must be between ${PASSWORD_CONSTRAINTS.MIN_LENGTH} and ${PASSWORD_CONSTRAINTS.MAX_LENGTH} characters`);\n }\n return trimmed;\n}\n","(function (factory) {\n if (typeof exports === 'object') {\n // Node/CommonJS\n module.exports = factory();\n } else if (typeof define === 'function' && define.amd) {\n // AMD\n define(factory);\n } else {\n // Browser globals (with support for web workers)\n var glob;\n\n try {\n glob = window;\n } catch (e) {\n glob = self;\n }\n\n glob.SparkMD5 = factory();\n }\n}(function (undefined) {\n\n 'use strict';\n\n /*\n * Fastest md5 implementation around (JKM md5).\n * Credits: Joseph Myers\n *\n * @see http://www.myersdaily.org/joseph/javascript/md5-text.html\n * @see http://jsperf.com/md5-shootout/7\n */\n\n /* this function is much faster,\n so if possible we use it. Some IEs\n are the only ones I know of that\n need the idiotic second function,\n generated by an if clause. */\n var add32 = function (a, b) {\n return (a + b) & 0xFFFFFFFF;\n },\n hex_chr = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'];\n\n\n function cmn(q, a, b, x, s, t) {\n a = add32(add32(a, q), add32(x, t));\n return add32((a << s) | (a >>> (32 - s)), b);\n }\n\n function md5cycle(x, k) {\n var a = x[0],\n b = x[1],\n c = x[2],\n d = x[3];\n\n a += (b & c | ~b & d) + k[0] - 680876936 | 0;\n a = (a << 7 | a >>> 25) + b | 0;\n d += (a & b | ~a & c) + k[1] - 389564586 | 0;\n d = (d << 12 | d >>> 20) + a | 0;\n c += (d & a | ~d & b) + k[2] + 606105819 | 0;\n c = (c << 17 | c >>> 15) + d | 0;\n b += (c & d | ~c & a) + k[3] - 1044525330 | 0;\n b = (b << 22 | b >>> 10) + c | 0;\n a += (b & c | ~b & d) + k[4] - 176418897 | 0;\n a = (a << 7 | a >>> 25) + b | 0;\n d += (a & b | ~a & c) + k[5] + 1200080426 | 0;\n d = (d << 12 | d >>> 20) + a | 0;\n c += (d & a | ~d & b) + k[6] - 1473231341 | 0;\n c = (c << 17 | c >>> 15) + d | 0;\n b += (c & d | ~c & a) + k[7] - 45705983 | 0;\n b = (b << 22 | b >>> 10) + c | 0;\n a += (b & c | ~b & d) + k[8] + 1770035416 | 0;\n a = (a << 7 | a >>> 25) + b | 0;\n d += (a & b | ~a & c) + k[9] - 1958414417 | 0;\n d = (d << 12 | d >>> 20) + a | 0;\n c += (d & a | ~d & b) + k[10] - 42063 | 0;\n c = (c << 17 | c >>> 15) + d | 0;\n b += (c & d | ~c & a) + k[11] - 1990404162 | 0;\n b = (b << 22 | b >>> 10) + c | 0;\n a += (b & c | ~b & d) + k[12] + 1804603682 | 0;\n a = (a << 7 | a >>> 25) + b | 0;\n d += (a & b | ~a & c) + k[13] - 40341101 | 0;\n d = (d << 12 | d >>> 20) + a | 0;\n c += (d & a | ~d & b) + k[14] - 1502002290 | 0;\n c = (c << 17 | c >>> 15) + d | 0;\n b += (c & d | ~c & a) + k[15] + 1236535329 | 0;\n b = (b << 22 | b >>> 10) + c | 0;\n\n a += (b & d | c & ~d) + k[1] - 165796510 | 0;\n a = (a << 5 | a >>> 27) + b | 0;\n d += (a & c | b & ~c) + k[6] - 1069501632 | 0;\n d = (d << 9 | d >>> 23) + a | 0;\n c += (d & b | a & ~b) + k[11] + 643717713 | 0;\n c = (c << 14 | c >>> 18) + d | 0;\n b += (c & a | d & ~a) + k[0] - 373897302 | 0;\n b = (b << 20 | b >>> 12) + c | 0;\n a += (b & d | c & ~d) + k[5] - 701558691 | 0;\n a = (a << 5 | a >>> 27) + b | 0;\n d += (a & c | b & ~c) + k[10] + 38016083 | 0;\n d = (d << 9 | d >>> 23) + a | 0;\n c += (d & b | a & ~b) + k[15] - 660478335 | 0;\n c = (c << 14 | c >>> 18) + d | 0;\n b += (c & a | d & ~a) + k[4] - 405537848 | 0;\n b = (b << 20 | b >>> 12) + c | 0;\n a += (b & d | c & ~d) + k[9] + 568446438 | 0;\n a = (a << 5 | a >>> 27) + b | 0;\n d += (a & c | b & ~c) + k[14] - 1019803690 | 0;\n d = (d << 9 | d >>> 23) + a | 0;\n c += (d & b | a & ~b) + k[3] - 187363961 | 0;\n c = (c << 14 | c >>> 18) + d | 0;\n b += (c & a | d & ~a) + k[8] + 1163531501 | 0;\n b = (b << 20 | b >>> 12) + c | 0;\n a += (b & d | c & ~d) + k[13] - 1444681467 | 0;\n a = (a << 5 | a >>> 27) + b | 0;\n d += (a & c | b & ~c) + k[2] - 51403784 | 0;\n d = (d << 9 | d >>> 23) + a | 0;\n c += (d & b | a & ~b) + k[7] + 1735328473 | 0;\n c = (c << 14 | c >>> 18) + d | 0;\n b += (c & a | d & ~a) + k[12] - 1926607734 | 0;\n b = (b << 20 | b >>> 12) + c | 0;\n\n a += (b ^ c ^ d) + k[5] - 378558 | 0;\n a = (a << 4 | a >>> 28) + b | 0;\n d += (a ^ b ^ c) + k[8] - 2022574463 | 0;\n d = (d << 11 | d >>> 21) + a | 0;\n c += (d ^ a ^ b) + k[11] + 1839030562 | 0;\n c = (c << 16 | c >>> 16) + d | 0;\n b += (c ^ d ^ a) + k[14] - 35309556 | 0;\n b = (b << 23 | b >>> 9) + c | 0;\n a += (b ^ c ^ d) + k[1] - 1530992060 | 0;\n a = (a << 4 | a >>> 28) + b | 0;\n d += (a ^ b ^ c) + k[4] + 1272893353 | 0;\n d = (d << 11 | d >>> 21) + a | 0;\n c += (d ^ a ^ b) + k[7] - 155497632 | 0;\n c = (c << 16 | c >>> 16) + d | 0;\n b += (c ^ d ^ a) + k[10] - 1094730640 | 0;\n b = (b << 23 | b >>> 9) + c | 0;\n a += (b ^ c ^ d) + k[13] + 681279174 | 0;\n a = (a << 4 | a >>> 28) + b | 0;\n d += (a ^ b ^ c) + k[0] - 358537222 | 0;\n d = (d << 11 | d >>> 21) + a | 0;\n c += (d ^ a ^ b) + k[3] - 722521979 | 0;\n c = (c << 16 | c >>> 16) + d | 0;\n b += (c ^ d ^ a) + k[6] + 76029189 | 0;\n b = (b << 23 | b >>> 9) + c | 0;\n a += (b ^ c ^ d) + k[9] - 640364487 | 0;\n a = (a << 4 | a >>> 28) + b | 0;\n d += (a ^ b ^ c) + k[12] - 421815835 | 0;\n d = (d << 11 | d >>> 21) + a | 0;\n c += (d ^ a ^ b) + k[15] + 530742520 | 0;\n c = (c << 16 | c >>> 16) + d | 0;\n b += (c ^ d ^ a) + k[2] - 995338651 | 0;\n b = (b << 23 | b >>> 9) + c | 0;\n\n a += (c ^ (b | ~d)) + k[0] - 198630844 | 0;\n a = (a << 6 | a >>> 26) + b | 0;\n d += (b ^ (a | ~c)) + k[7] + 1126891415 | 0;\n d = (d << 10 | d >>> 22) + a | 0;\n c += (a ^ (d | ~b)) + k[14] - 1416354905 | 0;\n c = (c << 15 | c >>> 17) + d | 0;\n b += (d ^ (c | ~a)) + k[5] - 57434055 | 0;\n b = (b << 21 |b >>> 11) + c | 0;\n a += (c ^ (b | ~d)) + k[12] + 1700485571 | 0;\n a = (a << 6 | a >>> 26) + b | 0;\n d += (b ^ (a | ~c)) + k[3] - 1894986606 | 0;\n d = (d << 10 | d >>> 22) + a | 0;\n c += (a ^ (d | ~b)) + k[10] - 1051523 | 0;\n c = (c << 15 | c >>> 17) + d | 0;\n b += (d ^ (c | ~a)) + k[1] - 2054922799 | 0;\n b = (b << 21 |b >>> 11) + c | 0;\n a += (c ^ (b | ~d)) + k[8] + 1873313359 | 0;\n a = (a << 6 | a >>> 26) + b | 0;\n d += (b ^ (a | ~c)) + k[15] - 30611744 | 0;\n d = (d << 10 | d >>> 22) + a | 0;\n c += (a ^ (d | ~b)) + k[6] - 1560198380 | 0;\n c = (c << 15 | c >>> 17) + d | 0;\n b += (d ^ (c | ~a)) + k[13] + 1309151649 | 0;\n b = (b << 21 |b >>> 11) + c | 0;\n a += (c ^ (b | ~d)) + k[4] - 145523070 | 0;\n a = (a << 6 | a >>> 26) + b | 0;\n d += (b ^ (a | ~c)) + k[11] - 1120210379 | 0;\n d = (d << 10 | d >>> 22) + a | 0;\n c += (a ^ (d | ~b)) + k[2] + 718787259 | 0;\n c = (c << 15 | c >>> 17) + d | 0;\n b += (d ^ (c | ~a)) + k[9] - 343485551 | 0;\n b = (b << 21 | b >>> 11) + c | 0;\n\n x[0] = a + x[0] | 0;\n x[1] = b + x[1] | 0;\n x[2] = c + x[2] | 0;\n x[3] = d + x[3] | 0;\n }\n\n function md5blk(s) {\n var md5blks = [],\n i; /* Andy King said do it this way. */\n\n for (i = 0; i < 64; i += 4) {\n md5blks[i >> 2] = s.charCodeAt(i) + (s.charCodeAt(i + 1) << 8) + (s.charCodeAt(i + 2) << 16) + (s.charCodeAt(i + 3) << 24);\n }\n return md5blks;\n }\n\n function md5blk_array(a) {\n var md5blks = [],\n i; /* Andy King said do it this way. */\n\n for (i = 0; i < 64; i += 4) {\n md5blks[i >> 2] = a[i] + (a[i + 1] << 8) + (a[i + 2] << 16) + (a[i + 3] << 24);\n }\n return md5blks;\n }\n\n function md51(s) {\n var n = s.length,\n state = [1732584193, -271733879, -1732584194, 271733878],\n i,\n length,\n tail,\n tmp,\n lo,\n hi;\n\n for (i = 64; i <= n; i += 64) {\n md5cycle(state, md5blk(s.substring(i - 64, i)));\n }\n s = s.substring(i - 64);\n length = s.length;\n tail = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];\n for (i = 0; i < length; i += 1) {\n tail[i >> 2] |= s.charCodeAt(i) << ((i % 4) << 3);\n }\n tail[i >> 2] |= 0x80 << ((i % 4) << 3);\n if (i > 55) {\n md5cycle(state, tail);\n for (i = 0; i < 16; i += 1) {\n tail[i] = 0;\n }\n }\n\n // Beware that the final length might not fit in 32 bits so we take care of that\n tmp = n * 8;\n tmp = tmp.toString(16).match(/(.*?)(.{0,8})$/);\n lo = parseInt(tmp[2], 16);\n hi = parseInt(tmp[1], 16) || 0;\n\n tail[14] = lo;\n tail[15] = hi;\n\n md5cycle(state, tail);\n return state;\n }\n\n function md51_array(a) {\n var n = a.length,\n state = [1732584193, -271733879, -1732584194, 271733878],\n i,\n length,\n tail,\n tmp,\n lo,\n hi;\n\n for (i = 64; i <= n; i += 64) {\n md5cycle(state, md5blk_array(a.subarray(i - 64, i)));\n }\n\n // Not sure if it is a bug, however IE10 will always produce a sub array of length 1\n // containing the last element of the parent array if the sub array specified starts\n // beyond the length of the parent array - weird.\n // https://connect.microsoft.com/IE/feedback/details/771452/typed-array-subarray-issue\n a = (i - 64) < n ? a.subarray(i - 64) : new Uint8Array(0);\n\n length = a.length;\n tail = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];\n for (i = 0; i < length; i += 1) {\n tail[i >> 2] |= a[i] << ((i % 4) << 3);\n }\n\n tail[i >> 2] |= 0x80 << ((i % 4) << 3);\n if (i > 55) {\n md5cycle(state, tail);\n for (i = 0; i < 16; i += 1) {\n tail[i] = 0;\n }\n }\n\n // Beware that the final length might not fit in 32 bits so we take care of that\n tmp = n * 8;\n tmp = tmp.toString(16).match(/(.*?)(.{0,8})$/);\n lo = parseInt(tmp[2], 16);\n hi = parseInt(tmp[1], 16) || 0;\n\n tail[14] = lo;\n tail[15] = hi;\n\n md5cycle(state, tail);\n\n return state;\n }\n\n function rhex(n) {\n var s = '',\n j;\n for (j = 0; j < 4; j += 1) {\n s += hex_chr[(n >> (j * 8 + 4)) & 0x0F] + hex_chr[(n >> (j * 8)) & 0x0F];\n }\n return s;\n }\n\n function hex(x) {\n var i;\n for (i = 0; i < x.length; i += 1) {\n x[i] = rhex(x[i]);\n }\n return x.join('');\n }\n\n // In some cases the fast add32 function cannot be used..\n if (hex(md51('hello')) !== '5d41402abc4b2a76b9719d911017c592') {\n add32 = function (x, y) {\n var lsw = (x & 0xFFFF) + (y & 0xFFFF),\n msw = (x >> 16) + (y >> 16) + (lsw >> 16);\n return (msw << 16) | (lsw & 0xFFFF);\n };\n }\n\n // ---------------------------------------------------\n\n /**\n * ArrayBuffer slice polyfill.\n *\n * @see https://github.com/ttaubert/node-arraybuffer-slice\n */\n\n if (typeof ArrayBuffer !== 'undefined' && !ArrayBuffer.prototype.slice) {\n (function () {\n function clamp(val, length) {\n val = (val | 0) || 0;\n\n if (val < 0) {\n return Math.max(val + length, 0);\n }\n\n return Math.min(val, length);\n }\n\n ArrayBuffer.prototype.slice = function (from, to) {\n var length = this.byteLength,\n begin = clamp(from, length),\n end = length,\n num,\n target,\n targetArray,\n sourceArray;\n\n if (to !== undefined) {\n end = clamp(to, length);\n }\n\n if (begin > end) {\n return new ArrayBuffer(0);\n }\n\n num = end - begin;\n target = new ArrayBuffer(num);\n targetArray = new Uint8Array(target);\n\n sourceArray = new Uint8Array(this, begin, num);\n targetArray.set(sourceArray);\n\n return target;\n };\n })();\n }\n\n // ---------------------------------------------------\n\n /**\n * Helpers.\n */\n\n function toUtf8(str) {\n if (/[\\u0080-\\uFFFF]/.test(str)) {\n str = unescape(encodeURIComponent(str));\n }\n\n return str;\n }\n\n function utf8Str2ArrayBuffer(str, returnUInt8Array) {\n var length = str.length,\n buff = new ArrayBuffer(length),\n arr = new Uint8Array(buff),\n i;\n\n for (i = 0; i < length; i += 1) {\n arr[i] = str.charCodeAt(i);\n }\n\n return returnUInt8Array ? arr : buff;\n }\n\n function arrayBuffer2Utf8Str(buff) {\n return String.fromCharCode.apply(null, new Uint8Array(buff));\n }\n\n function concatenateArrayBuffers(first, second, returnUInt8Array) {\n var result = new Uint8Array(first.byteLength + second.byteLength);\n\n result.set(new Uint8Array(first));\n result.set(new Uint8Array(second), first.byteLength);\n\n return returnUInt8Array ? result : result.buffer;\n }\n\n function hexToBinaryString(hex) {\n var bytes = [],\n length = hex.length,\n x;\n\n for (x = 0; x < length - 1; x += 2) {\n bytes.push(parseInt(hex.substr(x, 2), 16));\n }\n\n return String.fromCharCode.apply(String, bytes);\n }\n\n // ---------------------------------------------------\n\n /**\n * SparkMD5 OOP implementation.\n *\n * Use this class to perform an incremental md5, otherwise use the\n * static methods instead.\n */\n\n function SparkMD5() {\n // call reset to init the instance\n this.reset();\n }\n\n /**\n * Appends a string.\n * A conversion will be applied if an utf8 string is detected.\n *\n * @param {String} str The string to be appended\n *\n * @return {SparkMD5} The instance itself\n */\n SparkMD5.prototype.append = function (str) {\n // Converts the string to utf8 bytes if necessary\n // Then append as binary\n this.appendBinary(toUtf8(str));\n\n return this;\n };\n\n /**\n * Appends a binary string.\n *\n * @param {String} contents The binary string to be appended\n *\n * @return {SparkMD5} The instance itself\n */\n SparkMD5.prototype.appendBinary = function (contents) {\n this._buff += contents;\n this._length += contents.length;\n\n var length = this._buff.length,\n i;\n\n for (i = 64; i <= length; i += 64) {\n md5cycle(this._hash, md5blk(this._buff.substring(i - 64, i)));\n }\n\n this._buff = this._buff.substring(i - 64);\n\n return this;\n };\n\n /**\n * Finishes the incremental computation, reseting the internal state and\n * returning the result.\n *\n * @param {Boolean} raw True to get the raw string, false to get the hex string\n *\n * @return {String} The result\n */\n SparkMD5.prototype.end = function (raw) {\n var buff = this._buff,\n length = buff.length,\n i,\n tail = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],\n ret;\n\n for (i = 0; i < length; i += 1) {\n tail[i >> 2] |= buff.charCodeAt(i) << ((i % 4) << 3);\n }\n\n this._finish(tail, length);\n ret = hex(this._hash);\n\n if (raw) {\n ret = hexToBinaryString(ret);\n }\n\n this.reset();\n\n return ret;\n };\n\n /**\n * Resets the internal state of the computation.\n *\n * @return {SparkMD5} The instance itself\n */\n SparkMD5.prototype.reset = function () {\n this._buff = '';\n this._length = 0;\n this._hash = [1732584193, -271733879, -1732584194, 271733878];\n\n return this;\n };\n\n /**\n * Gets the internal state of the computation.\n *\n * @return {Object} The state\n */\n SparkMD5.prototype.getState = function () {\n return {\n buff: this._buff,\n length: this._length,\n hash: this._hash.slice()\n };\n };\n\n /**\n * Gets the internal state of the computation.\n *\n * @param {Object} state The state\n *\n * @return {SparkMD5} The instance itself\n */\n SparkMD5.prototype.setState = function (state) {\n this._buff = state.buff;\n this._length = state.length;\n this._hash = state.hash;\n\n return this;\n };\n\n /**\n * Releases memory used by the incremental buffer and other additional\n * resources. If you plan to use the instance again, use reset instead.\n */\n SparkMD5.prototype.destroy = function () {\n delete this._hash;\n delete this._buff;\n delete this._length;\n };\n\n /**\n * Finish the final calculation based on the tail.\n *\n * @param {Array} tail The tail (will be modified)\n * @param {Number} length The length of the remaining buffer\n */\n SparkMD5.prototype._finish = function (tail, length) {\n var i = length,\n tmp,\n lo,\n hi;\n\n tail[i >> 2] |= 0x80 << ((i % 4) << 3);\n if (i > 55) {\n md5cycle(this._hash, tail);\n for (i = 0; i < 16; i += 1) {\n tail[i] = 0;\n }\n }\n\n // Do the final computation based on the tail and length\n // Beware that the final length may not fit in 32 bits so we take care of that\n tmp = this._length * 8;\n tmp = tmp.toString(16).match(/(.*?)(.{0,8})$/);\n lo = parseInt(tmp[2], 16);\n hi = parseInt(tmp[1], 16) || 0;\n\n tail[14] = lo;\n tail[15] = hi;\n md5cycle(this._hash, tail);\n };\n\n /**\n * Performs the md5 hash on a string.\n * A conversion will be applied if utf8 string is detected.\n *\n * @param {String} str The string\n * @param {Boolean} [raw] True to get the raw string, false to get the hex string\n *\n * @return {String} The result\n */\n SparkMD5.hash = function (str, raw) {\n // Converts the string to utf8 bytes if necessary\n // Then compute it using the binary function\n return SparkMD5.hashBinary(toUtf8(str), raw);\n };\n\n /**\n * Performs the md5 hash on a binary string.\n *\n * @param {String} content The binary string\n * @param {Boolean} [raw] True to get the raw string, false to get the hex string\n *\n * @return {String} The result\n */\n SparkMD5.hashBinary = function (content, raw) {\n var hash = md51(content),\n ret = hex(hash);\n\n return raw ? hexToBinaryString(ret) : ret;\n };\n\n // ---------------------------------------------------\n\n /**\n * SparkMD5 OOP implementation for array buffers.\n *\n * Use this class to perform an incremental md5 ONLY for array buffers.\n */\n SparkMD5.ArrayBuffer = function () {\n // call reset to init the instance\n this.reset();\n };\n\n /**\n * Appends an array buffer.\n *\n * @param {ArrayBuffer} arr The array to be appended\n *\n * @return {SparkMD5.ArrayBuffer} The instance itself\n */\n SparkMD5.ArrayBuffer.prototype.append = function (arr) {\n var buff = concatenateArrayBuffers(this._buff.buffer, arr, true),\n length = buff.length,\n i;\n\n this._length += arr.byteLength;\n\n for (i = 64; i <= length; i += 64) {\n md5cycle(this._hash, md5blk_array(buff.subarray(i - 64, i)));\n }\n\n this._buff = (i - 64) < length ? new Uint8Array(buff.buffer.slice(i - 64)) : new Uint8Array(0);\n\n return this;\n };\n\n /**\n * Finishes the incremental computation, reseting the internal state and\n * returning the result.\n *\n * @param {Boolean} raw True to get the raw string, false to get the hex string\n *\n * @return {String} The result\n */\n SparkMD5.ArrayBuffer.prototype.end = function (raw) {\n var buff = this._buff,\n length = buff.length,\n tail = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],\n i,\n ret;\n\n for (i = 0; i < length; i += 1) {\n tail[i >> 2] |= buff[i] << ((i % 4) << 3);\n }\n\n this._finish(tail, length);\n ret = hex(this._hash);\n\n if (raw) {\n ret = hexToBinaryString(ret);\n }\n\n this.reset();\n\n return ret;\n };\n\n /**\n * Resets the internal state of the computation.\n *\n * @return {SparkMD5.ArrayBuffer} The instance itself\n */\n SparkMD5.ArrayBuffer.prototype.reset = function () {\n this._buff = new Uint8Array(0);\n this._length = 0;\n this._hash = [1732584193, -271733879, -1732584194, 271733878];\n\n return this;\n };\n\n /**\n * Gets the internal state of the computation.\n *\n * @return {Object} The state\n */\n SparkMD5.ArrayBuffer.prototype.getState = function () {\n var state = SparkMD5.prototype.getState.call(this);\n\n // Convert buffer to a string\n state.buff = arrayBuffer2Utf8Str(state.buff);\n\n return state;\n };\n\n /**\n * Gets the internal state of the computation.\n *\n * @param {Object} state The state\n *\n * @return {SparkMD5.ArrayBuffer} The instance itself\n */\n SparkMD5.ArrayBuffer.prototype.setState = function (state) {\n // Convert string to buffer\n state.buff = utf8Str2ArrayBuffer(state.buff, true);\n\n return SparkMD5.prototype.setState.call(this, state);\n };\n\n SparkMD5.ArrayBuffer.prototype.destroy = SparkMD5.prototype.destroy;\n\n SparkMD5.ArrayBuffer.prototype._finish = SparkMD5.prototype._finish;\n\n /**\n * Performs the md5 hash on an array buffer.\n *\n * @param {ArrayBuffer} arr The array buffer\n * @param {Boolean} [raw] True to get the raw string, false to get the hex one\n *\n * @return {String} The result\n */\n SparkMD5.ArrayBuffer.hash = function (arr, raw) {\n var hash = md51_array(new Uint8Array(arr)),\n ret = hex(hash);\n\n return raw ? hexToBinaryString(ret) : ret;\n };\n\n return SparkMD5;\n}));\n","/**\n * @file MD5 utility for Blob, Buffer, or file path inputs.\n */\nimport { ShipError } from '@shipstatic/types';\n\nexport interface MD5Result {\n md5: string;\n}\n\nasync function md5Blob(blob: Blob): Promise<MD5Result> {\n const SparkMD5 = (await import('spark-md5')).default;\n const spark = new SparkMD5.ArrayBuffer();\n const chunkSize = 2097152; // 2 MB\n for (let start = 0; start < blob.size; start += chunkSize) {\n const end = Math.min(start + chunkSize, blob.size);\n spark.append(await blob.slice(start, end).arrayBuffer());\n }\n return { md5: spark.end() };\n}\n\nasync function md5Buffer(buffer: Buffer): Promise<MD5Result> {\n const { createHash } = await import('node:crypto');\n const hash = createHash('md5');\n hash.update(buffer);\n return { md5: hash.digest('hex') };\n}\n\nasync function md5Path(path: string): Promise<MD5Result> {\n const { createHash } = await import('node:crypto');\n const { createReadStream } = await import('node:fs');\n return new Promise((resolve, reject) => {\n const hash = createHash('md5');\n const stream = createReadStream(path);\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\nexport async function calculateMD5(input: Blob | Buffer | string): Promise<MD5Result> {\n if (input instanceof Blob) return md5Blob(input);\n if (typeof Buffer !== 'undefined' && Buffer.isBuffer(input)) return md5Buffer(input);\n if (typeof input === 'string') return md5Path(input);\n throw ShipError.business('Invalid input for MD5 calculation');\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 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 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 * 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(/\\/+/g, '/').replace(/^\\/+/, '');\n}\n","/**\n * @file Deploy path optimization - the core logic that makes Ship deployments clean and intuitive.\n * Automatically strips common parent directories to create clean deployment URLs.\n */\n\nimport { normalizeWebPath } from './path.js';\n\n/**\n * Represents a file ready for deployment with its optimized path\n */\nexport interface DeployFile {\n /** The clean deployment path (e.g., \"assets/style.css\") */\n path: string;\n /** Original filename */\n name: string;\n}\n\n/**\n * Core path optimization logic.\n * Transforms messy local paths into clean deployment paths.\n *\n * @example\n * Input: [\"dist/index.html\", \"dist/assets/app.js\"]\n * Output: [\"index.html\", \"assets/app.js\"]\n *\n * @param filePaths - Raw file paths from the local filesystem\n * @param options - Path processing options\n */\nexport function optimizeDeployPaths(\n filePaths: string[],\n options: { flatten?: boolean } = {},\n): DeployFile[] {\n // When flattening is disabled, keep original structure\n if (options.flatten === false) {\n return filePaths.map((path) => ({\n path: normalizeWebPath(path),\n name: extractFileName(path),\n }));\n }\n\n // Find the common directory prefix to strip\n const commonPrefix = findCommonDirectory(filePaths);\n\n return filePaths.map((filePath) => {\n let deployPath = normalizeWebPath(filePath);\n\n // Strip the common prefix to create clean deployment paths\n if (commonPrefix) {\n const prefixToRemove = commonPrefix.endsWith('/') ? commonPrefix : `${commonPrefix}/`;\n if (deployPath.startsWith(prefixToRemove)) {\n deployPath = deployPath.substring(prefixToRemove.length);\n }\n }\n\n // Fallback to filename if path becomes empty\n if (!deployPath) {\n deployPath = extractFileName(filePath);\n }\n\n return {\n path: deployPath,\n name: extractFileName(filePath),\n };\n });\n}\n\n/**\n * Finds the common directory shared by all file paths.\n * This is what gets stripped to create clean deployment URLs.\n *\n * @example\n * [\"dist/index.html\", \"dist/assets/app.js\"] → \"dist\"\n * [\"src/components/A.tsx\", \"src/utils/B.ts\"] → \"src\"\n * [\"file1.txt\", \"file2.txt\", \"subdir/file3.txt\"] → \"\" (no common directory)\n */\nfunction findCommonDirectory(filePaths: string[]): string {\n if (!filePaths.length) return '';\n\n // Normalize all paths first\n const normalizedPaths = filePaths.map((path) => normalizeWebPath(path));\n\n // Find the common prefix among all file paths (not just directories)\n const pathSegments = normalizedPaths.map((path) => path.split('/'));\n const commonSegments: string[] = [];\n const minLength = Math.min(...pathSegments.map((segments) => segments.length));\n\n // Check each segment level to find the longest common prefix\n for (let i = 0; i < minLength - 1; i++) {\n // -1 because we don't want to include the filename\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 * Extracts just the filename from a file path\n */\nfunction extractFileName(path: string): string {\n return path.split(/[/\\\\]/).pop() || path;\n}\n","/**\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 File validation utilities for Ship SDK\n * Provides client-side validation for file uploads before deployment\n */\n\nimport type {\n FileValidationResult,\n FileValidationStatusType,\n PlatformLimits,\n ValidatableFile,\n ValidationIssue,\n} from '@shipstatic/types';\nimport {\n FileValidationStatus as FILE_VALIDATION_STATUS,\n hasUnbuiltMarker,\n hasUnsafeChars,\n isBlockedExtension,\n} from '@shipstatic/types';\n\nexport { FILE_VALIDATION_STATUS };\n\n/**\n * Format file size to human-readable string\n */\nexport function formatFileSize(bytes: number, decimals: number = 1): string {\n if (bytes === 0) return '0 Bytes';\n const k = 1024;\n const sizes = ['Bytes', 'KB', 'MB', 'GB'];\n const i = Math.floor(Math.log(bytes) / Math.log(k));\n return `${parseFloat((bytes / k ** i).toFixed(decimals))} ${sizes[i]}`;\n}\n\n/**\n * Validate filename for deployment safety\n *\n * Blocks only characters that genuinely break the upload→serve round-trip:\n * - # ? % URL round-trip breakers (fragment, query, encoding ambiguity)\n * - \\ Path separator confusion (buildFileKey splits on backslash)\n * - < > \" XSS vectors with zero legitimate use in filenames\n * - \\x00-\\x1f \\x7f Control characters (header injection, display corruption)\n *\n * Everything else is allowed — browser percent-encodes, Worker decodes, R2 matches.\n *\n * Additional checks: path traversal, reserved names, leading/trailing dots or spaces.\n */\nexport function validateFileName(filename: string): { valid: boolean; reason?: string } {\n if (hasUnsafeChars(filename)) {\n return { valid: false, reason: 'File name contains unsafe characters' };\n }\n\n if (filename.startsWith(' ') || filename.endsWith(' ')) {\n return { valid: false, reason: 'File name cannot start/end with spaces' };\n }\n\n if (filename.endsWith('.')) {\n return { valid: false, reason: 'File name cannot end with dots' };\n }\n\n const reservedNames = /^(CON|PRN|AUX|NUL|COM[1-9]|LPT[1-9])(\\.|$)/i;\n const nameWithoutPath = filename.split('/').pop() || filename;\n if (reservedNames.test(nameWithoutPath)) {\n return { valid: false, reason: 'File name uses a reserved system name' };\n }\n\n if (filename.includes('..')) {\n return { valid: false, reason: 'File name contains path traversal pattern' };\n }\n\n return { valid: true };\n}\n\n/**\n * Validate files against configuration limits with severity-based reporting\n *\n * Validation categorizes issues by severity:\n * - **Errors**: Block deployment (file too large, blocked extension, etc.)\n * - **Warnings**: Exclude files but allow deployment (empty files, etc.)\n *\n * @param files - Array of files to validate\n * @param config - Validation configuration from ship.getLimits()\n * @returns Validation result with errors and warnings\n *\n * @example\n * ```typescript\n * const config = await ship.getLimits();\n * const result = validateFiles(files, config);\n *\n * if (!result.canDeploy) {\n * // Has errors - deployment blocked\n * console.error('Deployment blocked:', result.errors);\n * } else if (result.warnings.length > 0) {\n * // Has warnings - deployment proceeds, some files excluded\n * console.warn('Files excluded:', result.warnings);\n * await ship.deploy(result.validFiles);\n * } else {\n * // All files valid\n * await ship.deploy(result.validFiles);\n * }\n * ```\n */\nexport function validateFiles<T extends ValidatableFile>(\n files: T[],\n config: PlatformLimits,\n): FileValidationResult<T> {\n const errors: ValidationIssue[] = [];\n const warnings: ValidationIssue[] = [];\n let fileStatuses: T[] = []; // Use 'let' for atomic enforcement later\n\n // Check at least 1 file required\n if (files.length === 0) {\n const issue: ValidationIssue = {\n file: '(no files)',\n message: 'At least one file must be provided',\n };\n errors.push(issue);\n\n return {\n files: [],\n validFiles: [],\n errors,\n warnings: [],\n canDeploy: false,\n };\n }\n\n // Check for unbuilt project markers (node_modules/, etc.)\n for (const file of files) {\n if (hasUnbuiltMarker(file.name)) {\n errors.push({\n file: file.name,\n message: `Unbuilt project detected — deploy your build output (dist/, build/, out/), not the project folder`,\n });\n return {\n files: files.map((f) => ({\n ...f,\n status: FILE_VALIDATION_STATUS.VALIDATION_FAILED,\n statusMessage: 'Unbuilt project detected',\n })),\n validFiles: [],\n errors,\n warnings: [],\n canDeploy: false,\n };\n }\n }\n\n // Check file count limit\n if (files.length > config.maxFilesCount) {\n const issue: ValidationIssue = {\n file: `(${files.length} files)`,\n message: `File count (${files.length}) exceeds limit of ${config.maxFilesCount}`,\n };\n errors.push(issue);\n\n return {\n files: files.map((f) => ({\n ...f,\n status: FILE_VALIDATION_STATUS.VALIDATION_FAILED,\n statusMessage: issue.message,\n })),\n validFiles: [],\n errors,\n warnings: [],\n canDeploy: false,\n };\n }\n\n // Validate each file\n let totalSize = 0;\n\n for (const file of files) {\n let fileStatus: FileValidationStatusType = FILE_VALIDATION_STATUS.READY;\n let statusMessage = 'Ready for upload';\n\n // Pre-compute filename validation\n const nameValidation = file.name\n ? validateFileName(file.name)\n : { valid: false, reason: 'File name cannot be empty' };\n\n // Check for processing errors\n if (file.status === FILE_VALIDATION_STATUS.PROCESSING_ERROR) {\n fileStatus = FILE_VALIDATION_STATUS.VALIDATION_FAILED;\n statusMessage = file.statusMessage || 'File failed during processing';\n errors.push({\n file: file.name,\n message: statusMessage,\n });\n }\n\n // EMPTY FILE - Warning (not error)\n else if (file.size === 0) {\n fileStatus = FILE_VALIDATION_STATUS.EXCLUDED;\n statusMessage = 'File is empty (0 bytes) and cannot be deployed due to storage limitations';\n warnings.push({\n file: file.name,\n message: statusMessage,\n });\n // Skip other validations for excluded files\n fileStatuses.push({\n ...file,\n status: fileStatus,\n statusMessage,\n });\n continue;\n }\n\n // Negative file size - Error\n else if (file.size < 0) {\n fileStatus = FILE_VALIDATION_STATUS.VALIDATION_FAILED;\n statusMessage = 'File size must be positive';\n errors.push({\n file: file.name,\n message: statusMessage,\n });\n }\n\n // File name validation\n else if (!file.name || file.name.trim().length === 0) {\n fileStatus = FILE_VALIDATION_STATUS.VALIDATION_FAILED;\n statusMessage = 'File name cannot be empty';\n errors.push({\n file: file.name || '(empty)',\n message: statusMessage,\n });\n } else if (file.name.includes('\\0')) {\n fileStatus = FILE_VALIDATION_STATUS.VALIDATION_FAILED;\n statusMessage = 'File name contains invalid characters (null byte)';\n errors.push({\n file: file.name,\n message: statusMessage,\n });\n } else if (!nameValidation.valid) {\n fileStatus = FILE_VALIDATION_STATUS.VALIDATION_FAILED;\n statusMessage = nameValidation.reason || 'Invalid file name';\n errors.push({\n file: file.name,\n message: statusMessage,\n });\n }\n\n // Blocked extension check\n else if (isBlockedExtension(file.name)) {\n fileStatus = FILE_VALIDATION_STATUS.VALIDATION_FAILED;\n statusMessage = `File extension not allowed: \"${file.name}\"`;\n errors.push({\n file: file.name,\n message: statusMessage,\n });\n }\n\n // File size validation\n else if (file.size > config.maxFileSize) {\n fileStatus = FILE_VALIDATION_STATUS.VALIDATION_FAILED;\n statusMessage = `File size (${formatFileSize(file.size)}) exceeds limit of ${formatFileSize(config.maxFileSize)}`;\n errors.push({\n file: file.name,\n message: statusMessage,\n });\n }\n\n // Total size validation\n else {\n totalSize += file.size;\n if (totalSize > config.maxTotalSize) {\n fileStatus = FILE_VALIDATION_STATUS.VALIDATION_FAILED;\n statusMessage = `Total size would exceed limit of ${formatFileSize(config.maxTotalSize)}`;\n errors.push({\n file: file.name,\n message: statusMessage,\n });\n }\n }\n\n fileStatuses.push({\n ...file,\n status: fileStatus,\n statusMessage,\n });\n }\n\n // ATOMIC ENFORCEMENT: Two-phase validation for optimal UX + atomic semantics\n // Phase 1 (above): Validate files individually to collect ALL errors\n // Phase 2 (below): Mark all files as failed if any errors exist\n //\n // Why two phases? We validate individually for better UX (users see all problems\n // at once and can fix everything in one pass), then enforce atomicity to maintain\n // deployment transaction semantics (all-or-nothing).\n if (errors.length > 0) {\n fileStatuses = fileStatuses.map((file) => {\n // Keep EXCLUDED files as-is (they're warnings, not errors)\n if (file.status === FILE_VALIDATION_STATUS.EXCLUDED) {\n return file;\n }\n\n // Mark ALL other files as VALIDATION_FAILED (atomic deployment)\n return {\n ...file,\n status: FILE_VALIDATION_STATUS.VALIDATION_FAILED,\n statusMessage:\n file.status === FILE_VALIDATION_STATUS.VALIDATION_FAILED\n ? file.statusMessage // Keep original error message for the file that actually failed\n : 'Deployment failed due to validation errors in bundle',\n };\n });\n }\n\n // Build atomic result\n // validFiles is empty if ANY errors exist (all-or-nothing)\n const validFiles =\n errors.length === 0\n ? fileStatuses.filter((f) => f.status === FILE_VALIDATION_STATUS.READY)\n : [];\n const canDeploy = errors.length === 0;\n\n return {\n files: fileStatuses,\n validFiles,\n errors,\n warnings,\n canDeploy,\n };\n}\n\n/**\n * Get only the valid files from validation results\n */\nexport function getValidFiles<T extends ValidatableFile>(files: T[]): T[] {\n return files.filter((f) => f.status === FILE_VALIDATION_STATUS.READY);\n}\n\n/**\n * Check if all valid files have required properties for upload\n * (Can be extended to check for MD5, etc.)\n */\nexport function allValidFilesReady<T extends ValidatableFile>(files: T[]): boolean {\n const validFiles = getValidFiles(files);\n return validFiles.length > 0;\n}\n","const ignoreList = [\n\t// # All\n\t'^npm-debug\\\\.log$', // Error log for npm\n\t'^\\\\..*\\\\.swp$', // Swap file for vim state\n\n\t// # macOS\n\t'^\\\\.DS_Store$', // Stores custom folder attributes\n\t'^\\\\.AppleDouble$', // Stores additional file resources\n\t'^\\\\.LSOverride$', // Contains the absolute path to the app to be used\n\t'^Icon\\\\r$', // Custom Finder icon: http://superuser.com/questions/298785/icon-file-on-os-x-desktop\n\t'^\\\\._.*', // Thumbnail\n\t'^\\\\.Spotlight-V100(?:$|\\\\/)', // Directory that might appear on external disk\n\t'\\\\.Trashes', // File that might appear on external disk\n\t'^__MACOSX$', // Resource fork\n\n\t// # Linux\n\t'~$', // Backup file\n\n\t// # Windows\n\t'^Thumbs\\\\.db$', // Image file cache\n\t'^ehthumbs\\\\.db$', // Folder config file\n\t'^[Dd]esktop\\\\.ini$', // Stores custom folder attributes\n\t'@eaDir$', // Synology Diskstation \"hidden\" folder where the server stores thumbnails\n];\n\nexport const junkRegex = new RegExp(ignoreList.join('|'));\n\nexport function isJunk(filename) {\n\treturn junkRegex.test(filename);\n}\n\nexport function isNotJunk(filename) {\n\treturn !isJunk(filename);\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 */\n\nimport { hasUnbuiltMarker, ShipError } from '@shipstatic/types';\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 = ['__MACOSX', '.Trashes', '.fseventsd', '.Spotlight-V100'] as const;\n\n/**\n * Filters an array of file paths, removing those considered junk\n *\n * Throws if any path contains an unbuilt project marker (e.g. `node_modules`, `package.json`).\n * This check runs first because the dot-file filter below would strip paths like\n * `node_modules/.pnpm/...`, destroying the evidence.\n *\n * A path is filtered out if any of these conditions are met:\n * 1. The basename is identified as junk by the 'junk' package (e.g., .DS_Store, Thumbs.db)\n * 2. Any path segment starts with a dot (e.g., .env, .git, .htaccess)\n * Exception: `.well-known` is allowed (RFC 8615 — ACME, security.txt, app links)\n * 3. Any path segment exceeds 255 characters (filesystem limit)\n * 4. 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 * Dot files are filtered for security — they typically contain sensitive configuration\n * (.env, .git) or are not meant to be served publicly. This matches server-side filtering.\n *\n * @param filePaths - An array of file path strings to filter\n * @param options - Optional settings\n * @param options.allowUnbuilt - When true, skip the unbuilt project marker check (for server-processed uploads)\n * @returns A new array containing only non-junk file paths\n * @throws {ShipError} If any path contains an unbuilt project marker (unless allowUnbuilt is true)\n *\n * @example\n * ```typescript\n * import { filterJunk } from '@shipstatic/ship';\n *\n * // Filter an array of file paths\n * const paths = ['index.html', '.DS_Store', '.gitattributes', '__MACOSX/file.txt', 'app.js'];\n * const clean = filterJunk(paths);\n * // Result: ['index.html', 'app.js']\n * ```\n *\n * @example\n * ```typescript\n * // Use with browser File objects\n * import { filterJunk } from '@shipstatic/ship';\n *\n * const files: File[] = [...]; // From input or drag-drop\n *\n * // Extract paths from File objects\n * const filePaths = files.map(f => f.webkitRelativePath || f.name);\n *\n * // Filter out junk paths\n * const validPaths = new Set(filterJunk(filePaths));\n *\n * // Filter the original File array\n * const validFiles = files.filter(f =>\n * validPaths.has(f.webkitRelativePath || f.name)\n * );\n * ```\n */\nexport function filterJunk(filePaths: string[], options?: { allowUnbuilt?: boolean }): string[] {\n if (!filePaths || filePaths.length === 0) {\n return [];\n }\n\n // Reject unbuilt projects before the dot-file filter removes evidence.\n // pnpm stores files under node_modules/.pnpm/ — the dot-file filter below\n // strips .pnpm/ paths, destroying the only signal that this is an unbuilt project.\n if (!options?.allowUnbuilt) {\n const marker = filePaths.find((p) => p && hasUnbuiltMarker(p));\n if (marker) {\n throw ShipError.business(\n 'Unbuilt project detected — deploy your build output (dist/, build/, out/), not the project folder',\n );\n }\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 // Filter out dot files and directories (security: prevents .env, .git, etc.)\n // .well-known is not junk — it's a standard directory (RFC 8615)\n // Path position constraints enforced at upload (buildFileKey) and serving (isBlockedDotFile)\n for (const part of parts) {\n if (part === '.well-known') continue;\n if (part.startsWith('.') || part.length > 255) {\n return false;\n }\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) => segment.toLowerCase() === junkDir.toLowerCase())) {\n return false;\n }\n }\n\n return true;\n });\n}\n","/**\n * @file Shared security validation for the deploy pipeline.\n * Used by both Node.js and browser file processing pipelines.\n */\nimport { isBlockedExtension, ShipError } from '@shipstatic/types';\nimport { validateFileName } from './file-validation.js';\n\n/**\n * Validate a deploy path for security concerns.\n * Rejects paths containing path traversal patterns or null bytes.\n *\n * Checks for:\n * - Null bytes (\\0) — path injection\n * - /../ — directory traversal within path\n * - ../ at start — upward traversal\n * - /.. at end — trailing traversal\n *\n * Does NOT reject double dots in filenames (e.g., \"foo..bar.txt\" is safe).\n *\n * @param deployPath - The deployment path to validate\n * @param sourceIdentifier - Human-readable identifier for error messages\n * @throws {ShipError} If the path contains unsafe patterns\n */\nexport function validateDeployPath(deployPath: string, sourceIdentifier: string): void {\n if (\n deployPath.includes('\\0') ||\n deployPath.includes('/../') ||\n deployPath.startsWith('../') ||\n deployPath.endsWith('/..')\n ) {\n throw ShipError.business(\n `Security error: Unsafe file path \"${deployPath}\" for file: ${sourceIdentifier}`,\n );\n }\n}\n\n/**\n * Validate a deploy file's name and extension.\n * Rejects unsafe filenames (shell/URL-dangerous chars, reserved names)\n * and blocked file extensions (.exe, .msi, .dll, etc.).\n *\n * @param deployPath - The deployment path to validate\n * @param sourceIdentifier - Human-readable identifier for error messages\n * @throws {ShipError} If the filename is unsafe or extension is blocked\n */\nexport function validateDeployFile(deployPath: string, sourceIdentifier: string): void {\n const nameCheck = validateFileName(deployPath);\n if (!nameCheck.valid) {\n throw ShipError.business(nameCheck.reason || 'Invalid file name');\n }\n\n if (isBlockedExtension(deployPath)) {\n throw ShipError.business(`File extension not allowed: \"${sourceIdentifier}\"`);\n }\n}\n","/**\n * @file Browser-specific file utilities for the Ship SDK.\n * Provides helpers for processing browser files into deploy-ready objects.\n *\n * Two modes:\n * - **Deploy** (default): Full validation pipeline — security, extensions, sizes, counts.\n * - **Server-processed** (build/prerender): Source files destined for server-side build.\n * Junk filtering and MD5 checksums only — the build service validates the output.\n *\n * Both modes share: environment check → extract paths → optimize paths → filter junk → MD5.\n */\nimport type { PlatformLimits } from '@shipstatic/types';\nimport { ShipError } from '@shipstatic/types';\nimport { optimizeDeployPaths } from '../../shared/lib/deploy-paths.js';\nimport { getENV } from '../../shared/lib/env.js';\nimport { filterJunk } from '../../shared/lib/junk.js';\nimport { calculateMD5 } from '../../shared/lib/md5.js';\nimport { validateDeployFile, validateDeployPath } from '../../shared/lib/security.js';\nimport type { DeploymentOptions, StaticFile } from '../../shared/types.js';\n\n/**\n * Processes browser files into an array of StaticFile objects ready for deploy.\n * Calculates MD5, filters junk files, validates sizes, and applies path optimization.\n *\n * For server-processed uploads (build/prerender), client-side deploy validation is\n * skipped — the build service produces and validates the actual deployment output.\n *\n * @param browserFiles - File[] to process for deploy.\n * @param options - Processing options including pathDetect for automatic path optimization.\n * @param platformLimits - Per-instance platform limits (file-size / count / total-size caps)\n * from the originating Ship's `GET /config` fetch. Passed in rather than read from a\n * module global so concurrent Ships against different API URLs cannot clobber each\n * other's caps.\n * @returns Promise resolving to an array of StaticFile objects.\n * @throws {ShipError} If called outside a browser or with invalid input.\n */\nexport async function processFilesForBrowser(\n browserFiles: File[],\n options: DeploymentOptions = {},\n platformLimits?: PlatformLimits,\n): Promise<StaticFile[]> {\n // 1. Environment check\n if (getENV() !== 'browser') {\n throw ShipError.business('processFilesForBrowser can only be called in a browser environment.');\n }\n\n // 2. Extract raw paths from File objects\n const rawPaths = browserFiles.map((file) => file.webkitRelativePath || file.name);\n\n // Server-processed uploads (build/prerender) send source files, not deploy output\n const isServerProcessed = options.build || options.prerender;\n\n // 3. Optimize paths for deployment (strip common root, flatten)\n const deployFiles = optimizeDeployPaths(rawPaths, { flatten: options.pathDetect !== false });\n const deployPaths = deployFiles.map((f) => f.path);\n\n // 4. Filter junk from deploy paths (allowUnbuilt for server-processed)\n const filteredSet = new Set(filterJunk(deployPaths, { allowUnbuilt: isServerProcessed }));\n const validPairs: Array<{ file: File; deployPath: string }> = [];\n for (let i = 0; i < browserFiles.length; i++) {\n if (filteredSet.has(deployPaths[i])) {\n validPairs.push({ file: browserFiles[i], deployPath: deployFiles[i].path });\n }\n }\n\n if (validPairs.length === 0) {\n return [];\n }\n\n // 5. Server-processed: skip deploy validation, just compute checksums\n if (isServerProcessed) {\n const results: StaticFile[] = [];\n for (let i = 0; i < validPairs.length; i++) {\n const { file, deployPath } = validPairs[i];\n if (file.size === 0) continue;\n const { md5 } = await calculateMD5(file);\n results.push({ path: deployPath, content: file, size: file.size, md5 });\n }\n return results;\n }\n\n // 6. Deploy: full validation pipeline\n if (!platformLimits) {\n throw ShipError.config(\n 'Platform limits not provided. processFilesForBrowser requires the limits ' +\n 'argument for deploy-mode validation — pass `ship.getLimits()` result.',\n );\n }\n const results: StaticFile[] = [];\n let totalSize = 0;\n\n for (let i = 0; i < validPairs.length; i++) {\n const { file, deployPath } = validPairs[i];\n\n // Security validation (shared with Node)\n validateDeployPath(deployPath, file.name);\n\n // Skip empty files — R2 cannot store zero-byte objects\n if (file.size === 0) {\n continue;\n }\n\n // Filename and extension validation (shared with Node)\n validateDeployFile(deployPath, file.name);\n\n // Validate file sizes (matches Node validation)\n if (file.size > platformLimits.maxFileSize) {\n throw ShipError.business(\n `File ${file.name} is too large. Maximum allowed size is ${platformLimits.maxFileSize / (1024 * 1024)}MB.`,\n );\n }\n totalSize += file.size;\n if (totalSize > platformLimits.maxTotalSize) {\n throw ShipError.business(\n `Total deploy size is too large. Maximum allowed is ${platformLimits.maxTotalSize / (1024 * 1024)}MB.`,\n );\n }\n\n // Calculate MD5 hash\n const { md5 } = await calculateMD5(file);\n\n results.push({\n path: deployPath,\n content: file,\n size: file.size,\n md5,\n });\n }\n\n // Validate file count (matches Node validation)\n if (results.length > platformLimits.maxFilesCount) {\n throw ShipError.business(\n `Too many files to deploy. Maximum allowed is ${platformLimits.maxFilesCount} files.`,\n );\n }\n\n return results;\n}\n","/**\n * @file Ship SDK for browser environments.\n *\n * Configuration is fully explicit — the browser has no env vars or config files\n * to inherit. The credential is supplied via the `token` constructor option\n * (or, for first-party browser apps, the cookie session via `session: true`).\n */\n\nimport { ShipError } from '@shipstatic/types';\nimport { Ship as BaseShip } from '../shared/base-ship.js';\nimport type {\n DeployBodyCreator,\n DeployInput,\n Deployment,\n DeploymentOptions,\n StaticFile,\n} from '../shared/types.js';\nimport { createDeployBody } from './core/deploy-body.js';\n\n// Export all shared functionality\nexport * from '../shared/index.js';\n\n/**\n * Ship SDK Client for browser environments.\n *\n * @example\n * ```typescript\n * // Deploy with a token obtained from your server\n * const ship = new Ship({\n * token: 'deploy-xxxx',\n * apiUrl: 'https://api.shipstatic.com',\n * });\n *\n * const files = Array.from(fileInput.files);\n * await ship.deploy(files);\n * ```\n */\nexport class Ship extends BaseShip {\n // No constructor override — the base class accepts `ShipClientOptions` and\n // browsers have no ambient credential source (no env vars, no filesystem).\n\n /**\n * Deploy `File[]` (typically from `<input type=\"file\">` or drag-and-drop)\n * to ShipStatic. Convenience shortcut for `ship.deployments.upload()`.\n *\n * Wrong-platform inputs (e.g. string paths) fail at compile time. For\n * platform-neutral code, use `ship.deployments.upload()`, which accepts\n * the wider `DeployInput` and validates at runtime — that asymmetry is\n * intentional: the convenience shortcut narrows; the resource-layer\n * contract stays platform-neutral.\n */\n async deploy(input: File[], options?: DeploymentOptions): Promise<Deployment> {\n return super.deploy(input, options);\n }\n\n protected async processInput(\n input: DeployInput,\n options: DeploymentOptions,\n ): Promise<StaticFile[]> {\n if (!Array.isArray(input) || !input.every((item) => item instanceof File)) {\n throw ShipError.business('Invalid input type for browser environment. Expected File[].');\n }\n\n if (input.length === 0) {\n throw ShipError.business('No files to deploy.');\n }\n\n const { processFilesForBrowser } = await import('./core/browser-files.js');\n return processFilesForBrowser(input, options, this.platformLimits ?? undefined);\n }\n\n protected getDeployBodyCreator(): DeployBodyCreator {\n return createDeployBody;\n }\n}\n\n// Default export (for `import Ship from '@shipstatic/ship'`)\nexport default Ship;\n\n// Browser-only utilities (validation + MD5 over `File` / `Blob` inputs)\nexport { processFilesForBrowser } from './core/browser-files.js';\n","/**\n * @file Base Ship SDK class — shared functionality across environments.\n *\n * The constructor is fully synchronous: an `ApiHttp` instance is built immediately\n * with whatever credentials the caller supplied (and, in Node, env vars merged in\n * by the subclass before `super()`). The only deferred work is the one-shot\n * `GET /limits` fetch that hydrates platform limits — that's lazy and runs on\n * first API call via `ensureInitialized()`.\n *\n * Subclasses only override what genuinely differs per environment:\n * - `processInput()` — Node reads paths from disk; Browser handles `File[]`\n * - `getDeployBodyCreator()` — Node streams Buffers; Browser builds Blobs\n *\n * Everything else (the credential slot, resources, events, lazy platform-limits)\n * lives here.\n */\n\nimport type {\n AccountResource,\n Deployment,\n DeploymentResource,\n DomainResource,\n PlatformLimits,\n StaticFile,\n TokenResource,\n} from '@shipstatic/types';\nimport { ShipError, validateCaller, validateToken } from '@shipstatic/types';\n\nimport { ApiHttp } from './api/http.js';\nimport {\n createAccountResource,\n createDeploymentResource,\n createDomainResource,\n createTokenResource,\n type DeployInput,\n} from './resources.js';\nimport type {\n DeployBodyCreator,\n DeploymentOptions,\n ShipClientOptions,\n ShipEvents,\n TokenProvider,\n} from './types.js';\n\n/**\n * Abstract base class for Ship SDK implementations.\n */\nexport abstract class Ship {\n // Resource handles, created once at construction. Each is a thin facade\n // bound to `this.http` plus the lazy-init callback.\n public readonly deployments: DeploymentResource;\n public readonly domains: DomainResource;\n public readonly account: AccountResource;\n public readonly tokens: TokenResource;\n\n // The HTTP client and merged options are private — subclasses interact\n // with the base class through the abstract methods below, never by\n // reaching into these fields. Tests bypass via `(ship as any).http = ...`.\n private readonly http: ApiHttp;\n private readonly clientOptions: ShipClientOptions;\n\n // Lazy-init plumbing for the one-shot `GET /config` fetch.\n // `platformLimits` is INSTANCE state (not a module-level singleton): two\n // Ships against different `apiUrl`s — staging + prod, multi-tenant\n // orchestrators, n8n with multiple credentials — must not clobber each\n // other's limits. Each instance owns its hydrated copy.\n // `protected` so subclasses' `processInput` can pass it down to the\n // platform-specific file-validation utilities.\n private initPromise: Promise<void> | null = null;\n protected platformLimits: PlatformLimits | null = null;\n\n // The credential slot — one platform token (any population) or a provider\n // that supplies one per request. Read dynamically on every request through\n // `getAuthHeaders`, so `setToken` takes effect without rebuilding the client.\n private credential: string | TokenProvider | null = null;\n\n constructor(options: ShipClientOptions = {}) {\n // SDK-boundary normalization: an empty-string token is absence of\n // credential intent, never a credential. Empty strings reach here from\n // shell-expansion of unset CI variables, empty form fields in browser\n // apps, and any other path that produces `''` instead of `undefined`.\n // Normalizing once at the SDK boundary covers every entry point: CLI,\n // Browser SDK, Node SDK, embedded consumers, and direct base-class use.\n options = {\n ...options,\n apiUrl: options.apiUrl || undefined,\n token: options.token || undefined,\n caller: options.caller || undefined,\n };\n this.clientOptions = options;\n\n // Caller identity is validated at the boundary like the token: a value\n // the API would silently drop (the header is unauthenticated) is a\n // configuration error here, never a quiet fallback to IP bucketing.\n if (options.caller !== undefined) {\n validateCaller(options.caller);\n }\n\n // One client, one identity. A token and a cookie session are different\n // principals — holding both is a configuration error, not a precedence\n // question.\n if (options.token && options.session) {\n throw ShipError.config('Provide either `token` or `session`, not both.');\n }\n\n // Static tokens are validated at the boundary (prefix-classified, same\n // rules the server applies); providers are invoked per request instead.\n if (typeof options.token === 'string') {\n validateToken(options.token);\n this.credential = options.token;\n } else if (options.token) {\n this.credential = options.token;\n }\n\n // Build the HTTP client once. The `getAuthHeaders` callback reads\n // `this.credential` dynamically on every request.\n this.http = new ApiHttp({\n ...options,\n getAuthHeaders: () => this.getAuthHeaders(),\n createDeployBody: this.getDeployBodyCreator(),\n });\n\n const ctx = {\n getApi: () => this.http,\n ensureInit: () => this.ensureInitialized(),\n };\n\n this.deployments = createDeploymentResource({\n ...ctx,\n processInput: (input, opts) => this.processInput(input, opts),\n clientDefaults: this.clientOptions,\n });\n this.domains = createDomainResource(ctx);\n this.account = createAccountResource(ctx);\n this.tokens = createTokenResource(ctx);\n }\n\n // Environment-specific behavior.\n protected abstract processInput(\n input: DeployInput,\n options: DeploymentOptions,\n ): Promise<StaticFile[]>;\n protected abstract getDeployBodyCreator(): DeployBodyCreator;\n\n /**\n * Lazy initialization — fetches platform limits (file size / count caps) once,\n * on the first API call. Subsequent calls reuse the resolved promise.\n */\n protected async ensureInitialized(): Promise<void> {\n if (!this.initPromise) {\n this.initPromise = this.fetchPlatformLimits();\n }\n return this.initPromise;\n }\n\n private async fetchPlatformLimits(): Promise<void> {\n try {\n this.platformLimits = await this.http.getLimits();\n } catch (error) {\n // Reset so the next API call can retry initialization.\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 await this.ensureInitialized();\n return this.http.ping();\n }\n\n /**\n * Deploy project (convenience shortcut to `ship.deployments.upload()`).\n */\n async deploy(input: DeployInput, options?: DeploymentOptions): Promise<Deployment> {\n return this.deployments.upload(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 platform limits (max file size, file count, total size).\n * Reuses the response fetched during initialization. Per-instance state —\n * does not leak between concurrent Ships against different API URLs.\n */\n async getLimits(): Promise<PlatformLimits> {\n if (this.platformLimits) return this.platformLimits;\n await this.ensureInitialized();\n // biome-ignore lint/style/noNonNullAssertion: ensureInitialized() hydrates platformLimits or throws\n return this.platformLimits!;\n }\n\n on<K extends keyof ShipEvents>(event: K, handler: (...args: ShipEvents[K]) => void): void {\n this.http.on(event, handler);\n }\n\n off<K extends keyof ShipEvents>(event: K, handler: (...args: ShipEvents[K]) => void): void {\n this.http.off(event, handler);\n }\n\n /**\n * Set global headers included in every request.\n * Useful for injecting custom headers (e.g. for admin impersonation).\n */\n setHeaders(headers: Record<string, string>): void {\n this.http.setGlobalHeaders(headers);\n }\n\n /**\n * Clear all custom global headers.\n */\n clearHeaders(): void {\n this.http.setGlobalHeaders({});\n }\n\n /**\n * Sets the client token — any platform token (API key, deploy token, OAuth\n * access token) or a {@link TokenProvider} invoked per request. Replaces\n * whatever credential the client held before.\n * @param token A platform token, sent verbatim, or a provider function\n */\n public setToken(token: string | TokenProvider): void {\n // One client, one identity — the constructor's token/session exclusion\n // holds for the client's whole life, not just its first moment.\n if (this.clientOptions.session) {\n throw ShipError.config('Provide either `token` or `session`, not both.');\n }\n if (typeof token === 'string') {\n if (!token) {\n throw ShipError.business('Invalid token provided. Token must be a non-empty string.');\n }\n validateToken(token);\n this.credential = token;\n return;\n }\n if (typeof token !== 'function') {\n throw ShipError.business(\n 'Invalid token provided. Token must be a non-empty string or a provider function.',\n );\n }\n this.credential = token;\n }\n\n /**\n * Resolve the credential slot into request headers. Async because a\n * provider may mint or refresh its token per request.\n *\n * Anonymity requires proven absence of credentials: a configured provider\n * that yields nothing is an error — the request fails typed rather than\n * silently proceeding as an anonymous public deploy. Empty-string\n * normalization at the constructor is the same invariant's boundary\n * condition: `''` is absence of intent, so it never reaches this point.\n */\n private async getAuthHeaders(): Promise<Record<string, string>> {\n if (this.credential === null) return {};\n const value = typeof this.credential === 'function' ? await this.credential() : this.credential;\n if (!value) {\n throw ShipError.authentication('Token provider returned no token.');\n }\n if (typeof value !== 'string') {\n throw ShipError.authentication('Token provider returned a non-string value.');\n }\n return { Authorization: `Bearer ${value}` };\n }\n}\n","/**\n * @file HTTP client for Ship API.\n */\nimport type {\n AccountGetResponse,\n Deployment,\n DeploymentCreateResponse,\n DeploymentListResponse,\n Domain,\n DomainDnsResponse,\n DomainListResponse,\n DomainRecordsResponse,\n DomainValidateResponse,\n PingResponse,\n PlatformLimits,\n SPACheckRequest,\n SPACheckResponse,\n StaticFile,\n TokenCreateResponse,\n TokenListResponse,\n} from '@shipstatic/types';\nimport { DEFAULT_API, ShipError } from '@shipstatic/types';\nimport { SimpleEvents } from '../events.js';\nimport { validateLabels, validatePassword } from '../lib/validation.js';\nimport type {\n ApiDeployOptions,\n DeployBodyCreator,\n DomainSetResult,\n Fetch,\n ShipClientOptions,\n} from '../types.js';\n\n// =============================================================================\n// CONSTANTS\n// =============================================================================\n\nconst ENDPOINTS = {\n DEPLOYMENTS: '/deployments',\n DOMAINS: '/domains',\n TOKENS: '/tokens',\n ACCOUNT: '/account',\n LIMITS: '/limits',\n PING: '/ping',\n SPA_CHECK: '/spa-check',\n} as const;\n\nconst DEFAULT_REQUEST_TIMEOUT = 30000;\n\n// =============================================================================\n// TYPES\n// =============================================================================\n\nexport interface ApiHttpOptions extends ShipClientOptions {\n /** Resolves the credential slot per request — async so token providers can mint/refresh. */\n getAuthHeaders: () => Record<string, string> | Promise<Record<string, string>>;\n createDeployBody: DeployBodyCreator;\n}\n\ninterface RequestResult<T> {\n data: T;\n status: number;\n}\n\n// =============================================================================\n// HTTP CLIENT\n// =============================================================================\n\nexport class ApiHttp extends SimpleEvents {\n private readonly apiUrl: string;\n private readonly getAuthHeadersCallback: () =>\n | Record<string, string>\n | Promise<Record<string, string>>;\n private readonly session: boolean;\n private readonly caller: string | undefined;\n private readonly timeout: number;\n private readonly fetch: Fetch;\n private readonly createDeployBody: DeployBodyCreator;\n private readonly deployEndpoint: string;\n private globalHeaders: Record<string, string> = {};\n\n constructor(options: ApiHttpOptions) {\n super();\n this.apiUrl = options.apiUrl || DEFAULT_API;\n this.getAuthHeadersCallback = options.getAuthHeaders;\n this.session = options.session ?? false;\n this.caller = options.caller;\n this.timeout = options.timeout ?? DEFAULT_REQUEST_TIMEOUT;\n // Bind to globalThis when falling back to the platform `fetch` — browsers\n // require `this === window` on `window.fetch` and throw \"Illegal invocation\"\n // when it's invoked as a property of any other object.\n this.fetch = options.fetch ?? globalThis.fetch.bind(globalThis);\n this.createDeployBody = options.createDeployBody;\n this.deployEndpoint = options.deployEndpoint || ENDPOINTS.DEPLOYMENTS;\n }\n\n /**\n * Set global headers included in every request.\n * Priority: globalHeaders (lowest) < instance auth < per-request headers (highest)\n */\n setGlobalHeaders(headers: Record<string, string>): void {\n this.globalHeaders = headers;\n }\n\n // ===========================================================================\n // CORE REQUEST INFRASTRUCTURE\n // ===========================================================================\n\n /**\n * Execute HTTP request with timeout, events, and error handling\n */\n private async executeRequest<T>(\n url: string,\n options: RequestInit,\n operationName: string,\n ): Promise<RequestResult<T>> {\n let cleanup = () => {};\n\n try {\n // Credential resolution runs inside the error boundary: a token\n // provider that throws or yields nothing fails the request through\n // the same typed path (and `error` event) as any transport failure.\n const headers = await this.mergeHeaders(options.headers as Record<string, string>);\n const timeout = this.createTimeoutSignal(options.signal);\n cleanup = timeout.cleanup;\n\n const fetchOptions: RequestInit = {\n ...options,\n headers,\n credentials: this.session && !headers.Authorization ? 'include' : undefined,\n signal: timeout.signal,\n };\n\n this.emit('request', url, fetchOptions);\n\n const response = await this.fetch(url, fetchOptions);\n cleanup();\n\n if (!response.ok) {\n throw await ShipError.fromHttpResponse(response, operationName);\n }\n\n this.emit('response', this.safeClone(response), url);\n const data = await this.parseResponse<T>(this.safeClone(response));\n return { data, status: response.status };\n } catch (error) {\n cleanup();\n // Normalize anything thrown above (credential resolution, fetch\n // failure, abort, response error) into a ShipError.\n // fromFetchError passes existing ShipErrors through unchanged.\n const shipError = ShipError.fromFetchError(error, operationName);\n this.emit('error', shipError, url);\n throw shipError;\n }\n }\n\n /**\n * Simple request - returns data only\n */\n private async request<T>(url: string, options: RequestInit, operationName: string): Promise<T> {\n const { data } = await this.executeRequest<T>(url, options, operationName);\n return data;\n }\n\n /**\n * Request with status - returns data and HTTP status code\n */\n private async requestWithStatus<T>(\n url: string,\n options: RequestInit,\n operationName: string,\n ): Promise<RequestResult<T>> {\n return this.executeRequest<T>(url, options, operationName);\n }\n\n // ===========================================================================\n // REQUEST HELPERS\n // ===========================================================================\n\n private async mergeHeaders(\n customHeaders: Record<string, string> = {},\n ): Promise<Record<string, string>> {\n // `caller` is instance identity metadata, like the credential: the\n // rate limiter buckets by X-Caller on every write, so it rides every\n // request rather than any single operation.\n return {\n ...this.globalHeaders,\n ...(this.caller ? { 'X-Caller': this.caller } : {}),\n ...(await this.getAuthHeadersCallback()),\n ...customHeaders,\n };\n }\n\n private createTimeoutSignal(existingSignal?: AbortSignal | null): {\n signal: AbortSignal;\n cleanup: () => void;\n } {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), this.timeout);\n\n if (existingSignal) {\n const abort = () => controller.abort();\n existingSignal.addEventListener('abort', abort);\n if (existingSignal.aborted) controller.abort();\n }\n\n return {\n signal: controller.signal,\n cleanup: () => clearTimeout(timeoutId),\n };\n }\n\n private safeClone(response: Response): Response {\n try {\n return response.clone();\n } catch {\n return response;\n }\n }\n\n private async parseResponse<T>(response: Response): Promise<T> {\n if (response.headers.get('Content-Length') === '0' || response.status === 204) {\n return undefined as T;\n }\n return response.json() as Promise<T>;\n }\n\n // ===========================================================================\n // PUBLIC API - DEPLOYMENTS\n // ===========================================================================\n\n async deploy(\n files: StaticFile[],\n options: ApiDeployOptions = {},\n ): Promise<DeploymentCreateResponse> {\n if (!files.length) {\n throw ShipError.business('No files to deploy');\n }\n for (const file of files) {\n if (!file.md5) {\n throw ShipError.file(`MD5 checksum missing for file: ${file.path}`, {\n filePath: file.path,\n });\n }\n }\n\n // Fast-fail on definitely-invalid input before constructing a multipart body.\n validatePassword(options.password);\n const labels = validateLabels(options.labels);\n\n const flags =\n options.build || options.prerender || options.spa\n ? { build: options.build, prerender: options.prerender, spa: options.spa }\n : undefined;\n const { body, headers: bodyHeaders } = await this.createDeployBody(files, {\n labels,\n via: options.via,\n password: options.password,\n flags,\n captcha: options.captcha,\n });\n\n return this.request<DeploymentCreateResponse>(\n `${this.apiUrl}${this.deployEndpoint}`,\n { method: 'POST', body, headers: bodyHeaders, signal: options.signal || null },\n 'Deploy',\n );\n }\n\n async listDeployments(): Promise<DeploymentListResponse> {\n return this.request(\n `${this.apiUrl}${ENDPOINTS.DEPLOYMENTS}`,\n { method: 'GET' },\n 'List deployments',\n );\n }\n\n async getDeployment(id: string): Promise<Deployment> {\n return this.request(\n `${this.apiUrl}${ENDPOINTS.DEPLOYMENTS}/${encodeURIComponent(id)}`,\n { method: 'GET' },\n 'Get deployment',\n );\n }\n\n async updateDeploymentLabels(id: string, labels: string[]): Promise<Deployment> {\n const normalized = validateLabels(labels);\n return this.request(\n `${this.apiUrl}${ENDPOINTS.DEPLOYMENTS}/${encodeURIComponent(id)}`,\n {\n method: 'PATCH',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ labels: normalized }),\n },\n 'Update deployment labels',\n );\n }\n\n async removeDeployment(id: string): Promise<void> {\n await this.request<void>(\n `${this.apiUrl}${ENDPOINTS.DEPLOYMENTS}/${encodeURIComponent(id)}`,\n { method: 'DELETE' },\n 'Remove deployment',\n );\n }\n\n // ===========================================================================\n // PUBLIC API - DOMAINS\n // ===========================================================================\n // All domain methods accept FQDN (Fully Qualified Domain Name) as the `name` parameter.\n // The SDK does not validate or normalize - the API handles all domain semantics.\n\n async setDomain(name: string, deployment?: string, labels?: string[]): Promise<DomainSetResult> {\n const normalized = validateLabels(labels);\n const body: { deployment?: string; labels?: string[] } = {};\n if (deployment) body.deployment = deployment;\n if (normalized !== undefined) body.labels = normalized;\n\n const { data, status } = await this.requestWithStatus<Domain>(\n `${this.apiUrl}${ENDPOINTS.DOMAINS}/${encodeURIComponent(name)}`,\n {\n method: 'PUT',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(body),\n },\n 'Set domain',\n );\n\n return { ...data, isCreate: status === 201 };\n }\n\n async listDomains(): Promise<DomainListResponse> {\n return this.request(`${this.apiUrl}${ENDPOINTS.DOMAINS}`, { method: 'GET' }, 'List domains');\n }\n\n async getDomain(name: string): Promise<Domain> {\n return this.request(\n `${this.apiUrl}${ENDPOINTS.DOMAINS}/${encodeURIComponent(name)}`,\n { method: 'GET' },\n 'Get domain',\n );\n }\n\n async removeDomain(name: string): Promise<void> {\n await this.request<void>(\n `${this.apiUrl}${ENDPOINTS.DOMAINS}/${encodeURIComponent(name)}`,\n { method: 'DELETE' },\n 'Remove domain',\n );\n }\n\n async verifyDomain(name: string): Promise<{ message: string }> {\n return this.request(\n `${this.apiUrl}${ENDPOINTS.DOMAINS}/${encodeURIComponent(name)}/verify`,\n { method: 'POST' },\n 'Verify domain',\n );\n }\n\n async getDomainDns(name: string): Promise<DomainDnsResponse> {\n return this.request(\n `${this.apiUrl}${ENDPOINTS.DOMAINS}/${encodeURIComponent(name)}/dns`,\n { method: 'GET' },\n 'Get domain DNS',\n );\n }\n\n async getDomainRecords(name: string): Promise<DomainRecordsResponse> {\n return this.request(\n `${this.apiUrl}${ENDPOINTS.DOMAINS}/${encodeURIComponent(name)}/records`,\n { method: 'GET' },\n 'Get domain records',\n );\n }\n\n async getDomainShare(name: string): Promise<{ domain: string; hash: string }> {\n return this.request(\n `${this.apiUrl}${ENDPOINTS.DOMAINS}/${encodeURIComponent(name)}/share`,\n { method: 'GET' },\n 'Get domain share',\n );\n }\n\n async validateDomain(name: string): Promise<DomainValidateResponse> {\n return this.request(\n `${this.apiUrl}${ENDPOINTS.DOMAINS}/validate`,\n {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ domain: name }),\n },\n 'Validate domain',\n );\n }\n\n // ===========================================================================\n // PUBLIC API - TOKENS\n // ===========================================================================\n\n async createToken(ttl?: number, labels?: string[]): Promise<TokenCreateResponse> {\n const normalized = validateLabels(labels);\n const body: { ttl?: number; labels?: string[] } = {};\n if (ttl !== undefined) body.ttl = ttl;\n if (normalized !== undefined) body.labels = normalized;\n\n return this.request(\n `${this.apiUrl}${ENDPOINTS.TOKENS}`,\n {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(body),\n },\n 'Create token',\n );\n }\n\n async listTokens(): Promise<TokenListResponse> {\n return this.request(`${this.apiUrl}${ENDPOINTS.TOKENS}`, { method: 'GET' }, 'List tokens');\n }\n\n async removeToken(token: string): Promise<void> {\n await this.request<void>(\n `${this.apiUrl}${ENDPOINTS.TOKENS}/${encodeURIComponent(token)}`,\n { method: 'DELETE' },\n 'Remove token',\n );\n }\n\n // ===========================================================================\n // PUBLIC API - ACCOUNT & CONFIG\n // ===========================================================================\n\n async getAccount(): Promise<AccountGetResponse> {\n return this.request(`${this.apiUrl}${ENDPOINTS.ACCOUNT}`, { method: 'GET' }, 'Get account');\n }\n\n async getLimits(): Promise<PlatformLimits> {\n return this.request(`${this.apiUrl}${ENDPOINTS.LIMITS}`, { method: 'GET' }, 'Get limits');\n }\n\n async ping(): Promise<boolean> {\n const data = await this.request<PingResponse>(\n `${this.apiUrl}${ENDPOINTS.PING}`,\n { method: 'GET' },\n 'Ping',\n );\n return data?.success || false;\n }\n\n // ===========================================================================\n // PUBLIC API - SPA CHECK\n // ===========================================================================\n\n async checkSPA(files: StaticFile[], _options: ApiDeployOptions = {}): Promise<boolean> {\n const indexFile = files.find((f) => f.path === 'index.html' || f.path === '/index.html');\n if (!indexFile || indexFile.size > 100 * 1024) {\n return false;\n }\n\n let indexContent: string;\n if (typeof Buffer !== 'undefined' && Buffer.isBuffer(indexFile.content)) {\n indexContent = indexFile.content.toString('utf-8');\n } else if (typeof Blob !== 'undefined' && indexFile.content instanceof Blob) {\n indexContent = await indexFile.content.text();\n } else if (typeof File !== 'undefined' && indexFile.content instanceof File) {\n indexContent = await indexFile.content.text();\n } else {\n return false;\n }\n\n const body: SPACheckRequest = { files: files.map((f) => f.path), index: indexContent };\n const response = await this.request<SPACheckResponse>(\n `${this.apiUrl}${ENDPOINTS.SPA_CHECK}`,\n {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(body),\n },\n 'SPA check',\n );\n\n return response.isSPA;\n }\n}\n","/**\n * Event system for Ship SDK\n * Lightweight, reliable event handling with proper error boundaries\n */\n\nimport type { ShipEvents } from './types.js';\n\n/**\n * Lightweight typed event emitter.\n *\n * Public API: `on()` / `off()`. `emit()` is internal — only the SDK\n * publishes events. Throwing handlers are evicted automatically and\n * surfaced as `error` events on the next tick.\n */\nexport class SimpleEvents {\n // biome-ignore lint/complexity/noBannedTypes: the registry is heterogeneous by design — per-event signatures are enforced at the on()/emit() boundary\n private handlers = new Map<string, Set<Function>>();\n\n /**\n * Add event handler\n */\n on<K extends keyof ShipEvents>(event: K, handler: (...args: ShipEvents[K]) => void): void {\n if (!this.handlers.has(event as string)) {\n this.handlers.set(event as string, new Set());\n }\n this.handlers.get(event as string)?.add(handler);\n }\n\n /**\n * Remove event handler\n */\n off<K extends keyof ShipEvents>(event: K, handler: (...args: ShipEvents[K]) => void): void {\n const eventHandlers = this.handlers.get(event as string);\n if (eventHandlers) {\n eventHandlers.delete(handler);\n if (eventHandlers.size === 0) {\n this.handlers.delete(event as string);\n }\n }\n }\n\n /**\n * Emit event (internal use only)\n * @internal\n */\n emit<K extends keyof ShipEvents>(event: K, ...args: ShipEvents[K]): void {\n const eventHandlers = this.handlers.get(event as string);\n if (!eventHandlers) return;\n\n // Snapshot handlers so a handler that mutates the set during iteration\n // (e.g. by removing itself) doesn't skip or duplicate calls.\n const handlerArray = Array.from(eventHandlers);\n\n for (const handler of handlerArray) {\n try {\n handler(...args);\n } catch (error) {\n // A throwing handler is treated as broken — drop it so we don't\n // repeatedly invoke it and re-emit the failure as an `error` event\n // for observability. Defer the re-emit so the next tick has a clean\n // call stack and we can't recurse if the error handler also throws.\n eventHandlers.delete(handler);\n\n if (event !== 'error') {\n setTimeout(() => {\n const err = error instanceof Error ? error : new Error(String(error));\n this.emit('error', err, String(event));\n }, 0);\n }\n }\n }\n }\n}\n","/**\n * @file Client-side input validation for SDK request boundaries.\n *\n * These validators run before request construction. Constants come from\n * `@shipstatic/types` (`LABEL_CONSTRAINTS`, `LABEL_PATTERN`) so the SDK and\n * API agree on the rules.\n */\n\nimport { LABEL_CONSTRAINTS, LABEL_PATTERN, ShipError } from '@shipstatic/types';\n\n// Re-export the canonical password validator from `@shipstatic/types` so\n// existing SDK callers (`http.ts`) keep their `from '../lib/validation.js'`\n// import path unchanged. The types-tier definition is the single source of\n// truth — see `@shipstatic/types/CLAUDE.md` \"Validation: format vs policy\".\nexport { validatePassword } from '@shipstatic/types';\n\n/**\n * Validate and normalize an array of labels.\n *\n * Lowercases and trims each entry, enforces per-label length and pattern\n * (`LABEL_CONSTRAINTS` / `LABEL_PATTERN`), count cap, and uniqueness after\n * normalization. Returns the normalized array. An empty array is valid and\n * signals \"clear all labels\" on label-update operations.\n */\nexport function validateLabels(labels: string[]): string[];\nexport function validateLabels(labels: string[] | undefined | null): string[] | undefined;\nexport function validateLabels(labels: string[] | undefined | null): string[] | undefined {\n if (labels === undefined || labels === null) return undefined;\n if (labels.length === 0) return labels;\n\n if (labels.length > LABEL_CONSTRAINTS.MAX_COUNT) {\n throw ShipError.validation(`Maximum ${LABEL_CONSTRAINTS.MAX_COUNT} labels allowed`);\n }\n\n const normalized = labels.map((label, i) => {\n if (typeof label !== 'string') {\n throw ShipError.validation(`Label at index ${i} must be a string`);\n }\n const cleaned = label.trim().toLowerCase();\n if (cleaned.length < LABEL_CONSTRAINTS.MIN_LENGTH) {\n throw ShipError.validation(\n `Labels must be at least ${LABEL_CONSTRAINTS.MIN_LENGTH} characters long`,\n );\n }\n if (cleaned.length > LABEL_CONSTRAINTS.MAX_LENGTH) {\n throw ShipError.validation(\n `Labels must be no more than ${LABEL_CONSTRAINTS.MAX_LENGTH} characters long`,\n );\n }\n if (!LABEL_PATTERN.test(cleaned)) {\n throw ShipError.validation(\n `Labels must start and end with alphanumeric characters, with optional separators (${LABEL_CONSTRAINTS.SEPARATORS}) between segments`,\n );\n }\n return cleaned;\n });\n\n const unique = [...new Set(normalized)];\n if (unique.length !== normalized.length) {\n throw ShipError.validation('Duplicate labels are not allowed');\n }\n\n return unique;\n}\n","/**\n * Ship SDK resource factory functions.\n */\nimport {\n type AccountResource,\n type DeployInput,\n type DeploymentResource,\n type DomainResource,\n ShipError,\n type StaticFile,\n type TokenResource,\n} from '@shipstatic/types';\n\nexport type {\n AccountResource,\n DeployInput,\n DeploymentResource,\n DomainResource,\n StaticFile,\n TokenResource,\n};\n\nimport type { ApiHttp } from './api/http.js';\nimport { mergeDeployOptions } from './core/config.js';\nimport { detectAndConfigureSPA } from './lib/spa.js';\nimport type { DeploymentOptions, ShipClientOptions } from './types.js';\n\n/**\n * Shared context for all resource factories.\n */\nexport interface ResourceContext {\n getApi: () => ApiHttp;\n ensureInit: () => Promise<void>;\n}\n\n/**\n * Extended context for deployment resource.\n */\nexport interface DeploymentResourceContext extends ResourceContext {\n processInput: (input: DeployInput, options: DeploymentOptions) => Promise<StaticFile[]>;\n clientDefaults?: ShipClientOptions;\n}\n\n/**\n * Upload deployment resource with all CRUD operations.\n *\n * There is no client-side auth branching: an upload from a credential-less\n * client simply carries no `Authorization` header, and the API grants the\n * public-account agent identity per request (claim URL + expiry on the\n * response). The SDK stays a transparent pipe either way.\n */\nexport function createDeploymentResource(ctx: DeploymentResourceContext): DeploymentResource {\n const { getApi, ensureInit, processInput, clientDefaults } = ctx;\n\n return {\n upload: async (input: DeployInput, options: DeploymentOptions = {}) => {\n await ensureInit();\n\n const mergedOptions = clientDefaults ? mergeDeployOptions(options, clientDefaults) : options;\n\n if (!processInput) {\n throw ShipError.config('processInput function is not provided.');\n }\n\n const apiClient = getApi();\n let staticFiles = await processInput(input, mergedOptions);\n staticFiles = await detectAndConfigureSPA(staticFiles, apiClient, mergedOptions);\n\n return apiClient.deploy(staticFiles, mergedOptions);\n },\n\n list: async () => {\n await ensureInit();\n return getApi().listDeployments();\n },\n\n get: async (id: string) => {\n await ensureInit();\n return getApi().getDeployment(id);\n },\n\n set: async (id: string, options: { labels: string[] }) => {\n await ensureInit();\n return getApi().updateDeploymentLabels(id, options.labels);\n },\n\n remove: async (id: string) => {\n await ensureInit();\n await getApi().removeDeployment(id);\n },\n };\n}\n\n/**\n * Create domain resource with all CRUD operations.\n *\n * @remarks\n * The `name` parameter in all methods is an FQDN (Fully Qualified Domain Name).\n * The SDK does not validate or normalize domain names - the API handles all domain semantics.\n */\nexport function createDomainResource(ctx: ResourceContext): DomainResource {\n const { getApi, ensureInit } = ctx;\n\n return {\n // INTENTIONAL DESIGN: The API does NOT support unlinking domains (setting deployment to null).\n // Once a domain is linked to a deployment, it must always have a deployment.\n // Supported: reserve (omit deployment), link, switch deployments atomically, delete entirely.\n // Not supported: unlink after linking (creates ambiguous state with no clear use case).\n // See npm/ship/CLAUDE.md \"Domain Write Semantics\" for full rationale.\n set: async (name: string, options: { deployment?: string; labels?: string[] } = {}) => {\n await ensureInit();\n return getApi().setDomain(name, options.deployment, options.labels);\n },\n\n list: async () => {\n await ensureInit();\n return getApi().listDomains();\n },\n\n get: async (name: string) => {\n await ensureInit();\n return getApi().getDomain(name);\n },\n\n remove: async (name: string) => {\n await ensureInit();\n await getApi().removeDomain(name);\n },\n\n verify: async (name: string) => {\n await ensureInit();\n return getApi().verifyDomain(name);\n },\n\n validate: async (name: string) => {\n await ensureInit();\n return getApi().validateDomain(name);\n },\n\n dns: async (name: string) => {\n await ensureInit();\n return getApi().getDomainDns(name);\n },\n\n records: async (name: string) => {\n await ensureInit();\n return getApi().getDomainRecords(name);\n },\n\n share: async (name: string) => {\n await ensureInit();\n return getApi().getDomainShare(name);\n },\n };\n}\n\n/**\n * Create account resource (whoami functionality).\n */\nexport function createAccountResource(ctx: ResourceContext): AccountResource {\n const { getApi, ensureInit } = ctx;\n\n return {\n get: async () => {\n await ensureInit();\n return getApi().getAccount();\n },\n };\n}\n\n/**\n * Create token resource for managing deploy tokens.\n */\nexport function createTokenResource(ctx: ResourceContext): TokenResource {\n const { getApi, ensureInit } = ctx;\n\n return {\n create: async (options: { ttl?: number; labels?: string[] } = {}) => {\n await ensureInit();\n return getApi().createToken(options.ttl, options.labels);\n },\n\n list: async () => {\n await ensureInit();\n return getApi().listTokens();\n },\n\n remove: async (token: string) => {\n await ensureInit();\n await getApi().removeToken(token);\n },\n };\n}\n","/**\n * @file Cross-platform configuration helpers.\n *\n * One pure helper used by the deployment resource:\n *\n * - `mergeDeployOptions(perCallOptions, clientDefaults)` — overlays\n * instance-level defaults under per-call overrides for a single deploy.\n *\n * Deploy options are pure deploy concerns (progress, timeout, concurrency).\n * Credentials, the API URL, and the caller identifier are client identity —\n * they live on the instance, never per call: one client is one principal\n * speaking for one end user against one API. Callers that need a different\n * identity construct another Ship.\n */\n\nimport type { DeploymentOptions, ShipClientOptions } from '../types.js';\n\n/**\n * Overlay client-level defaults under per-call deploy options.\n *\n * Per-call options always win — they're the explicit override for a single\n * `deployments.upload()`. Defaults fill in only when the per-call option is\n * `undefined` (an explicit `null` / empty value passes through).\n */\nexport function mergeDeployOptions(\n options: DeploymentOptions,\n clientDefaults: ShipClientOptions,\n): DeploymentOptions {\n const result: DeploymentOptions = { ...options };\n\n if (result.timeout === undefined && clientDefaults.timeout !== undefined) {\n result.timeout = clientDefaults.timeout;\n }\n if (result.maxConcurrency === undefined && clientDefaults.maxConcurrency !== undefined) {\n result.maxConcurrency = clientDefaults.maxConcurrency;\n }\n if (result.onProgress === undefined && clientDefaults.onProgress !== undefined) {\n result.onProgress = clientDefaults.onProgress;\n }\n\n return result;\n}\n","/**\n * @file SPA detection and auto-configuration utilities.\n *\n * Provides SPA detection and ship.json generation functionality\n * that can be used by both Node.js and browser environments.\n */\n\nimport { DEPLOYMENT_CONFIG_FILENAME, SPA_DEFAULT_CONFIG } from '@shipstatic/types';\nimport type { ApiHttp } from '../api/http.js';\nimport type { DeploymentOptions, StaticFile } from '../types.js';\nimport { calculateMD5 } from './md5.js';\n\n/**\n * Creates ship.json configuration for SPA projects.\n * @returns Promise resolving to StaticFile with SPA configuration\n */\nexport async function createSPAConfig(): Promise<StaticFile> {\n const configString = JSON.stringify(SPA_DEFAULT_CONFIG, null, 2);\n\n // Create content that works in both browser and Node.js environments\n let content: Buffer | Blob;\n if (typeof Buffer !== 'undefined') {\n // Node.js environment\n content = Buffer.from(configString, 'utf-8');\n } else {\n // Browser environment\n content = new Blob([configString], { type: 'application/json' });\n }\n\n const { md5 } = await calculateMD5(content);\n\n return {\n path: DEPLOYMENT_CONFIG_FILENAME,\n content,\n size: configString.length,\n md5,\n };\n}\n\n/**\n * Detects SPA projects and auto-generates configuration.\n * This function can be used by both Node.js and browser environments.\n *\n * @param files - Array of StaticFiles to analyze\n * @param apiClient - HTTP client for API communication\n * @param options - Deployment options containing SPA detection settings\n * @returns Promise resolving to files array with optional SPA config added\n */\nexport async function detectAndConfigureSPA(\n files: StaticFile[],\n apiClient: ApiHttp,\n options: DeploymentOptions,\n): Promise<StaticFile[]> {\n // Skip if disabled, config already exists, or server will handle detection\n if (\n options.spaDetect === false ||\n options.spa ||\n options.build ||\n options.prerender ||\n files.some((f) => f.path === DEPLOYMENT_CONFIG_FILENAME)\n ) {\n return files;\n }\n\n try {\n const isSPA = await apiClient.checkSPA(files, options);\n\n if (isSPA) {\n const spaConfig = await createSPAConfig();\n return [...files, spaConfig];\n }\n } catch (_error) {\n // SPA detection failed, continue silently without auto-config\n }\n\n return files;\n}\n","/**\n * Browser-specific deploy body creation.\n */\nimport { ShipError } from '@shipstatic/types';\nimport type { DeployBody, DeployBodyContext, StaticFile } from '../../shared/types.js';\n\nexport async function createDeployBody(\n files: StaticFile[],\n context: DeployBodyContext = {},\n): Promise<DeployBody> {\n const { labels, via, password, flags, captcha } = context;\n const formData = new FormData();\n const checksums: string[] = [];\n\n for (const file of files) {\n // 1. Validate content type\n if (!(file.content instanceof File || file.content instanceof Blob)) {\n throw ShipError.file(`Unsupported file.content type for browser: ${file.path}`, {\n filePath: file.path,\n });\n }\n\n // 2. Validate md5\n if (!file.md5) {\n throw ShipError.file(`File missing md5 checksum: ${file.path}`, { filePath: file.path });\n }\n\n // 3. Create File and append — API derives Content-Type from extension\n const fileInstance = new File([file.content], file.path, { type: 'application/octet-stream' });\n formData.append('files[]', fileInstance);\n checksums.push(file.md5);\n }\n\n formData.append('checksums', JSON.stringify(checksums));\n\n if (labels && labels.length > 0) formData.append('labels', JSON.stringify(labels));\n if (via) formData.append('via', via);\n if (password) formData.append('password', password);\n if (flags?.build) formData.append('build', 'true');\n if (flags?.prerender) formData.append('prerender', 'true');\n if (flags?.spa) formData.append('spa', 'true');\n if (captcha) formData.append('captcha', captcha);\n\n return { body: formData, headers: {} };\n}\n","/**\n * @file Shared SDK exports - environment agnostic.\n */\n\nexport type { Account, Deployment, Domain, PingResponse } from '@shipstatic/types';\n// Re-export types from @shipstatic/types\nexport { ErrorType, ShipError } from '@shipstatic/types';\nexport * from './api/http.js';\nexport { Ship } from './base-ship.js';\nexport * from './core/config.js';\nexport * from './core/constants.js';\nexport * from './lib/deploy-paths.js';\nexport * from './lib/env.js';\nexport * from './lib/file-validation.js';\nexport * from './lib/junk.js';\n// Shared utilities\nexport * from './lib/md5.js';\nexport * from './lib/security.js';\nexport * from './lib/text.js';\n// Core functionality\nexport * from './resources.js';\nexport * from './types.js';\n","/**\n * @file SDK-specific constants.\n * Platform constants are now in @shipstatic/types.\n */\n\n// Re-export platform constants for convenience\nexport { DEFAULT_API } from '@shipstatic/types';\n","/**\n * Utility functions for string manipulation.\n */\n\n/**\n * Simple utility to pluralize a word based on a count.\n * @param count The number to determine pluralization.\n * @param singular The singular form of the word.\n * @param plural The plural form of the word.\n * @param includeCount Whether to include the count in the returned string. Defaults to true.\n * @returns A string with the count and the correctly pluralized word.\n */\nexport function pluralize(\n count: number,\n singular: string,\n plural: string,\n includeCount: boolean = true,\n): string {\n const word = count === 1 ? singular : plural;\n return includeCount ? `${count} ${word}` : word;\n}\n"],"mappings":"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","names":["isShipError","error","isBlockedExtension","filename","dotIndex","ext","BLOCKED_EXTENSIONS","hasUnsafeChars","UNSAFE_FILENAME_CHARS","hasUnbuiltMarker","filePath","s","UNBUILT_PROJECT_MARKERS","classifyToken","token","API_KEY","TokenKind","DEPLOY_TOKEN","validatePrefixedCredential","value","shape","label","ShipError","hexPart","validateApiKey","apiKey","validateDeployToken","deployToken","validateToken","validateCaller","caller","CALLER","validateApiUrl","apiUrl","url","isDeployment","input","isPlatformDomain","domain","platformDomain","isCustomDomain","extractSubdomain","generateDeploymentUrl","deployment","generateDomainUrl","serializeLabels","labels","deserializeLabels","labelsJson","parsed","validatePassword","trimmed","PASSWORD_CONSTRAINTS","DeploymentStatus","DomainStatus","AccountPlan","ErrorType","CLIENT_ONLY_ERROR_TYPES","ERROR_CATEGORIES","SERVER_PRODUCIBLE_ERROR_TYPES","AuthMethod","OAuthScope","DEPLOYMENT_CONFIG_FILENAME","SPA_DEFAULT_CONFIG","DEFAULT_API","FileValidationStatus","LABEL_CONSTRAINTS","LABEL_PATTERN","init_dist","__esmMin","t","_ShipError","type","message","status","details","__publicField","authDetails","response","operationName","bodyType","json","obj","text","cause","op","resource","id","errorType","require_spark_md5","__commonJSMin","exports","module","factory","glob","undefined","add32","a","b","hex_chr","cmn","q","x","s","t","md5cycle","k","c","d","md5blk","md5blks","i","md5blk_array","md51","n","state","length","tail","tmp","lo","hi","md51_array","rhex","j","hex","y","lsw","msw","clamp","val","from","to","begin","end","num","target","targetArray","sourceArray","toUtf8","str","utf8Str2ArrayBuffer","returnUInt8Array","buff","arr","arrayBuffer2Utf8Str","concatenateArrayBuffers","first","second","result","hexToBinaryString","bytes","SparkMD5","contents","raw","ret","content","hash","md5Blob","blob","SparkMD5","spark","chunkSize","start","end","md5Buffer","buffer","createHash","hash","md5Path","path","createReadStream","resolve","reject","stream","err","ShipError","chunk","calculateMD5","input","init_md5","__esmMin","init_dist","normalizeWebPath","path","init_path","__esmMin","optimizeDeployPaths","filePaths","options","path","normalizeWebPath","extractFileName","commonPrefix","findCommonDirectory","filePath","deployPath","prefixToRemove","pathSegments","commonSegments","minLength","segments","i","segment","init_deploy_paths","__esmMin","init_path","__setTestEnvironment","env","_testEnvironment","detectEnvironment","getENV","init_env","__esmMin","formatFileSize","bytes","decimals","k","sizes","i","validateFileName","filename","hasUnsafeChars","reservedNames","nameWithoutPath","validateFiles","files","config","errors","warnings","fileStatuses","issue","file","hasUnbuiltMarker","f","FileValidationStatus","totalSize","fileStatus","statusMessage","nameValidation","isBlockedExtension","validFiles","canDeploy","getValidFiles","allValidFilesReady","init_file_validation","__esmMin","init_dist","isJunk","filename","junkRegex","ignoreList","init_junk","__esmMin","filterJunk","filePaths","options","p","hasUnbuiltMarker","ShipError","filePath","parts","basename","isJunk","part","directorySegments","segment","JUNK_DIRECTORIES","junkDir","init_junk","__esmMin","init_dist","validateDeployPath","deployPath","sourceIdentifier","ShipError","validateDeployFile","nameCheck","validateFileName","isBlockedExtension","init_security","__esmMin","init_dist","init_file_validation","browser_files_exports","__export","processFilesForBrowser","browserFiles","options","platformLimits","getENV","ShipError","rawPaths","file","isServerProcessed","deployFiles","optimizeDeployPaths","deployPaths","f","filteredSet","filterJunk","validPairs","i","results","deployPath","md5","calculateMD5","totalSize","validateDeployPath","validateDeployFile","init_browser_files","__esmMin","init_dist","init_deploy_paths","init_env","init_junk","init_md5","init_security","init_dist","init_dist","init_dist","SimpleEvents","event","handler","eventHandlers","args","handlerArray","error","err","init_dist","validateLabels","labels","LABEL_CONSTRAINTS","ShipError","normalized","label","i","cleaned","LABEL_PATTERN","unique","ENDPOINTS","DEFAULT_REQUEST_TIMEOUT","ApiHttp","SimpleEvents","options","DEFAULT_API","headers","url","operationName","cleanup","timeout","fetchOptions","response","ShipError","error","shipError","data","customHeaders","existingSignal","controller","timeoutId","abort","files","file","validatePassword","labels","validateLabels","flags","body","bodyHeaders","id","normalized","name","deployment","status","ttl","token","_options","indexFile","f","indexContent","init_dist","mergeDeployOptions","options","clientDefaults","result","init_dist","init_md5","createSPAConfig","configString","SPA_DEFAULT_CONFIG","content","md5","calculateMD5","DEPLOYMENT_CONFIG_FILENAME","detectAndConfigureSPA","files","apiClient","options","f","spaConfig","createDeploymentResource","ctx","getApi","ensureInit","processInput","clientDefaults","input","options","mergedOptions","mergeDeployOptions","ShipError","apiClient","staticFiles","detectAndConfigureSPA","id","createDomainResource","name","createAccountResource","createTokenResource","token","Ship","options","validateCaller","ShipError","validateToken","ApiHttp","ctx","createDeploymentResource","input","opts","createDomainResource","createAccountResource","createTokenResource","error","event","handler","headers","token","value","init_dist","createDeployBody","files","context","labels","via","password","flags","captcha","formData","checksums","file","ShipError","fileInstance","init_dist","init_dist","init_deploy_paths","init_env","init_file_validation","init_junk","init_md5","init_security","pluralize","count","singular","plural","includeCount","word","init_browser_files","Ship","input","options","item","ShipError","processFilesForBrowser","createDeployBody","browser_default"]}
1
+ {"version":3,"sources":["../node_modules/.pnpm/@shipstatic+types@2.5.0-beta.1/node_modules/@shipstatic/types/dist/index.js","../node_modules/.pnpm/spark-md5@3.0.2/node_modules/spark-md5/spark-md5.js","../build-shims/empty.cjs","../src/shared/lib/md5.ts","../src/shared/lib/path.ts","../src/shared/lib/deploy-paths.ts","../src/shared/lib/env.ts","../src/shared/lib/file-validation.ts","../node_modules/.pnpm/junk@4.0.1/node_modules/junk/index.js","../src/shared/lib/junk.ts","../src/shared/lib/security.ts","../src/browser/core/browser-files.ts","../src/browser/index.ts","../src/shared/base-ship.ts","../src/shared/api/http.ts","../src/shared/events.ts","../src/shared/lib/validation.ts","../src/shared/resources.ts","../src/shared/lib/spa.ts","../src/browser/core/deploy-body.ts","../src/shared/index.ts","../src/shared/core/constants.ts","../src/shared/lib/text.ts"],"sourcesContent":["/**\n * @file Shared TypeScript types, constants, and utilities for the ShipStatic platform.\n * This package is the single source of truth for all shared data structures.\n */\n// =============================================================================\n// I. CORE ENTITIES\n// =============================================================================\n/**\n * Deployment status constants\n */\nexport const DeploymentStatus = {\n PENDING: 'pending',\n SUCCESS: 'success',\n FAILED: 'failed',\n DELETING: 'deleting',\n};\n// =============================================================================\n// DOMAIN TYPES\n// =============================================================================\n/**\n * Domain status constants\n *\n * - PENDING: DNS not configured\n * - PARTIAL: DNS partially configured\n * - SUCCESS: DNS fully verified\n * - PAUSED: Domain paused due to plan enforcement (billing)\n */\nexport const DomainStatus = {\n PENDING: 'pending',\n PARTIAL: 'partial',\n SUCCESS: 'success',\n PAUSED: 'paused',\n};\n// =============================================================================\n// ACCOUNT TYPES\n// =============================================================================\n/**\n * Account plan constants\n */\nexport const AccountPlan = {\n FREE: 'free',\n STANDARD: 'standard',\n SPONSORED: 'sponsored',\n ENTERPRISE: 'enterprise',\n SUSPENDED: 'suspended',\n TERMINATING: 'terminating',\n TERMINATED: 'terminated',\n};\n// =============================================================================\n// ERROR SYSTEM\n// =============================================================================\n/**\n * All possible error types in the ShipStatic platform.\n *\n * Developer-friendly key names map to stable wire-format string values.\n * Both the value and the type are exported under the same name so callers\n * can use `ErrorType.Validation` (value comparison) and `: ErrorType` (type\n * annotation) without ceremony — matching the pattern other status objects\n * (`DeploymentStatus`, `DomainStatus`, `AccountPlan`, `AuthMethod`) follow.\n */\nexport const ErrorType = {\n /** Validation failed (400). Input shape is wrong. */\n Validation: 'validation_failed',\n /** Resource not found (404). */\n NotFound: 'not_found',\n /** Authenticated but not allowed (403). User lacks permission for this action. */\n Forbidden: 'forbidden',\n /** Rate limit exceeded (429). */\n RateLimit: 'rate_limit_exceeded',\n /** Authentication required or failed (401). Missing/invalid credentials. */\n Authentication: 'authentication_failed',\n /** Business rule violation. Catch-all for 4xx state-rule errors that aren't more specific. */\n Business: 'business_logic_error',\n /** API server error (500). Generic server-side fault. */\n Api: 'internal_server_error',\n /** Network/connection error. Client-side only — set by HTTP clients on fetch failure; never produced server-side. */\n Network: 'network_error',\n /** Operation was cancelled. Client-side only — set on `AbortSignal` abort; never produced server-side. */\n Cancelled: 'operation_cancelled',\n /** File operation error. Client-side only — set by SDK during local file processing; never produced server-side. */\n File: 'file_error',\n /** Configuration error. Client-side only — set by SDK during config parsing/validation; never produced server-side. */\n Config: 'config_error',\n};\n/**\n * Error types that originate exclusively on the client (HTTP clients, SDK\n * file processing, local config parsing). These never appear on the wire\n * from the server, so `fromHttpResponse` will not trust them even if a\n * misbehaving server claims one in `body.error`.\n */\nconst CLIENT_ONLY_ERROR_TYPES = new Set([\n ErrorType.Network,\n ErrorType.Cancelled,\n ErrorType.File,\n ErrorType.Config,\n]);\n/**\n * Categorizes error types for the `isClientError` / `isNetworkError` /\n * `isAuthError` helpers. Each `Set` is typed against the wider `ErrorType`\n * union so `.has(error.type)` accepts any value from the union.\n */\nconst ERROR_CATEGORIES = {\n client: new Set([\n ErrorType.Business,\n ErrorType.Config,\n ErrorType.File,\n ErrorType.Forbidden,\n ErrorType.Validation,\n ]),\n network: new Set([ErrorType.Network]),\n auth: new Set([ErrorType.Authentication]),\n};\n/**\n * Error types the server can legitimately produce on the wire. Used by\n * `ShipError.fromHttpResponse` to validate the body's `error` field before\n * trusting it as `ShipError.type`. Derived by exclusion from\n * `CLIENT_ONLY_ERROR_TYPES` so adding a new server-producible type to\n * `ErrorType` is automatically picked up.\n */\nconst SERVER_PRODUCIBLE_ERROR_TYPES = new Set(Object.values(ErrorType).filter((t) => !CLIENT_ONLY_ERROR_TYPES.has(t)));\n/**\n * Simple unified error class for both API and SDK\n */\nexport class ShipError extends Error {\n type;\n status;\n details;\n constructor(type, message, status, details) {\n super(message);\n this.type = type;\n this.status = status;\n this.details = details;\n this.name = 'ShipError';\n }\n /** Convert to wire format */\n toResponse() {\n // Strip authentication details when they carry an `internal` telemetry\n // tag (see `ShipError.authentication` JSDoc) — these are server-side\n // diagnostics like 'session_invalid' that must not leak to clients.\n const authDetails = this.details;\n const details = this.type === ErrorType.Authentication && authDetails?.internal ? undefined : this.details;\n return {\n error: this.type,\n message: this.message,\n status: this.status,\n details,\n };\n }\n /**\n * Construct a `ShipError` from an HTTP error response.\n *\n * Best-effort body parse for `{ message, error?, details? }`. Message\n * resolution: `body.message` → `body.error` → `\"<operationName> failed with\n * status <N>\"`.\n *\n * Type resolution: trusts `body.error` when it's a known server-producible\n * `ErrorType` (preserves the wire's intent — server's\n * `ShipError.validation(...)` round-trips back to `ErrorType.Validation`\n * on the client). Falls back to status-derived (401 → Authentication,\n * 403 → Forbidden, 429 → RateLimit, else → Api) for non-API responses\n * (CDN errors, intermediaries) or malformed bodies. Client-only types\n * (`Network`, `Cancelled`, `File`, `Config`) are filtered out of the\n * trusted set — a misbehaving server claiming one of those is ignored.\n *\n * `operationName` (e.g. `\"Get account\"`) is used to compose the fallback\n * message. Defaults to `\"Request\"`. Same convention as `fromFetchError`.\n *\n * Async because it reads the response body. Returns rather than throws so\n * callers can compose; most will `throw await ShipError.fromHttpResponse(...)`.\n */\n static async fromHttpResponse(response, operationName) {\n let message;\n let details;\n let bodyType;\n try {\n const contentType = response.headers.get('content-type');\n if (contentType?.includes('application/json')) {\n const json = await response.json();\n if (json && typeof json === 'object') {\n const obj = json;\n if (typeof obj.message === 'string')\n message = obj.message;\n else if (typeof obj.error === 'string')\n message = obj.error;\n details = obj.details;\n if (typeof obj.error === 'string' && SERVER_PRODUCIBLE_ERROR_TYPES.has(obj.error)) {\n bodyType = obj.error;\n }\n }\n }\n else {\n const text = await response.text();\n if (text)\n message = text;\n }\n }\n catch {\n // Body unreadable; fall through to operationName-derived message.\n }\n // Rate-limit (and 503) timing rides the `Retry-After` HEADER, which a\n // body-only reader would drop. Lift it into `details` as seconds so\n // consumers can back off from the typed error alone, without keeping the\n // raw Response around. Body-carried fields are preserved and win.\n const retryAfterHeader = response.headers.get('retry-after');\n if (retryAfterHeader !== null) {\n const value = retryAfterHeader.trim();\n const seconds = /^\\d+$/.test(value)\n ? Number(value)\n : Math.ceil((Date.parse(value) - Date.now()) / 1000);\n if (Number.isFinite(seconds) && seconds >= 0) {\n const existing = details && typeof details === 'object' ? details : {};\n if (existing.retryAfter === undefined) {\n details = { ...existing, retryAfter: seconds };\n }\n }\n }\n message = message || `${operationName || 'Request'} failed with status ${response.status}`;\n const type = bodyType ??\n (response.status === 401\n ? ErrorType.Authentication\n : response.status === 403\n ? ErrorType.Forbidden\n : response.status === 429\n ? ErrorType.RateLimit\n : ErrorType.Api);\n return new ShipError(type, message, response.status, details);\n }\n /**\n * Construct a `ShipError` from an error caught around a `fetch()` call.\n *\n * The mirror of `fromHttpResponse` for the *other* side of the HTTP error\n * story — the network layer failing (offline, CORS, abort) rather than the\n * server returning a non-OK response.\n *\n * Routing:\n * - Already a `ShipError` → returned as-is (caller's intent preserved)\n * - `AbortError` → `ShipError.cancelled(...)`\n * - `TypeError` whose message mentions \"fetch\" → `ShipError.network(...)`\n * - Any other `Error` → `ShipError(Api, ...)` (no HTTP status — fetch never reached the server)\n * - Anything else (string, undefined, etc.) → `ShipError(Api, ...)`\n *\n * The optional `operationName` is composed into the message for context:\n * `\"Get account was cancelled\"`, `\"Get account failed: ...\"`. Defaults to\n * `\"Request\"` when omitted.\n */\n static fromFetchError(cause, operationName) {\n if (isShipError(cause))\n return cause;\n const op = operationName || 'Request';\n if (cause instanceof Error) {\n if (cause.name === 'AbortError') {\n return ShipError.cancelled(`${op} was cancelled`);\n }\n if (cause instanceof TypeError && cause.message.includes('fetch')) {\n return ShipError.network(`${op} failed: ${cause.message}`, { cause });\n }\n return new ShipError(ErrorType.Api, `${op} failed: ${cause.message}`);\n }\n return new ShipError(ErrorType.Api, `${op} failed: Unknown error`);\n }\n // Factory methods. Uniform shape `(message, details?)` with two principled\n // exceptions: `notFound` composes its message from (resource, id?), and\n // `business` / `api` accept an optional status because they're the\n // multi-status fallbacks.\n static validation(message, details) {\n return new ShipError(ErrorType.Validation, message, 400, details);\n }\n static notFound(resource, id) {\n const message = id ? `${resource} ${id} not found` : `${resource} not found`;\n return new ShipError(ErrorType.NotFound, message, 404);\n }\n static forbidden(message, details) {\n return new ShipError(ErrorType.Forbidden, message, 403, details);\n }\n static rateLimit(message = 'Too many requests', details) {\n return new ShipError(ErrorType.RateLimit, message, 429, details);\n }\n /**\n * Construct an Authentication (401) error.\n *\n * **Telemetry pattern — `details: { internal: '<tag>' }`.** When the\n * server creates an auth error with an `internal` key in `details`\n * (e.g. `{ internal: 'session_invalid' }`), `toResponse()` strips the\n * entire `details` object before serialization. This keeps the wire\n * response a clean \"Authentication failed\" while preserving granular\n * server-side telemetry (which strategy/check failed) for logs and tests.\n *\n * Use this pattern in API auth code; do not put client-visible info under\n * `internal`. Other `details` keys round-trip normally.\n */\n static authentication(message = 'Authentication required', details) {\n return new ShipError(ErrorType.Authentication, message, 401, details);\n }\n static business(message, status = 400, details) {\n return new ShipError(ErrorType.Business, message, status, details);\n }\n static network(message, details) {\n return new ShipError(ErrorType.Network, message, undefined, details);\n }\n static cancelled(message, details) {\n return new ShipError(ErrorType.Cancelled, message, undefined, details);\n }\n static file(message, details) {\n return new ShipError(ErrorType.File, message, undefined, details);\n }\n static config(message, details) {\n return new ShipError(ErrorType.Config, message, undefined, details);\n }\n static api(message, status = 500, details) {\n return new ShipError(ErrorType.Api, message, status, details);\n }\n // Semantic-category type guards. For specific-type checks, use\n // `error.type === ErrorType.X` directly or the generic `isType(t)`.\n isClientError() {\n return ERROR_CATEGORIES.client.has(this.type);\n }\n isNetworkError() {\n return ERROR_CATEGORIES.network.has(this.type);\n }\n isAuthError() {\n return ERROR_CATEGORIES.auth.has(this.type);\n }\n isType(errorType) {\n return this.type === errorType;\n }\n}\n/**\n * Type guard to check if an unknown value is a ShipError.\n *\n * Uses structural checking instead of instanceof to handle module duplication\n * in bundled applications where multiple copies of the ShipError class may exist.\n *\n * @example\n * if (isShipError(error)) {\n * console.log(error.status, error.message);\n * }\n */\nexport function isShipError(error) {\n return (error !== null &&\n typeof error === 'object' &&\n 'name' in error &&\n error.name === 'ShipError' &&\n 'status' in error);\n}\n// =============================================================================\n// EXTENSION BLOCKLIST\n// =============================================================================\n/**\n * Blocked file extensions — files that cannot be uploaded.\n *\n * We accept any file type by default and derive Content-Type from the\n * extension at serve time (via mime-db in the API worker). Unknown extensions\n * are served as `application/octet-stream` with `X-Content-Type-Options: nosniff`.\n *\n * The blocklist targets file types that pose direct security risks when hosted:\n * executables, disk images, malware vectors, dangerous scripts, and shortcuts.\n */\nexport const BLOCKED_EXTENSIONS = new Set([\n // Executables\n 'exe',\n 'msi',\n 'dll',\n 'scr',\n 'bat',\n 'cmd',\n 'com',\n 'pif',\n 'app',\n 'deb',\n 'rpm',\n // Installers\n 'pkg',\n 'mpkg',\n // Disk images\n 'dmg',\n 'iso',\n 'img',\n // Malware vectors\n 'cab',\n 'cpl',\n 'chm',\n // Dangerous scripts\n 'ps1',\n 'vbs',\n 'vbe',\n 'ws',\n 'wsf',\n 'wsc',\n 'wsh',\n 'reg',\n // Java\n 'jar',\n 'jnlp',\n // Mobile/browser packages\n 'apk',\n 'crx',\n // Shortcut/link\n 'lnk',\n 'inf',\n 'hta',\n]);\n/**\n * Check if a filename has a blocked extension.\n * Extracts the extension from the filename and checks against the blocklist.\n * Case-insensitive. Returns false for files without extensions.\n *\n * @example\n * isBlockedExtension('virus.exe') // true\n * isBlockedExtension('app.dmg') // true\n * isBlockedExtension('style.css') // false\n * isBlockedExtension('data.custom') // false\n * isBlockedExtension('README') // false\n */\nexport function isBlockedExtension(filename) {\n const dotIndex = filename.lastIndexOf('.');\n if (dotIndex === -1 || dotIndex === filename.length - 1)\n return false;\n const ext = filename.slice(dotIndex + 1).toLowerCase();\n return BLOCKED_EXTENSIONS.has(ext);\n}\n// =============================================================================\n// FILENAME CHARACTER VALIDATION\n// =============================================================================\n/**\n * Characters that are unsafe in filenames for static hosting.\n *\n * Blocks only characters that genuinely break the upload→serve round-trip:\n * - # ? % URL round-trip breakers (fragment, query, encoding ambiguity)\n * - \\ Path separator confusion (upload splits on backslash)\n * - < > \" XSS vectors with zero legitimate use in filenames\n * - \\x00-\\x1f \\x7f Control characters (header injection, display corruption)\n *\n * Everything else is allowed — browser percent-encodes, Worker decodes, R2 matches.\n */\n// biome-ignore lint/suspicious/noControlCharactersInRegex: blocking control characters is this regex's purpose\nexport const UNSAFE_FILENAME_CHARS = /[\\x00-\\x1f\\x7f#?%\\\\<>\"]/;\n/**\n * Check if a filename contains unsafe characters.\n *\n * @example\n * hasUnsafeChars('saved_resource(1).html') // false — parentheses are safe\n * hasUnsafeChars('page[slug].js') // false — brackets are safe\n * hasUnsafeChars('file#anchor.html') // true — # breaks URL resolution\n * hasUnsafeChars('file<tag>.html') // true — < is an XSS vector\n */\nexport function hasUnsafeChars(filename) {\n return UNSAFE_FILENAME_CHARS.test(filename);\n}\n// =============================================================================\n// UNBUILT PROJECT MARKERS\n// =============================================================================\n/**\n * Path segment names that indicate an unbuilt project was uploaded instead of build output.\n * Used for early detection in CLI, browser, and server validation.\n */\nexport const UNBUILT_PROJECT_MARKERS = new Set([\n 'node_modules',\n 'package.json',\n]);\n/**\n * Check if a file path contains an unbuilt project marker.\n *\n * @example\n * hasUnbuiltMarker('node_modules/react/index.js') // true\n * hasUnbuiltMarker('package.json') // true\n * hasUnbuiltMarker('dist/index.html') // false\n */\nexport function hasUnbuiltMarker(filePath) {\n const segments = filePath.replace(/\\\\/g, '/').split('/').filter(Boolean);\n return segments.some((s) => UNBUILT_PROJECT_MARKERS.has(s));\n}\n// =============================================================================\n// CREDENTIAL SHAPES\n// =============================================================================\n// The one address for credential vocabulary: where human identity lives\n// (AUTH_BASE_PATH), how a request is authorized (AuthMethod), the shapes\n// that distinguish populations on the wire (API_KEY, DEPLOY_TOKEN, CALLER),\n// the single dispatch over them (TokenKind, classifyToken), and the\n// delegated-access scopes (OAuthScope).\n/**\n * Where human identity is mounted on the API host. The API mounts Better\n * Auth at this path (sign-in, sign-out, session reads, admin impersonation)\n * and the web console's auth client posts to it — shared here so the two\n * halves of the auth pair agree by construction, the same way both sides\n * already share the credential prefixes below.\n */\nexport const AUTH_BASE_PATH = '/auth';\n/**\n * How a request (or recorded activity) was authorized.\n *\n * Client populations: `SESSION` (first-party cookie), `API_KEY` (`ship-`\n * key), `TOKEN` (`deploy-` deploy token), `AGENT` (anonymous public deploy —\n * no credential; the platform grants the public-account identity per\n * request), `OAUTH` (delegated access token). Server populations: `WEBHOOK`\n * (signed webhook processing), `SYSTEM` (scheduled/background jobs).\n */\nexport const AuthMethod = {\n SESSION: 'session',\n API_KEY: 'apiKey',\n TOKEN: 'token',\n AGENT: 'agent',\n OAUTH: 'oauth',\n WEBHOOK: 'webhook',\n SYSTEM: 'system',\n};\n/**\n * Shape constants for API keys (`ship-{64 hex chars}`).\n * Single source of truth used by validation utilities and auth middleware.\n */\nexport const API_KEY = {\n /** Prefix that identifies an API key. */\n PREFIX: 'ship-',\n /** Number of hex characters following the prefix. */\n HEX_LENGTH: 64,\n /** Total length of an API key including prefix (`PREFIX.length + HEX_LENGTH = 69`). */\n TOTAL_LENGTH: 69,\n /** Number of trailing characters used to display a redacted hint (e.g. last 4). */\n HINT_LENGTH: 4,\n};\n/**\n * Shape constants for deploy tokens (`deploy-{64 hex chars}`).\n * Single source of truth used by validation utilities and auth middleware.\n */\nexport const DEPLOY_TOKEN = {\n /** Prefix that identifies a deploy token. */\n PREFIX: 'deploy-',\n /** Number of hex characters following the prefix. */\n HEX_LENGTH: 64,\n /** Total length of a deploy token including prefix (`PREFIX.length + HEX_LENGTH = 71`). */\n TOTAL_LENGTH: 71,\n};\n/**\n * Shape constants for caller identifiers (the `X-Caller` instance-identity\n * header — rate-limit bucketing for multi-tenant orchestrators). The API\n * normalizes case and silently ignores malformed values (the header is\n * unauthenticated); clients validate at the boundary via `validateCaller`,\n * so a value the server would drop fails fast instead.\n */\nexport const CALLER = {\n /** HTTP header name. */\n HEADER: 'X-Caller',\n /** Maximum identifier length. */\n MAX_LENGTH: 128,\n /** Allowed characters: alphanumeric, dot, underscore, hyphen. */\n PATTERN: /^[a-zA-Z0-9._-]+$/,\n};\n/**\n * Token populations distinguishable by shape. The platform carries every\n * client token in one wire slot (`Authorization: Bearer <value>`) and\n * classifies by value, never by a side channel — this is the classifier.\n *\n * `API_KEY` and `DEPLOY_TOKEN` *are* `AuthMethod.API_KEY` and\n * `AuthMethod.TOKEN` — the equality is structural, so a classification flows\n * straight into an auth method and the pair can never drift. `OPAQUE` is any\n * other value — shape says nothing about it, so only a lookup can. Today the\n * server refuses every opaque bearer; the OAuth access-token population\n * resolves there when the authorization server ships.\n */\nexport const TokenKind = {\n API_KEY: AuthMethod.API_KEY,\n DEPLOY_TOKEN: AuthMethod.TOKEN,\n OPAQUE: 'opaque',\n};\n/**\n * Classify a client token by shape. The single dispatch used by both sides\n * of the wire: API auth middleware (which population is this credential?)\n * and SDK validation (which format rules apply before sending?). Sharing it\n * is what guarantees client and server can never disagree on dispatch.\n */\nexport function classifyToken(token) {\n if (token.startsWith(API_KEY.PREFIX))\n return TokenKind.API_KEY;\n if (token.startsWith(DEPLOY_TOKEN.PREFIX))\n return TokenKind.DEPLOY_TOKEN;\n return TokenKind.OPAQUE;\n}\n/**\n * OAuth scope vocabulary for delegated third-party access tokens.\n * Single source of truth used by the authorization server (advertised in\n * `scopes_supported`), the API's scope-enforcement middleware, and consent UI\n * copy. The standard `offline_access` scope (refresh tokens) is not platform\n * vocabulary and is deliberately absent — the middleware never checks it.\n *\n * Deliberately absent by design: any `tokens:*` scope, `account:write`, or\n * admin scope — a delegated app must never mint credentials, delete the\n * account, or act as admin.\n */\nexport const OAuthScope = {\n ACCOUNT_READ: 'account:read',\n DEPLOYMENTS_READ: 'deployments:read',\n DEPLOYMENTS_WRITE: 'deployments:write',\n DOMAINS_READ: 'domains:read',\n DOMAINS_WRITE: 'domains:write',\n};\n// =============================================================================\n// DEPLOYMENT CONFIGURATION CONSTANTS\n// =============================================================================\nexport const DEPLOYMENT_CONFIG_FILENAME = 'ship.json';\n/** Default ship.json config for SPA routing. Single source of truth — used by both API and SDK. */\nexport const SPA_DEFAULT_CONFIG = {\n rewrites: [{ source: '/(.*)', destination: '/index.html' }],\n};\n// =============================================================================\n// VALIDATION UTILITIES\n// =============================================================================\n/**\n * Shared rule for prefixed credentials: `{PREFIX}{HEX_LENGTH hex chars}`.\n * The regex derives from the shape constants, so the validators can never\n * drift from the shapes `classifyToken` dispatches on.\n */\nfunction validatePrefixedCredential(value, shape, label) {\n if (!value.startsWith(shape.PREFIX)) {\n throw ShipError.validation(`${label} must start with \"${shape.PREFIX}\"`);\n }\n if (value.length !== shape.TOTAL_LENGTH) {\n throw ShipError.validation(`${label} must be ${shape.TOTAL_LENGTH} characters total (${shape.PREFIX} + ${shape.HEX_LENGTH} hex chars)`);\n }\n const hexPart = value.slice(shape.PREFIX.length);\n if (!new RegExp(`^[a-f0-9]{${shape.HEX_LENGTH}}$`, 'i').test(hexPart)) {\n throw ShipError.validation(`${label} must contain ${shape.HEX_LENGTH} hexadecimal characters after \"${shape.PREFIX}\" prefix`);\n }\n}\n/**\n * Validate API key format\n */\nexport function validateApiKey(apiKey) {\n validatePrefixedCredential(apiKey, API_KEY, 'API key');\n}\n/**\n * Validate deploy token format\n */\nexport function validateDeployToken(deployToken) {\n validatePrefixedCredential(deployToken, DEPLOY_TOKEN, 'Deploy token');\n}\n/**\n * Validate a client token of any population. Classifies by shape and applies\n * the matching format rules: `ship-` keys and `deploy-` deploy tokens are\n * validated strictly; opaque tokens (OAuth access tokens, future populations)\n * only need to be non-empty — their validity is the server's to decide.\n */\nexport function validateToken(token) {\n switch (classifyToken(token)) {\n case TokenKind.API_KEY:\n validateApiKey(token);\n return;\n case TokenKind.DEPLOY_TOKEN:\n validateDeployToken(token);\n return;\n case TokenKind.OPAQUE:\n if (!token)\n throw ShipError.validation('Token must be a non-empty string');\n }\n}\n/**\n * Validate a caller identifier against the `CALLER` shape. The server\n * silently ignores malformed values (the header is unauthenticated); clients\n * call this at configuration time so the drop never silently happens.\n */\nexport function validateCaller(caller) {\n if (!caller || caller.length > CALLER.MAX_LENGTH || !CALLER.PATTERN.test(caller)) {\n throw ShipError.validation(`Caller must be 1-${CALLER.MAX_LENGTH} characters: letters, digits, dots, underscores, or hyphens`);\n }\n}\n/**\n * Validate API URL format\n */\nexport function validateApiUrl(apiUrl) {\n try {\n const url = new URL(apiUrl);\n if (!['http:', 'https:'].includes(url.protocol)) {\n throw ShipError.validation('API URL must use http:// or https:// protocol');\n }\n if (url.pathname !== '/' && url.pathname !== '') {\n throw ShipError.validation('API URL must not contain a path');\n }\n if (url.search || url.hash) {\n throw ShipError.validation('API URL must not contain query parameters or fragments');\n }\n }\n catch (error) {\n if (isShipError(error)) {\n throw error;\n }\n throw ShipError.validation('API URL must be a valid URL');\n }\n}\n/**\n * Check if a string matches the deployment identifier pattern (word-word-alphanumeric7).\n * Example: \"happy-cat-abc1234.shipstatic.com\"\n */\nexport function isDeployment(input) {\n return /^[a-z]+-[a-z]+-[a-z0-9]{7}(\\.[a-z0-9.-]+)?$/i.test(input);\n}\n// =============================================================================\n// PLATFORM CONSTANTS\n// =============================================================================\n/** Default API URL if not otherwise configured. */\nexport const DEFAULT_API = 'https://api.shipstatic.com';\n// =============================================================================\n// FILE UPLOAD TYPES\n// =============================================================================\n/**\n * File status constants for validation state tracking\n */\nexport const FileValidationStatus = {\n /** File is pending validation */\n PENDING: 'pending',\n /** File failed during processing (before validation) */\n PROCESSING_ERROR: 'processing_error',\n /** File was excluded by validation warning (not an error) */\n EXCLUDED: 'excluded',\n /** File failed validation (blocks deployment) */\n VALIDATION_FAILED: 'validation_failed',\n /** File passed validation and is ready for deployment */\n READY: 'ready',\n};\n// =============================================================================\n// DOMAIN UTILITIES\n// =============================================================================\n/**\n * Check if a domain is a platform domain (subdomain of our platform).\n * Platform domains are free and don't require DNS verification.\n *\n * @example isPlatformDomain(\"www.shipstatic.com\", \"shipstatic.com\") → true\n * @example isPlatformDomain(\"example.com\", \"shipstatic.com\") → false\n */\nexport function isPlatformDomain(domain, platformDomain) {\n return domain.endsWith(`.${platformDomain}`);\n}\n/**\n * Check if a domain is a custom domain (not a platform subdomain).\n * Custom domains are billable and require DNS verification.\n *\n * @example isCustomDomain(\"example.com\", \"shipstatic.com\") → true\n * @example isCustomDomain(\"www.shipstatic.com\", \"shipstatic.com\") → false\n */\nexport function isCustomDomain(domain, platformDomain) {\n return !isPlatformDomain(domain, platformDomain);\n}\n/**\n * Extract subdomain from a platform domain.\n * Returns null if not a platform domain.\n *\n * @example extractSubdomain(\"www.shipstatic.com\", \"shipstatic.com\") → \"www\"\n * @example extractSubdomain(\"example.com\", \"shipstatic.com\") → null\n */\nexport function extractSubdomain(domain, platformDomain) {\n if (!isPlatformDomain(domain, platformDomain)) {\n return null;\n }\n return domain.slice(0, -(platformDomain.length + 1)); // +1 for the dot\n}\n/**\n * Generate HTTPS URL for a deployment hostname.\n */\nexport function generateDeploymentUrl(deployment) {\n return `https://${deployment}`;\n}\n/**\n * Generate HTTPS URL for a domain.\n */\nexport function generateDomainUrl(domain) {\n return `https://${domain}`;\n}\n// =============================================================================\n// LABEL UTILITIES\n// =============================================================================\n/**\n * Label validation constraints shared across UI and API.\n * These rules define the single source of truth for label validation.\n */\nexport const LABEL_CONSTRAINTS = {\n /** Minimum label length in characters */\n MIN_LENGTH: 3,\n /** Maximum label length in characters (concise labels, matches Stack Overflow's original limit) */\n MAX_LENGTH: 25,\n /** Maximum number of labels allowed per resource */\n MAX_COUNT: 10,\n /** Allowed separator characters between label segments */\n SEPARATORS: '._-',\n};\n/**\n * Label validation pattern.\n * Must start and end with alphanumeric (a-z, 0-9).\n * Can contain separators (. _ -) between segments, but not consecutive.\n *\n * Valid examples: 'production', 'v1.2.3', 'api_v2', 'us-east-1'\n * Invalid examples: 'ab' (too short), '-prod' (starts with separator), 'foo--bar' (consecutive separators)\n */\nexport const LABEL_PATTERN = /^[a-z0-9]+(?:[._-][a-z0-9]+)*$/;\n/**\n * Serialize labels array to JSON string for database storage.\n * Returns null for empty or undefined arrays.\n *\n * @example serializeLabels(['web', 'production']) → '[\"web\",\"production\"]'\n * @example serializeLabels([]) → null\n * @example serializeLabels(undefined) → null\n */\nexport function serializeLabels(labels) {\n if (!labels || labels.length === 0)\n return null;\n return JSON.stringify(labels);\n}\n/**\n * Deserialize labels from JSON string to array.\n * Always returns an array — empty array for null/empty/invalid input.\n *\n * @example deserializeLabels('[\"web\",\"production\"]') → ['web', 'production']\n * @example deserializeLabels(null) → []\n * @example deserializeLabels('') → []\n */\nexport function deserializeLabels(labelsJson) {\n if (!labelsJson)\n return [];\n try {\n const parsed = JSON.parse(labelsJson);\n return Array.isArray(parsed) ? parsed : [];\n }\n catch {\n return [];\n }\n}\n// =============================================================================\n// PASSWORD UTILITIES\n// =============================================================================\n/**\n * Length constraints for the optional deployment password\n * (`DeploymentUploadOptions.password`). Single source of truth shared across\n * platform consumers.\n */\nexport const PASSWORD_CONSTRAINTS = {\n /** Minimum password length in characters */\n MIN_LENGTH: 6,\n /** Maximum password length in characters */\n MAX_LENGTH: 128,\n};\n/**\n * Validate an optional deployment password and return it normalized.\n *\n * Absent (`undefined` / `null`) → returns `undefined`. Present → trim\n * leading/trailing whitespace, then validate against `PASSWORD_CONSTRAINTS`\n * length bounds (internal whitespace is significant and counts toward\n * length). Throws `ShipError.validation` on breach; returns the trimmed\n * value.\n *\n * The trim is canonical: at upload, the API hashes the trimmed value; at\n * unlock, the router trims submissions before hashing. Submission and storage\n * agree byte-for-byte. Length validation runs on the trimmed value because\n * that's the user's actual intent — and it disarms a class of invisible\n * foot-guns (trailing newlines from copy/paste, mobile auto-spacing,\n * password-manager artifacts).\n *\n * Single source of truth shared by SDK (client-side validation, return\n * ignored) and API (server-side enforcement, return threaded into config).\n * Length is part of the wire-format contract; strength rules, if added later,\n * stay server-side. See `CLAUDE.md` \"Validation: format vs policy\".\n */\nexport function validatePassword(value) {\n if (value === undefined || value === null)\n return undefined;\n if (typeof value !== 'string') {\n throw ShipError.validation('Password must be a string');\n }\n const trimmed = value.trim();\n if (trimmed.length < PASSWORD_CONSTRAINTS.MIN_LENGTH ||\n trimmed.length > PASSWORD_CONSTRAINTS.MAX_LENGTH) {\n throw ShipError.validation(`Password must be between ${PASSWORD_CONSTRAINTS.MIN_LENGTH} and ${PASSWORD_CONSTRAINTS.MAX_LENGTH} characters`);\n }\n return trimmed;\n}\n","(function (factory) {\n if (typeof exports === 'object') {\n // Node/CommonJS\n module.exports = factory();\n } else if (typeof define === 'function' && define.amd) {\n // AMD\n define(factory);\n } else {\n // Browser globals (with support for web workers)\n var glob;\n\n try {\n glob = window;\n } catch (e) {\n glob = self;\n }\n\n glob.SparkMD5 = factory();\n }\n}(function (undefined) {\n\n 'use strict';\n\n /*\n * Fastest md5 implementation around (JKM md5).\n * Credits: Joseph Myers\n *\n * @see http://www.myersdaily.org/joseph/javascript/md5-text.html\n * @see http://jsperf.com/md5-shootout/7\n */\n\n /* this function is much faster,\n so if possible we use it. Some IEs\n are the only ones I know of that\n need the idiotic second function,\n generated by an if clause. */\n var add32 = function (a, b) {\n return (a + b) & 0xFFFFFFFF;\n },\n hex_chr = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'];\n\n\n function cmn(q, a, b, x, s, t) {\n a = add32(add32(a, q), add32(x, t));\n return add32((a << s) | (a >>> (32 - s)), b);\n }\n\n function md5cycle(x, k) {\n var a = x[0],\n b = x[1],\n c = x[2],\n d = x[3];\n\n a += (b & c | ~b & d) + k[0] - 680876936 | 0;\n a = (a << 7 | a >>> 25) + b | 0;\n d += (a & b | ~a & c) + k[1] - 389564586 | 0;\n d = (d << 12 | d >>> 20) + a | 0;\n c += (d & a | ~d & b) + k[2] + 606105819 | 0;\n c = (c << 17 | c >>> 15) + d | 0;\n b += (c & d | ~c & a) + k[3] - 1044525330 | 0;\n b = (b << 22 | b >>> 10) + c | 0;\n a += (b & c | ~b & d) + k[4] - 176418897 | 0;\n a = (a << 7 | a >>> 25) + b | 0;\n d += (a & b | ~a & c) + k[5] + 1200080426 | 0;\n d = (d << 12 | d >>> 20) + a | 0;\n c += (d & a | ~d & b) + k[6] - 1473231341 | 0;\n c = (c << 17 | c >>> 15) + d | 0;\n b += (c & d | ~c & a) + k[7] - 45705983 | 0;\n b = (b << 22 | b >>> 10) + c | 0;\n a += (b & c | ~b & d) + k[8] + 1770035416 | 0;\n a = (a << 7 | a >>> 25) + b | 0;\n d += (a & b | ~a & c) + k[9] - 1958414417 | 0;\n d = (d << 12 | d >>> 20) + a | 0;\n c += (d & a | ~d & b) + k[10] - 42063 | 0;\n c = (c << 17 | c >>> 15) + d | 0;\n b += (c & d | ~c & a) + k[11] - 1990404162 | 0;\n b = (b << 22 | b >>> 10) + c | 0;\n a += (b & c | ~b & d) + k[12] + 1804603682 | 0;\n a = (a << 7 | a >>> 25) + b | 0;\n d += (a & b | ~a & c) + k[13] - 40341101 | 0;\n d = (d << 12 | d >>> 20) + a | 0;\n c += (d & a | ~d & b) + k[14] - 1502002290 | 0;\n c = (c << 17 | c >>> 15) + d | 0;\n b += (c & d | ~c & a) + k[15] + 1236535329 | 0;\n b = (b << 22 | b >>> 10) + c | 0;\n\n a += (b & d | c & ~d) + k[1] - 165796510 | 0;\n a = (a << 5 | a >>> 27) + b | 0;\n d += (a & c | b & ~c) + k[6] - 1069501632 | 0;\n d = (d << 9 | d >>> 23) + a | 0;\n c += (d & b | a & ~b) + k[11] + 643717713 | 0;\n c = (c << 14 | c >>> 18) + d | 0;\n b += (c & a | d & ~a) + k[0] - 373897302 | 0;\n b = (b << 20 | b >>> 12) + c | 0;\n a += (b & d | c & ~d) + k[5] - 701558691 | 0;\n a = (a << 5 | a >>> 27) + b | 0;\n d += (a & c | b & ~c) + k[10] + 38016083 | 0;\n d = (d << 9 | d >>> 23) + a | 0;\n c += (d & b | a & ~b) + k[15] - 660478335 | 0;\n c = (c << 14 | c >>> 18) + d | 0;\n b += (c & a | d & ~a) + k[4] - 405537848 | 0;\n b = (b << 20 | b >>> 12) + c | 0;\n a += (b & d | c & ~d) + k[9] + 568446438 | 0;\n a = (a << 5 | a >>> 27) + b | 0;\n d += (a & c | b & ~c) + k[14] - 1019803690 | 0;\n d = (d << 9 | d >>> 23) + a | 0;\n c += (d & b | a & ~b) + k[3] - 187363961 | 0;\n c = (c << 14 | c >>> 18) + d | 0;\n b += (c & a | d & ~a) + k[8] + 1163531501 | 0;\n b = (b << 20 | b >>> 12) + c | 0;\n a += (b & d | c & ~d) + k[13] - 1444681467 | 0;\n a = (a << 5 | a >>> 27) + b | 0;\n d += (a & c | b & ~c) + k[2] - 51403784 | 0;\n d = (d << 9 | d >>> 23) + a | 0;\n c += (d & b | a & ~b) + k[7] + 1735328473 | 0;\n c = (c << 14 | c >>> 18) + d | 0;\n b += (c & a | d & ~a) + k[12] - 1926607734 | 0;\n b = (b << 20 | b >>> 12) + c | 0;\n\n a += (b ^ c ^ d) + k[5] - 378558 | 0;\n a = (a << 4 | a >>> 28) + b | 0;\n d += (a ^ b ^ c) + k[8] - 2022574463 | 0;\n d = (d << 11 | d >>> 21) + a | 0;\n c += (d ^ a ^ b) + k[11] + 1839030562 | 0;\n c = (c << 16 | c >>> 16) + d | 0;\n b += (c ^ d ^ a) + k[14] - 35309556 | 0;\n b = (b << 23 | b >>> 9) + c | 0;\n a += (b ^ c ^ d) + k[1] - 1530992060 | 0;\n a = (a << 4 | a >>> 28) + b | 0;\n d += (a ^ b ^ c) + k[4] + 1272893353 | 0;\n d = (d << 11 | d >>> 21) + a | 0;\n c += (d ^ a ^ b) + k[7] - 155497632 | 0;\n c = (c << 16 | c >>> 16) + d | 0;\n b += (c ^ d ^ a) + k[10] - 1094730640 | 0;\n b = (b << 23 | b >>> 9) + c | 0;\n a += (b ^ c ^ d) + k[13] + 681279174 | 0;\n a = (a << 4 | a >>> 28) + b | 0;\n d += (a ^ b ^ c) + k[0] - 358537222 | 0;\n d = (d << 11 | d >>> 21) + a | 0;\n c += (d ^ a ^ b) + k[3] - 722521979 | 0;\n c = (c << 16 | c >>> 16) + d | 0;\n b += (c ^ d ^ a) + k[6] + 76029189 | 0;\n b = (b << 23 | b >>> 9) + c | 0;\n a += (b ^ c ^ d) + k[9] - 640364487 | 0;\n a = (a << 4 | a >>> 28) + b | 0;\n d += (a ^ b ^ c) + k[12] - 421815835 | 0;\n d = (d << 11 | d >>> 21) + a | 0;\n c += (d ^ a ^ b) + k[15] + 530742520 | 0;\n c = (c << 16 | c >>> 16) + d | 0;\n b += (c ^ d ^ a) + k[2] - 995338651 | 0;\n b = (b << 23 | b >>> 9) + c | 0;\n\n a += (c ^ (b | ~d)) + k[0] - 198630844 | 0;\n a = (a << 6 | a >>> 26) + b | 0;\n d += (b ^ (a | ~c)) + k[7] + 1126891415 | 0;\n d = (d << 10 | d >>> 22) + a | 0;\n c += (a ^ (d | ~b)) + k[14] - 1416354905 | 0;\n c = (c << 15 | c >>> 17) + d | 0;\n b += (d ^ (c | ~a)) + k[5] - 57434055 | 0;\n b = (b << 21 |b >>> 11) + c | 0;\n a += (c ^ (b | ~d)) + k[12] + 1700485571 | 0;\n a = (a << 6 | a >>> 26) + b | 0;\n d += (b ^ (a | ~c)) + k[3] - 1894986606 | 0;\n d = (d << 10 | d >>> 22) + a | 0;\n c += (a ^ (d | ~b)) + k[10] - 1051523 | 0;\n c = (c << 15 | c >>> 17) + d | 0;\n b += (d ^ (c | ~a)) + k[1] - 2054922799 | 0;\n b = (b << 21 |b >>> 11) + c | 0;\n a += (c ^ (b | ~d)) + k[8] + 1873313359 | 0;\n a = (a << 6 | a >>> 26) + b | 0;\n d += (b ^ (a | ~c)) + k[15] - 30611744 | 0;\n d = (d << 10 | d >>> 22) + a | 0;\n c += (a ^ (d | ~b)) + k[6] - 1560198380 | 0;\n c = (c << 15 | c >>> 17) + d | 0;\n b += (d ^ (c | ~a)) + k[13] + 1309151649 | 0;\n b = (b << 21 |b >>> 11) + c | 0;\n a += (c ^ (b | ~d)) + k[4] - 145523070 | 0;\n a = (a << 6 | a >>> 26) + b | 0;\n d += (b ^ (a | ~c)) + k[11] - 1120210379 | 0;\n d = (d << 10 | d >>> 22) + a | 0;\n c += (a ^ (d | ~b)) + k[2] + 718787259 | 0;\n c = (c << 15 | c >>> 17) + d | 0;\n b += (d ^ (c | ~a)) + k[9] - 343485551 | 0;\n b = (b << 21 | b >>> 11) + c | 0;\n\n x[0] = a + x[0] | 0;\n x[1] = b + x[1] | 0;\n x[2] = c + x[2] | 0;\n x[3] = d + x[3] | 0;\n }\n\n function md5blk(s) {\n var md5blks = [],\n i; /* Andy King said do it this way. */\n\n for (i = 0; i < 64; i += 4) {\n md5blks[i >> 2] = s.charCodeAt(i) + (s.charCodeAt(i + 1) << 8) + (s.charCodeAt(i + 2) << 16) + (s.charCodeAt(i + 3) << 24);\n }\n return md5blks;\n }\n\n function md5blk_array(a) {\n var md5blks = [],\n i; /* Andy King said do it this way. */\n\n for (i = 0; i < 64; i += 4) {\n md5blks[i >> 2] = a[i] + (a[i + 1] << 8) + (a[i + 2] << 16) + (a[i + 3] << 24);\n }\n return md5blks;\n }\n\n function md51(s) {\n var n = s.length,\n state = [1732584193, -271733879, -1732584194, 271733878],\n i,\n length,\n tail,\n tmp,\n lo,\n hi;\n\n for (i = 64; i <= n; i += 64) {\n md5cycle(state, md5blk(s.substring(i - 64, i)));\n }\n s = s.substring(i - 64);\n length = s.length;\n tail = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];\n for (i = 0; i < length; i += 1) {\n tail[i >> 2] |= s.charCodeAt(i) << ((i % 4) << 3);\n }\n tail[i >> 2] |= 0x80 << ((i % 4) << 3);\n if (i > 55) {\n md5cycle(state, tail);\n for (i = 0; i < 16; i += 1) {\n tail[i] = 0;\n }\n }\n\n // Beware that the final length might not fit in 32 bits so we take care of that\n tmp = n * 8;\n tmp = tmp.toString(16).match(/(.*?)(.{0,8})$/);\n lo = parseInt(tmp[2], 16);\n hi = parseInt(tmp[1], 16) || 0;\n\n tail[14] = lo;\n tail[15] = hi;\n\n md5cycle(state, tail);\n return state;\n }\n\n function md51_array(a) {\n var n = a.length,\n state = [1732584193, -271733879, -1732584194, 271733878],\n i,\n length,\n tail,\n tmp,\n lo,\n hi;\n\n for (i = 64; i <= n; i += 64) {\n md5cycle(state, md5blk_array(a.subarray(i - 64, i)));\n }\n\n // Not sure if it is a bug, however IE10 will always produce a sub array of length 1\n // containing the last element of the parent array if the sub array specified starts\n // beyond the length of the parent array - weird.\n // https://connect.microsoft.com/IE/feedback/details/771452/typed-array-subarray-issue\n a = (i - 64) < n ? a.subarray(i - 64) : new Uint8Array(0);\n\n length = a.length;\n tail = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];\n for (i = 0; i < length; i += 1) {\n tail[i >> 2] |= a[i] << ((i % 4) << 3);\n }\n\n tail[i >> 2] |= 0x80 << ((i % 4) << 3);\n if (i > 55) {\n md5cycle(state, tail);\n for (i = 0; i < 16; i += 1) {\n tail[i] = 0;\n }\n }\n\n // Beware that the final length might not fit in 32 bits so we take care of that\n tmp = n * 8;\n tmp = tmp.toString(16).match(/(.*?)(.{0,8})$/);\n lo = parseInt(tmp[2], 16);\n hi = parseInt(tmp[1], 16) || 0;\n\n tail[14] = lo;\n tail[15] = hi;\n\n md5cycle(state, tail);\n\n return state;\n }\n\n function rhex(n) {\n var s = '',\n j;\n for (j = 0; j < 4; j += 1) {\n s += hex_chr[(n >> (j * 8 + 4)) & 0x0F] + hex_chr[(n >> (j * 8)) & 0x0F];\n }\n return s;\n }\n\n function hex(x) {\n var i;\n for (i = 0; i < x.length; i += 1) {\n x[i] = rhex(x[i]);\n }\n return x.join('');\n }\n\n // In some cases the fast add32 function cannot be used..\n if (hex(md51('hello')) !== '5d41402abc4b2a76b9719d911017c592') {\n add32 = function (x, y) {\n var lsw = (x & 0xFFFF) + (y & 0xFFFF),\n msw = (x >> 16) + (y >> 16) + (lsw >> 16);\n return (msw << 16) | (lsw & 0xFFFF);\n };\n }\n\n // ---------------------------------------------------\n\n /**\n * ArrayBuffer slice polyfill.\n *\n * @see https://github.com/ttaubert/node-arraybuffer-slice\n */\n\n if (typeof ArrayBuffer !== 'undefined' && !ArrayBuffer.prototype.slice) {\n (function () {\n function clamp(val, length) {\n val = (val | 0) || 0;\n\n if (val < 0) {\n return Math.max(val + length, 0);\n }\n\n return Math.min(val, length);\n }\n\n ArrayBuffer.prototype.slice = function (from, to) {\n var length = this.byteLength,\n begin = clamp(from, length),\n end = length,\n num,\n target,\n targetArray,\n sourceArray;\n\n if (to !== undefined) {\n end = clamp(to, length);\n }\n\n if (begin > end) {\n return new ArrayBuffer(0);\n }\n\n num = end - begin;\n target = new ArrayBuffer(num);\n targetArray = new Uint8Array(target);\n\n sourceArray = new Uint8Array(this, begin, num);\n targetArray.set(sourceArray);\n\n return target;\n };\n })();\n }\n\n // ---------------------------------------------------\n\n /**\n * Helpers.\n */\n\n function toUtf8(str) {\n if (/[\\u0080-\\uFFFF]/.test(str)) {\n str = unescape(encodeURIComponent(str));\n }\n\n return str;\n }\n\n function utf8Str2ArrayBuffer(str, returnUInt8Array) {\n var length = str.length,\n buff = new ArrayBuffer(length),\n arr = new Uint8Array(buff),\n i;\n\n for (i = 0; i < length; i += 1) {\n arr[i] = str.charCodeAt(i);\n }\n\n return returnUInt8Array ? arr : buff;\n }\n\n function arrayBuffer2Utf8Str(buff) {\n return String.fromCharCode.apply(null, new Uint8Array(buff));\n }\n\n function concatenateArrayBuffers(first, second, returnUInt8Array) {\n var result = new Uint8Array(first.byteLength + second.byteLength);\n\n result.set(new Uint8Array(first));\n result.set(new Uint8Array(second), first.byteLength);\n\n return returnUInt8Array ? result : result.buffer;\n }\n\n function hexToBinaryString(hex) {\n var bytes = [],\n length = hex.length,\n x;\n\n for (x = 0; x < length - 1; x += 2) {\n bytes.push(parseInt(hex.substr(x, 2), 16));\n }\n\n return String.fromCharCode.apply(String, bytes);\n }\n\n // ---------------------------------------------------\n\n /**\n * SparkMD5 OOP implementation.\n *\n * Use this class to perform an incremental md5, otherwise use the\n * static methods instead.\n */\n\n function SparkMD5() {\n // call reset to init the instance\n this.reset();\n }\n\n /**\n * Appends a string.\n * A conversion will be applied if an utf8 string is detected.\n *\n * @param {String} str The string to be appended\n *\n * @return {SparkMD5} The instance itself\n */\n SparkMD5.prototype.append = function (str) {\n // Converts the string to utf8 bytes if necessary\n // Then append as binary\n this.appendBinary(toUtf8(str));\n\n return this;\n };\n\n /**\n * Appends a binary string.\n *\n * @param {String} contents The binary string to be appended\n *\n * @return {SparkMD5} The instance itself\n */\n SparkMD5.prototype.appendBinary = function (contents) {\n this._buff += contents;\n this._length += contents.length;\n\n var length = this._buff.length,\n i;\n\n for (i = 64; i <= length; i += 64) {\n md5cycle(this._hash, md5blk(this._buff.substring(i - 64, i)));\n }\n\n this._buff = this._buff.substring(i - 64);\n\n return this;\n };\n\n /**\n * Finishes the incremental computation, reseting the internal state and\n * returning the result.\n *\n * @param {Boolean} raw True to get the raw string, false to get the hex string\n *\n * @return {String} The result\n */\n SparkMD5.prototype.end = function (raw) {\n var buff = this._buff,\n length = buff.length,\n i,\n tail = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],\n ret;\n\n for (i = 0; i < length; i += 1) {\n tail[i >> 2] |= buff.charCodeAt(i) << ((i % 4) << 3);\n }\n\n this._finish(tail, length);\n ret = hex(this._hash);\n\n if (raw) {\n ret = hexToBinaryString(ret);\n }\n\n this.reset();\n\n return ret;\n };\n\n /**\n * Resets the internal state of the computation.\n *\n * @return {SparkMD5} The instance itself\n */\n SparkMD5.prototype.reset = function () {\n this._buff = '';\n this._length = 0;\n this._hash = [1732584193, -271733879, -1732584194, 271733878];\n\n return this;\n };\n\n /**\n * Gets the internal state of the computation.\n *\n * @return {Object} The state\n */\n SparkMD5.prototype.getState = function () {\n return {\n buff: this._buff,\n length: this._length,\n hash: this._hash.slice()\n };\n };\n\n /**\n * Gets the internal state of the computation.\n *\n * @param {Object} state The state\n *\n * @return {SparkMD5} The instance itself\n */\n SparkMD5.prototype.setState = function (state) {\n this._buff = state.buff;\n this._length = state.length;\n this._hash = state.hash;\n\n return this;\n };\n\n /**\n * Releases memory used by the incremental buffer and other additional\n * resources. If you plan to use the instance again, use reset instead.\n */\n SparkMD5.prototype.destroy = function () {\n delete this._hash;\n delete this._buff;\n delete this._length;\n };\n\n /**\n * Finish the final calculation based on the tail.\n *\n * @param {Array} tail The tail (will be modified)\n * @param {Number} length The length of the remaining buffer\n */\n SparkMD5.prototype._finish = function (tail, length) {\n var i = length,\n tmp,\n lo,\n hi;\n\n tail[i >> 2] |= 0x80 << ((i % 4) << 3);\n if (i > 55) {\n md5cycle(this._hash, tail);\n for (i = 0; i < 16; i += 1) {\n tail[i] = 0;\n }\n }\n\n // Do the final computation based on the tail and length\n // Beware that the final length may not fit in 32 bits so we take care of that\n tmp = this._length * 8;\n tmp = tmp.toString(16).match(/(.*?)(.{0,8})$/);\n lo = parseInt(tmp[2], 16);\n hi = parseInt(tmp[1], 16) || 0;\n\n tail[14] = lo;\n tail[15] = hi;\n md5cycle(this._hash, tail);\n };\n\n /**\n * Performs the md5 hash on a string.\n * A conversion will be applied if utf8 string is detected.\n *\n * @param {String} str The string\n * @param {Boolean} [raw] True to get the raw string, false to get the hex string\n *\n * @return {String} The result\n */\n SparkMD5.hash = function (str, raw) {\n // Converts the string to utf8 bytes if necessary\n // Then compute it using the binary function\n return SparkMD5.hashBinary(toUtf8(str), raw);\n };\n\n /**\n * Performs the md5 hash on a binary string.\n *\n * @param {String} content The binary string\n * @param {Boolean} [raw] True to get the raw string, false to get the hex string\n *\n * @return {String} The result\n */\n SparkMD5.hashBinary = function (content, raw) {\n var hash = md51(content),\n ret = hex(hash);\n\n return raw ? hexToBinaryString(ret) : ret;\n };\n\n // ---------------------------------------------------\n\n /**\n * SparkMD5 OOP implementation for array buffers.\n *\n * Use this class to perform an incremental md5 ONLY for array buffers.\n */\n SparkMD5.ArrayBuffer = function () {\n // call reset to init the instance\n this.reset();\n };\n\n /**\n * Appends an array buffer.\n *\n * @param {ArrayBuffer} arr The array to be appended\n *\n * @return {SparkMD5.ArrayBuffer} The instance itself\n */\n SparkMD5.ArrayBuffer.prototype.append = function (arr) {\n var buff = concatenateArrayBuffers(this._buff.buffer, arr, true),\n length = buff.length,\n i;\n\n this._length += arr.byteLength;\n\n for (i = 64; i <= length; i += 64) {\n md5cycle(this._hash, md5blk_array(buff.subarray(i - 64, i)));\n }\n\n this._buff = (i - 64) < length ? new Uint8Array(buff.buffer.slice(i - 64)) : new Uint8Array(0);\n\n return this;\n };\n\n /**\n * Finishes the incremental computation, reseting the internal state and\n * returning the result.\n *\n * @param {Boolean} raw True to get the raw string, false to get the hex string\n *\n * @return {String} The result\n */\n SparkMD5.ArrayBuffer.prototype.end = function (raw) {\n var buff = this._buff,\n length = buff.length,\n tail = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],\n i,\n ret;\n\n for (i = 0; i < length; i += 1) {\n tail[i >> 2] |= buff[i] << ((i % 4) << 3);\n }\n\n this._finish(tail, length);\n ret = hex(this._hash);\n\n if (raw) {\n ret = hexToBinaryString(ret);\n }\n\n this.reset();\n\n return ret;\n };\n\n /**\n * Resets the internal state of the computation.\n *\n * @return {SparkMD5.ArrayBuffer} The instance itself\n */\n SparkMD5.ArrayBuffer.prototype.reset = function () {\n this._buff = new Uint8Array(0);\n this._length = 0;\n this._hash = [1732584193, -271733879, -1732584194, 271733878];\n\n return this;\n };\n\n /**\n * Gets the internal state of the computation.\n *\n * @return {Object} The state\n */\n SparkMD5.ArrayBuffer.prototype.getState = function () {\n var state = SparkMD5.prototype.getState.call(this);\n\n // Convert buffer to a string\n state.buff = arrayBuffer2Utf8Str(state.buff);\n\n return state;\n };\n\n /**\n * Gets the internal state of the computation.\n *\n * @param {Object} state The state\n *\n * @return {SparkMD5.ArrayBuffer} The instance itself\n */\n SparkMD5.ArrayBuffer.prototype.setState = function (state) {\n // Convert string to buffer\n state.buff = utf8Str2ArrayBuffer(state.buff, true);\n\n return SparkMD5.prototype.setState.call(this, state);\n };\n\n SparkMD5.ArrayBuffer.prototype.destroy = SparkMD5.prototype.destroy;\n\n SparkMD5.ArrayBuffer.prototype._finish = SparkMD5.prototype._finish;\n\n /**\n * Performs the md5 hash on an array buffer.\n *\n * @param {ArrayBuffer} arr The array buffer\n * @param {Boolean} [raw] True to get the raw string, false to get the hex one\n *\n * @return {String} The result\n */\n SparkMD5.ArrayBuffer.hash = function (arr, raw) {\n var hash = md51_array(new Uint8Array(arr)),\n ret = hex(hash);\n\n return raw ? hexToBinaryString(ret) : ret;\n };\n\n return SparkMD5;\n}));\n","// This is an empty module to shim Node.js built-ins for browser builds\n// where the full functionality is not applicable or polyfilled.\nmodule.exports = {};\n","/**\n * @file MD5 utility for Blob, Buffer, or file path inputs.\n */\nimport { ShipError } from '@shipstatic/types';\n\nexport interface MD5Result {\n md5: string;\n}\n\nasync function md5Blob(blob: Blob): Promise<MD5Result> {\n const SparkMD5 = (await import('spark-md5')).default;\n const spark = new SparkMD5.ArrayBuffer();\n const chunkSize = 2097152; // 2 MB\n for (let start = 0; start < blob.size; start += chunkSize) {\n const end = Math.min(start + chunkSize, blob.size);\n spark.append(await blob.slice(start, end).arrayBuffer());\n }\n return { md5: spark.end() };\n}\n\nasync function md5Buffer(buffer: Buffer): Promise<MD5Result> {\n // biome-ignore lint/style/useNodejsImportProtocol: the browser build shims bare specifiers only — `node:` bypasses esbuild alias resolution (see tsup.config.ts)\n const { createHash } = await import('crypto');\n const hash = createHash('md5');\n hash.update(buffer);\n return { md5: hash.digest('hex') };\n}\n\nasync function md5Path(path: string): Promise<MD5Result> {\n // biome-ignore lint/style/useNodejsImportProtocol: see md5Buffer — bare specifier is load-bearing for the browser shim\n const { createHash } = await import('crypto');\n // biome-ignore lint/style/useNodejsImportProtocol: see md5Buffer — bare specifier is load-bearing for the browser shim\n const { createReadStream } = await import('fs');\n return new Promise((resolve, reject) => {\n const hash = createHash('md5');\n const stream = createReadStream(path);\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\nexport async function calculateMD5(input: Blob | Buffer | string): Promise<MD5Result> {\n if (input instanceof Blob) return md5Blob(input);\n if (typeof Buffer !== 'undefined' && Buffer.isBuffer(input)) return md5Buffer(input);\n if (typeof input === 'string') return md5Path(input);\n throw ShipError.business('Invalid input for MD5 calculation');\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 * The basis of `pathDetect`: the common parent is the prefix stripped from\n * every deploy path so a deployment's root is the site's root.\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 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 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 * 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(/\\/+/g, '/').replace(/^\\/+/, '');\n}\n","/**\n * @file Deploy path optimization - the core logic that makes Ship deployments clean and intuitive.\n * Automatically strips common parent directories to create clean deployment URLs.\n */\n\nimport { normalizeWebPath } from './path.js';\n\n/**\n * Represents a file ready for deployment with its optimized path\n */\nexport interface DeployFile {\n /** The clean deployment path (e.g., \"assets/style.css\") */\n path: string;\n /** Original filename */\n name: string;\n}\n\n/**\n * Core path optimization logic.\n * Transforms messy local paths into clean deployment paths.\n *\n * @example\n * Input: [\"dist/index.html\", \"dist/assets/app.js\"]\n * Output: [\"index.html\", \"assets/app.js\"]\n *\n * @param filePaths - Raw file paths from the local filesystem\n * @param options - Path processing options\n */\nexport function optimizeDeployPaths(\n filePaths: string[],\n options: { flatten?: boolean } = {},\n): DeployFile[] {\n // When flattening is disabled, keep original structure\n if (options.flatten === false) {\n return filePaths.map((path) => ({\n path: normalizeWebPath(path),\n name: extractFileName(path),\n }));\n }\n\n // Find the common directory prefix to strip\n const commonPrefix = findCommonDirectory(filePaths);\n\n return filePaths.map((filePath) => {\n let deployPath = normalizeWebPath(filePath);\n\n // Strip the common prefix to create clean deployment paths\n if (commonPrefix) {\n const prefixToRemove = commonPrefix.endsWith('/') ? commonPrefix : `${commonPrefix}/`;\n if (deployPath.startsWith(prefixToRemove)) {\n deployPath = deployPath.substring(prefixToRemove.length);\n }\n }\n\n // Fallback to filename if path becomes empty\n if (!deployPath) {\n deployPath = extractFileName(filePath);\n }\n\n return {\n path: deployPath,\n name: extractFileName(filePath),\n };\n });\n}\n\n/**\n * Finds the common directory shared by all file paths.\n * This is what gets stripped to create clean deployment URLs.\n *\n * @example\n * [\"dist/index.html\", \"dist/assets/app.js\"] → \"dist\"\n * [\"src/components/A.tsx\", \"src/utils/B.ts\"] → \"src\"\n * [\"file1.txt\", \"file2.txt\", \"subdir/file3.txt\"] → \"\" (no common directory)\n */\nfunction findCommonDirectory(filePaths: string[]): string {\n if (!filePaths.length) return '';\n\n // Normalize all paths first\n const normalizedPaths = filePaths.map((path) => normalizeWebPath(path));\n\n // Find the common prefix among all file paths (not just directories)\n const pathSegments = normalizedPaths.map((path) => path.split('/'));\n const commonSegments: string[] = [];\n const minLength = Math.min(...pathSegments.map((segments) => segments.length));\n\n // Check each segment level to find the longest common prefix\n for (let i = 0; i < minLength - 1; i++) {\n // -1 because we don't want to include the filename\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 * Extracts just the filename from a file path\n */\nfunction extractFileName(path: string): string {\n return path.split(/[/\\\\]/).pop() || path;\n}\n","/**\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 File validation utilities for Ship SDK\n * Provides client-side validation for file uploads before deployment\n */\n\nimport type {\n FileValidationResult,\n FileValidationStatusType,\n PlatformLimits,\n ValidatableFile,\n ValidationIssue,\n} from '@shipstatic/types';\nimport {\n FileValidationStatus as FILE_VALIDATION_STATUS,\n hasUnbuiltMarker,\n hasUnsafeChars,\n isBlockedExtension,\n} from '@shipstatic/types';\n\n// Re-exported because it constrains three exported generics (validateFiles,\n// getValidFiles, allValidFilesReady) — a consumer cannot name the bound\n// otherwise, which is why the tests were inventing their own copy.\nexport type { ValidatableFile };\nexport { FILE_VALIDATION_STATUS };\n\n/**\n * Format file size to human-readable string\n */\nexport function formatFileSize(bytes: number, decimals: number = 1): string {\n if (bytes === 0) return '0 Bytes';\n const k = 1024;\n const sizes = ['Bytes', 'KB', 'MB', 'GB'];\n const i = Math.floor(Math.log(bytes) / Math.log(k));\n return `${parseFloat((bytes / k ** i).toFixed(decimals))} ${sizes[i]}`;\n}\n\n/**\n * Validate filename for deployment safety\n *\n * Blocks only characters that genuinely break the upload→serve round-trip:\n * - # ? % URL round-trip breakers (fragment, query, encoding ambiguity)\n * - \\ Path separator confusion (buildFileKey splits on backslash)\n * - < > \" XSS vectors with zero legitimate use in filenames\n * - \\x00-\\x1f \\x7f Control characters (header injection, display corruption)\n *\n * Everything else is allowed — browser percent-encodes, Worker decodes, R2 matches.\n *\n * Additional checks: path traversal, reserved names, leading/trailing dots or spaces.\n */\nexport function validateFileName(filename: string): { valid: boolean; reason?: string } {\n if (hasUnsafeChars(filename)) {\n return { valid: false, reason: 'File name contains unsafe characters' };\n }\n\n if (filename.startsWith(' ') || filename.endsWith(' ')) {\n return { valid: false, reason: 'File name cannot start/end with spaces' };\n }\n\n if (filename.endsWith('.')) {\n return { valid: false, reason: 'File name cannot end with dots' };\n }\n\n const reservedNames = /^(CON|PRN|AUX|NUL|COM[1-9]|LPT[1-9])(\\.|$)/i;\n const nameWithoutPath = filename.split('/').pop() || filename;\n if (reservedNames.test(nameWithoutPath)) {\n return { valid: false, reason: 'File name uses a reserved system name' };\n }\n\n if (filename.includes('..')) {\n return { valid: false, reason: 'File name contains path traversal pattern' };\n }\n\n return { valid: true };\n}\n\n/**\n * Validate files against configuration limits with severity-based reporting\n *\n * Validation categorizes issues by severity:\n * - **Errors**: Block deployment (file too large, blocked extension, etc.)\n * - **Warnings**: Exclude files but allow deployment (empty files, etc.)\n *\n * @param files - Array of files to validate\n * @param config - Validation configuration from ship.getLimits()\n * @returns Validation result with errors and warnings\n *\n * @example\n * ```typescript\n * const config = await ship.getLimits();\n * const result = validateFiles(files, config);\n *\n * if (!result.canDeploy) {\n * // Has errors - deployment blocked\n * console.error('Deployment blocked:', result.errors);\n * } else if (result.warnings.length > 0) {\n * // Has warnings - deployment proceeds, some files excluded\n * console.warn('Files excluded:', result.warnings);\n * await ship.deploy(result.validFiles);\n * } else {\n * // All files valid\n * await ship.deploy(result.validFiles);\n * }\n * ```\n */\nexport function validateFiles<T extends ValidatableFile>(\n files: T[],\n config: PlatformLimits,\n): FileValidationResult<T> {\n const errors: ValidationIssue[] = [];\n const warnings: ValidationIssue[] = [];\n let fileStatuses: T[] = []; // Use 'let' for atomic enforcement later\n\n // Check at least 1 file required\n if (files.length === 0) {\n const issue: ValidationIssue = {\n file: '(no files)',\n message: 'At least one file must be provided',\n };\n errors.push(issue);\n\n return {\n files: [],\n validFiles: [],\n errors,\n warnings: [],\n canDeploy: false,\n };\n }\n\n // Check for unbuilt project markers (node_modules/, etc.)\n for (const file of files) {\n if (hasUnbuiltMarker(file.name)) {\n errors.push({\n file: file.name,\n message: `Unbuilt project detected — deploy your build output (dist/, build/, out/), not the project folder`,\n });\n return {\n files: files.map((f) => ({\n ...f,\n status: FILE_VALIDATION_STATUS.VALIDATION_FAILED,\n statusMessage: 'Unbuilt project detected',\n })),\n validFiles: [],\n errors,\n warnings: [],\n canDeploy: false,\n };\n }\n }\n\n // Check file count limit\n if (files.length > config.maxFilesCount) {\n const issue: ValidationIssue = {\n file: `(${files.length} files)`,\n message: `File count (${files.length}) exceeds limit of ${config.maxFilesCount}`,\n };\n errors.push(issue);\n\n return {\n files: files.map((f) => ({\n ...f,\n status: FILE_VALIDATION_STATUS.VALIDATION_FAILED,\n statusMessage: issue.message,\n })),\n validFiles: [],\n errors,\n warnings: [],\n canDeploy: false,\n };\n }\n\n // Validate each file\n let totalSize = 0;\n\n for (const file of files) {\n let fileStatus: FileValidationStatusType = FILE_VALIDATION_STATUS.READY;\n let statusMessage = 'Ready for upload';\n\n // Pre-compute filename validation\n const nameValidation = file.name\n ? validateFileName(file.name)\n : { valid: false, reason: 'File name cannot be empty' };\n\n // Check for processing errors\n if (file.status === FILE_VALIDATION_STATUS.PROCESSING_ERROR) {\n fileStatus = FILE_VALIDATION_STATUS.VALIDATION_FAILED;\n statusMessage = file.statusMessage || 'File failed during processing';\n errors.push({\n file: file.name,\n message: statusMessage,\n });\n }\n\n // EMPTY FILE - Warning (not error)\n else if (file.size === 0) {\n fileStatus = FILE_VALIDATION_STATUS.EXCLUDED;\n statusMessage = 'File is empty (0 bytes) and cannot be deployed due to storage limitations';\n warnings.push({\n file: file.name,\n message: statusMessage,\n });\n // Skip other validations for excluded files\n fileStatuses.push({\n ...file,\n status: fileStatus,\n statusMessage,\n });\n continue;\n }\n\n // Negative file size - Error\n else if (file.size < 0) {\n fileStatus = FILE_VALIDATION_STATUS.VALIDATION_FAILED;\n statusMessage = 'File size must be positive';\n errors.push({\n file: file.name,\n message: statusMessage,\n });\n }\n\n // File name validation\n else if (!file.name || file.name.trim().length === 0) {\n fileStatus = FILE_VALIDATION_STATUS.VALIDATION_FAILED;\n statusMessage = 'File name cannot be empty';\n errors.push({\n file: file.name || '(empty)',\n message: statusMessage,\n });\n } else if (file.name.includes('\\0')) {\n fileStatus = FILE_VALIDATION_STATUS.VALIDATION_FAILED;\n statusMessage = 'File name contains invalid characters (null byte)';\n errors.push({\n file: file.name,\n message: statusMessage,\n });\n } else if (!nameValidation.valid) {\n fileStatus = FILE_VALIDATION_STATUS.VALIDATION_FAILED;\n statusMessage = nameValidation.reason || 'Invalid file name';\n errors.push({\n file: file.name,\n message: statusMessage,\n });\n }\n\n // Blocked extension check\n else if (isBlockedExtension(file.name)) {\n fileStatus = FILE_VALIDATION_STATUS.VALIDATION_FAILED;\n statusMessage = `File extension not allowed: \"${file.name}\"`;\n errors.push({\n file: file.name,\n message: statusMessage,\n });\n }\n\n // File size validation\n else if (file.size > config.maxFileSize) {\n fileStatus = FILE_VALIDATION_STATUS.VALIDATION_FAILED;\n statusMessage = `File size (${formatFileSize(file.size)}) exceeds limit of ${formatFileSize(config.maxFileSize)}`;\n errors.push({\n file: file.name,\n message: statusMessage,\n });\n }\n\n // Total size validation\n else {\n totalSize += file.size;\n if (totalSize > config.maxTotalSize) {\n fileStatus = FILE_VALIDATION_STATUS.VALIDATION_FAILED;\n statusMessage = `Total size would exceed limit of ${formatFileSize(config.maxTotalSize)}`;\n errors.push({\n file: file.name,\n message: statusMessage,\n });\n }\n }\n\n fileStatuses.push({\n ...file,\n status: fileStatus,\n statusMessage,\n });\n }\n\n // ATOMIC ENFORCEMENT: Two-phase validation for optimal UX + atomic semantics\n // Phase 1 (above): Validate files individually to collect ALL errors\n // Phase 2 (below): Mark all files as failed if any errors exist\n //\n // Why two phases? We validate individually for better UX (users see all problems\n // at once and can fix everything in one pass), then enforce atomicity to maintain\n // deployment transaction semantics (all-or-nothing).\n if (errors.length > 0) {\n fileStatuses = fileStatuses.map((file) => {\n // Keep EXCLUDED files as-is (they're warnings, not errors)\n if (file.status === FILE_VALIDATION_STATUS.EXCLUDED) {\n return file;\n }\n\n // Mark ALL other files as VALIDATION_FAILED (atomic deployment)\n return {\n ...file,\n status: FILE_VALIDATION_STATUS.VALIDATION_FAILED,\n statusMessage:\n file.status === FILE_VALIDATION_STATUS.VALIDATION_FAILED\n ? file.statusMessage // Keep original error message for the file that actually failed\n : 'Deployment failed due to validation errors in bundle',\n };\n });\n }\n\n // Build atomic result\n // validFiles is empty if ANY errors exist (all-or-nothing)\n const validFiles =\n errors.length === 0\n ? fileStatuses.filter((f) => f.status === FILE_VALIDATION_STATUS.READY)\n : [];\n const canDeploy = errors.length === 0;\n\n return {\n files: fileStatuses,\n validFiles,\n errors,\n warnings,\n canDeploy,\n };\n}\n\n/**\n * Get only the valid files from validation results\n */\nexport function getValidFiles<T extends ValidatableFile>(files: T[]): T[] {\n return files.filter((f) => f.status === FILE_VALIDATION_STATUS.READY);\n}\n\n/**\n * Check if all valid files have required properties for upload\n * (Can be extended to check for MD5, etc.)\n */\nexport function allValidFilesReady<T extends ValidatableFile>(files: T[]): boolean {\n const validFiles = getValidFiles(files);\n return validFiles.length > 0;\n}\n","const ignoreList = [\n\t// # All\n\t'^npm-debug\\\\.log$', // Error log for npm\n\t'^\\\\..*\\\\.swp$', // Swap file for vim state\n\n\t// # macOS\n\t'^\\\\.DS_Store$', // Stores custom folder attributes\n\t'^\\\\.AppleDouble$', // Stores additional file resources\n\t'^\\\\.LSOverride$', // Contains the absolute path to the app to be used\n\t'^Icon\\\\r$', // Custom Finder icon: http://superuser.com/questions/298785/icon-file-on-os-x-desktop\n\t'^\\\\._.*', // Thumbnail\n\t'^\\\\.Spotlight-V100(?:$|\\\\/)', // Directory that might appear on external disk\n\t'\\\\.Trashes', // File that might appear on external disk\n\t'^__MACOSX$', // Resource fork\n\n\t// # Linux\n\t'~$', // Backup file\n\n\t// # Windows\n\t'^Thumbs\\\\.db$', // Image file cache\n\t'^ehthumbs\\\\.db$', // Folder config file\n\t'^[Dd]esktop\\\\.ini$', // Stores custom folder attributes\n\t'@eaDir$', // Synology Diskstation \"hidden\" folder where the server stores thumbnails\n];\n\nexport const junkRegex = new RegExp(ignoreList.join('|'));\n\nexport function isJunk(filename) {\n\treturn junkRegex.test(filename);\n}\n\nexport function isNotJunk(filename) {\n\treturn !isJunk(filename);\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 */\n\nimport { hasUnbuiltMarker, ShipError } from '@shipstatic/types';\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 = ['__MACOSX', '.Trashes', '.fseventsd', '.Spotlight-V100'] as const;\n\n/**\n * Filters an array of file paths, removing those considered junk\n *\n * Throws if any path contains an unbuilt project marker (e.g. `node_modules`, `package.json`).\n * This check runs first because the dot-file filter below would strip paths like\n * `node_modules/.pnpm/...`, destroying the evidence.\n *\n * A path is filtered out if any of these conditions are met:\n * 1. The basename is identified as junk by the 'junk' package (e.g., .DS_Store, Thumbs.db)\n * 2. Any path segment starts with a dot (e.g., .env, .git, .htaccess)\n * Exception: `.well-known` is allowed (RFC 8615 — ACME, security.txt, app links)\n * 3. Any path segment exceeds 255 characters (filesystem limit)\n * 4. 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 * Dot files are filtered for security — they typically contain sensitive configuration\n * (.env, .git) or are not meant to be served publicly. This matches server-side filtering.\n *\n * @param filePaths - An array of file path strings to filter\n * @param options - Optional settings\n * @param options.allowUnbuilt - When true, skip the unbuilt project marker check (for server-processed uploads)\n * @returns A new array containing only non-junk file paths\n * @throws {ShipError} If any path contains an unbuilt project marker (unless allowUnbuilt is true)\n *\n * @example\n * ```typescript\n * import { filterJunk } from '@shipstatic/ship';\n *\n * // Filter an array of file paths\n * const paths = ['index.html', '.DS_Store', '.gitattributes', '__MACOSX/file.txt', 'app.js'];\n * const clean = filterJunk(paths);\n * // Result: ['index.html', 'app.js']\n * ```\n *\n * @example\n * ```typescript\n * // Use with browser File objects\n * import { filterJunk } from '@shipstatic/ship';\n *\n * const files: File[] = [...]; // From input or drag-drop\n *\n * // Extract paths from File objects\n * const filePaths = files.map(f => f.webkitRelativePath || f.name);\n *\n * // Filter out junk paths\n * const validPaths = new Set(filterJunk(filePaths));\n *\n * // Filter the original File array\n * const validFiles = files.filter(f =>\n * validPaths.has(f.webkitRelativePath || f.name)\n * );\n * ```\n */\nexport function filterJunk(filePaths: string[], options?: { allowUnbuilt?: boolean }): string[] {\n if (!filePaths || filePaths.length === 0) {\n return [];\n }\n\n // Reject unbuilt projects before the dot-file filter removes evidence.\n // pnpm stores files under node_modules/.pnpm/ — the dot-file filter below\n // strips .pnpm/ paths, destroying the only signal that this is an unbuilt project.\n if (!options?.allowUnbuilt) {\n const marker = filePaths.find((p) => p && hasUnbuiltMarker(p));\n if (marker) {\n throw ShipError.business(\n 'Unbuilt project detected — deploy your build output (dist/, build/, out/), not the project folder',\n );\n }\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 // Filter out dot files and directories (security: prevents .env, .git, etc.)\n // .well-known is not junk — it's a standard directory (RFC 8615)\n // Path position constraints enforced at upload (buildFileKey) and serving (isBlockedDotFile)\n for (const part of parts) {\n if (part === '.well-known') continue;\n if (part.startsWith('.') || part.length > 255) {\n return false;\n }\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) => segment.toLowerCase() === junkDir.toLowerCase())) {\n return false;\n }\n }\n\n return true;\n });\n}\n","/**\n * @file Shared security validation for the deploy pipeline.\n * Used by both Node.js and browser file processing pipelines.\n */\nimport { isBlockedExtension, ShipError } from '@shipstatic/types';\nimport { validateFileName } from './file-validation.js';\n\n/**\n * Validate a deploy path for security concerns.\n * Rejects paths containing path traversal patterns or null bytes.\n *\n * Checks for:\n * - Null bytes (\\0) — path injection\n * - /../ — directory traversal within path\n * - ../ at start — upward traversal\n * - /.. at end — trailing traversal\n *\n * Does NOT reject double dots in filenames (e.g., \"foo..bar.txt\" is safe).\n *\n * @param deployPath - The deployment path to validate\n * @param sourceIdentifier - Human-readable identifier for error messages\n * @throws {ShipError} If the path contains unsafe patterns\n */\nexport function validateDeployPath(deployPath: string, sourceIdentifier: string): void {\n if (\n deployPath.includes('\\0') ||\n deployPath.includes('/../') ||\n deployPath.startsWith('../') ||\n deployPath.endsWith('/..')\n ) {\n throw ShipError.business(\n `Security error: Unsafe file path \"${deployPath}\" for file: ${sourceIdentifier}`,\n );\n }\n}\n\n/**\n * Validate a deploy file's name and extension.\n * Rejects unsafe filenames (shell/URL-dangerous chars, reserved names)\n * and blocked file extensions (.exe, .msi, .dll, etc.).\n *\n * @param deployPath - The deployment path to validate\n * @param sourceIdentifier - Human-readable identifier for error messages\n * @throws {ShipError} If the filename is unsafe or extension is blocked\n */\nexport function validateDeployFile(deployPath: string, sourceIdentifier: string): void {\n const nameCheck = validateFileName(deployPath);\n if (!nameCheck.valid) {\n throw ShipError.business(nameCheck.reason || 'Invalid file name');\n }\n\n if (isBlockedExtension(deployPath)) {\n throw ShipError.business(`File extension not allowed: \"${sourceIdentifier}\"`);\n }\n}\n","/**\n * @file Browser-specific file utilities for the Ship SDK.\n * Provides helpers for processing browser files into deploy-ready objects.\n *\n * Two modes:\n * - **Deploy** (default): Full validation pipeline — security, extensions, sizes, counts.\n * - **Server-processed** (build/prerender): Source files destined for server-side build.\n * Junk filtering and MD5 checksums only — the build service validates the output.\n *\n * Both modes share: environment check → extract paths → optimize paths → filter junk → MD5.\n */\nimport type { PlatformLimits } from '@shipstatic/types';\nimport { ShipError } from '@shipstatic/types';\nimport { optimizeDeployPaths } from '../../shared/lib/deploy-paths.js';\nimport { getENV } from '../../shared/lib/env.js';\nimport { filterJunk } from '../../shared/lib/junk.js';\nimport { calculateMD5 } from '../../shared/lib/md5.js';\nimport { validateDeployFile, validateDeployPath } from '../../shared/lib/security.js';\nimport type { DeploymentOptions, StaticFile } from '../../shared/types.js';\n\n/**\n * Processes browser files into an array of StaticFile objects ready for deploy.\n * Calculates MD5, filters junk files, validates sizes, and applies path optimization.\n *\n * For server-processed uploads (build/prerender), client-side deploy validation is\n * skipped — the build service produces and validates the actual deployment output.\n *\n * @param browserFiles - File[] to process for deploy.\n * @param options - Processing options including pathDetect for automatic path optimization.\n * @param platformLimits - Per-instance platform limits (file-size / count / total-size caps)\n * from the originating Ship's `GET /limits` fetch. Passed in rather than read from a\n * module global so concurrent Ships against different API URLs cannot clobber each\n * other's caps.\n * @returns Promise resolving to an array of StaticFile objects.\n * @throws {ShipError} If called outside a browser or with invalid input.\n */\nexport async function processFilesForBrowser(\n browserFiles: File[],\n options: DeploymentOptions = {},\n platformLimits?: PlatformLimits,\n): Promise<StaticFile[]> {\n // 1. Environment check\n if (getENV() !== 'browser') {\n throw ShipError.business('processFilesForBrowser can only be called in a browser environment.');\n }\n\n // 2. Extract raw paths from File objects\n const rawPaths = browserFiles.map((file) => file.webkitRelativePath || file.name);\n\n // Server-processed uploads (build/prerender) send source files, not deploy output\n const isServerProcessed = options.build || options.prerender;\n\n // 3. Optimize paths for deployment (strip common root, flatten)\n const deployFiles = optimizeDeployPaths(rawPaths, { flatten: options.pathDetect !== false });\n const deployPaths = deployFiles.map((f) => f.path);\n\n // 4. Filter junk from deploy paths (allowUnbuilt for server-processed)\n const filteredSet = new Set(filterJunk(deployPaths, { allowUnbuilt: isServerProcessed }));\n const validPairs: Array<{ file: File; deployPath: string }> = [];\n for (let i = 0; i < browserFiles.length; i++) {\n if (filteredSet.has(deployPaths[i])) {\n validPairs.push({ file: browserFiles[i], deployPath: deployFiles[i].path });\n }\n }\n\n if (validPairs.length === 0) {\n return [];\n }\n\n // 5. Server-processed: skip deploy validation, just compute checksums\n if (isServerProcessed) {\n const results: StaticFile[] = [];\n for (let i = 0; i < validPairs.length; i++) {\n const { file, deployPath } = validPairs[i];\n if (file.size === 0) continue;\n const { md5 } = await calculateMD5(file);\n results.push({ path: deployPath, content: file, size: file.size, md5 });\n }\n return results;\n }\n\n // 6. Deploy: full validation pipeline\n if (!platformLimits) {\n throw ShipError.config(\n 'Platform limits not provided. processFilesForBrowser requires the limits ' +\n 'argument for deploy-mode validation — pass `ship.getLimits()` result.',\n );\n }\n const results: StaticFile[] = [];\n let totalSize = 0;\n\n for (let i = 0; i < validPairs.length; i++) {\n const { file, deployPath } = validPairs[i];\n\n // Security validation (shared with Node)\n validateDeployPath(deployPath, file.name);\n\n // Skip empty files — R2 cannot store zero-byte objects\n if (file.size === 0) {\n continue;\n }\n\n // Filename and extension validation (shared with Node)\n validateDeployFile(deployPath, file.name);\n\n // Validate file sizes (matches Node validation)\n if (file.size > platformLimits.maxFileSize) {\n throw ShipError.business(\n `File ${file.name} is too large. Maximum allowed size is ${platformLimits.maxFileSize / (1024 * 1024)}MB.`,\n );\n }\n totalSize += file.size;\n if (totalSize > platformLimits.maxTotalSize) {\n throw ShipError.business(\n `Total deploy size is too large. Maximum allowed is ${platformLimits.maxTotalSize / (1024 * 1024)}MB.`,\n );\n }\n\n // Calculate MD5 hash\n const { md5 } = await calculateMD5(file);\n\n results.push({\n path: deployPath,\n content: file,\n size: file.size,\n md5,\n });\n }\n\n // Validate file count (matches Node validation)\n if (results.length > platformLimits.maxFilesCount) {\n throw ShipError.business(\n `Too many files to deploy. Maximum allowed is ${platformLimits.maxFilesCount} files.`,\n );\n }\n\n return results;\n}\n","/**\n * @file Ship SDK for browser environments.\n *\n * Configuration is fully explicit — the browser has no env vars or config files\n * to inherit. The credential is supplied via the `token` constructor option\n * (or, for first-party browser apps, the cookie session via `session: true`).\n */\n\nimport { ShipError } from '@shipstatic/types';\nimport { Ship as BaseShip } from '../shared/base-ship.js';\nimport type {\n DeployBodyCreator,\n DeployInput,\n DeploymentCreateResponse,\n DeploymentOptions,\n StaticFile,\n} from '../shared/types.js';\nimport { createDeployBody } from './core/deploy-body.js';\n\n// Export all shared functionality\nexport * from '../shared/index.js';\n\n/**\n * Ship SDK Client for browser environments.\n *\n * @example\n * ```typescript\n * // Deploy with a token obtained from your server\n * const ship = new Ship({\n * token: 'deploy-xxxx',\n * apiUrl: 'https://api.shipstatic.com',\n * });\n *\n * const files = Array.from(fileInput.files);\n * await ship.deploy(files);\n * ```\n */\nexport class Ship extends BaseShip {\n // No constructor override — the base class accepts `ShipClientOptions` and\n // browsers have no ambient credential source (no env vars, no filesystem).\n\n /**\n * Deploy `File[]` (typically from `<input type=\"file\">` or drag-and-drop)\n * to ShipStatic. Convenience shortcut for `ship.deployments.upload()`.\n *\n * Wrong-platform inputs (e.g. string paths) fail at compile time. For\n * platform-neutral code, use `ship.deployments.upload()`, which accepts\n * the wider `DeployInput` and validates at runtime — that asymmetry is\n * intentional: the convenience shortcut narrows; the resource-layer\n * contract stays platform-neutral.\n */\n async deploy(input: File[], options?: DeploymentOptions): Promise<DeploymentCreateResponse> {\n return super.deploy(input, options);\n }\n\n protected async processInput(\n input: DeployInput,\n options: DeploymentOptions,\n ): Promise<StaticFile[]> {\n if (!Array.isArray(input) || !input.every((item) => item instanceof File)) {\n throw ShipError.business('Invalid input type for browser environment. Expected File[].');\n }\n\n if (input.length === 0) {\n throw ShipError.business('No files to deploy.');\n }\n\n const { processFilesForBrowser } = await import('./core/browser-files.js');\n return processFilesForBrowser(input, options, this.platformLimits ?? undefined);\n }\n\n protected getDeployBodyCreator(): DeployBodyCreator {\n return createDeployBody;\n }\n}\n\n// Default export (for `import Ship from '@shipstatic/ship'`)\nexport default Ship;\n\n// Browser-only utilities (validation + MD5 over `File` / `Blob` inputs)\nexport { processFilesForBrowser } from './core/browser-files.js';\n","/**\n * @file Base Ship SDK class — shared functionality across environments.\n *\n * The constructor is fully synchronous: an `ApiHttp` instance is built immediately\n * with whatever credentials the caller supplied (and, in Node, env vars merged in\n * by the subclass before `super()`). The only deferred work is the one-shot\n * `GET /limits` fetch that hydrates platform limits — that's lazy and runs on\n * first API call via `ensureInitialized()`.\n *\n * Subclasses only override what genuinely differs per environment:\n * - `processInput()` — Node reads paths from disk; Browser handles `File[]`\n * - `getDeployBodyCreator()` — Node streams Buffers; Browser builds Blobs\n *\n * Everything else (the credential slot, resources, events, lazy platform-limits)\n * lives here.\n */\n\nimport type {\n AccountResource,\n DeploymentCreateResponse,\n DeploymentResource,\n DomainResource,\n PlatformLimits,\n StaticFile,\n TokenResource,\n} from '@shipstatic/types';\nimport { ShipError, validateCaller, validateToken } from '@shipstatic/types';\n\nimport { ApiHttp } from './api/http.js';\nimport {\n createAccountResource,\n createDeploymentResource,\n createDomainResource,\n createTokenResource,\n type DeployInput,\n} from './resources.js';\nimport type {\n DeployBodyCreator,\n DeploymentOptions,\n ShipClientOptions,\n ShipEvents,\n TokenProvider,\n} from './types.js';\n\n/**\n * Abstract base class for Ship SDK implementations.\n */\nexport abstract class Ship {\n // Resource handles, created once at construction. Each is a thin facade\n // bound to `this.http` plus the lazy-init callback.\n // Parameterized with the SDK's extended options (timeout, callbacks,\n // signal…) — the interface's documented extension point, so typed\n // consumers can pass them without casts.\n public readonly deployments: DeploymentResource<DeploymentOptions>;\n public readonly domains: DomainResource;\n public readonly account: AccountResource;\n public readonly tokens: TokenResource;\n\n // The HTTP client and merged options are private — subclasses interact\n // with the base class through the abstract methods below, never by\n // reaching into these fields. Tests bypass via `(ship as any).http = ...`.\n private readonly http: ApiHttp;\n private readonly clientOptions: ShipClientOptions;\n\n // Lazy-init plumbing for the one-shot `GET /limits` fetch.\n // `platformLimits` is INSTANCE state (not a module-level singleton): two\n // Ships against different `apiUrl`s — staging + prod, multi-tenant\n // orchestrators, n8n with multiple credentials — must not clobber each\n // other's limits. Each instance owns its hydrated copy.\n // `protected` so subclasses' `processInput` can pass it down to the\n // platform-specific file-validation utilities.\n private initPromise: Promise<void> | null = null;\n protected platformLimits: PlatformLimits | null = null;\n\n // The credential slot — one platform token (any population) or a provider\n // that supplies one per request. Read dynamically on every request through\n // `getAuthHeaders`, so `setToken` takes effect without rebuilding the client.\n private credential: string | TokenProvider | null = null;\n\n constructor(options: ShipClientOptions = {}) {\n // SDK-boundary normalization: an empty-string token is absence of\n // credential intent, never a credential. Empty strings reach here from\n // shell-expansion of unset CI variables, empty form fields in browser\n // apps, and any other path that produces `''` instead of `undefined`.\n // Normalizing once at the SDK boundary covers every entry point: CLI,\n // Browser SDK, Node SDK, embedded consumers, and direct base-class use.\n options = {\n ...options,\n apiUrl: options.apiUrl || undefined,\n token: options.token || undefined,\n caller: options.caller || undefined,\n };\n this.clientOptions = options;\n\n // Caller identity is validated at the boundary like the token: a value\n // the API would silently drop (the header is unauthenticated) is a\n // configuration error here, never a quiet fallback to IP bucketing.\n if (options.caller !== undefined) {\n validateCaller(options.caller);\n }\n\n // One client, one identity. A token and a cookie session are different\n // principals — holding both is a configuration error, not a precedence\n // question.\n if (options.token && options.session) {\n throw ShipError.config('Provide either `token` or `session`, not both.');\n }\n\n // Static tokens are validated at the boundary (prefix-classified, same\n // rules the server applies); providers are invoked per request instead.\n if (typeof options.token === 'string') {\n validateToken(options.token);\n this.credential = options.token;\n } else if (options.token) {\n this.credential = options.token;\n }\n\n // Build the HTTP client once. The `getAuthHeaders` callback reads\n // `this.credential` dynamically on every request.\n this.http = new ApiHttp({\n ...options,\n getAuthHeaders: () => this.getAuthHeaders(),\n createDeployBody: this.getDeployBodyCreator(),\n });\n\n const ctx = {\n getApi: () => this.http,\n ensureInit: () => this.ensureInitialized(),\n };\n\n this.deployments = createDeploymentResource({\n ...ctx,\n processInput: (input, opts) => this.processInput(input, opts),\n });\n this.domains = createDomainResource(ctx);\n this.account = createAccountResource(ctx);\n this.tokens = createTokenResource(ctx);\n }\n\n // Environment-specific behavior.\n protected abstract processInput(\n input: DeployInput,\n options: DeploymentOptions,\n ): Promise<StaticFile[]>;\n protected abstract getDeployBodyCreator(): DeployBodyCreator;\n\n /**\n * Lazy initialization — fetches platform limits (file size / count caps) once,\n * on the first API call. Subsequent calls reuse the resolved promise.\n */\n protected async ensureInitialized(): Promise<void> {\n if (!this.initPromise) {\n this.initPromise = this.fetchPlatformLimits();\n }\n return this.initPromise;\n }\n\n private async fetchPlatformLimits(): Promise<void> {\n try {\n this.platformLimits = await this.http.getLimits();\n } catch (error) {\n // Reset so the next API call can retry initialization.\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 await this.ensureInitialized();\n return this.http.ping();\n }\n\n /**\n * Deploy project (convenience shortcut to `ship.deployments.upload()`).\n */\n async deploy(input: DeployInput, options?: DeploymentOptions): Promise<DeploymentCreateResponse> {\n return this.deployments.upload(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 platform limits (max file size, file count, total size).\n * Reuses the response fetched during initialization. Per-instance state —\n * does not leak between concurrent Ships against different API URLs.\n */\n async getLimits(): Promise<PlatformLimits> {\n if (this.platformLimits) return this.platformLimits;\n await this.ensureInitialized();\n // biome-ignore lint/style/noNonNullAssertion: ensureInitialized() hydrates platformLimits or throws\n return this.platformLimits!;\n }\n\n on<K extends keyof ShipEvents>(event: K, handler: (...args: ShipEvents[K]) => void): void {\n this.http.on(event, handler);\n }\n\n off<K extends keyof ShipEvents>(event: K, handler: (...args: ShipEvents[K]) => void): void {\n this.http.off(event, handler);\n }\n\n /**\n * Set global headers included in every request.\n * Useful for injecting custom headers (e.g. for admin impersonation).\n */\n setHeaders(headers: Record<string, string>): void {\n this.http.setGlobalHeaders(headers);\n }\n\n /**\n * Clear all custom global headers.\n */\n clearHeaders(): void {\n this.http.setGlobalHeaders({});\n }\n\n /**\n * Sets the client token — any platform token (API key, deploy token, OAuth\n * access token) or a {@link TokenProvider} invoked per request. Replaces\n * whatever credential the client held before.\n * @param token A platform token, sent verbatim, or a provider function\n */\n public setToken(token: string | TokenProvider): void {\n // One client, one identity — the constructor's token/session exclusion\n // holds for the client's whole life, not just its first moment.\n if (this.clientOptions.session) {\n throw ShipError.config('Provide either `token` or `session`, not both.');\n }\n if (typeof token === 'string') {\n if (!token) {\n throw ShipError.business('Invalid token provided. Token must be a non-empty string.');\n }\n validateToken(token);\n this.credential = token;\n return;\n }\n if (typeof token !== 'function') {\n throw ShipError.business(\n 'Invalid token provided. Token must be a non-empty string or a provider function.',\n );\n }\n this.credential = token;\n }\n\n /**\n * Resolve the credential slot into request headers. Async because a\n * provider may mint or refresh its token per request.\n *\n * Anonymity requires proven absence of credentials: a configured provider\n * that yields nothing is an error — the request fails typed rather than\n * silently proceeding as an anonymous public deploy. Empty-string\n * normalization at the constructor is the same invariant's boundary\n * condition: `''` is absence of intent, so it never reaches this point.\n */\n private async getAuthHeaders(): Promise<Record<string, string>> {\n if (this.credential === null) return {};\n const value = typeof this.credential === 'function' ? await this.credential() : this.credential;\n if (!value) {\n throw ShipError.authentication('Token provider returned no token.');\n }\n if (typeof value !== 'string') {\n throw ShipError.authentication('Token provider returned a non-string value.');\n }\n return { Authorization: `Bearer ${value}` };\n }\n}\n","/**\n * @file HTTP client for Ship API.\n */\nimport type {\n AccountGetResponse,\n Deployment,\n DeploymentCreateResponse,\n DeploymentListResponse,\n Domain,\n DomainDnsResponse,\n DomainListResponse,\n DomainRecordsResponse,\n DomainValidateResponse,\n ListOptions,\n PingResponse,\n PlatformLimits,\n SPACheckRequest,\n SPACheckResponse,\n StaticFile,\n TokenCreateResponse,\n TokenListResponse,\n} from '@shipstatic/types';\nimport { DEFAULT_API, ShipError } from '@shipstatic/types';\nimport { SimpleEvents } from '../events.js';\nimport { validateLabels, validatePassword } from '../lib/validation.js';\nimport type {\n ApiDeployOptions,\n DeployBodyCreator,\n DomainSetResult,\n Fetch,\n ShipClientOptions,\n} from '../types.js';\n\n// =============================================================================\n// CONSTANTS\n// =============================================================================\n\nconst ENDPOINTS = {\n DEPLOYMENTS: '/deployments',\n DOMAINS: '/domains',\n TOKENS: '/tokens',\n ACCOUNT: '/account',\n LIMITS: '/limits',\n PING: '/ping',\n SPA_CHECK: '/spa-check',\n} as const;\n\nconst DEFAULT_REQUEST_TIMEOUT = 30000;\n\n/**\n * Serialize pagination options into a query string, or '' when there are\n * none — the paginated list endpoints accept `limit` and `cursor`.\n */\nfunction listQuery(options?: ListOptions): string {\n const params = new URLSearchParams();\n if (options?.limit !== undefined) params.set('limit', String(options.limit));\n if (options?.cursor !== undefined) params.set('cursor', options.cursor);\n const query = params.toString();\n return query ? `?${query}` : '';\n}\n\n// =============================================================================\n// TYPES\n// =============================================================================\n\nexport interface ApiHttpOptions extends ShipClientOptions {\n /** Resolves the credential slot per request — async so token providers can mint/refresh. */\n getAuthHeaders: () => Record<string, string> | Promise<Record<string, string>>;\n createDeployBody: DeployBodyCreator;\n}\n\ninterface RequestResult<T> {\n data: T;\n status: number;\n}\n\n// =============================================================================\n// HTTP CLIENT\n// =============================================================================\n\nexport class ApiHttp extends SimpleEvents {\n private readonly apiUrl: string;\n private readonly getAuthHeadersCallback: () =>\n | Record<string, string>\n | Promise<Record<string, string>>;\n private readonly session: boolean;\n private readonly caller: string | undefined;\n private readonly timeout: number;\n private readonly fetch: Fetch;\n private readonly createDeployBody: DeployBodyCreator;\n private readonly deployEndpoint: string;\n private globalHeaders: Record<string, string> = {};\n\n constructor(options: ApiHttpOptions) {\n super();\n this.apiUrl = options.apiUrl || DEFAULT_API;\n this.getAuthHeadersCallback = options.getAuthHeaders;\n this.session = options.session ?? false;\n this.caller = options.caller;\n this.timeout = options.timeout ?? DEFAULT_REQUEST_TIMEOUT;\n // Bind to globalThis when falling back to the platform `fetch` — browsers\n // require `this === window` on `window.fetch` and throw \"Illegal invocation\"\n // when it's invoked as a property of any other object.\n this.fetch = options.fetch ?? globalThis.fetch.bind(globalThis);\n this.createDeployBody = options.createDeployBody;\n this.deployEndpoint = options.deployEndpoint || ENDPOINTS.DEPLOYMENTS;\n }\n\n /**\n * Set global headers included in every request.\n * Priority: globalHeaders (lowest) < instance auth < per-request headers (highest)\n */\n setGlobalHeaders(headers: Record<string, string>): void {\n this.globalHeaders = headers;\n }\n\n // ===========================================================================\n // CORE REQUEST INFRASTRUCTURE\n // ===========================================================================\n\n /**\n * Execute HTTP request with timeout, events, and error handling\n */\n private async executeRequest<T>(\n url: string,\n options: RequestInit,\n operationName: string,\n ): Promise<RequestResult<T>> {\n let cleanup = () => {};\n\n try {\n // Credential resolution runs inside the error boundary: a token\n // provider that throws or yields nothing fails the request through\n // the same typed path (and `error` event) as any transport failure.\n const headers = await this.mergeHeaders(options.headers as Record<string, string>);\n const timeout = this.createTimeoutSignal(options.signal);\n cleanup = timeout.cleanup;\n\n const fetchOptions: RequestInit = {\n ...options,\n headers,\n credentials: this.session && !headers.Authorization ? 'include' : undefined,\n signal: timeout.signal,\n };\n\n this.emit('request', url, fetchOptions);\n\n const response = await this.fetch(url, fetchOptions);\n cleanup();\n\n if (!response.ok) {\n throw await ShipError.fromHttpResponse(response, operationName);\n }\n\n this.emit('response', this.safeClone(response), url);\n const data = await this.parseResponse<T>(this.safeClone(response));\n return { data, status: response.status };\n } catch (error) {\n cleanup();\n // Normalize anything thrown above (credential resolution, fetch\n // failure, abort, response error) into a ShipError.\n // fromFetchError passes existing ShipErrors through unchanged.\n const shipError = ShipError.fromFetchError(error, operationName);\n this.emit('error', shipError, url);\n throw shipError;\n }\n }\n\n /**\n * Simple request - returns data only\n */\n private async request<T>(url: string, options: RequestInit, operationName: string): Promise<T> {\n const { data } = await this.executeRequest<T>(url, options, operationName);\n return data;\n }\n\n /**\n * Request with status - returns data and HTTP status code\n */\n private async requestWithStatus<T>(\n url: string,\n options: RequestInit,\n operationName: string,\n ): Promise<RequestResult<T>> {\n return this.executeRequest<T>(url, options, operationName);\n }\n\n // ===========================================================================\n // REQUEST HELPERS\n // ===========================================================================\n\n private async mergeHeaders(\n customHeaders: Record<string, string> = {},\n ): Promise<Record<string, string>> {\n // `caller` is instance identity metadata, like the credential: the\n // rate limiter buckets by X-Caller on every write, so it rides every\n // request rather than any single operation.\n return {\n ...this.globalHeaders,\n ...(this.caller ? { 'X-Caller': this.caller } : {}),\n ...(await this.getAuthHeadersCallback()),\n ...customHeaders,\n };\n }\n\n private createTimeoutSignal(existingSignal?: AbortSignal | null): {\n signal: AbortSignal;\n cleanup: () => void;\n } {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), this.timeout);\n\n if (existingSignal) {\n const abort = () => controller.abort();\n existingSignal.addEventListener('abort', abort);\n if (existingSignal.aborted) controller.abort();\n }\n\n return {\n signal: controller.signal,\n cleanup: () => clearTimeout(timeoutId),\n };\n }\n\n private safeClone(response: Response): Response {\n try {\n return response.clone();\n } catch {\n return response;\n }\n }\n\n private async parseResponse<T>(response: Response): Promise<T> {\n if (response.headers.get('Content-Length') === '0' || response.status === 204) {\n return undefined as T;\n }\n return response.json() as Promise<T>;\n }\n\n // ===========================================================================\n // PUBLIC API - DEPLOYMENTS\n // ===========================================================================\n\n async deploy(\n files: StaticFile[],\n options: ApiDeployOptions = {},\n ): Promise<DeploymentCreateResponse> {\n if (!files.length) {\n throw ShipError.business('No files to deploy');\n }\n for (const file of files) {\n if (!file.md5) {\n throw ShipError.file(`MD5 checksum missing for file: ${file.path}`, {\n filePath: file.path,\n });\n }\n }\n\n // Fast-fail on definitely-invalid input before constructing a multipart body.\n validatePassword(options.password);\n const labels = validateLabels(options.labels);\n\n const flags =\n options.build || options.prerender || options.spa\n ? { build: options.build, prerender: options.prerender, spa: options.spa }\n : undefined;\n const { body, headers: bodyHeaders } = await this.createDeployBody(files, {\n labels,\n via: options.via,\n password: options.password,\n flags,\n captcha: options.captcha,\n });\n\n return this.request<DeploymentCreateResponse>(\n `${this.apiUrl}${this.deployEndpoint}`,\n { method: 'POST', body, headers: bodyHeaders, signal: options.signal || null },\n 'Deploy',\n );\n }\n\n async listDeployments(options?: ListOptions): Promise<DeploymentListResponse> {\n return this.request(\n `${this.apiUrl}${ENDPOINTS.DEPLOYMENTS}${listQuery(options)}`,\n { method: 'GET' },\n 'List deployments',\n );\n }\n\n async getDeployment(id: string): Promise<Deployment> {\n return this.request(\n `${this.apiUrl}${ENDPOINTS.DEPLOYMENTS}/${encodeURIComponent(id)}`,\n { method: 'GET' },\n 'Get deployment',\n );\n }\n\n async updateDeploymentLabels(id: string, labels: string[]): Promise<Deployment> {\n const normalized = validateLabels(labels);\n return this.request(\n `${this.apiUrl}${ENDPOINTS.DEPLOYMENTS}/${encodeURIComponent(id)}`,\n {\n method: 'PATCH',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ labels: normalized }),\n },\n 'Update deployment labels',\n );\n }\n\n async removeDeployment(id: string): Promise<void> {\n await this.request<void>(\n `${this.apiUrl}${ENDPOINTS.DEPLOYMENTS}/${encodeURIComponent(id)}`,\n { method: 'DELETE' },\n 'Remove deployment',\n );\n }\n\n // ===========================================================================\n // PUBLIC API - DOMAINS\n // ===========================================================================\n // All domain methods accept FQDN (Fully Qualified Domain Name) as the `name` parameter.\n // The SDK does not validate or normalize - the API handles all domain semantics.\n\n async setDomain(name: string, deployment?: string, labels?: string[]): Promise<DomainSetResult> {\n const normalized = validateLabels(labels);\n const body: { deployment?: string; labels?: string[] } = {};\n if (deployment) body.deployment = deployment;\n if (normalized !== undefined) body.labels = normalized;\n\n const { data, status } = await this.requestWithStatus<Domain>(\n `${this.apiUrl}${ENDPOINTS.DOMAINS}/${encodeURIComponent(name)}`,\n {\n method: 'PUT',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(body),\n },\n 'Set domain',\n );\n\n return { ...data, isCreate: status === 201 };\n }\n\n async listDomains(options?: ListOptions): Promise<DomainListResponse> {\n return this.request(\n `${this.apiUrl}${ENDPOINTS.DOMAINS}${listQuery(options)}`,\n { method: 'GET' },\n 'List domains',\n );\n }\n\n async getDomain(name: string): Promise<Domain> {\n return this.request(\n `${this.apiUrl}${ENDPOINTS.DOMAINS}/${encodeURIComponent(name)}`,\n { method: 'GET' },\n 'Get domain',\n );\n }\n\n async removeDomain(name: string): Promise<void> {\n await this.request<void>(\n `${this.apiUrl}${ENDPOINTS.DOMAINS}/${encodeURIComponent(name)}`,\n { method: 'DELETE' },\n 'Remove domain',\n );\n }\n\n async verifyDomain(name: string): Promise<{ message: string }> {\n return this.request(\n `${this.apiUrl}${ENDPOINTS.DOMAINS}/${encodeURIComponent(name)}/verify`,\n { method: 'POST' },\n 'Verify domain',\n );\n }\n\n async getDomainDns(name: string): Promise<DomainDnsResponse> {\n return this.request(\n `${this.apiUrl}${ENDPOINTS.DOMAINS}/${encodeURIComponent(name)}/dns`,\n { method: 'GET' },\n 'Get domain DNS',\n );\n }\n\n async getDomainRecords(name: string): Promise<DomainRecordsResponse> {\n return this.request(\n `${this.apiUrl}${ENDPOINTS.DOMAINS}/${encodeURIComponent(name)}/records`,\n { method: 'GET' },\n 'Get domain records',\n );\n }\n\n async getDomainShare(name: string): Promise<{ domain: string; hash: string }> {\n return this.request(\n `${this.apiUrl}${ENDPOINTS.DOMAINS}/${encodeURIComponent(name)}/share`,\n { method: 'GET' },\n 'Get domain share',\n );\n }\n\n async validateDomain(name: string): Promise<DomainValidateResponse> {\n return this.request(\n `${this.apiUrl}${ENDPOINTS.DOMAINS}/validate`,\n {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ domain: name }),\n },\n 'Validate domain',\n );\n }\n\n // ===========================================================================\n // PUBLIC API - TOKENS\n // ===========================================================================\n\n async createToken(ttl?: number, labels?: string[]): Promise<TokenCreateResponse> {\n const normalized = validateLabels(labels);\n const body: { ttl?: number; labels?: string[] } = {};\n if (ttl !== undefined) body.ttl = ttl;\n if (normalized !== undefined) body.labels = normalized;\n\n return this.request(\n `${this.apiUrl}${ENDPOINTS.TOKENS}`,\n {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(body),\n },\n 'Create token',\n );\n }\n\n async listTokens(): Promise<TokenListResponse> {\n return this.request(`${this.apiUrl}${ENDPOINTS.TOKENS}`, { method: 'GET' }, 'List tokens');\n }\n\n async removeToken(token: string): Promise<void> {\n await this.request<void>(\n `${this.apiUrl}${ENDPOINTS.TOKENS}/${encodeURIComponent(token)}`,\n { method: 'DELETE' },\n 'Remove token',\n );\n }\n\n // ===========================================================================\n // PUBLIC API - ACCOUNT & CONFIG\n // ===========================================================================\n\n async getAccount(): Promise<AccountGetResponse> {\n return this.request(`${this.apiUrl}${ENDPOINTS.ACCOUNT}`, { method: 'GET' }, 'Get account');\n }\n\n async getLimits(): Promise<PlatformLimits> {\n return this.request(`${this.apiUrl}${ENDPOINTS.LIMITS}`, { method: 'GET' }, 'Get limits');\n }\n\n async ping(): Promise<boolean> {\n const data = await this.request<PingResponse>(\n `${this.apiUrl}${ENDPOINTS.PING}`,\n { method: 'GET' },\n 'Ping',\n );\n return data?.success || false;\n }\n\n // ===========================================================================\n // PUBLIC API - SPA CHECK\n // ===========================================================================\n\n async checkSPA(files: StaticFile[], _options: ApiDeployOptions = {}): Promise<boolean> {\n const indexFile = files.find((f) => f.path === 'index.html' || f.path === '/index.html');\n if (!indexFile || indexFile.size > 100 * 1024) {\n return false;\n }\n\n let indexContent: string;\n if (typeof Buffer !== 'undefined' && Buffer.isBuffer(indexFile.content)) {\n indexContent = indexFile.content.toString('utf-8');\n } else if (typeof Blob !== 'undefined' && indexFile.content instanceof Blob) {\n indexContent = await indexFile.content.text();\n } else if (typeof File !== 'undefined' && indexFile.content instanceof File) {\n indexContent = await indexFile.content.text();\n } else {\n return false;\n }\n\n const body: SPACheckRequest = { files: files.map((f) => f.path), index: indexContent };\n const response = await this.request<SPACheckResponse>(\n `${this.apiUrl}${ENDPOINTS.SPA_CHECK}`,\n {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(body),\n },\n 'SPA check',\n );\n\n return response.isSPA;\n }\n}\n","/**\n * Event system for Ship SDK\n * Lightweight, reliable event handling with proper error boundaries\n */\n\nimport type { ShipEvents } from './types.js';\n\n/**\n * Lightweight typed event emitter.\n *\n * Public API: `on()` / `off()`. `emit()` is internal — only the SDK\n * publishes events. Throwing handlers are evicted automatically and\n * surfaced as `error` events on the next tick.\n */\nexport class SimpleEvents {\n // biome-ignore lint/complexity/noBannedTypes: the registry is heterogeneous by design — per-event signatures are enforced at the on()/emit() boundary\n private handlers = new Map<string, Set<Function>>();\n\n /**\n * Add event handler\n */\n on<K extends keyof ShipEvents>(event: K, handler: (...args: ShipEvents[K]) => void): void {\n if (!this.handlers.has(event as string)) {\n this.handlers.set(event as string, new Set());\n }\n this.handlers.get(event as string)?.add(handler);\n }\n\n /**\n * Remove event handler\n */\n off<K extends keyof ShipEvents>(event: K, handler: (...args: ShipEvents[K]) => void): void {\n const eventHandlers = this.handlers.get(event as string);\n if (eventHandlers) {\n eventHandlers.delete(handler);\n if (eventHandlers.size === 0) {\n this.handlers.delete(event as string);\n }\n }\n }\n\n /**\n * Emit event (internal use only)\n * @internal\n */\n emit<K extends keyof ShipEvents>(event: K, ...args: ShipEvents[K]): void {\n const eventHandlers = this.handlers.get(event as string);\n if (!eventHandlers) return;\n\n // Snapshot handlers so a handler that mutates the set during iteration\n // (e.g. by removing itself) doesn't skip or duplicate calls.\n const handlerArray = Array.from(eventHandlers);\n\n for (const handler of handlerArray) {\n try {\n handler(...args);\n } catch (error) {\n // A throwing handler is treated as broken — drop it so we don't\n // repeatedly invoke it and re-emit the failure as an `error` event\n // for observability. Defer the re-emit so the next tick has a clean\n // call stack and we can't recurse if the error handler also throws.\n eventHandlers.delete(handler);\n\n if (event !== 'error') {\n setTimeout(() => {\n const err = error instanceof Error ? error : new Error(String(error));\n this.emit('error', err, String(event));\n }, 0);\n }\n }\n }\n }\n}\n","/**\n * @file Client-side input validation for SDK request boundaries.\n *\n * These validators run before request construction. Constants come from\n * `@shipstatic/types` (`LABEL_CONSTRAINTS`, `LABEL_PATTERN`) so the SDK and\n * API agree on the rules.\n */\n\nimport { LABEL_CONSTRAINTS, LABEL_PATTERN, ShipError } from '@shipstatic/types';\n\n// Re-export the canonical password validator from `@shipstatic/types` so\n// existing SDK callers (`http.ts`) keep their `from '../lib/validation.js'`\n// import path unchanged. The types-tier definition is the single source of\n// truth — see `@shipstatic/types/CLAUDE.md` \"Validation: format vs policy\".\nexport { validatePassword } from '@shipstatic/types';\n\n/**\n * Validate and normalize an array of labels.\n *\n * Lowercases and trims each entry, enforces per-label length and pattern\n * (`LABEL_CONSTRAINTS` / `LABEL_PATTERN`), count cap, and uniqueness after\n * normalization. Returns the normalized array. An empty array is valid and\n * signals \"clear all labels\" on label-update operations.\n */\nexport function validateLabels(labels: string[]): string[];\nexport function validateLabels(labels: string[] | undefined | null): string[] | undefined;\nexport function validateLabels(labels: string[] | undefined | null): string[] | undefined {\n if (labels === undefined || labels === null) return undefined;\n if (labels.length === 0) return labels;\n\n if (labels.length > LABEL_CONSTRAINTS.MAX_COUNT) {\n throw ShipError.validation(`Maximum ${LABEL_CONSTRAINTS.MAX_COUNT} labels allowed`);\n }\n\n const normalized = labels.map((label, i) => {\n if (typeof label !== 'string') {\n throw ShipError.validation(`Label at index ${i} must be a string`);\n }\n const cleaned = label.trim().toLowerCase();\n if (cleaned.length < LABEL_CONSTRAINTS.MIN_LENGTH) {\n throw ShipError.validation(\n `Labels must be at least ${LABEL_CONSTRAINTS.MIN_LENGTH} characters long`,\n );\n }\n if (cleaned.length > LABEL_CONSTRAINTS.MAX_LENGTH) {\n throw ShipError.validation(\n `Labels must be no more than ${LABEL_CONSTRAINTS.MAX_LENGTH} characters long`,\n );\n }\n if (!LABEL_PATTERN.test(cleaned)) {\n throw ShipError.validation(\n `Labels must start and end with alphanumeric characters, with optional separators (${LABEL_CONSTRAINTS.SEPARATORS}) between segments`,\n );\n }\n return cleaned;\n });\n\n const unique = [...new Set(normalized)];\n if (unique.length !== normalized.length) {\n throw ShipError.validation('Duplicate labels are not allowed');\n }\n\n return unique;\n}\n","/**\n * Ship SDK resource factory functions.\n */\nimport {\n type AccountResource,\n type DeployInput,\n type DeploymentResource,\n type DomainResource,\n type ListOptions,\n ShipError,\n type StaticFile,\n type TokenResource,\n} from '@shipstatic/types';\n\nexport type {\n AccountResource,\n DeployInput,\n DeploymentResource,\n DomainResource,\n StaticFile,\n TokenResource,\n};\n\nimport type { ApiHttp } from './api/http.js';\nimport { detectAndConfigureSPA } from './lib/spa.js';\nimport type { DeploymentOptions } from './types.js';\n\n/**\n * Shared context for all resource factories.\n */\nexport interface ResourceContext {\n getApi: () => ApiHttp;\n ensureInit: () => Promise<void>;\n}\n\n/**\n * Extended context for deployment resource.\n */\nexport interface DeploymentResourceContext extends ResourceContext {\n processInput: (input: DeployInput, options: DeploymentOptions) => Promise<StaticFile[]>;\n}\n\n/**\n * Upload deployment resource with all CRUD operations.\n *\n * There is no client-side auth branching: an upload from a credential-less\n * client simply carries no `Authorization` header, and the API grants the\n * public-account agent identity per request (claim URL + expiry on the\n * response). The SDK stays a transparent pipe either way.\n */\nexport function createDeploymentResource(\n ctx: DeploymentResourceContext,\n): DeploymentResource<DeploymentOptions> {\n const { getApi, ensureInit, processInput } = ctx;\n\n return {\n upload: async (input: DeployInput, options: DeploymentOptions = {}) => {\n await ensureInit();\n\n if (!processInput) {\n throw ShipError.config('processInput function is not provided.');\n }\n\n const apiClient = getApi();\n let staticFiles = await processInput(input, options);\n staticFiles = await detectAndConfigureSPA(staticFiles, apiClient, options);\n\n return apiClient.deploy(staticFiles, options);\n },\n\n list: async (options?: ListOptions) => {\n await ensureInit();\n return getApi().listDeployments(options);\n },\n\n get: async (id: string) => {\n await ensureInit();\n return getApi().getDeployment(id);\n },\n\n set: async (id: string, options: { labels: string[] }) => {\n await ensureInit();\n return getApi().updateDeploymentLabels(id, options.labels);\n },\n\n remove: async (id: string) => {\n await ensureInit();\n await getApi().removeDeployment(id);\n },\n };\n}\n\n/**\n * Create domain resource with all CRUD operations.\n *\n * @remarks\n * The `name` parameter in all methods is an FQDN (Fully Qualified Domain Name).\n * The SDK does not validate or normalize domain names - the API handles all domain semantics.\n */\nexport function createDomainResource(ctx: ResourceContext): DomainResource {\n const { getApi, ensureInit } = ctx;\n\n return {\n // INTENTIONAL DESIGN: The API does NOT support unlinking domains (setting deployment to null).\n // Once a domain is linked to a deployment, it must always have a deployment.\n // Supported: reserve (omit deployment), link, switch deployments atomically, delete entirely.\n // Not supported: unlink after linking (creates ambiguous state with no clear use case).\n // See npm/ship/CLAUDE.md \"Domain Write Semantics\" for full rationale.\n set: async (name: string, options: { deployment?: string; labels?: string[] } = {}) => {\n await ensureInit();\n return getApi().setDomain(name, options.deployment, options.labels);\n },\n\n list: async (options?: ListOptions) => {\n await ensureInit();\n return getApi().listDomains(options);\n },\n\n get: async (name: string) => {\n await ensureInit();\n return getApi().getDomain(name);\n },\n\n remove: async (name: string) => {\n await ensureInit();\n await getApi().removeDomain(name);\n },\n\n verify: async (name: string) => {\n await ensureInit();\n return getApi().verifyDomain(name);\n },\n\n validate: async (name: string) => {\n await ensureInit();\n return getApi().validateDomain(name);\n },\n\n dns: async (name: string) => {\n await ensureInit();\n return getApi().getDomainDns(name);\n },\n\n records: async (name: string) => {\n await ensureInit();\n return getApi().getDomainRecords(name);\n },\n\n share: async (name: string) => {\n await ensureInit();\n return getApi().getDomainShare(name);\n },\n };\n}\n\n/**\n * Create account resource (whoami functionality).\n */\nexport function createAccountResource(ctx: ResourceContext): AccountResource {\n const { getApi, ensureInit } = ctx;\n\n return {\n get: async () => {\n await ensureInit();\n return getApi().getAccount();\n },\n };\n}\n\n/**\n * Create token resource for managing deploy tokens.\n */\nexport function createTokenResource(ctx: ResourceContext): TokenResource {\n const { getApi, ensureInit } = ctx;\n\n return {\n create: async (options: { ttl?: number; labels?: string[] } = {}) => {\n await ensureInit();\n return getApi().createToken(options.ttl, options.labels);\n },\n\n list: async () => {\n await ensureInit();\n return getApi().listTokens();\n },\n\n remove: async (token: string) => {\n await ensureInit();\n await getApi().removeToken(token);\n },\n };\n}\n","/**\n * @file SPA detection and auto-configuration utilities.\n *\n * Provides SPA detection and ship.json generation functionality\n * that can be used by both Node.js and browser environments.\n */\n\nimport { DEPLOYMENT_CONFIG_FILENAME, SPA_DEFAULT_CONFIG } from '@shipstatic/types';\nimport type { ApiHttp } from '../api/http.js';\nimport type { DeploymentOptions, StaticFile } from '../types.js';\nimport { calculateMD5 } from './md5.js';\n\n/**\n * Creates ship.json configuration for SPA projects.\n * @returns Promise resolving to StaticFile with SPA configuration\n */\nexport async function createSPAConfig(): Promise<StaticFile> {\n const configString = JSON.stringify(SPA_DEFAULT_CONFIG, null, 2);\n\n // Create content that works in both browser and Node.js environments\n let content: Buffer | Blob;\n if (typeof Buffer !== 'undefined') {\n // Node.js environment\n content = Buffer.from(configString, 'utf-8');\n } else {\n // Browser environment\n content = new Blob([configString], { type: 'application/json' });\n }\n\n const { md5 } = await calculateMD5(content);\n\n return {\n path: DEPLOYMENT_CONFIG_FILENAME,\n content,\n size: configString.length,\n md5,\n };\n}\n\n/**\n * Detects SPA projects and auto-generates configuration.\n * This function can be used by both Node.js and browser environments.\n *\n * @param files - Array of StaticFiles to analyze\n * @param apiClient - HTTP client for API communication\n * @param options - Deployment options containing SPA detection settings\n * @returns Promise resolving to files array with optional SPA config added\n */\nexport async function detectAndConfigureSPA(\n files: StaticFile[],\n apiClient: ApiHttp,\n options: DeploymentOptions,\n): Promise<StaticFile[]> {\n // Skip if disabled, config already exists, or server will handle detection\n if (\n options.spaDetect === false ||\n options.spa ||\n options.build ||\n options.prerender ||\n files.some((f) => f.path === DEPLOYMENT_CONFIG_FILENAME)\n ) {\n return files;\n }\n\n try {\n const isSPA = await apiClient.checkSPA(files, options);\n\n if (isSPA) {\n const spaConfig = await createSPAConfig();\n return [...files, spaConfig];\n }\n } catch (_error) {\n // SPA detection failed, continue silently without auto-config\n }\n\n return files;\n}\n","/**\n * Browser-specific deploy body creation.\n */\nimport { ShipError } from '@shipstatic/types';\nimport type { DeployBody, DeployBodyContext, StaticFile } from '../../shared/types.js';\n\nexport async function createDeployBody(\n files: StaticFile[],\n context: DeployBodyContext = {},\n): Promise<DeployBody> {\n const { labels, via, password, flags, captcha } = context;\n const formData = new FormData();\n const checksums: string[] = [];\n\n for (const file of files) {\n // 1. Validate content type\n if (!(file.content instanceof File || file.content instanceof Blob)) {\n throw ShipError.file(`Unsupported file.content type for browser: ${file.path}`, {\n filePath: file.path,\n });\n }\n\n // 2. Validate md5\n if (!file.md5) {\n throw ShipError.file(`File missing md5 checksum: ${file.path}`, { filePath: file.path });\n }\n\n // 3. Create File and append — API derives Content-Type from extension\n const fileInstance = new File([file.content], file.path, { type: 'application/octet-stream' });\n formData.append('files[]', fileInstance);\n checksums.push(file.md5);\n }\n\n formData.append('checksums', JSON.stringify(checksums));\n\n if (labels && labels.length > 0) formData.append('labels', JSON.stringify(labels));\n if (via) formData.append('via', via);\n if (password) formData.append('password', password);\n if (flags?.build) formData.append('build', 'true');\n if (flags?.prerender) formData.append('prerender', 'true');\n if (flags?.spa) formData.append('spa', 'true');\n if (captcha) formData.append('captcha', captcha);\n\n return { body: formData, headers: {} };\n}\n","/**\n * @file Shared SDK exports - environment agnostic.\n */\n\nexport type { Account, Deployment, Domain, PingResponse } from '@shipstatic/types';\n// Re-export types from @shipstatic/types\nexport { ErrorType, ShipError } from '@shipstatic/types';\nexport * from './api/http.js';\nexport { Ship } from './base-ship.js';\nexport * from './core/constants.js';\nexport * from './lib/deploy-paths.js';\nexport * from './lib/env.js';\nexport * from './lib/file-validation.js';\nexport * from './lib/junk.js';\n// Shared utilities\nexport * from './lib/md5.js';\nexport * from './lib/security.js';\nexport * from './lib/text.js';\n// Core functionality\nexport * from './resources.js';\nexport * from './types.js';\n","/**\n * @file SDK-specific constants.\n * Platform constants are now in @shipstatic/types.\n */\n\n// Re-export platform constants for convenience\nexport { DEFAULT_API } from '@shipstatic/types';\n","/**\n * Utility functions for string manipulation.\n */\n\n/**\n * Simple utility to pluralize a word based on a count.\n * @param count The number to determine pluralization.\n * @param singular The singular form of the word.\n * @param plural The plural form of the word.\n * @param includeCount Whether to include the count in the returned string. Defaults to true.\n * @returns A string with the count and the correctly pluralized word.\n */\nexport function pluralize(\n count: number,\n singular: string,\n plural: string,\n includeCount: boolean = true,\n): string {\n const word = count === 1 ? singular : plural;\n return includeCount ? `${count} ${word}` : word;\n}\n"],"mappings":"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","names":["isShipError","error","isBlockedExtension","filename","dotIndex","ext","BLOCKED_EXTENSIONS","hasUnsafeChars","UNSAFE_FILENAME_CHARS","hasUnbuiltMarker","filePath","s","UNBUILT_PROJECT_MARKERS","classifyToken","token","API_KEY","TokenKind","DEPLOY_TOKEN","validatePrefixedCredential","value","shape","label","ShipError","hexPart","validateApiKey","apiKey","validateDeployToken","deployToken","validateToken","validateCaller","caller","CALLER","validateApiUrl","apiUrl","url","isDeployment","input","isPlatformDomain","domain","platformDomain","isCustomDomain","extractSubdomain","generateDeploymentUrl","deployment","generateDomainUrl","serializeLabels","labels","deserializeLabels","labelsJson","parsed","validatePassword","trimmed","PASSWORD_CONSTRAINTS","DeploymentStatus","DomainStatus","AccountPlan","ErrorType","CLIENT_ONLY_ERROR_TYPES","ERROR_CATEGORIES","SERVER_PRODUCIBLE_ERROR_TYPES","AUTH_BASE_PATH","AuthMethod","OAuthScope","DEPLOYMENT_CONFIG_FILENAME","SPA_DEFAULT_CONFIG","DEFAULT_API","FileValidationStatus","LABEL_CONSTRAINTS","LABEL_PATTERN","init_dist","__esmMin","t","_ShipError","type","message","status","details","__publicField","authDetails","response","operationName","bodyType","json","obj","text","retryAfterHeader","seconds","existing","cause","op","resource","id","errorType","require_spark_md5","__commonJSMin","exports","module","factory","glob","undefined","add32","a","b","hex_chr","cmn","q","x","s","t","md5cycle","k","c","d","md5blk","md5blks","i","md5blk_array","md51","n","state","length","tail","tmp","lo","hi","md51_array","rhex","j","hex","y","lsw","msw","clamp","val","from","to","begin","end","num","target","targetArray","sourceArray","toUtf8","str","utf8Str2ArrayBuffer","returnUInt8Array","buff","arr","arrayBuffer2Utf8Str","concatenateArrayBuffers","first","second","result","hexToBinaryString","bytes","SparkMD5","contents","raw","ret","content","hash","require_empty","__commonJSMin","exports","module","md5Blob","blob","SparkMD5","spark","chunkSize","start","end","md5Buffer","buffer","createHash","hash","md5Path","path","createReadStream","resolve","reject","stream","err","ShipError","chunk","calculateMD5","input","init_md5","__esmMin","init_dist","normalizeWebPath","path","init_path","__esmMin","optimizeDeployPaths","filePaths","options","path","normalizeWebPath","extractFileName","commonPrefix","findCommonDirectory","filePath","deployPath","prefixToRemove","pathSegments","commonSegments","minLength","segments","i","segment","init_deploy_paths","__esmMin","init_path","__setTestEnvironment","env","_testEnvironment","detectEnvironment","getENV","init_env","__esmMin","formatFileSize","bytes","decimals","k","sizes","i","validateFileName","filename","hasUnsafeChars","reservedNames","nameWithoutPath","validateFiles","files","config","errors","warnings","fileStatuses","issue","file","hasUnbuiltMarker","f","FileValidationStatus","totalSize","fileStatus","statusMessage","nameValidation","isBlockedExtension","validFiles","canDeploy","getValidFiles","allValidFilesReady","init_file_validation","__esmMin","init_dist","isJunk","filename","junkRegex","ignoreList","init_junk","__esmMin","filterJunk","filePaths","options","p","hasUnbuiltMarker","ShipError","filePath","parts","basename","isJunk","part","directorySegments","segment","JUNK_DIRECTORIES","junkDir","init_junk","__esmMin","init_dist","validateDeployPath","deployPath","sourceIdentifier","ShipError","validateDeployFile","nameCheck","validateFileName","isBlockedExtension","init_security","__esmMin","init_dist","init_file_validation","browser_files_exports","__export","processFilesForBrowser","browserFiles","options","platformLimits","getENV","ShipError","rawPaths","file","isServerProcessed","deployFiles","optimizeDeployPaths","deployPaths","f","filteredSet","filterJunk","validPairs","i","results","deployPath","md5","calculateMD5","totalSize","validateDeployPath","validateDeployFile","init_browser_files","__esmMin","init_dist","init_deploy_paths","init_env","init_junk","init_md5","init_security","init_dist","init_dist","init_dist","SimpleEvents","event","handler","eventHandlers","args","handlerArray","error","err","init_dist","validateLabels","labels","LABEL_CONSTRAINTS","ShipError","normalized","label","i","cleaned","LABEL_PATTERN","unique","ENDPOINTS","DEFAULT_REQUEST_TIMEOUT","listQuery","options","params","query","ApiHttp","SimpleEvents","DEFAULT_API","headers","url","operationName","cleanup","timeout","fetchOptions","response","ShipError","error","shipError","data","customHeaders","existingSignal","controller","timeoutId","abort","files","file","validatePassword","labels","validateLabels","flags","body","bodyHeaders","id","normalized","name","deployment","status","ttl","token","_options","indexFile","f","indexContent","init_dist","init_dist","init_md5","createSPAConfig","configString","SPA_DEFAULT_CONFIG","content","md5","calculateMD5","DEPLOYMENT_CONFIG_FILENAME","detectAndConfigureSPA","files","apiClient","options","f","spaConfig","createDeploymentResource","ctx","getApi","ensureInit","processInput","input","options","ShipError","apiClient","staticFiles","detectAndConfigureSPA","id","createDomainResource","name","createAccountResource","createTokenResource","token","Ship","options","validateCaller","ShipError","validateToken","ApiHttp","ctx","createDeploymentResource","input","opts","createDomainResource","createAccountResource","createTokenResource","error","event","handler","headers","token","value","init_dist","createDeployBody","files","context","labels","via","password","flags","captcha","formData","checksums","file","ShipError","fileInstance","init_dist","init_dist","init_deploy_paths","init_env","init_file_validation","init_junk","init_md5","init_security","pluralize","count","singular","plural","includeCount","word","init_browser_files","Ship","input","options","item","ShipError","processFilesForBrowser","createDeployBody","browser_default"]}