@dropgate/core 2.0.0-beta.2 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +76 -15
- package/dist/index.browser.js +1 -1
- package/dist/index.browser.js.map +1 -1
- package/dist/index.cjs +385 -138
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +182 -120
- package/dist/index.d.ts +182 -120
- package/dist/index.js +383 -138
- package/dist/index.js.map +1 -1
- package/dist/p2p/index.cjs +268 -62
- package/dist/p2p/index.cjs.map +1 -1
- package/dist/p2p/index.d.cts +154 -92
- package/dist/p2p/index.d.ts +154 -92
- package/dist/p2p/index.js +267 -62
- package/dist/p2p/index.js.map +1 -1
- package/package.json +88 -88
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/constants.ts","../src/errors.ts","../src/adapters/defaults.ts","../src/utils/base64.ts","../src/utils/lifetime.ts","../src/utils/semver.ts","../src/utils/filename.ts","../src/utils/network.ts","../src/crypto/decrypt.ts","../src/crypto/index.ts","../src/crypto/encrypt.ts","../src/client/DropgateClient.ts","../src/p2p/utils.ts","../src/p2p/helpers.ts","../src/p2p/send.ts","../src/p2p/receive.ts"],"sourcesContent":["/**\n * Default chunk size for file uploads (5MB)\n */\nexport const DEFAULT_CHUNK_SIZE = 5 * 1024 * 1024;\n\n/**\n * AES-GCM initialization vector size in bytes\n */\nexport const AES_GCM_IV_BYTES = 12;\n\n/**\n * AES-GCM authentication tag size in bytes\n */\nexport const AES_GCM_TAG_BYTES = 16;\n\n/**\n * Total encryption overhead per chunk (IV + tag)\n */\nexport const ENCRYPTION_OVERHEAD_PER_CHUNK = AES_GCM_IV_BYTES + AES_GCM_TAG_BYTES;\n\n/**\n * Maximum file size (in bytes) that can be downloaded without an onData callback.\n * Files larger than this require streaming via onData to avoid memory exhaustion.\n * Default: 100MB\n */\nexport const MAX_IN_MEMORY_DOWNLOAD_BYTES = 100 * 1024 * 1024;\n","export interface DropgateErrorOptions {\n code?: string;\n details?: unknown;\n cause?: unknown;\n}\n\n/**\n * Base error class for all Dropgate errors\n */\nexport class DropgateError extends Error {\n readonly code: string;\n readonly details?: unknown;\n\n constructor(message: string, opts: DropgateErrorOptions = {}) {\n super(message);\n this.name = this.constructor.name;\n this.code = opts.code || 'DROPGATE_ERROR';\n this.details = opts.details;\n if (opts.cause !== undefined) {\n // Use Object.defineProperty for cause to maintain compatibility\n Object.defineProperty(this, 'cause', {\n value: opts.cause,\n writable: false,\n enumerable: false,\n configurable: true,\n });\n }\n }\n}\n\n/**\n * Validation error for invalid inputs\n */\nexport class DropgateValidationError extends DropgateError {\n constructor(message: string, opts: DropgateErrorOptions = {}) {\n super(message, { ...opts, code: opts.code || 'VALIDATION_ERROR' });\n }\n}\n\n/**\n * Network error for connection issues\n */\nexport class DropgateNetworkError extends DropgateError {\n constructor(message: string, opts: DropgateErrorOptions = {}) {\n super(message, { ...opts, code: opts.code || 'NETWORK_ERROR' });\n }\n}\n\n/**\n * Protocol error for server communication issues\n */\nexport class DropgateProtocolError extends DropgateError {\n constructor(message: string, opts: DropgateErrorOptions = {}) {\n super(message, { ...opts, code: opts.code || 'PROTOCOL_ERROR' });\n }\n}\n\n/**\n * Abort error - replacement for DOMException with AbortError name\n * Used when operations are cancelled\n */\nexport class DropgateAbortError extends DropgateError {\n constructor(message = 'Operation aborted') {\n super(message, { code: 'ABORT_ERROR' });\n this.name = 'AbortError';\n }\n}\n\n/**\n * Timeout error - replacement for DOMException with TimeoutError name\n * Used when operations exceed their time limit\n */\nexport class DropgateTimeoutError extends DropgateError {\n constructor(message = 'Request timed out') {\n super(message, { code: 'TIMEOUT_ERROR' });\n this.name = 'TimeoutError';\n }\n}\n","import type { Base64Adapter, CryptoAdapter, FetchFn } from '../types.js';\n\n/**\n * Get the default Base64 adapter for the current environment.\n * Automatically detects Node.js Buffer vs browser btoa/atob.\n */\nexport function getDefaultBase64(): Base64Adapter {\n // Check for Node.js Buffer (works in Node.js and some bundlers)\n if (typeof Buffer !== 'undefined' && typeof Buffer.from === 'function') {\n return {\n encode(bytes: Uint8Array): string {\n return Buffer.from(bytes).toString('base64');\n },\n decode(b64: string): Uint8Array {\n return new Uint8Array(Buffer.from(b64, 'base64'));\n },\n };\n }\n\n // Browser fallback using btoa/atob\n if (typeof btoa === 'function' && typeof atob === 'function') {\n return {\n encode(bytes: Uint8Array): string {\n let binary = '';\n for (let i = 0; i < bytes.length; i++) {\n binary += String.fromCharCode(bytes[i]);\n }\n return btoa(binary);\n },\n decode(b64: string): Uint8Array {\n const binary = atob(b64);\n const out = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) {\n out[i] = binary.charCodeAt(i);\n }\n return out;\n },\n };\n }\n\n throw new Error(\n 'No Base64 implementation available. Provide a Base64Adapter via options.'\n );\n}\n\n/**\n * Get the default crypto object for the current environment.\n * Returns globalThis.crypto if available.\n */\nexport function getDefaultCrypto(): CryptoAdapter | undefined {\n return globalThis.crypto as CryptoAdapter | undefined;\n}\n\n/**\n * Get the default fetch function for the current environment.\n * Returns globalThis.fetch if available.\n */\nexport function getDefaultFetch(): FetchFn | undefined {\n return globalThis.fetch?.bind(globalThis) as FetchFn | undefined;\n}\n","import type { Base64Adapter } from '../types.js';\nimport { getDefaultBase64 } from '../adapters/defaults.js';\n\nlet defaultAdapter: Base64Adapter | null = null;\n\nfunction getAdapter(adapter?: Base64Adapter): Base64Adapter {\n if (adapter) return adapter;\n if (!defaultAdapter) {\n defaultAdapter = getDefaultBase64();\n }\n return defaultAdapter;\n}\n\n/**\n * Convert a Uint8Array to a base64 string\n */\nexport function bytesToBase64(bytes: Uint8Array, adapter?: Base64Adapter): string {\n return getAdapter(adapter).encode(bytes);\n}\n\n/**\n * Convert an ArrayBuffer to a base64 string\n */\nexport function arrayBufferToBase64(buf: ArrayBuffer, adapter?: Base64Adapter): string {\n return bytesToBase64(new Uint8Array(buf), adapter);\n}\n\n/**\n * Convert a base64 string to a Uint8Array\n */\nexport function base64ToBytes(b64: string, adapter?: Base64Adapter): Uint8Array {\n return getAdapter(adapter).decode(b64);\n}\n","type LifetimeUnit = 'minutes' | 'hours' | 'days' | 'unlimited';\n\nconst MULTIPLIERS: Record<string, number> = {\n minutes: 60 * 1000,\n hours: 60 * 60 * 1000,\n days: 24 * 60 * 60 * 1000,\n};\n\n/**\n * Convert a lifetime value and unit to milliseconds.\n * Returns 0 for 'unlimited' or invalid inputs.\n */\nexport function lifetimeToMs(value: number, unit: LifetimeUnit | string): number {\n const u = String(unit || '').toLowerCase();\n const v = Number(value);\n\n if (u === 'unlimited') return 0;\n if (!Number.isFinite(v) || v <= 0) return 0;\n\n const m = MULTIPLIERS[u];\n if (!m) return 0;\n\n return Math.round(v * m);\n}\n","export interface SemverParts {\n major: number;\n minor: number;\n}\n\n/**\n * Parse a semver string and extract major.minor parts.\n * Returns { major: 0, minor: 0 } for invalid inputs.\n */\nexport function parseSemverMajorMinor(version: string | undefined | null): SemverParts {\n const parts = String(version || '')\n .split('.')\n .map((p) => Number(p));\n\n const major = Number.isFinite(parts[0]) ? parts[0] : 0;\n const minor = Number.isFinite(parts[1]) ? parts[1] : 0;\n\n return { major, minor };\n}\n","import { DropgateValidationError } from '../errors.js';\n\n/**\n * Validate a plain (non-encrypted) filename.\n * Throws DropgateValidationError if invalid.\n */\nexport function validatePlainFilename(filename: string): void {\n if (typeof filename !== 'string' || filename.trim().length === 0) {\n throw new DropgateValidationError(\n 'Invalid filename. Must be a non-empty string.'\n );\n }\n\n if (filename.length > 255 || /[\\/\\\\]/.test(filename)) {\n throw new DropgateValidationError(\n 'Invalid filename. Contains illegal characters or is too long.'\n );\n }\n}\n","import { DropgateAbortError, DropgateTimeoutError, DropgateValidationError } from '../errors.js';\nimport type { FetchFn, ServerTarget } from '../types.js';\n\n/**\n * Parse a server URL string into host, port, and secure components.\n * If no protocol is specified, defaults to HTTPS.\n */\nexport function parseServerUrl(urlStr: string): ServerTarget {\n let normalized = urlStr.trim();\n if (!normalized.startsWith('http://') && !normalized.startsWith('https://')) {\n normalized = 'https://' + normalized;\n }\n const url = new URL(normalized);\n return {\n host: url.hostname,\n port: url.port ? Number(url.port) : undefined,\n secure: url.protocol === 'https:',\n };\n}\n\n/**\n * Build a base URL from host, port, and secure options.\n */\nexport function buildBaseUrl(opts: ServerTarget): string {\n const { host, port, secure } = opts;\n\n if (!host || typeof host !== 'string') {\n throw new DropgateValidationError('Server host is required.');\n }\n\n const protocol = secure === false ? 'http' : 'https';\n const portSuffix = port ? `:${port}` : '';\n\n return `${protocol}://${host}${portSuffix}`;\n}\n\n/**\n * Sleep for a specified duration, with optional abort signal support.\n */\nexport function sleep(ms: number, signal?: AbortSignal): Promise<void> {\n return new Promise((resolve, reject) => {\n if (signal?.aborted) {\n return reject(signal.reason || new DropgateAbortError());\n }\n\n const t = setTimeout(resolve, ms);\n\n if (signal) {\n signal.addEventListener(\n 'abort',\n () => {\n clearTimeout(t);\n reject(signal.reason || new DropgateAbortError());\n },\n { once: true }\n );\n }\n });\n}\n\nexport interface AbortSignalWithCleanup {\n signal: AbortSignal;\n cleanup: () => void;\n}\n\n/**\n * Create an AbortSignal that combines a parent signal with a timeout.\n */\nexport function makeAbortSignal(\n parentSignal?: AbortSignal | null,\n timeoutMs?: number\n): AbortSignalWithCleanup {\n const controller = new AbortController();\n let timeoutId: ReturnType<typeof setTimeout> | null = null;\n\n const abort = (reason?: unknown): void => {\n if (!controller.signal.aborted) {\n controller.abort(reason);\n }\n };\n\n if (parentSignal) {\n if (parentSignal.aborted) {\n abort(parentSignal.reason);\n } else {\n parentSignal.addEventListener('abort', () => abort(parentSignal.reason), {\n once: true,\n });\n }\n }\n\n if (Number.isFinite(timeoutMs) && timeoutMs! > 0) {\n timeoutId = setTimeout(() => {\n abort(new DropgateTimeoutError());\n }, timeoutMs);\n }\n\n return {\n signal: controller.signal,\n cleanup: () => {\n if (timeoutId) clearTimeout(timeoutId);\n },\n };\n}\n\nexport interface FetchJsonResult {\n res: Response;\n json: unknown;\n text: string;\n}\n\nexport interface FetchJsonOptions extends Omit<RequestInit, 'signal'> {\n timeoutMs?: number;\n signal?: AbortSignal;\n}\n\n/**\n * Fetch JSON from a URL with timeout and error handling.\n */\nexport async function fetchJson(\n fetchFn: FetchFn,\n url: string,\n opts: FetchJsonOptions = {}\n): Promise<FetchJsonResult> {\n const { timeoutMs, signal, ...rest } = opts;\n const { signal: s, cleanup } = makeAbortSignal(signal, timeoutMs);\n\n try {\n const res = await fetchFn(url, { ...rest, signal: s });\n const text = await res.text();\n\n let json: unknown = null;\n try {\n json = text ? JSON.parse(text) : null;\n } catch {\n // Ignore parse errors - json will remain null\n }\n\n return { res, json, text };\n } finally {\n cleanup();\n }\n}\n","import { AES_GCM_IV_BYTES } from '../constants.js';\r\nimport type { CryptoAdapter, Base64Adapter } from '../types.js';\r\nimport { getDefaultBase64 } from '../adapters/defaults.js';\r\n\r\n/**\r\n * Import a base64-encoded AES-GCM key.\r\n * @param cryptoObj - Crypto adapter for key import.\r\n * @param keyB64 - Base64-encoded key bytes.\r\n * @param base64 - Optional base64 adapter.\r\n * @returns The imported CryptoKey.\r\n */\r\nexport async function importKeyFromBase64(\r\n cryptoObj: CryptoAdapter,\r\n keyB64: string,\r\n base64?: Base64Adapter\r\n): Promise<CryptoKey> {\r\n const adapter = base64 || getDefaultBase64();\r\n const keyBytes = adapter.decode(keyB64);\r\n // Create a new ArrayBuffer copy to satisfy TypeScript's BufferSource type\r\n const keyBuffer = new Uint8Array(keyBytes).buffer;\r\n return cryptoObj.subtle.importKey(\r\n 'raw',\r\n keyBuffer,\r\n { name: 'AES-GCM' },\r\n true,\r\n ['decrypt']\r\n );\r\n}\r\n\r\n/**\r\n * Decrypt an AES-GCM encrypted chunk.\r\n * Expected layout: [IV (12 bytes)] + [ciphertext + tag]\r\n * @param cryptoObj - Crypto adapter for decryption.\r\n * @param encryptedData - The encrypted data with IV prepended.\r\n * @param key - The AES-GCM decryption key.\r\n * @returns The decrypted data as ArrayBuffer.\r\n */\r\nexport async function decryptChunk(\r\n cryptoObj: CryptoAdapter,\r\n encryptedData: Uint8Array,\r\n key: CryptoKey\r\n): Promise<ArrayBuffer> {\r\n const iv = encryptedData.slice(0, AES_GCM_IV_BYTES);\r\n const ciphertext = encryptedData.slice(AES_GCM_IV_BYTES);\r\n return cryptoObj.subtle.decrypt(\r\n { name: 'AES-GCM', iv },\r\n key,\r\n ciphertext\r\n );\r\n}\r\n\r\n/**\r\n * Decrypt a base64-encoded encrypted filename.\r\n * @param cryptoObj - Crypto adapter for decryption.\r\n * @param encryptedFilenameB64 - Base64-encoded encrypted filename.\r\n * @param key - The AES-GCM decryption key.\r\n * @param base64 - Optional base64 adapter.\r\n * @returns The decrypted filename string.\r\n */\r\nexport async function decryptFilenameFromBase64(\r\n cryptoObj: CryptoAdapter,\r\n encryptedFilenameB64: string,\r\n key: CryptoKey,\r\n base64?: Base64Adapter\r\n): Promise<string> {\r\n const adapter = base64 || getDefaultBase64();\r\n const encryptedBytes = adapter.decode(encryptedFilenameB64);\r\n const decryptedBuffer = await decryptChunk(cryptoObj, encryptedBytes, key);\r\n return new TextDecoder().decode(decryptedBuffer);\r\n}\r\n","import type { CryptoAdapter } from '../types.js';\nimport { arrayBufferToBase64 } from '../utils/base64.js';\n\n/**\n * Compute SHA-256 hash of data and return as hex string.\n */\nexport async function sha256Hex(\n cryptoObj: CryptoAdapter,\n data: ArrayBuffer\n): Promise<string> {\n const hashBuffer = await cryptoObj.subtle.digest('SHA-256', data);\n const arr = new Uint8Array(hashBuffer);\n let hex = '';\n for (let i = 0; i < arr.length; i++) {\n hex += arr[i].toString(16).padStart(2, '0');\n }\n return hex;\n}\n\n/**\n * Generate a new AES-GCM 256-bit encryption key.\n */\nexport async function generateAesGcmKey(\n cryptoObj: CryptoAdapter\n): Promise<CryptoKey> {\n return cryptoObj.subtle.generateKey(\n { name: 'AES-GCM', length: 256 },\n true,\n ['encrypt', 'decrypt']\n );\n}\n\n/**\n * Export a CryptoKey to a base64-encoded raw key.\n */\nexport async function exportKeyBase64(\n cryptoObj: CryptoAdapter,\n key: CryptoKey\n): Promise<string> {\n const raw = await cryptoObj.subtle.exportKey('raw', key);\n return arrayBufferToBase64(raw);\n}\n\n// Re-export decryption functions\nexport { importKeyFromBase64, decryptChunk, decryptFilenameFromBase64 } from './decrypt.js';\n","import { AES_GCM_IV_BYTES } from '../constants.js';\nimport type { CryptoAdapter } from '../types.js';\nimport { arrayBufferToBase64 } from '../utils/base64.js';\n\n/**\n * Encrypt data using AES-GCM and return as a Blob with IV prepended.\n * Layout: [IV (12 bytes)] + [ciphertext + tag]\n */\nexport async function encryptToBlob(\n cryptoObj: CryptoAdapter,\n dataBuffer: ArrayBuffer,\n key: CryptoKey\n): Promise<Blob> {\n const iv = cryptoObj.getRandomValues(new Uint8Array(AES_GCM_IV_BYTES));\n const encrypted = await cryptoObj.subtle.encrypt(\n { name: 'AES-GCM', iv },\n key,\n dataBuffer\n );\n return new Blob([iv, new Uint8Array(encrypted)]);\n}\n\n/**\n * Encrypt a filename using AES-GCM and return as base64.\n */\nexport async function encryptFilenameToBase64(\n cryptoObj: CryptoAdapter,\n filename: string,\n key: CryptoKey\n): Promise<string> {\n const bytes = new TextEncoder().encode(String(filename));\n const blob = await encryptToBlob(cryptoObj, bytes.buffer, key);\n const buf = await blob.arrayBuffer();\n return arrayBufferToBase64(buf);\n}\n","import { DEFAULT_CHUNK_SIZE, ENCRYPTION_OVERHEAD_PER_CHUNK, MAX_IN_MEMORY_DOWNLOAD_BYTES } from '../constants.js';\nimport {\n DropgateError,\n DropgateValidationError,\n DropgateNetworkError,\n DropgateProtocolError,\n DropgateAbortError,\n} from '../errors.js';\nimport type {\n CryptoAdapter,\n FetchFn,\n LoggerFn,\n ServerInfo,\n CompatibilityResult,\n ShareTargetResult,\n UploadResult,\n ProgressEvent,\n DropgateClientOptions,\n UploadOptions,\n GetServerInfoOptions,\n ValidateUploadOptions,\n FileSource,\n Base64Adapter,\n DownloadOptions,\n DownloadResult,\n DownloadProgressEvent,\n FileMetadata,\n} from '../types.js';\nimport { getDefaultCrypto, getDefaultFetch, getDefaultBase64 } from '../adapters/defaults.js';\nimport { makeAbortSignal, fetchJson, sleep, buildBaseUrl } from '../utils/network.js';\nimport { parseSemverMajorMinor } from '../utils/semver.js';\nimport { validatePlainFilename } from '../utils/filename.js';\nimport { sha256Hex, generateAesGcmKey, exportKeyBase64, importKeyFromBase64, decryptChunk, decryptFilenameFromBase64 } from '../crypto/index.js';\nimport { encryptToBlob, encryptFilenameToBase64 } from '../crypto/encrypt.js';\n\n/**\n * Estimate total upload size including encryption overhead.\n */\nexport function estimateTotalUploadSizeBytes(\n fileSizeBytes: number,\n totalChunks: number,\n isEncrypted: boolean\n): number {\n const base = Number(fileSizeBytes) || 0;\n if (!isEncrypted) return base;\n return base + (Number(totalChunks) || 0) * ENCRYPTION_OVERHEAD_PER_CHUNK;\n}\n\n/**\n * Headless, environment-agnostic client for Dropgate file uploads.\n * Handles server communication, encryption, and chunked uploads.\n */\nexport class DropgateClient {\n /** Client version string for compatibility checking. */\n readonly clientVersion: string;\n /** Chunk size in bytes for upload splitting. */\n readonly chunkSize: number;\n /** Fetch implementation used for HTTP requests. */\n readonly fetchFn: FetchFn;\n /** Crypto implementation for encryption operations. */\n readonly cryptoObj: CryptoAdapter;\n /** Base64 encoder/decoder for binary data. */\n readonly base64: Base64Adapter;\n /** Optional logger for debug output. */\n readonly logger: LoggerFn | null;\n\n /**\n * Create a new DropgateClient instance.\n * @param opts - Client configuration options.\n * @throws {DropgateValidationError} If clientVersion is missing or invalid.\n */\n constructor(opts: DropgateClientOptions) {\n if (!opts || typeof opts.clientVersion !== 'string') {\n throw new DropgateValidationError(\n 'DropgateClient requires clientVersion (string).'\n );\n }\n\n this.clientVersion = opts.clientVersion;\n this.chunkSize = Number.isFinite(opts.chunkSize)\n ? opts.chunkSize!\n : DEFAULT_CHUNK_SIZE;\n\n const fetchFn = opts.fetchFn || getDefaultFetch();\n if (!fetchFn) {\n throw new DropgateValidationError('No fetch() implementation found.');\n }\n this.fetchFn = fetchFn;\n\n const cryptoObj = opts.cryptoObj || getDefaultCrypto();\n if (!cryptoObj) {\n throw new DropgateValidationError('No crypto implementation found.');\n }\n this.cryptoObj = cryptoObj;\n\n this.base64 = opts.base64 || getDefaultBase64();\n this.logger = opts.logger || null;\n }\n\n /**\n * Fetch server information from the /api/info endpoint.\n * @param opts - Server target and request options.\n * @returns The server base URL and server info object.\n * @throws {DropgateNetworkError} If the server cannot be reached.\n * @throws {DropgateProtocolError} If the server returns an invalid response.\n */\n async getServerInfo(\n opts: GetServerInfoOptions\n ): Promise<{ baseUrl: string; serverInfo: ServerInfo }> {\n const { host, port, secure, timeoutMs = 5000, signal } = opts;\n\n const baseUrl = buildBaseUrl({ host, port, secure });\n\n try {\n const { res, json } = await fetchJson(\n this.fetchFn,\n `${baseUrl}/api/info`,\n {\n method: 'GET',\n timeoutMs,\n signal,\n headers: { Accept: 'application/json' },\n }\n );\n\n if (res.ok && json && typeof json === 'object' && 'version' in json) {\n return { baseUrl, serverInfo: json as ServerInfo };\n }\n\n throw new DropgateProtocolError(\n `Server info request failed (status ${res.status}).`\n );\n } catch (err) {\n if (err instanceof DropgateError) throw err;\n throw new DropgateNetworkError('Could not reach server /api/info.', {\n cause: err,\n });\n }\n }\n\n /**\n * Resolve a user-entered sharing code or URL via the server.\n * @param value - The sharing code or URL to resolve.\n * @param opts - Server target and request options.\n * @returns The resolved share target information.\n * @throws {DropgateProtocolError} If the share lookup fails.\n */\n async resolveShareTarget(\n value: string,\n opts: GetServerInfoOptions\n ): Promise<ShareTargetResult> {\n const { host, port, secure, timeoutMs = 5000, signal } = opts;\n\n const baseUrl = buildBaseUrl({ host, port, secure });\n\n const { res, json } = await fetchJson(\n this.fetchFn,\n `${baseUrl}/api/resolve`,\n {\n method: 'POST',\n timeoutMs,\n signal,\n headers: {\n 'Content-Type': 'application/json',\n Accept: 'application/json',\n },\n body: JSON.stringify({ value }),\n }\n );\n\n if (!res.ok) {\n const msg =\n (json && typeof json === 'object' && 'error' in json\n ? (json as { error: string }).error\n : null) || `Share lookup failed (status ${res.status}).`;\n throw new DropgateProtocolError(msg, { details: json });\n }\n\n return (json as ShareTargetResult) || { valid: false, reason: 'Unknown response.' };\n }\n\n /**\n * Check version compatibility between this client and a server.\n * @param serverInfo - Server info containing the version to check against.\n * @returns Compatibility result with status and message.\n */\n checkCompatibility(serverInfo: ServerInfo): CompatibilityResult {\n const serverVersion = String(serverInfo?.version || '0.0.0');\n const clientVersion = String(this.clientVersion || '0.0.0');\n\n const c = parseSemverMajorMinor(clientVersion);\n const s = parseSemverMajorMinor(serverVersion);\n\n if (c.major !== s.major) {\n return {\n compatible: false,\n clientVersion,\n serverVersion,\n message: `Incompatible versions. Client v${clientVersion}, Server v${serverVersion}${serverInfo?.name ? ` (${serverInfo.name})` : ''}.`,\n };\n }\n\n if (c.minor > s.minor) {\n return {\n compatible: true,\n clientVersion,\n serverVersion,\n message: `Client (v${clientVersion}) is newer than Server (v${serverVersion})${serverInfo?.name ? ` (${serverInfo.name})` : ''}. Some features may not work.`,\n };\n }\n\n return {\n compatible: true,\n clientVersion,\n serverVersion,\n message: `Server: v${serverVersion}, Client: v${clientVersion}${serverInfo?.name ? ` (${serverInfo.name})` : ''}.`,\n };\n }\n\n /**\n * Validate file and upload settings against server capabilities.\n * @param opts - Validation options containing file, settings, and server info.\n * @returns True if validation passes.\n * @throws {DropgateValidationError} If any validation check fails.\n */\n validateUploadInputs(opts: ValidateUploadOptions): boolean {\n const { file, lifetimeMs, encrypt, serverInfo } = opts;\n const caps = serverInfo?.capabilities?.upload;\n\n if (!caps || !caps.enabled) {\n throw new DropgateValidationError('Server does not support file uploads.');\n }\n\n // Check file validity\n const fileSize = Number(file?.size || 0);\n if (!file || !Number.isFinite(fileSize) || fileSize <= 0) {\n throw new DropgateValidationError('File is missing or invalid.');\n }\n\n // maxSizeMB: 0 means unlimited\n const maxMB = Number(caps.maxSizeMB);\n if (Number.isFinite(maxMB) && maxMB > 0) {\n const limitBytes = maxMB * 1000 * 1000;\n const totalChunks = Math.ceil(fileSize / this.chunkSize);\n const estimatedBytes = estimateTotalUploadSizeBytes(\n fileSize,\n totalChunks,\n Boolean(encrypt)\n );\n if (estimatedBytes > limitBytes) {\n const msg = encrypt\n ? `File too large once encryption overhead is included. Server limit: ${maxMB} MB.`\n : `File too large. Server limit: ${maxMB} MB.`;\n throw new DropgateValidationError(msg);\n }\n }\n\n // maxLifetimeHours: 0 means unlimited is allowed\n const maxHours = Number(caps.maxLifetimeHours);\n const lt = Number(lifetimeMs);\n if (!Number.isFinite(lt) || lt < 0 || !Number.isInteger(lt)) {\n throw new DropgateValidationError(\n 'Invalid lifetime. Must be a non-negative integer (milliseconds).'\n );\n }\n\n if (Number.isFinite(maxHours) && maxHours > 0) {\n const limitMs = Math.round(maxHours * 60 * 60 * 1000);\n if (lt === 0) {\n throw new DropgateValidationError(\n `Server does not allow unlimited file lifetime. Max: ${maxHours} hours.`\n );\n }\n if (lt > limitMs) {\n throw new DropgateValidationError(\n `File lifetime too long. Server limit: ${maxHours} hours.`\n );\n }\n }\n\n // Encryption support\n if (encrypt && !caps.e2ee) {\n throw new DropgateValidationError(\n 'Server does not support end-to-end encryption.'\n );\n }\n\n return true;\n }\n\n /**\n * Upload a file to the server with optional encryption.\n * @param opts - Upload options including file, server target, and settings.\n * @returns Upload result containing the download URL and file identifiers.\n * @throws {DropgateValidationError} If input validation fails.\n * @throws {DropgateNetworkError} If the server cannot be reached.\n * @throws {DropgateProtocolError} If the server returns an error.\n * @throws {DropgateAbortError} If the upload is cancelled.\n */\n async uploadFile(opts: UploadOptions): Promise<UploadResult> {\n const {\n host,\n port,\n secure,\n file,\n lifetimeMs,\n encrypt,\n filenameOverride,\n onProgress,\n signal,\n timeouts = {},\n retry = {},\n } = opts;\n\n const progress = (evt: ProgressEvent): void => {\n try {\n if (onProgress) onProgress(evt);\n } catch {\n // Ignore UI callback failures\n }\n };\n\n if (!this.cryptoObj?.subtle) {\n throw new DropgateValidationError(\n 'Web Crypto API not available (crypto.subtle).'\n );\n }\n\n // 0) Get server info + compat\n progress({ phase: 'server-info', text: 'Checking server...' });\n\n let baseUrl: string;\n let serverInfo: ServerInfo;\n try {\n const res = await this.getServerInfo({\n host,\n port,\n secure,\n timeoutMs: timeouts.serverInfoMs ?? 5000,\n signal,\n });\n baseUrl = res.baseUrl;\n serverInfo = res.serverInfo;\n } catch (err) {\n if (err instanceof DropgateError) throw err;\n throw new DropgateNetworkError('Could not connect to the server.', {\n cause: err,\n });\n }\n\n const compat = this.checkCompatibility(serverInfo);\n progress({ phase: 'server-compat', text: compat.message });\n if (!compat.compatible) {\n throw new DropgateValidationError(compat.message);\n }\n\n // 1) Validate inputs\n const filename = filenameOverride ?? file.name ?? 'file';\n\n if (!encrypt) {\n validatePlainFilename(filename);\n }\n\n this.validateUploadInputs({ file, lifetimeMs, encrypt, serverInfo });\n\n // 2) Encryption prep\n let cryptoKey: CryptoKey | null = null;\n let keyB64: string | null = null;\n let transmittedFilename = filename;\n\n if (encrypt) {\n progress({ phase: 'crypto', text: 'Generating encryption key...' });\n try {\n cryptoKey = await generateAesGcmKey(this.cryptoObj);\n keyB64 = await exportKeyBase64(this.cryptoObj, cryptoKey);\n transmittedFilename = await encryptFilenameToBase64(\n this.cryptoObj,\n filename,\n cryptoKey\n );\n } catch (err) {\n throw new DropgateError('Failed to prepare encryption.', {\n code: 'CRYPTO_PREP_FAILED',\n cause: err,\n });\n }\n }\n\n // 3) Compute reservation sizes\n const totalChunks = Math.ceil(file.size / this.chunkSize);\n const totalUploadSize = estimateTotalUploadSizeBytes(\n file.size,\n totalChunks,\n encrypt\n );\n\n // 4) Init\n progress({ phase: 'init', text: 'Reserving server storage...' });\n\n const initPayload = {\n filename: transmittedFilename,\n lifetime: lifetimeMs,\n isEncrypted: Boolean(encrypt),\n totalSize: totalUploadSize,\n totalChunks,\n };\n\n const initRes = await fetchJson(this.fetchFn, `${baseUrl}/upload/init`, {\n method: 'POST',\n timeoutMs: timeouts.initMs ?? 15000,\n signal,\n headers: {\n 'Content-Type': 'application/json',\n Accept: 'application/json',\n },\n body: JSON.stringify(initPayload),\n });\n\n if (!initRes.res.ok) {\n const errorJson = initRes.json as { error?: string } | null;\n const msg =\n errorJson?.error ||\n `Server initialisation failed: ${initRes.res.status}`;\n throw new DropgateProtocolError(msg, {\n details: initRes.json || initRes.text,\n });\n }\n\n const initJson = initRes.json as { uploadId?: string } | null;\n const uploadId = initJson?.uploadId;\n if (!uploadId || typeof uploadId !== 'string') {\n throw new DropgateProtocolError(\n 'Server did not return a valid uploadId.'\n );\n }\n\n // 5) Chunks\n const retries = Number.isFinite(retry.retries) ? retry.retries! : 5;\n const baseBackoffMs = Number.isFinite(retry.backoffMs)\n ? retry.backoffMs!\n : 1000;\n const maxBackoffMs = Number.isFinite(retry.maxBackoffMs)\n ? retry.maxBackoffMs!\n : 30000;\n\n for (let i = 0; i < totalChunks; i++) {\n if (signal?.aborted) {\n throw signal.reason || new DropgateAbortError();\n }\n\n const start = i * this.chunkSize;\n const end = Math.min(start + this.chunkSize, file.size);\n let chunkBlob: Blob | FileSource = file.slice(start, end);\n\n const percentComplete = (i / totalChunks) * 100;\n progress({\n phase: 'chunk',\n text: `Uploading chunk ${i + 1} of ${totalChunks}...`,\n percent: percentComplete,\n chunkIndex: i,\n totalChunks,\n });\n\n // Get ArrayBuffer from the slice\n const chunkBuffer = await chunkBlob.arrayBuffer();\n\n // Encrypt if needed\n let uploadBlob: Blob;\n if (encrypt && cryptoKey) {\n uploadBlob = await encryptToBlob(this.cryptoObj, chunkBuffer, cryptoKey);\n } else {\n uploadBlob = new Blob([chunkBuffer]);\n }\n\n // Server validates: chunk <= 5MB + 1024\n if (uploadBlob.size > DEFAULT_CHUNK_SIZE + 1024) {\n throw new DropgateValidationError(\n 'Chunk too large (client-side). Check chunk size settings.'\n );\n }\n\n // Hash encrypted/plain payload\n const toHash = await uploadBlob.arrayBuffer();\n const hashHex = await sha256Hex(this.cryptoObj, toHash);\n\n const headers: Record<string, string> = {\n 'Content-Type': 'application/octet-stream',\n 'X-Upload-ID': uploadId,\n 'X-Chunk-Index': String(i),\n 'X-Chunk-Hash': hashHex,\n };\n\n const chunkUrl = `${baseUrl}/upload/chunk`;\n await this.attemptChunkUpload(\n chunkUrl,\n {\n method: 'POST',\n headers,\n body: uploadBlob,\n },\n {\n retries,\n backoffMs: baseBackoffMs,\n maxBackoffMs,\n timeoutMs: timeouts.chunkMs ?? 60000,\n signal,\n progress,\n chunkIndex: i,\n totalChunks,\n }\n );\n }\n\n // 6) Complete\n progress({ phase: 'complete', text: 'Finalising upload...', percent: 100 });\n\n const completeRes = await fetchJson(\n this.fetchFn,\n `${baseUrl}/upload/complete`,\n {\n method: 'POST',\n timeoutMs: timeouts.completeMs ?? 30000,\n signal,\n headers: {\n 'Content-Type': 'application/json',\n Accept: 'application/json',\n },\n body: JSON.stringify({ uploadId }),\n }\n );\n\n if (!completeRes.res.ok) {\n const errorJson = completeRes.json as { error?: string } | null;\n const msg = errorJson?.error || 'Finalisation failed.';\n throw new DropgateProtocolError(msg, {\n details: completeRes.json || completeRes.text,\n });\n }\n\n const completeJson = completeRes.json as { id?: string } | null;\n const fileId = completeJson?.id;\n if (!fileId || typeof fileId !== 'string') {\n throw new DropgateProtocolError(\n 'Server did not return a valid file id.'\n );\n }\n\n let downloadUrl = `${baseUrl}/${fileId}`;\n if (encrypt && keyB64) {\n downloadUrl += `#${keyB64}`;\n }\n\n progress({ phase: 'done', text: 'Upload successful!', percent: 100 });\n\n return {\n downloadUrl,\n fileId,\n uploadId,\n baseUrl,\n ...(encrypt && keyB64 ? { keyB64 } : {}),\n };\n }\n\n /**\n * Download a file from the server with optional decryption.\n *\n * **Important:** For large files, you must provide an `onData` callback to stream\n * data incrementally. Without it, the entire file is buffered in memory, which will\n * cause memory exhaustion for large files. Files exceeding 100MB without an `onData`\n * callback will throw a validation error.\n *\n * @param opts - Download options including file ID, server target, and optional key.\n * @param opts.onData - Streaming callback that receives data chunks. Required for files > 100MB.\n * @returns Download result containing filename and received bytes.\n * @throws {DropgateValidationError} If input validation fails or file is too large without onData.\n * @throws {DropgateNetworkError} If the server cannot be reached.\n * @throws {DropgateProtocolError} If the server returns an error.\n * @throws {DropgateAbortError} If the download is cancelled.\n */\n async downloadFile(opts: DownloadOptions): Promise<DownloadResult> {\n const {\n host,\n port,\n secure,\n fileId,\n keyB64,\n onProgress,\n onData,\n signal,\n timeoutMs = 60000,\n } = opts;\n\n const progress = (evt: DownloadProgressEvent): void => {\n try {\n if (onProgress) onProgress(evt);\n } catch {\n // Ignore UI callback failures\n }\n };\n\n if (!fileId || typeof fileId !== 'string') {\n throw new DropgateValidationError('File ID is required.');\n }\n\n const baseUrl = buildBaseUrl({ host, port, secure });\n\n // 1) Fetch metadata\n progress({ phase: 'metadata', text: 'Fetching file info...', receivedBytes: 0, totalBytes: 0, percent: 0 });\n\n const { signal: metaSignal, cleanup: metaCleanup } = makeAbortSignal(signal, timeoutMs);\n let metadata: FileMetadata;\n\n try {\n const metaRes = await this.fetchFn(`${baseUrl}/api/file/${fileId}/meta`, {\n method: 'GET',\n headers: { Accept: 'application/json' },\n signal: metaSignal,\n });\n\n if (!metaRes.ok) {\n if (metaRes.status === 404) {\n throw new DropgateProtocolError('File not found or has expired.');\n }\n throw new DropgateProtocolError(`Failed to fetch file metadata (status ${metaRes.status}).`);\n }\n\n metadata = await metaRes.json() as FileMetadata;\n } catch (err) {\n if (err instanceof DropgateError) throw err;\n if (err instanceof Error && err.name === 'AbortError') {\n throw new DropgateAbortError('Download cancelled.');\n }\n throw new DropgateNetworkError('Could not fetch file metadata.', { cause: err });\n } finally {\n metaCleanup();\n }\n\n const isEncrypted = Boolean(metadata.isEncrypted);\n const totalBytes = metadata.sizeBytes || 0;\n\n // Check if file is too large to buffer in memory without streaming\n if (!onData && totalBytes > MAX_IN_MEMORY_DOWNLOAD_BYTES) {\n const sizeMB = Math.round(totalBytes / (1024 * 1024));\n const limitMB = Math.round(MAX_IN_MEMORY_DOWNLOAD_BYTES / (1024 * 1024));\n throw new DropgateValidationError(\n `File is too large (${sizeMB}MB) to download without streaming. ` +\n `Provide an onData callback to stream files larger than ${limitMB}MB.`\n );\n }\n\n // 2) Decrypt filename if encrypted\n let filename: string;\n let cryptoKey: CryptoKey | undefined;\n\n if (isEncrypted) {\n if (!keyB64) {\n throw new DropgateValidationError('Decryption key is required for encrypted files.');\n }\n\n if (!this.cryptoObj?.subtle) {\n throw new DropgateValidationError('Web Crypto API not available for decryption.');\n }\n\n progress({ phase: 'decrypting', text: 'Preparing decryption...', receivedBytes: 0, totalBytes: 0, percent: 0 });\n\n try {\n cryptoKey = await importKeyFromBase64(this.cryptoObj, keyB64, this.base64);\n filename = await decryptFilenameFromBase64(\n this.cryptoObj,\n metadata.encryptedFilename!,\n cryptoKey,\n this.base64\n );\n } catch (err) {\n throw new DropgateError('Failed to decrypt filename. Invalid key or corrupted data.', {\n code: 'DECRYPT_FILENAME_FAILED',\n cause: err,\n });\n }\n } else {\n filename = metadata.filename || 'file';\n }\n\n // 3) Download file content\n progress({ phase: 'downloading', text: 'Starting download...', percent: 0, receivedBytes: 0, totalBytes });\n\n const { signal: downloadSignal, cleanup: downloadCleanup } = makeAbortSignal(signal, timeoutMs);\n let receivedBytes = 0;\n const dataChunks: Uint8Array[] = [];\n const collectData = !onData;\n\n try {\n const downloadRes = await this.fetchFn(`${baseUrl}/api/file/${fileId}`, {\n method: 'GET',\n signal: downloadSignal,\n });\n\n if (!downloadRes.ok) {\n throw new DropgateProtocolError(`Download failed (status ${downloadRes.status}).`);\n }\n\n if (!downloadRes.body) {\n throw new DropgateProtocolError('Streaming response not available.');\n }\n\n const reader = downloadRes.body.getReader();\n\n if (isEncrypted && cryptoKey) {\n // Encrypted: buffer and decrypt chunks\n // Use a chunk array to avoid repeated array copying on each read\n const ENCRYPTED_CHUNK_SIZE = this.chunkSize + ENCRYPTION_OVERHEAD_PER_CHUNK;\n const pendingChunks: Uint8Array[] = [];\n let pendingLength = 0;\n\n // Helper to concatenate pending chunks into a single buffer\n const flushPending = (): Uint8Array => {\n if (pendingChunks.length === 0) return new Uint8Array(0);\n if (pendingChunks.length === 1) {\n const result = pendingChunks[0];\n pendingChunks.length = 0;\n pendingLength = 0;\n return result;\n }\n const result = new Uint8Array(pendingLength);\n let offset = 0;\n for (const chunk of pendingChunks) {\n result.set(chunk, offset);\n offset += chunk.length;\n }\n pendingChunks.length = 0;\n pendingLength = 0;\n return result;\n };\n\n while (true) {\n if (signal?.aborted) {\n throw new DropgateAbortError('Download cancelled.');\n }\n\n const { done, value } = await reader.read();\n if (done) break;\n\n // Append to pending chunks (no copying yet)\n pendingChunks.push(value);\n pendingLength += value.length;\n\n // Process complete encrypted chunks when we have enough data\n while (pendingLength >= ENCRYPTED_CHUNK_SIZE) {\n const buffer = flushPending();\n const encryptedChunk = buffer.subarray(0, ENCRYPTED_CHUNK_SIZE);\n\n // Keep the remainder for next iteration\n if (buffer.length > ENCRYPTED_CHUNK_SIZE) {\n const remainder = buffer.subarray(ENCRYPTED_CHUNK_SIZE);\n pendingChunks.push(remainder);\n pendingLength = remainder.length;\n }\n\n const decryptedBuffer = await decryptChunk(this.cryptoObj, encryptedChunk, cryptoKey);\n const decryptedData = new Uint8Array(decryptedBuffer);\n\n if (collectData) {\n dataChunks.push(decryptedData);\n } else {\n await onData!(decryptedData);\n }\n }\n\n receivedBytes += value.length;\n const percent = totalBytes > 0 ? Math.round((receivedBytes / totalBytes) * 100) : 0;\n progress({\n phase: 'decrypting',\n text: `Downloading & decrypting... (${percent}%)`,\n percent,\n receivedBytes,\n totalBytes,\n });\n }\n\n // Process remaining buffer (final chunk)\n if (pendingLength > 0) {\n const buffer = flushPending();\n const decryptedBuffer = await decryptChunk(this.cryptoObj, buffer, cryptoKey);\n const decryptedData = new Uint8Array(decryptedBuffer);\n\n if (collectData) {\n dataChunks.push(decryptedData);\n } else {\n await onData!(decryptedData);\n }\n }\n } else {\n // Plain: stream through directly\n while (true) {\n if (signal?.aborted) {\n throw new DropgateAbortError('Download cancelled.');\n }\n\n const { done, value } = await reader.read();\n if (done) break;\n\n if (collectData) {\n dataChunks.push(value);\n } else {\n await onData!(value);\n }\n\n receivedBytes += value.length;\n const percent = totalBytes > 0 ? Math.round((receivedBytes / totalBytes) * 100) : 0;\n progress({\n phase: 'downloading',\n text: `Downloading... (${percent}%)`,\n percent,\n receivedBytes,\n totalBytes,\n });\n }\n }\n } catch (err) {\n if (err instanceof DropgateError) throw err;\n if (err instanceof Error && err.name === 'AbortError') {\n throw new DropgateAbortError('Download cancelled.');\n }\n throw new DropgateNetworkError('Download failed.', { cause: err });\n } finally {\n downloadCleanup();\n }\n\n progress({ phase: 'complete', text: 'Download complete!', percent: 100, receivedBytes, totalBytes });\n\n // Combine collected data if not using callback\n let data: Uint8Array | undefined;\n if (collectData && dataChunks.length > 0) {\n const totalLength = dataChunks.reduce((sum, chunk) => sum + chunk.length, 0);\n data = new Uint8Array(totalLength);\n let offset = 0;\n for (const chunk of dataChunks) {\n data.set(chunk, offset);\n offset += chunk.length;\n }\n }\n\n return {\n filename,\n receivedBytes,\n wasEncrypted: isEncrypted,\n ...(data ? { data } : {}),\n };\n }\n\n private async attemptChunkUpload(\n url: string,\n fetchOptions: RequestInit,\n opts: {\n retries: number;\n backoffMs: number;\n maxBackoffMs: number;\n timeoutMs: number;\n signal?: AbortSignal;\n progress: (evt: ProgressEvent) => void;\n chunkIndex: number;\n totalChunks: number;\n }\n ): Promise<void> {\n const {\n retries,\n backoffMs,\n maxBackoffMs,\n timeoutMs,\n signal,\n progress,\n chunkIndex,\n totalChunks,\n } = opts;\n\n let attemptsLeft = retries;\n let currentBackoff = backoffMs;\n const maxRetries = retries;\n\n while (true) {\n if (signal?.aborted) {\n throw signal.reason || new DropgateAbortError();\n }\n\n const { signal: s, cleanup } = makeAbortSignal(signal, timeoutMs);\n try {\n const res = await this.fetchFn(url, { ...fetchOptions, signal: s });\n if (res.ok) return;\n\n const text = await res.text().catch(() => '');\n const err = new DropgateProtocolError(\n `Chunk ${chunkIndex + 1} failed (HTTP ${res.status}).`,\n {\n details: { status: res.status, bodySnippet: text.slice(0, 120) },\n }\n );\n throw err;\n } catch (err) {\n cleanup();\n\n // AbortError should not retry\n if (\n err instanceof Error &&\n (err.name === 'AbortError' || (err as { code?: string }).code === 'ABORT_ERR')\n ) {\n throw err;\n }\n if (signal?.aborted) {\n throw signal.reason || new DropgateAbortError();\n }\n\n if (attemptsLeft <= 0) {\n throw err instanceof DropgateError\n ? err\n : new DropgateNetworkError('Chunk upload failed.', { cause: err });\n }\n\n const attemptNumber = maxRetries - attemptsLeft + 1;\n let remaining = currentBackoff;\n const tick = 100;\n while (remaining > 0) {\n const secondsLeft = (remaining / 1000).toFixed(1);\n progress({\n phase: 'retry-wait',\n text: `Chunk upload failed. Retrying in ${secondsLeft}s... (${attemptNumber}/${maxRetries})`,\n chunkIndex,\n totalChunks,\n });\n await sleep(Math.min(tick, remaining), signal);\n remaining -= tick;\n }\n\n progress({\n phase: 'retry',\n text: `Chunk upload failed. Retrying now... (${attemptNumber}/${maxRetries})`,\n chunkIndex,\n totalChunks,\n });\n\n attemptsLeft -= 1;\n currentBackoff = Math.min(currentBackoff * 2, maxBackoffMs);\n continue;\n } finally {\n cleanup();\n }\n }\n }\n}\n","import type { CryptoAdapter } from '../types.js';\nimport { getDefaultCrypto } from '../adapters/defaults.js';\n\n/**\n * Check if a hostname is localhost\n */\nexport function isLocalhostHostname(hostname: string): boolean {\n const host = String(hostname || '').toLowerCase();\n return host === 'localhost' || host === '127.0.0.1' || host === '::1';\n}\n\n/**\n * Check if the current context allows P2P (HTTPS or localhost)\n */\nexport function isSecureContextForP2P(\n hostname?: string,\n isSecureContext?: boolean\n): boolean {\n return Boolean(isSecureContext) || isLocalhostHostname(hostname || '');\n}\n\n/**\n * Generate a P2P sharing code using cryptographically secure random.\n * Format: XXXX-0000 (4 letters + 4 digits)\n */\nexport function generateP2PCode(cryptoObj?: CryptoAdapter): string {\n const crypto = cryptoObj || getDefaultCrypto();\n const letters = 'ABCDEFGHJKLMNPQRSTUVWXYZ'; // Excluded I and O to avoid confusion\n\n if (crypto) {\n const randomBytes = new Uint8Array(8);\n crypto.getRandomValues(randomBytes);\n\n let letterPart = '';\n for (let i = 0; i < 4; i++) {\n letterPart += letters[randomBytes[i] % letters.length];\n }\n\n let numberPart = '';\n for (let i = 4; i < 8; i++) {\n numberPart += (randomBytes[i] % 10).toString();\n }\n\n return `${letterPart}-${numberPart}`;\n }\n\n // Fallback to Math.random (less secure, but works everywhere)\n let a = '';\n for (let i = 0; i < 4; i++) {\n a += letters[Math.floor(Math.random() * letters.length)];\n }\n let b = '';\n for (let i = 0; i < 4; i++) {\n b += Math.floor(Math.random() * 10);\n }\n return `${a}-${b}`;\n}\n\n/**\n * Check if a string looks like a P2P sharing code\n */\nexport function isP2PCodeLike(code: string): boolean {\n return /^[A-Z]{4}-\\d{4}$/.test(String(code || '').trim());\n}\n","import { DropgateNetworkError } from '../errors.js';\nimport type { PeerInstance, PeerOptions } from './types.js';\n\nexport interface BuildPeerOptionsInput {\n host?: string;\n port?: number;\n peerjsPath?: string;\n secure?: boolean;\n iceServers?: RTCIceServer[];\n}\n\n/**\n * Build PeerJS connection options\n */\nexport function buildPeerOptions(opts: BuildPeerOptionsInput = {}): PeerOptions {\n const { host, port, peerjsPath = '/peerjs', secure = false, iceServers = [] } = opts;\n\n const peerOpts: PeerOptions = {\n host,\n path: peerjsPath,\n secure,\n config: { iceServers },\n debug: 0,\n };\n\n if (port) {\n peerOpts.port = port;\n }\n\n return peerOpts;\n}\n\nexport interface CreatePeerWithRetriesOptions {\n code?: string | null;\n codeGenerator: () => string;\n maxAttempts: number;\n buildPeer: (id: string) => PeerInstance;\n onCode?: (code: string, attempt: number) => void;\n}\n\n/**\n * Create a peer with retries if the code is already taken\n */\nexport async function createPeerWithRetries(\n opts: CreatePeerWithRetriesOptions\n): Promise<{ peer: PeerInstance; code: string }> {\n const { code, codeGenerator, maxAttempts, buildPeer, onCode } = opts;\n\n let nextCode = code || codeGenerator();\n let peer: PeerInstance | null = null;\n let lastError: Error | null = null;\n\n for (let attempt = 0; attempt < maxAttempts; attempt++) {\n onCode?.(nextCode, attempt);\n\n try {\n peer = await new Promise<PeerInstance>((resolve, reject) => {\n const instance = buildPeer(nextCode);\n instance.on('open', () => resolve(instance));\n instance.on('error', (err: Error) => {\n try {\n instance.destroy();\n } catch {\n // Ignore destroy errors\n }\n reject(err);\n });\n });\n\n return { peer, code: nextCode };\n } catch (err) {\n lastError = err as Error;\n nextCode = codeGenerator();\n }\n }\n\n throw lastError || new DropgateNetworkError('Could not establish PeerJS connection.');\n}\n","import { DropgateValidationError, DropgateNetworkError } from '../errors.js';\nimport { sleep } from '../utils/network.js';\nimport type { P2PSendOptions, P2PSendSession, DataConnection } from './types.js';\nimport { generateP2PCode } from './utils.js';\nimport { buildPeerOptions, createPeerWithRetries } from './helpers.js';\n\n/**\n * Start a direct transfer (P2P) sender session.\n *\n * IMPORTANT: Consumer must provide the PeerJS Peer constructor.\n * This removes DOM coupling (no script injection).\n *\n * Example:\n * ```js\n * import Peer from 'peerjs';\n * import { startP2PSend } from '@dropgate/core/p2p';\n *\n * const session = await startP2PSend({\n * file: myFile,\n * Peer,\n * host: 'dropgate.link',\n * secure: true,\n * onCode: (code) => console.log('Share this code:', code),\n * onProgress: (evt) => console.log(`${evt.percent}% sent`),\n * onComplete: () => console.log('Done!'),\n * });\n * ```\n */\nexport async function startP2PSend(opts: P2PSendOptions): Promise<P2PSendSession> {\n const {\n file,\n Peer,\n serverInfo,\n host,\n port,\n peerjsPath,\n secure = false,\n iceServers,\n codeGenerator,\n cryptoObj,\n maxAttempts = 4,\n chunkSize = 256 * 1024,\n readyTimeoutMs = 8000,\n endAckTimeoutMs = 15000,\n bufferHighWaterMark = 8 * 1024 * 1024,\n bufferLowWaterMark = 2 * 1024 * 1024,\n onCode,\n onStatus,\n onProgress,\n onComplete,\n onError,\n } = opts;\n\n // Validate required options\n if (!file) {\n throw new DropgateValidationError('File is missing.');\n }\n\n if (!Peer) {\n throw new DropgateValidationError(\n 'PeerJS Peer constructor is required. Install peerjs and pass it as the Peer option.'\n );\n }\n\n // Check P2P capabilities if serverInfo is provided\n const p2pCaps = serverInfo?.capabilities?.p2p;\n if (serverInfo && !p2pCaps?.enabled) {\n throw new DropgateValidationError('Direct transfer is disabled on this server.');\n }\n\n // Determine options (use serverInfo if available)\n const finalPath = peerjsPath ?? p2pCaps?.peerjsPath ?? '/peerjs';\n const finalIceServers = iceServers ?? p2pCaps?.iceServers ?? [];\n\n // Build peer options\n const peerOpts = buildPeerOptions({\n host,\n port,\n peerjsPath: finalPath,\n secure,\n iceServers: finalIceServers,\n });\n\n // Create the code generator\n const finalCodeGenerator = codeGenerator || (() => generateP2PCode(cryptoObj));\n\n // Create peer with retries\n const buildPeer = (id: string) => new Peer(id, peerOpts);\n const { peer, code } = await createPeerWithRetries({\n code: null,\n codeGenerator: finalCodeGenerator,\n maxAttempts,\n buildPeer,\n onCode,\n });\n\n let stopped = false;\n let activeConn: DataConnection | null = null;\n let transferActive = false;\n let transferCompleted = false;\n\n const reportProgress = (data: { received: number; total: number }): void => {\n const safeTotal =\n Number.isFinite(data.total) && data.total > 0 ? data.total : file.size;\n const safeReceived = Math.min(Number(data.received) || 0, safeTotal || 0);\n const percent = safeTotal ? (safeReceived / safeTotal) * 100 : 0;\n onProgress?.({ sent: safeReceived, total: safeTotal, percent });\n };\n\n const stop = (): void => {\n stopped = true;\n try {\n activeConn?.close();\n } catch {\n // Ignore close errors\n }\n try {\n peer.destroy();\n } catch {\n // Ignore destroy errors\n }\n };\n\n peer.on('connection', (conn: DataConnection) => {\n if (stopped) return;\n\n if (activeConn) {\n try {\n conn.send({ t: 'error', message: 'Another receiver is already connected.' });\n } catch {\n // Ignore send errors\n }\n try {\n conn.close();\n } catch {\n // Ignore close errors\n }\n return;\n }\n\n activeConn = conn;\n onStatus?.({ phase: 'connected', message: 'Connected. Starting transfer...' });\n\n let readyResolve: (() => void) | null = null;\n let ackResolve: ((data: unknown) => void) | null = null;\n\n const readyPromise = new Promise<void>((resolve) => {\n readyResolve = resolve;\n });\n\n const ackPromise = new Promise<unknown>((resolve) => {\n ackResolve = resolve;\n });\n\n conn.on('data', (data: unknown) => {\n if (\n !data ||\n typeof data !== 'object' ||\n data instanceof ArrayBuffer ||\n ArrayBuffer.isView(data)\n ) {\n return;\n }\n\n const msg = data as { t?: string; received?: number; total?: number; phase?: string; message?: string };\n if (!msg.t) return;\n\n if (msg.t === 'ready') {\n readyResolve?.();\n return;\n }\n\n if (msg.t === 'progress') {\n reportProgress({ received: msg.received || 0, total: msg.total || 0 });\n return;\n }\n\n if (msg.t === 'ack' && msg.phase === 'end') {\n ackResolve?.(msg);\n return;\n }\n\n if (msg.t === 'error') {\n onError?.(new DropgateNetworkError(msg.message || 'Receiver reported an error.'));\n stop();\n }\n });\n\n conn.on('open', async () => {\n try {\n transferActive = true;\n if (stopped) return;\n\n conn.send({\n t: 'meta',\n name: file.name,\n size: file.size,\n mime: file.type || 'application/octet-stream',\n });\n\n let sent = 0;\n const total = file.size;\n const dc = conn._dc;\n\n if (dc && Number.isFinite(bufferLowWaterMark)) {\n try {\n dc.bufferedAmountLowThreshold = bufferLowWaterMark;\n } catch {\n // Ignore threshold setting errors\n }\n }\n\n // Wait for ready signal\n await Promise.race([readyPromise, sleep(readyTimeoutMs).catch(() => null)]);\n\n // Send file in chunks\n for (let offset = 0; offset < total; offset += chunkSize) {\n if (stopped) return;\n\n const slice = file.slice(offset, offset + chunkSize);\n const buf = await slice.arrayBuffer();\n conn.send(buf);\n sent += buf.byteLength;\n\n // Flow control\n if (dc) {\n while (dc.bufferedAmount > bufferHighWaterMark) {\n await new Promise<void>((resolve) => {\n const fallback = setTimeout(resolve, 60);\n try {\n dc.addEventListener(\n 'bufferedamountlow',\n () => {\n clearTimeout(fallback);\n resolve();\n },\n { once: true }\n );\n } catch {\n // Fallback only\n }\n });\n }\n }\n }\n\n if (stopped) return;\n conn.send({ t: 'end' });\n\n // Wait for acknowledgment\n const ackTimeoutMs = Number.isFinite(endAckTimeoutMs)\n ? Math.max(endAckTimeoutMs, Math.ceil(file.size / (1024 * 1024)) * 1000)\n : null;\n\n const ackResult = await Promise.race([\n ackPromise,\n sleep(ackTimeoutMs || 15000).catch(() => null),\n ]);\n\n if (!ackResult || typeof ackResult !== 'object') {\n throw new DropgateNetworkError('Receiver did not confirm completion.');\n }\n\n const ackData = ackResult as { total?: number; received?: number };\n const ackTotal = Number(ackData.total) || file.size;\n const ackReceived = Number(ackData.received) || 0;\n\n if (ackTotal && ackReceived < ackTotal) {\n throw new DropgateNetworkError('Receiver reported an incomplete transfer.');\n }\n\n reportProgress({ received: ackReceived || ackTotal, total: ackTotal });\n transferCompleted = true;\n transferActive = false;\n onComplete?.();\n stop();\n } catch (err) {\n onError?.(err as Error);\n stop();\n }\n });\n\n conn.on('error', (err: Error) => {\n onError?.(err);\n stop();\n });\n\n conn.on('close', () => {\n if (!transferCompleted && transferActive && !stopped) {\n onError?.(\n new DropgateNetworkError('Receiver disconnected before transfer completed.')\n );\n }\n stop();\n });\n });\n\n return { peer, code, stop };\n}\n","import { DropgateValidationError, DropgateNetworkError } from '../errors.js';\nimport type { P2PReceiveOptions, P2PReceiveSession } from './types.js';\nimport { isP2PCodeLike } from './utils.js';\nimport { buildPeerOptions } from './helpers.js';\n\n/**\n * Start a direct transfer (P2P) receiver session.\n *\n * IMPORTANT: Consumer must provide the PeerJS Peer constructor and handle file writing.\n * This removes DOM coupling (no streamSaver).\n *\n * Example:\n * ```js\n * import Peer from 'peerjs';\n * import { startP2PReceive } from '@dropgate/core/p2p';\n *\n * let writer;\n * const session = await startP2PReceive({\n * code: 'ABCD-1234',\n * Peer,\n * host: 'dropgate.link',\n * secure: true,\n * onMeta: ({ name, total }) => {\n * // Consumer creates file writer\n * writer = createWriteStream(name);\n * },\n * onData: async (chunk) => {\n * // Consumer writes data\n * await writer.write(chunk);\n * },\n * onComplete: () => {\n * writer.close();\n * console.log('Done!');\n * },\n * });\n * ```\n */\nexport async function startP2PReceive(opts: P2PReceiveOptions): Promise<P2PReceiveSession> {\n const {\n code,\n Peer,\n serverInfo,\n host,\n port,\n peerjsPath,\n secure = false,\n iceServers,\n onStatus,\n onMeta,\n onData,\n onProgress,\n onComplete,\n onError,\n onDisconnect,\n } = opts;\n\n // Validate required options\n if (!code) {\n throw new DropgateValidationError('No sharing code was provided.');\n }\n\n if (!Peer) {\n throw new DropgateValidationError(\n 'PeerJS Peer constructor is required. Install peerjs and pass it as the Peer option.'\n );\n }\n\n // Check P2P capabilities if serverInfo is provided\n const p2pCaps = serverInfo?.capabilities?.p2p;\n if (serverInfo && !p2pCaps?.enabled) {\n throw new DropgateValidationError('Direct transfer is disabled on this server.');\n }\n\n // Validate and normalize code\n const normalizedCode = String(code).trim().replace(/\\s+/g, '').toUpperCase();\n if (!isP2PCodeLike(normalizedCode)) {\n throw new DropgateValidationError('Invalid direct transfer code.');\n }\n\n // Determine options (use serverInfo if available)\n const finalPath = peerjsPath ?? p2pCaps?.peerjsPath ?? '/peerjs';\n const finalIceServers = iceServers ?? p2pCaps?.iceServers ?? [];\n\n // Build peer options\n const peerOpts = buildPeerOptions({\n host,\n port,\n peerjsPath: finalPath,\n secure,\n iceServers: finalIceServers,\n });\n\n // Create peer (receiver doesn't need a specific ID)\n const peer = new Peer(undefined, peerOpts);\n\n let total = 0;\n let received = 0;\n let lastProgressSentAt = 0;\n const progressIntervalMs = 120;\n let writeQueue = Promise.resolve();\n\n const stop = (): void => {\n try {\n peer.destroy();\n } catch {\n // Ignore destroy errors\n }\n };\n\n peer.on('error', (err: Error) => {\n onError?.(err);\n stop();\n });\n\n peer.on('open', () => {\n const conn = peer.connect(normalizedCode, { reliable: true });\n\n conn.on('open', () => {\n onStatus?.({ phase: 'connected', message: 'Waiting for file details...' });\n });\n\n conn.on('data', async (data: unknown) => {\n try {\n // Handle control messages\n if (\n data &&\n typeof data === 'object' &&\n !(data instanceof ArrayBuffer) &&\n !ArrayBuffer.isView(data)\n ) {\n const msg = data as {\n t?: string;\n name?: string;\n size?: number;\n message?: string;\n };\n\n if (msg.t === 'meta') {\n const name = String(msg.name || 'file');\n total = Number(msg.size) || 0;\n received = 0;\n writeQueue = Promise.resolve();\n\n onMeta?.({ name, total });\n onProgress?.({ received, total, percent: 0 });\n\n try {\n conn.send({ t: 'ready' });\n } catch {\n // Ignore send errors\n }\n return;\n }\n\n if (msg.t === 'end') {\n await writeQueue;\n\n if (total && received < total) {\n const err = new DropgateNetworkError(\n 'Transfer ended before the full file was received.'\n );\n try {\n conn.send({ t: 'error', message: err.message });\n } catch {\n // Ignore send errors\n }\n throw err;\n }\n\n onComplete?.({ received, total });\n\n try {\n conn.send({ t: 'ack', phase: 'end', received, total });\n } catch {\n // Ignore send errors\n }\n return;\n }\n\n if (msg.t === 'error') {\n throw new DropgateNetworkError(msg.message || 'Sender reported an error.');\n }\n\n return;\n }\n\n // Handle binary data\n let bufPromise: Promise<Uint8Array>;\n\n if (data instanceof ArrayBuffer) {\n bufPromise = Promise.resolve(new Uint8Array(data));\n } else if (ArrayBuffer.isView(data)) {\n bufPromise = Promise.resolve(\n new Uint8Array(data.buffer, data.byteOffset, data.byteLength)\n );\n } else if (typeof Blob !== 'undefined' && data instanceof Blob) {\n bufPromise = data.arrayBuffer().then((buffer) => new Uint8Array(buffer));\n } else {\n return;\n }\n\n writeQueue = writeQueue\n .then(async () => {\n const buf = await bufPromise;\n\n // Call consumer's onData handler\n if (onData) {\n await onData(buf);\n }\n\n received += buf.byteLength;\n const percent = total ? Math.min(100, (received / total) * 100) : 0;\n onProgress?.({ received, total, percent });\n\n const now = Date.now();\n if (received === total || now - lastProgressSentAt >= progressIntervalMs) {\n lastProgressSentAt = now;\n try {\n conn.send({ t: 'progress', received, total });\n } catch {\n // Ignore send errors\n }\n }\n })\n .catch((err) => {\n try {\n conn.send({\n t: 'error',\n message: (err as Error)?.message || 'Receiver write failed.',\n });\n } catch {\n // Ignore send errors\n }\n onError?.(err as Error);\n stop();\n });\n } catch (err) {\n onError?.(err as Error);\n stop();\n }\n });\n\n conn.on('close', () => {\n if (received > 0 && total > 0 && received < total) {\n onDisconnect?.();\n }\n });\n });\n\n return { peer, stop };\n}\n"],"mappings":"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","names":["result"]}
|
|
1
|
+
{"version":3,"sources":["../src/constants.ts","../src/errors.ts","../src/adapters/defaults.ts","../src/utils/base64.ts","../src/utils/lifetime.ts","../src/utils/semver.ts","../src/utils/filename.ts","../src/utils/network.ts","../src/crypto/decrypt.ts","../src/crypto/index.ts","../src/crypto/encrypt.ts","../src/client/DropgateClient.ts","../src/p2p/utils.ts","../src/p2p/helpers.ts","../src/p2p/send.ts","../src/p2p/receive.ts"],"sourcesContent":["/**\r\n * Default chunk size for file uploads (5MB)\r\n */\r\nexport const DEFAULT_CHUNK_SIZE = 5 * 1024 * 1024;\r\n\r\n/**\r\n * AES-GCM initialization vector size in bytes\r\n */\r\nexport const AES_GCM_IV_BYTES = 12;\r\n\r\n/**\r\n * AES-GCM authentication tag size in bytes\r\n */\r\nexport const AES_GCM_TAG_BYTES = 16;\r\n\r\n/**\r\n * Total encryption overhead per chunk (IV + tag)\r\n */\r\nexport const ENCRYPTION_OVERHEAD_PER_CHUNK = AES_GCM_IV_BYTES + AES_GCM_TAG_BYTES;\r\n\r\n/**\r\n * Maximum file size (in bytes) that can be downloaded without an onData callback.\r\n * Files larger than this require streaming via onData to avoid memory exhaustion.\r\n * Default: 100MB\r\n */\r\nexport const MAX_IN_MEMORY_DOWNLOAD_BYTES = 100 * 1024 * 1024;\r\n","export interface DropgateErrorOptions {\r\n code?: string;\r\n details?: unknown;\r\n cause?: unknown;\r\n}\r\n\r\n/**\r\n * Base error class for all Dropgate errors\r\n */\r\nexport class DropgateError extends Error {\r\n readonly code: string;\r\n readonly details?: unknown;\r\n\r\n constructor(message: string, opts: DropgateErrorOptions = {}) {\r\n super(message);\r\n this.name = this.constructor.name;\r\n this.code = opts.code || 'DROPGATE_ERROR';\r\n this.details = opts.details;\r\n if (opts.cause !== undefined) {\r\n // Use Object.defineProperty for cause to maintain compatibility\r\n Object.defineProperty(this, 'cause', {\r\n value: opts.cause,\r\n writable: false,\r\n enumerable: false,\r\n configurable: true,\r\n });\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Validation error for invalid inputs\r\n */\r\nexport class DropgateValidationError extends DropgateError {\r\n constructor(message: string, opts: DropgateErrorOptions = {}) {\r\n super(message, { ...opts, code: opts.code || 'VALIDATION_ERROR' });\r\n }\r\n}\r\n\r\n/**\r\n * Network error for connection issues\r\n */\r\nexport class DropgateNetworkError extends DropgateError {\r\n constructor(message: string, opts: DropgateErrorOptions = {}) {\r\n super(message, { ...opts, code: opts.code || 'NETWORK_ERROR' });\r\n }\r\n}\r\n\r\n/**\r\n * Protocol error for server communication issues\r\n */\r\nexport class DropgateProtocolError extends DropgateError {\r\n constructor(message: string, opts: DropgateErrorOptions = {}) {\r\n super(message, { ...opts, code: opts.code || 'PROTOCOL_ERROR' });\r\n }\r\n}\r\n\r\n/**\r\n * Abort error - replacement for DOMException with AbortError name\r\n * Used when operations are cancelled\r\n */\r\nexport class DropgateAbortError extends DropgateError {\r\n constructor(message = 'Operation aborted') {\r\n super(message, { code: 'ABORT_ERROR' });\r\n this.name = 'AbortError';\r\n }\r\n}\r\n\r\n/**\r\n * Timeout error - replacement for DOMException with TimeoutError name\r\n * Used when operations exceed their time limit\r\n */\r\nexport class DropgateTimeoutError extends DropgateError {\r\n constructor(message = 'Request timed out') {\r\n super(message, { code: 'TIMEOUT_ERROR' });\r\n this.name = 'TimeoutError';\r\n }\r\n}\r\n","import type { Base64Adapter, CryptoAdapter, FetchFn } from '../types.js';\r\n\r\n/**\r\n * Get the default Base64 adapter for the current environment.\r\n * Automatically detects Node.js Buffer vs browser btoa/atob.\r\n */\r\nexport function getDefaultBase64(): Base64Adapter {\r\n // Check for Node.js Buffer (works in Node.js and some bundlers)\r\n if (typeof Buffer !== 'undefined' && typeof Buffer.from === 'function') {\r\n return {\r\n encode(bytes: Uint8Array): string {\r\n return Buffer.from(bytes).toString('base64');\r\n },\r\n decode(b64: string): Uint8Array {\r\n return new Uint8Array(Buffer.from(b64, 'base64'));\r\n },\r\n };\r\n }\r\n\r\n // Browser fallback using btoa/atob\r\n if (typeof btoa === 'function' && typeof atob === 'function') {\r\n return {\r\n encode(bytes: Uint8Array): string {\r\n let binary = '';\r\n for (let i = 0; i < bytes.length; i++) {\r\n binary += String.fromCharCode(bytes[i]);\r\n }\r\n return btoa(binary);\r\n },\r\n decode(b64: string): Uint8Array {\r\n const binary = atob(b64);\r\n const out = new Uint8Array(binary.length);\r\n for (let i = 0; i < binary.length; i++) {\r\n out[i] = binary.charCodeAt(i);\r\n }\r\n return out;\r\n },\r\n };\r\n }\r\n\r\n throw new Error(\r\n 'No Base64 implementation available. Provide a Base64Adapter via options.'\r\n );\r\n}\r\n\r\n/**\r\n * Get the default crypto object for the current environment.\r\n * Returns globalThis.crypto if available.\r\n */\r\nexport function getDefaultCrypto(): CryptoAdapter | undefined {\r\n return globalThis.crypto as CryptoAdapter | undefined;\r\n}\r\n\r\n/**\r\n * Get the default fetch function for the current environment.\r\n * Returns globalThis.fetch if available.\r\n */\r\nexport function getDefaultFetch(): FetchFn | undefined {\r\n return globalThis.fetch?.bind(globalThis) as FetchFn | undefined;\r\n}\r\n","import type { Base64Adapter } from '../types.js';\r\nimport { getDefaultBase64 } from '../adapters/defaults.js';\r\n\r\nlet defaultAdapter: Base64Adapter | null = null;\r\n\r\nfunction getAdapter(adapter?: Base64Adapter): Base64Adapter {\r\n if (adapter) return adapter;\r\n if (!defaultAdapter) {\r\n defaultAdapter = getDefaultBase64();\r\n }\r\n return defaultAdapter;\r\n}\r\n\r\n/**\r\n * Convert a Uint8Array to a base64 string\r\n */\r\nexport function bytesToBase64(bytes: Uint8Array, adapter?: Base64Adapter): string {\r\n return getAdapter(adapter).encode(bytes);\r\n}\r\n\r\n/**\r\n * Convert an ArrayBuffer to a base64 string\r\n */\r\nexport function arrayBufferToBase64(buf: ArrayBuffer, adapter?: Base64Adapter): string {\r\n return bytesToBase64(new Uint8Array(buf), adapter);\r\n}\r\n\r\n/**\r\n * Convert a base64 string to a Uint8Array\r\n */\r\nexport function base64ToBytes(b64: string, adapter?: Base64Adapter): Uint8Array {\r\n return getAdapter(adapter).decode(b64);\r\n}\r\n","type LifetimeUnit = 'minutes' | 'hours' | 'days' | 'unlimited';\r\n\r\nconst MULTIPLIERS: Record<string, number> = {\r\n minutes: 60 * 1000,\r\n hours: 60 * 60 * 1000,\r\n days: 24 * 60 * 60 * 1000,\r\n};\r\n\r\n/**\r\n * Convert a lifetime value and unit to milliseconds.\r\n * Returns 0 for 'unlimited' or invalid inputs.\r\n */\r\nexport function lifetimeToMs(value: number, unit: LifetimeUnit | string): number {\r\n const u = String(unit || '').toLowerCase();\r\n const v = Number(value);\r\n\r\n if (u === 'unlimited') return 0;\r\n if (!Number.isFinite(v) || v <= 0) return 0;\r\n\r\n const m = MULTIPLIERS[u];\r\n if (!m) return 0;\r\n\r\n return Math.round(v * m);\r\n}\r\n","export interface SemverParts {\r\n major: number;\r\n minor: number;\r\n}\r\n\r\n/**\r\n * Parse a semver string and extract major.minor parts.\r\n * Returns { major: 0, minor: 0 } for invalid inputs.\r\n */\r\nexport function parseSemverMajorMinor(version: string | undefined | null): SemverParts {\r\n const parts = String(version || '')\r\n .split('.')\r\n .map((p) => Number(p));\r\n\r\n const major = Number.isFinite(parts[0]) ? parts[0] : 0;\r\n const minor = Number.isFinite(parts[1]) ? parts[1] : 0;\r\n\r\n return { major, minor };\r\n}\r\n","import { DropgateValidationError } from '../errors.js';\r\n\r\n/**\r\n * Validate a plain (non-encrypted) filename.\r\n * Throws DropgateValidationError if invalid.\r\n */\r\nexport function validatePlainFilename(filename: string): void {\r\n if (typeof filename !== 'string' || filename.trim().length === 0) {\r\n throw new DropgateValidationError(\r\n 'Invalid filename. Must be a non-empty string.'\r\n );\r\n }\r\n\r\n if (filename.length > 255 || /[\\/\\\\]/.test(filename)) {\r\n throw new DropgateValidationError(\r\n 'Invalid filename. Contains illegal characters or is too long.'\r\n );\r\n }\r\n}\r\n","import { DropgateAbortError, DropgateTimeoutError, DropgateValidationError } from '../errors.js';\r\nimport type { FetchFn, ServerTarget } from '../types.js';\r\n\r\n/**\r\n * Parse a server URL string into host, port, and secure components.\r\n * If no protocol is specified, defaults to HTTPS.\r\n */\r\nexport function parseServerUrl(urlStr: string): ServerTarget {\r\n let normalized = urlStr.trim();\r\n if (!normalized.startsWith('http://') && !normalized.startsWith('https://')) {\r\n normalized = 'https://' + normalized;\r\n }\r\n const url = new URL(normalized);\r\n return {\r\n host: url.hostname,\r\n port: url.port ? Number(url.port) : undefined,\r\n secure: url.protocol === 'https:',\r\n };\r\n}\r\n\r\n/**\r\n * Build a base URL from host, port, and secure options.\r\n */\r\nexport function buildBaseUrl(opts: ServerTarget): string {\r\n const { host, port, secure } = opts;\r\n\r\n if (!host || typeof host !== 'string') {\r\n throw new DropgateValidationError('Server host is required.');\r\n }\r\n\r\n const protocol = secure === false ? 'http' : 'https';\r\n const portSuffix = port ? `:${port}` : '';\r\n\r\n return `${protocol}://${host}${portSuffix}`;\r\n}\r\n\r\n/**\r\n * Sleep for a specified duration, with optional abort signal support.\r\n */\r\nexport function sleep(ms: number, signal?: AbortSignal): Promise<void> {\r\n return new Promise((resolve, reject) => {\r\n if (signal?.aborted) {\r\n return reject(signal.reason || new DropgateAbortError());\r\n }\r\n\r\n const t = setTimeout(resolve, ms);\r\n\r\n if (signal) {\r\n signal.addEventListener(\r\n 'abort',\r\n () => {\r\n clearTimeout(t);\r\n reject(signal.reason || new DropgateAbortError());\r\n },\r\n { once: true }\r\n );\r\n }\r\n });\r\n}\r\n\r\nexport interface AbortSignalWithCleanup {\r\n signal: AbortSignal;\r\n cleanup: () => void;\r\n}\r\n\r\n/**\r\n * Create an AbortSignal that combines a parent signal with a timeout.\r\n */\r\nexport function makeAbortSignal(\r\n parentSignal?: AbortSignal | null,\r\n timeoutMs?: number\r\n): AbortSignalWithCleanup {\r\n const controller = new AbortController();\r\n let timeoutId: ReturnType<typeof setTimeout> | null = null;\r\n\r\n const abort = (reason?: unknown): void => {\r\n if (!controller.signal.aborted) {\r\n controller.abort(reason);\r\n }\r\n };\r\n\r\n if (parentSignal) {\r\n if (parentSignal.aborted) {\r\n abort(parentSignal.reason);\r\n } else {\r\n parentSignal.addEventListener('abort', () => abort(parentSignal.reason), {\r\n once: true,\r\n });\r\n }\r\n }\r\n\r\n if (Number.isFinite(timeoutMs) && timeoutMs! > 0) {\r\n timeoutId = setTimeout(() => {\r\n abort(new DropgateTimeoutError());\r\n }, timeoutMs);\r\n }\r\n\r\n return {\r\n signal: controller.signal,\r\n cleanup: () => {\r\n if (timeoutId) clearTimeout(timeoutId);\r\n },\r\n };\r\n}\r\n\r\nexport interface FetchJsonResult {\r\n res: Response;\r\n json: unknown;\r\n text: string;\r\n}\r\n\r\nexport interface FetchJsonOptions extends Omit<RequestInit, 'signal'> {\r\n timeoutMs?: number;\r\n signal?: AbortSignal;\r\n}\r\n\r\n/**\r\n * Fetch JSON from a URL with timeout and error handling.\r\n */\r\nexport async function fetchJson(\r\n fetchFn: FetchFn,\r\n url: string,\r\n opts: FetchJsonOptions = {}\r\n): Promise<FetchJsonResult> {\r\n const { timeoutMs, signal, ...rest } = opts;\r\n const { signal: s, cleanup } = makeAbortSignal(signal, timeoutMs);\r\n\r\n try {\r\n const res = await fetchFn(url, { ...rest, signal: s });\r\n const text = await res.text();\r\n\r\n let json: unknown = null;\r\n try {\r\n json = text ? JSON.parse(text) : null;\r\n } catch {\r\n // Ignore parse errors - json will remain null\r\n }\r\n\r\n return { res, json, text };\r\n } finally {\r\n cleanup();\r\n }\r\n}\r\n","import { AES_GCM_IV_BYTES } from '../constants.js';\r\nimport type { CryptoAdapter, Base64Adapter } from '../types.js';\r\nimport { getDefaultBase64 } from '../adapters/defaults.js';\r\n\r\n/**\r\n * Import a base64-encoded AES-GCM key.\r\n * @param cryptoObj - Crypto adapter for key import.\r\n * @param keyB64 - Base64-encoded key bytes.\r\n * @param base64 - Optional base64 adapter.\r\n * @returns The imported CryptoKey.\r\n */\r\nexport async function importKeyFromBase64(\r\n cryptoObj: CryptoAdapter,\r\n keyB64: string,\r\n base64?: Base64Adapter\r\n): Promise<CryptoKey> {\r\n const adapter = base64 || getDefaultBase64();\r\n const keyBytes = adapter.decode(keyB64);\r\n // Create a new ArrayBuffer copy to satisfy TypeScript's BufferSource type\r\n const keyBuffer = new Uint8Array(keyBytes).buffer;\r\n return cryptoObj.subtle.importKey(\r\n 'raw',\r\n keyBuffer,\r\n { name: 'AES-GCM' },\r\n true,\r\n ['decrypt']\r\n );\r\n}\r\n\r\n/**\r\n * Decrypt an AES-GCM encrypted chunk.\r\n * Expected layout: [IV (12 bytes)] + [ciphertext + tag]\r\n * @param cryptoObj - Crypto adapter for decryption.\r\n * @param encryptedData - The encrypted data with IV prepended.\r\n * @param key - The AES-GCM decryption key.\r\n * @returns The decrypted data as ArrayBuffer.\r\n */\r\nexport async function decryptChunk(\r\n cryptoObj: CryptoAdapter,\r\n encryptedData: Uint8Array,\r\n key: CryptoKey\r\n): Promise<ArrayBuffer> {\r\n const iv = encryptedData.slice(0, AES_GCM_IV_BYTES);\r\n const ciphertext = encryptedData.slice(AES_GCM_IV_BYTES);\r\n return cryptoObj.subtle.decrypt(\r\n { name: 'AES-GCM', iv },\r\n key,\r\n ciphertext\r\n );\r\n}\r\n\r\n/**\r\n * Decrypt a base64-encoded encrypted filename.\r\n * @param cryptoObj - Crypto adapter for decryption.\r\n * @param encryptedFilenameB64 - Base64-encoded encrypted filename.\r\n * @param key - The AES-GCM decryption key.\r\n * @param base64 - Optional base64 adapter.\r\n * @returns The decrypted filename string.\r\n */\r\nexport async function decryptFilenameFromBase64(\r\n cryptoObj: CryptoAdapter,\r\n encryptedFilenameB64: string,\r\n key: CryptoKey,\r\n base64?: Base64Adapter\r\n): Promise<string> {\r\n const adapter = base64 || getDefaultBase64();\r\n const encryptedBytes = adapter.decode(encryptedFilenameB64);\r\n const decryptedBuffer = await decryptChunk(cryptoObj, encryptedBytes, key);\r\n return new TextDecoder().decode(decryptedBuffer);\r\n}\r\n","import type { CryptoAdapter } from '../types.js';\r\nimport { arrayBufferToBase64 } from '../utils/base64.js';\r\n\r\n/**\r\n * Compute SHA-256 hash of data and return as hex string.\r\n */\r\nexport async function sha256Hex(\r\n cryptoObj: CryptoAdapter,\r\n data: ArrayBuffer\r\n): Promise<string> {\r\n const hashBuffer = await cryptoObj.subtle.digest('SHA-256', data);\r\n const arr = new Uint8Array(hashBuffer);\r\n let hex = '';\r\n for (let i = 0; i < arr.length; i++) {\r\n hex += arr[i].toString(16).padStart(2, '0');\r\n }\r\n return hex;\r\n}\r\n\r\n/**\r\n * Generate a new AES-GCM 256-bit encryption key.\r\n */\r\nexport async function generateAesGcmKey(\r\n cryptoObj: CryptoAdapter\r\n): Promise<CryptoKey> {\r\n return cryptoObj.subtle.generateKey(\r\n { name: 'AES-GCM', length: 256 },\r\n true,\r\n ['encrypt', 'decrypt']\r\n );\r\n}\r\n\r\n/**\r\n * Export a CryptoKey to a base64-encoded raw key.\r\n */\r\nexport async function exportKeyBase64(\r\n cryptoObj: CryptoAdapter,\r\n key: CryptoKey\r\n): Promise<string> {\r\n const raw = await cryptoObj.subtle.exportKey('raw', key);\r\n return arrayBufferToBase64(raw);\r\n}\r\n\r\n// Re-export decryption functions\r\nexport { importKeyFromBase64, decryptChunk, decryptFilenameFromBase64 } from './decrypt.js';\r\n","import { AES_GCM_IV_BYTES } from '../constants.js';\r\nimport type { CryptoAdapter } from '../types.js';\r\nimport { arrayBufferToBase64 } from '../utils/base64.js';\r\n\r\n/**\r\n * Encrypt data using AES-GCM and return as a Blob with IV prepended.\r\n * Layout: [IV (12 bytes)] + [ciphertext + tag]\r\n */\r\nexport async function encryptToBlob(\r\n cryptoObj: CryptoAdapter,\r\n dataBuffer: ArrayBuffer,\r\n key: CryptoKey\r\n): Promise<Blob> {\r\n const iv = cryptoObj.getRandomValues(new Uint8Array(AES_GCM_IV_BYTES));\r\n const encrypted = await cryptoObj.subtle.encrypt(\r\n { name: 'AES-GCM', iv },\r\n key,\r\n dataBuffer\r\n );\r\n return new Blob([iv, new Uint8Array(encrypted)]);\r\n}\r\n\r\n/**\r\n * Encrypt a filename using AES-GCM and return as base64.\r\n */\r\nexport async function encryptFilenameToBase64(\r\n cryptoObj: CryptoAdapter,\r\n filename: string,\r\n key: CryptoKey\r\n): Promise<string> {\r\n const bytes = new TextEncoder().encode(String(filename));\r\n const blob = await encryptToBlob(cryptoObj, bytes.buffer, key);\r\n const buf = await blob.arrayBuffer();\r\n return arrayBufferToBase64(buf);\r\n}\r\n","import { DEFAULT_CHUNK_SIZE, ENCRYPTION_OVERHEAD_PER_CHUNK, MAX_IN_MEMORY_DOWNLOAD_BYTES } from '../constants.js';\r\nimport {\r\n DropgateError,\r\n DropgateValidationError,\r\n DropgateNetworkError,\r\n DropgateProtocolError,\r\n DropgateAbortError,\r\n} from '../errors.js';\r\nimport type {\r\n CryptoAdapter,\r\n FetchFn,\r\n LoggerFn,\r\n ServerInfo,\r\n CompatibilityResult,\r\n ShareTargetResult,\r\n UploadResult,\r\n UploadProgressEvent,\r\n DropgateClientOptions,\r\n UploadOptions,\r\n GetServerInfoOptions,\r\n ValidateUploadOptions,\r\n FileSource,\r\n Base64Adapter,\r\n DownloadOptions,\r\n DownloadResult,\r\n DownloadProgressEvent,\r\n FileMetadata,\r\n} from '../types.js';\r\nimport { getDefaultCrypto, getDefaultFetch, getDefaultBase64 } from '../adapters/defaults.js';\r\nimport { makeAbortSignal, fetchJson, sleep, buildBaseUrl } from '../utils/network.js';\r\nimport { parseSemverMajorMinor } from '../utils/semver.js';\r\nimport { validatePlainFilename } from '../utils/filename.js';\r\nimport { sha256Hex, generateAesGcmKey, exportKeyBase64, importKeyFromBase64, decryptChunk, decryptFilenameFromBase64 } from '../crypto/index.js';\r\nimport { encryptToBlob, encryptFilenameToBase64 } from '../crypto/encrypt.js';\r\n\r\n/**\r\n * Estimate total upload size including encryption overhead.\r\n */\r\nexport function estimateTotalUploadSizeBytes(\r\n fileSizeBytes: number,\r\n totalChunks: number,\r\n isEncrypted: boolean\r\n): number {\r\n const base = Number(fileSizeBytes) || 0;\r\n if (!isEncrypted) return base;\r\n return base + (Number(totalChunks) || 0) * ENCRYPTION_OVERHEAD_PER_CHUNK;\r\n}\r\n\r\n/**\r\n * Fetch server information from the /api/info endpoint.\r\n * @param opts - Server target and request options.\r\n * @returns The server base URL and server info object.\r\n * @throws {DropgateNetworkError} If the server cannot be reached.\r\n * @throws {DropgateProtocolError} If the server returns an invalid response.\r\n */\r\nexport async function getServerInfo(\r\n opts: GetServerInfoOptions\r\n): Promise<{ baseUrl: string; serverInfo: ServerInfo }> {\r\n const { host, port, secure, timeoutMs = 5000, signal, fetchFn: customFetch } = opts;\r\n\r\n const fetchFn = customFetch || getDefaultFetch();\r\n if (!fetchFn) {\r\n throw new DropgateValidationError('No fetch() implementation found.');\r\n }\r\n\r\n const baseUrl = buildBaseUrl({ host, port, secure });\r\n\r\n try {\r\n const { res, json } = await fetchJson(\r\n fetchFn,\r\n `${baseUrl}/api/info`,\r\n {\r\n method: 'GET',\r\n timeoutMs,\r\n signal,\r\n headers: { Accept: 'application/json' },\r\n }\r\n );\r\n\r\n if (res.ok && json && typeof json === 'object' && 'version' in json) {\r\n return { baseUrl, serverInfo: json as ServerInfo };\r\n }\r\n\r\n throw new DropgateProtocolError(\r\n `Server info request failed (status ${res.status}).`\r\n );\r\n } catch (err) {\r\n if (err instanceof DropgateError) throw err;\r\n throw new DropgateNetworkError('Could not reach server /api/info.', {\r\n cause: err,\r\n });\r\n }\r\n}\r\n\r\n/**\r\n * Headless, environment-agnostic client for Dropgate file uploads.\r\n * Handles server communication, encryption, and chunked uploads.\r\n */\r\nexport class DropgateClient {\r\n /** Client version string for compatibility checking. */\r\n readonly clientVersion: string;\r\n /** Chunk size in bytes for upload splitting. */\r\n readonly chunkSize: number;\r\n /** Fetch implementation used for HTTP requests. */\r\n readonly fetchFn: FetchFn;\r\n /** Crypto implementation for encryption operations. */\r\n readonly cryptoObj: CryptoAdapter;\r\n /** Base64 encoder/decoder for binary data. */\r\n readonly base64: Base64Adapter;\r\n /** Optional logger for debug output. */\r\n readonly logger: LoggerFn | null;\r\n\r\n /**\r\n * Create a new DropgateClient instance.\r\n * @param opts - Client configuration options.\r\n * @throws {DropgateValidationError} If clientVersion is missing or invalid.\r\n */\r\n constructor(opts: DropgateClientOptions) {\r\n if (!opts || typeof opts.clientVersion !== 'string') {\r\n throw new DropgateValidationError(\r\n 'DropgateClient requires clientVersion (string).'\r\n );\r\n }\r\n\r\n this.clientVersion = opts.clientVersion;\r\n this.chunkSize = Number.isFinite(opts.chunkSize)\r\n ? opts.chunkSize!\r\n : DEFAULT_CHUNK_SIZE;\r\n\r\n const fetchFn = opts.fetchFn || getDefaultFetch();\r\n if (!fetchFn) {\r\n throw new DropgateValidationError('No fetch() implementation found.');\r\n }\r\n this.fetchFn = fetchFn;\r\n\r\n const cryptoObj = opts.cryptoObj || getDefaultCrypto();\r\n if (!cryptoObj) {\r\n throw new DropgateValidationError('No crypto implementation found.');\r\n }\r\n this.cryptoObj = cryptoObj;\r\n\r\n this.base64 = opts.base64 || getDefaultBase64();\r\n this.logger = opts.logger || null;\r\n }\r\n\r\n /**\r\n * Resolve a user-entered sharing code or URL via the server.\r\n * @param value - The sharing code or URL to resolve.\r\n * @param opts - Server target and request options.\r\n * @returns The resolved share target information.\r\n * @throws {DropgateProtocolError} If the share lookup fails.\r\n */\r\n async resolveShareTarget(\r\n value: string,\r\n opts: GetServerInfoOptions\r\n ): Promise<ShareTargetResult> {\r\n const { timeoutMs = 5000, signal } = opts;\r\n\r\n // Check server compatibility before resolving\r\n const compat = await this.checkCompatibility(opts);\r\n if (!compat.compatible) {\r\n throw new DropgateValidationError(compat.message);\r\n }\r\n\r\n const { baseUrl } = compat;\r\n\r\n const { res, json } = await fetchJson(\r\n this.fetchFn,\r\n `${baseUrl}/api/resolve`,\r\n {\r\n method: 'POST',\r\n timeoutMs,\r\n signal,\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n Accept: 'application/json',\r\n },\r\n body: JSON.stringify({ value }),\r\n }\r\n );\r\n\r\n if (!res.ok) {\r\n const msg =\r\n (json && typeof json === 'object' && 'error' in json\r\n ? (json as { error: string }).error\r\n : null) || `Share lookup failed (status ${res.status}).`;\r\n throw new DropgateProtocolError(msg, { details: json });\r\n }\r\n\r\n return (json as ShareTargetResult) || { valid: false, reason: 'Unknown response.' };\r\n }\r\n\r\n /**\r\n * Check version compatibility between this client and a server.\r\n * Fetches server info internally using getServerInfo.\r\n * @param opts - Server target and request options.\r\n * @returns Compatibility result with status, message, and server info.\r\n * @throws {DropgateNetworkError} If the server cannot be reached.\r\n * @throws {DropgateProtocolError} If the server returns an invalid response.\r\n */\r\n async checkCompatibility(\r\n opts: GetServerInfoOptions\r\n ): Promise<CompatibilityResult & { serverInfo: ServerInfo; baseUrl: string }> {\r\n let baseUrl: string;\r\n let serverInfo: ServerInfo;\r\n\r\n try {\r\n const result = await getServerInfo({ ...opts, fetchFn: this.fetchFn });\r\n baseUrl = result.baseUrl;\r\n serverInfo = result.serverInfo;\r\n } catch (err) {\r\n if (err instanceof DropgateError) throw err;\r\n throw new DropgateNetworkError('Could not connect to the server.', {\r\n cause: err,\r\n });\r\n }\r\n\r\n const serverVersion = String(serverInfo?.version || '0.0.0');\r\n const clientVersion = String(this.clientVersion || '0.0.0');\r\n\r\n const c = parseSemverMajorMinor(clientVersion);\r\n const s = parseSemverMajorMinor(serverVersion);\r\n\r\n if (c.major !== s.major) {\r\n return {\r\n compatible: false,\r\n clientVersion,\r\n serverVersion,\r\n message: `Incompatible versions. Client v${clientVersion}, Server v${serverVersion}${serverInfo?.name ? ` (${serverInfo.name})` : ''}.`,\r\n serverInfo,\r\n baseUrl,\r\n };\r\n }\r\n\r\n if (c.minor > s.minor) {\r\n return {\r\n compatible: true,\r\n clientVersion,\r\n serverVersion,\r\n message: `Client (v${clientVersion}) is newer than Server (v${serverVersion})${serverInfo?.name ? ` (${serverInfo.name})` : ''}. Some features may not work.`,\r\n serverInfo,\r\n baseUrl,\r\n };\r\n }\r\n\r\n return {\r\n compatible: true,\r\n clientVersion,\r\n serverVersion,\r\n message: `Server: v${serverVersion}, Client: v${clientVersion}${serverInfo?.name ? ` (${serverInfo.name})` : ''}.`,\r\n serverInfo,\r\n baseUrl,\r\n };\r\n }\r\n\r\n /**\r\n * Validate file and upload settings against server capabilities.\r\n * @param opts - Validation options containing file, settings, and server info.\r\n * @returns True if validation passes.\r\n * @throws {DropgateValidationError} If any validation check fails.\r\n */\r\n validateUploadInputs(opts: ValidateUploadOptions): boolean {\r\n const { file, lifetimeMs, encrypt, serverInfo } = opts;\r\n const caps = serverInfo?.capabilities?.upload;\r\n\r\n if (!caps || !caps.enabled) {\r\n throw new DropgateValidationError('Server does not support file uploads.');\r\n }\r\n\r\n // Check file validity\r\n const fileSize = Number(file?.size || 0);\r\n if (!file || !Number.isFinite(fileSize) || fileSize <= 0) {\r\n throw new DropgateValidationError('File is missing or invalid.');\r\n }\r\n\r\n // maxSizeMB: 0 means unlimited\r\n const maxMB = Number(caps.maxSizeMB);\r\n if (Number.isFinite(maxMB) && maxMB > 0) {\r\n const limitBytes = maxMB * 1000 * 1000;\r\n const totalChunks = Math.ceil(fileSize / this.chunkSize);\r\n const estimatedBytes = estimateTotalUploadSizeBytes(\r\n fileSize,\r\n totalChunks,\r\n Boolean(encrypt)\r\n );\r\n if (estimatedBytes > limitBytes) {\r\n const msg = encrypt\r\n ? `File too large once encryption overhead is included. Server limit: ${maxMB} MB.`\r\n : `File too large. Server limit: ${maxMB} MB.`;\r\n throw new DropgateValidationError(msg);\r\n }\r\n }\r\n\r\n // maxLifetimeHours: 0 means unlimited is allowed\r\n const maxHours = Number(caps.maxLifetimeHours);\r\n const lt = Number(lifetimeMs);\r\n if (!Number.isFinite(lt) || lt < 0 || !Number.isInteger(lt)) {\r\n throw new DropgateValidationError(\r\n 'Invalid lifetime. Must be a non-negative integer (milliseconds).'\r\n );\r\n }\r\n\r\n if (Number.isFinite(maxHours) && maxHours > 0) {\r\n const limitMs = Math.round(maxHours * 60 * 60 * 1000);\r\n if (lt === 0) {\r\n throw new DropgateValidationError(\r\n `Server does not allow unlimited file lifetime. Max: ${maxHours} hours.`\r\n );\r\n }\r\n if (lt > limitMs) {\r\n throw new DropgateValidationError(\r\n `File lifetime too long. Server limit: ${maxHours} hours.`\r\n );\r\n }\r\n }\r\n\r\n // Encryption support\r\n if (encrypt && !caps.e2ee) {\r\n throw new DropgateValidationError(\r\n 'Server does not support end-to-end encryption.'\r\n );\r\n }\r\n\r\n return true;\r\n }\r\n\r\n /**\r\n * Upload a file to the server with optional encryption.\r\n * @param opts - Upload options including file, server target, and settings.\r\n * @returns Upload result containing the download URL and file identifiers.\r\n * @throws {DropgateValidationError} If input validation fails.\r\n * @throws {DropgateNetworkError} If the server cannot be reached.\r\n * @throws {DropgateProtocolError} If the server returns an error.\r\n * @throws {DropgateAbortError} If the upload is cancelled.\r\n */\r\n async uploadFile(opts: UploadOptions): Promise<UploadResult> {\r\n const {\r\n host,\r\n port,\r\n secure,\r\n file,\r\n lifetimeMs,\r\n encrypt,\r\n filenameOverride,\r\n onProgress,\r\n signal,\r\n timeouts = {},\r\n retry = {},\r\n } = opts;\r\n\r\n const progress = (evt: UploadProgressEvent): void => {\r\n try {\r\n if (onProgress) onProgress(evt);\r\n } catch {\r\n // Ignore UI callback failures\r\n }\r\n };\r\n\r\n if (!this.cryptoObj?.subtle) {\r\n throw new DropgateValidationError(\r\n 'Web Crypto API not available (crypto.subtle).'\r\n );\r\n }\r\n\r\n const fileSizeBytes = file.size;\r\n\r\n // 0) Get server info + compat\r\n progress({ phase: 'server-info', text: 'Checking server...', percent: 0, processedBytes: 0, totalBytes: fileSizeBytes });\r\n\r\n const compat = await this.checkCompatibility({\r\n host,\r\n port,\r\n secure,\r\n timeoutMs: timeouts.serverInfoMs ?? 5000,\r\n signal,\r\n });\r\n\r\n const { baseUrl, serverInfo } = compat;\r\n progress({ phase: 'server-compat', text: compat.message, percent: 0, processedBytes: 0, totalBytes: fileSizeBytes });\r\n if (!compat.compatible) {\r\n throw new DropgateValidationError(compat.message);\r\n }\r\n\r\n // 1) Validate inputs\r\n const filename = filenameOverride ?? file.name ?? 'file';\r\n\r\n if (!encrypt) {\r\n validatePlainFilename(filename);\r\n }\r\n\r\n this.validateUploadInputs({ file, lifetimeMs, encrypt, serverInfo });\r\n\r\n // 2) Encryption prep\r\n let cryptoKey: CryptoKey | null = null;\r\n let keyB64: string | null = null;\r\n let transmittedFilename = filename;\r\n\r\n if (encrypt) {\r\n progress({ phase: 'crypto', text: 'Generating encryption key...', percent: 0, processedBytes: 0, totalBytes: fileSizeBytes });\r\n try {\r\n cryptoKey = await generateAesGcmKey(this.cryptoObj);\r\n keyB64 = await exportKeyBase64(this.cryptoObj, cryptoKey);\r\n transmittedFilename = await encryptFilenameToBase64(\r\n this.cryptoObj,\r\n filename,\r\n cryptoKey\r\n );\r\n } catch (err) {\r\n throw new DropgateError('Failed to prepare encryption.', {\r\n code: 'CRYPTO_PREP_FAILED',\r\n cause: err,\r\n });\r\n }\r\n }\r\n\r\n // 3) Compute reservation sizes\r\n const totalChunks = Math.ceil(file.size / this.chunkSize);\r\n const totalUploadSize = estimateTotalUploadSizeBytes(\r\n file.size,\r\n totalChunks,\r\n encrypt\r\n );\r\n\r\n // 4) Init\r\n progress({ phase: 'init', text: 'Reserving server storage...', percent: 0, processedBytes: 0, totalBytes: fileSizeBytes });\r\n\r\n const initPayload = {\r\n filename: transmittedFilename,\r\n lifetime: lifetimeMs,\r\n isEncrypted: Boolean(encrypt),\r\n totalSize: totalUploadSize,\r\n totalChunks,\r\n };\r\n\r\n const initRes = await fetchJson(this.fetchFn, `${baseUrl}/upload/init`, {\r\n method: 'POST',\r\n timeoutMs: timeouts.initMs ?? 15000,\r\n signal,\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n Accept: 'application/json',\r\n },\r\n body: JSON.stringify(initPayload),\r\n });\r\n\r\n if (!initRes.res.ok) {\r\n const errorJson = initRes.json as { error?: string } | null;\r\n const msg =\r\n errorJson?.error ||\r\n `Server initialisation failed: ${initRes.res.status}`;\r\n throw new DropgateProtocolError(msg, {\r\n details: initRes.json || initRes.text,\r\n });\r\n }\r\n\r\n const initJson = initRes.json as { uploadId?: string } | null;\r\n const uploadId = initJson?.uploadId;\r\n if (!uploadId || typeof uploadId !== 'string') {\r\n throw new DropgateProtocolError(\r\n 'Server did not return a valid uploadId.'\r\n );\r\n }\r\n\r\n // 5) Chunks\r\n const retries = Number.isFinite(retry.retries) ? retry.retries! : 5;\r\n const baseBackoffMs = Number.isFinite(retry.backoffMs)\r\n ? retry.backoffMs!\r\n : 1000;\r\n const maxBackoffMs = Number.isFinite(retry.maxBackoffMs)\r\n ? retry.maxBackoffMs!\r\n : 30000;\r\n\r\n for (let i = 0; i < totalChunks; i++) {\r\n if (signal?.aborted) {\r\n throw signal.reason || new DropgateAbortError();\r\n }\r\n\r\n const start = i * this.chunkSize;\r\n const end = Math.min(start + this.chunkSize, file.size);\r\n let chunkBlob: Blob | FileSource = file.slice(start, end);\r\n\r\n const percentComplete = (i / totalChunks) * 100;\r\n const processedBytes = i * this.chunkSize;\r\n progress({\r\n phase: 'chunk',\r\n text: `Uploading chunk ${i + 1} of ${totalChunks}...`,\r\n percent: percentComplete,\r\n processedBytes,\r\n totalBytes: fileSizeBytes,\r\n chunkIndex: i,\r\n totalChunks,\r\n });\r\n\r\n // Get ArrayBuffer from the slice\r\n const chunkBuffer = await chunkBlob.arrayBuffer();\r\n\r\n // Encrypt if needed\r\n let uploadBlob: Blob;\r\n if (encrypt && cryptoKey) {\r\n uploadBlob = await encryptToBlob(this.cryptoObj, chunkBuffer, cryptoKey);\r\n } else {\r\n uploadBlob = new Blob([chunkBuffer]);\r\n }\r\n\r\n // Server validates: chunk <= 5MB + 1024\r\n if (uploadBlob.size > DEFAULT_CHUNK_SIZE + 1024) {\r\n throw new DropgateValidationError(\r\n 'Chunk too large (client-side). Check chunk size settings.'\r\n );\r\n }\r\n\r\n // Hash encrypted/plain payload\r\n const toHash = await uploadBlob.arrayBuffer();\r\n const hashHex = await sha256Hex(this.cryptoObj, toHash);\r\n\r\n const headers: Record<string, string> = {\r\n 'Content-Type': 'application/octet-stream',\r\n 'X-Upload-ID': uploadId,\r\n 'X-Chunk-Index': String(i),\r\n 'X-Chunk-Hash': hashHex,\r\n };\r\n\r\n const chunkUrl = `${baseUrl}/upload/chunk`;\r\n await this.attemptChunkUpload(\r\n chunkUrl,\r\n {\r\n method: 'POST',\r\n headers,\r\n body: uploadBlob,\r\n },\r\n {\r\n retries,\r\n backoffMs: baseBackoffMs,\r\n maxBackoffMs,\r\n timeoutMs: timeouts.chunkMs ?? 60000,\r\n signal,\r\n progress,\r\n chunkIndex: i,\r\n totalChunks,\r\n chunkSize: this.chunkSize,\r\n fileSizeBytes,\r\n }\r\n );\r\n }\r\n\r\n // 6) Complete\r\n progress({ phase: 'complete', text: 'Finalising upload...', percent: 100, processedBytes: fileSizeBytes, totalBytes: fileSizeBytes });\r\n\r\n const completeRes = await fetchJson(\r\n this.fetchFn,\r\n `${baseUrl}/upload/complete`,\r\n {\r\n method: 'POST',\r\n timeoutMs: timeouts.completeMs ?? 30000,\r\n signal,\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n Accept: 'application/json',\r\n },\r\n body: JSON.stringify({ uploadId }),\r\n }\r\n );\r\n\r\n if (!completeRes.res.ok) {\r\n const errorJson = completeRes.json as { error?: string } | null;\r\n const msg = errorJson?.error || 'Finalisation failed.';\r\n throw new DropgateProtocolError(msg, {\r\n details: completeRes.json || completeRes.text,\r\n });\r\n }\r\n\r\n const completeJson = completeRes.json as { id?: string } | null;\r\n const fileId = completeJson?.id;\r\n if (!fileId || typeof fileId !== 'string') {\r\n throw new DropgateProtocolError(\r\n 'Server did not return a valid file id.'\r\n );\r\n }\r\n\r\n let downloadUrl = `${baseUrl}/${fileId}`;\r\n if (encrypt && keyB64) {\r\n downloadUrl += `#${keyB64}`;\r\n }\r\n\r\n progress({ phase: 'done', text: 'Upload successful!', percent: 100, processedBytes: fileSizeBytes, totalBytes: fileSizeBytes });\r\n\r\n return {\r\n downloadUrl,\r\n fileId,\r\n uploadId,\r\n baseUrl,\r\n ...(encrypt && keyB64 ? { keyB64 } : {}),\r\n };\r\n }\r\n\r\n /**\r\n * Download a file from the server with optional decryption.\r\n *\r\n * **Important:** For large files, you must provide an `onData` callback to stream\r\n * data incrementally. Without it, the entire file is buffered in memory, which will\r\n * cause memory exhaustion for large files. Files exceeding 100MB without an `onData`\r\n * callback will throw a validation error.\r\n *\r\n * @param opts - Download options including file ID, server target, and optional key.\r\n * @param opts.onData - Streaming callback that receives data chunks. Required for files > 100MB.\r\n * @returns Download result containing filename and received bytes.\r\n * @throws {DropgateValidationError} If input validation fails or file is too large without onData.\r\n * @throws {DropgateNetworkError} If the server cannot be reached.\r\n * @throws {DropgateProtocolError} If the server returns an error.\r\n * @throws {DropgateAbortError} If the download is cancelled.\r\n */\r\n async downloadFile(opts: DownloadOptions): Promise<DownloadResult> {\r\n const {\r\n host,\r\n port,\r\n secure,\r\n fileId,\r\n keyB64,\r\n onProgress,\r\n onData,\r\n signal,\r\n timeoutMs = 60000,\r\n } = opts;\r\n\r\n const progress = (evt: DownloadProgressEvent): void => {\r\n try {\r\n if (onProgress) onProgress(evt);\r\n } catch {\r\n // Ignore UI callback failures\r\n }\r\n };\r\n\r\n if (!fileId || typeof fileId !== 'string') {\r\n throw new DropgateValidationError('File ID is required.');\r\n }\r\n\r\n // 0) Get server info + compat\r\n progress({ phase: 'server-info', text: 'Checking server...', processedBytes: 0, totalBytes: 0, percent: 0 });\r\n\r\n const compat = await this.checkCompatibility({\r\n host,\r\n port,\r\n secure,\r\n timeoutMs,\r\n signal,\r\n });\r\n\r\n const { baseUrl } = compat;\r\n progress({ phase: 'server-compat', text: compat.message, processedBytes: 0, totalBytes: 0, percent: 0 });\r\n if (!compat.compatible) {\r\n throw new DropgateValidationError(compat.message);\r\n }\r\n\r\n // 1) Fetch metadata\r\n progress({ phase: 'metadata', text: 'Fetching file info...', processedBytes: 0, totalBytes: 0, percent: 0 });\r\n\r\n const { signal: metaSignal, cleanup: metaCleanup } = makeAbortSignal(signal, timeoutMs);\r\n let metadata: FileMetadata;\r\n\r\n try {\r\n const metaRes = await this.fetchFn(`${baseUrl}/api/file/${fileId}/meta`, {\r\n method: 'GET',\r\n headers: { Accept: 'application/json' },\r\n signal: metaSignal,\r\n });\r\n\r\n if (!metaRes.ok) {\r\n if (metaRes.status === 404) {\r\n throw new DropgateProtocolError('File not found or has expired.');\r\n }\r\n throw new DropgateProtocolError(`Failed to fetch file metadata (status ${metaRes.status}).`);\r\n }\r\n\r\n metadata = await metaRes.json() as FileMetadata;\r\n } catch (err) {\r\n if (err instanceof DropgateError) throw err;\r\n if (err instanceof Error && err.name === 'AbortError') {\r\n throw new DropgateAbortError('Download cancelled.');\r\n }\r\n throw new DropgateNetworkError('Could not fetch file metadata.', { cause: err });\r\n } finally {\r\n metaCleanup();\r\n }\r\n\r\n const isEncrypted = Boolean(metadata.isEncrypted);\r\n const totalBytes = metadata.sizeBytes || 0;\r\n\r\n // Check if file is too large to buffer in memory without streaming\r\n if (!onData && totalBytes > MAX_IN_MEMORY_DOWNLOAD_BYTES) {\r\n const sizeMB = Math.round(totalBytes / (1024 * 1024));\r\n const limitMB = Math.round(MAX_IN_MEMORY_DOWNLOAD_BYTES / (1024 * 1024));\r\n throw new DropgateValidationError(\r\n `File is too large (${sizeMB}MB) to download without streaming. ` +\r\n `Provide an onData callback to stream files larger than ${limitMB}MB.`\r\n );\r\n }\r\n\r\n // 2) Decrypt filename if encrypted\r\n let filename: string;\r\n let cryptoKey: CryptoKey | undefined;\r\n\r\n if (isEncrypted) {\r\n if (!keyB64) {\r\n throw new DropgateValidationError('Decryption key is required for encrypted files.');\r\n }\r\n\r\n if (!this.cryptoObj?.subtle) {\r\n throw new DropgateValidationError('Web Crypto API not available for decryption.');\r\n }\r\n\r\n progress({ phase: 'decrypting', text: 'Preparing decryption...', processedBytes: 0, totalBytes: 0, percent: 0 });\r\n\r\n try {\r\n cryptoKey = await importKeyFromBase64(this.cryptoObj, keyB64, this.base64);\r\n filename = await decryptFilenameFromBase64(\r\n this.cryptoObj,\r\n metadata.encryptedFilename!,\r\n cryptoKey,\r\n this.base64\r\n );\r\n } catch (err) {\r\n throw new DropgateError('Failed to decrypt filename. Invalid key or corrupted data.', {\r\n code: 'DECRYPT_FILENAME_FAILED',\r\n cause: err,\r\n });\r\n }\r\n } else {\r\n filename = metadata.filename || 'file';\r\n }\r\n\r\n // 3) Download file content\r\n progress({ phase: 'downloading', text: 'Starting download...', percent: 0, processedBytes: 0, totalBytes });\r\n\r\n const { signal: downloadSignal, cleanup: downloadCleanup } = makeAbortSignal(signal, timeoutMs);\r\n let receivedBytes = 0;\r\n const dataChunks: Uint8Array[] = [];\r\n const collectData = !onData;\r\n\r\n try {\r\n const downloadRes = await this.fetchFn(`${baseUrl}/api/file/${fileId}`, {\r\n method: 'GET',\r\n signal: downloadSignal,\r\n });\r\n\r\n if (!downloadRes.ok) {\r\n throw new DropgateProtocolError(`Download failed (status ${downloadRes.status}).`);\r\n }\r\n\r\n if (!downloadRes.body) {\r\n throw new DropgateProtocolError('Streaming response not available.');\r\n }\r\n\r\n const reader = downloadRes.body.getReader();\r\n\r\n if (isEncrypted && cryptoKey) {\r\n // Encrypted: buffer and decrypt chunks\r\n // Use a chunk array to avoid repeated array copying on each read\r\n const ENCRYPTED_CHUNK_SIZE = this.chunkSize + ENCRYPTION_OVERHEAD_PER_CHUNK;\r\n const pendingChunks: Uint8Array[] = [];\r\n let pendingLength = 0;\r\n\r\n // Helper to concatenate pending chunks into a single buffer\r\n const flushPending = (): Uint8Array => {\r\n if (pendingChunks.length === 0) return new Uint8Array(0);\r\n if (pendingChunks.length === 1) {\r\n const result = pendingChunks[0];\r\n pendingChunks.length = 0;\r\n pendingLength = 0;\r\n return result;\r\n }\r\n const result = new Uint8Array(pendingLength);\r\n let offset = 0;\r\n for (const chunk of pendingChunks) {\r\n result.set(chunk, offset);\r\n offset += chunk.length;\r\n }\r\n pendingChunks.length = 0;\r\n pendingLength = 0;\r\n return result;\r\n };\r\n\r\n while (true) {\r\n if (signal?.aborted) {\r\n throw new DropgateAbortError('Download cancelled.');\r\n }\r\n\r\n const { done, value } = await reader.read();\r\n if (done) break;\r\n\r\n // Append to pending chunks (no copying yet)\r\n pendingChunks.push(value);\r\n pendingLength += value.length;\r\n\r\n // Process complete encrypted chunks when we have enough data\r\n while (pendingLength >= ENCRYPTED_CHUNK_SIZE) {\r\n const buffer = flushPending();\r\n const encryptedChunk = buffer.subarray(0, ENCRYPTED_CHUNK_SIZE);\r\n\r\n // Keep the remainder for next iteration\r\n if (buffer.length > ENCRYPTED_CHUNK_SIZE) {\r\n const remainder = buffer.subarray(ENCRYPTED_CHUNK_SIZE);\r\n pendingChunks.push(remainder);\r\n pendingLength = remainder.length;\r\n }\r\n\r\n const decryptedBuffer = await decryptChunk(this.cryptoObj, encryptedChunk, cryptoKey);\r\n const decryptedData = new Uint8Array(decryptedBuffer);\r\n\r\n if (collectData) {\r\n dataChunks.push(decryptedData);\r\n } else {\r\n await onData!(decryptedData);\r\n }\r\n }\r\n\r\n receivedBytes += value.length;\r\n const percent = totalBytes > 0 ? Math.round((receivedBytes / totalBytes) * 100) : 0;\r\n progress({\r\n phase: 'decrypting',\r\n text: `Downloading & decrypting... (${percent}%)`,\r\n percent,\r\n processedBytes: receivedBytes,\r\n totalBytes,\r\n });\r\n }\r\n\r\n // Process remaining buffer (final chunk)\r\n if (pendingLength > 0) {\r\n const buffer = flushPending();\r\n const decryptedBuffer = await decryptChunk(this.cryptoObj, buffer, cryptoKey);\r\n const decryptedData = new Uint8Array(decryptedBuffer);\r\n\r\n if (collectData) {\r\n dataChunks.push(decryptedData);\r\n } else {\r\n await onData!(decryptedData);\r\n }\r\n }\r\n } else {\r\n // Plain: stream through directly\r\n while (true) {\r\n if (signal?.aborted) {\r\n throw new DropgateAbortError('Download cancelled.');\r\n }\r\n\r\n const { done, value } = await reader.read();\r\n if (done) break;\r\n\r\n if (collectData) {\r\n dataChunks.push(value);\r\n } else {\r\n await onData!(value);\r\n }\r\n\r\n receivedBytes += value.length;\r\n const percent = totalBytes > 0 ? Math.round((receivedBytes / totalBytes) * 100) : 0;\r\n progress({\r\n phase: 'downloading',\r\n text: `Downloading... (${percent}%)`,\r\n percent,\r\n processedBytes: receivedBytes,\r\n totalBytes,\r\n });\r\n }\r\n }\r\n } catch (err) {\r\n if (err instanceof DropgateError) throw err;\r\n if (err instanceof Error && err.name === 'AbortError') {\r\n throw new DropgateAbortError('Download cancelled.');\r\n }\r\n throw new DropgateNetworkError('Download failed.', { cause: err });\r\n } finally {\r\n downloadCleanup();\r\n }\r\n\r\n progress({ phase: 'complete', text: 'Download complete!', percent: 100, processedBytes: receivedBytes, totalBytes });\r\n\r\n // Combine collected data if not using callback\r\n let data: Uint8Array | undefined;\r\n if (collectData && dataChunks.length > 0) {\r\n const totalLength = dataChunks.reduce((sum, chunk) => sum + chunk.length, 0);\r\n data = new Uint8Array(totalLength);\r\n let offset = 0;\r\n for (const chunk of dataChunks) {\r\n data.set(chunk, offset);\r\n offset += chunk.length;\r\n }\r\n }\r\n\r\n return {\r\n filename,\r\n receivedBytes,\r\n wasEncrypted: isEncrypted,\r\n ...(data ? { data } : {}),\r\n };\r\n }\r\n\r\n private async attemptChunkUpload(\r\n url: string,\r\n fetchOptions: RequestInit,\r\n opts: {\r\n retries: number;\r\n backoffMs: number;\r\n maxBackoffMs: number;\r\n timeoutMs: number;\r\n signal?: AbortSignal;\r\n progress: (evt: UploadProgressEvent) => void;\r\n chunkIndex: number;\r\n totalChunks: number;\r\n chunkSize: number;\r\n fileSizeBytes: number;\r\n }\r\n ): Promise<void> {\r\n const {\r\n retries,\r\n backoffMs,\r\n maxBackoffMs,\r\n timeoutMs,\r\n signal,\r\n progress,\r\n chunkIndex,\r\n totalChunks,\r\n chunkSize,\r\n fileSizeBytes,\r\n } = opts;\r\n\r\n let attemptsLeft = retries;\r\n let currentBackoff = backoffMs;\r\n const maxRetries = retries;\r\n\r\n while (true) {\r\n if (signal?.aborted) {\r\n throw signal.reason || new DropgateAbortError();\r\n }\r\n\r\n const { signal: s, cleanup } = makeAbortSignal(signal, timeoutMs);\r\n try {\r\n const res = await this.fetchFn(url, { ...fetchOptions, signal: s });\r\n if (res.ok) return;\r\n\r\n const text = await res.text().catch(() => '');\r\n const err = new DropgateProtocolError(\r\n `Chunk ${chunkIndex + 1} failed (HTTP ${res.status}).`,\r\n {\r\n details: { status: res.status, bodySnippet: text.slice(0, 120) },\r\n }\r\n );\r\n throw err;\r\n } catch (err) {\r\n cleanup();\r\n\r\n // AbortError should not retry\r\n if (\r\n err instanceof Error &&\r\n (err.name === 'AbortError' || (err as { code?: string }).code === 'ABORT_ERR')\r\n ) {\r\n throw err;\r\n }\r\n if (signal?.aborted) {\r\n throw signal.reason || new DropgateAbortError();\r\n }\r\n\r\n if (attemptsLeft <= 0) {\r\n throw err instanceof DropgateError\r\n ? err\r\n : new DropgateNetworkError('Chunk upload failed.', { cause: err });\r\n }\r\n\r\n const attemptNumber = maxRetries - attemptsLeft + 1;\r\n const processedBytes = chunkIndex * chunkSize;\r\n const percent = (chunkIndex / totalChunks) * 100;\r\n let remaining = currentBackoff;\r\n const tick = 100;\r\n while (remaining > 0) {\r\n const secondsLeft = (remaining / 1000).toFixed(1);\r\n progress({\r\n phase: 'retry-wait',\r\n text: `Chunk upload failed. Retrying in ${secondsLeft}s... (${attemptNumber}/${maxRetries})`,\r\n percent,\r\n processedBytes,\r\n totalBytes: fileSizeBytes,\r\n chunkIndex,\r\n totalChunks,\r\n });\r\n await sleep(Math.min(tick, remaining), signal);\r\n remaining -= tick;\r\n }\r\n\r\n progress({\r\n phase: 'retry',\r\n text: `Chunk upload failed. Retrying now... (${attemptNumber}/${maxRetries})`,\r\n percent,\r\n processedBytes,\r\n totalBytes: fileSizeBytes,\r\n chunkIndex,\r\n totalChunks,\r\n });\r\n\r\n attemptsLeft -= 1;\r\n currentBackoff = Math.min(currentBackoff * 2, maxBackoffMs);\r\n continue;\r\n } finally {\r\n cleanup();\r\n }\r\n }\r\n }\r\n}\r\n","import type { CryptoAdapter } from '../types.js';\r\nimport { getDefaultCrypto } from '../adapters/defaults.js';\r\n\r\n/**\r\n * Check if a hostname is localhost\r\n */\r\nexport function isLocalhostHostname(hostname: string): boolean {\r\n const host = String(hostname || '').toLowerCase();\r\n return host === 'localhost' || host === '127.0.0.1' || host === '::1';\r\n}\r\n\r\n/**\r\n * Check if the current context allows P2P (HTTPS or localhost)\r\n */\r\nexport function isSecureContextForP2P(\r\n hostname?: string,\r\n isSecureContext?: boolean\r\n): boolean {\r\n return Boolean(isSecureContext) || isLocalhostHostname(hostname || '');\r\n}\r\n\r\n/**\r\n * Generate a P2P sharing code using cryptographically secure random.\r\n * Format: XXXX-0000 (4 letters + 4 digits)\r\n */\r\nexport function generateP2PCode(cryptoObj?: CryptoAdapter): string {\r\n const crypto = cryptoObj || getDefaultCrypto();\r\n const letters = 'ABCDEFGHJKLMNPQRSTUVWXYZ'; // Excluded I and O to avoid confusion\r\n\r\n if (crypto) {\r\n const randomBytes = new Uint8Array(8);\r\n crypto.getRandomValues(randomBytes);\r\n\r\n let letterPart = '';\r\n for (let i = 0; i < 4; i++) {\r\n letterPart += letters[randomBytes[i] % letters.length];\r\n }\r\n\r\n let numberPart = '';\r\n for (let i = 4; i < 8; i++) {\r\n numberPart += (randomBytes[i] % 10).toString();\r\n }\r\n\r\n return `${letterPart}-${numberPart}`;\r\n }\r\n\r\n // Fallback to Math.random (less secure, but works everywhere)\r\n let a = '';\r\n for (let i = 0; i < 4; i++) {\r\n a += letters[Math.floor(Math.random() * letters.length)];\r\n }\r\n let b = '';\r\n for (let i = 0; i < 4; i++) {\r\n b += Math.floor(Math.random() * 10);\r\n }\r\n return `${a}-${b}`;\r\n}\r\n\r\n/**\r\n * Check if a string looks like a P2P sharing code\r\n */\r\nexport function isP2PCodeLike(code: string): boolean {\r\n return /^[A-Z]{4}-\\d{4}$/.test(String(code || '').trim());\r\n}\r\n","import { DropgateNetworkError } from '../errors.js';\r\nimport type { P2PCapabilities } from '../types.js';\r\nimport type { PeerInstance, PeerOptions, P2PServerConfig } from './types.js';\r\n\r\n/**\r\n * Resolve P2P server configuration from user options and server capabilities.\r\n * User-provided values take precedence over server capabilities.\r\n */\r\nexport function resolvePeerConfig(\r\n userConfig: P2PServerConfig,\r\n serverCaps?: P2PCapabilities\r\n): { path: string; iceServers: RTCIceServer[] } {\r\n return {\r\n path: userConfig.peerjsPath ?? serverCaps?.peerjsPath ?? '/peerjs',\r\n iceServers: userConfig.iceServers ?? serverCaps?.iceServers ?? [],\r\n };\r\n}\r\n\r\n/**\r\n * Build PeerJS connection options from P2P server configuration.\r\n */\r\nexport function buildPeerOptions(config: P2PServerConfig = {}): PeerOptions {\r\n const { host, port, peerjsPath = '/peerjs', secure = false, iceServers = [] } = config;\r\n\r\n const peerOpts: PeerOptions = {\r\n host,\r\n path: peerjsPath,\r\n secure,\r\n config: { iceServers },\r\n debug: 0,\r\n };\r\n\r\n if (port) {\r\n peerOpts.port = port;\r\n }\r\n\r\n return peerOpts;\r\n}\r\n\r\nexport interface CreatePeerWithRetriesOptions {\r\n code?: string | null;\r\n codeGenerator: () => string;\r\n maxAttempts: number;\r\n buildPeer: (id: string) => PeerInstance;\r\n onCode?: (code: string, attempt: number) => void;\r\n}\r\n\r\n/**\r\n * Create a peer with retries if the code is already taken\r\n */\r\nexport async function createPeerWithRetries(\r\n opts: CreatePeerWithRetriesOptions\r\n): Promise<{ peer: PeerInstance; code: string }> {\r\n const { code, codeGenerator, maxAttempts, buildPeer, onCode } = opts;\r\n\r\n let nextCode = code || codeGenerator();\r\n let peer: PeerInstance | null = null;\r\n let lastError: Error | null = null;\r\n\r\n for (let attempt = 0; attempt < maxAttempts; attempt++) {\r\n onCode?.(nextCode, attempt);\r\n\r\n try {\r\n peer = await new Promise<PeerInstance>((resolve, reject) => {\r\n const instance = buildPeer(nextCode);\r\n instance.on('open', () => resolve(instance));\r\n instance.on('error', (err: Error) => {\r\n try {\r\n instance.destroy();\r\n } catch {\r\n // Ignore destroy errors\r\n }\r\n reject(err);\r\n });\r\n });\r\n\r\n return { peer, code: nextCode };\r\n } catch (err) {\r\n lastError = err as Error;\r\n nextCode = codeGenerator();\r\n }\r\n }\r\n\r\n throw lastError || new DropgateNetworkError('Could not establish PeerJS connection.');\r\n}\r\n","import { DropgateValidationError, DropgateNetworkError } from '../errors.js';\r\nimport { sleep } from '../utils/network.js';\r\nimport type { P2PSendOptions, P2PSendSession, P2PSendState, DataConnection } from './types.js';\r\nimport { generateP2PCode } from './utils.js';\r\nimport { buildPeerOptions, createPeerWithRetries, resolvePeerConfig } from './helpers.js';\r\n\r\n/**\r\n * Generate a unique session ID for transfer tracking.\r\n * Uses crypto.randomUUID if available, falls back to timestamp + random.\r\n */\r\nfunction generateSessionId(): string {\r\n if (typeof crypto !== 'undefined' && crypto.randomUUID) {\r\n return crypto.randomUUID();\r\n }\r\n // Fallback for environments without crypto.randomUUID\r\n return `${Date.now()}-${Math.random().toString(36).substring(2, 11)}`;\r\n}\r\n\r\n/**\r\n * Start a direct transfer (P2P) sender session.\r\n *\r\n * IMPORTANT: Consumer must provide the PeerJS Peer constructor.\r\n * This removes DOM coupling (no script injection).\r\n *\r\n * Example:\r\n * ```js\r\n * import Peer from 'peerjs';\r\n * import { startP2PSend } from '@dropgate/core/p2p';\r\n *\r\n * const session = await startP2PSend({\r\n * file: myFile,\r\n * Peer,\r\n * host: 'dropgate.link',\r\n * secure: true,\r\n * onCode: (code) => console.log('Share this code:', code),\r\n * onProgress: (evt) => console.log(`${evt.percent}% sent`),\r\n * onComplete: () => console.log('Done!'),\r\n * });\r\n * ```\r\n */\r\nexport async function startP2PSend(opts: P2PSendOptions): Promise<P2PSendSession> {\r\n const {\r\n file,\r\n Peer,\r\n serverInfo,\r\n host,\r\n port,\r\n peerjsPath,\r\n secure = false,\r\n iceServers,\r\n codeGenerator,\r\n cryptoObj,\r\n maxAttempts = 4,\r\n chunkSize = 256 * 1024,\r\n endAckTimeoutMs = 15000,\r\n bufferHighWaterMark = 8 * 1024 * 1024,\r\n bufferLowWaterMark = 2 * 1024 * 1024,\r\n heartbeatIntervalMs = 5000,\r\n onCode,\r\n onStatus,\r\n onProgress,\r\n onComplete,\r\n onError,\r\n onDisconnect,\r\n } = opts;\r\n\r\n // Validate required options\r\n if (!file) {\r\n throw new DropgateValidationError('File is missing.');\r\n }\r\n\r\n if (!Peer) {\r\n throw new DropgateValidationError(\r\n 'PeerJS Peer constructor is required. Install peerjs and pass it as the Peer option.'\r\n );\r\n }\r\n\r\n // Check P2P capabilities if serverInfo is provided\r\n const p2pCaps = serverInfo?.capabilities?.p2p;\r\n if (serverInfo && !p2pCaps?.enabled) {\r\n throw new DropgateValidationError('Direct transfer is disabled on this server.');\r\n }\r\n\r\n // Resolve config from user options and server capabilities\r\n const { path: finalPath, iceServers: finalIceServers } = resolvePeerConfig(\r\n { peerjsPath, iceServers },\r\n p2pCaps\r\n );\r\n\r\n // Build peer options\r\n const peerOpts = buildPeerOptions({\r\n host,\r\n port,\r\n peerjsPath: finalPath,\r\n secure,\r\n iceServers: finalIceServers,\r\n });\r\n\r\n // Create the code generator\r\n const finalCodeGenerator = codeGenerator || (() => generateP2PCode(cryptoObj));\r\n\r\n // Create peer with retries\r\n const buildPeer = (id: string) => new Peer(id, peerOpts);\r\n const { peer, code } = await createPeerWithRetries({\r\n code: null,\r\n codeGenerator: finalCodeGenerator,\r\n maxAttempts,\r\n buildPeer,\r\n onCode,\r\n });\r\n\r\n // Generate unique session ID for this transfer\r\n const sessionId = generateSessionId();\r\n\r\n // State machine - replaces boolean flags to prevent race conditions\r\n let state: P2PSendState = 'listening';\r\n let activeConn: DataConnection | null = null;\r\n let sentBytes = 0;\r\n let heartbeatTimer: ReturnType<typeof setInterval> | null = null;\r\n\r\n const reportProgress = (data: { received: number; total: number }): void => {\r\n const safeTotal =\r\n Number.isFinite(data.total) && data.total > 0 ? data.total : file.size;\r\n const safeReceived = Math.min(Number(data.received) || 0, safeTotal || 0);\r\n const percent = safeTotal ? (safeReceived / safeTotal) * 100 : 0;\r\n onProgress?.({ processedBytes: safeReceived, totalBytes: safeTotal, percent });\r\n };\r\n\r\n // Safe error handler - prevents calling onError after completion\r\n const safeError = (err: Error): void => {\r\n if (state === 'closed' || state === 'completed') return;\r\n state = 'closed';\r\n onError?.(err);\r\n cleanup();\r\n };\r\n\r\n // Safe complete handler - only fires from finishing state\r\n const safeComplete = (): void => {\r\n if (state !== 'finishing') return;\r\n state = 'completed';\r\n onComplete?.();\r\n cleanup();\r\n };\r\n\r\n // Cleanup all resources\r\n const cleanup = (): void => {\r\n // Clear heartbeat timer\r\n if (heartbeatTimer) {\r\n clearInterval(heartbeatTimer);\r\n heartbeatTimer = null;\r\n }\r\n\r\n // Remove beforeunload listener if in browser\r\n if (typeof window !== 'undefined') {\r\n window.removeEventListener('beforeunload', handleUnload);\r\n }\r\n\r\n try {\r\n activeConn?.close();\r\n } catch {\r\n // Ignore close errors\r\n }\r\n try {\r\n peer.destroy();\r\n } catch {\r\n // Ignore destroy errors\r\n }\r\n };\r\n\r\n // Handle browser tab close/refresh\r\n const handleUnload = (): void => {\r\n try {\r\n activeConn?.send({ t: 'error', message: 'Sender closed the connection.' });\r\n } catch {\r\n // Best effort\r\n }\r\n stop();\r\n };\r\n\r\n // Add beforeunload listener if in browser\r\n if (typeof window !== 'undefined') {\r\n window.addEventListener('beforeunload', handleUnload);\r\n }\r\n\r\n const stop = (): void => {\r\n if (state === 'closed') return;\r\n state = 'closed';\r\n cleanup();\r\n };\r\n\r\n // Helper to check if session is stopped - bypasses TypeScript narrowing\r\n // which doesn't understand state can change asynchronously\r\n const isStopped = (): boolean => state === 'closed';\r\n\r\n peer.on('connection', (conn: DataConnection) => {\r\n if (state === 'closed') return;\r\n\r\n // Connection replacement logic - allow new connections if old one is dead\r\n if (activeConn) {\r\n // Check if existing connection is actually still open\r\n // @ts-expect-error - open property may exist on PeerJS connections\r\n const isOldConnOpen = activeConn.open !== false;\r\n\r\n if (isOldConnOpen && state === 'transferring') {\r\n // Actively transferring, reject new connection\r\n try {\r\n conn.send({ t: 'error', message: 'Transfer already in progress.' });\r\n } catch {\r\n // Ignore send errors\r\n }\r\n try {\r\n conn.close();\r\n } catch {\r\n // Ignore close errors\r\n }\r\n return;\r\n } else if (!isOldConnOpen) {\r\n // Old connection is dead, clean it up and accept new one\r\n try {\r\n activeConn.close();\r\n } catch {\r\n // Ignore\r\n }\r\n activeConn = null;\r\n // Reset state to allow new transfer\r\n state = 'listening';\r\n sentBytes = 0;\r\n } else {\r\n // Connection exists but not transferring (maybe in negotiating state)\r\n // Reject to avoid confusion\r\n try {\r\n conn.send({ t: 'error', message: 'Another receiver is already connected.' });\r\n } catch {\r\n // Ignore send errors\r\n }\r\n try {\r\n conn.close();\r\n } catch {\r\n // Ignore close errors\r\n }\r\n return;\r\n }\r\n }\r\n\r\n activeConn = conn;\r\n state = 'negotiating';\r\n onStatus?.({ phase: 'waiting', message: 'Connected. Waiting for receiver to accept...' });\r\n\r\n let readyResolve: (() => void) | null = null;\r\n let ackResolve: ((data: unknown) => void) | null = null;\r\n\r\n const readyPromise = new Promise<void>((resolve) => {\r\n readyResolve = resolve;\r\n });\r\n\r\n const ackPromise = new Promise<unknown>((resolve) => {\r\n ackResolve = resolve;\r\n });\r\n\r\n conn.on('data', (data: unknown) => {\r\n if (\r\n !data ||\r\n typeof data !== 'object' ||\r\n data instanceof ArrayBuffer ||\r\n ArrayBuffer.isView(data)\r\n ) {\r\n return;\r\n }\r\n\r\n const msg = data as { t?: string; received?: number; total?: number; phase?: string; message?: string };\r\n if (!msg.t) return;\r\n\r\n if (msg.t === 'ready') {\r\n onStatus?.({ phase: 'transferring', message: 'Receiver accepted. Starting transfer...' });\r\n readyResolve?.();\r\n return;\r\n }\r\n\r\n if (msg.t === 'progress') {\r\n reportProgress({ received: msg.received || 0, total: msg.total || 0 });\r\n return;\r\n }\r\n\r\n if (msg.t === 'ack' && msg.phase === 'end') {\r\n ackResolve?.(msg);\r\n return;\r\n }\r\n\r\n if (msg.t === 'pong') {\r\n // Heartbeat response received, connection is alive\r\n return;\r\n }\r\n\r\n if (msg.t === 'error') {\r\n safeError(new DropgateNetworkError(msg.message || 'Receiver reported an error.'));\r\n }\r\n });\r\n\r\n conn.on('open', async () => {\r\n try {\r\n if (isStopped()) return;\r\n\r\n // Send metadata with sessionId\r\n conn.send({\r\n t: 'meta',\r\n sessionId,\r\n name: file.name,\r\n size: file.size,\r\n mime: file.type || 'application/octet-stream',\r\n });\r\n\r\n const total = file.size;\r\n const dc = conn._dc;\r\n\r\n if (dc && Number.isFinite(bufferLowWaterMark)) {\r\n try {\r\n dc.bufferedAmountLowThreshold = bufferLowWaterMark;\r\n } catch {\r\n // Ignore threshold setting errors\r\n }\r\n }\r\n\r\n // Wait for ready signal\r\n await readyPromise;\r\n if (isStopped()) return;\r\n\r\n // Start heartbeat for long transfers\r\n if (heartbeatIntervalMs > 0) {\r\n heartbeatTimer = setInterval(() => {\r\n if (state === 'transferring' || state === 'finishing') {\r\n try {\r\n conn.send({ t: 'ping' });\r\n } catch {\r\n // Ignore ping errors\r\n }\r\n }\r\n }, heartbeatIntervalMs);\r\n }\r\n\r\n state = 'transferring';\r\n\r\n // Send file in chunks\r\n for (let offset = 0; offset < total; offset += chunkSize) {\r\n if (isStopped()) return;\r\n\r\n const slice = file.slice(offset, offset + chunkSize);\r\n const buf = await slice.arrayBuffer();\r\n conn.send(buf);\r\n sentBytes += buf.byteLength;\r\n\r\n // Flow control\r\n if (dc) {\r\n while (dc.bufferedAmount > bufferHighWaterMark) {\r\n await new Promise<void>((resolve) => {\r\n const fallback = setTimeout(resolve, 60);\r\n try {\r\n dc.addEventListener(\r\n 'bufferedamountlow',\r\n () => {\r\n clearTimeout(fallback);\r\n resolve();\r\n },\r\n { once: true }\r\n );\r\n } catch {\r\n // Fallback only\r\n }\r\n });\r\n }\r\n }\r\n }\r\n\r\n if (isStopped()) return;\r\n\r\n state = 'finishing';\r\n conn.send({ t: 'end' });\r\n\r\n // Wait for acknowledgment\r\n const ackTimeoutMs = Number.isFinite(endAckTimeoutMs)\r\n ? Math.max(endAckTimeoutMs, Math.ceil(file.size / (1024 * 1024)) * 1000)\r\n : null;\r\n\r\n const ackResult = await Promise.race([\r\n ackPromise,\r\n sleep(ackTimeoutMs || 15000).catch(() => null),\r\n ]);\r\n\r\n if (isStopped()) return;\r\n\r\n if (!ackResult || typeof ackResult !== 'object') {\r\n throw new DropgateNetworkError('Receiver did not confirm completion.');\r\n }\r\n\r\n const ackData = ackResult as { total?: number; received?: number };\r\n const ackTotal = Number(ackData.total) || file.size;\r\n const ackReceived = Number(ackData.received) || 0;\r\n\r\n if (ackTotal && ackReceived < ackTotal) {\r\n throw new DropgateNetworkError('Receiver reported an incomplete transfer.');\r\n }\r\n\r\n reportProgress({ received: ackReceived || ackTotal, total: ackTotal });\r\n safeComplete();\r\n } catch (err) {\r\n safeError(err as Error);\r\n }\r\n });\r\n\r\n conn.on('error', (err: Error) => {\r\n safeError(err);\r\n });\r\n\r\n conn.on('close', () => {\r\n if (state === 'closed' || state === 'completed') {\r\n // Clean shutdown, ensure full cleanup\r\n cleanup();\r\n return;\r\n }\r\n\r\n if (state === 'transferring' || state === 'finishing') {\r\n // Disconnected during transfer\r\n safeError(\r\n new DropgateNetworkError('Receiver disconnected before transfer completed.')\r\n );\r\n } else {\r\n // Disconnected before transfer started (during waiting/negotiating phase)\r\n // Reset state to allow reconnection\r\n activeConn = null;\r\n state = 'listening';\r\n sentBytes = 0;\r\n onDisconnect?.();\r\n }\r\n });\r\n });\r\n\r\n return {\r\n peer,\r\n code,\r\n sessionId,\r\n stop,\r\n getStatus: () => state,\r\n getBytesSent: () => sentBytes,\r\n getConnectedPeerId: () => {\r\n if (!activeConn) return null;\r\n // @ts-expect-error - peer property exists on PeerJS DataConnection\r\n return activeConn.peer || null;\r\n },\r\n };\r\n}\r\n","import { DropgateValidationError, DropgateNetworkError } from '../errors.js';\r\nimport type { P2PReceiveOptions, P2PReceiveSession, P2PReceiveState, DataConnection } from './types.js';\r\nimport { isP2PCodeLike } from './utils.js';\r\nimport { buildPeerOptions, resolvePeerConfig } from './helpers.js';\r\n\r\n/**\r\n * Start a direct transfer (P2P) receiver session.\r\n *\r\n * IMPORTANT: Consumer must provide the PeerJS Peer constructor and handle file writing.\r\n * This removes DOM coupling (no streamSaver).\r\n *\r\n * Example:\r\n * ```js\r\n * import Peer from 'peerjs';\r\n * import { startP2PReceive } from '@dropgate/core/p2p';\r\n *\r\n * let writer;\r\n * const session = await startP2PReceive({\r\n * code: 'ABCD-1234',\r\n * Peer,\r\n * host: 'dropgate.link',\r\n * secure: true,\r\n * onMeta: ({ name, total }) => {\r\n * // Consumer creates file writer\r\n * writer = createWriteStream(name);\r\n * },\r\n * onData: async (chunk) => {\r\n * // Consumer writes data\r\n * await writer.write(chunk);\r\n * },\r\n * onComplete: () => {\r\n * writer.close();\r\n * console.log('Done!');\r\n * },\r\n * });\r\n * ```\r\n */\r\nexport async function startP2PReceive(opts: P2PReceiveOptions): Promise<P2PReceiveSession> {\r\n const {\r\n code,\r\n Peer,\r\n serverInfo,\r\n host,\r\n port,\r\n peerjsPath,\r\n secure = false,\r\n iceServers,\r\n autoReady = true,\r\n watchdogTimeoutMs = 15000,\r\n onStatus,\r\n onMeta,\r\n onData,\r\n onProgress,\r\n onComplete,\r\n onError,\r\n onDisconnect,\r\n } = opts;\r\n\r\n // Validate required options\r\n if (!code) {\r\n throw new DropgateValidationError('No sharing code was provided.');\r\n }\r\n\r\n if (!Peer) {\r\n throw new DropgateValidationError(\r\n 'PeerJS Peer constructor is required. Install peerjs and pass it as the Peer option.'\r\n );\r\n }\r\n\r\n // Check P2P capabilities if serverInfo is provided\r\n const p2pCaps = serverInfo?.capabilities?.p2p;\r\n if (serverInfo && !p2pCaps?.enabled) {\r\n throw new DropgateValidationError('Direct transfer is disabled on this server.');\r\n }\r\n\r\n // Validate and normalize code\r\n const normalizedCode = String(code).trim().replace(/\\s+/g, '').toUpperCase();\r\n if (!isP2PCodeLike(normalizedCode)) {\r\n throw new DropgateValidationError('Invalid direct transfer code.');\r\n }\r\n\r\n // Resolve config from user options and server capabilities\r\n const { path: finalPath, iceServers: finalIceServers } = resolvePeerConfig(\r\n { peerjsPath, iceServers },\r\n p2pCaps\r\n );\r\n\r\n // Build peer options\r\n const peerOpts = buildPeerOptions({\r\n host,\r\n port,\r\n peerjsPath: finalPath,\r\n secure,\r\n iceServers: finalIceServers,\r\n });\r\n\r\n // Create peer (receiver doesn't need a specific ID)\r\n const peer = new Peer(undefined, peerOpts);\r\n\r\n // State machine - replaces boolean flags to prevent race conditions\r\n let state: P2PReceiveState = 'initializing';\r\n let total = 0;\r\n let received = 0;\r\n let currentSessionId: string | null = null;\r\n let lastProgressSentAt = 0;\r\n const progressIntervalMs = 120;\r\n let writeQueue = Promise.resolve();\r\n let watchdogTimer: ReturnType<typeof setTimeout> | null = null;\r\n let activeConn: DataConnection | null = null;\r\n\r\n // Watchdog - detects dead connections during transfer\r\n const resetWatchdog = (): void => {\r\n if (watchdogTimeoutMs <= 0) return;\r\n\r\n if (watchdogTimer) {\r\n clearTimeout(watchdogTimer);\r\n }\r\n\r\n watchdogTimer = setTimeout(() => {\r\n if (state === 'transferring') {\r\n safeError(new DropgateNetworkError('Connection timed out (no data received).'));\r\n }\r\n }, watchdogTimeoutMs);\r\n };\r\n\r\n const clearWatchdog = (): void => {\r\n if (watchdogTimer) {\r\n clearTimeout(watchdogTimer);\r\n watchdogTimer = null;\r\n }\r\n };\r\n\r\n // Safe error handler - prevents calling onError after completion\r\n const safeError = (err: Error): void => {\r\n if (state === 'closed' || state === 'completed') return;\r\n state = 'closed';\r\n onError?.(err);\r\n cleanup();\r\n };\r\n\r\n // Safe complete handler - only fires from transferring state\r\n const safeComplete = (completeData: { received: number; total: number }): void => {\r\n if (state !== 'transferring') return;\r\n state = 'completed';\r\n onComplete?.(completeData);\r\n cleanup();\r\n };\r\n\r\n // Cleanup all resources\r\n const cleanup = (): void => {\r\n clearWatchdog();\r\n\r\n // Remove beforeunload listener if in browser\r\n if (typeof window !== 'undefined') {\r\n window.removeEventListener('beforeunload', handleUnload);\r\n }\r\n\r\n try {\r\n peer.destroy();\r\n } catch {\r\n // Ignore destroy errors\r\n }\r\n };\r\n\r\n // Handle browser tab close/refresh\r\n const handleUnload = (): void => {\r\n try {\r\n activeConn?.send({ t: 'error', message: 'Receiver closed the connection.' });\r\n } catch {\r\n // Best effort\r\n }\r\n stop();\r\n };\r\n\r\n // Add beforeunload listener if in browser\r\n if (typeof window !== 'undefined') {\r\n window.addEventListener('beforeunload', handleUnload);\r\n }\r\n\r\n const stop = (): void => {\r\n if (state === 'closed') return;\r\n state = 'closed';\r\n cleanup();\r\n };\r\n\r\n peer.on('error', (err: Error) => {\r\n safeError(err);\r\n });\r\n\r\n peer.on('open', () => {\r\n state = 'connecting';\r\n const conn = peer.connect(normalizedCode, { reliable: true });\r\n activeConn = conn;\r\n\r\n conn.on('open', () => {\r\n state = 'negotiating';\r\n onStatus?.({ phase: 'connected', message: 'Waiting for file details...' });\r\n });\r\n\r\n conn.on('data', async (data: unknown) => {\r\n try {\r\n // Reset watchdog on any data received\r\n resetWatchdog();\r\n\r\n // Handle control messages\r\n if (\r\n data &&\r\n typeof data === 'object' &&\r\n !(data instanceof ArrayBuffer) &&\r\n !ArrayBuffer.isView(data)\r\n ) {\r\n const msg = data as {\r\n t?: string;\r\n sessionId?: string;\r\n name?: string;\r\n size?: number;\r\n message?: string;\r\n };\r\n\r\n if (msg.t === 'meta') {\r\n // Session ID validation - reject if we're busy with a different session\r\n if (currentSessionId && msg.sessionId && msg.sessionId !== currentSessionId) {\r\n try {\r\n conn.send({ t: 'error', message: 'Busy with another session.' });\r\n } catch {\r\n // Ignore send errors\r\n }\r\n return;\r\n }\r\n\r\n // Store the session ID for this transfer\r\n if (msg.sessionId) {\r\n currentSessionId = msg.sessionId;\r\n }\r\n\r\n const name = String(msg.name || 'file');\r\n total = Number(msg.size) || 0;\r\n received = 0;\r\n writeQueue = Promise.resolve();\r\n\r\n // Function to send ready signal - called automatically if autoReady is true,\r\n // or passed to onMeta callback for manual invocation if autoReady is false\r\n const sendReady = (): void => {\r\n state = 'transferring';\r\n // Start watchdog once we're ready to receive data\r\n resetWatchdog();\r\n try {\r\n conn.send({ t: 'ready' });\r\n } catch {\r\n // Ignore send errors\r\n }\r\n };\r\n\r\n if (autoReady) {\r\n onMeta?.({ name, total });\r\n onProgress?.({ processedBytes: received, totalBytes: total, percent: 0 });\r\n sendReady();\r\n } else {\r\n // Pass sendReady function to callback so consumer can trigger transfer start\r\n onMeta?.({ name, total, sendReady });\r\n onProgress?.({ processedBytes: received, totalBytes: total, percent: 0 });\r\n }\r\n return;\r\n }\r\n\r\n if (msg.t === 'ping') {\r\n // Respond to heartbeat - keeps watchdog alive and confirms we're active\r\n try {\r\n conn.send({ t: 'pong' });\r\n } catch {\r\n // Ignore send errors\r\n }\r\n return;\r\n }\r\n\r\n if (msg.t === 'end') {\r\n clearWatchdog();\r\n await writeQueue;\r\n\r\n if (total && received < total) {\r\n const err = new DropgateNetworkError(\r\n 'Transfer ended before the full file was received.'\r\n );\r\n try {\r\n conn.send({ t: 'error', message: err.message });\r\n } catch {\r\n // Ignore send errors\r\n }\r\n throw err;\r\n }\r\n\r\n try {\r\n conn.send({ t: 'ack', phase: 'end', received, total });\r\n } catch {\r\n // Ignore send errors\r\n }\r\n\r\n safeComplete({ received, total });\r\n return;\r\n }\r\n\r\n if (msg.t === 'error') {\r\n throw new DropgateNetworkError(msg.message || 'Sender reported an error.');\r\n }\r\n\r\n return;\r\n }\r\n\r\n // Handle binary data\r\n let bufPromise: Promise<Uint8Array>;\r\n\r\n if (data instanceof ArrayBuffer) {\r\n bufPromise = Promise.resolve(new Uint8Array(data));\r\n } else if (ArrayBuffer.isView(data)) {\r\n bufPromise = Promise.resolve(\r\n new Uint8Array(data.buffer, data.byteOffset, data.byteLength)\r\n );\r\n } else if (typeof Blob !== 'undefined' && data instanceof Blob) {\r\n bufPromise = data.arrayBuffer().then((buffer) => new Uint8Array(buffer));\r\n } else {\r\n return;\r\n }\r\n\r\n writeQueue = writeQueue\r\n .then(async () => {\r\n const buf = await bufPromise;\r\n\r\n // Call consumer's onData handler\r\n if (onData) {\r\n await onData(buf);\r\n }\r\n\r\n received += buf.byteLength;\r\n const percent = total ? Math.min(100, (received / total) * 100) : 0;\r\n onProgress?.({ processedBytes: received, totalBytes: total, percent });\r\n\r\n const now = Date.now();\r\n if (received === total || now - lastProgressSentAt >= progressIntervalMs) {\r\n lastProgressSentAt = now;\r\n try {\r\n conn.send({ t: 'progress', received, total });\r\n } catch {\r\n // Ignore send errors\r\n }\r\n }\r\n })\r\n .catch((err) => {\r\n try {\r\n conn.send({\r\n t: 'error',\r\n message: (err as Error)?.message || 'Receiver write failed.',\r\n });\r\n } catch {\r\n // Ignore send errors\r\n }\r\n safeError(err as Error);\r\n });\r\n } catch (err) {\r\n safeError(err as Error);\r\n }\r\n });\r\n\r\n conn.on('close', () => {\r\n if (state === 'closed' || state === 'completed') {\r\n // Clean shutdown, ensure full cleanup\r\n cleanup();\r\n return;\r\n }\r\n\r\n // Sender disconnected before transfer completed\r\n if (state === 'transferring') {\r\n // We were mid-transfer\r\n safeError(new DropgateNetworkError('Sender disconnected during transfer.'));\r\n } else if (state === 'negotiating') {\r\n // We had metadata but transfer hadn't started\r\n state = 'closed';\r\n cleanup();\r\n onDisconnect?.();\r\n } else {\r\n // Disconnected before we even got file metadata\r\n safeError(new DropgateNetworkError('Sender disconnected before file details were received.'));\r\n }\r\n });\r\n });\r\n\r\n return {\r\n peer,\r\n stop,\r\n getStatus: () => state,\r\n getBytesReceived: () => received,\r\n getTotalBytes: () => total,\r\n getSessionId: () => currentSessionId,\r\n };\r\n}\r\n"],"mappings":"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","names":["result","crypto"]}
|