@dropgate/core 3.0.0 → 3.0.2
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/LICENSE +201 -0
- package/README.md +4 -4
- package/dist/index.browser.js +1 -1
- package/dist/index.browser.js.map +1 -1
- package/dist/index.cjs +97 -6
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +97 -6
- package/dist/index.js.map +1 -1
- package/dist/p2p/index.cjs +97 -6
- package/dist/p2p/index.cjs.map +1 -1
- package/dist/p2p/index.js +97 -6
- package/dist/p2p/index.js.map +1 -1
- package/package.json +2 -2
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/sha256-fallback.ts","../src/crypto/decrypt.ts","../src/crypto/index.ts","../src/crypto/encrypt.ts","../node_modules/fflate/esm/browser.js","../src/zip/stream-zip.ts","../src/p2p/utils.ts","../src/p2p/helpers.ts","../src/p2p/protocol.ts","../src/p2p/send.ts","../src/p2p/receive.ts","../src/client/DropgateClient.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, opts.cause !== undefined ? { cause: opts.cause } : undefined);\r\n this.name = this.constructor.name;\r\n this.code = opts.code || 'DROPGATE_ERROR';\r\n this.details = opts.details;\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","/**\r\n * Pure-JS SHA-256 implementation for chunk integrity hashing ONLY.\r\n *\r\n * This exists solely as a fallback when crypto.subtle is unavailable\r\n * (e.g. insecure HTTP contexts). It MUST NOT be used for any\r\n * encryption, key derivation, or other security-critical operations.\r\n *\r\n * Based on the FIPS 180-4 specification.\r\n */\r\n\r\n// SHA-256 constants: first 32 bits of the fractional parts of the cube roots of the first 64 primes\r\nconst K = new Uint32Array([\r\n 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,\r\n 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,\r\n 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,\r\n 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,\r\n 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,\r\n 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,\r\n 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,\r\n 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,\r\n]);\r\n\r\nfunction rotr(x: number, n: number): number {\r\n return (x >>> n) | (x << (32 - n));\r\n}\r\n\r\n/**\r\n * Compute SHA-256 hash of an ArrayBuffer and return the raw digest.\r\n * FOR INTEGRITY VERIFICATION ONLY - not for cryptographic security operations.\r\n */\r\nexport function sha256Fallback(data: ArrayBuffer): ArrayBuffer {\r\n const bytes = new Uint8Array(data);\r\n const bitLen = bytes.length * 8;\r\n\r\n // Pre-processing: pad to 512-bit (64-byte) block boundary\r\n // message + 0x80 + zeros + 8-byte big-endian length\r\n const padded = new Uint8Array(\r\n Math.ceil((bytes.length + 9) / 64) * 64\r\n );\r\n padded.set(bytes);\r\n padded[bytes.length] = 0x80;\r\n\r\n // Append original length in bits as 64-bit big-endian\r\n const view = new DataView(padded.buffer);\r\n // bitLen fits in 53-bit JS number; write high 32 and low 32\r\n view.setUint32(padded.length - 8, (bitLen / 0x100000000) >>> 0, false);\r\n view.setUint32(padded.length - 4, bitLen >>> 0, false);\r\n\r\n // Initial hash values: first 32 bits of the fractional parts of the square roots of the first 8 primes\r\n let h0 = 0x6a09e667;\r\n let h1 = 0xbb67ae85;\r\n let h2 = 0x3c6ef372;\r\n let h3 = 0xa54ff53a;\r\n let h4 = 0x510e527f;\r\n let h5 = 0x9b05688c;\r\n let h6 = 0x1f83d9ab;\r\n let h7 = 0x5be0cd19;\r\n\r\n const W = new Uint32Array(64);\r\n\r\n for (let offset = 0; offset < padded.length; offset += 64) {\r\n // Prepare message schedule\r\n for (let i = 0; i < 16; i++) {\r\n W[i] = view.getUint32(offset + i * 4, false);\r\n }\r\n for (let i = 16; i < 64; i++) {\r\n const s0 = rotr(W[i - 15], 7) ^ rotr(W[i - 15], 18) ^ (W[i - 15] >>> 3);\r\n const s1 = rotr(W[i - 2], 17) ^ rotr(W[i - 2], 19) ^ (W[i - 2] >>> 10);\r\n W[i] = (W[i - 16] + s0 + W[i - 7] + s1) | 0;\r\n }\r\n\r\n let a = h0, b = h1, c = h2, d = h3, e = h4, f = h5, g = h6, h = h7;\r\n\r\n for (let i = 0; i < 64; i++) {\r\n const S1 = rotr(e, 6) ^ rotr(e, 11) ^ rotr(e, 25);\r\n const ch = (e & f) ^ (~e & g);\r\n const temp1 = (h + S1 + ch + K[i] + W[i]) | 0;\r\n const S0 = rotr(a, 2) ^ rotr(a, 13) ^ rotr(a, 22);\r\n const maj = (a & b) ^ (a & c) ^ (b & c);\r\n const temp2 = (S0 + maj) | 0;\r\n\r\n h = g;\r\n g = f;\r\n f = e;\r\n e = (d + temp1) | 0;\r\n d = c;\r\n c = b;\r\n b = a;\r\n a = (temp1 + temp2) | 0;\r\n }\r\n\r\n h0 = (h0 + a) | 0;\r\n h1 = (h1 + b) | 0;\r\n h2 = (h2 + c) | 0;\r\n h3 = (h3 + d) | 0;\r\n h4 = (h4 + e) | 0;\r\n h5 = (h5 + f) | 0;\r\n h6 = (h6 + g) | 0;\r\n h7 = (h7 + h) | 0;\r\n }\r\n\r\n const result = new ArrayBuffer(32);\r\n const out = new DataView(result);\r\n out.setUint32(0, h0, false);\r\n out.setUint32(4, h1, false);\r\n out.setUint32(8, h2, false);\r\n out.setUint32(12, h3, false);\r\n out.setUint32(16, h4, false);\r\n out.setUint32(20, h5, false);\r\n out.setUint32(24, h6, false);\r\n out.setUint32(28, h7, false);\r\n\r\n return result;\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\nimport { sha256Fallback } from './sha256-fallback.js';\r\n\r\n/**\r\n * Convert a raw SHA-256 digest ArrayBuffer to a hex string.\r\n */\r\nfunction digestToHex(hashBuffer: ArrayBuffer): string {\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 * Compute SHA-256 hash of data and return as hex string.\r\n *\r\n * Uses crypto.subtle when available. Falls back to a pure-JS\r\n * implementation for integrity hashing on insecure contexts.\r\n * The fallback MUST NOT be used for encryption operations.\r\n */\r\nexport async function sha256Hex(\r\n cryptoObj: CryptoAdapter,\r\n data: ArrayBuffer\r\n): Promise<string> {\r\n if (cryptoObj?.subtle) {\r\n const hashBuffer = await cryptoObj.subtle.digest('SHA-256', data);\r\n return digestToHex(hashBuffer);\r\n }\r\n // Fallback: pure-JS SHA-256 for integrity verification only\r\n return digestToHex(sha256Fallback(data));\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","// DEFLATE is a complex format; to read this code, you should probably check the RFC first:\n// https://tools.ietf.org/html/rfc1951\n// You may also wish to take a look at the guide I made about this program:\n// https://gist.github.com/101arrowz/253f31eb5abc3d9275ab943003ffecad\n// Some of the following code is similar to that of UZIP.js:\n// https://github.com/photopea/UZIP.js\n// However, the vast majority of the codebase has diverged from UZIP.js to increase performance and reduce bundle size.\n// Sometimes 0 will appear where -1 would be more appropriate. This is because using a uint\n// is better for memory in most engines (I *think*).\nvar ch2 = {};\nvar wk = (function (c, id, msg, transfer, cb) {\n var w = new Worker(ch2[id] || (ch2[id] = URL.createObjectURL(new Blob([\n c + ';addEventListener(\"error\",function(e){e=e.error;postMessage({$e$:[e.message,e.code,e.stack]})})'\n ], { type: 'text/javascript' }))));\n w.onmessage = function (e) {\n var d = e.data, ed = d.$e$;\n if (ed) {\n var err = new Error(ed[0]);\n err['code'] = ed[1];\n err.stack = ed[2];\n cb(err, null);\n }\n else\n cb(null, d);\n };\n w.postMessage(msg, transfer);\n return w;\n});\n\n// aliases for shorter compressed code (most minifers don't do this)\nvar u8 = Uint8Array, u16 = Uint16Array, i32 = Int32Array;\n// fixed length extra bits\nvar fleb = new u8([0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, /* unused */ 0, 0, /* impossible */ 0]);\n// fixed distance extra bits\nvar fdeb = new u8([0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, /* unused */ 0, 0]);\n// code length index map\nvar clim = new u8([16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15]);\n// get base, reverse index map from extra bits\nvar freb = function (eb, start) {\n var b = new u16(31);\n for (var i = 0; i < 31; ++i) {\n b[i] = start += 1 << eb[i - 1];\n }\n // numbers here are at max 18 bits\n var r = new i32(b[30]);\n for (var i = 1; i < 30; ++i) {\n for (var j = b[i]; j < b[i + 1]; ++j) {\n r[j] = ((j - b[i]) << 5) | i;\n }\n }\n return { b: b, r: r };\n};\nvar _a = freb(fleb, 2), fl = _a.b, revfl = _a.r;\n// we can ignore the fact that the other numbers are wrong; they never happen anyway\nfl[28] = 258, revfl[258] = 28;\nvar _b = freb(fdeb, 0), fd = _b.b, revfd = _b.r;\n// map of value to reverse (assuming 16 bits)\nvar rev = new u16(32768);\nfor (var i = 0; i < 32768; ++i) {\n // reverse table algorithm from SO\n var x = ((i & 0xAAAA) >> 1) | ((i & 0x5555) << 1);\n x = ((x & 0xCCCC) >> 2) | ((x & 0x3333) << 2);\n x = ((x & 0xF0F0) >> 4) | ((x & 0x0F0F) << 4);\n rev[i] = (((x & 0xFF00) >> 8) | ((x & 0x00FF) << 8)) >> 1;\n}\n// create huffman tree from u8 \"map\": index -> code length for code index\n// mb (max bits) must be at most 15\n// TODO: optimize/split up?\nvar hMap = (function (cd, mb, r) {\n var s = cd.length;\n // index\n var i = 0;\n // u16 \"map\": index -> # of codes with bit length = index\n var l = new u16(mb);\n // length of cd must be 288 (total # of codes)\n for (; i < s; ++i) {\n if (cd[i])\n ++l[cd[i] - 1];\n }\n // u16 \"map\": index -> minimum code for bit length = index\n var le = new u16(mb);\n for (i = 1; i < mb; ++i) {\n le[i] = (le[i - 1] + l[i - 1]) << 1;\n }\n var co;\n if (r) {\n // u16 \"map\": index -> number of actual bits, symbol for code\n co = new u16(1 << mb);\n // bits to remove for reverser\n var rvb = 15 - mb;\n for (i = 0; i < s; ++i) {\n // ignore 0 lengths\n if (cd[i]) {\n // num encoding both symbol and bits read\n var sv = (i << 4) | cd[i];\n // free bits\n var r_1 = mb - cd[i];\n // start value\n var v = le[cd[i] - 1]++ << r_1;\n // m is end value\n for (var m = v | ((1 << r_1) - 1); v <= m; ++v) {\n // every 16 bit value starting with the code yields the same result\n co[rev[v] >> rvb] = sv;\n }\n }\n }\n }\n else {\n co = new u16(s);\n for (i = 0; i < s; ++i) {\n if (cd[i]) {\n co[i] = rev[le[cd[i] - 1]++] >> (15 - cd[i]);\n }\n }\n }\n return co;\n});\n// fixed length tree\nvar flt = new u8(288);\nfor (var i = 0; i < 144; ++i)\n flt[i] = 8;\nfor (var i = 144; i < 256; ++i)\n flt[i] = 9;\nfor (var i = 256; i < 280; ++i)\n flt[i] = 7;\nfor (var i = 280; i < 288; ++i)\n flt[i] = 8;\n// fixed distance tree\nvar fdt = new u8(32);\nfor (var i = 0; i < 32; ++i)\n fdt[i] = 5;\n// fixed length map\nvar flm = /*#__PURE__*/ hMap(flt, 9, 0), flrm = /*#__PURE__*/ hMap(flt, 9, 1);\n// fixed distance map\nvar fdm = /*#__PURE__*/ hMap(fdt, 5, 0), fdrm = /*#__PURE__*/ hMap(fdt, 5, 1);\n// find max of array\nvar max = function (a) {\n var m = a[0];\n for (var i = 1; i < a.length; ++i) {\n if (a[i] > m)\n m = a[i];\n }\n return m;\n};\n// read d, starting at bit p and mask with m\nvar bits = function (d, p, m) {\n var o = (p / 8) | 0;\n return ((d[o] | (d[o + 1] << 8)) >> (p & 7)) & m;\n};\n// read d, starting at bit p continuing for at least 16 bits\nvar bits16 = function (d, p) {\n var o = (p / 8) | 0;\n return ((d[o] | (d[o + 1] << 8) | (d[o + 2] << 16)) >> (p & 7));\n};\n// get end of byte\nvar shft = function (p) { return ((p + 7) / 8) | 0; };\n// typed array slice - allows garbage collector to free original reference,\n// while being more compatible than .slice\nvar slc = function (v, s, e) {\n if (s == null || s < 0)\n s = 0;\n if (e == null || e > v.length)\n e = v.length;\n // can't use .constructor in case user-supplied\n return new u8(v.subarray(s, e));\n};\n/**\n * Codes for errors generated within this library\n */\nexport var FlateErrorCode = {\n UnexpectedEOF: 0,\n InvalidBlockType: 1,\n InvalidLengthLiteral: 2,\n InvalidDistance: 3,\n StreamFinished: 4,\n NoStreamHandler: 5,\n InvalidHeader: 6,\n NoCallback: 7,\n InvalidUTF8: 8,\n ExtraFieldTooLong: 9,\n InvalidDate: 10,\n FilenameTooLong: 11,\n StreamFinishing: 12,\n InvalidZipData: 13,\n UnknownCompressionMethod: 14\n};\n// error codes\nvar ec = [\n 'unexpected EOF',\n 'invalid block type',\n 'invalid length/literal',\n 'invalid distance',\n 'stream finished',\n 'no stream handler',\n ,\n 'no callback',\n 'invalid UTF-8 data',\n 'extra field too long',\n 'date not in range 1980-2099',\n 'filename too long',\n 'stream finishing',\n 'invalid zip data'\n // determined by unknown compression method\n];\n;\nvar err = function (ind, msg, nt) {\n var e = new Error(msg || ec[ind]);\n e.code = ind;\n if (Error.captureStackTrace)\n Error.captureStackTrace(e, err);\n if (!nt)\n throw e;\n return e;\n};\n// expands raw DEFLATE data\nvar inflt = function (dat, st, buf, dict) {\n // source length dict length\n var sl = dat.length, dl = dict ? dict.length : 0;\n if (!sl || st.f && !st.l)\n return buf || new u8(0);\n var noBuf = !buf;\n // have to estimate size\n var resize = noBuf || st.i != 2;\n // no state\n var noSt = st.i;\n // Assumes roughly 33% compression ratio average\n if (noBuf)\n buf = new u8(sl * 3);\n // ensure buffer can fit at least l elements\n var cbuf = function (l) {\n var bl = buf.length;\n // need to increase size to fit\n if (l > bl) {\n // Double or set to necessary, whichever is greater\n var nbuf = new u8(Math.max(bl * 2, l));\n nbuf.set(buf);\n buf = nbuf;\n }\n };\n // last chunk bitpos bytes\n var final = st.f || 0, pos = st.p || 0, bt = st.b || 0, lm = st.l, dm = st.d, lbt = st.m, dbt = st.n;\n // total bits\n var tbts = sl * 8;\n do {\n if (!lm) {\n // BFINAL - this is only 1 when last chunk is next\n final = bits(dat, pos, 1);\n // type: 0 = no compression, 1 = fixed huffman, 2 = dynamic huffman\n var type = bits(dat, pos + 1, 3);\n pos += 3;\n if (!type) {\n // go to end of byte boundary\n var s = shft(pos) + 4, l = dat[s - 4] | (dat[s - 3] << 8), t = s + l;\n if (t > sl) {\n if (noSt)\n err(0);\n break;\n }\n // ensure size\n if (resize)\n cbuf(bt + l);\n // Copy over uncompressed data\n buf.set(dat.subarray(s, t), bt);\n // Get new bitpos, update byte count\n st.b = bt += l, st.p = pos = t * 8, st.f = final;\n continue;\n }\n else if (type == 1)\n lm = flrm, dm = fdrm, lbt = 9, dbt = 5;\n else if (type == 2) {\n // literal lengths\n var hLit = bits(dat, pos, 31) + 257, hcLen = bits(dat, pos + 10, 15) + 4;\n var tl = hLit + bits(dat, pos + 5, 31) + 1;\n pos += 14;\n // length+distance tree\n var ldt = new u8(tl);\n // code length tree\n var clt = new u8(19);\n for (var i = 0; i < hcLen; ++i) {\n // use index map to get real code\n clt[clim[i]] = bits(dat, pos + i * 3, 7);\n }\n pos += hcLen * 3;\n // code lengths bits\n var clb = max(clt), clbmsk = (1 << clb) - 1;\n // code lengths map\n var clm = hMap(clt, clb, 1);\n for (var i = 0; i < tl;) {\n var r = clm[bits(dat, pos, clbmsk)];\n // bits read\n pos += r & 15;\n // symbol\n var s = r >> 4;\n // code length to copy\n if (s < 16) {\n ldt[i++] = s;\n }\n else {\n // copy count\n var c = 0, n = 0;\n if (s == 16)\n n = 3 + bits(dat, pos, 3), pos += 2, c = ldt[i - 1];\n else if (s == 17)\n n = 3 + bits(dat, pos, 7), pos += 3;\n else if (s == 18)\n n = 11 + bits(dat, pos, 127), pos += 7;\n while (n--)\n ldt[i++] = c;\n }\n }\n // length tree distance tree\n var lt = ldt.subarray(0, hLit), dt = ldt.subarray(hLit);\n // max length bits\n lbt = max(lt);\n // max dist bits\n dbt = max(dt);\n lm = hMap(lt, lbt, 1);\n dm = hMap(dt, dbt, 1);\n }\n else\n err(1);\n if (pos > tbts) {\n if (noSt)\n err(0);\n break;\n }\n }\n // Make sure the buffer can hold this + the largest possible addition\n // Maximum chunk size (practically, theoretically infinite) is 2^17\n if (resize)\n cbuf(bt + 131072);\n var lms = (1 << lbt) - 1, dms = (1 << dbt) - 1;\n var lpos = pos;\n for (;; lpos = pos) {\n // bits read, code\n var c = lm[bits16(dat, pos) & lms], sym = c >> 4;\n pos += c & 15;\n if (pos > tbts) {\n if (noSt)\n err(0);\n break;\n }\n if (!c)\n err(2);\n if (sym < 256)\n buf[bt++] = sym;\n else if (sym == 256) {\n lpos = pos, lm = null;\n break;\n }\n else {\n var add = sym - 254;\n // no extra bits needed if less\n if (sym > 264) {\n // index\n var i = sym - 257, b = fleb[i];\n add = bits(dat, pos, (1 << b) - 1) + fl[i];\n pos += b;\n }\n // dist\n var d = dm[bits16(dat, pos) & dms], dsym = d >> 4;\n if (!d)\n err(3);\n pos += d & 15;\n var dt = fd[dsym];\n if (dsym > 3) {\n var b = fdeb[dsym];\n dt += bits16(dat, pos) & (1 << b) - 1, pos += b;\n }\n if (pos > tbts) {\n if (noSt)\n err(0);\n break;\n }\n if (resize)\n cbuf(bt + 131072);\n var end = bt + add;\n if (bt < dt) {\n var shift = dl - dt, dend = Math.min(dt, end);\n if (shift + bt < 0)\n err(3);\n for (; bt < dend; ++bt)\n buf[bt] = dict[shift + bt];\n }\n for (; bt < end; ++bt)\n buf[bt] = buf[bt - dt];\n }\n }\n st.l = lm, st.p = lpos, st.b = bt, st.f = final;\n if (lm)\n final = 1, st.m = lbt, st.d = dm, st.n = dbt;\n } while (!final);\n // don't reallocate for streams or user buffers\n return bt != buf.length && noBuf ? slc(buf, 0, bt) : buf.subarray(0, bt);\n};\n// starting at p, write the minimum number of bits that can hold v to d\nvar wbits = function (d, p, v) {\n v <<= p & 7;\n var o = (p / 8) | 0;\n d[o] |= v;\n d[o + 1] |= v >> 8;\n};\n// starting at p, write the minimum number of bits (>8) that can hold v to d\nvar wbits16 = function (d, p, v) {\n v <<= p & 7;\n var o = (p / 8) | 0;\n d[o] |= v;\n d[o + 1] |= v >> 8;\n d[o + 2] |= v >> 16;\n};\n// creates code lengths from a frequency table\nvar hTree = function (d, mb) {\n // Need extra info to make a tree\n var t = [];\n for (var i = 0; i < d.length; ++i) {\n if (d[i])\n t.push({ s: i, f: d[i] });\n }\n var s = t.length;\n var t2 = t.slice();\n if (!s)\n return { t: et, l: 0 };\n if (s == 1) {\n var v = new u8(t[0].s + 1);\n v[t[0].s] = 1;\n return { t: v, l: 1 };\n }\n t.sort(function (a, b) { return a.f - b.f; });\n // after i2 reaches last ind, will be stopped\n // freq must be greater than largest possible number of symbols\n t.push({ s: -1, f: 25001 });\n var l = t[0], r = t[1], i0 = 0, i1 = 1, i2 = 2;\n t[0] = { s: -1, f: l.f + r.f, l: l, r: r };\n // efficient algorithm from UZIP.js\n // i0 is lookbehind, i2 is lookahead - after processing two low-freq\n // symbols that combined have high freq, will start processing i2 (high-freq,\n // non-composite) symbols instead\n // see https://reddit.com/r/photopea/comments/ikekht/uzipjs_questions/\n while (i1 != s - 1) {\n l = t[t[i0].f < t[i2].f ? i0++ : i2++];\n r = t[i0 != i1 && t[i0].f < t[i2].f ? i0++ : i2++];\n t[i1++] = { s: -1, f: l.f + r.f, l: l, r: r };\n }\n var maxSym = t2[0].s;\n for (var i = 1; i < s; ++i) {\n if (t2[i].s > maxSym)\n maxSym = t2[i].s;\n }\n // code lengths\n var tr = new u16(maxSym + 1);\n // max bits in tree\n var mbt = ln(t[i1 - 1], tr, 0);\n if (mbt > mb) {\n // more algorithms from UZIP.js\n // TODO: find out how this code works (debt)\n // ind debt\n var i = 0, dt = 0;\n // left cost\n var lft = mbt - mb, cst = 1 << lft;\n t2.sort(function (a, b) { return tr[b.s] - tr[a.s] || a.f - b.f; });\n for (; i < s; ++i) {\n var i2_1 = t2[i].s;\n if (tr[i2_1] > mb) {\n dt += cst - (1 << (mbt - tr[i2_1]));\n tr[i2_1] = mb;\n }\n else\n break;\n }\n dt >>= lft;\n while (dt > 0) {\n var i2_2 = t2[i].s;\n if (tr[i2_2] < mb)\n dt -= 1 << (mb - tr[i2_2]++ - 1);\n else\n ++i;\n }\n for (; i >= 0 && dt; --i) {\n var i2_3 = t2[i].s;\n if (tr[i2_3] == mb) {\n --tr[i2_3];\n ++dt;\n }\n }\n mbt = mb;\n }\n return { t: new u8(tr), l: mbt };\n};\n// get the max length and assign length codes\nvar ln = function (n, l, d) {\n return n.s == -1\n ? Math.max(ln(n.l, l, d + 1), ln(n.r, l, d + 1))\n : (l[n.s] = d);\n};\n// length codes generation\nvar lc = function (c) {\n var s = c.length;\n // Note that the semicolon was intentional\n while (s && !c[--s])\n ;\n var cl = new u16(++s);\n // ind num streak\n var cli = 0, cln = c[0], cls = 1;\n var w = function (v) { cl[cli++] = v; };\n for (var i = 1; i <= s; ++i) {\n if (c[i] == cln && i != s)\n ++cls;\n else {\n if (!cln && cls > 2) {\n for (; cls > 138; cls -= 138)\n w(32754);\n if (cls > 2) {\n w(cls > 10 ? ((cls - 11) << 5) | 28690 : ((cls - 3) << 5) | 12305);\n cls = 0;\n }\n }\n else if (cls > 3) {\n w(cln), --cls;\n for (; cls > 6; cls -= 6)\n w(8304);\n if (cls > 2)\n w(((cls - 3) << 5) | 8208), cls = 0;\n }\n while (cls--)\n w(cln);\n cls = 1;\n cln = c[i];\n }\n }\n return { c: cl.subarray(0, cli), n: s };\n};\n// calculate the length of output from tree, code lengths\nvar clen = function (cf, cl) {\n var l = 0;\n for (var i = 0; i < cl.length; ++i)\n l += cf[i] * cl[i];\n return l;\n};\n// writes a fixed block\n// returns the new bit pos\nvar wfblk = function (out, pos, dat) {\n // no need to write 00 as type: TypedArray defaults to 0\n var s = dat.length;\n var o = shft(pos + 2);\n out[o] = s & 255;\n out[o + 1] = s >> 8;\n out[o + 2] = out[o] ^ 255;\n out[o + 3] = out[o + 1] ^ 255;\n for (var i = 0; i < s; ++i)\n out[o + i + 4] = dat[i];\n return (o + 4 + s) * 8;\n};\n// writes a block\nvar wblk = function (dat, out, final, syms, lf, df, eb, li, bs, bl, p) {\n wbits(out, p++, final);\n ++lf[256];\n var _a = hTree(lf, 15), dlt = _a.t, mlb = _a.l;\n var _b = hTree(df, 15), ddt = _b.t, mdb = _b.l;\n var _c = lc(dlt), lclt = _c.c, nlc = _c.n;\n var _d = lc(ddt), lcdt = _d.c, ndc = _d.n;\n var lcfreq = new u16(19);\n for (var i = 0; i < lclt.length; ++i)\n ++lcfreq[lclt[i] & 31];\n for (var i = 0; i < lcdt.length; ++i)\n ++lcfreq[lcdt[i] & 31];\n var _e = hTree(lcfreq, 7), lct = _e.t, mlcb = _e.l;\n var nlcc = 19;\n for (; nlcc > 4 && !lct[clim[nlcc - 1]]; --nlcc)\n ;\n var flen = (bl + 5) << 3;\n var ftlen = clen(lf, flt) + clen(df, fdt) + eb;\n var dtlen = clen(lf, dlt) + clen(df, ddt) + eb + 14 + 3 * nlcc + clen(lcfreq, lct) + 2 * lcfreq[16] + 3 * lcfreq[17] + 7 * lcfreq[18];\n if (bs >= 0 && flen <= ftlen && flen <= dtlen)\n return wfblk(out, p, dat.subarray(bs, bs + bl));\n var lm, ll, dm, dl;\n wbits(out, p, 1 + (dtlen < ftlen)), p += 2;\n if (dtlen < ftlen) {\n lm = hMap(dlt, mlb, 0), ll = dlt, dm = hMap(ddt, mdb, 0), dl = ddt;\n var llm = hMap(lct, mlcb, 0);\n wbits(out, p, nlc - 257);\n wbits(out, p + 5, ndc - 1);\n wbits(out, p + 10, nlcc - 4);\n p += 14;\n for (var i = 0; i < nlcc; ++i)\n wbits(out, p + 3 * i, lct[clim[i]]);\n p += 3 * nlcc;\n var lcts = [lclt, lcdt];\n for (var it = 0; it < 2; ++it) {\n var clct = lcts[it];\n for (var i = 0; i < clct.length; ++i) {\n var len = clct[i] & 31;\n wbits(out, p, llm[len]), p += lct[len];\n if (len > 15)\n wbits(out, p, (clct[i] >> 5) & 127), p += clct[i] >> 12;\n }\n }\n }\n else {\n lm = flm, ll = flt, dm = fdm, dl = fdt;\n }\n for (var i = 0; i < li; ++i) {\n var sym = syms[i];\n if (sym > 255) {\n var len = (sym >> 18) & 31;\n wbits16(out, p, lm[len + 257]), p += ll[len + 257];\n if (len > 7)\n wbits(out, p, (sym >> 23) & 31), p += fleb[len];\n var dst = sym & 31;\n wbits16(out, p, dm[dst]), p += dl[dst];\n if (dst > 3)\n wbits16(out, p, (sym >> 5) & 8191), p += fdeb[dst];\n }\n else {\n wbits16(out, p, lm[sym]), p += ll[sym];\n }\n }\n wbits16(out, p, lm[256]);\n return p + ll[256];\n};\n// deflate options (nice << 13) | chain\nvar deo = /*#__PURE__*/ new i32([65540, 131080, 131088, 131104, 262176, 1048704, 1048832, 2114560, 2117632]);\n// empty\nvar et = /*#__PURE__*/ new u8(0);\n// compresses data into a raw DEFLATE buffer\nvar dflt = function (dat, lvl, plvl, pre, post, st) {\n var s = st.z || dat.length;\n var o = new u8(pre + s + 5 * (1 + Math.ceil(s / 7000)) + post);\n // writing to this writes to the output buffer\n var w = o.subarray(pre, o.length - post);\n var lst = st.l;\n var pos = (st.r || 0) & 7;\n if (lvl) {\n if (pos)\n w[0] = st.r >> 3;\n var opt = deo[lvl - 1];\n var n = opt >> 13, c = opt & 8191;\n var msk_1 = (1 << plvl) - 1;\n // prev 2-byte val map curr 2-byte val map\n var prev = st.p || new u16(32768), head = st.h || new u16(msk_1 + 1);\n var bs1_1 = Math.ceil(plvl / 3), bs2_1 = 2 * bs1_1;\n var hsh = function (i) { return (dat[i] ^ (dat[i + 1] << bs1_1) ^ (dat[i + 2] << bs2_1)) & msk_1; };\n // 24576 is an arbitrary number of maximum symbols per block\n // 424 buffer for last block\n var syms = new i32(25000);\n // length/literal freq distance freq\n var lf = new u16(288), df = new u16(32);\n // l/lcnt exbits index l/lind waitdx blkpos\n var lc_1 = 0, eb = 0, i = st.i || 0, li = 0, wi = st.w || 0, bs = 0;\n for (; i + 2 < s; ++i) {\n // hash value\n var hv = hsh(i);\n // index mod 32768 previous index mod\n var imod = i & 32767, pimod = head[hv];\n prev[imod] = pimod;\n head[hv] = imod;\n // We always should modify head and prev, but only add symbols if\n // this data is not yet processed (\"wait\" for wait index)\n if (wi <= i) {\n // bytes remaining\n var rem = s - i;\n if ((lc_1 > 7000 || li > 24576) && (rem > 423 || !lst)) {\n pos = wblk(dat, w, 0, syms, lf, df, eb, li, bs, i - bs, pos);\n li = lc_1 = eb = 0, bs = i;\n for (var j = 0; j < 286; ++j)\n lf[j] = 0;\n for (var j = 0; j < 30; ++j)\n df[j] = 0;\n }\n // len dist chain\n var l = 2, d = 0, ch_1 = c, dif = imod - pimod & 32767;\n if (rem > 2 && hv == hsh(i - dif)) {\n var maxn = Math.min(n, rem) - 1;\n var maxd = Math.min(32767, i);\n // max possible length\n // not capped at dif because decompressors implement \"rolling\" index population\n var ml = Math.min(258, rem);\n while (dif <= maxd && --ch_1 && imod != pimod) {\n if (dat[i + l] == dat[i + l - dif]) {\n var nl = 0;\n for (; nl < ml && dat[i + nl] == dat[i + nl - dif]; ++nl)\n ;\n if (nl > l) {\n l = nl, d = dif;\n // break out early when we reach \"nice\" (we are satisfied enough)\n if (nl > maxn)\n break;\n // now, find the rarest 2-byte sequence within this\n // length of literals and search for that instead.\n // Much faster than just using the start\n var mmd = Math.min(dif, nl - 2);\n var md = 0;\n for (var j = 0; j < mmd; ++j) {\n var ti = i - dif + j & 32767;\n var pti = prev[ti];\n var cd = ti - pti & 32767;\n if (cd > md)\n md = cd, pimod = ti;\n }\n }\n }\n // check the previous match\n imod = pimod, pimod = prev[imod];\n dif += imod - pimod & 32767;\n }\n }\n // d will be nonzero only when a match was found\n if (d) {\n // store both dist and len data in one int32\n // Make sure this is recognized as a len/dist with 28th bit (2^28)\n syms[li++] = 268435456 | (revfl[l] << 18) | revfd[d];\n var lin = revfl[l] & 31, din = revfd[d] & 31;\n eb += fleb[lin] + fdeb[din];\n ++lf[257 + lin];\n ++df[din];\n wi = i + l;\n ++lc_1;\n }\n else {\n syms[li++] = dat[i];\n ++lf[dat[i]];\n }\n }\n }\n for (i = Math.max(i, wi); i < s; ++i) {\n syms[li++] = dat[i];\n ++lf[dat[i]];\n }\n pos = wblk(dat, w, lst, syms, lf, df, eb, li, bs, i - bs, pos);\n if (!lst) {\n st.r = (pos & 7) | w[(pos / 8) | 0] << 3;\n // shft(pos) now 1 less if pos & 7 != 0\n pos -= 7;\n st.h = head, st.p = prev, st.i = i, st.w = wi;\n }\n }\n else {\n for (var i = st.w || 0; i < s + lst; i += 65535) {\n // end\n var e = i + 65535;\n if (e >= s) {\n // write final block\n w[(pos / 8) | 0] = lst;\n e = s;\n }\n pos = wfblk(w, pos + 1, dat.subarray(i, e));\n }\n st.i = s;\n }\n return slc(o, 0, pre + shft(pos) + post);\n};\n// CRC32 table\nvar crct = /*#__PURE__*/ (function () {\n var t = new Int32Array(256);\n for (var i = 0; i < 256; ++i) {\n var c = i, k = 9;\n while (--k)\n c = ((c & 1) && -306674912) ^ (c >>> 1);\n t[i] = c;\n }\n return t;\n})();\n// CRC32\nvar crc = function () {\n var c = -1;\n return {\n p: function (d) {\n // closures have awful performance\n var cr = c;\n for (var i = 0; i < d.length; ++i)\n cr = crct[(cr & 255) ^ d[i]] ^ (cr >>> 8);\n c = cr;\n },\n d: function () { return ~c; }\n };\n};\n// Adler32\nvar adler = function () {\n var a = 1, b = 0;\n return {\n p: function (d) {\n // closures have awful performance\n var n = a, m = b;\n var l = d.length | 0;\n for (var i = 0; i != l;) {\n var e = Math.min(i + 2655, l);\n for (; i < e; ++i)\n m += n += d[i];\n n = (n & 65535) + 15 * (n >> 16), m = (m & 65535) + 15 * (m >> 16);\n }\n a = n, b = m;\n },\n d: function () {\n a %= 65521, b %= 65521;\n return (a & 255) << 24 | (a & 0xFF00) << 8 | (b & 255) << 8 | (b >> 8);\n }\n };\n};\n;\n// deflate with opts\nvar dopt = function (dat, opt, pre, post, st) {\n if (!st) {\n st = { l: 1 };\n if (opt.dictionary) {\n var dict = opt.dictionary.subarray(-32768);\n var newDat = new u8(dict.length + dat.length);\n newDat.set(dict);\n newDat.set(dat, dict.length);\n dat = newDat;\n st.w = dict.length;\n }\n }\n return dflt(dat, opt.level == null ? 6 : opt.level, opt.mem == null ? (st.l ? Math.ceil(Math.max(8, Math.min(13, Math.log(dat.length))) * 1.5) : 20) : (12 + opt.mem), pre, post, st);\n};\n// Walmart object spread\nvar mrg = function (a, b) {\n var o = {};\n for (var k in a)\n o[k] = a[k];\n for (var k in b)\n o[k] = b[k];\n return o;\n};\n// worker clone\n// This is possibly the craziest part of the entire codebase, despite how simple it may seem.\n// The only parameter to this function is a closure that returns an array of variables outside of the function scope.\n// We're going to try to figure out the variable names used in the closure as strings because that is crucial for workerization.\n// We will return an object mapping of true variable name to value (basically, the current scope as a JS object).\n// The reason we can't just use the original variable names is minifiers mangling the toplevel scope.\n// This took me three weeks to figure out how to do.\nvar wcln = function (fn, fnStr, td) {\n var dt = fn();\n var st = fn.toString();\n var ks = st.slice(st.indexOf('[') + 1, st.lastIndexOf(']')).replace(/\\s+/g, '').split(',');\n for (var i = 0; i < dt.length; ++i) {\n var v = dt[i], k = ks[i];\n if (typeof v == 'function') {\n fnStr += ';' + k + '=';\n var st_1 = v.toString();\n if (v.prototype) {\n // for global objects\n if (st_1.indexOf('[native code]') != -1) {\n var spInd = st_1.indexOf(' ', 8) + 1;\n fnStr += st_1.slice(spInd, st_1.indexOf('(', spInd));\n }\n else {\n fnStr += st_1;\n for (var t in v.prototype)\n fnStr += ';' + k + '.prototype.' + t + '=' + v.prototype[t].toString();\n }\n }\n else\n fnStr += st_1;\n }\n else\n td[k] = v;\n }\n return fnStr;\n};\nvar ch = [];\n// clone bufs\nvar cbfs = function (v) {\n var tl = [];\n for (var k in v) {\n if (v[k].buffer) {\n tl.push((v[k] = new v[k].constructor(v[k])).buffer);\n }\n }\n return tl;\n};\n// use a worker to execute code\nvar wrkr = function (fns, init, id, cb) {\n if (!ch[id]) {\n var fnStr = '', td_1 = {}, m = fns.length - 1;\n for (var i = 0; i < m; ++i)\n fnStr = wcln(fns[i], fnStr, td_1);\n ch[id] = { c: wcln(fns[m], fnStr, td_1), e: td_1 };\n }\n var td = mrg({}, ch[id].e);\n return wk(ch[id].c + ';onmessage=function(e){for(var k in e.data)self[k]=e.data[k];onmessage=' + init.toString() + '}', id, td, cbfs(td), cb);\n};\n// base async inflate fn\nvar bInflt = function () { return [u8, u16, i32, fleb, fdeb, clim, fl, fd, flrm, fdrm, rev, ec, hMap, max, bits, bits16, shft, slc, err, inflt, inflateSync, pbf, gopt]; };\nvar bDflt = function () { return [u8, u16, i32, fleb, fdeb, clim, revfl, revfd, flm, flt, fdm, fdt, rev, deo, et, hMap, wbits, wbits16, hTree, ln, lc, clen, wfblk, wblk, shft, slc, dflt, dopt, deflateSync, pbf]; };\n// gzip extra\nvar gze = function () { return [gzh, gzhl, wbytes, crc, crct]; };\n// gunzip extra\nvar guze = function () { return [gzs, gzl]; };\n// zlib extra\nvar zle = function () { return [zlh, wbytes, adler]; };\n// unzlib extra\nvar zule = function () { return [zls]; };\n// post buf\nvar pbf = function (msg) { return postMessage(msg, [msg.buffer]); };\n// get opts\nvar gopt = function (o) { return o && {\n out: o.size && new u8(o.size),\n dictionary: o.dictionary\n}; };\n// async helper\nvar cbify = function (dat, opts, fns, init, id, cb) {\n var w = wrkr(fns, init, id, function (err, dat) {\n w.terminate();\n cb(err, dat);\n });\n w.postMessage([dat, opts], opts.consume ? [dat.buffer] : []);\n return function () { w.terminate(); };\n};\n// auto stream\nvar astrm = function (strm) {\n strm.ondata = function (dat, final) { return postMessage([dat, final], [dat.buffer]); };\n return function (ev) {\n if (ev.data.length) {\n strm.push(ev.data[0], ev.data[1]);\n postMessage([ev.data[0].length]);\n }\n else\n strm.flush();\n };\n};\n// async stream attach\nvar astrmify = function (fns, strm, opts, init, id, flush, ext) {\n var t;\n var w = wrkr(fns, init, id, function (err, dat) {\n if (err)\n w.terminate(), strm.ondata.call(strm, err);\n else if (!Array.isArray(dat))\n ext(dat);\n else if (dat.length == 1) {\n strm.queuedSize -= dat[0];\n if (strm.ondrain)\n strm.ondrain(dat[0]);\n }\n else {\n if (dat[1])\n w.terminate();\n strm.ondata.call(strm, err, dat[0], dat[1]);\n }\n });\n w.postMessage(opts);\n strm.queuedSize = 0;\n strm.push = function (d, f) {\n if (!strm.ondata)\n err(5);\n if (t)\n strm.ondata(err(4, 0, 1), null, !!f);\n strm.queuedSize += d.length;\n w.postMessage([d, t = f], [d.buffer]);\n };\n strm.terminate = function () { w.terminate(); };\n if (flush) {\n strm.flush = function () { w.postMessage([]); };\n }\n};\n// read 2 bytes\nvar b2 = function (d, b) { return d[b] | (d[b + 1] << 8); };\n// read 4 bytes\nvar b4 = function (d, b) { return (d[b] | (d[b + 1] << 8) | (d[b + 2] << 16) | (d[b + 3] << 24)) >>> 0; };\nvar b8 = function (d, b) { return b4(d, b) + (b4(d, b + 4) * 4294967296); };\n// write bytes\nvar wbytes = function (d, b, v) {\n for (; v; ++b)\n d[b] = v, v >>>= 8;\n};\n// gzip header\nvar gzh = function (c, o) {\n var fn = o.filename;\n c[0] = 31, c[1] = 139, c[2] = 8, c[8] = o.level < 2 ? 4 : o.level == 9 ? 2 : 0, c[9] = 3; // assume Unix\n if (o.mtime != 0)\n wbytes(c, 4, Math.floor(new Date(o.mtime || Date.now()) / 1000));\n if (fn) {\n c[3] = 8;\n for (var i = 0; i <= fn.length; ++i)\n c[i + 10] = fn.charCodeAt(i);\n }\n};\n// gzip footer: -8 to -4 = CRC, -4 to -0 is length\n// gzip start\nvar gzs = function (d) {\n if (d[0] != 31 || d[1] != 139 || d[2] != 8)\n err(6, 'invalid gzip data');\n var flg = d[3];\n var st = 10;\n if (flg & 4)\n st += (d[10] | d[11] << 8) + 2;\n for (var zs = (flg >> 3 & 1) + (flg >> 4 & 1); zs > 0; zs -= !d[st++])\n ;\n return st + (flg & 2);\n};\n// gzip length\nvar gzl = function (d) {\n var l = d.length;\n return (d[l - 4] | d[l - 3] << 8 | d[l - 2] << 16 | d[l - 1] << 24) >>> 0;\n};\n// gzip header length\nvar gzhl = function (o) { return 10 + (o.filename ? o.filename.length + 1 : 0); };\n// zlib header\nvar zlh = function (c, o) {\n var lv = o.level, fl = lv == 0 ? 0 : lv < 6 ? 1 : lv == 9 ? 3 : 2;\n c[0] = 120, c[1] = (fl << 6) | (o.dictionary && 32);\n c[1] |= 31 - ((c[0] << 8) | c[1]) % 31;\n if (o.dictionary) {\n var h = adler();\n h.p(o.dictionary);\n wbytes(c, 2, h.d());\n }\n};\n// zlib start\nvar zls = function (d, dict) {\n if ((d[0] & 15) != 8 || (d[0] >> 4) > 7 || ((d[0] << 8 | d[1]) % 31))\n err(6, 'invalid zlib data');\n if ((d[1] >> 5 & 1) == +!dict)\n err(6, 'invalid zlib data: ' + (d[1] & 32 ? 'need' : 'unexpected') + ' dictionary');\n return (d[1] >> 3 & 4) + 2;\n};\nfunction StrmOpt(opts, cb) {\n if (typeof opts == 'function')\n cb = opts, opts = {};\n this.ondata = cb;\n return opts;\n}\n/**\n * Streaming DEFLATE compression\n */\nvar Deflate = /*#__PURE__*/ (function () {\n function Deflate(opts, cb) {\n if (typeof opts == 'function')\n cb = opts, opts = {};\n this.ondata = cb;\n this.o = opts || {};\n this.s = { l: 0, i: 32768, w: 32768, z: 32768 };\n // Buffer length must always be 0 mod 32768 for index calculations to be correct when modifying head and prev\n // 98304 = 32768 (lookback) + 65536 (common chunk size)\n this.b = new u8(98304);\n if (this.o.dictionary) {\n var dict = this.o.dictionary.subarray(-32768);\n this.b.set(dict, 32768 - dict.length);\n this.s.i = 32768 - dict.length;\n }\n }\n Deflate.prototype.p = function (c, f) {\n this.ondata(dopt(c, this.o, 0, 0, this.s), f);\n };\n /**\n * Pushes a chunk to be deflated\n * @param chunk The chunk to push\n * @param final Whether this is the last chunk\n */\n Deflate.prototype.push = function (chunk, final) {\n if (!this.ondata)\n err(5);\n if (this.s.l)\n err(4);\n var endLen = chunk.length + this.s.z;\n if (endLen > this.b.length) {\n if (endLen > 2 * this.b.length - 32768) {\n var newBuf = new u8(endLen & -32768);\n newBuf.set(this.b.subarray(0, this.s.z));\n this.b = newBuf;\n }\n var split = this.b.length - this.s.z;\n this.b.set(chunk.subarray(0, split), this.s.z);\n this.s.z = this.b.length;\n this.p(this.b, false);\n this.b.set(this.b.subarray(-32768));\n this.b.set(chunk.subarray(split), 32768);\n this.s.z = chunk.length - split + 32768;\n this.s.i = 32766, this.s.w = 32768;\n }\n else {\n this.b.set(chunk, this.s.z);\n this.s.z += chunk.length;\n }\n this.s.l = final & 1;\n if (this.s.z > this.s.w + 8191 || final) {\n this.p(this.b, final || false);\n this.s.w = this.s.i, this.s.i -= 2;\n }\n };\n /**\n * Flushes buffered uncompressed data. Useful to immediately retrieve the\n * deflated output for small inputs.\n */\n Deflate.prototype.flush = function () {\n if (!this.ondata)\n err(5);\n if (this.s.l)\n err(4);\n this.p(this.b, false);\n this.s.w = this.s.i, this.s.i -= 2;\n };\n return Deflate;\n}());\nexport { Deflate };\n/**\n * Asynchronous streaming DEFLATE compression\n */\nvar AsyncDeflate = /*#__PURE__*/ (function () {\n function AsyncDeflate(opts, cb) {\n astrmify([\n bDflt,\n function () { return [astrm, Deflate]; }\n ], this, StrmOpt.call(this, opts, cb), function (ev) {\n var strm = new Deflate(ev.data);\n onmessage = astrm(strm);\n }, 6, 1);\n }\n return AsyncDeflate;\n}());\nexport { AsyncDeflate };\nexport function deflate(data, opts, cb) {\n if (!cb)\n cb = opts, opts = {};\n if (typeof cb != 'function')\n err(7);\n return cbify(data, opts, [\n bDflt,\n ], function (ev) { return pbf(deflateSync(ev.data[0], ev.data[1])); }, 0, cb);\n}\n/**\n * Compresses data with DEFLATE without any wrapper\n * @param data The data to compress\n * @param opts The compression options\n * @returns The deflated version of the data\n */\nexport function deflateSync(data, opts) {\n return dopt(data, opts || {}, 0, 0);\n}\n/**\n * Streaming DEFLATE decompression\n */\nvar Inflate = /*#__PURE__*/ (function () {\n function Inflate(opts, cb) {\n // no StrmOpt here to avoid adding to workerizer\n if (typeof opts == 'function')\n cb = opts, opts = {};\n this.ondata = cb;\n var dict = opts && opts.dictionary && opts.dictionary.subarray(-32768);\n this.s = { i: 0, b: dict ? dict.length : 0 };\n this.o = new u8(32768);\n this.p = new u8(0);\n if (dict)\n this.o.set(dict);\n }\n Inflate.prototype.e = function (c) {\n if (!this.ondata)\n err(5);\n if (this.d)\n err(4);\n if (!this.p.length)\n this.p = c;\n else if (c.length) {\n var n = new u8(this.p.length + c.length);\n n.set(this.p), n.set(c, this.p.length), this.p = n;\n }\n };\n Inflate.prototype.c = function (final) {\n this.s.i = +(this.d = final || false);\n var bts = this.s.b;\n var dt = inflt(this.p, this.s, this.o);\n this.ondata(slc(dt, bts, this.s.b), this.d);\n this.o = slc(dt, this.s.b - 32768), this.s.b = this.o.length;\n this.p = slc(this.p, (this.s.p / 8) | 0), this.s.p &= 7;\n };\n /**\n * Pushes a chunk to be inflated\n * @param chunk The chunk to push\n * @param final Whether this is the final chunk\n */\n Inflate.prototype.push = function (chunk, final) {\n this.e(chunk), this.c(final);\n };\n return Inflate;\n}());\nexport { Inflate };\n/**\n * Asynchronous streaming DEFLATE decompression\n */\nvar AsyncInflate = /*#__PURE__*/ (function () {\n function AsyncInflate(opts, cb) {\n astrmify([\n bInflt,\n function () { return [astrm, Inflate]; }\n ], this, StrmOpt.call(this, opts, cb), function (ev) {\n var strm = new Inflate(ev.data);\n onmessage = astrm(strm);\n }, 7, 0);\n }\n return AsyncInflate;\n}());\nexport { AsyncInflate };\nexport function inflate(data, opts, cb) {\n if (!cb)\n cb = opts, opts = {};\n if (typeof cb != 'function')\n err(7);\n return cbify(data, opts, [\n bInflt\n ], function (ev) { return pbf(inflateSync(ev.data[0], gopt(ev.data[1]))); }, 1, cb);\n}\n/**\n * Expands DEFLATE data with no wrapper\n * @param data The data to decompress\n * @param opts The decompression options\n * @returns The decompressed version of the data\n */\nexport function inflateSync(data, opts) {\n return inflt(data, { i: 2 }, opts && opts.out, opts && opts.dictionary);\n}\n// before you yell at me for not just using extends, my reason is that TS inheritance is hard to workerize.\n/**\n * Streaming GZIP compression\n */\nvar Gzip = /*#__PURE__*/ (function () {\n function Gzip(opts, cb) {\n this.c = crc();\n this.l = 0;\n this.v = 1;\n Deflate.call(this, opts, cb);\n }\n /**\n * Pushes a chunk to be GZIPped\n * @param chunk The chunk to push\n * @param final Whether this is the last chunk\n */\n Gzip.prototype.push = function (chunk, final) {\n this.c.p(chunk);\n this.l += chunk.length;\n Deflate.prototype.push.call(this, chunk, final);\n };\n Gzip.prototype.p = function (c, f) {\n var raw = dopt(c, this.o, this.v && gzhl(this.o), f && 8, this.s);\n if (this.v)\n gzh(raw, this.o), this.v = 0;\n if (f)\n wbytes(raw, raw.length - 8, this.c.d()), wbytes(raw, raw.length - 4, this.l);\n this.ondata(raw, f);\n };\n /**\n * Flushes buffered uncompressed data. Useful to immediately retrieve the\n * GZIPped output for small inputs.\n */\n Gzip.prototype.flush = function () {\n Deflate.prototype.flush.call(this);\n };\n return Gzip;\n}());\nexport { Gzip };\n/**\n * Asynchronous streaming GZIP compression\n */\nvar AsyncGzip = /*#__PURE__*/ (function () {\n function AsyncGzip(opts, cb) {\n astrmify([\n bDflt,\n gze,\n function () { return [astrm, Deflate, Gzip]; }\n ], this, StrmOpt.call(this, opts, cb), function (ev) {\n var strm = new Gzip(ev.data);\n onmessage = astrm(strm);\n }, 8, 1);\n }\n return AsyncGzip;\n}());\nexport { AsyncGzip };\nexport function gzip(data, opts, cb) {\n if (!cb)\n cb = opts, opts = {};\n if (typeof cb != 'function')\n err(7);\n return cbify(data, opts, [\n bDflt,\n gze,\n function () { return [gzipSync]; }\n ], function (ev) { return pbf(gzipSync(ev.data[0], ev.data[1])); }, 2, cb);\n}\n/**\n * Compresses data with GZIP\n * @param data The data to compress\n * @param opts The compression options\n * @returns The gzipped version of the data\n */\nexport function gzipSync(data, opts) {\n if (!opts)\n opts = {};\n var c = crc(), l = data.length;\n c.p(data);\n var d = dopt(data, opts, gzhl(opts), 8), s = d.length;\n return gzh(d, opts), wbytes(d, s - 8, c.d()), wbytes(d, s - 4, l), d;\n}\n/**\n * Streaming single or multi-member GZIP decompression\n */\nvar Gunzip = /*#__PURE__*/ (function () {\n function Gunzip(opts, cb) {\n this.v = 1;\n this.r = 0;\n Inflate.call(this, opts, cb);\n }\n /**\n * Pushes a chunk to be GUNZIPped\n * @param chunk The chunk to push\n * @param final Whether this is the last chunk\n */\n Gunzip.prototype.push = function (chunk, final) {\n Inflate.prototype.e.call(this, chunk);\n this.r += chunk.length;\n if (this.v) {\n var p = this.p.subarray(this.v - 1);\n var s = p.length > 3 ? gzs(p) : 4;\n if (s > p.length) {\n if (!final)\n return;\n }\n else if (this.v > 1 && this.onmember) {\n this.onmember(this.r - p.length);\n }\n this.p = p.subarray(s), this.v = 0;\n }\n // necessary to prevent TS from using the closure value\n // This allows for workerization to function correctly\n Inflate.prototype.c.call(this, final);\n // process concatenated GZIP\n if (this.s.f && !this.s.l && !final) {\n this.v = shft(this.s.p) + 9;\n this.s = { i: 0 };\n this.o = new u8(0);\n this.push(new u8(0), final);\n }\n };\n return Gunzip;\n}());\nexport { Gunzip };\n/**\n * Asynchronous streaming single or multi-member GZIP decompression\n */\nvar AsyncGunzip = /*#__PURE__*/ (function () {\n function AsyncGunzip(opts, cb) {\n var _this = this;\n astrmify([\n bInflt,\n guze,\n function () { return [astrm, Inflate, Gunzip]; }\n ], this, StrmOpt.call(this, opts, cb), function (ev) {\n var strm = new Gunzip(ev.data);\n strm.onmember = function (offset) { return postMessage(offset); };\n onmessage = astrm(strm);\n }, 9, 0, function (offset) { return _this.onmember && _this.onmember(offset); });\n }\n return AsyncGunzip;\n}());\nexport { AsyncGunzip };\nexport function gunzip(data, opts, cb) {\n if (!cb)\n cb = opts, opts = {};\n if (typeof cb != 'function')\n err(7);\n return cbify(data, opts, [\n bInflt,\n guze,\n function () { return [gunzipSync]; }\n ], function (ev) { return pbf(gunzipSync(ev.data[0], ev.data[1])); }, 3, cb);\n}\n/**\n * Expands GZIP data\n * @param data The data to decompress\n * @param opts The decompression options\n * @returns The decompressed version of the data\n */\nexport function gunzipSync(data, opts) {\n var st = gzs(data);\n if (st + 8 > data.length)\n err(6, 'invalid gzip data');\n return inflt(data.subarray(st, -8), { i: 2 }, opts && opts.out || new u8(gzl(data)), opts && opts.dictionary);\n}\n/**\n * Streaming Zlib compression\n */\nvar Zlib = /*#__PURE__*/ (function () {\n function Zlib(opts, cb) {\n this.c = adler();\n this.v = 1;\n Deflate.call(this, opts, cb);\n }\n /**\n * Pushes a chunk to be zlibbed\n * @param chunk The chunk to push\n * @param final Whether this is the last chunk\n */\n Zlib.prototype.push = function (chunk, final) {\n this.c.p(chunk);\n Deflate.prototype.push.call(this, chunk, final);\n };\n Zlib.prototype.p = function (c, f) {\n var raw = dopt(c, this.o, this.v && (this.o.dictionary ? 6 : 2), f && 4, this.s);\n if (this.v)\n zlh(raw, this.o), this.v = 0;\n if (f)\n wbytes(raw, raw.length - 4, this.c.d());\n this.ondata(raw, f);\n };\n /**\n * Flushes buffered uncompressed data. Useful to immediately retrieve the\n * zlibbed output for small inputs.\n */\n Zlib.prototype.flush = function () {\n Deflate.prototype.flush.call(this);\n };\n return Zlib;\n}());\nexport { Zlib };\n/**\n * Asynchronous streaming Zlib compression\n */\nvar AsyncZlib = /*#__PURE__*/ (function () {\n function AsyncZlib(opts, cb) {\n astrmify([\n bDflt,\n zle,\n function () { return [astrm, Deflate, Zlib]; }\n ], this, StrmOpt.call(this, opts, cb), function (ev) {\n var strm = new Zlib(ev.data);\n onmessage = astrm(strm);\n }, 10, 1);\n }\n return AsyncZlib;\n}());\nexport { AsyncZlib };\nexport function zlib(data, opts, cb) {\n if (!cb)\n cb = opts, opts = {};\n if (typeof cb != 'function')\n err(7);\n return cbify(data, opts, [\n bDflt,\n zle,\n function () { return [zlibSync]; }\n ], function (ev) { return pbf(zlibSync(ev.data[0], ev.data[1])); }, 4, cb);\n}\n/**\n * Compress data with Zlib\n * @param data The data to compress\n * @param opts The compression options\n * @returns The zlib-compressed version of the data\n */\nexport function zlibSync(data, opts) {\n if (!opts)\n opts = {};\n var a = adler();\n a.p(data);\n var d = dopt(data, opts, opts.dictionary ? 6 : 2, 4);\n return zlh(d, opts), wbytes(d, d.length - 4, a.d()), d;\n}\n/**\n * Streaming Zlib decompression\n */\nvar Unzlib = /*#__PURE__*/ (function () {\n function Unzlib(opts, cb) {\n Inflate.call(this, opts, cb);\n this.v = opts && opts.dictionary ? 2 : 1;\n }\n /**\n * Pushes a chunk to be unzlibbed\n * @param chunk The chunk to push\n * @param final Whether this is the last chunk\n */\n Unzlib.prototype.push = function (chunk, final) {\n Inflate.prototype.e.call(this, chunk);\n if (this.v) {\n if (this.p.length < 6 && !final)\n return;\n this.p = this.p.subarray(zls(this.p, this.v - 1)), this.v = 0;\n }\n if (final) {\n if (this.p.length < 4)\n err(6, 'invalid zlib data');\n this.p = this.p.subarray(0, -4);\n }\n // necessary to prevent TS from using the closure value\n // This allows for workerization to function correctly\n Inflate.prototype.c.call(this, final);\n };\n return Unzlib;\n}());\nexport { Unzlib };\n/**\n * Asynchronous streaming Zlib decompression\n */\nvar AsyncUnzlib = /*#__PURE__*/ (function () {\n function AsyncUnzlib(opts, cb) {\n astrmify([\n bInflt,\n zule,\n function () { return [astrm, Inflate, Unzlib]; }\n ], this, StrmOpt.call(this, opts, cb), function (ev) {\n var strm = new Unzlib(ev.data);\n onmessage = astrm(strm);\n }, 11, 0);\n }\n return AsyncUnzlib;\n}());\nexport { AsyncUnzlib };\nexport function unzlib(data, opts, cb) {\n if (!cb)\n cb = opts, opts = {};\n if (typeof cb != 'function')\n err(7);\n return cbify(data, opts, [\n bInflt,\n zule,\n function () { return [unzlibSync]; }\n ], function (ev) { return pbf(unzlibSync(ev.data[0], gopt(ev.data[1]))); }, 5, cb);\n}\n/**\n * Expands Zlib data\n * @param data The data to decompress\n * @param opts The decompression options\n * @returns The decompressed version of the data\n */\nexport function unzlibSync(data, opts) {\n return inflt(data.subarray(zls(data, opts && opts.dictionary), -4), { i: 2 }, opts && opts.out, opts && opts.dictionary);\n}\n// Default algorithm for compression (used because having a known output size allows faster decompression)\nexport { gzip as compress, AsyncGzip as AsyncCompress };\nexport { gzipSync as compressSync, Gzip as Compress };\n/**\n * Streaming GZIP, Zlib, or raw DEFLATE decompression\n */\nvar Decompress = /*#__PURE__*/ (function () {\n function Decompress(opts, cb) {\n this.o = StrmOpt.call(this, opts, cb) || {};\n this.G = Gunzip;\n this.I = Inflate;\n this.Z = Unzlib;\n }\n // init substream\n // overriden by AsyncDecompress\n Decompress.prototype.i = function () {\n var _this = this;\n this.s.ondata = function (dat, final) {\n _this.ondata(dat, final);\n };\n };\n /**\n * Pushes a chunk to be decompressed\n * @param chunk The chunk to push\n * @param final Whether this is the last chunk\n */\n Decompress.prototype.push = function (chunk, final) {\n if (!this.ondata)\n err(5);\n if (!this.s) {\n if (this.p && this.p.length) {\n var n = new u8(this.p.length + chunk.length);\n n.set(this.p), n.set(chunk, this.p.length);\n }\n else\n this.p = chunk;\n if (this.p.length > 2) {\n this.s = (this.p[0] == 31 && this.p[1] == 139 && this.p[2] == 8)\n ? new this.G(this.o)\n : ((this.p[0] & 15) != 8 || (this.p[0] >> 4) > 7 || ((this.p[0] << 8 | this.p[1]) % 31))\n ? new this.I(this.o)\n : new this.Z(this.o);\n this.i();\n this.s.push(this.p, final);\n this.p = null;\n }\n }\n else\n this.s.push(chunk, final);\n };\n return Decompress;\n}());\nexport { Decompress };\n/**\n * Asynchronous streaming GZIP, Zlib, or raw DEFLATE decompression\n */\nvar AsyncDecompress = /*#__PURE__*/ (function () {\n function AsyncDecompress(opts, cb) {\n Decompress.call(this, opts, cb);\n this.queuedSize = 0;\n this.G = AsyncGunzip;\n this.I = AsyncInflate;\n this.Z = AsyncUnzlib;\n }\n AsyncDecompress.prototype.i = function () {\n var _this = this;\n this.s.ondata = function (err, dat, final) {\n _this.ondata(err, dat, final);\n };\n this.s.ondrain = function (size) {\n _this.queuedSize -= size;\n if (_this.ondrain)\n _this.ondrain(size);\n };\n };\n /**\n * Pushes a chunk to be decompressed\n * @param chunk The chunk to push\n * @param final Whether this is the last chunk\n */\n AsyncDecompress.prototype.push = function (chunk, final) {\n this.queuedSize += chunk.length;\n Decompress.prototype.push.call(this, chunk, final);\n };\n return AsyncDecompress;\n}());\nexport { AsyncDecompress };\nexport function decompress(data, opts, cb) {\n if (!cb)\n cb = opts, opts = {};\n if (typeof cb != 'function')\n err(7);\n return (data[0] == 31 && data[1] == 139 && data[2] == 8)\n ? gunzip(data, opts, cb)\n : ((data[0] & 15) != 8 || (data[0] >> 4) > 7 || ((data[0] << 8 | data[1]) % 31))\n ? inflate(data, opts, cb)\n : unzlib(data, opts, cb);\n}\n/**\n * Expands compressed GZIP, Zlib, or raw DEFLATE data, automatically detecting the format\n * @param data The data to decompress\n * @param opts The decompression options\n * @returns The decompressed version of the data\n */\nexport function decompressSync(data, opts) {\n return (data[0] == 31 && data[1] == 139 && data[2] == 8)\n ? gunzipSync(data, opts)\n : ((data[0] & 15) != 8 || (data[0] >> 4) > 7 || ((data[0] << 8 | data[1]) % 31))\n ? inflateSync(data, opts)\n : unzlibSync(data, opts);\n}\n// flatten a directory structure\nvar fltn = function (d, p, t, o) {\n for (var k in d) {\n var val = d[k], n = p + k, op = o;\n if (Array.isArray(val))\n op = mrg(o, val[1]), val = val[0];\n if (val instanceof u8)\n t[n] = [val, op];\n else {\n t[n += '/'] = [new u8(0), op];\n fltn(val, n, t, o);\n }\n }\n};\n// text encoder\nvar te = typeof TextEncoder != 'undefined' && /*#__PURE__*/ new TextEncoder();\n// text decoder\nvar td = typeof TextDecoder != 'undefined' && /*#__PURE__*/ new TextDecoder();\n// text decoder stream\nvar tds = 0;\ntry {\n td.decode(et, { stream: true });\n tds = 1;\n}\ncatch (e) { }\n// decode UTF8\nvar dutf8 = function (d) {\n for (var r = '', i = 0;;) {\n var c = d[i++];\n var eb = (c > 127) + (c > 223) + (c > 239);\n if (i + eb > d.length)\n return { s: r, r: slc(d, i - 1) };\n if (!eb)\n r += String.fromCharCode(c);\n else if (eb == 3) {\n c = ((c & 15) << 18 | (d[i++] & 63) << 12 | (d[i++] & 63) << 6 | (d[i++] & 63)) - 65536,\n r += String.fromCharCode(55296 | (c >> 10), 56320 | (c & 1023));\n }\n else if (eb & 1)\n r += String.fromCharCode((c & 31) << 6 | (d[i++] & 63));\n else\n r += String.fromCharCode((c & 15) << 12 | (d[i++] & 63) << 6 | (d[i++] & 63));\n }\n};\n/**\n * Streaming UTF-8 decoding\n */\nvar DecodeUTF8 = /*#__PURE__*/ (function () {\n /**\n * Creates a UTF-8 decoding stream\n * @param cb The callback to call whenever data is decoded\n */\n function DecodeUTF8(cb) {\n this.ondata = cb;\n if (tds)\n this.t = new TextDecoder();\n else\n this.p = et;\n }\n /**\n * Pushes a chunk to be decoded from UTF-8 binary\n * @param chunk The chunk to push\n * @param final Whether this is the last chunk\n */\n DecodeUTF8.prototype.push = function (chunk, final) {\n if (!this.ondata)\n err(5);\n final = !!final;\n if (this.t) {\n this.ondata(this.t.decode(chunk, { stream: true }), final);\n if (final) {\n if (this.t.decode().length)\n err(8);\n this.t = null;\n }\n return;\n }\n if (!this.p)\n err(4);\n var dat = new u8(this.p.length + chunk.length);\n dat.set(this.p);\n dat.set(chunk, this.p.length);\n var _a = dutf8(dat), s = _a.s, r = _a.r;\n if (final) {\n if (r.length)\n err(8);\n this.p = null;\n }\n else\n this.p = r;\n this.ondata(s, final);\n };\n return DecodeUTF8;\n}());\nexport { DecodeUTF8 };\n/**\n * Streaming UTF-8 encoding\n */\nvar EncodeUTF8 = /*#__PURE__*/ (function () {\n /**\n * Creates a UTF-8 decoding stream\n * @param cb The callback to call whenever data is encoded\n */\n function EncodeUTF8(cb) {\n this.ondata = cb;\n }\n /**\n * Pushes a chunk to be encoded to UTF-8\n * @param chunk The string data to push\n * @param final Whether this is the last chunk\n */\n EncodeUTF8.prototype.push = function (chunk, final) {\n if (!this.ondata)\n err(5);\n if (this.d)\n err(4);\n this.ondata(strToU8(chunk), this.d = final || false);\n };\n return EncodeUTF8;\n}());\nexport { EncodeUTF8 };\n/**\n * Converts a string into a Uint8Array for use with compression/decompression methods\n * @param str The string to encode\n * @param latin1 Whether or not to interpret the data as Latin-1. This should\n * not need to be true unless decoding a binary string.\n * @returns The string encoded in UTF-8/Latin-1 binary\n */\nexport function strToU8(str, latin1) {\n if (latin1) {\n var ar_1 = new u8(str.length);\n for (var i = 0; i < str.length; ++i)\n ar_1[i] = str.charCodeAt(i);\n return ar_1;\n }\n if (te)\n return te.encode(str);\n var l = str.length;\n var ar = new u8(str.length + (str.length >> 1));\n var ai = 0;\n var w = function (v) { ar[ai++] = v; };\n for (var i = 0; i < l; ++i) {\n if (ai + 5 > ar.length) {\n var n = new u8(ai + 8 + ((l - i) << 1));\n n.set(ar);\n ar = n;\n }\n var c = str.charCodeAt(i);\n if (c < 128 || latin1)\n w(c);\n else if (c < 2048)\n w(192 | (c >> 6)), w(128 | (c & 63));\n else if (c > 55295 && c < 57344)\n c = 65536 + (c & 1023 << 10) | (str.charCodeAt(++i) & 1023),\n w(240 | (c >> 18)), w(128 | ((c >> 12) & 63)), w(128 | ((c >> 6) & 63)), w(128 | (c & 63));\n else\n w(224 | (c >> 12)), w(128 | ((c >> 6) & 63)), w(128 | (c & 63));\n }\n return slc(ar, 0, ai);\n}\n/**\n * Converts a Uint8Array to a string\n * @param dat The data to decode to string\n * @param latin1 Whether or not to interpret the data as Latin-1. This should\n * not need to be true unless encoding to binary string.\n * @returns The original UTF-8/Latin-1 string\n */\nexport function strFromU8(dat, latin1) {\n if (latin1) {\n var r = '';\n for (var i = 0; i < dat.length; i += 16384)\n r += String.fromCharCode.apply(null, dat.subarray(i, i + 16384));\n return r;\n }\n else if (td) {\n return td.decode(dat);\n }\n else {\n var _a = dutf8(dat), s = _a.s, r = _a.r;\n if (r.length)\n err(8);\n return s;\n }\n}\n;\n// deflate bit flag\nvar dbf = function (l) { return l == 1 ? 3 : l < 6 ? 2 : l == 9 ? 1 : 0; };\n// skip local zip header\nvar slzh = function (d, b) { return b + 30 + b2(d, b + 26) + b2(d, b + 28); };\n// read zip header\nvar zh = function (d, b, z) {\n var fnl = b2(d, b + 28), fn = strFromU8(d.subarray(b + 46, b + 46 + fnl), !(b2(d, b + 8) & 2048)), es = b + 46 + fnl, bs = b4(d, b + 20);\n var _a = z && bs == 4294967295 ? z64e(d, es) : [bs, b4(d, b + 24), b4(d, b + 42)], sc = _a[0], su = _a[1], off = _a[2];\n return [b2(d, b + 10), sc, su, fn, es + b2(d, b + 30) + b2(d, b + 32), off];\n};\n// read zip64 extra field\nvar z64e = function (d, b) {\n for (; b2(d, b) != 1; b += 4 + b2(d, b + 2))\n ;\n return [b8(d, b + 12), b8(d, b + 4), b8(d, b + 20)];\n};\n// extra field length\nvar exfl = function (ex) {\n var le = 0;\n if (ex) {\n for (var k in ex) {\n var l = ex[k].length;\n if (l > 65535)\n err(9);\n le += l + 4;\n }\n }\n return le;\n};\n// write zip header\nvar wzh = function (d, b, f, fn, u, c, ce, co) {\n var fl = fn.length, ex = f.extra, col = co && co.length;\n var exl = exfl(ex);\n wbytes(d, b, ce != null ? 0x2014B50 : 0x4034B50), b += 4;\n if (ce != null)\n d[b++] = 20, d[b++] = f.os;\n d[b] = 20, b += 2; // spec compliance? what's that?\n d[b++] = (f.flag << 1) | (c < 0 && 8), d[b++] = u && 8;\n d[b++] = f.compression & 255, d[b++] = f.compression >> 8;\n var dt = new Date(f.mtime == null ? Date.now() : f.mtime), y = dt.getFullYear() - 1980;\n if (y < 0 || y > 119)\n err(10);\n wbytes(d, b, (y << 25) | ((dt.getMonth() + 1) << 21) | (dt.getDate() << 16) | (dt.getHours() << 11) | (dt.getMinutes() << 5) | (dt.getSeconds() >> 1)), b += 4;\n if (c != -1) {\n wbytes(d, b, f.crc);\n wbytes(d, b + 4, c < 0 ? -c - 2 : c);\n wbytes(d, b + 8, f.size);\n }\n wbytes(d, b + 12, fl);\n wbytes(d, b + 14, exl), b += 16;\n if (ce != null) {\n wbytes(d, b, col);\n wbytes(d, b + 6, f.attrs);\n wbytes(d, b + 10, ce), b += 14;\n }\n d.set(fn, b);\n b += fl;\n if (exl) {\n for (var k in ex) {\n var exf = ex[k], l = exf.length;\n wbytes(d, b, +k);\n wbytes(d, b + 2, l);\n d.set(exf, b + 4), b += 4 + l;\n }\n }\n if (col)\n d.set(co, b), b += col;\n return b;\n};\n// write zip footer (end of central directory)\nvar wzf = function (o, b, c, d, e) {\n wbytes(o, b, 0x6054B50); // skip disk\n wbytes(o, b + 8, c);\n wbytes(o, b + 10, c);\n wbytes(o, b + 12, d);\n wbytes(o, b + 16, e);\n};\n/**\n * A pass-through stream to keep data uncompressed in a ZIP archive.\n */\nvar ZipPassThrough = /*#__PURE__*/ (function () {\n /**\n * Creates a pass-through stream that can be added to ZIP archives\n * @param filename The filename to associate with this data stream\n */\n function ZipPassThrough(filename) {\n this.filename = filename;\n this.c = crc();\n this.size = 0;\n this.compression = 0;\n }\n /**\n * Processes a chunk and pushes to the output stream. You can override this\n * method in a subclass for custom behavior, but by default this passes\n * the data through. You must call this.ondata(err, chunk, final) at some\n * point in this method.\n * @param chunk The chunk to process\n * @param final Whether this is the last chunk\n */\n ZipPassThrough.prototype.process = function (chunk, final) {\n this.ondata(null, chunk, final);\n };\n /**\n * Pushes a chunk to be added. If you are subclassing this with a custom\n * compression algorithm, note that you must push data from the source\n * file only, pre-compression.\n * @param chunk The chunk to push\n * @param final Whether this is the last chunk\n */\n ZipPassThrough.prototype.push = function (chunk, final) {\n if (!this.ondata)\n err(5);\n this.c.p(chunk);\n this.size += chunk.length;\n if (final)\n this.crc = this.c.d();\n this.process(chunk, final || false);\n };\n return ZipPassThrough;\n}());\nexport { ZipPassThrough };\n// I don't extend because TypeScript extension adds 1kB of runtime bloat\n/**\n * Streaming DEFLATE compression for ZIP archives. Prefer using AsyncZipDeflate\n * for better performance\n */\nvar ZipDeflate = /*#__PURE__*/ (function () {\n /**\n * Creates a DEFLATE stream that can be added to ZIP archives\n * @param filename The filename to associate with this data stream\n * @param opts The compression options\n */\n function ZipDeflate(filename, opts) {\n var _this = this;\n if (!opts)\n opts = {};\n ZipPassThrough.call(this, filename);\n this.d = new Deflate(opts, function (dat, final) {\n _this.ondata(null, dat, final);\n });\n this.compression = 8;\n this.flag = dbf(opts.level);\n }\n ZipDeflate.prototype.process = function (chunk, final) {\n try {\n this.d.push(chunk, final);\n }\n catch (e) {\n this.ondata(e, null, final);\n }\n };\n /**\n * Pushes a chunk to be deflated\n * @param chunk The chunk to push\n * @param final Whether this is the last chunk\n */\n ZipDeflate.prototype.push = function (chunk, final) {\n ZipPassThrough.prototype.push.call(this, chunk, final);\n };\n return ZipDeflate;\n}());\nexport { ZipDeflate };\n/**\n * Asynchronous streaming DEFLATE compression for ZIP archives\n */\nvar AsyncZipDeflate = /*#__PURE__*/ (function () {\n /**\n * Creates an asynchronous DEFLATE stream that can be added to ZIP archives\n * @param filename The filename to associate with this data stream\n * @param opts The compression options\n */\n function AsyncZipDeflate(filename, opts) {\n var _this = this;\n if (!opts)\n opts = {};\n ZipPassThrough.call(this, filename);\n this.d = new AsyncDeflate(opts, function (err, dat, final) {\n _this.ondata(err, dat, final);\n });\n this.compression = 8;\n this.flag = dbf(opts.level);\n this.terminate = this.d.terminate;\n }\n AsyncZipDeflate.prototype.process = function (chunk, final) {\n this.d.push(chunk, final);\n };\n /**\n * Pushes a chunk to be deflated\n * @param chunk The chunk to push\n * @param final Whether this is the last chunk\n */\n AsyncZipDeflate.prototype.push = function (chunk, final) {\n ZipPassThrough.prototype.push.call(this, chunk, final);\n };\n return AsyncZipDeflate;\n}());\nexport { AsyncZipDeflate };\n// TODO: Better tree shaking\n/**\n * A zippable archive to which files can incrementally be added\n */\nvar Zip = /*#__PURE__*/ (function () {\n /**\n * Creates an empty ZIP archive to which files can be added\n * @param cb The callback to call whenever data for the generated ZIP archive\n * is available\n */\n function Zip(cb) {\n this.ondata = cb;\n this.u = [];\n this.d = 1;\n }\n /**\n * Adds a file to the ZIP archive\n * @param file The file stream to add\n */\n Zip.prototype.add = function (file) {\n var _this = this;\n if (!this.ondata)\n err(5);\n // finishing or finished\n if (this.d & 2)\n this.ondata(err(4 + (this.d & 1) * 8, 0, 1), null, false);\n else {\n var f = strToU8(file.filename), fl_1 = f.length;\n var com = file.comment, o = com && strToU8(com);\n var u = fl_1 != file.filename.length || (o && (com.length != o.length));\n var hl_1 = fl_1 + exfl(file.extra) + 30;\n if (fl_1 > 65535)\n this.ondata(err(11, 0, 1), null, false);\n var header = new u8(hl_1);\n wzh(header, 0, file, f, u, -1);\n var chks_1 = [header];\n var pAll_1 = function () {\n for (var _i = 0, chks_2 = chks_1; _i < chks_2.length; _i++) {\n var chk = chks_2[_i];\n _this.ondata(null, chk, false);\n }\n chks_1 = [];\n };\n var tr_1 = this.d;\n this.d = 0;\n var ind_1 = this.u.length;\n var uf_1 = mrg(file, {\n f: f,\n u: u,\n o: o,\n t: function () {\n if (file.terminate)\n file.terminate();\n },\n r: function () {\n pAll_1();\n if (tr_1) {\n var nxt = _this.u[ind_1 + 1];\n if (nxt)\n nxt.r();\n else\n _this.d = 1;\n }\n tr_1 = 1;\n }\n });\n var cl_1 = 0;\n file.ondata = function (err, dat, final) {\n if (err) {\n _this.ondata(err, dat, final);\n _this.terminate();\n }\n else {\n cl_1 += dat.length;\n chks_1.push(dat);\n if (final) {\n var dd = new u8(16);\n wbytes(dd, 0, 0x8074B50);\n wbytes(dd, 4, file.crc);\n wbytes(dd, 8, cl_1);\n wbytes(dd, 12, file.size);\n chks_1.push(dd);\n uf_1.c = cl_1, uf_1.b = hl_1 + cl_1 + 16, uf_1.crc = file.crc, uf_1.size = file.size;\n if (tr_1)\n uf_1.r();\n tr_1 = 1;\n }\n else if (tr_1)\n pAll_1();\n }\n };\n this.u.push(uf_1);\n }\n };\n /**\n * Ends the process of adding files and prepares to emit the final chunks.\n * This *must* be called after adding all desired files for the resulting\n * ZIP file to work properly.\n */\n Zip.prototype.end = function () {\n var _this = this;\n if (this.d & 2) {\n this.ondata(err(4 + (this.d & 1) * 8, 0, 1), null, true);\n return;\n }\n if (this.d)\n this.e();\n else\n this.u.push({\n r: function () {\n if (!(_this.d & 1))\n return;\n _this.u.splice(-1, 1);\n _this.e();\n },\n t: function () { }\n });\n this.d = 3;\n };\n Zip.prototype.e = function () {\n var bt = 0, l = 0, tl = 0;\n for (var _i = 0, _a = this.u; _i < _a.length; _i++) {\n var f = _a[_i];\n tl += 46 + f.f.length + exfl(f.extra) + (f.o ? f.o.length : 0);\n }\n var out = new u8(tl + 22);\n for (var _b = 0, _c = this.u; _b < _c.length; _b++) {\n var f = _c[_b];\n wzh(out, bt, f, f.f, f.u, -f.c - 2, l, f.o);\n bt += 46 + f.f.length + exfl(f.extra) + (f.o ? f.o.length : 0), l += f.b;\n }\n wzf(out, bt, this.u.length, tl, l);\n this.ondata(null, out, true);\n this.d = 2;\n };\n /**\n * A method to terminate any internal workers used by the stream. Subsequent\n * calls to add() will fail.\n */\n Zip.prototype.terminate = function () {\n for (var _i = 0, _a = this.u; _i < _a.length; _i++) {\n var f = _a[_i];\n f.t();\n }\n this.d = 2;\n };\n return Zip;\n}());\nexport { Zip };\nexport function zip(data, opts, cb) {\n if (!cb)\n cb = opts, opts = {};\n if (typeof cb != 'function')\n err(7);\n var r = {};\n fltn(data, '', r, opts);\n var k = Object.keys(r);\n var lft = k.length, o = 0, tot = 0;\n var slft = lft, files = new Array(lft);\n var term = [];\n var tAll = function () {\n for (var i = 0; i < term.length; ++i)\n term[i]();\n };\n var cbd = function (a, b) {\n mt(function () { cb(a, b); });\n };\n mt(function () { cbd = cb; });\n var cbf = function () {\n var out = new u8(tot + 22), oe = o, cdl = tot - o;\n tot = 0;\n for (var i = 0; i < slft; ++i) {\n var f = files[i];\n try {\n var l = f.c.length;\n wzh(out, tot, f, f.f, f.u, l);\n var badd = 30 + f.f.length + exfl(f.extra);\n var loc = tot + badd;\n out.set(f.c, loc);\n wzh(out, o, f, f.f, f.u, l, tot, f.m), o += 16 + badd + (f.m ? f.m.length : 0), tot = loc + l;\n }\n catch (e) {\n return cbd(e, null);\n }\n }\n wzf(out, o, files.length, cdl, oe);\n cbd(null, out);\n };\n if (!lft)\n cbf();\n var _loop_1 = function (i) {\n var fn = k[i];\n var _a = r[fn], file = _a[0], p = _a[1];\n var c = crc(), size = file.length;\n c.p(file);\n var f = strToU8(fn), s = f.length;\n var com = p.comment, m = com && strToU8(com), ms = m && m.length;\n var exl = exfl(p.extra);\n var compression = p.level == 0 ? 0 : 8;\n var cbl = function (e, d) {\n if (e) {\n tAll();\n cbd(e, null);\n }\n else {\n var l = d.length;\n files[i] = mrg(p, {\n size: size,\n crc: c.d(),\n c: d,\n f: f,\n m: m,\n u: s != fn.length || (m && (com.length != ms)),\n compression: compression\n });\n o += 30 + s + exl + l;\n tot += 76 + 2 * (s + exl) + (ms || 0) + l;\n if (!--lft)\n cbf();\n }\n };\n if (s > 65535)\n cbl(err(11, 0, 1), null);\n if (!compression)\n cbl(null, file);\n else if (size < 160000) {\n try {\n cbl(null, deflateSync(file, p));\n }\n catch (e) {\n cbl(e, null);\n }\n }\n else\n term.push(deflate(file, p, cbl));\n };\n // Cannot use lft because it can decrease\n for (var i = 0; i < slft; ++i) {\n _loop_1(i);\n }\n return tAll;\n}\n/**\n * Synchronously creates a ZIP file. Prefer using `zip` for better performance\n * with more than one file.\n * @param data The directory structure for the ZIP archive\n * @param opts The main options, merged with per-file options\n * @returns The generated ZIP archive\n */\nexport function zipSync(data, opts) {\n if (!opts)\n opts = {};\n var r = {};\n var files = [];\n fltn(data, '', r, opts);\n var o = 0;\n var tot = 0;\n for (var fn in r) {\n var _a = r[fn], file = _a[0], p = _a[1];\n var compression = p.level == 0 ? 0 : 8;\n var f = strToU8(fn), s = f.length;\n var com = p.comment, m = com && strToU8(com), ms = m && m.length;\n var exl = exfl(p.extra);\n if (s > 65535)\n err(11);\n var d = compression ? deflateSync(file, p) : file, l = d.length;\n var c = crc();\n c.p(file);\n files.push(mrg(p, {\n size: file.length,\n crc: c.d(),\n c: d,\n f: f,\n m: m,\n u: s != fn.length || (m && (com.length != ms)),\n o: o,\n compression: compression\n }));\n o += 30 + s + exl + l;\n tot += 76 + 2 * (s + exl) + (ms || 0) + l;\n }\n var out = new u8(tot + 22), oe = o, cdl = tot - o;\n for (var i = 0; i < files.length; ++i) {\n var f = files[i];\n wzh(out, f.o, f, f.f, f.u, f.c.length);\n var badd = 30 + f.f.length + exfl(f.extra);\n out.set(f.c, f.o + badd);\n wzh(out, o, f, f.f, f.u, f.c.length, f.o, f.m), o += 16 + badd + (f.m ? f.m.length : 0);\n }\n wzf(out, o, files.length, cdl, oe);\n return out;\n}\n/**\n * Streaming pass-through decompression for ZIP archives\n */\nvar UnzipPassThrough = /*#__PURE__*/ (function () {\n function UnzipPassThrough() {\n }\n UnzipPassThrough.prototype.push = function (data, final) {\n this.ondata(null, data, final);\n };\n UnzipPassThrough.compression = 0;\n return UnzipPassThrough;\n}());\nexport { UnzipPassThrough };\n/**\n * Streaming DEFLATE decompression for ZIP archives. Prefer AsyncZipInflate for\n * better performance.\n */\nvar UnzipInflate = /*#__PURE__*/ (function () {\n /**\n * Creates a DEFLATE decompression that can be used in ZIP archives\n */\n function UnzipInflate() {\n var _this = this;\n this.i = new Inflate(function (dat, final) {\n _this.ondata(null, dat, final);\n });\n }\n UnzipInflate.prototype.push = function (data, final) {\n try {\n this.i.push(data, final);\n }\n catch (e) {\n this.ondata(e, null, final);\n }\n };\n UnzipInflate.compression = 8;\n return UnzipInflate;\n}());\nexport { UnzipInflate };\n/**\n * Asynchronous streaming DEFLATE decompression for ZIP archives\n */\nvar AsyncUnzipInflate = /*#__PURE__*/ (function () {\n /**\n * Creates a DEFLATE decompression that can be used in ZIP archives\n */\n function AsyncUnzipInflate(_, sz) {\n var _this = this;\n if (sz < 320000) {\n this.i = new Inflate(function (dat, final) {\n _this.ondata(null, dat, final);\n });\n }\n else {\n this.i = new AsyncInflate(function (err, dat, final) {\n _this.ondata(err, dat, final);\n });\n this.terminate = this.i.terminate;\n }\n }\n AsyncUnzipInflate.prototype.push = function (data, final) {\n if (this.i.terminate)\n data = slc(data, 0);\n this.i.push(data, final);\n };\n AsyncUnzipInflate.compression = 8;\n return AsyncUnzipInflate;\n}());\nexport { AsyncUnzipInflate };\n/**\n * A ZIP archive decompression stream that emits files as they are discovered\n */\nvar Unzip = /*#__PURE__*/ (function () {\n /**\n * Creates a ZIP decompression stream\n * @param cb The callback to call whenever a file in the ZIP archive is found\n */\n function Unzip(cb) {\n this.onfile = cb;\n this.k = [];\n this.o = {\n 0: UnzipPassThrough\n };\n this.p = et;\n }\n /**\n * Pushes a chunk to be unzipped\n * @param chunk The chunk to push\n * @param final Whether this is the last chunk\n */\n Unzip.prototype.push = function (chunk, final) {\n var _this = this;\n if (!this.onfile)\n err(5);\n if (!this.p)\n err(4);\n if (this.c > 0) {\n var len = Math.min(this.c, chunk.length);\n var toAdd = chunk.subarray(0, len);\n this.c -= len;\n if (this.d)\n this.d.push(toAdd, !this.c);\n else\n this.k[0].push(toAdd);\n chunk = chunk.subarray(len);\n if (chunk.length)\n return this.push(chunk, final);\n }\n else {\n var f = 0, i = 0, is = void 0, buf = void 0;\n if (!this.p.length)\n buf = chunk;\n else if (!chunk.length)\n buf = this.p;\n else {\n buf = new u8(this.p.length + chunk.length);\n buf.set(this.p), buf.set(chunk, this.p.length);\n }\n var l = buf.length, oc = this.c, add = oc && this.d;\n var _loop_2 = function () {\n var _a;\n var sig = b4(buf, i);\n if (sig == 0x4034B50) {\n f = 1, is = i;\n this_1.d = null;\n this_1.c = 0;\n var bf = b2(buf, i + 6), cmp_1 = b2(buf, i + 8), u = bf & 2048, dd = bf & 8, fnl = b2(buf, i + 26), es = b2(buf, i + 28);\n if (l > i + 30 + fnl + es) {\n var chks_3 = [];\n this_1.k.unshift(chks_3);\n f = 2;\n var sc_1 = b4(buf, i + 18), su_1 = b4(buf, i + 22);\n var fn_1 = strFromU8(buf.subarray(i + 30, i += 30 + fnl), !u);\n if (sc_1 == 4294967295) {\n _a = dd ? [-2] : z64e(buf, i), sc_1 = _a[0], su_1 = _a[1];\n }\n else if (dd)\n sc_1 = -1;\n i += es;\n this_1.c = sc_1;\n var d_1;\n var file_1 = {\n name: fn_1,\n compression: cmp_1,\n start: function () {\n if (!file_1.ondata)\n err(5);\n if (!sc_1)\n file_1.ondata(null, et, true);\n else {\n var ctr = _this.o[cmp_1];\n if (!ctr)\n file_1.ondata(err(14, 'unknown compression type ' + cmp_1, 1), null, false);\n d_1 = sc_1 < 0 ? new ctr(fn_1) : new ctr(fn_1, sc_1, su_1);\n d_1.ondata = function (err, dat, final) { file_1.ondata(err, dat, final); };\n for (var _i = 0, chks_4 = chks_3; _i < chks_4.length; _i++) {\n var dat = chks_4[_i];\n d_1.push(dat, false);\n }\n if (_this.k[0] == chks_3 && _this.c)\n _this.d = d_1;\n else\n d_1.push(et, true);\n }\n },\n terminate: function () {\n if (d_1 && d_1.terminate)\n d_1.terminate();\n }\n };\n if (sc_1 >= 0)\n file_1.size = sc_1, file_1.originalSize = su_1;\n this_1.onfile(file_1);\n }\n return \"break\";\n }\n else if (oc) {\n if (sig == 0x8074B50) {\n is = i += 12 + (oc == -2 && 8), f = 3, this_1.c = 0;\n return \"break\";\n }\n else if (sig == 0x2014B50) {\n is = i -= 4, f = 3, this_1.c = 0;\n return \"break\";\n }\n }\n };\n var this_1 = this;\n for (; i < l - 4; ++i) {\n var state_1 = _loop_2();\n if (state_1 === \"break\")\n break;\n }\n this.p = et;\n if (oc < 0) {\n var dat = f ? buf.subarray(0, is - 12 - (oc == -2 && 8) - (b4(buf, is - 16) == 0x8074B50 && 4)) : buf.subarray(0, i);\n if (add)\n add.push(dat, !!f);\n else\n this.k[+(f == 2)].push(dat);\n }\n if (f & 2)\n return this.push(buf.subarray(i), final);\n this.p = buf.subarray(i);\n }\n if (final) {\n if (this.c)\n err(13);\n this.p = null;\n }\n };\n /**\n * Registers a decoder with the stream, allowing for files compressed with\n * the compression type provided to be expanded correctly\n * @param decoder The decoder constructor\n */\n Unzip.prototype.register = function (decoder) {\n this.o[decoder.compression] = decoder;\n };\n return Unzip;\n}());\nexport { Unzip };\nvar mt = typeof queueMicrotask == 'function' ? queueMicrotask : typeof setTimeout == 'function' ? setTimeout : function (fn) { fn(); };\nexport function unzip(data, opts, cb) {\n if (!cb)\n cb = opts, opts = {};\n if (typeof cb != 'function')\n err(7);\n var term = [];\n var tAll = function () {\n for (var i = 0; i < term.length; ++i)\n term[i]();\n };\n var files = {};\n var cbd = function (a, b) {\n mt(function () { cb(a, b); });\n };\n mt(function () { cbd = cb; });\n var e = data.length - 22;\n for (; b4(data, e) != 0x6054B50; --e) {\n if (!e || data.length - e > 65558) {\n cbd(err(13, 0, 1), null);\n return tAll;\n }\n }\n ;\n var lft = b2(data, e + 8);\n if (lft) {\n var c = lft;\n var o = b4(data, e + 16);\n var z = o == 4294967295 || c == 65535;\n if (z) {\n var ze = b4(data, e - 12);\n z = b4(data, ze) == 0x6064B50;\n if (z) {\n c = lft = b4(data, ze + 32);\n o = b4(data, ze + 48);\n }\n }\n var fltr = opts && opts.filter;\n var _loop_3 = function (i) {\n var _a = zh(data, o, z), c_1 = _a[0], sc = _a[1], su = _a[2], fn = _a[3], no = _a[4], off = _a[5], b = slzh(data, off);\n o = no;\n var cbl = function (e, d) {\n if (e) {\n tAll();\n cbd(e, null);\n }\n else {\n if (d)\n files[fn] = d;\n if (!--lft)\n cbd(null, files);\n }\n };\n if (!fltr || fltr({\n name: fn,\n size: sc,\n originalSize: su,\n compression: c_1\n })) {\n if (!c_1)\n cbl(null, slc(data, b, b + sc));\n else if (c_1 == 8) {\n var infl = data.subarray(b, b + sc);\n // Synchronously decompress under 512KB, or barely-compressed data\n if (su < 524288 || sc > 0.8 * su) {\n try {\n cbl(null, inflateSync(infl, { out: new u8(su) }));\n }\n catch (e) {\n cbl(e, null);\n }\n }\n else\n term.push(inflate(infl, { size: su }, cbl));\n }\n else\n cbl(err(14, 'unknown compression type ' + c_1, 1), null);\n }\n else\n cbl(null, null);\n };\n for (var i = 0; i < c; ++i) {\n _loop_3(i);\n }\n }\n else\n cbd(null, {});\n return tAll;\n}\n/**\n * Synchronously decompresses a ZIP archive. Prefer using `unzip` for better\n * performance with more than one file.\n * @param data The raw compressed ZIP file\n * @param opts The ZIP extraction options\n * @returns The decompressed files\n */\nexport function unzipSync(data, opts) {\n var files = {};\n var e = data.length - 22;\n for (; b4(data, e) != 0x6054B50; --e) {\n if (!e || data.length - e > 65558)\n err(13);\n }\n ;\n var c = b2(data, e + 8);\n if (!c)\n return {};\n var o = b4(data, e + 16);\n var z = o == 4294967295 || c == 65535;\n if (z) {\n var ze = b4(data, e - 12);\n z = b4(data, ze) == 0x6064B50;\n if (z) {\n c = b4(data, ze + 32);\n o = b4(data, ze + 48);\n }\n }\n var fltr = opts && opts.filter;\n for (var i = 0; i < c; ++i) {\n var _a = zh(data, o, z), c_2 = _a[0], sc = _a[1], su = _a[2], fn = _a[3], no = _a[4], off = _a[5], b = slzh(data, off);\n o = no;\n if (!fltr || fltr({\n name: fn,\n size: sc,\n originalSize: su,\n compression: c_2\n })) {\n if (!c_2)\n files[fn] = slc(data, b, b + sc);\n else if (c_2 == 8)\n files[fn] = inflateSync(data.subarray(b, b + sc), { out: new u8(su) });\n else\n err(14, 'unknown compression type ' + c_2);\n }\n }\n return files;\n}\n","import { Zip, ZipPassThrough } from 'fflate';\n\n/**\n * Streaming ZIP writer that assembles files into a ZIP archive on the fly.\n * Uses fflate's store mode (no compression) for maximum speed and minimal memory usage.\n * Works in both Node.js and browser environments.\n */\nexport class StreamingZipWriter {\n private zip: InstanceType<typeof Zip>;\n private currentFile: InstanceType<typeof ZipPassThrough> | null = null;\n private onData: (chunk: Uint8Array) => void | Promise<void>;\n private finalized = false;\n private pendingWrites: Promise<void> = Promise.resolve();\n\n constructor(onData: (chunk: Uint8Array) => void | Promise<void>) {\n this.onData = onData;\n this.zip = new Zip((err, data, _final) => {\n if (err) throw err;\n // Queue data delivery to handle async consumers\n this.pendingWrites = this.pendingWrites.then(() => this.onData(data));\n });\n }\n\n /**\n * Begin a new file entry in the ZIP.\n * Must call endFile() before starting another file.\n * @param name - Filename within the ZIP archive.\n */\n startFile(name: string): void {\n if (this.currentFile) {\n throw new Error('Must call endFile() before starting a new file.');\n }\n if (this.finalized) {\n throw new Error('ZIP has already been finalized.');\n }\n const entry = new ZipPassThrough(name);\n this.zip.add(entry);\n this.currentFile = entry;\n }\n\n /**\n * Write a chunk of data to the current file entry.\n * @param data - The data chunk to write.\n */\n writeChunk(data: Uint8Array): void {\n if (!this.currentFile) {\n throw new Error('No file started. Call startFile() first.');\n }\n this.currentFile.push(data, false);\n }\n\n /**\n * End the current file entry.\n */\n endFile(): void {\n if (!this.currentFile) {\n throw new Error('No file to end.');\n }\n this.currentFile.push(new Uint8Array(0), true);\n this.currentFile = null;\n }\n\n /**\n * Finalize the ZIP archive. Must be called after all files are written.\n * Waits for all pending async writes to complete before resolving.\n */\n async finalize(): Promise<void> {\n if (this.currentFile) {\n throw new Error('Cannot finalize with an open file. Call endFile() first.');\n }\n if (this.finalized) return;\n this.finalized = true;\n this.zip.end();\n // Wait for all queued onData callbacks to complete\n await this.pendingWrites;\n }\n}\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","/**\r\n * P2P Protocol Definitions\r\n *\r\n * This file defines the application-level protocol for P2P file transfers.\r\n * Protocol version 2 introduces:\r\n * - Explicit handshake with version negotiation\r\n * - Chunk-level acknowledgments for flow control\r\n * - Multiple end-ack retries for reliability\r\n * - Resume capability support\r\n *\r\n * Protocol version 3 introduces:\r\n * - Multi-file transfers via file_list, file_end, file_end_ack messages\r\n * - Sequential file-by-file transfer within a single session\r\n */\r\n\r\n// Protocol version for forward compatibility\r\nexport const P2P_PROTOCOL_VERSION = 3;\r\n\r\n/**\r\n * All possible P2P message types.\r\n */\r\nexport type P2PMessageType =\r\n | 'hello' // Initial handshake with protocol version\r\n | 'file_list' // v3: List of files in multi-file transfer\r\n | 'meta' // File metadata (name, size, mime)\r\n | 'ready' // Receiver is ready to receive\r\n | 'chunk' // Data chunk with sequence number\r\n | 'chunk_ack' // Chunk acknowledgment (for flow control)\r\n | 'file_end' // v3: Current file fully sent\r\n | 'file_end_ack' // v3: Current file receipt confirmed\r\n | 'end' // All chunks/files sent\r\n | 'end_ack' // Transfer verified complete\r\n | 'ping' // Heartbeat\r\n | 'pong' // Heartbeat response\r\n | 'error' // Error occurred\r\n | 'cancelled' // User cancelled\r\n | 'resume' // Request to resume from offset\r\n | 'resume_ack'; // Resume position confirmed\r\n\r\n/**\r\n * Base interface for all P2P messages.\r\n */\r\nexport interface P2PMessageBase {\r\n t: P2PMessageType;\r\n}\r\n\r\n/**\r\n * Initial handshake message exchanged between sender and receiver.\r\n */\r\nexport interface P2PHelloMessage extends P2PMessageBase {\r\n t: 'hello';\r\n protocolVersion: number;\r\n sessionId: string;\r\n}\r\n\r\n/**\r\n * v3: File list sent by sender after handshake for multi-file transfers.\r\n */\r\nexport interface P2PFileListMessage extends P2PMessageBase {\r\n t: 'file_list';\r\n fileCount: number;\r\n files: Array<{ name: string; size: number; mime: string }>;\r\n totalSize: number;\r\n}\r\n\r\n/**\r\n * File metadata sent by sender after handshake (single file)\r\n * or before each file's chunks in multi-file mode.\r\n */\r\nexport interface P2PMetaMessage extends P2PMessageBase {\r\n t: 'meta';\r\n sessionId: string;\r\n name: string;\r\n size: number;\r\n mime: string;\r\n /** v3: File index within the file list (0-based). Absent for single-file transfers. */\r\n fileIndex?: number;\r\n}\r\n\r\n/**\r\n * Receiver signals readiness to receive data.\r\n */\r\nexport interface P2PReadyMessage extends P2PMessageBase {\r\n t: 'ready';\r\n}\r\n\r\n/**\r\n * Chunk header sent before binary data.\r\n * The actual binary data follows immediately after this message.\r\n */\r\nexport interface P2PChunkMessage extends P2PMessageBase {\r\n t: 'chunk';\r\n seq: number; // Sequence number for ordering/ack\r\n offset: number; // Byte offset in file\r\n size: number; // Size of this chunk\r\n total: number; // Total file size\r\n}\r\n\r\n/**\r\n * Acknowledgment for a received chunk.\r\n */\r\nexport interface P2PChunkAckMessage extends P2PMessageBase {\r\n t: 'chunk_ack';\r\n seq: number; // Acknowledged sequence number\r\n received: number; // Total bytes received so far\r\n}\r\n\r\n/**\r\n * v3: Sender signals all chunks for the current file have been sent.\r\n */\r\nexport interface P2PFileEndMessage extends P2PMessageBase {\r\n t: 'file_end';\r\n fileIndex: number;\r\n attempt?: number;\r\n}\r\n\r\n/**\r\n * v3: Receiver confirms current file receipt.\r\n */\r\nexport interface P2PFileEndAckMessage extends P2PMessageBase {\r\n t: 'file_end_ack';\r\n fileIndex: number;\r\n received: number;\r\n size: number;\r\n}\r\n\r\n/**\r\n * Sender signals all chunks have been sent.\r\n */\r\nexport interface P2PEndMessage extends P2PMessageBase {\r\n t: 'end';\r\n attempt?: number; // Retry attempt number\r\n}\r\n\r\n/**\r\n * Receiver confirms transfer completion.\r\n */\r\nexport interface P2PEndAckMessage extends P2PMessageBase {\r\n t: 'end_ack';\r\n received: number;\r\n total: number;\r\n}\r\n\r\n/**\r\n * Heartbeat ping.\r\n */\r\nexport interface P2PPingMessage extends P2PMessageBase {\r\n t: 'ping';\r\n timestamp: number;\r\n}\r\n\r\n/**\r\n * Heartbeat pong response.\r\n */\r\nexport interface P2PPongMessage extends P2PMessageBase {\r\n t: 'pong';\r\n timestamp: number;\r\n}\r\n\r\n/**\r\n * Error message sent when something goes wrong.\r\n */\r\nexport interface P2PErrorMessage extends P2PMessageBase {\r\n t: 'error';\r\n message: string;\r\n code?: string;\r\n}\r\n\r\n/**\r\n * Cancellation message sent when user cancels transfer.\r\n */\r\nexport interface P2PCancelledMessage extends P2PMessageBase {\r\n t: 'cancelled';\r\n reason?: string;\r\n}\r\n\r\n/**\r\n * Resume request sent by receiver to continue interrupted transfer.\r\n */\r\nexport interface P2PResumeMessage extends P2PMessageBase {\r\n t: 'resume';\r\n sessionId: string;\r\n receivedBytes: number;\r\n}\r\n\r\n/**\r\n * Resume acknowledgment from sender.\r\n */\r\nexport interface P2PResumeAckMessage extends P2PMessageBase {\r\n t: 'resume_ack';\r\n resumeFromOffset: number;\r\n accepted: boolean;\r\n}\r\n\r\n/**\r\n * Union type of all possible P2P messages.\r\n */\r\nexport type P2PMessage =\r\n | P2PHelloMessage\r\n | P2PFileListMessage\r\n | P2PMetaMessage\r\n | P2PReadyMessage\r\n | P2PChunkMessage\r\n | P2PChunkAckMessage\r\n | P2PFileEndMessage\r\n | P2PFileEndAckMessage\r\n | P2PEndMessage\r\n | P2PEndAckMessage\r\n | P2PPingMessage\r\n | P2PPongMessage\r\n | P2PErrorMessage\r\n | P2PCancelledMessage\r\n | P2PResumeMessage\r\n | P2PResumeAckMessage;\r\n\r\n/**\r\n * Type guard to check if a value is a valid P2P message.\r\n */\r\nexport function isP2PMessage(value: unknown): value is P2PMessage {\r\n if (!value || typeof value !== 'object') return false;\r\n const msg = value as Record<string, unknown>;\r\n return typeof msg.t === 'string' && [\r\n 'hello', 'file_list', 'meta', 'ready', 'chunk', 'chunk_ack',\r\n 'file_end', 'file_end_ack', 'end', 'end_ack', 'ping', 'pong',\r\n 'error', 'cancelled', 'resume', 'resume_ack'\r\n ].includes(msg.t);\r\n}\r\n\r\n/**\r\n * Check if protocol versions are compatible.\r\n */\r\nexport function isProtocolCompatible(\r\n senderVersion: number,\r\n receiverVersion: number\r\n): boolean {\r\n return senderVersion === receiverVersion;\r\n}\r\n\r\n/**\r\n * Default chunk size for P2P transfers (64KB).\r\n * Smaller than standard upload to reduce latency and improve flow control.\r\n */\r\nexport const P2P_CHUNK_SIZE = 64 * 1024;\r\n\r\n/**\r\n * Default maximum unacknowledged chunks before sender pauses.\r\n * This creates backpressure when receiver is slow.\r\n */\r\nexport const P2P_MAX_UNACKED_CHUNKS = 32;\r\n\r\n/**\r\n * Default timeout for waiting on end acknowledgment (ms).\r\n */\r\nexport const P2P_END_ACK_TIMEOUT_MS = 15000;\r\n\r\n/**\r\n * Number of times to retry sending end message.\r\n */\r\nexport const P2P_END_ACK_RETRIES = 3;\r\n\r\n/**\r\n * Delay between multiple end_ack sends from receiver (ms).\r\n */\r\nexport const P2P_END_ACK_RETRY_DELAY_MS = 100;\r\n\r\n/**\r\n * Grace period after connection close before declaring failure (ms).\r\n * Allows for brief reconnection attempts.\r\n */\r\nexport const P2P_CLOSE_GRACE_PERIOD_MS = 2000;\r\n","import { DropgateValidationError, DropgateNetworkError } from '../errors.js';\r\nimport { sleep } from '../utils/network.js';\r\nimport type {\r\n P2PSendOptions,\r\n P2PSendSession,\r\n P2PSendState,\r\n DataConnection,\r\n P2PConnectionHealthEvent,\r\n} from './types.js';\r\nimport { generateP2PCode } from './utils.js';\r\nimport { buildPeerOptions, createPeerWithRetries, resolvePeerConfig } from './helpers.js';\r\nimport type { FileSource } from '../types.js';\r\nimport {\r\n P2P_PROTOCOL_VERSION,\r\n P2P_CHUNK_SIZE,\r\n P2P_MAX_UNACKED_CHUNKS,\r\n P2P_END_ACK_TIMEOUT_MS,\r\n P2P_END_ACK_RETRIES,\r\n P2P_CLOSE_GRACE_PERIOD_MS,\r\n isP2PMessage,\r\n type P2PChunkAckMessage,\r\n type P2PEndAckMessage,\r\n type P2PFileEndAckMessage,\r\n} from './protocol.js';\r\n\r\n/**\r\n * Generate a unique session ID for transfer tracking.\r\n */\r\nfunction generateSessionId(): string {\r\n return crypto.randomUUID();\r\n}\r\n\r\n/**\r\n * Allowed state transitions to prevent invalid state changes.\r\n * This enforces a strict state machine where transitions only happen\r\n * in the expected order, preventing race conditions.\r\n */\r\nconst ALLOWED_TRANSITIONS: Record<P2PSendState, P2PSendState[]> = {\r\n initializing: ['listening', 'closed'],\r\n listening: ['handshaking', 'closed', 'cancelled'],\r\n handshaking: ['negotiating', 'closed', 'cancelled'],\r\n negotiating: ['transferring', 'closed', 'cancelled'],\r\n transferring: ['finishing', 'closed', 'cancelled'],\r\n finishing: ['awaiting_ack', 'closed', 'cancelled'],\r\n awaiting_ack: ['completed', 'closed', 'cancelled'],\r\n completed: ['closed'],\r\n cancelled: ['closed'],\r\n closed: [],\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 * Protocol v2 features:\r\n * - Explicit version handshake\r\n * - Chunk-level acknowledgments for flow control\r\n * - Multiple end-ack retries for reliability\r\n * - Stream-through design for unlimited file sizes\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 = P2P_CHUNK_SIZE,\r\n endAckTimeoutMs = P2P_END_ACK_TIMEOUT_MS,\r\n bufferHighWaterMark = 8 * 1024 * 1024,\r\n bufferLowWaterMark = 2 * 1024 * 1024,\r\n heartbeatIntervalMs = 5000,\r\n chunkAcknowledgments = true,\r\n maxUnackedChunks = P2P_MAX_UNACKED_CHUNKS,\r\n onCode,\r\n onStatus,\r\n onProgress,\r\n onComplete,\r\n onError,\r\n onDisconnect,\r\n onCancel,\r\n onConnectionHealth,\r\n } = opts;\r\n\r\n // Normalize to files array\r\n const files: FileSource[] = Array.isArray(file) ? file : [file];\r\n const isMultiFile = files.length > 1;\r\n const totalSize = files.reduce((sum, f) => sum + f.size, 0);\r\n\r\n // Validate required options\r\n if (!files.length) {\r\n throw new DropgateValidationError('At least one file is required.');\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 let healthCheckTimer: ReturnType<typeof setInterval> | null = null;\r\n let lastActivityTime = Date.now();\r\n\r\n // Chunk acknowledgment tracking\r\n const unackedChunks = new Map<number, { offset: number; size: number; sentAt: number }>();\r\n let nextSeq = 0;\r\n let ackResolvers: Array<() => void> = [];\r\n\r\n /**\r\n * Attempt a state transition. Returns true if transition was valid.\r\n * Logs a warning for invalid transitions but doesn't throw.\r\n */\r\n const transitionTo = (newState: P2PSendState): boolean => {\r\n if (!ALLOWED_TRANSITIONS[state].includes(newState)) {\r\n console.warn(`[P2P Send] Invalid state transition: ${state} -> ${newState}`);\r\n return false;\r\n }\r\n state = newState;\r\n return true;\r\n };\r\n\r\n const reportProgress = (data: { received: number; total: number }): void => {\r\n if (isStopped()) return;\r\n const safeTotal =\r\n Number.isFinite(data.total) && data.total > 0 ? data.total : totalSize;\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 or cancellation\r\n const safeError = (err: Error): void => {\r\n if (state === 'closed' || state === 'completed' || state === 'cancelled') return;\r\n transitionTo('closed');\r\n onError?.(err);\r\n cleanup();\r\n };\r\n\r\n // Safe complete handler - only fires from awaiting_ack state\r\n const safeComplete = (): void => {\r\n if (state !== 'awaiting_ack' && state !== 'finishing') return;\r\n transitionTo('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 // Clear health check timer\r\n if (healthCheckTimer) {\r\n clearInterval(healthCheckTimer);\r\n healthCheckTimer = null;\r\n }\r\n\r\n // Clear any pending ack resolvers\r\n ackResolvers.forEach((resolve) => resolve());\r\n ackResolvers = [];\r\n unackedChunks.clear();\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' || state === 'cancelled') return;\r\n\r\n // If already completed, just cleanup without callbacks\r\n if (state === 'completed') {\r\n cleanup();\r\n return;\r\n }\r\n\r\n const wasActive = state === 'transferring' || state === 'finishing' || state === 'awaiting_ack';\r\n transitionTo('cancelled');\r\n\r\n // Notify peer before cleanup\r\n try {\r\n // @ts-expect-error - open property may exist on PeerJS connections\r\n if (activeConn && activeConn.open) {\r\n activeConn.send({ t: 'cancelled', message: 'Sender cancelled the transfer.' });\r\n }\r\n } catch {\r\n // Best effort\r\n }\r\n\r\n if (wasActive && onCancel) {\r\n onCancel({ cancelledBy: 'sender' });\r\n }\r\n\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' || state === 'cancelled';\r\n\r\n // Connection health monitoring\r\n const startHealthMonitoring = (conn: DataConnection): void => {\r\n if (!onConnectionHealth) return;\r\n\r\n healthCheckTimer = setInterval(() => {\r\n if (isStopped()) return;\r\n const dc = conn._dc;\r\n if (!dc) return;\r\n\r\n // Note: iceConnectionState is on RTCPeerConnection, not RTCDataChannel\r\n // We can only report bufferedAmount and readyState from the data channel\r\n const health: P2PConnectionHealthEvent = {\r\n iceConnectionState: (dc.readyState === 'open' ? 'connected' : 'disconnected') as P2PConnectionHealthEvent['iceConnectionState'],\r\n bufferedAmount: dc.bufferedAmount,\r\n lastActivityMs: Date.now() - lastActivityTime,\r\n };\r\n\r\n onConnectionHealth(health);\r\n }, 2000);\r\n };\r\n\r\n // Handle chunk acknowledgment\r\n const handleChunkAck = (msg: P2PChunkAckMessage): void => {\r\n lastActivityTime = Date.now();\r\n unackedChunks.delete(msg.seq);\r\n reportProgress({ received: msg.received, total: totalSize });\r\n\r\n // Resolve any pending waitForAck promises\r\n const resolver = ackResolvers.shift();\r\n if (resolver) resolver();\r\n };\r\n\r\n // Wait for chunk acknowledgment when too many unacked\r\n const waitForAck = (): Promise<void> => {\r\n return new Promise((resolve) => {\r\n ackResolvers.push(resolve);\r\n });\r\n };\r\n\r\n // Send chunk with sequence tracking\r\n const sendChunk = async (conn: DataConnection, data: ArrayBuffer, offset: number, fileTotal?: number): Promise<void> => {\r\n // Wait if too many unacknowledged chunks (flow control)\r\n if (chunkAcknowledgments) {\r\n while (unackedChunks.size >= maxUnackedChunks) {\r\n await Promise.race([\r\n waitForAck(),\r\n sleep(1000), // Timeout to prevent deadlock\r\n ]);\r\n if (isStopped()) return;\r\n }\r\n }\r\n\r\n const seq = nextSeq++;\r\n if (chunkAcknowledgments) {\r\n unackedChunks.set(seq, { offset, size: data.byteLength, sentAt: Date.now() });\r\n }\r\n\r\n // Send chunk header then binary data\r\n conn.send({ t: 'chunk', seq, offset, size: data.byteLength, total: fileTotal ?? totalSize });\r\n conn.send(data);\r\n sentBytes += data.byteLength;\r\n\r\n // Buffer-based flow control using data channel thresholds\r\n const dc = conn._dc;\r\n if (dc && bufferHighWaterMark > 0) {\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 if (isStopped()) return;\r\n }\r\n }\r\n };\r\n\r\n // Robust end-ack with retries\r\n const waitForEndAck = async (\r\n conn: DataConnection,\r\n ackPromise: Promise<P2PEndAckMessage>\r\n ): Promise<P2PEndAckMessage> => {\r\n const baseTimeout = endAckTimeoutMs;\r\n\r\n for (let attempt = 0; attempt < P2P_END_ACK_RETRIES; attempt++) {\r\n conn.send({ t: 'end', attempt });\r\n\r\n const timeout = baseTimeout * Math.pow(1.5, attempt);\r\n const result = await Promise.race([\r\n ackPromise,\r\n sleep(timeout).then(() => null as P2PEndAckMessage | null),\r\n ]);\r\n\r\n if (result && result.t === 'end_ack') {\r\n return result;\r\n }\r\n\r\n // Check if connection is still alive\r\n if (isStopped()) {\r\n throw new DropgateNetworkError('Connection closed during completion.');\r\n }\r\n }\r\n\r\n throw new DropgateNetworkError('Receiver did not confirm completion after retries.');\r\n };\r\n\r\n peer.on('connection', (conn: DataConnection) => {\r\n if (isStopped()) 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 nextSeq = 0;\r\n unackedChunks.clear();\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 transitionTo('handshaking');\r\n if (!isStopped()) onStatus?.({ phase: 'connected', message: 'Receiver connected.' });\r\n lastActivityTime = Date.now();\r\n\r\n let helloResolve: ((version: number) => void) | null = null;\r\n let readyResolve: (() => void) | null = null;\r\n let endAckResolve: ((msg: P2PEndAckMessage) => void) | null = null;\r\n let fileEndAckResolve: ((msg: P2PFileEndAckMessage) => void) | null = null;\r\n\r\n const helloPromise = new Promise<number>((resolve) => {\r\n helloResolve = resolve;\r\n });\r\n\r\n const readyPromise = new Promise<void>((resolve) => {\r\n readyResolve = resolve;\r\n });\r\n\r\n const endAckPromise = new Promise<P2PEndAckMessage>((resolve) => {\r\n endAckResolve = resolve;\r\n });\r\n\r\n conn.on('data', (data: unknown) => {\r\n lastActivityTime = Date.now();\r\n\r\n // Handle binary data (we don't expect binary from receiver)\r\n if (data instanceof ArrayBuffer || ArrayBuffer.isView(data)) {\r\n return;\r\n }\r\n\r\n if (!isP2PMessage(data)) return;\r\n\r\n const msg = data;\r\n\r\n switch (msg.t) {\r\n case 'hello':\r\n helloResolve?.(msg.protocolVersion);\r\n break;\r\n\r\n case 'ready':\r\n if (!isStopped()) onStatus?.({ phase: 'transferring', message: 'Receiver accepted. Starting transfer...' });\r\n readyResolve?.();\r\n break;\r\n\r\n case 'chunk_ack':\r\n handleChunkAck(msg as P2PChunkAckMessage);\r\n break;\r\n\r\n case 'file_end_ack':\r\n fileEndAckResolve?.(msg as P2PFileEndAckMessage);\r\n break;\r\n\r\n case 'end_ack':\r\n endAckResolve?.(msg as P2PEndAckMessage);\r\n break;\r\n\r\n case 'pong':\r\n // Heartbeat response received, connection is alive\r\n break;\r\n\r\n case 'error':\r\n safeError(new DropgateNetworkError(msg.message || 'Receiver reported an error.'));\r\n break;\r\n\r\n case 'cancelled':\r\n if (state === 'cancelled' || state === 'closed' || state === 'completed') return;\r\n transitionTo('cancelled');\r\n onCancel?.({ cancelledBy: 'receiver', message: msg.reason });\r\n cleanup();\r\n break;\r\n }\r\n });\r\n\r\n conn.on('open', async () => {\r\n try {\r\n if (isStopped()) return;\r\n\r\n // Start health monitoring\r\n startHealthMonitoring(conn);\r\n\r\n // Protocol v2: Send hello first\r\n conn.send({\r\n t: 'hello',\r\n protocolVersion: P2P_PROTOCOL_VERSION,\r\n sessionId,\r\n });\r\n\r\n // Wait for receiver's hello (with timeout)\r\n const receiverVersion = await Promise.race([\r\n helloPromise,\r\n sleep(10000).then(() => null as number | null),\r\n ]);\r\n\r\n if (isStopped()) return;\r\n\r\n if (receiverVersion === null) {\r\n throw new DropgateNetworkError('Receiver did not respond to handshake.');\r\n } else if (receiverVersion !== P2P_PROTOCOL_VERSION) {\r\n throw new DropgateNetworkError(\r\n `Protocol version mismatch: sender v${P2P_PROTOCOL_VERSION}, receiver v${receiverVersion}`\r\n );\r\n }\r\n\r\n transitionTo('negotiating');\r\n if (!isStopped()) onStatus?.({ phase: 'waiting', message: 'Connected. Waiting for receiver to accept...' });\r\n\r\n // v3: Send file_list for multi-file transfers\r\n if (isMultiFile) {\r\n conn.send({\r\n t: 'file_list',\r\n fileCount: files.length,\r\n files: files.map(f => ({ name: f.name, size: f.size, mime: f.type || 'application/octet-stream' })),\r\n totalSize,\r\n });\r\n }\r\n\r\n // Send metadata for the first file (or the only file)\r\n conn.send({\r\n t: 'meta',\r\n sessionId,\r\n name: files[0].name,\r\n size: files[0].size,\r\n mime: files[0].type || 'application/octet-stream',\r\n ...(isMultiFile ? { fileIndex: 0 } : {}),\r\n });\r\n\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' || state === 'awaiting_ack') {\r\n try {\r\n conn.send({ t: 'ping', timestamp: Date.now() });\r\n } catch {\r\n // Ignore ping errors\r\n }\r\n }\r\n }, heartbeatIntervalMs);\r\n }\r\n\r\n transitionTo('transferring');\r\n\r\n let overallSentBytes = 0;\r\n\r\n // Send file(s) in chunks\r\n for (let fi = 0; fi < files.length; fi++) {\r\n const currentFile = files[fi];\r\n\r\n // For multi-file (after first file), send meta for subsequent files\r\n if (isMultiFile && fi > 0) {\r\n conn.send({\r\n t: 'meta',\r\n sessionId,\r\n name: currentFile.name,\r\n size: currentFile.size,\r\n mime: currentFile.type || 'application/octet-stream',\r\n fileIndex: fi,\r\n });\r\n }\r\n\r\n // Send this file's chunks\r\n for (let offset = 0; offset < currentFile.size; offset += chunkSize) {\r\n if (isStopped()) return;\r\n\r\n const slice = currentFile.slice(offset, offset + chunkSize);\r\n const buf = await slice.arrayBuffer();\r\n if (isStopped()) return;\r\n\r\n await sendChunk(conn, buf, offset, currentFile.size);\r\n overallSentBytes += buf.byteLength;\r\n reportProgress({ received: overallSentBytes, total: totalSize });\r\n }\r\n\r\n if (isStopped()) return;\r\n\r\n // For multi-file: send file_end and wait for file_end_ack\r\n if (isMultiFile) {\r\n const fileEndAckPromise = new Promise<P2PFileEndAckMessage>((resolve) => {\r\n fileEndAckResolve = resolve;\r\n });\r\n\r\n conn.send({ t: 'file_end', fileIndex: fi });\r\n\r\n const feAck = await Promise.race([\r\n fileEndAckPromise,\r\n sleep(endAckTimeoutMs).then(() => null as P2PFileEndAckMessage | null),\r\n ]);\r\n\r\n if (isStopped()) return;\r\n\r\n if (!feAck) {\r\n throw new DropgateNetworkError(`Receiver did not confirm receipt of file ${fi + 1}/${files.length}.`);\r\n }\r\n }\r\n }\r\n\r\n if (isStopped()) return;\r\n\r\n transitionTo('finishing');\r\n transitionTo('awaiting_ack');\r\n\r\n // Wait for end acknowledgment with retries\r\n const ackResult = await waitForEndAck(conn, endAckPromise);\r\n\r\n if (isStopped()) return;\r\n\r\n const ackTotal = Number(ackResult.total) || totalSize;\r\n const ackReceived = Number(ackResult.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' || state === 'cancelled') {\r\n // Clean shutdown or already cancelled, ensure full cleanup\r\n cleanup();\r\n return;\r\n }\r\n\r\n // Special handling for awaiting_ack state - give grace period\r\n if (state === 'awaiting_ack') {\r\n // Connection closed while waiting for end_ack\r\n // Give a grace period for the ack to have been processed\r\n setTimeout(() => {\r\n if (state === 'awaiting_ack') {\r\n // Still waiting, treat as failure\r\n safeError(new DropgateNetworkError('Connection closed while awaiting confirmation.'));\r\n }\r\n }, P2P_CLOSE_GRACE_PERIOD_MS);\r\n return;\r\n }\r\n\r\n if (state === 'transferring' || state === 'finishing') {\r\n // Connection closed during active transfer — the receiver either cancelled\r\n // or disconnected. Treat as a receiver-initiated cancellation so the UI\r\n // can reset cleanly instead of showing a raw error.\r\n transitionTo('cancelled');\r\n onCancel?.({ cancelledBy: 'receiver' });\r\n cleanup();\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 nextSeq = 0;\r\n unackedChunks.clear();\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 { sleep } from '../utils/network.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\nimport {\r\n P2P_PROTOCOL_VERSION,\r\n P2P_END_ACK_RETRY_DELAY_MS,\r\n isP2PMessage,\r\n type P2PChunkMessage,\r\n type P2PFileListMessage,\r\n} from './protocol.js';\r\n\r\n/**\r\n * Allowed state transitions to prevent invalid state changes.\r\n */\r\nconst ALLOWED_TRANSITIONS: Record<P2PReceiveState, P2PReceiveState[]> = {\r\n initializing: ['connecting', 'closed'],\r\n connecting: ['handshaking', 'closed', 'cancelled'],\r\n handshaking: ['negotiating', 'closed', 'cancelled'],\r\n negotiating: ['transferring', 'closed', 'cancelled'],\r\n transferring: ['completed', 'closed', 'cancelled'],\r\n completed: ['closed'],\r\n cancelled: ['closed'],\r\n closed: [],\r\n};\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 * Protocol v2 features:\r\n * - Explicit version handshake\r\n * - Chunk-level acknowledgments for flow control\r\n * - Multiple end-ack sends for reliability\r\n * - Stream-through design for unlimited file sizes\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 onFileStart,\r\n onFileEnd,\r\n onComplete,\r\n onError,\r\n onDisconnect,\r\n onCancel,\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 writeQueue = Promise.resolve();\r\n let watchdogTimer: ReturnType<typeof setTimeout> | null = null;\r\n let activeConn: DataConnection | null = null;\r\n\r\n let pendingChunk: P2PChunkMessage | null = null;\r\n\r\n // Multi-file tracking (v3)\r\n let fileList: P2PFileListMessage | null = null;\r\n let currentFileReceived = 0;\r\n let totalReceivedAllFiles = 0;\r\n\r\n /**\r\n * Attempt a state transition. Returns true if transition was valid.\r\n */\r\n const transitionTo = (newState: P2PReceiveState): boolean => {\r\n if (!ALLOWED_TRANSITIONS[state].includes(newState)) {\r\n console.warn(`[P2P Receive] Invalid state transition: ${state} -> ${newState}`);\r\n return false;\r\n }\r\n state = newState;\r\n return true;\r\n };\r\n\r\n // Helper to check if session is stopped\r\n const isStopped = (): boolean => state === 'closed' || state === 'cancelled';\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 or cancellation\r\n const safeError = (err: Error): void => {\r\n if (state === 'closed' || state === 'completed' || state === 'cancelled') return;\r\n transitionTo('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 transitionTo('completed');\r\n onComplete?.(completeData);\r\n // Don't immediately cleanup - let acks be sent first\r\n // The sender will close the connection after receiving ack\r\n // Our close handler will call cleanup when that happens\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' || state === 'cancelled') return;\r\n\r\n // If already completed, just cleanup without callbacks\r\n if (state === 'completed') {\r\n cleanup();\r\n return;\r\n }\r\n\r\n const wasActive = state === 'transferring';\r\n transitionTo('cancelled');\r\n\r\n // Notify peer before cleanup\r\n try {\r\n // @ts-expect-error - open property may exist on PeerJS connections\r\n if (activeConn && activeConn.open) {\r\n activeConn.send({ t: 'cancelled', reason: 'Receiver cancelled the transfer.' });\r\n }\r\n } catch {\r\n // Best effort\r\n }\r\n\r\n if (wasActive && onCancel) {\r\n onCancel({ cancelledBy: 'receiver' });\r\n }\r\n\r\n cleanup();\r\n };\r\n\r\n // Send chunk acknowledgment\r\n const sendChunkAck = (conn: DataConnection, seq: number): void => {\r\n try {\r\n conn.send({ t: 'chunk_ack', seq, received });\r\n } catch {\r\n // Ignore send errors\r\n }\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 transitionTo('connecting');\r\n const conn = peer.connect(normalizedCode, { reliable: true });\r\n activeConn = conn;\r\n\r\n conn.on('open', () => {\r\n transitionTo('handshaking');\r\n onStatus?.({ phase: 'connected', message: 'Connected.' });\r\n\r\n // Send our hello immediately\r\n conn.send({\r\n t: 'hello',\r\n protocolVersion: P2P_PROTOCOL_VERSION,\r\n sessionId: '',\r\n });\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 binary data - this is file content\r\n if (data instanceof ArrayBuffer || ArrayBuffer.isView(data) ||\r\n (typeof Blob !== 'undefined' && data instanceof Blob)) {\r\n\r\n // Process the binary chunk\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 // Queue the write operation\r\n const chunkSeq = pendingChunk?.seq ?? -1;\r\n pendingChunk = null;\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 (stream-through, no buffering)\r\n if (onData) {\r\n await onData(buf);\r\n }\r\n\r\n received += buf.byteLength;\r\n currentFileReceived += buf.byteLength;\r\n const progressReceived = fileList ? (totalReceivedAllFiles + currentFileReceived) : received;\r\n const progressTotal = fileList ? fileList.totalSize : total;\r\n const percent = progressTotal ? Math.min(100, (progressReceived / progressTotal) * 100) : 0;\r\n if (!isStopped()) onProgress?.({ processedBytes: progressReceived, totalBytes: progressTotal, percent });\r\n\r\n // Send chunk acknowledgment\r\n if (chunkSeq >= 0) {\r\n sendChunkAck(conn, chunkSeq);\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\r\n return;\r\n }\r\n\r\n // Handle control messages\r\n if (!isP2PMessage(data)) return;\r\n\r\n const msg = data;\r\n\r\n switch (msg.t) {\r\n case 'hello':\r\n currentSessionId = msg.sessionId || null;\r\n transitionTo('negotiating');\r\n onStatus?.({ phase: 'waiting', message: 'Waiting for file details...' });\r\n break;\r\n\r\n case 'file_list':\r\n // v3: Store file list for multi-file transfer\r\n fileList = msg as P2PFileListMessage;\r\n total = fileList.totalSize;\r\n break;\r\n\r\n case 'meta': {\r\n // For multi-file: meta comes for each file (first triggers ready, subsequent auto-transition)\r\n if (state !== 'negotiating' && !(state === 'transferring' && fileList)) {\r\n return;\r\n }\r\n\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 const fileSize = Number(msg.size) || 0;\r\n const fi = msg.fileIndex;\r\n\r\n // For multi-file subsequent files, reset per-file tracking\r\n if (fileList && typeof fi === 'number' && fi > 0) {\r\n currentFileReceived = 0;\r\n // Don't reset writeQueue or received - they accumulate\r\n onFileStart?.({ fileIndex: fi, name, size: fileSize });\r\n break; // Already transferring, no need for ready signal\r\n }\r\n\r\n // First file (or single file transfer)\r\n received = 0;\r\n currentFileReceived = 0;\r\n totalReceivedAllFiles = 0;\r\n if (!fileList) {\r\n total = fileSize;\r\n }\r\n writeQueue = Promise.resolve();\r\n\r\n // Function to send ready signal\r\n const sendReady = (): void => {\r\n transitionTo('transferring');\r\n // Start watchdog once we're ready to receive data\r\n resetWatchdog();\r\n // Notify consumer about first file start (for multi-file ZIP assembly)\r\n if (fileList) {\r\n onFileStart?.({ fileIndex: 0, name, size: fileSize });\r\n }\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 // Build metadata event\r\n const metaEvt: Parameters<NonNullable<typeof onMeta>>[0] = { name, total };\r\n if (fileList) {\r\n metaEvt.fileCount = fileList.fileCount;\r\n metaEvt.files = fileList.files.map(f => ({ name: f.name, size: f.size }));\r\n metaEvt.totalSize = fileList.totalSize;\r\n }\r\n\r\n if (autoReady) {\r\n if (!isStopped()) {\r\n onMeta?.(metaEvt);\r\n onProgress?.({ processedBytes: received, totalBytes: total, percent: 0 });\r\n }\r\n sendReady();\r\n } else {\r\n // Pass sendReady function to callback so consumer can trigger transfer start\r\n metaEvt.sendReady = sendReady;\r\n if (!isStopped()) {\r\n onMeta?.(metaEvt);\r\n onProgress?.({ processedBytes: received, totalBytes: total, percent: 0 });\r\n }\r\n }\r\n break;\r\n }\r\n\r\n case 'chunk':\r\n pendingChunk = msg as P2PChunkMessage;\r\n break;\r\n\r\n case 'ping':\r\n // Respond to heartbeat - keeps watchdog alive and confirms we're active\r\n try {\r\n conn.send({ t: 'pong', timestamp: Date.now() });\r\n } catch {\r\n // Ignore send errors\r\n }\r\n break;\r\n\r\n case 'file_end': {\r\n // v3: Current file complete, ack it\r\n clearWatchdog();\r\n await writeQueue;\r\n\r\n const feIdx = msg.fileIndex;\r\n onFileEnd?.({ fileIndex: feIdx, receivedBytes: currentFileReceived });\r\n\r\n try {\r\n conn.send({ t: 'file_end_ack', fileIndex: feIdx, received: currentFileReceived, size: currentFileReceived });\r\n } catch {\r\n // Ignore send errors\r\n }\r\n\r\n totalReceivedAllFiles += currentFileReceived;\r\n currentFileReceived = 0;\r\n\r\n // Restart watchdog for next file\r\n resetWatchdog();\r\n break;\r\n }\r\n\r\n case 'end':\r\n clearWatchdog();\r\n await writeQueue;\r\n\r\n // For multi-file, use totalReceivedAllFiles + any remaining\r\n const finalReceived = fileList ? (totalReceivedAllFiles + currentFileReceived) : received;\r\n const finalTotal = fileList ? fileList.totalSize : total;\r\n\r\n if (finalTotal && finalReceived < finalTotal) {\r\n const err = new DropgateNetworkError(\r\n 'Transfer ended before all data 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 // Send end_ack immediately so sender can complete\r\n try {\r\n conn.send({ t: 'end_ack', received: finalReceived, total: finalTotal });\r\n } catch {\r\n // Ignore send errors\r\n }\r\n\r\n // Mark as completed - protects against close handler race\r\n safeComplete({ received: finalReceived, total: finalTotal });\r\n\r\n // Send additional acks for reliability (fire-and-forget, best effort)\r\n (async () => {\r\n for (let i = 0; i < 2; i++) {\r\n await sleep(P2P_END_ACK_RETRY_DELAY_MS);\r\n try {\r\n conn.send({ t: 'end_ack', received: finalReceived, total: finalTotal });\r\n } catch {\r\n break; // Connection closed\r\n }\r\n }\r\n })().catch(() => { });\r\n break;\r\n\r\n case 'error':\r\n throw new DropgateNetworkError(msg.message || 'Sender reported an error.');\r\n\r\n case 'cancelled':\r\n if (state === 'cancelled' || state === 'closed' || state === 'completed') return;\r\n transitionTo('cancelled');\r\n onCancel?.({ cancelledBy: 'sender', message: msg.reason });\r\n cleanup();\r\n break;\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' || state === 'cancelled') {\r\n // Clean shutdown or already cancelled, ensure full cleanup\r\n cleanup();\r\n return;\r\n }\r\n\r\n // Sender disconnected or cancelled before transfer completed\r\n if (state === 'transferring') {\r\n // Connection closed during active transfer — the sender either cancelled\r\n // or disconnected. Treat as a sender-initiated cancellation so the UI\r\n // can show a clean message instead of a raw error.\r\n transitionTo('cancelled');\r\n onCancel?.({ cancelledBy: 'sender' });\r\n cleanup();\r\n } else if (state === 'negotiating') {\r\n // We had metadata but transfer hadn't started\r\n transitionTo('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","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 ServerInfo,\r\n ServerTarget,\r\n CompatibilityResult,\r\n ShareTargetResult,\r\n UploadResult,\r\n UploadSession,\r\n UploadProgressEvent,\r\n DropgateClientOptions,\r\n UploadFilesOptions,\r\n GetServerInfoOptions,\r\n ConnectOptions,\r\n ValidateUploadOptions,\r\n FileSource,\r\n Base64Adapter,\r\n DownloadFilesOptions,\r\n DownloadResult,\r\n DownloadProgressEvent,\r\n FileMetadata,\r\n BundleMetadata,\r\n} from '../types.js';\r\nimport type {\r\n P2PSendFileOptions,\r\n P2PReceiveFileOptions,\r\n P2PSendSession,\r\n P2PReceiveSession,\r\n} from '../p2p/types.js';\r\nimport { getDefaultCrypto, getDefaultFetch, getDefaultBase64 } from '../adapters/defaults.js';\r\nimport { makeAbortSignal, fetchJson, sleep, buildBaseUrl, parseServerUrl } 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\nimport { startP2PSend } from '../p2p/send.js';\r\nimport { startP2PReceive } from '../p2p/receive.js';\r\nimport { resolvePeerConfig } from '../p2p/helpers.js';\r\nimport { StreamingZipWriter } from '../zip/stream-zip.js';\r\n\r\n/**\r\n * Resolve a server option (URL string or ServerTarget) to a base URL string.\r\n */\r\nfunction resolveServerToBaseUrl(server: string | ServerTarget): string {\r\n if (typeof server === 'string') {\r\n return buildBaseUrl(parseServerUrl(server));\r\n }\r\n return buildBaseUrl(server);\r\n}\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 { server, 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 = resolveServerToBaseUrl(server);\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 operations.\r\n * Handles server communication, encryption, chunked uploads, downloads, and P2P transfers.\r\n *\r\n * Server connection is configured once in the constructor — all methods use\r\n * the stored server URL and cached server info automatically.\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\r\n /** Resolved base URL (e.g. 'https://dropgate.link'). May change during HTTP fallback. */\r\n baseUrl: string;\r\n\r\n /** Whether to automatically retry with HTTP when HTTPS fails. */\r\n private _fallbackToHttp: boolean;\r\n /** Cached compatibility result (null until first connect()). */\r\n private _compat: (CompatibilityResult & { serverInfo: ServerInfo; baseUrl: string }) | null = null;\r\n /** In-flight connect promise to deduplicate concurrent calls. */\r\n private _connectPromise: Promise<CompatibilityResult & { serverInfo: ServerInfo; baseUrl: string }> | null = null;\r\n\r\n /**\r\n * Create a new DropgateClient instance.\r\n * @param opts - Client configuration options including server URL.\r\n * @throws {DropgateValidationError} If clientVersion or server 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 if (!opts.server) {\r\n throw new DropgateValidationError(\r\n 'DropgateClient requires server (URL string or ServerTarget object).'\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._fallbackToHttp = Boolean(opts.fallbackToHttp);\r\n\r\n // Resolve server to baseUrl\r\n this.baseUrl = resolveServerToBaseUrl(opts.server);\r\n }\r\n\r\n /**\r\n * Get the server target (host, port, secure) derived from the current baseUrl.\r\n * Useful for passing to standalone functions that still need a ServerTarget.\r\n */\r\n get serverTarget(): ServerTarget {\r\n const url = new URL(this.baseUrl);\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 * Connect to the server: fetch server info and check version compatibility.\r\n * Results are cached — subsequent calls return instantly without network requests.\r\n * Concurrent calls are deduplicated.\r\n *\r\n * @param opts - Optional timeout and abort signal.\r\n * @returns Compatibility result with 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 connect(\r\n opts?: ConnectOptions\r\n ): Promise<CompatibilityResult & { serverInfo: ServerInfo; baseUrl: string }> {\r\n // Return cached result if available\r\n if (this._compat) return this._compat;\r\n\r\n // Deduplicate concurrent connect calls\r\n if (!this._connectPromise) {\r\n this._connectPromise = this._fetchAndCheckCompat(opts).finally(() => {\r\n this._connectPromise = null;\r\n });\r\n }\r\n\r\n return this._connectPromise;\r\n }\r\n\r\n private async _fetchAndCheckCompat(\r\n opts?: ConnectOptions\r\n ): Promise<CompatibilityResult & { serverInfo: ServerInfo; baseUrl: string }> {\r\n const { timeoutMs = 5000, signal } = opts ?? {};\r\n\r\n let baseUrl = this.baseUrl;\r\n let serverInfo: ServerInfo;\r\n\r\n try {\r\n const result = await getServerInfo({\r\n server: baseUrl,\r\n timeoutMs,\r\n signal,\r\n fetchFn: this.fetchFn,\r\n });\r\n baseUrl = result.baseUrl;\r\n serverInfo = result.serverInfo;\r\n } catch (err) {\r\n // HTTP fallback: if HTTPS failed and fallback is enabled, retry with HTTP\r\n if (this._fallbackToHttp && this.baseUrl.startsWith('https://')) {\r\n const httpBaseUrl = this.baseUrl.replace('https://', 'http://');\r\n try {\r\n const result = await getServerInfo({\r\n server: httpBaseUrl,\r\n timeoutMs,\r\n signal,\r\n fetchFn: this.fetchFn,\r\n });\r\n // HTTP worked — update stored baseUrl\r\n this.baseUrl = httpBaseUrl;\r\n baseUrl = result.baseUrl;\r\n serverInfo = result.serverInfo;\r\n } catch {\r\n // Both failed — throw the original HTTPS error\r\n if (err instanceof DropgateError) throw err;\r\n throw new DropgateNetworkError('Could not connect to the server.', { cause: err });\r\n }\r\n } else {\r\n if (err instanceof DropgateError) throw err;\r\n throw new DropgateNetworkError('Could not connect to the server.', { cause: err });\r\n }\r\n }\r\n\r\n const compat = this._checkVersionCompat(serverInfo!);\r\n this._compat = { ...compat, serverInfo: serverInfo!, baseUrl };\r\n return this._compat;\r\n }\r\n\r\n /**\r\n * Pure version compatibility check (no network calls).\r\n */\r\n private _checkVersionCompat(serverInfo: ServerInfo): CompatibilityResult {\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 };\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 };\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 };\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 - Optional timeout and abort signal.\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?: ConnectOptions\r\n ): Promise<ShareTargetResult> {\r\n const { timeoutMs = 5000, signal } = opts ?? {};\r\n\r\n // Check server compatibility (uses cache)\r\n const compat = await this.connect(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 * Fetch metadata for a single file from the server.\r\n * @param fileId - The file ID to fetch metadata for.\r\n * @param opts - Optional connection options (timeout, signal).\r\n * @returns File metadata including size, filename, and encryption status.\r\n * @throws {DropgateNetworkError} If the server cannot be reached.\r\n * @throws {DropgateProtocolError} If the file is not found or server returns an error.\r\n */\r\n async getFileMetadata(\r\n fileId: string,\r\n opts?: ConnectOptions\r\n ): Promise<FileMetadata> {\r\n if (!fileId || typeof fileId !== 'string') {\r\n throw new DropgateValidationError('File ID is required.');\r\n }\r\n\r\n const { timeoutMs = 5000, signal } = opts ?? {};\r\n\r\n const url = `${this.baseUrl}/api/file/${encodeURIComponent(fileId)}/meta`;\r\n const { res, json } = await fetchJson(this.fetchFn, url, {\r\n method: 'GET',\r\n timeoutMs,\r\n signal,\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) || `Failed to fetch file metadata (status ${res.status}).`;\r\n throw new DropgateProtocolError(msg, { details: json });\r\n }\r\n\r\n return json as FileMetadata;\r\n }\r\n\r\n /**\r\n * Fetch metadata for a bundle from the server and derive computed fields.\r\n * For sealed bundles, decrypts the manifest to extract file list.\r\n * Automatically derives totalSizeBytes and fileCount from the files array.\r\n * @param bundleId - The bundle ID to fetch metadata for.\r\n * @param keyB64 - Base64-encoded decryption key (required for encrypted bundles).\r\n * @param opts - Optional connection options (timeout, signal).\r\n * @returns Complete bundle metadata with all files and computed fields.\r\n * @throws {DropgateNetworkError} If the server cannot be reached.\r\n * @throws {DropgateProtocolError} If the bundle is not found or server returns an error.\r\n * @throws {DropgateValidationError} If decryption key is missing for encrypted bundle.\r\n */\r\n async getBundleMetadata(\r\n bundleId: string,\r\n keyB64?: string,\r\n opts?: ConnectOptions\r\n ): Promise<BundleMetadata> {\r\n if (!bundleId || typeof bundleId !== 'string') {\r\n throw new DropgateValidationError('Bundle ID is required.');\r\n }\r\n\r\n const { timeoutMs = 5000, signal } = opts ?? {};\r\n\r\n const url = `${this.baseUrl}/api/bundle/${encodeURIComponent(bundleId)}/meta`;\r\n const { res, json } = await fetchJson(this.fetchFn, url, {\r\n method: 'GET',\r\n timeoutMs,\r\n signal,\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) || `Failed to fetch bundle metadata (status ${res.status}).`;\r\n throw new DropgateProtocolError(msg, { details: json });\r\n }\r\n\r\n const serverMeta = json as {\r\n isEncrypted: boolean;\r\n sealed?: boolean;\r\n encryptedManifest?: string;\r\n files?: Array<{\r\n fileId: string;\r\n sizeBytes: number;\r\n filename?: string;\r\n encryptedFilename?: string;\r\n }>;\r\n };\r\n\r\n let files: Array<{\r\n fileId: string;\r\n sizeBytes: number;\r\n filename?: string;\r\n encryptedFilename?: string;\r\n }> = [];\r\n\r\n // Handle sealed bundles: decrypt manifest to get file list\r\n if (serverMeta.sealed && serverMeta.encryptedManifest) {\r\n if (!keyB64) {\r\n throw new DropgateValidationError(\r\n 'Decryption key (keyB64) is required for encrypted sealed bundles.'\r\n );\r\n }\r\n\r\n const key = await importKeyFromBase64(this.cryptoObj, keyB64);\r\n const encryptedBytes = this.base64.decode(serverMeta.encryptedManifest);\r\n const decryptedBuffer = await decryptChunk(this.cryptoObj, encryptedBytes, key);\r\n const manifestJson = new TextDecoder().decode(decryptedBuffer);\r\n const manifest = JSON.parse(manifestJson) as {\r\n files: Array<{ fileId: string; sizeBytes: number; name: string }>\r\n };\r\n\r\n // Map manifest files to consistent format (name -> filename for consistency)\r\n files = manifest.files.map(f => ({\r\n fileId: f.fileId,\r\n sizeBytes: f.sizeBytes,\r\n filename: f.name,\r\n }));\r\n } else if (serverMeta.files) {\r\n // Unsealed bundle: use files from server response\r\n files = serverMeta.files;\r\n } else {\r\n throw new DropgateProtocolError('Invalid bundle metadata: missing files or manifest.');\r\n }\r\n\r\n // Derive totalSizeBytes and fileCount from files array\r\n const totalSizeBytes = files.reduce((sum, f) => sum + (f.sizeBytes || 0), 0);\r\n const fileCount = files.length;\r\n\r\n return {\r\n isEncrypted: serverMeta.isEncrypted,\r\n sealed: serverMeta.sealed,\r\n encryptedManifest: serverMeta.encryptedManifest,\r\n files,\r\n totalSizeBytes,\r\n fileCount,\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 { files: rawFiles, 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 const files = Array.isArray(rawFiles) ? rawFiles : [rawFiles];\r\n if (files.length === 0) {\r\n throw new DropgateValidationError('At least one file is required.');\r\n }\r\n\r\n // Validate each file and check size limits\r\n for (let i = 0; i < files.length; i++) {\r\n const file = files[i];\r\n const fileSize = Number(file?.size || 0);\r\n if (!file || !Number.isFinite(fileSize) || fileSize <= 0) {\r\n throw new DropgateValidationError(`File at index ${i} is missing or invalid.`);\r\n }\r\n\r\n // maxSizeMB: 0 means unlimited (per-file check)\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 validationChunkSize = (Number.isFinite(caps.chunkSize) && caps.chunkSize! > 0)\r\n ? caps.chunkSize!\r\n : this.chunkSize;\r\n const totalChunks = Math.ceil(fileSize / validationChunkSize);\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 at index ${i} too large once encryption overhead is included. Server limit: ${maxMB} MB.`\r\n : `File at index ${i} too large. Server limit: ${maxMB} MB.`;\r\n throw new DropgateValidationError(msg);\r\n }\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 'End-to-end encryption is not supported on this server.'\r\n );\r\n }\r\n\r\n return true;\r\n }\r\n\r\n /**\r\n * Upload one or more files to the server with optional encryption.\r\n * Single files use the standard upload protocol.\r\n * Multiple files use the bundle protocol, grouping files under a single download link.\r\n *\r\n * @param opts - Upload options including file(s) and settings.\r\n * @returns Upload session with result promise and cancellation support.\r\n */\r\n async uploadFiles(opts: UploadFilesOptions): Promise<UploadSession> {\r\n const {\r\n files: rawFiles,\r\n lifetimeMs,\r\n encrypt,\r\n maxDownloads,\r\n filenameOverrides,\r\n onProgress,\r\n onCancel,\r\n signal,\r\n timeouts = {},\r\n retry = {},\r\n } = opts;\r\n\r\n const files = Array.isArray(rawFiles) ? rawFiles : [rawFiles];\r\n if (files.length === 0) {\r\n throw new DropgateValidationError('At least one file is required.');\r\n }\r\n\r\n const internalController = signal ? null : new AbortController();\r\n const effectiveSignal = signal || internalController?.signal;\r\n\r\n let uploadState: 'initializing' | 'uploading' | 'completing' | 'completed' | 'cancelled' | 'error' = 'initializing';\r\n const currentUploadIds: string[] = [];\r\n\r\n const totalSizeBytes = files.reduce((sum, f) => sum + f.size, 0);\r\n\r\n const uploadPromise = (async (): Promise<UploadResult> => {\r\n try {\r\n const progress = (evt: UploadProgressEvent): void => {\r\n try { if (onProgress) onProgress(evt); } catch { /* Ignore */ }\r\n };\r\n\r\n // 0) Get server info + compat (uses cache)\r\n progress({ phase: 'server-info', text: 'Checking server...', percent: 0, processedBytes: 0, totalBytes: totalSizeBytes });\r\n\r\n const compat = await this.connect({\r\n timeoutMs: timeouts.serverInfoMs ?? 5000,\r\n signal: effectiveSignal,\r\n });\r\n\r\n const { baseUrl, serverInfo } = compat;\r\n progress({ phase: 'server-compat', text: compat.message, percent: 0, processedBytes: 0, totalBytes: totalSizeBytes });\r\n if (!compat.compatible) {\r\n throw new DropgateValidationError(compat.message);\r\n }\r\n\r\n // 1) Resolve filenames\r\n const filenames = files.map((f, i) => filenameOverrides?.[i] ?? f.name ?? 'file');\r\n\r\n // Resolve encrypt option: default to true if server supports E2EE\r\n const serverSupportsE2EE = Boolean(serverInfo?.capabilities?.upload?.e2ee);\r\n const effectiveEncrypt = encrypt ?? serverSupportsE2EE;\r\n\r\n if (!effectiveEncrypt) {\r\n for (const name of filenames) validatePlainFilename(name);\r\n }\r\n\r\n this.validateUploadInputs({ files, lifetimeMs, encrypt: effectiveEncrypt, serverInfo });\r\n\r\n // 2) Encryption prep (single key for all files)\r\n let cryptoKey: CryptoKey | null = null;\r\n let keyB64: string | null = null;\r\n const transmittedFilenames: string[] = [];\r\n\r\n if (effectiveEncrypt) {\r\n if (!this.cryptoObj?.subtle) {\r\n throw new DropgateValidationError(\r\n 'Web Crypto API not available (crypto.subtle). Encryption requires a secure context (HTTPS or localhost).'\r\n );\r\n }\r\n progress({ phase: 'crypto', text: 'Generating encryption key...', percent: 0, processedBytes: 0, totalBytes: totalSizeBytes });\r\n try {\r\n cryptoKey = await generateAesGcmKey(this.cryptoObj);\r\n keyB64 = await exportKeyBase64(this.cryptoObj, cryptoKey);\r\n for (const name of filenames) {\r\n transmittedFilenames.push(\r\n await encryptFilenameToBase64(this.cryptoObj, name, cryptoKey)\r\n );\r\n }\r\n } catch (err) {\r\n throw new DropgateError('Failed to prepare encryption.', { code: 'CRYPTO_PREP_FAILED', cause: err });\r\n }\r\n } else {\r\n transmittedFilenames.push(...filenames);\r\n }\r\n\r\n // 3) Compute chunk sizes\r\n const serverChunkSize = serverInfo?.capabilities?.upload?.chunkSize;\r\n const effectiveChunkSize = (Number.isFinite(serverChunkSize) && serverChunkSize! > 0)\r\n ? serverChunkSize!\r\n : this.chunkSize;\r\n\r\n const retries = Number.isFinite(retry.retries) ? retry.retries! : 5;\r\n const baseBackoffMs = Number.isFinite(retry.backoffMs) ? retry.backoffMs! : 1000;\r\n const maxBackoffMs = Number.isFinite(retry.maxBackoffMs) ? retry.maxBackoffMs! : 30000;\r\n\r\n // ========== SINGLE FILE ==========\r\n if (files.length === 1) {\r\n const file = files[0];\r\n const totalChunks = Math.ceil(file.size / effectiveChunkSize);\r\n const totalUploadSize = estimateTotalUploadSizeBytes(file.size, totalChunks, effectiveEncrypt);\r\n\r\n // Init\r\n progress({ phase: 'init', text: 'Reserving server storage...', percent: 0, processedBytes: 0, totalBytes: file.size });\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: effectiveSignal,\r\n headers: { 'Content-Type': 'application/json', Accept: 'application/json' },\r\n body: JSON.stringify({\r\n filename: transmittedFilenames[0],\r\n lifetime: lifetimeMs,\r\n isEncrypted: effectiveEncrypt,\r\n totalSize: totalUploadSize,\r\n totalChunks,\r\n ...(maxDownloads !== undefined ? { maxDownloads } : {}),\r\n }),\r\n });\r\n\r\n if (!initRes.res.ok) {\r\n const errorJson = initRes.json as { error?: string } | null;\r\n throw new DropgateProtocolError(errorJson?.error || `Server initialisation failed: ${initRes.res.status}`, { details: initRes.json || initRes.text });\r\n }\r\n\r\n const uploadId = (initRes.json as { uploadId?: string })?.uploadId;\r\n if (!uploadId) throw new DropgateProtocolError('Server did not return a valid uploadId.');\r\n currentUploadIds.push(uploadId);\r\n uploadState = 'uploading';\r\n\r\n // Chunks\r\n await this._uploadFileChunks({\r\n file, uploadId, cryptoKey, effectiveChunkSize, totalChunks, totalUploadSize,\r\n baseOffset: 0, totalBytesAllFiles: file.size,\r\n progress, signal: effectiveSignal, baseUrl,\r\n retries, backoffMs: baseBackoffMs, maxBackoffMs,\r\n chunkTimeoutMs: timeouts.chunkMs ?? 60000,\r\n });\r\n\r\n // Complete\r\n progress({ phase: 'complete', text: 'Finalising upload...', percent: 100, processedBytes: file.size, totalBytes: file.size });\r\n uploadState = 'completing';\r\n\r\n const completeRes = await fetchJson(this.fetchFn, `${baseUrl}/upload/complete`, {\r\n method: 'POST',\r\n timeoutMs: timeouts.completeMs ?? 30000,\r\n signal: effectiveSignal,\r\n headers: { 'Content-Type': 'application/json', Accept: 'application/json' },\r\n body: JSON.stringify({ uploadId }),\r\n });\r\n\r\n if (!completeRes.res.ok) {\r\n const errorJson = completeRes.json as { error?: string } | null;\r\n throw new DropgateProtocolError(errorJson?.error || 'Finalisation failed.', { details: completeRes.json || completeRes.text });\r\n }\r\n\r\n const fileId = (completeRes.json as { id?: string })?.id;\r\n if (!fileId) throw new DropgateProtocolError('Server did not return a valid file id.');\r\n\r\n let downloadUrl = `${baseUrl}/${fileId}`;\r\n if (effectiveEncrypt && keyB64) downloadUrl += `#${keyB64}`;\r\n\r\n progress({ phase: 'done', text: 'Upload successful!', percent: 100, processedBytes: file.size, totalBytes: file.size });\r\n uploadState = 'completed';\r\n\r\n return {\r\n downloadUrl, fileId, uploadId, baseUrl,\r\n ...(effectiveEncrypt && keyB64 ? { keyB64 } : {}),\r\n };\r\n }\r\n\r\n // ========== MULTI-FILE (BUNDLE) ==========\r\n // Prepare per-file metadata\r\n const fileManifest = files.map((f, i) => {\r\n const totalChunks = Math.ceil(f.size / effectiveChunkSize);\r\n const totalUploadSize = estimateTotalUploadSizeBytes(f.size, totalChunks, effectiveEncrypt);\r\n return { filename: transmittedFilenames[i], totalSize: totalUploadSize, totalChunks };\r\n });\r\n\r\n // Init bundle\r\n progress({ phase: 'init', text: `Reserving server storage for ${files.length} files...`, percent: 0, processedBytes: 0, totalBytes: totalSizeBytes, totalFiles: files.length });\r\n\r\n const initBundleRes = await fetchJson(this.fetchFn, `${baseUrl}/upload/init-bundle`, {\r\n method: 'POST',\r\n timeoutMs: timeouts.initMs ?? 15000,\r\n signal: effectiveSignal,\r\n headers: { 'Content-Type': 'application/json', Accept: 'application/json' },\r\n body: JSON.stringify({\r\n fileCount: files.length,\r\n files: fileManifest,\r\n lifetime: lifetimeMs,\r\n isEncrypted: effectiveEncrypt,\r\n ...(maxDownloads !== undefined ? { maxDownloads } : {}),\r\n }),\r\n });\r\n\r\n if (!initBundleRes.res.ok) {\r\n const errorJson = initBundleRes.json as { error?: string } | null;\r\n throw new DropgateProtocolError(errorJson?.error || `Bundle initialisation failed: ${initBundleRes.res.status}`, { details: initBundleRes.json || initBundleRes.text });\r\n }\r\n\r\n const bundleInitJson = initBundleRes.json as { bundleUploadId?: string; fileUploadIds?: string[] } | null;\r\n const bundleUploadId = bundleInitJson?.bundleUploadId;\r\n const fileUploadIds = bundleInitJson?.fileUploadIds;\r\n if (!bundleUploadId || !fileUploadIds || fileUploadIds.length !== files.length) {\r\n throw new DropgateProtocolError('Server did not return valid bundle upload IDs.');\r\n }\r\n currentUploadIds.push(...fileUploadIds);\r\n uploadState = 'uploading';\r\n\r\n // Upload each file sequentially\r\n const fileResults: Array<{ fileId: string; name: string; size: number }> = [];\r\n let cumulativeBytes = 0;\r\n\r\n for (let fi = 0; fi < files.length; fi++) {\r\n const file = files[fi];\r\n const uploadId = fileUploadIds[fi];\r\n const totalChunks = fileManifest[fi].totalChunks;\r\n const totalUploadSize = fileManifest[fi].totalSize;\r\n\r\n progress({\r\n phase: 'file-start', text: `Uploading file ${fi + 1} of ${files.length}: ${filenames[fi]}`,\r\n percent: totalSizeBytes > 0 ? (cumulativeBytes / totalSizeBytes) * 100 : 0,\r\n processedBytes: cumulativeBytes, totalBytes: totalSizeBytes,\r\n fileIndex: fi, totalFiles: files.length, currentFileName: filenames[fi],\r\n });\r\n\r\n await this._uploadFileChunks({\r\n file, uploadId, cryptoKey, effectiveChunkSize, totalChunks, totalUploadSize,\r\n baseOffset: cumulativeBytes, totalBytesAllFiles: totalSizeBytes,\r\n progress, signal: effectiveSignal, baseUrl,\r\n retries, backoffMs: baseBackoffMs, maxBackoffMs,\r\n chunkTimeoutMs: timeouts.chunkMs ?? 60000,\r\n fileIndex: fi, totalFiles: files.length, currentFileName: filenames[fi],\r\n });\r\n\r\n // Complete individual file\r\n const completeRes = await fetchJson(this.fetchFn, `${baseUrl}/upload/complete`, {\r\n method: 'POST',\r\n timeoutMs: timeouts.completeMs ?? 30000,\r\n signal: effectiveSignal,\r\n headers: { 'Content-Type': 'application/json', Accept: 'application/json' },\r\n body: JSON.stringify({ uploadId }),\r\n });\r\n\r\n if (!completeRes.res.ok) {\r\n const errorJson = completeRes.json as { error?: string } | null;\r\n throw new DropgateProtocolError(errorJson?.error || `File ${fi + 1} finalisation failed.`, { details: completeRes.json || completeRes.text });\r\n }\r\n\r\n const fileId = (completeRes.json as { id?: string })?.id;\r\n if (!fileId) throw new DropgateProtocolError(`Server did not return a valid file id for file ${fi + 1}.`);\r\n\r\n fileResults.push({ fileId, name: filenames[fi], size: file.size });\r\n cumulativeBytes += file.size;\r\n\r\n progress({\r\n phase: 'file-complete', text: `File ${fi + 1} of ${files.length} uploaded.`,\r\n percent: totalSizeBytes > 0 ? (cumulativeBytes / totalSizeBytes) * 100 : 0,\r\n processedBytes: cumulativeBytes, totalBytes: totalSizeBytes,\r\n fileIndex: fi, totalFiles: files.length, currentFileName: filenames[fi],\r\n });\r\n }\r\n\r\n // Complete bundle\r\n progress({ phase: 'complete', text: 'Finalising bundle...', percent: 100, processedBytes: totalSizeBytes, totalBytes: totalSizeBytes });\r\n uploadState = 'completing';\r\n\r\n // For encrypted bundles, build and encrypt the manifest client-side.\r\n // The server stores only the opaque blob and cannot read which files belong to the bundle.\r\n let encryptedManifestB64: string | undefined;\r\n if (effectiveEncrypt && cryptoKey) {\r\n const manifest = JSON.stringify({\r\n files: fileResults.map(r => ({\r\n fileId: r.fileId,\r\n name: r.name,\r\n sizeBytes: r.size,\r\n })),\r\n });\r\n const manifestBytes = new TextEncoder().encode(manifest);\r\n const encryptedBlob = await encryptToBlob(this.cryptoObj, manifestBytes.buffer, cryptoKey);\r\n const encryptedBuffer = new Uint8Array(await encryptedBlob.arrayBuffer());\r\n encryptedManifestB64 = this.base64.encode(encryptedBuffer);\r\n }\r\n\r\n const completeBundleRes = await fetchJson(this.fetchFn, `${baseUrl}/upload/complete-bundle`, {\r\n method: 'POST',\r\n timeoutMs: timeouts.completeMs ?? 30000,\r\n signal: effectiveSignal,\r\n headers: { 'Content-Type': 'application/json', Accept: 'application/json' },\r\n body: JSON.stringify({\r\n bundleUploadId,\r\n ...(encryptedManifestB64 ? { encryptedManifest: encryptedManifestB64 } : {}),\r\n }),\r\n });\r\n\r\n if (!completeBundleRes.res.ok) {\r\n const errorJson = completeBundleRes.json as { error?: string } | null;\r\n throw new DropgateProtocolError(errorJson?.error || 'Bundle finalisation failed.', { details: completeBundleRes.json || completeBundleRes.text });\r\n }\r\n\r\n const bundleId = (completeBundleRes.json as { bundleId?: string })?.bundleId;\r\n if (!bundleId) throw new DropgateProtocolError('Server did not return a valid bundle id.');\r\n\r\n let downloadUrl = `${baseUrl}/b/${bundleId}`;\r\n if (effectiveEncrypt && keyB64) downloadUrl += `#${keyB64}`;\r\n\r\n progress({ phase: 'done', text: 'Upload successful!', percent: 100, processedBytes: totalSizeBytes, totalBytes: totalSizeBytes });\r\n uploadState = 'completed';\r\n\r\n return {\r\n downloadUrl, bundleId, baseUrl, files: fileResults,\r\n ...(effectiveEncrypt && keyB64 ? { keyB64 } : {}),\r\n };\r\n\r\n } catch (err) {\r\n if (err instanceof Error && (err.name === 'AbortError' || err.message?.includes('abort'))) {\r\n uploadState = 'cancelled';\r\n onCancel?.();\r\n } else {\r\n uploadState = 'error';\r\n }\r\n throw err;\r\n }\r\n })();\r\n\r\n const callCancelEndpoint = async (uploadId: string): Promise<void> => {\r\n try {\r\n await fetchJson(this.fetchFn, `${this.baseUrl}/upload/cancel`, {\r\n method: 'POST', timeoutMs: 5000,\r\n headers: { 'Content-Type': 'application/json', Accept: 'application/json' },\r\n body: JSON.stringify({ uploadId }),\r\n });\r\n } catch { /* Best effort */ }\r\n };\r\n\r\n return {\r\n result: uploadPromise,\r\n cancel: (reason?: string) => {\r\n if (uploadState === 'completed' || uploadState === 'cancelled') return;\r\n uploadState = 'cancelled';\r\n for (const id of currentUploadIds) {\r\n callCancelEndpoint(id).catch(() => { });\r\n }\r\n internalController?.abort(new DropgateAbortError(reason || 'Upload cancelled by user.'));\r\n },\r\n getStatus: () => uploadState,\r\n };\r\n }\r\n\r\n /**\r\n * Upload a single file's chunks to the server. Used internally by uploadFiles().\r\n */\r\n private async _uploadFileChunks(params: {\r\n file: FileSource;\r\n uploadId: string;\r\n cryptoKey: CryptoKey | null;\r\n effectiveChunkSize: number;\r\n totalChunks: number;\r\n totalUploadSize: number;\r\n baseOffset: number;\r\n totalBytesAllFiles: number;\r\n progress: (evt: UploadProgressEvent) => void;\r\n signal?: AbortSignal;\r\n baseUrl: string;\r\n retries: number;\r\n backoffMs: number;\r\n maxBackoffMs: number;\r\n chunkTimeoutMs: number;\r\n fileIndex?: number;\r\n totalFiles?: number;\r\n currentFileName?: string;\r\n }): Promise<void> {\r\n const {\r\n file, uploadId, cryptoKey, effectiveChunkSize, totalChunks,\r\n baseOffset, totalBytesAllFiles, progress, signal, baseUrl,\r\n retries, backoffMs, maxBackoffMs, chunkTimeoutMs,\r\n fileIndex, totalFiles, currentFileName,\r\n } = params;\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 * effectiveChunkSize;\r\n const end = Math.min(start + effectiveChunkSize, file.size);\r\n const chunkSlice = file.slice(start, end);\r\n\r\n const processedBytes = baseOffset + start;\r\n const percent = totalBytesAllFiles > 0 ? (processedBytes / totalBytesAllFiles) * 100 : 0;\r\n progress({\r\n phase: 'chunk',\r\n text: `Uploading chunk ${i + 1} of ${totalChunks}...`,\r\n percent, processedBytes, totalBytes: totalBytesAllFiles,\r\n chunkIndex: i, totalChunks,\r\n ...(fileIndex !== undefined ? { fileIndex, totalFiles, currentFileName } : {}),\r\n });\r\n\r\n const chunkBuffer = await chunkSlice.arrayBuffer();\r\n\r\n let uploadBlob: Blob;\r\n if (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 if (uploadBlob.size > effectiveChunkSize + 1024) {\r\n throw new DropgateValidationError('Chunk too large (client-side). Check chunk size settings.');\r\n }\r\n\r\n const toHash = await uploadBlob.arrayBuffer();\r\n const hashHex = await sha256Hex(this.cryptoObj, toHash);\r\n\r\n await this._attemptChunkUpload(\r\n `${baseUrl}/upload/chunk`,\r\n { method: 'POST', headers: { 'Content-Type': 'application/octet-stream', 'X-Upload-ID': uploadId, 'X-Chunk-Index': String(i), 'X-Chunk-Hash': hashHex }, body: uploadBlob },\r\n { retries, backoffMs, maxBackoffMs, timeoutMs: chunkTimeoutMs, signal, progress, chunkIndex: i, totalChunks, chunkSize: effectiveChunkSize, fileSizeBytes: totalBytesAllFiles }\r\n );\r\n }\r\n }\r\n\r\n /**\r\n * Download one or more files from the server with optional decryption.\r\n *\r\n * For single files, use `fileId`. For bundles, use `bundleId`.\r\n * With `asZip: true` on bundles, streams a ZIP archive via `onData`.\r\n * Without `asZip`, delivers files individually via `onFileStart`/`onFileData`/`onFileEnd`.\r\n *\r\n * @param opts - Download options including file/bundle ID and optional key.\r\n * @returns Download result containing filename(s) and received bytes.\r\n */\r\n async downloadFiles(opts: DownloadFilesOptions): Promise<DownloadResult> {\r\n const {\r\n fileId,\r\n bundleId,\r\n keyB64,\r\n asZip,\r\n zipFilename: _zipFilename,\r\n onProgress,\r\n onData,\r\n onFileStart,\r\n onFileData,\r\n onFileEnd,\r\n signal,\r\n timeoutMs = 60000,\r\n } = opts;\r\n\r\n const progress = (evt: DownloadProgressEvent): void => {\r\n try { if (onProgress) onProgress(evt); } catch { /* Ignore */ }\r\n };\r\n\r\n if (!fileId && !bundleId) {\r\n throw new DropgateValidationError('Either fileId or bundleId is required.');\r\n }\r\n\r\n // 0) Connect\r\n progress({ phase: 'server-info', text: 'Checking server...', processedBytes: 0, totalBytes: 0, percent: 0 });\r\n const compat = await this.connect({ timeoutMs, signal });\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) throw new DropgateValidationError(compat.message);\r\n\r\n // ========== SINGLE FILE ==========\r\n if (fileId) {\r\n return this._downloadSingleFile({ fileId, keyB64, onProgress, onData, signal, timeoutMs, baseUrl, compat });\r\n }\r\n\r\n // ========== BUNDLE ==========\r\n progress({ phase: 'metadata', text: 'Fetching bundle info...', processedBytes: 0, totalBytes: 0, percent: 0 });\r\n\r\n // Use getBundleMetadata to fetch metadata with proper derivation\r\n let bundleMeta: BundleMetadata;\r\n try {\r\n bundleMeta = await this.getBundleMetadata(bundleId!, keyB64, { timeoutMs, signal });\r\n } catch (err) {\r\n if (err instanceof DropgateError) throw err;\r\n if (err instanceof Error && err.name === 'AbortError') throw new DropgateAbortError('Download cancelled.');\r\n throw new DropgateNetworkError('Could not fetch bundle metadata.', { cause: err });\r\n }\r\n\r\n const isEncrypted = Boolean(bundleMeta.isEncrypted);\r\n const totalBytes = bundleMeta.totalSizeBytes || 0;\r\n\r\n // Decrypt filenames (and manifest for sealed bundles)\r\n let cryptoKey: CryptoKey | undefined;\r\n const filenames: string[] = [];\r\n\r\n if (isEncrypted) {\r\n if (!keyB64) throw new DropgateValidationError('Decryption key is required for encrypted bundles.');\r\n if (!this.cryptoObj?.subtle) throw new DropgateValidationError('Web Crypto API not available for decryption.');\r\n\r\n try {\r\n cryptoKey = await importKeyFromBase64(this.cryptoObj, keyB64, this.base64);\r\n\r\n if (bundleMeta.sealed && bundleMeta.encryptedManifest) {\r\n // Sealed bundle: decrypt the manifest to get the file list\r\n const encryptedBytes = this.base64.decode(bundleMeta.encryptedManifest);\r\n const decryptedBuffer = await decryptChunk(this.cryptoObj, encryptedBytes, cryptoKey);\r\n const manifestJson = new TextDecoder().decode(decryptedBuffer);\r\n const manifest = JSON.parse(manifestJson) as { files: Array<{ fileId: string; name: string; sizeBytes: number }> };\r\n\r\n // Populate bundleMeta.files from the decrypted manifest\r\n bundleMeta.files = manifest.files.map(f => ({\r\n fileId: f.fileId,\r\n sizeBytes: f.sizeBytes,\r\n filename: f.name,\r\n }));\r\n bundleMeta.fileCount = bundleMeta.files.length;\r\n\r\n for (const f of bundleMeta.files) {\r\n filenames.push(f.filename || 'file');\r\n }\r\n } else {\r\n // Non-sealed encrypted bundle: decrypt individual filenames\r\n for (const f of bundleMeta.files) {\r\n filenames.push(await decryptFilenameFromBase64(this.cryptoObj, f.encryptedFilename!, cryptoKey, this.base64));\r\n }\r\n }\r\n } catch (err) {\r\n throw new DropgateError('Failed to decrypt bundle manifest.', { code: 'DECRYPT_MANIFEST_FAILED', cause: err });\r\n }\r\n } else {\r\n for (const f of bundleMeta.files) {\r\n filenames.push(f.filename || 'file');\r\n }\r\n }\r\n\r\n let totalReceivedBytes = 0;\r\n\r\n if (asZip && onData) {\r\n // ===== BUNDLE AS ZIP =====\r\n const zipWriter = new StreamingZipWriter(onData);\r\n\r\n for (let fi = 0; fi < bundleMeta.files.length; fi++) {\r\n const fileMeta = bundleMeta.files[fi];\r\n const name = filenames[fi];\r\n\r\n progress({\r\n phase: 'zipping', text: `Downloading ${name}...`,\r\n percent: totalBytes > 0 ? (totalReceivedBytes / totalBytes) * 100 : 0,\r\n processedBytes: totalReceivedBytes, totalBytes,\r\n fileIndex: fi, totalFiles: bundleMeta.files.length, currentFileName: name,\r\n });\r\n\r\n zipWriter.startFile(name);\r\n\r\n // Download and stream this file into the ZIP\r\n const baseReceivedBytes = totalReceivedBytes;\r\n const bytesReceived = await this._streamFileIntoCallback(\r\n baseUrl, fileMeta.fileId, isEncrypted, cryptoKey, compat,\r\n signal, timeoutMs,\r\n (chunk) => { zipWriter.writeChunk(chunk); },\r\n (fileBytes) => {\r\n const current = baseReceivedBytes + fileBytes;\r\n progress({\r\n phase: 'zipping', text: `Downloading ${name}...`,\r\n percent: totalBytes > 0 ? (current / totalBytes) * 100 : 0,\r\n processedBytes: current, totalBytes,\r\n fileIndex: fi, totalFiles: bundleMeta.files.length, currentFileName: name,\r\n });\r\n },\r\n );\r\n\r\n zipWriter.endFile();\r\n totalReceivedBytes += bytesReceived;\r\n }\r\n\r\n await zipWriter.finalize();\r\n\r\n // Notify server of bundle download\r\n try {\r\n await fetchJson(this.fetchFn, `${baseUrl}/api/bundle/${bundleId}/downloaded`, {\r\n method: 'POST', timeoutMs: 5000,\r\n headers: { 'Content-Type': 'application/json', Accept: 'application/json' },\r\n body: '{}',\r\n });\r\n } catch { /* Best effort */ }\r\n\r\n progress({ phase: 'complete', text: 'Download complete!', percent: 100, processedBytes: totalReceivedBytes, totalBytes });\r\n\r\n return { filenames, receivedBytes: totalReceivedBytes, wasEncrypted: isEncrypted };\r\n\r\n } else {\r\n // ===== BUNDLE AS INDIVIDUAL FILES =====\r\n const dataCallback = onFileData || onData;\r\n\r\n for (let fi = 0; fi < bundleMeta.files.length; fi++) {\r\n const fileMeta = bundleMeta.files[fi];\r\n const name = filenames[fi];\r\n\r\n progress({\r\n phase: 'downloading', text: `Downloading ${name}...`,\r\n percent: totalBytes > 0 ? (totalReceivedBytes / totalBytes) * 100 : 0,\r\n processedBytes: totalReceivedBytes, totalBytes,\r\n fileIndex: fi, totalFiles: bundleMeta.files.length, currentFileName: name,\r\n });\r\n\r\n onFileStart?.({ name, size: fileMeta.sizeBytes, index: fi });\r\n\r\n const baseReceivedBytes = totalReceivedBytes;\r\n const bytesReceived = await this._streamFileIntoCallback(\r\n baseUrl, fileMeta.fileId, isEncrypted, cryptoKey, compat,\r\n signal, timeoutMs,\r\n dataCallback ? (chunk) => dataCallback(chunk) : undefined,\r\n (fileBytes) => {\r\n const current = baseReceivedBytes + fileBytes;\r\n progress({\r\n phase: 'downloading', text: `Downloading ${name}...`,\r\n percent: totalBytes > 0 ? (current / totalBytes) * 100 : 0,\r\n processedBytes: current, totalBytes,\r\n fileIndex: fi, totalFiles: bundleMeta.files.length, currentFileName: name,\r\n });\r\n },\r\n );\r\n\r\n onFileEnd?.({ name, index: fi });\r\n totalReceivedBytes += bytesReceived;\r\n }\r\n\r\n progress({ phase: 'complete', text: 'Download complete!', percent: 100, processedBytes: totalReceivedBytes, totalBytes });\r\n\r\n return { filenames, receivedBytes: totalReceivedBytes, wasEncrypted: isEncrypted };\r\n }\r\n }\r\n\r\n /**\r\n * Download a single file, handling encryption/decryption internally.\r\n * Preserves the original downloadFile() behavior.\r\n */\r\n private async _downloadSingleFile(params: {\r\n fileId: string;\r\n keyB64?: string;\r\n onProgress?: (evt: DownloadProgressEvent) => void;\r\n onData?: (chunk: Uint8Array) => Promise<void> | void;\r\n signal?: AbortSignal;\r\n timeoutMs: number;\r\n baseUrl: string;\r\n compat: CompatibilityResult & { serverInfo: ServerInfo; baseUrl: string };\r\n }): Promise<DownloadResult> {\r\n const { fileId, keyB64, onProgress, onData, signal, timeoutMs, baseUrl, compat } = params;\r\n\r\n const progress = (evt: DownloadProgressEvent): void => {\r\n try { if (onProgress) onProgress(evt); } catch { /* Ignore */ }\r\n };\r\n\r\n // Fetch metadata\r\n progress({ phase: 'metadata', text: 'Fetching file info...', processedBytes: 0, totalBytes: 0, percent: 0 });\r\n\r\n // Use getFileMetadata for consistent metadata fetching\r\n let metadata: FileMetadata;\r\n try {\r\n metadata = await this.getFileMetadata(fileId, { timeoutMs, signal });\r\n } catch (err) {\r\n if (err instanceof DropgateError) throw err;\r\n if (err instanceof Error && err.name === 'AbortError') throw new DropgateAbortError('Download cancelled.');\r\n throw new DropgateNetworkError('Could not fetch file metadata.', { cause: err });\r\n }\r\n\r\n const isEncrypted = Boolean(metadata.isEncrypted);\r\n const totalBytes = metadata.sizeBytes || 0;\r\n\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. Provide an onData callback to stream files larger than ${limitMB}MB.`\r\n );\r\n }\r\n\r\n // Decrypt filename\r\n let filename: string;\r\n let cryptoKey: CryptoKey | undefined;\r\n\r\n if (isEncrypted) {\r\n if (!keyB64) throw new DropgateValidationError('Decryption key is required for encrypted files.');\r\n if (!this.cryptoObj?.subtle) throw new DropgateValidationError('Web Crypto API not available for decryption.');\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(this.cryptoObj, metadata.encryptedFilename!, cryptoKey, this.base64);\r\n } catch (err) {\r\n throw new DropgateError('Failed to decrypt filename.', { code: 'DECRYPT_FILENAME_FAILED', cause: err });\r\n }\r\n } else {\r\n filename = metadata.filename || 'file';\r\n }\r\n\r\n // Download\r\n progress({ phase: 'downloading', text: 'Starting download...', percent: 0, processedBytes: 0, totalBytes });\r\n\r\n const dataChunks: Uint8Array[] = [];\r\n const collectData = !onData;\r\n\r\n const receivedBytes = await this._streamFileIntoCallback(\r\n baseUrl, fileId, isEncrypted, cryptoKey, compat, signal, timeoutMs,\r\n async (chunk) => {\r\n if (collectData) {\r\n dataChunks.push(chunk);\r\n } else {\r\n await onData!(chunk);\r\n }\r\n },\r\n (bytes) => {\r\n progress({\r\n phase: 'downloading', text: 'Downloading...',\r\n percent: totalBytes > 0 ? (bytes / totalBytes) * 100 : 0,\r\n processedBytes: bytes, totalBytes,\r\n });\r\n },\r\n );\r\n\r\n progress({ phase: 'complete', text: 'Download complete!', percent: 100, processedBytes: receivedBytes, totalBytes });\r\n\r\n let data: Uint8Array | undefined;\r\n if (collectData && dataChunks.length > 0) {\r\n const totalLength = dataChunks.reduce((sum, c) => sum + c.length, 0);\r\n data = new Uint8Array(totalLength);\r\n let offset = 0;\r\n for (const c of dataChunks) { data.set(c, offset); offset += c.length; }\r\n }\r\n\r\n return {\r\n filename, receivedBytes, wasEncrypted: isEncrypted,\r\n ...(data ? { data } : {}),\r\n };\r\n }\r\n\r\n /**\r\n * Stream a single file's content into a callback, handling decryption if needed.\r\n * Returns total bytes received from the network (encrypted size).\r\n */\r\n private async _streamFileIntoCallback(\r\n baseUrl: string,\r\n fileId: string,\r\n isEncrypted: boolean,\r\n cryptoKey: CryptoKey | undefined,\r\n compat: CompatibilityResult & { serverInfo: ServerInfo; baseUrl: string },\r\n signal: AbortSignal | undefined,\r\n timeoutMs: number,\r\n onChunk?: (chunk: Uint8Array) => void | Promise<void>,\r\n onBytesReceived?: (receivedBytes: number) => void,\r\n ): Promise<number> {\r\n const { signal: downloadSignal, cleanup: downloadCleanup } = makeAbortSignal(signal, timeoutMs);\r\n let receivedBytes = 0;\r\n\r\n try {\r\n const downloadRes = await this.fetchFn(`${baseUrl}/api/file/${fileId}`, {\r\n method: 'GET', signal: downloadSignal,\r\n });\r\n\r\n if (!downloadRes.ok) throw new DropgateProtocolError(`Download failed (status ${downloadRes.status}).`);\r\n if (!downloadRes.body) throw new DropgateProtocolError('Streaming response not available.');\r\n\r\n const reader = downloadRes.body.getReader();\r\n\r\n if (isEncrypted && cryptoKey) {\r\n const downloadChunkSize = (Number.isFinite(compat.serverInfo?.capabilities?.upload?.chunkSize) && compat.serverInfo.capabilities!.upload!.chunkSize! > 0)\r\n ? compat.serverInfo.capabilities!.upload!.chunkSize!\r\n : this.chunkSize;\r\n const ENCRYPTED_CHUNK_SIZE = downloadChunkSize + ENCRYPTION_OVERHEAD_PER_CHUNK;\r\n const pendingChunks: Uint8Array[] = [];\r\n let pendingLength = 0;\r\n\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) { result.set(chunk, offset); offset += chunk.length; }\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) throw new DropgateAbortError('Download cancelled.');\r\n const { done, value } = await reader.read();\r\n if (done) break;\r\n\r\n pendingChunks.push(value);\r\n pendingLength += value.length;\r\n receivedBytes += value.length;\r\n if (onBytesReceived) onBytesReceived(receivedBytes);\r\n\r\n while (pendingLength >= ENCRYPTED_CHUNK_SIZE) {\r\n const buffer = flushPending();\r\n const encryptedChunk = buffer.subarray(0, ENCRYPTED_CHUNK_SIZE);\r\n if (buffer.length > ENCRYPTED_CHUNK_SIZE) {\r\n pendingChunks.push(buffer.subarray(ENCRYPTED_CHUNK_SIZE));\r\n pendingLength = buffer.length - ENCRYPTED_CHUNK_SIZE;\r\n }\r\n\r\n const decryptedBuffer = await decryptChunk(this.cryptoObj, encryptedChunk, cryptoKey);\r\n if (onChunk) await onChunk(new Uint8Array(decryptedBuffer));\r\n }\r\n }\r\n\r\n if (pendingLength > 0) {\r\n const buffer = flushPending();\r\n const decryptedBuffer = await decryptChunk(this.cryptoObj, buffer, cryptoKey);\r\n if (onChunk) await onChunk(new Uint8Array(decryptedBuffer));\r\n }\r\n } else {\r\n while (true) {\r\n if (signal?.aborted) throw new DropgateAbortError('Download cancelled.');\r\n const { done, value } = await reader.read();\r\n if (done) break;\r\n receivedBytes += value.length;\r\n if (onBytesReceived) onBytesReceived(receivedBytes);\r\n if (onChunk) await onChunk(value);\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') throw new DropgateAbortError('Download cancelled.');\r\n throw new DropgateNetworkError('Download failed.', { cause: err });\r\n } finally {\r\n downloadCleanup();\r\n }\r\n\r\n return receivedBytes;\r\n }\r\n\r\n /**\r\n * Start a P2P send session. Connects to the signalling server and waits for a receiver.\r\n *\r\n * Server info, peerjsPath, iceServers, and cryptoObj are provided automatically\r\n * from the client's cached server info and configuration.\r\n *\r\n * @param opts - P2P send options (file, Peer constructor, callbacks, tuning).\r\n * @returns P2P send session with control methods.\r\n * @throws {DropgateValidationError} If P2P is not enabled on the server.\r\n * @throws {DropgateNetworkError} If the signalling server cannot be reached.\r\n */\r\n async p2pSend(opts: P2PSendFileOptions): Promise<P2PSendSession> {\r\n const compat = await this.connect();\r\n if (!compat.compatible) {\r\n throw new DropgateValidationError(compat.message);\r\n }\r\n\r\n const { serverInfo } = compat;\r\n const p2pCaps = serverInfo?.capabilities?.p2p;\r\n if (!p2pCaps?.enabled) {\r\n throw new DropgateValidationError('Direct transfer is disabled on this server.');\r\n }\r\n\r\n const { host, port, secure } = this.serverTarget;\r\n const { path: peerjsPath, iceServers } = resolvePeerConfig({}, p2pCaps);\r\n\r\n return startP2PSend({\r\n ...opts,\r\n host,\r\n port,\r\n secure,\r\n peerjsPath,\r\n iceServers,\r\n serverInfo,\r\n cryptoObj: this.cryptoObj,\r\n });\r\n }\r\n\r\n /**\r\n * Start a P2P receive session. Connects to a sender via their sharing code.\r\n *\r\n * Server info, peerjsPath, and iceServers are provided automatically\r\n * from the client's cached server info.\r\n *\r\n * @param opts - P2P receive options (code, Peer constructor, callbacks, tuning).\r\n * @returns P2P receive session with control methods.\r\n * @throws {DropgateValidationError} If P2P is not enabled on the server.\r\n * @throws {DropgateNetworkError} If the signalling server cannot be reached.\r\n */\r\n async p2pReceive(opts: P2PReceiveFileOptions): Promise<P2PReceiveSession> {\r\n const compat = await this.connect();\r\n if (!compat.compatible) {\r\n throw new DropgateValidationError(compat.message);\r\n }\r\n\r\n const { serverInfo } = compat;\r\n const p2pCaps = serverInfo?.capabilities?.p2p;\r\n if (!p2pCaps?.enabled) {\r\n throw new DropgateValidationError('Direct transfer is disabled on this server.');\r\n }\r\n\r\n const { host, port, secure } = this.serverTarget;\r\n const { path: peerjsPath, iceServers } = resolvePeerConfig({}, p2pCaps);\r\n\r\n return startP2PReceive({\r\n ...opts,\r\n host,\r\n port,\r\n secure,\r\n peerjsPath,\r\n iceServers,\r\n serverInfo,\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"],"mappings":"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","names":["fl","ZipPassThrough","Zip","err","_a","_b","err","crypto","err","err","ALLOWED_TRANSITIONS","err","err","downloadUrl","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/sha256-fallback.ts","../src/crypto/decrypt.ts","../src/crypto/index.ts","../src/crypto/encrypt.ts","../node_modules/fflate/esm/browser.js","../src/zip/stream-zip.ts","../src/p2p/utils.ts","../src/p2p/helpers.ts","../src/p2p/protocol.ts","../src/p2p/send.ts","../src/p2p/receive.ts","../src/client/DropgateClient.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, opts.cause !== undefined ? { cause: opts.cause } : undefined);\r\n this.name = this.constructor.name;\r\n this.code = opts.code || 'DROPGATE_ERROR';\r\n this.details = opts.details;\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","/**\r\n * Pure-JS SHA-256 implementation for chunk integrity hashing ONLY.\r\n *\r\n * This exists solely as a fallback when crypto.subtle is unavailable\r\n * (e.g. insecure HTTP contexts). It MUST NOT be used for any\r\n * encryption, key derivation, or other security-critical operations.\r\n *\r\n * Based on the FIPS 180-4 specification.\r\n */\r\n\r\n// SHA-256 constants: first 32 bits of the fractional parts of the cube roots of the first 64 primes\r\nconst K = new Uint32Array([\r\n 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,\r\n 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,\r\n 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,\r\n 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,\r\n 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,\r\n 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,\r\n 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,\r\n 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,\r\n]);\r\n\r\nfunction rotr(x: number, n: number): number {\r\n return (x >>> n) | (x << (32 - n));\r\n}\r\n\r\n/**\r\n * Compute SHA-256 hash of an ArrayBuffer and return the raw digest.\r\n * FOR INTEGRITY VERIFICATION ONLY - not for cryptographic security operations.\r\n */\r\nexport function sha256Fallback(data: ArrayBuffer): ArrayBuffer {\r\n const bytes = new Uint8Array(data);\r\n const bitLen = bytes.length * 8;\r\n\r\n // Pre-processing: pad to 512-bit (64-byte) block boundary\r\n // message + 0x80 + zeros + 8-byte big-endian length\r\n const padded = new Uint8Array(\r\n Math.ceil((bytes.length + 9) / 64) * 64\r\n );\r\n padded.set(bytes);\r\n padded[bytes.length] = 0x80;\r\n\r\n // Append original length in bits as 64-bit big-endian\r\n const view = new DataView(padded.buffer);\r\n // bitLen fits in 53-bit JS number; write high 32 and low 32\r\n view.setUint32(padded.length - 8, (bitLen / 0x100000000) >>> 0, false);\r\n view.setUint32(padded.length - 4, bitLen >>> 0, false);\r\n\r\n // Initial hash values: first 32 bits of the fractional parts of the square roots of the first 8 primes\r\n let h0 = 0x6a09e667;\r\n let h1 = 0xbb67ae85;\r\n let h2 = 0x3c6ef372;\r\n let h3 = 0xa54ff53a;\r\n let h4 = 0x510e527f;\r\n let h5 = 0x9b05688c;\r\n let h6 = 0x1f83d9ab;\r\n let h7 = 0x5be0cd19;\r\n\r\n const W = new Uint32Array(64);\r\n\r\n for (let offset = 0; offset < padded.length; offset += 64) {\r\n // Prepare message schedule\r\n for (let i = 0; i < 16; i++) {\r\n W[i] = view.getUint32(offset + i * 4, false);\r\n }\r\n for (let i = 16; i < 64; i++) {\r\n const s0 = rotr(W[i - 15], 7) ^ rotr(W[i - 15], 18) ^ (W[i - 15] >>> 3);\r\n const s1 = rotr(W[i - 2], 17) ^ rotr(W[i - 2], 19) ^ (W[i - 2] >>> 10);\r\n W[i] = (W[i - 16] + s0 + W[i - 7] + s1) | 0;\r\n }\r\n\r\n let a = h0, b = h1, c = h2, d = h3, e = h4, f = h5, g = h6, h = h7;\r\n\r\n for (let i = 0; i < 64; i++) {\r\n const S1 = rotr(e, 6) ^ rotr(e, 11) ^ rotr(e, 25);\r\n const ch = (e & f) ^ (~e & g);\r\n const temp1 = (h + S1 + ch + K[i] + W[i]) | 0;\r\n const S0 = rotr(a, 2) ^ rotr(a, 13) ^ rotr(a, 22);\r\n const maj = (a & b) ^ (a & c) ^ (b & c);\r\n const temp2 = (S0 + maj) | 0;\r\n\r\n h = g;\r\n g = f;\r\n f = e;\r\n e = (d + temp1) | 0;\r\n d = c;\r\n c = b;\r\n b = a;\r\n a = (temp1 + temp2) | 0;\r\n }\r\n\r\n h0 = (h0 + a) | 0;\r\n h1 = (h1 + b) | 0;\r\n h2 = (h2 + c) | 0;\r\n h3 = (h3 + d) | 0;\r\n h4 = (h4 + e) | 0;\r\n h5 = (h5 + f) | 0;\r\n h6 = (h6 + g) | 0;\r\n h7 = (h7 + h) | 0;\r\n }\r\n\r\n const result = new ArrayBuffer(32);\r\n const out = new DataView(result);\r\n out.setUint32(0, h0, false);\r\n out.setUint32(4, h1, false);\r\n out.setUint32(8, h2, false);\r\n out.setUint32(12, h3, false);\r\n out.setUint32(16, h4, false);\r\n out.setUint32(20, h5, false);\r\n out.setUint32(24, h6, false);\r\n out.setUint32(28, h7, false);\r\n\r\n return result;\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\nimport { sha256Fallback } from './sha256-fallback.js';\r\n\r\n/**\r\n * Convert a raw SHA-256 digest ArrayBuffer to a hex string.\r\n */\r\nfunction digestToHex(hashBuffer: ArrayBuffer): string {\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 * Compute SHA-256 hash of data and return as hex string.\r\n *\r\n * Uses crypto.subtle when available. Falls back to a pure-JS\r\n * implementation for integrity hashing on insecure contexts.\r\n * The fallback MUST NOT be used for encryption operations.\r\n */\r\nexport async function sha256Hex(\r\n cryptoObj: CryptoAdapter,\r\n data: ArrayBuffer\r\n): Promise<string> {\r\n if (cryptoObj?.subtle) {\r\n const hashBuffer = await cryptoObj.subtle.digest('SHA-256', data);\r\n return digestToHex(hashBuffer);\r\n }\r\n // Fallback: pure-JS SHA-256 for integrity verification only\r\n return digestToHex(sha256Fallback(data));\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","// DEFLATE is a complex format; to read this code, you should probably check the RFC first:\n// https://tools.ietf.org/html/rfc1951\n// You may also wish to take a look at the guide I made about this program:\n// https://gist.github.com/101arrowz/253f31eb5abc3d9275ab943003ffecad\n// Some of the following code is similar to that of UZIP.js:\n// https://github.com/photopea/UZIP.js\n// However, the vast majority of the codebase has diverged from UZIP.js to increase performance and reduce bundle size.\n// Sometimes 0 will appear where -1 would be more appropriate. This is because using a uint\n// is better for memory in most engines (I *think*).\nvar ch2 = {};\nvar wk = (function (c, id, msg, transfer, cb) {\n var w = new Worker(ch2[id] || (ch2[id] = URL.createObjectURL(new Blob([\n c + ';addEventListener(\"error\",function(e){e=e.error;postMessage({$e$:[e.message,e.code,e.stack]})})'\n ], { type: 'text/javascript' }))));\n w.onmessage = function (e) {\n var d = e.data, ed = d.$e$;\n if (ed) {\n var err = new Error(ed[0]);\n err['code'] = ed[1];\n err.stack = ed[2];\n cb(err, null);\n }\n else\n cb(null, d);\n };\n w.postMessage(msg, transfer);\n return w;\n});\n\n// aliases for shorter compressed code (most minifers don't do this)\nvar u8 = Uint8Array, u16 = Uint16Array, i32 = Int32Array;\n// fixed length extra bits\nvar fleb = new u8([0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, /* unused */ 0, 0, /* impossible */ 0]);\n// fixed distance extra bits\nvar fdeb = new u8([0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, /* unused */ 0, 0]);\n// code length index map\nvar clim = new u8([16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15]);\n// get base, reverse index map from extra bits\nvar freb = function (eb, start) {\n var b = new u16(31);\n for (var i = 0; i < 31; ++i) {\n b[i] = start += 1 << eb[i - 1];\n }\n // numbers here are at max 18 bits\n var r = new i32(b[30]);\n for (var i = 1; i < 30; ++i) {\n for (var j = b[i]; j < b[i + 1]; ++j) {\n r[j] = ((j - b[i]) << 5) | i;\n }\n }\n return { b: b, r: r };\n};\nvar _a = freb(fleb, 2), fl = _a.b, revfl = _a.r;\n// we can ignore the fact that the other numbers are wrong; they never happen anyway\nfl[28] = 258, revfl[258] = 28;\nvar _b = freb(fdeb, 0), fd = _b.b, revfd = _b.r;\n// map of value to reverse (assuming 16 bits)\nvar rev = new u16(32768);\nfor (var i = 0; i < 32768; ++i) {\n // reverse table algorithm from SO\n var x = ((i & 0xAAAA) >> 1) | ((i & 0x5555) << 1);\n x = ((x & 0xCCCC) >> 2) | ((x & 0x3333) << 2);\n x = ((x & 0xF0F0) >> 4) | ((x & 0x0F0F) << 4);\n rev[i] = (((x & 0xFF00) >> 8) | ((x & 0x00FF) << 8)) >> 1;\n}\n// create huffman tree from u8 \"map\": index -> code length for code index\n// mb (max bits) must be at most 15\n// TODO: optimize/split up?\nvar hMap = (function (cd, mb, r) {\n var s = cd.length;\n // index\n var i = 0;\n // u16 \"map\": index -> # of codes with bit length = index\n var l = new u16(mb);\n // length of cd must be 288 (total # of codes)\n for (; i < s; ++i) {\n if (cd[i])\n ++l[cd[i] - 1];\n }\n // u16 \"map\": index -> minimum code for bit length = index\n var le = new u16(mb);\n for (i = 1; i < mb; ++i) {\n le[i] = (le[i - 1] + l[i - 1]) << 1;\n }\n var co;\n if (r) {\n // u16 \"map\": index -> number of actual bits, symbol for code\n co = new u16(1 << mb);\n // bits to remove for reverser\n var rvb = 15 - mb;\n for (i = 0; i < s; ++i) {\n // ignore 0 lengths\n if (cd[i]) {\n // num encoding both symbol and bits read\n var sv = (i << 4) | cd[i];\n // free bits\n var r_1 = mb - cd[i];\n // start value\n var v = le[cd[i] - 1]++ << r_1;\n // m is end value\n for (var m = v | ((1 << r_1) - 1); v <= m; ++v) {\n // every 16 bit value starting with the code yields the same result\n co[rev[v] >> rvb] = sv;\n }\n }\n }\n }\n else {\n co = new u16(s);\n for (i = 0; i < s; ++i) {\n if (cd[i]) {\n co[i] = rev[le[cd[i] - 1]++] >> (15 - cd[i]);\n }\n }\n }\n return co;\n});\n// fixed length tree\nvar flt = new u8(288);\nfor (var i = 0; i < 144; ++i)\n flt[i] = 8;\nfor (var i = 144; i < 256; ++i)\n flt[i] = 9;\nfor (var i = 256; i < 280; ++i)\n flt[i] = 7;\nfor (var i = 280; i < 288; ++i)\n flt[i] = 8;\n// fixed distance tree\nvar fdt = new u8(32);\nfor (var i = 0; i < 32; ++i)\n fdt[i] = 5;\n// fixed length map\nvar flm = /*#__PURE__*/ hMap(flt, 9, 0), flrm = /*#__PURE__*/ hMap(flt, 9, 1);\n// fixed distance map\nvar fdm = /*#__PURE__*/ hMap(fdt, 5, 0), fdrm = /*#__PURE__*/ hMap(fdt, 5, 1);\n// find max of array\nvar max = function (a) {\n var m = a[0];\n for (var i = 1; i < a.length; ++i) {\n if (a[i] > m)\n m = a[i];\n }\n return m;\n};\n// read d, starting at bit p and mask with m\nvar bits = function (d, p, m) {\n var o = (p / 8) | 0;\n return ((d[o] | (d[o + 1] << 8)) >> (p & 7)) & m;\n};\n// read d, starting at bit p continuing for at least 16 bits\nvar bits16 = function (d, p) {\n var o = (p / 8) | 0;\n return ((d[o] | (d[o + 1] << 8) | (d[o + 2] << 16)) >> (p & 7));\n};\n// get end of byte\nvar shft = function (p) { return ((p + 7) / 8) | 0; };\n// typed array slice - allows garbage collector to free original reference,\n// while being more compatible than .slice\nvar slc = function (v, s, e) {\n if (s == null || s < 0)\n s = 0;\n if (e == null || e > v.length)\n e = v.length;\n // can't use .constructor in case user-supplied\n return new u8(v.subarray(s, e));\n};\n/**\n * Codes for errors generated within this library\n */\nexport var FlateErrorCode = {\n UnexpectedEOF: 0,\n InvalidBlockType: 1,\n InvalidLengthLiteral: 2,\n InvalidDistance: 3,\n StreamFinished: 4,\n NoStreamHandler: 5,\n InvalidHeader: 6,\n NoCallback: 7,\n InvalidUTF8: 8,\n ExtraFieldTooLong: 9,\n InvalidDate: 10,\n FilenameTooLong: 11,\n StreamFinishing: 12,\n InvalidZipData: 13,\n UnknownCompressionMethod: 14\n};\n// error codes\nvar ec = [\n 'unexpected EOF',\n 'invalid block type',\n 'invalid length/literal',\n 'invalid distance',\n 'stream finished',\n 'no stream handler',\n ,\n 'no callback',\n 'invalid UTF-8 data',\n 'extra field too long',\n 'date not in range 1980-2099',\n 'filename too long',\n 'stream finishing',\n 'invalid zip data'\n // determined by unknown compression method\n];\n;\nvar err = function (ind, msg, nt) {\n var e = new Error(msg || ec[ind]);\n e.code = ind;\n if (Error.captureStackTrace)\n Error.captureStackTrace(e, err);\n if (!nt)\n throw e;\n return e;\n};\n// expands raw DEFLATE data\nvar inflt = function (dat, st, buf, dict) {\n // source length dict length\n var sl = dat.length, dl = dict ? dict.length : 0;\n if (!sl || st.f && !st.l)\n return buf || new u8(0);\n var noBuf = !buf;\n // have to estimate size\n var resize = noBuf || st.i != 2;\n // no state\n var noSt = st.i;\n // Assumes roughly 33% compression ratio average\n if (noBuf)\n buf = new u8(sl * 3);\n // ensure buffer can fit at least l elements\n var cbuf = function (l) {\n var bl = buf.length;\n // need to increase size to fit\n if (l > bl) {\n // Double or set to necessary, whichever is greater\n var nbuf = new u8(Math.max(bl * 2, l));\n nbuf.set(buf);\n buf = nbuf;\n }\n };\n // last chunk bitpos bytes\n var final = st.f || 0, pos = st.p || 0, bt = st.b || 0, lm = st.l, dm = st.d, lbt = st.m, dbt = st.n;\n // total bits\n var tbts = sl * 8;\n do {\n if (!lm) {\n // BFINAL - this is only 1 when last chunk is next\n final = bits(dat, pos, 1);\n // type: 0 = no compression, 1 = fixed huffman, 2 = dynamic huffman\n var type = bits(dat, pos + 1, 3);\n pos += 3;\n if (!type) {\n // go to end of byte boundary\n var s = shft(pos) + 4, l = dat[s - 4] | (dat[s - 3] << 8), t = s + l;\n if (t > sl) {\n if (noSt)\n err(0);\n break;\n }\n // ensure size\n if (resize)\n cbuf(bt + l);\n // Copy over uncompressed data\n buf.set(dat.subarray(s, t), bt);\n // Get new bitpos, update byte count\n st.b = bt += l, st.p = pos = t * 8, st.f = final;\n continue;\n }\n else if (type == 1)\n lm = flrm, dm = fdrm, lbt = 9, dbt = 5;\n else if (type == 2) {\n // literal lengths\n var hLit = bits(dat, pos, 31) + 257, hcLen = bits(dat, pos + 10, 15) + 4;\n var tl = hLit + bits(dat, pos + 5, 31) + 1;\n pos += 14;\n // length+distance tree\n var ldt = new u8(tl);\n // code length tree\n var clt = new u8(19);\n for (var i = 0; i < hcLen; ++i) {\n // use index map to get real code\n clt[clim[i]] = bits(dat, pos + i * 3, 7);\n }\n pos += hcLen * 3;\n // code lengths bits\n var clb = max(clt), clbmsk = (1 << clb) - 1;\n // code lengths map\n var clm = hMap(clt, clb, 1);\n for (var i = 0; i < tl;) {\n var r = clm[bits(dat, pos, clbmsk)];\n // bits read\n pos += r & 15;\n // symbol\n var s = r >> 4;\n // code length to copy\n if (s < 16) {\n ldt[i++] = s;\n }\n else {\n // copy count\n var c = 0, n = 0;\n if (s == 16)\n n = 3 + bits(dat, pos, 3), pos += 2, c = ldt[i - 1];\n else if (s == 17)\n n = 3 + bits(dat, pos, 7), pos += 3;\n else if (s == 18)\n n = 11 + bits(dat, pos, 127), pos += 7;\n while (n--)\n ldt[i++] = c;\n }\n }\n // length tree distance tree\n var lt = ldt.subarray(0, hLit), dt = ldt.subarray(hLit);\n // max length bits\n lbt = max(lt);\n // max dist bits\n dbt = max(dt);\n lm = hMap(lt, lbt, 1);\n dm = hMap(dt, dbt, 1);\n }\n else\n err(1);\n if (pos > tbts) {\n if (noSt)\n err(0);\n break;\n }\n }\n // Make sure the buffer can hold this + the largest possible addition\n // Maximum chunk size (practically, theoretically infinite) is 2^17\n if (resize)\n cbuf(bt + 131072);\n var lms = (1 << lbt) - 1, dms = (1 << dbt) - 1;\n var lpos = pos;\n for (;; lpos = pos) {\n // bits read, code\n var c = lm[bits16(dat, pos) & lms], sym = c >> 4;\n pos += c & 15;\n if (pos > tbts) {\n if (noSt)\n err(0);\n break;\n }\n if (!c)\n err(2);\n if (sym < 256)\n buf[bt++] = sym;\n else if (sym == 256) {\n lpos = pos, lm = null;\n break;\n }\n else {\n var add = sym - 254;\n // no extra bits needed if less\n if (sym > 264) {\n // index\n var i = sym - 257, b = fleb[i];\n add = bits(dat, pos, (1 << b) - 1) + fl[i];\n pos += b;\n }\n // dist\n var d = dm[bits16(dat, pos) & dms], dsym = d >> 4;\n if (!d)\n err(3);\n pos += d & 15;\n var dt = fd[dsym];\n if (dsym > 3) {\n var b = fdeb[dsym];\n dt += bits16(dat, pos) & (1 << b) - 1, pos += b;\n }\n if (pos > tbts) {\n if (noSt)\n err(0);\n break;\n }\n if (resize)\n cbuf(bt + 131072);\n var end = bt + add;\n if (bt < dt) {\n var shift = dl - dt, dend = Math.min(dt, end);\n if (shift + bt < 0)\n err(3);\n for (; bt < dend; ++bt)\n buf[bt] = dict[shift + bt];\n }\n for (; bt < end; ++bt)\n buf[bt] = buf[bt - dt];\n }\n }\n st.l = lm, st.p = lpos, st.b = bt, st.f = final;\n if (lm)\n final = 1, st.m = lbt, st.d = dm, st.n = dbt;\n } while (!final);\n // don't reallocate for streams or user buffers\n return bt != buf.length && noBuf ? slc(buf, 0, bt) : buf.subarray(0, bt);\n};\n// starting at p, write the minimum number of bits that can hold v to d\nvar wbits = function (d, p, v) {\n v <<= p & 7;\n var o = (p / 8) | 0;\n d[o] |= v;\n d[o + 1] |= v >> 8;\n};\n// starting at p, write the minimum number of bits (>8) that can hold v to d\nvar wbits16 = function (d, p, v) {\n v <<= p & 7;\n var o = (p / 8) | 0;\n d[o] |= v;\n d[o + 1] |= v >> 8;\n d[o + 2] |= v >> 16;\n};\n// creates code lengths from a frequency table\nvar hTree = function (d, mb) {\n // Need extra info to make a tree\n var t = [];\n for (var i = 0; i < d.length; ++i) {\n if (d[i])\n t.push({ s: i, f: d[i] });\n }\n var s = t.length;\n var t2 = t.slice();\n if (!s)\n return { t: et, l: 0 };\n if (s == 1) {\n var v = new u8(t[0].s + 1);\n v[t[0].s] = 1;\n return { t: v, l: 1 };\n }\n t.sort(function (a, b) { return a.f - b.f; });\n // after i2 reaches last ind, will be stopped\n // freq must be greater than largest possible number of symbols\n t.push({ s: -1, f: 25001 });\n var l = t[0], r = t[1], i0 = 0, i1 = 1, i2 = 2;\n t[0] = { s: -1, f: l.f + r.f, l: l, r: r };\n // efficient algorithm from UZIP.js\n // i0 is lookbehind, i2 is lookahead - after processing two low-freq\n // symbols that combined have high freq, will start processing i2 (high-freq,\n // non-composite) symbols instead\n // see https://reddit.com/r/photopea/comments/ikekht/uzipjs_questions/\n while (i1 != s - 1) {\n l = t[t[i0].f < t[i2].f ? i0++ : i2++];\n r = t[i0 != i1 && t[i0].f < t[i2].f ? i0++ : i2++];\n t[i1++] = { s: -1, f: l.f + r.f, l: l, r: r };\n }\n var maxSym = t2[0].s;\n for (var i = 1; i < s; ++i) {\n if (t2[i].s > maxSym)\n maxSym = t2[i].s;\n }\n // code lengths\n var tr = new u16(maxSym + 1);\n // max bits in tree\n var mbt = ln(t[i1 - 1], tr, 0);\n if (mbt > mb) {\n // more algorithms from UZIP.js\n // TODO: find out how this code works (debt)\n // ind debt\n var i = 0, dt = 0;\n // left cost\n var lft = mbt - mb, cst = 1 << lft;\n t2.sort(function (a, b) { return tr[b.s] - tr[a.s] || a.f - b.f; });\n for (; i < s; ++i) {\n var i2_1 = t2[i].s;\n if (tr[i2_1] > mb) {\n dt += cst - (1 << (mbt - tr[i2_1]));\n tr[i2_1] = mb;\n }\n else\n break;\n }\n dt >>= lft;\n while (dt > 0) {\n var i2_2 = t2[i].s;\n if (tr[i2_2] < mb)\n dt -= 1 << (mb - tr[i2_2]++ - 1);\n else\n ++i;\n }\n for (; i >= 0 && dt; --i) {\n var i2_3 = t2[i].s;\n if (tr[i2_3] == mb) {\n --tr[i2_3];\n ++dt;\n }\n }\n mbt = mb;\n }\n return { t: new u8(tr), l: mbt };\n};\n// get the max length and assign length codes\nvar ln = function (n, l, d) {\n return n.s == -1\n ? Math.max(ln(n.l, l, d + 1), ln(n.r, l, d + 1))\n : (l[n.s] = d);\n};\n// length codes generation\nvar lc = function (c) {\n var s = c.length;\n // Note that the semicolon was intentional\n while (s && !c[--s])\n ;\n var cl = new u16(++s);\n // ind num streak\n var cli = 0, cln = c[0], cls = 1;\n var w = function (v) { cl[cli++] = v; };\n for (var i = 1; i <= s; ++i) {\n if (c[i] == cln && i != s)\n ++cls;\n else {\n if (!cln && cls > 2) {\n for (; cls > 138; cls -= 138)\n w(32754);\n if (cls > 2) {\n w(cls > 10 ? ((cls - 11) << 5) | 28690 : ((cls - 3) << 5) | 12305);\n cls = 0;\n }\n }\n else if (cls > 3) {\n w(cln), --cls;\n for (; cls > 6; cls -= 6)\n w(8304);\n if (cls > 2)\n w(((cls - 3) << 5) | 8208), cls = 0;\n }\n while (cls--)\n w(cln);\n cls = 1;\n cln = c[i];\n }\n }\n return { c: cl.subarray(0, cli), n: s };\n};\n// calculate the length of output from tree, code lengths\nvar clen = function (cf, cl) {\n var l = 0;\n for (var i = 0; i < cl.length; ++i)\n l += cf[i] * cl[i];\n return l;\n};\n// writes a fixed block\n// returns the new bit pos\nvar wfblk = function (out, pos, dat) {\n // no need to write 00 as type: TypedArray defaults to 0\n var s = dat.length;\n var o = shft(pos + 2);\n out[o] = s & 255;\n out[o + 1] = s >> 8;\n out[o + 2] = out[o] ^ 255;\n out[o + 3] = out[o + 1] ^ 255;\n for (var i = 0; i < s; ++i)\n out[o + i + 4] = dat[i];\n return (o + 4 + s) * 8;\n};\n// writes a block\nvar wblk = function (dat, out, final, syms, lf, df, eb, li, bs, bl, p) {\n wbits(out, p++, final);\n ++lf[256];\n var _a = hTree(lf, 15), dlt = _a.t, mlb = _a.l;\n var _b = hTree(df, 15), ddt = _b.t, mdb = _b.l;\n var _c = lc(dlt), lclt = _c.c, nlc = _c.n;\n var _d = lc(ddt), lcdt = _d.c, ndc = _d.n;\n var lcfreq = new u16(19);\n for (var i = 0; i < lclt.length; ++i)\n ++lcfreq[lclt[i] & 31];\n for (var i = 0; i < lcdt.length; ++i)\n ++lcfreq[lcdt[i] & 31];\n var _e = hTree(lcfreq, 7), lct = _e.t, mlcb = _e.l;\n var nlcc = 19;\n for (; nlcc > 4 && !lct[clim[nlcc - 1]]; --nlcc)\n ;\n var flen = (bl + 5) << 3;\n var ftlen = clen(lf, flt) + clen(df, fdt) + eb;\n var dtlen = clen(lf, dlt) + clen(df, ddt) + eb + 14 + 3 * nlcc + clen(lcfreq, lct) + 2 * lcfreq[16] + 3 * lcfreq[17] + 7 * lcfreq[18];\n if (bs >= 0 && flen <= ftlen && flen <= dtlen)\n return wfblk(out, p, dat.subarray(bs, bs + bl));\n var lm, ll, dm, dl;\n wbits(out, p, 1 + (dtlen < ftlen)), p += 2;\n if (dtlen < ftlen) {\n lm = hMap(dlt, mlb, 0), ll = dlt, dm = hMap(ddt, mdb, 0), dl = ddt;\n var llm = hMap(lct, mlcb, 0);\n wbits(out, p, nlc - 257);\n wbits(out, p + 5, ndc - 1);\n wbits(out, p + 10, nlcc - 4);\n p += 14;\n for (var i = 0; i < nlcc; ++i)\n wbits(out, p + 3 * i, lct[clim[i]]);\n p += 3 * nlcc;\n var lcts = [lclt, lcdt];\n for (var it = 0; it < 2; ++it) {\n var clct = lcts[it];\n for (var i = 0; i < clct.length; ++i) {\n var len = clct[i] & 31;\n wbits(out, p, llm[len]), p += lct[len];\n if (len > 15)\n wbits(out, p, (clct[i] >> 5) & 127), p += clct[i] >> 12;\n }\n }\n }\n else {\n lm = flm, ll = flt, dm = fdm, dl = fdt;\n }\n for (var i = 0; i < li; ++i) {\n var sym = syms[i];\n if (sym > 255) {\n var len = (sym >> 18) & 31;\n wbits16(out, p, lm[len + 257]), p += ll[len + 257];\n if (len > 7)\n wbits(out, p, (sym >> 23) & 31), p += fleb[len];\n var dst = sym & 31;\n wbits16(out, p, dm[dst]), p += dl[dst];\n if (dst > 3)\n wbits16(out, p, (sym >> 5) & 8191), p += fdeb[dst];\n }\n else {\n wbits16(out, p, lm[sym]), p += ll[sym];\n }\n }\n wbits16(out, p, lm[256]);\n return p + ll[256];\n};\n// deflate options (nice << 13) | chain\nvar deo = /*#__PURE__*/ new i32([65540, 131080, 131088, 131104, 262176, 1048704, 1048832, 2114560, 2117632]);\n// empty\nvar et = /*#__PURE__*/ new u8(0);\n// compresses data into a raw DEFLATE buffer\nvar dflt = function (dat, lvl, plvl, pre, post, st) {\n var s = st.z || dat.length;\n var o = new u8(pre + s + 5 * (1 + Math.ceil(s / 7000)) + post);\n // writing to this writes to the output buffer\n var w = o.subarray(pre, o.length - post);\n var lst = st.l;\n var pos = (st.r || 0) & 7;\n if (lvl) {\n if (pos)\n w[0] = st.r >> 3;\n var opt = deo[lvl - 1];\n var n = opt >> 13, c = opt & 8191;\n var msk_1 = (1 << plvl) - 1;\n // prev 2-byte val map curr 2-byte val map\n var prev = st.p || new u16(32768), head = st.h || new u16(msk_1 + 1);\n var bs1_1 = Math.ceil(plvl / 3), bs2_1 = 2 * bs1_1;\n var hsh = function (i) { return (dat[i] ^ (dat[i + 1] << bs1_1) ^ (dat[i + 2] << bs2_1)) & msk_1; };\n // 24576 is an arbitrary number of maximum symbols per block\n // 424 buffer for last block\n var syms = new i32(25000);\n // length/literal freq distance freq\n var lf = new u16(288), df = new u16(32);\n // l/lcnt exbits index l/lind waitdx blkpos\n var lc_1 = 0, eb = 0, i = st.i || 0, li = 0, wi = st.w || 0, bs = 0;\n for (; i + 2 < s; ++i) {\n // hash value\n var hv = hsh(i);\n // index mod 32768 previous index mod\n var imod = i & 32767, pimod = head[hv];\n prev[imod] = pimod;\n head[hv] = imod;\n // We always should modify head and prev, but only add symbols if\n // this data is not yet processed (\"wait\" for wait index)\n if (wi <= i) {\n // bytes remaining\n var rem = s - i;\n if ((lc_1 > 7000 || li > 24576) && (rem > 423 || !lst)) {\n pos = wblk(dat, w, 0, syms, lf, df, eb, li, bs, i - bs, pos);\n li = lc_1 = eb = 0, bs = i;\n for (var j = 0; j < 286; ++j)\n lf[j] = 0;\n for (var j = 0; j < 30; ++j)\n df[j] = 0;\n }\n // len dist chain\n var l = 2, d = 0, ch_1 = c, dif = imod - pimod & 32767;\n if (rem > 2 && hv == hsh(i - dif)) {\n var maxn = Math.min(n, rem) - 1;\n var maxd = Math.min(32767, i);\n // max possible length\n // not capped at dif because decompressors implement \"rolling\" index population\n var ml = Math.min(258, rem);\n while (dif <= maxd && --ch_1 && imod != pimod) {\n if (dat[i + l] == dat[i + l - dif]) {\n var nl = 0;\n for (; nl < ml && dat[i + nl] == dat[i + nl - dif]; ++nl)\n ;\n if (nl > l) {\n l = nl, d = dif;\n // break out early when we reach \"nice\" (we are satisfied enough)\n if (nl > maxn)\n break;\n // now, find the rarest 2-byte sequence within this\n // length of literals and search for that instead.\n // Much faster than just using the start\n var mmd = Math.min(dif, nl - 2);\n var md = 0;\n for (var j = 0; j < mmd; ++j) {\n var ti = i - dif + j & 32767;\n var pti = prev[ti];\n var cd = ti - pti & 32767;\n if (cd > md)\n md = cd, pimod = ti;\n }\n }\n }\n // check the previous match\n imod = pimod, pimod = prev[imod];\n dif += imod - pimod & 32767;\n }\n }\n // d will be nonzero only when a match was found\n if (d) {\n // store both dist and len data in one int32\n // Make sure this is recognized as a len/dist with 28th bit (2^28)\n syms[li++] = 268435456 | (revfl[l] << 18) | revfd[d];\n var lin = revfl[l] & 31, din = revfd[d] & 31;\n eb += fleb[lin] + fdeb[din];\n ++lf[257 + lin];\n ++df[din];\n wi = i + l;\n ++lc_1;\n }\n else {\n syms[li++] = dat[i];\n ++lf[dat[i]];\n }\n }\n }\n for (i = Math.max(i, wi); i < s; ++i) {\n syms[li++] = dat[i];\n ++lf[dat[i]];\n }\n pos = wblk(dat, w, lst, syms, lf, df, eb, li, bs, i - bs, pos);\n if (!lst) {\n st.r = (pos & 7) | w[(pos / 8) | 0] << 3;\n // shft(pos) now 1 less if pos & 7 != 0\n pos -= 7;\n st.h = head, st.p = prev, st.i = i, st.w = wi;\n }\n }\n else {\n for (var i = st.w || 0; i < s + lst; i += 65535) {\n // end\n var e = i + 65535;\n if (e >= s) {\n // write final block\n w[(pos / 8) | 0] = lst;\n e = s;\n }\n pos = wfblk(w, pos + 1, dat.subarray(i, e));\n }\n st.i = s;\n }\n return slc(o, 0, pre + shft(pos) + post);\n};\n// CRC32 table\nvar crct = /*#__PURE__*/ (function () {\n var t = new Int32Array(256);\n for (var i = 0; i < 256; ++i) {\n var c = i, k = 9;\n while (--k)\n c = ((c & 1) && -306674912) ^ (c >>> 1);\n t[i] = c;\n }\n return t;\n})();\n// CRC32\nvar crc = function () {\n var c = -1;\n return {\n p: function (d) {\n // closures have awful performance\n var cr = c;\n for (var i = 0; i < d.length; ++i)\n cr = crct[(cr & 255) ^ d[i]] ^ (cr >>> 8);\n c = cr;\n },\n d: function () { return ~c; }\n };\n};\n// Adler32\nvar adler = function () {\n var a = 1, b = 0;\n return {\n p: function (d) {\n // closures have awful performance\n var n = a, m = b;\n var l = d.length | 0;\n for (var i = 0; i != l;) {\n var e = Math.min(i + 2655, l);\n for (; i < e; ++i)\n m += n += d[i];\n n = (n & 65535) + 15 * (n >> 16), m = (m & 65535) + 15 * (m >> 16);\n }\n a = n, b = m;\n },\n d: function () {\n a %= 65521, b %= 65521;\n return (a & 255) << 24 | (a & 0xFF00) << 8 | (b & 255) << 8 | (b >> 8);\n }\n };\n};\n;\n// deflate with opts\nvar dopt = function (dat, opt, pre, post, st) {\n if (!st) {\n st = { l: 1 };\n if (opt.dictionary) {\n var dict = opt.dictionary.subarray(-32768);\n var newDat = new u8(dict.length + dat.length);\n newDat.set(dict);\n newDat.set(dat, dict.length);\n dat = newDat;\n st.w = dict.length;\n }\n }\n return dflt(dat, opt.level == null ? 6 : opt.level, opt.mem == null ? (st.l ? Math.ceil(Math.max(8, Math.min(13, Math.log(dat.length))) * 1.5) : 20) : (12 + opt.mem), pre, post, st);\n};\n// Walmart object spread\nvar mrg = function (a, b) {\n var o = {};\n for (var k in a)\n o[k] = a[k];\n for (var k in b)\n o[k] = b[k];\n return o;\n};\n// worker clone\n// This is possibly the craziest part of the entire codebase, despite how simple it may seem.\n// The only parameter to this function is a closure that returns an array of variables outside of the function scope.\n// We're going to try to figure out the variable names used in the closure as strings because that is crucial for workerization.\n// We will return an object mapping of true variable name to value (basically, the current scope as a JS object).\n// The reason we can't just use the original variable names is minifiers mangling the toplevel scope.\n// This took me three weeks to figure out how to do.\nvar wcln = function (fn, fnStr, td) {\n var dt = fn();\n var st = fn.toString();\n var ks = st.slice(st.indexOf('[') + 1, st.lastIndexOf(']')).replace(/\\s+/g, '').split(',');\n for (var i = 0; i < dt.length; ++i) {\n var v = dt[i], k = ks[i];\n if (typeof v == 'function') {\n fnStr += ';' + k + '=';\n var st_1 = v.toString();\n if (v.prototype) {\n // for global objects\n if (st_1.indexOf('[native code]') != -1) {\n var spInd = st_1.indexOf(' ', 8) + 1;\n fnStr += st_1.slice(spInd, st_1.indexOf('(', spInd));\n }\n else {\n fnStr += st_1;\n for (var t in v.prototype)\n fnStr += ';' + k + '.prototype.' + t + '=' + v.prototype[t].toString();\n }\n }\n else\n fnStr += st_1;\n }\n else\n td[k] = v;\n }\n return fnStr;\n};\nvar ch = [];\n// clone bufs\nvar cbfs = function (v) {\n var tl = [];\n for (var k in v) {\n if (v[k].buffer) {\n tl.push((v[k] = new v[k].constructor(v[k])).buffer);\n }\n }\n return tl;\n};\n// use a worker to execute code\nvar wrkr = function (fns, init, id, cb) {\n if (!ch[id]) {\n var fnStr = '', td_1 = {}, m = fns.length - 1;\n for (var i = 0; i < m; ++i)\n fnStr = wcln(fns[i], fnStr, td_1);\n ch[id] = { c: wcln(fns[m], fnStr, td_1), e: td_1 };\n }\n var td = mrg({}, ch[id].e);\n return wk(ch[id].c + ';onmessage=function(e){for(var k in e.data)self[k]=e.data[k];onmessage=' + init.toString() + '}', id, td, cbfs(td), cb);\n};\n// base async inflate fn\nvar bInflt = function () { return [u8, u16, i32, fleb, fdeb, clim, fl, fd, flrm, fdrm, rev, ec, hMap, max, bits, bits16, shft, slc, err, inflt, inflateSync, pbf, gopt]; };\nvar bDflt = function () { return [u8, u16, i32, fleb, fdeb, clim, revfl, revfd, flm, flt, fdm, fdt, rev, deo, et, hMap, wbits, wbits16, hTree, ln, lc, clen, wfblk, wblk, shft, slc, dflt, dopt, deflateSync, pbf]; };\n// gzip extra\nvar gze = function () { return [gzh, gzhl, wbytes, crc, crct]; };\n// gunzip extra\nvar guze = function () { return [gzs, gzl]; };\n// zlib extra\nvar zle = function () { return [zlh, wbytes, adler]; };\n// unzlib extra\nvar zule = function () { return [zls]; };\n// post buf\nvar pbf = function (msg) { return postMessage(msg, [msg.buffer]); };\n// get opts\nvar gopt = function (o) { return o && {\n out: o.size && new u8(o.size),\n dictionary: o.dictionary\n}; };\n// async helper\nvar cbify = function (dat, opts, fns, init, id, cb) {\n var w = wrkr(fns, init, id, function (err, dat) {\n w.terminate();\n cb(err, dat);\n });\n w.postMessage([dat, opts], opts.consume ? [dat.buffer] : []);\n return function () { w.terminate(); };\n};\n// auto stream\nvar astrm = function (strm) {\n strm.ondata = function (dat, final) { return postMessage([dat, final], [dat.buffer]); };\n return function (ev) {\n if (ev.data.length) {\n strm.push(ev.data[0], ev.data[1]);\n postMessage([ev.data[0].length]);\n }\n else\n strm.flush();\n };\n};\n// async stream attach\nvar astrmify = function (fns, strm, opts, init, id, flush, ext) {\n var t;\n var w = wrkr(fns, init, id, function (err, dat) {\n if (err)\n w.terminate(), strm.ondata.call(strm, err);\n else if (!Array.isArray(dat))\n ext(dat);\n else if (dat.length == 1) {\n strm.queuedSize -= dat[0];\n if (strm.ondrain)\n strm.ondrain(dat[0]);\n }\n else {\n if (dat[1])\n w.terminate();\n strm.ondata.call(strm, err, dat[0], dat[1]);\n }\n });\n w.postMessage(opts);\n strm.queuedSize = 0;\n strm.push = function (d, f) {\n if (!strm.ondata)\n err(5);\n if (t)\n strm.ondata(err(4, 0, 1), null, !!f);\n strm.queuedSize += d.length;\n w.postMessage([d, t = f], [d.buffer]);\n };\n strm.terminate = function () { w.terminate(); };\n if (flush) {\n strm.flush = function () { w.postMessage([]); };\n }\n};\n// read 2 bytes\nvar b2 = function (d, b) { return d[b] | (d[b + 1] << 8); };\n// read 4 bytes\nvar b4 = function (d, b) { return (d[b] | (d[b + 1] << 8) | (d[b + 2] << 16) | (d[b + 3] << 24)) >>> 0; };\nvar b8 = function (d, b) { return b4(d, b) + (b4(d, b + 4) * 4294967296); };\n// write bytes\nvar wbytes = function (d, b, v) {\n for (; v; ++b)\n d[b] = v, v >>>= 8;\n};\n// gzip header\nvar gzh = function (c, o) {\n var fn = o.filename;\n c[0] = 31, c[1] = 139, c[2] = 8, c[8] = o.level < 2 ? 4 : o.level == 9 ? 2 : 0, c[9] = 3; // assume Unix\n if (o.mtime != 0)\n wbytes(c, 4, Math.floor(new Date(o.mtime || Date.now()) / 1000));\n if (fn) {\n c[3] = 8;\n for (var i = 0; i <= fn.length; ++i)\n c[i + 10] = fn.charCodeAt(i);\n }\n};\n// gzip footer: -8 to -4 = CRC, -4 to -0 is length\n// gzip start\nvar gzs = function (d) {\n if (d[0] != 31 || d[1] != 139 || d[2] != 8)\n err(6, 'invalid gzip data');\n var flg = d[3];\n var st = 10;\n if (flg & 4)\n st += (d[10] | d[11] << 8) + 2;\n for (var zs = (flg >> 3 & 1) + (flg >> 4 & 1); zs > 0; zs -= !d[st++])\n ;\n return st + (flg & 2);\n};\n// gzip length\nvar gzl = function (d) {\n var l = d.length;\n return (d[l - 4] | d[l - 3] << 8 | d[l - 2] << 16 | d[l - 1] << 24) >>> 0;\n};\n// gzip header length\nvar gzhl = function (o) { return 10 + (o.filename ? o.filename.length + 1 : 0); };\n// zlib header\nvar zlh = function (c, o) {\n var lv = o.level, fl = lv == 0 ? 0 : lv < 6 ? 1 : lv == 9 ? 3 : 2;\n c[0] = 120, c[1] = (fl << 6) | (o.dictionary && 32);\n c[1] |= 31 - ((c[0] << 8) | c[1]) % 31;\n if (o.dictionary) {\n var h = adler();\n h.p(o.dictionary);\n wbytes(c, 2, h.d());\n }\n};\n// zlib start\nvar zls = function (d, dict) {\n if ((d[0] & 15) != 8 || (d[0] >> 4) > 7 || ((d[0] << 8 | d[1]) % 31))\n err(6, 'invalid zlib data');\n if ((d[1] >> 5 & 1) == +!dict)\n err(6, 'invalid zlib data: ' + (d[1] & 32 ? 'need' : 'unexpected') + ' dictionary');\n return (d[1] >> 3 & 4) + 2;\n};\nfunction StrmOpt(opts, cb) {\n if (typeof opts == 'function')\n cb = opts, opts = {};\n this.ondata = cb;\n return opts;\n}\n/**\n * Streaming DEFLATE compression\n */\nvar Deflate = /*#__PURE__*/ (function () {\n function Deflate(opts, cb) {\n if (typeof opts == 'function')\n cb = opts, opts = {};\n this.ondata = cb;\n this.o = opts || {};\n this.s = { l: 0, i: 32768, w: 32768, z: 32768 };\n // Buffer length must always be 0 mod 32768 for index calculations to be correct when modifying head and prev\n // 98304 = 32768 (lookback) + 65536 (common chunk size)\n this.b = new u8(98304);\n if (this.o.dictionary) {\n var dict = this.o.dictionary.subarray(-32768);\n this.b.set(dict, 32768 - dict.length);\n this.s.i = 32768 - dict.length;\n }\n }\n Deflate.prototype.p = function (c, f) {\n this.ondata(dopt(c, this.o, 0, 0, this.s), f);\n };\n /**\n * Pushes a chunk to be deflated\n * @param chunk The chunk to push\n * @param final Whether this is the last chunk\n */\n Deflate.prototype.push = function (chunk, final) {\n if (!this.ondata)\n err(5);\n if (this.s.l)\n err(4);\n var endLen = chunk.length + this.s.z;\n if (endLen > this.b.length) {\n if (endLen > 2 * this.b.length - 32768) {\n var newBuf = new u8(endLen & -32768);\n newBuf.set(this.b.subarray(0, this.s.z));\n this.b = newBuf;\n }\n var split = this.b.length - this.s.z;\n this.b.set(chunk.subarray(0, split), this.s.z);\n this.s.z = this.b.length;\n this.p(this.b, false);\n this.b.set(this.b.subarray(-32768));\n this.b.set(chunk.subarray(split), 32768);\n this.s.z = chunk.length - split + 32768;\n this.s.i = 32766, this.s.w = 32768;\n }\n else {\n this.b.set(chunk, this.s.z);\n this.s.z += chunk.length;\n }\n this.s.l = final & 1;\n if (this.s.z > this.s.w + 8191 || final) {\n this.p(this.b, final || false);\n this.s.w = this.s.i, this.s.i -= 2;\n }\n };\n /**\n * Flushes buffered uncompressed data. Useful to immediately retrieve the\n * deflated output for small inputs.\n */\n Deflate.prototype.flush = function () {\n if (!this.ondata)\n err(5);\n if (this.s.l)\n err(4);\n this.p(this.b, false);\n this.s.w = this.s.i, this.s.i -= 2;\n };\n return Deflate;\n}());\nexport { Deflate };\n/**\n * Asynchronous streaming DEFLATE compression\n */\nvar AsyncDeflate = /*#__PURE__*/ (function () {\n function AsyncDeflate(opts, cb) {\n astrmify([\n bDflt,\n function () { return [astrm, Deflate]; }\n ], this, StrmOpt.call(this, opts, cb), function (ev) {\n var strm = new Deflate(ev.data);\n onmessage = astrm(strm);\n }, 6, 1);\n }\n return AsyncDeflate;\n}());\nexport { AsyncDeflate };\nexport function deflate(data, opts, cb) {\n if (!cb)\n cb = opts, opts = {};\n if (typeof cb != 'function')\n err(7);\n return cbify(data, opts, [\n bDflt,\n ], function (ev) { return pbf(deflateSync(ev.data[0], ev.data[1])); }, 0, cb);\n}\n/**\n * Compresses data with DEFLATE without any wrapper\n * @param data The data to compress\n * @param opts The compression options\n * @returns The deflated version of the data\n */\nexport function deflateSync(data, opts) {\n return dopt(data, opts || {}, 0, 0);\n}\n/**\n * Streaming DEFLATE decompression\n */\nvar Inflate = /*#__PURE__*/ (function () {\n function Inflate(opts, cb) {\n // no StrmOpt here to avoid adding to workerizer\n if (typeof opts == 'function')\n cb = opts, opts = {};\n this.ondata = cb;\n var dict = opts && opts.dictionary && opts.dictionary.subarray(-32768);\n this.s = { i: 0, b: dict ? dict.length : 0 };\n this.o = new u8(32768);\n this.p = new u8(0);\n if (dict)\n this.o.set(dict);\n }\n Inflate.prototype.e = function (c) {\n if (!this.ondata)\n err(5);\n if (this.d)\n err(4);\n if (!this.p.length)\n this.p = c;\n else if (c.length) {\n var n = new u8(this.p.length + c.length);\n n.set(this.p), n.set(c, this.p.length), this.p = n;\n }\n };\n Inflate.prototype.c = function (final) {\n this.s.i = +(this.d = final || false);\n var bts = this.s.b;\n var dt = inflt(this.p, this.s, this.o);\n this.ondata(slc(dt, bts, this.s.b), this.d);\n this.o = slc(dt, this.s.b - 32768), this.s.b = this.o.length;\n this.p = slc(this.p, (this.s.p / 8) | 0), this.s.p &= 7;\n };\n /**\n * Pushes a chunk to be inflated\n * @param chunk The chunk to push\n * @param final Whether this is the final chunk\n */\n Inflate.prototype.push = function (chunk, final) {\n this.e(chunk), this.c(final);\n };\n return Inflate;\n}());\nexport { Inflate };\n/**\n * Asynchronous streaming DEFLATE decompression\n */\nvar AsyncInflate = /*#__PURE__*/ (function () {\n function AsyncInflate(opts, cb) {\n astrmify([\n bInflt,\n function () { return [astrm, Inflate]; }\n ], this, StrmOpt.call(this, opts, cb), function (ev) {\n var strm = new Inflate(ev.data);\n onmessage = astrm(strm);\n }, 7, 0);\n }\n return AsyncInflate;\n}());\nexport { AsyncInflate };\nexport function inflate(data, opts, cb) {\n if (!cb)\n cb = opts, opts = {};\n if (typeof cb != 'function')\n err(7);\n return cbify(data, opts, [\n bInflt\n ], function (ev) { return pbf(inflateSync(ev.data[0], gopt(ev.data[1]))); }, 1, cb);\n}\n/**\n * Expands DEFLATE data with no wrapper\n * @param data The data to decompress\n * @param opts The decompression options\n * @returns The decompressed version of the data\n */\nexport function inflateSync(data, opts) {\n return inflt(data, { i: 2 }, opts && opts.out, opts && opts.dictionary);\n}\n// before you yell at me for not just using extends, my reason is that TS inheritance is hard to workerize.\n/**\n * Streaming GZIP compression\n */\nvar Gzip = /*#__PURE__*/ (function () {\n function Gzip(opts, cb) {\n this.c = crc();\n this.l = 0;\n this.v = 1;\n Deflate.call(this, opts, cb);\n }\n /**\n * Pushes a chunk to be GZIPped\n * @param chunk The chunk to push\n * @param final Whether this is the last chunk\n */\n Gzip.prototype.push = function (chunk, final) {\n this.c.p(chunk);\n this.l += chunk.length;\n Deflate.prototype.push.call(this, chunk, final);\n };\n Gzip.prototype.p = function (c, f) {\n var raw = dopt(c, this.o, this.v && gzhl(this.o), f && 8, this.s);\n if (this.v)\n gzh(raw, this.o), this.v = 0;\n if (f)\n wbytes(raw, raw.length - 8, this.c.d()), wbytes(raw, raw.length - 4, this.l);\n this.ondata(raw, f);\n };\n /**\n * Flushes buffered uncompressed data. Useful to immediately retrieve the\n * GZIPped output for small inputs.\n */\n Gzip.prototype.flush = function () {\n Deflate.prototype.flush.call(this);\n };\n return Gzip;\n}());\nexport { Gzip };\n/**\n * Asynchronous streaming GZIP compression\n */\nvar AsyncGzip = /*#__PURE__*/ (function () {\n function AsyncGzip(opts, cb) {\n astrmify([\n bDflt,\n gze,\n function () { return [astrm, Deflate, Gzip]; }\n ], this, StrmOpt.call(this, opts, cb), function (ev) {\n var strm = new Gzip(ev.data);\n onmessage = astrm(strm);\n }, 8, 1);\n }\n return AsyncGzip;\n}());\nexport { AsyncGzip };\nexport function gzip(data, opts, cb) {\n if (!cb)\n cb = opts, opts = {};\n if (typeof cb != 'function')\n err(7);\n return cbify(data, opts, [\n bDflt,\n gze,\n function () { return [gzipSync]; }\n ], function (ev) { return pbf(gzipSync(ev.data[0], ev.data[1])); }, 2, cb);\n}\n/**\n * Compresses data with GZIP\n * @param data The data to compress\n * @param opts The compression options\n * @returns The gzipped version of the data\n */\nexport function gzipSync(data, opts) {\n if (!opts)\n opts = {};\n var c = crc(), l = data.length;\n c.p(data);\n var d = dopt(data, opts, gzhl(opts), 8), s = d.length;\n return gzh(d, opts), wbytes(d, s - 8, c.d()), wbytes(d, s - 4, l), d;\n}\n/**\n * Streaming single or multi-member GZIP decompression\n */\nvar Gunzip = /*#__PURE__*/ (function () {\n function Gunzip(opts, cb) {\n this.v = 1;\n this.r = 0;\n Inflate.call(this, opts, cb);\n }\n /**\n * Pushes a chunk to be GUNZIPped\n * @param chunk The chunk to push\n * @param final Whether this is the last chunk\n */\n Gunzip.prototype.push = function (chunk, final) {\n Inflate.prototype.e.call(this, chunk);\n this.r += chunk.length;\n if (this.v) {\n var p = this.p.subarray(this.v - 1);\n var s = p.length > 3 ? gzs(p) : 4;\n if (s > p.length) {\n if (!final)\n return;\n }\n else if (this.v > 1 && this.onmember) {\n this.onmember(this.r - p.length);\n }\n this.p = p.subarray(s), this.v = 0;\n }\n // necessary to prevent TS from using the closure value\n // This allows for workerization to function correctly\n Inflate.prototype.c.call(this, final);\n // process concatenated GZIP\n if (this.s.f && !this.s.l && !final) {\n this.v = shft(this.s.p) + 9;\n this.s = { i: 0 };\n this.o = new u8(0);\n this.push(new u8(0), final);\n }\n };\n return Gunzip;\n}());\nexport { Gunzip };\n/**\n * Asynchronous streaming single or multi-member GZIP decompression\n */\nvar AsyncGunzip = /*#__PURE__*/ (function () {\n function AsyncGunzip(opts, cb) {\n var _this = this;\n astrmify([\n bInflt,\n guze,\n function () { return [astrm, Inflate, Gunzip]; }\n ], this, StrmOpt.call(this, opts, cb), function (ev) {\n var strm = new Gunzip(ev.data);\n strm.onmember = function (offset) { return postMessage(offset); };\n onmessage = astrm(strm);\n }, 9, 0, function (offset) { return _this.onmember && _this.onmember(offset); });\n }\n return AsyncGunzip;\n}());\nexport { AsyncGunzip };\nexport function gunzip(data, opts, cb) {\n if (!cb)\n cb = opts, opts = {};\n if (typeof cb != 'function')\n err(7);\n return cbify(data, opts, [\n bInflt,\n guze,\n function () { return [gunzipSync]; }\n ], function (ev) { return pbf(gunzipSync(ev.data[0], ev.data[1])); }, 3, cb);\n}\n/**\n * Expands GZIP data\n * @param data The data to decompress\n * @param opts The decompression options\n * @returns The decompressed version of the data\n */\nexport function gunzipSync(data, opts) {\n var st = gzs(data);\n if (st + 8 > data.length)\n err(6, 'invalid gzip data');\n return inflt(data.subarray(st, -8), { i: 2 }, opts && opts.out || new u8(gzl(data)), opts && opts.dictionary);\n}\n/**\n * Streaming Zlib compression\n */\nvar Zlib = /*#__PURE__*/ (function () {\n function Zlib(opts, cb) {\n this.c = adler();\n this.v = 1;\n Deflate.call(this, opts, cb);\n }\n /**\n * Pushes a chunk to be zlibbed\n * @param chunk The chunk to push\n * @param final Whether this is the last chunk\n */\n Zlib.prototype.push = function (chunk, final) {\n this.c.p(chunk);\n Deflate.prototype.push.call(this, chunk, final);\n };\n Zlib.prototype.p = function (c, f) {\n var raw = dopt(c, this.o, this.v && (this.o.dictionary ? 6 : 2), f && 4, this.s);\n if (this.v)\n zlh(raw, this.o), this.v = 0;\n if (f)\n wbytes(raw, raw.length - 4, this.c.d());\n this.ondata(raw, f);\n };\n /**\n * Flushes buffered uncompressed data. Useful to immediately retrieve the\n * zlibbed output for small inputs.\n */\n Zlib.prototype.flush = function () {\n Deflate.prototype.flush.call(this);\n };\n return Zlib;\n}());\nexport { Zlib };\n/**\n * Asynchronous streaming Zlib compression\n */\nvar AsyncZlib = /*#__PURE__*/ (function () {\n function AsyncZlib(opts, cb) {\n astrmify([\n bDflt,\n zle,\n function () { return [astrm, Deflate, Zlib]; }\n ], this, StrmOpt.call(this, opts, cb), function (ev) {\n var strm = new Zlib(ev.data);\n onmessage = astrm(strm);\n }, 10, 1);\n }\n return AsyncZlib;\n}());\nexport { AsyncZlib };\nexport function zlib(data, opts, cb) {\n if (!cb)\n cb = opts, opts = {};\n if (typeof cb != 'function')\n err(7);\n return cbify(data, opts, [\n bDflt,\n zle,\n function () { return [zlibSync]; }\n ], function (ev) { return pbf(zlibSync(ev.data[0], ev.data[1])); }, 4, cb);\n}\n/**\n * Compress data with Zlib\n * @param data The data to compress\n * @param opts The compression options\n * @returns The zlib-compressed version of the data\n */\nexport function zlibSync(data, opts) {\n if (!opts)\n opts = {};\n var a = adler();\n a.p(data);\n var d = dopt(data, opts, opts.dictionary ? 6 : 2, 4);\n return zlh(d, opts), wbytes(d, d.length - 4, a.d()), d;\n}\n/**\n * Streaming Zlib decompression\n */\nvar Unzlib = /*#__PURE__*/ (function () {\n function Unzlib(opts, cb) {\n Inflate.call(this, opts, cb);\n this.v = opts && opts.dictionary ? 2 : 1;\n }\n /**\n * Pushes a chunk to be unzlibbed\n * @param chunk The chunk to push\n * @param final Whether this is the last chunk\n */\n Unzlib.prototype.push = function (chunk, final) {\n Inflate.prototype.e.call(this, chunk);\n if (this.v) {\n if (this.p.length < 6 && !final)\n return;\n this.p = this.p.subarray(zls(this.p, this.v - 1)), this.v = 0;\n }\n if (final) {\n if (this.p.length < 4)\n err(6, 'invalid zlib data');\n this.p = this.p.subarray(0, -4);\n }\n // necessary to prevent TS from using the closure value\n // This allows for workerization to function correctly\n Inflate.prototype.c.call(this, final);\n };\n return Unzlib;\n}());\nexport { Unzlib };\n/**\n * Asynchronous streaming Zlib decompression\n */\nvar AsyncUnzlib = /*#__PURE__*/ (function () {\n function AsyncUnzlib(opts, cb) {\n astrmify([\n bInflt,\n zule,\n function () { return [astrm, Inflate, Unzlib]; }\n ], this, StrmOpt.call(this, opts, cb), function (ev) {\n var strm = new Unzlib(ev.data);\n onmessage = astrm(strm);\n }, 11, 0);\n }\n return AsyncUnzlib;\n}());\nexport { AsyncUnzlib };\nexport function unzlib(data, opts, cb) {\n if (!cb)\n cb = opts, opts = {};\n if (typeof cb != 'function')\n err(7);\n return cbify(data, opts, [\n bInflt,\n zule,\n function () { return [unzlibSync]; }\n ], function (ev) { return pbf(unzlibSync(ev.data[0], gopt(ev.data[1]))); }, 5, cb);\n}\n/**\n * Expands Zlib data\n * @param data The data to decompress\n * @param opts The decompression options\n * @returns The decompressed version of the data\n */\nexport function unzlibSync(data, opts) {\n return inflt(data.subarray(zls(data, opts && opts.dictionary), -4), { i: 2 }, opts && opts.out, opts && opts.dictionary);\n}\n// Default algorithm for compression (used because having a known output size allows faster decompression)\nexport { gzip as compress, AsyncGzip as AsyncCompress };\nexport { gzipSync as compressSync, Gzip as Compress };\n/**\n * Streaming GZIP, Zlib, or raw DEFLATE decompression\n */\nvar Decompress = /*#__PURE__*/ (function () {\n function Decompress(opts, cb) {\n this.o = StrmOpt.call(this, opts, cb) || {};\n this.G = Gunzip;\n this.I = Inflate;\n this.Z = Unzlib;\n }\n // init substream\n // overriden by AsyncDecompress\n Decompress.prototype.i = function () {\n var _this = this;\n this.s.ondata = function (dat, final) {\n _this.ondata(dat, final);\n };\n };\n /**\n * Pushes a chunk to be decompressed\n * @param chunk The chunk to push\n * @param final Whether this is the last chunk\n */\n Decompress.prototype.push = function (chunk, final) {\n if (!this.ondata)\n err(5);\n if (!this.s) {\n if (this.p && this.p.length) {\n var n = new u8(this.p.length + chunk.length);\n n.set(this.p), n.set(chunk, this.p.length);\n }\n else\n this.p = chunk;\n if (this.p.length > 2) {\n this.s = (this.p[0] == 31 && this.p[1] == 139 && this.p[2] == 8)\n ? new this.G(this.o)\n : ((this.p[0] & 15) != 8 || (this.p[0] >> 4) > 7 || ((this.p[0] << 8 | this.p[1]) % 31))\n ? new this.I(this.o)\n : new this.Z(this.o);\n this.i();\n this.s.push(this.p, final);\n this.p = null;\n }\n }\n else\n this.s.push(chunk, final);\n };\n return Decompress;\n}());\nexport { Decompress };\n/**\n * Asynchronous streaming GZIP, Zlib, or raw DEFLATE decompression\n */\nvar AsyncDecompress = /*#__PURE__*/ (function () {\n function AsyncDecompress(opts, cb) {\n Decompress.call(this, opts, cb);\n this.queuedSize = 0;\n this.G = AsyncGunzip;\n this.I = AsyncInflate;\n this.Z = AsyncUnzlib;\n }\n AsyncDecompress.prototype.i = function () {\n var _this = this;\n this.s.ondata = function (err, dat, final) {\n _this.ondata(err, dat, final);\n };\n this.s.ondrain = function (size) {\n _this.queuedSize -= size;\n if (_this.ondrain)\n _this.ondrain(size);\n };\n };\n /**\n * Pushes a chunk to be decompressed\n * @param chunk The chunk to push\n * @param final Whether this is the last chunk\n */\n AsyncDecompress.prototype.push = function (chunk, final) {\n this.queuedSize += chunk.length;\n Decompress.prototype.push.call(this, chunk, final);\n };\n return AsyncDecompress;\n}());\nexport { AsyncDecompress };\nexport function decompress(data, opts, cb) {\n if (!cb)\n cb = opts, opts = {};\n if (typeof cb != 'function')\n err(7);\n return (data[0] == 31 && data[1] == 139 && data[2] == 8)\n ? gunzip(data, opts, cb)\n : ((data[0] & 15) != 8 || (data[0] >> 4) > 7 || ((data[0] << 8 | data[1]) % 31))\n ? inflate(data, opts, cb)\n : unzlib(data, opts, cb);\n}\n/**\n * Expands compressed GZIP, Zlib, or raw DEFLATE data, automatically detecting the format\n * @param data The data to decompress\n * @param opts The decompression options\n * @returns The decompressed version of the data\n */\nexport function decompressSync(data, opts) {\n return (data[0] == 31 && data[1] == 139 && data[2] == 8)\n ? gunzipSync(data, opts)\n : ((data[0] & 15) != 8 || (data[0] >> 4) > 7 || ((data[0] << 8 | data[1]) % 31))\n ? inflateSync(data, opts)\n : unzlibSync(data, opts);\n}\n// flatten a directory structure\nvar fltn = function (d, p, t, o) {\n for (var k in d) {\n var val = d[k], n = p + k, op = o;\n if (Array.isArray(val))\n op = mrg(o, val[1]), val = val[0];\n if (val instanceof u8)\n t[n] = [val, op];\n else {\n t[n += '/'] = [new u8(0), op];\n fltn(val, n, t, o);\n }\n }\n};\n// text encoder\nvar te = typeof TextEncoder != 'undefined' && /*#__PURE__*/ new TextEncoder();\n// text decoder\nvar td = typeof TextDecoder != 'undefined' && /*#__PURE__*/ new TextDecoder();\n// text decoder stream\nvar tds = 0;\ntry {\n td.decode(et, { stream: true });\n tds = 1;\n}\ncatch (e) { }\n// decode UTF8\nvar dutf8 = function (d) {\n for (var r = '', i = 0;;) {\n var c = d[i++];\n var eb = (c > 127) + (c > 223) + (c > 239);\n if (i + eb > d.length)\n return { s: r, r: slc(d, i - 1) };\n if (!eb)\n r += String.fromCharCode(c);\n else if (eb == 3) {\n c = ((c & 15) << 18 | (d[i++] & 63) << 12 | (d[i++] & 63) << 6 | (d[i++] & 63)) - 65536,\n r += String.fromCharCode(55296 | (c >> 10), 56320 | (c & 1023));\n }\n else if (eb & 1)\n r += String.fromCharCode((c & 31) << 6 | (d[i++] & 63));\n else\n r += String.fromCharCode((c & 15) << 12 | (d[i++] & 63) << 6 | (d[i++] & 63));\n }\n};\n/**\n * Streaming UTF-8 decoding\n */\nvar DecodeUTF8 = /*#__PURE__*/ (function () {\n /**\n * Creates a UTF-8 decoding stream\n * @param cb The callback to call whenever data is decoded\n */\n function DecodeUTF8(cb) {\n this.ondata = cb;\n if (tds)\n this.t = new TextDecoder();\n else\n this.p = et;\n }\n /**\n * Pushes a chunk to be decoded from UTF-8 binary\n * @param chunk The chunk to push\n * @param final Whether this is the last chunk\n */\n DecodeUTF8.prototype.push = function (chunk, final) {\n if (!this.ondata)\n err(5);\n final = !!final;\n if (this.t) {\n this.ondata(this.t.decode(chunk, { stream: true }), final);\n if (final) {\n if (this.t.decode().length)\n err(8);\n this.t = null;\n }\n return;\n }\n if (!this.p)\n err(4);\n var dat = new u8(this.p.length + chunk.length);\n dat.set(this.p);\n dat.set(chunk, this.p.length);\n var _a = dutf8(dat), s = _a.s, r = _a.r;\n if (final) {\n if (r.length)\n err(8);\n this.p = null;\n }\n else\n this.p = r;\n this.ondata(s, final);\n };\n return DecodeUTF8;\n}());\nexport { DecodeUTF8 };\n/**\n * Streaming UTF-8 encoding\n */\nvar EncodeUTF8 = /*#__PURE__*/ (function () {\n /**\n * Creates a UTF-8 decoding stream\n * @param cb The callback to call whenever data is encoded\n */\n function EncodeUTF8(cb) {\n this.ondata = cb;\n }\n /**\n * Pushes a chunk to be encoded to UTF-8\n * @param chunk The string data to push\n * @param final Whether this is the last chunk\n */\n EncodeUTF8.prototype.push = function (chunk, final) {\n if (!this.ondata)\n err(5);\n if (this.d)\n err(4);\n this.ondata(strToU8(chunk), this.d = final || false);\n };\n return EncodeUTF8;\n}());\nexport { EncodeUTF8 };\n/**\n * Converts a string into a Uint8Array for use with compression/decompression methods\n * @param str The string to encode\n * @param latin1 Whether or not to interpret the data as Latin-1. This should\n * not need to be true unless decoding a binary string.\n * @returns The string encoded in UTF-8/Latin-1 binary\n */\nexport function strToU8(str, latin1) {\n if (latin1) {\n var ar_1 = new u8(str.length);\n for (var i = 0; i < str.length; ++i)\n ar_1[i] = str.charCodeAt(i);\n return ar_1;\n }\n if (te)\n return te.encode(str);\n var l = str.length;\n var ar = new u8(str.length + (str.length >> 1));\n var ai = 0;\n var w = function (v) { ar[ai++] = v; };\n for (var i = 0; i < l; ++i) {\n if (ai + 5 > ar.length) {\n var n = new u8(ai + 8 + ((l - i) << 1));\n n.set(ar);\n ar = n;\n }\n var c = str.charCodeAt(i);\n if (c < 128 || latin1)\n w(c);\n else if (c < 2048)\n w(192 | (c >> 6)), w(128 | (c & 63));\n else if (c > 55295 && c < 57344)\n c = 65536 + (c & 1023 << 10) | (str.charCodeAt(++i) & 1023),\n w(240 | (c >> 18)), w(128 | ((c >> 12) & 63)), w(128 | ((c >> 6) & 63)), w(128 | (c & 63));\n else\n w(224 | (c >> 12)), w(128 | ((c >> 6) & 63)), w(128 | (c & 63));\n }\n return slc(ar, 0, ai);\n}\n/**\n * Converts a Uint8Array to a string\n * @param dat The data to decode to string\n * @param latin1 Whether or not to interpret the data as Latin-1. This should\n * not need to be true unless encoding to binary string.\n * @returns The original UTF-8/Latin-1 string\n */\nexport function strFromU8(dat, latin1) {\n if (latin1) {\n var r = '';\n for (var i = 0; i < dat.length; i += 16384)\n r += String.fromCharCode.apply(null, dat.subarray(i, i + 16384));\n return r;\n }\n else if (td) {\n return td.decode(dat);\n }\n else {\n var _a = dutf8(dat), s = _a.s, r = _a.r;\n if (r.length)\n err(8);\n return s;\n }\n}\n;\n// deflate bit flag\nvar dbf = function (l) { return l == 1 ? 3 : l < 6 ? 2 : l == 9 ? 1 : 0; };\n// skip local zip header\nvar slzh = function (d, b) { return b + 30 + b2(d, b + 26) + b2(d, b + 28); };\n// read zip header\nvar zh = function (d, b, z) {\n var fnl = b2(d, b + 28), fn = strFromU8(d.subarray(b + 46, b + 46 + fnl), !(b2(d, b + 8) & 2048)), es = b + 46 + fnl, bs = b4(d, b + 20);\n var _a = z && bs == 4294967295 ? z64e(d, es) : [bs, b4(d, b + 24), b4(d, b + 42)], sc = _a[0], su = _a[1], off = _a[2];\n return [b2(d, b + 10), sc, su, fn, es + b2(d, b + 30) + b2(d, b + 32), off];\n};\n// read zip64 extra field\nvar z64e = function (d, b) {\n for (; b2(d, b) != 1; b += 4 + b2(d, b + 2))\n ;\n return [b8(d, b + 12), b8(d, b + 4), b8(d, b + 20)];\n};\n// extra field length\nvar exfl = function (ex) {\n var le = 0;\n if (ex) {\n for (var k in ex) {\n var l = ex[k].length;\n if (l > 65535)\n err(9);\n le += l + 4;\n }\n }\n return le;\n};\n// write zip header\nvar wzh = function (d, b, f, fn, u, c, ce, co) {\n var fl = fn.length, ex = f.extra, col = co && co.length;\n var exl = exfl(ex);\n wbytes(d, b, ce != null ? 0x2014B50 : 0x4034B50), b += 4;\n if (ce != null)\n d[b++] = 20, d[b++] = f.os;\n d[b] = 20, b += 2; // spec compliance? what's that?\n d[b++] = (f.flag << 1) | (c < 0 && 8), d[b++] = u && 8;\n d[b++] = f.compression & 255, d[b++] = f.compression >> 8;\n var dt = new Date(f.mtime == null ? Date.now() : f.mtime), y = dt.getFullYear() - 1980;\n if (y < 0 || y > 119)\n err(10);\n wbytes(d, b, (y << 25) | ((dt.getMonth() + 1) << 21) | (dt.getDate() << 16) | (dt.getHours() << 11) | (dt.getMinutes() << 5) | (dt.getSeconds() >> 1)), b += 4;\n if (c != -1) {\n wbytes(d, b, f.crc);\n wbytes(d, b + 4, c < 0 ? -c - 2 : c);\n wbytes(d, b + 8, f.size);\n }\n wbytes(d, b + 12, fl);\n wbytes(d, b + 14, exl), b += 16;\n if (ce != null) {\n wbytes(d, b, col);\n wbytes(d, b + 6, f.attrs);\n wbytes(d, b + 10, ce), b += 14;\n }\n d.set(fn, b);\n b += fl;\n if (exl) {\n for (var k in ex) {\n var exf = ex[k], l = exf.length;\n wbytes(d, b, +k);\n wbytes(d, b + 2, l);\n d.set(exf, b + 4), b += 4 + l;\n }\n }\n if (col)\n d.set(co, b), b += col;\n return b;\n};\n// write zip footer (end of central directory)\nvar wzf = function (o, b, c, d, e) {\n wbytes(o, b, 0x6054B50); // skip disk\n wbytes(o, b + 8, c);\n wbytes(o, b + 10, c);\n wbytes(o, b + 12, d);\n wbytes(o, b + 16, e);\n};\n/**\n * A pass-through stream to keep data uncompressed in a ZIP archive.\n */\nvar ZipPassThrough = /*#__PURE__*/ (function () {\n /**\n * Creates a pass-through stream that can be added to ZIP archives\n * @param filename The filename to associate with this data stream\n */\n function ZipPassThrough(filename) {\n this.filename = filename;\n this.c = crc();\n this.size = 0;\n this.compression = 0;\n }\n /**\n * Processes a chunk and pushes to the output stream. You can override this\n * method in a subclass for custom behavior, but by default this passes\n * the data through. You must call this.ondata(err, chunk, final) at some\n * point in this method.\n * @param chunk The chunk to process\n * @param final Whether this is the last chunk\n */\n ZipPassThrough.prototype.process = function (chunk, final) {\n this.ondata(null, chunk, final);\n };\n /**\n * Pushes a chunk to be added. If you are subclassing this with a custom\n * compression algorithm, note that you must push data from the source\n * file only, pre-compression.\n * @param chunk The chunk to push\n * @param final Whether this is the last chunk\n */\n ZipPassThrough.prototype.push = function (chunk, final) {\n if (!this.ondata)\n err(5);\n this.c.p(chunk);\n this.size += chunk.length;\n if (final)\n this.crc = this.c.d();\n this.process(chunk, final || false);\n };\n return ZipPassThrough;\n}());\nexport { ZipPassThrough };\n// I don't extend because TypeScript extension adds 1kB of runtime bloat\n/**\n * Streaming DEFLATE compression for ZIP archives. Prefer using AsyncZipDeflate\n * for better performance\n */\nvar ZipDeflate = /*#__PURE__*/ (function () {\n /**\n * Creates a DEFLATE stream that can be added to ZIP archives\n * @param filename The filename to associate with this data stream\n * @param opts The compression options\n */\n function ZipDeflate(filename, opts) {\n var _this = this;\n if (!opts)\n opts = {};\n ZipPassThrough.call(this, filename);\n this.d = new Deflate(opts, function (dat, final) {\n _this.ondata(null, dat, final);\n });\n this.compression = 8;\n this.flag = dbf(opts.level);\n }\n ZipDeflate.prototype.process = function (chunk, final) {\n try {\n this.d.push(chunk, final);\n }\n catch (e) {\n this.ondata(e, null, final);\n }\n };\n /**\n * Pushes a chunk to be deflated\n * @param chunk The chunk to push\n * @param final Whether this is the last chunk\n */\n ZipDeflate.prototype.push = function (chunk, final) {\n ZipPassThrough.prototype.push.call(this, chunk, final);\n };\n return ZipDeflate;\n}());\nexport { ZipDeflate };\n/**\n * Asynchronous streaming DEFLATE compression for ZIP archives\n */\nvar AsyncZipDeflate = /*#__PURE__*/ (function () {\n /**\n * Creates an asynchronous DEFLATE stream that can be added to ZIP archives\n * @param filename The filename to associate with this data stream\n * @param opts The compression options\n */\n function AsyncZipDeflate(filename, opts) {\n var _this = this;\n if (!opts)\n opts = {};\n ZipPassThrough.call(this, filename);\n this.d = new AsyncDeflate(opts, function (err, dat, final) {\n _this.ondata(err, dat, final);\n });\n this.compression = 8;\n this.flag = dbf(opts.level);\n this.terminate = this.d.terminate;\n }\n AsyncZipDeflate.prototype.process = function (chunk, final) {\n this.d.push(chunk, final);\n };\n /**\n * Pushes a chunk to be deflated\n * @param chunk The chunk to push\n * @param final Whether this is the last chunk\n */\n AsyncZipDeflate.prototype.push = function (chunk, final) {\n ZipPassThrough.prototype.push.call(this, chunk, final);\n };\n return AsyncZipDeflate;\n}());\nexport { AsyncZipDeflate };\n// TODO: Better tree shaking\n/**\n * A zippable archive to which files can incrementally be added\n */\nvar Zip = /*#__PURE__*/ (function () {\n /**\n * Creates an empty ZIP archive to which files can be added\n * @param cb The callback to call whenever data for the generated ZIP archive\n * is available\n */\n function Zip(cb) {\n this.ondata = cb;\n this.u = [];\n this.d = 1;\n }\n /**\n * Adds a file to the ZIP archive\n * @param file The file stream to add\n */\n Zip.prototype.add = function (file) {\n var _this = this;\n if (!this.ondata)\n err(5);\n // finishing or finished\n if (this.d & 2)\n this.ondata(err(4 + (this.d & 1) * 8, 0, 1), null, false);\n else {\n var f = strToU8(file.filename), fl_1 = f.length;\n var com = file.comment, o = com && strToU8(com);\n var u = fl_1 != file.filename.length || (o && (com.length != o.length));\n var hl_1 = fl_1 + exfl(file.extra) + 30;\n if (fl_1 > 65535)\n this.ondata(err(11, 0, 1), null, false);\n var header = new u8(hl_1);\n wzh(header, 0, file, f, u, -1);\n var chks_1 = [header];\n var pAll_1 = function () {\n for (var _i = 0, chks_2 = chks_1; _i < chks_2.length; _i++) {\n var chk = chks_2[_i];\n _this.ondata(null, chk, false);\n }\n chks_1 = [];\n };\n var tr_1 = this.d;\n this.d = 0;\n var ind_1 = this.u.length;\n var uf_1 = mrg(file, {\n f: f,\n u: u,\n o: o,\n t: function () {\n if (file.terminate)\n file.terminate();\n },\n r: function () {\n pAll_1();\n if (tr_1) {\n var nxt = _this.u[ind_1 + 1];\n if (nxt)\n nxt.r();\n else\n _this.d = 1;\n }\n tr_1 = 1;\n }\n });\n var cl_1 = 0;\n file.ondata = function (err, dat, final) {\n if (err) {\n _this.ondata(err, dat, final);\n _this.terminate();\n }\n else {\n cl_1 += dat.length;\n chks_1.push(dat);\n if (final) {\n var dd = new u8(16);\n wbytes(dd, 0, 0x8074B50);\n wbytes(dd, 4, file.crc);\n wbytes(dd, 8, cl_1);\n wbytes(dd, 12, file.size);\n chks_1.push(dd);\n uf_1.c = cl_1, uf_1.b = hl_1 + cl_1 + 16, uf_1.crc = file.crc, uf_1.size = file.size;\n if (tr_1)\n uf_1.r();\n tr_1 = 1;\n }\n else if (tr_1)\n pAll_1();\n }\n };\n this.u.push(uf_1);\n }\n };\n /**\n * Ends the process of adding files and prepares to emit the final chunks.\n * This *must* be called after adding all desired files for the resulting\n * ZIP file to work properly.\n */\n Zip.prototype.end = function () {\n var _this = this;\n if (this.d & 2) {\n this.ondata(err(4 + (this.d & 1) * 8, 0, 1), null, true);\n return;\n }\n if (this.d)\n this.e();\n else\n this.u.push({\n r: function () {\n if (!(_this.d & 1))\n return;\n _this.u.splice(-1, 1);\n _this.e();\n },\n t: function () { }\n });\n this.d = 3;\n };\n Zip.prototype.e = function () {\n var bt = 0, l = 0, tl = 0;\n for (var _i = 0, _a = this.u; _i < _a.length; _i++) {\n var f = _a[_i];\n tl += 46 + f.f.length + exfl(f.extra) + (f.o ? f.o.length : 0);\n }\n var out = new u8(tl + 22);\n for (var _b = 0, _c = this.u; _b < _c.length; _b++) {\n var f = _c[_b];\n wzh(out, bt, f, f.f, f.u, -f.c - 2, l, f.o);\n bt += 46 + f.f.length + exfl(f.extra) + (f.o ? f.o.length : 0), l += f.b;\n }\n wzf(out, bt, this.u.length, tl, l);\n this.ondata(null, out, true);\n this.d = 2;\n };\n /**\n * A method to terminate any internal workers used by the stream. Subsequent\n * calls to add() will fail.\n */\n Zip.prototype.terminate = function () {\n for (var _i = 0, _a = this.u; _i < _a.length; _i++) {\n var f = _a[_i];\n f.t();\n }\n this.d = 2;\n };\n return Zip;\n}());\nexport { Zip };\nexport function zip(data, opts, cb) {\n if (!cb)\n cb = opts, opts = {};\n if (typeof cb != 'function')\n err(7);\n var r = {};\n fltn(data, '', r, opts);\n var k = Object.keys(r);\n var lft = k.length, o = 0, tot = 0;\n var slft = lft, files = new Array(lft);\n var term = [];\n var tAll = function () {\n for (var i = 0; i < term.length; ++i)\n term[i]();\n };\n var cbd = function (a, b) {\n mt(function () { cb(a, b); });\n };\n mt(function () { cbd = cb; });\n var cbf = function () {\n var out = new u8(tot + 22), oe = o, cdl = tot - o;\n tot = 0;\n for (var i = 0; i < slft; ++i) {\n var f = files[i];\n try {\n var l = f.c.length;\n wzh(out, tot, f, f.f, f.u, l);\n var badd = 30 + f.f.length + exfl(f.extra);\n var loc = tot + badd;\n out.set(f.c, loc);\n wzh(out, o, f, f.f, f.u, l, tot, f.m), o += 16 + badd + (f.m ? f.m.length : 0), tot = loc + l;\n }\n catch (e) {\n return cbd(e, null);\n }\n }\n wzf(out, o, files.length, cdl, oe);\n cbd(null, out);\n };\n if (!lft)\n cbf();\n var _loop_1 = function (i) {\n var fn = k[i];\n var _a = r[fn], file = _a[0], p = _a[1];\n var c = crc(), size = file.length;\n c.p(file);\n var f = strToU8(fn), s = f.length;\n var com = p.comment, m = com && strToU8(com), ms = m && m.length;\n var exl = exfl(p.extra);\n var compression = p.level == 0 ? 0 : 8;\n var cbl = function (e, d) {\n if (e) {\n tAll();\n cbd(e, null);\n }\n else {\n var l = d.length;\n files[i] = mrg(p, {\n size: size,\n crc: c.d(),\n c: d,\n f: f,\n m: m,\n u: s != fn.length || (m && (com.length != ms)),\n compression: compression\n });\n o += 30 + s + exl + l;\n tot += 76 + 2 * (s + exl) + (ms || 0) + l;\n if (!--lft)\n cbf();\n }\n };\n if (s > 65535)\n cbl(err(11, 0, 1), null);\n if (!compression)\n cbl(null, file);\n else if (size < 160000) {\n try {\n cbl(null, deflateSync(file, p));\n }\n catch (e) {\n cbl(e, null);\n }\n }\n else\n term.push(deflate(file, p, cbl));\n };\n // Cannot use lft because it can decrease\n for (var i = 0; i < slft; ++i) {\n _loop_1(i);\n }\n return tAll;\n}\n/**\n * Synchronously creates a ZIP file. Prefer using `zip` for better performance\n * with more than one file.\n * @param data The directory structure for the ZIP archive\n * @param opts The main options, merged with per-file options\n * @returns The generated ZIP archive\n */\nexport function zipSync(data, opts) {\n if (!opts)\n opts = {};\n var r = {};\n var files = [];\n fltn(data, '', r, opts);\n var o = 0;\n var tot = 0;\n for (var fn in r) {\n var _a = r[fn], file = _a[0], p = _a[1];\n var compression = p.level == 0 ? 0 : 8;\n var f = strToU8(fn), s = f.length;\n var com = p.comment, m = com && strToU8(com), ms = m && m.length;\n var exl = exfl(p.extra);\n if (s > 65535)\n err(11);\n var d = compression ? deflateSync(file, p) : file, l = d.length;\n var c = crc();\n c.p(file);\n files.push(mrg(p, {\n size: file.length,\n crc: c.d(),\n c: d,\n f: f,\n m: m,\n u: s != fn.length || (m && (com.length != ms)),\n o: o,\n compression: compression\n }));\n o += 30 + s + exl + l;\n tot += 76 + 2 * (s + exl) + (ms || 0) + l;\n }\n var out = new u8(tot + 22), oe = o, cdl = tot - o;\n for (var i = 0; i < files.length; ++i) {\n var f = files[i];\n wzh(out, f.o, f, f.f, f.u, f.c.length);\n var badd = 30 + f.f.length + exfl(f.extra);\n out.set(f.c, f.o + badd);\n wzh(out, o, f, f.f, f.u, f.c.length, f.o, f.m), o += 16 + badd + (f.m ? f.m.length : 0);\n }\n wzf(out, o, files.length, cdl, oe);\n return out;\n}\n/**\n * Streaming pass-through decompression for ZIP archives\n */\nvar UnzipPassThrough = /*#__PURE__*/ (function () {\n function UnzipPassThrough() {\n }\n UnzipPassThrough.prototype.push = function (data, final) {\n this.ondata(null, data, final);\n };\n UnzipPassThrough.compression = 0;\n return UnzipPassThrough;\n}());\nexport { UnzipPassThrough };\n/**\n * Streaming DEFLATE decompression for ZIP archives. Prefer AsyncZipInflate for\n * better performance.\n */\nvar UnzipInflate = /*#__PURE__*/ (function () {\n /**\n * Creates a DEFLATE decompression that can be used in ZIP archives\n */\n function UnzipInflate() {\n var _this = this;\n this.i = new Inflate(function (dat, final) {\n _this.ondata(null, dat, final);\n });\n }\n UnzipInflate.prototype.push = function (data, final) {\n try {\n this.i.push(data, final);\n }\n catch (e) {\n this.ondata(e, null, final);\n }\n };\n UnzipInflate.compression = 8;\n return UnzipInflate;\n}());\nexport { UnzipInflate };\n/**\n * Asynchronous streaming DEFLATE decompression for ZIP archives\n */\nvar AsyncUnzipInflate = /*#__PURE__*/ (function () {\n /**\n * Creates a DEFLATE decompression that can be used in ZIP archives\n */\n function AsyncUnzipInflate(_, sz) {\n var _this = this;\n if (sz < 320000) {\n this.i = new Inflate(function (dat, final) {\n _this.ondata(null, dat, final);\n });\n }\n else {\n this.i = new AsyncInflate(function (err, dat, final) {\n _this.ondata(err, dat, final);\n });\n this.terminate = this.i.terminate;\n }\n }\n AsyncUnzipInflate.prototype.push = function (data, final) {\n if (this.i.terminate)\n data = slc(data, 0);\n this.i.push(data, final);\n };\n AsyncUnzipInflate.compression = 8;\n return AsyncUnzipInflate;\n}());\nexport { AsyncUnzipInflate };\n/**\n * A ZIP archive decompression stream that emits files as they are discovered\n */\nvar Unzip = /*#__PURE__*/ (function () {\n /**\n * Creates a ZIP decompression stream\n * @param cb The callback to call whenever a file in the ZIP archive is found\n */\n function Unzip(cb) {\n this.onfile = cb;\n this.k = [];\n this.o = {\n 0: UnzipPassThrough\n };\n this.p = et;\n }\n /**\n * Pushes a chunk to be unzipped\n * @param chunk The chunk to push\n * @param final Whether this is the last chunk\n */\n Unzip.prototype.push = function (chunk, final) {\n var _this = this;\n if (!this.onfile)\n err(5);\n if (!this.p)\n err(4);\n if (this.c > 0) {\n var len = Math.min(this.c, chunk.length);\n var toAdd = chunk.subarray(0, len);\n this.c -= len;\n if (this.d)\n this.d.push(toAdd, !this.c);\n else\n this.k[0].push(toAdd);\n chunk = chunk.subarray(len);\n if (chunk.length)\n return this.push(chunk, final);\n }\n else {\n var f = 0, i = 0, is = void 0, buf = void 0;\n if (!this.p.length)\n buf = chunk;\n else if (!chunk.length)\n buf = this.p;\n else {\n buf = new u8(this.p.length + chunk.length);\n buf.set(this.p), buf.set(chunk, this.p.length);\n }\n var l = buf.length, oc = this.c, add = oc && this.d;\n var _loop_2 = function () {\n var _a;\n var sig = b4(buf, i);\n if (sig == 0x4034B50) {\n f = 1, is = i;\n this_1.d = null;\n this_1.c = 0;\n var bf = b2(buf, i + 6), cmp_1 = b2(buf, i + 8), u = bf & 2048, dd = bf & 8, fnl = b2(buf, i + 26), es = b2(buf, i + 28);\n if (l > i + 30 + fnl + es) {\n var chks_3 = [];\n this_1.k.unshift(chks_3);\n f = 2;\n var sc_1 = b4(buf, i + 18), su_1 = b4(buf, i + 22);\n var fn_1 = strFromU8(buf.subarray(i + 30, i += 30 + fnl), !u);\n if (sc_1 == 4294967295) {\n _a = dd ? [-2] : z64e(buf, i), sc_1 = _a[0], su_1 = _a[1];\n }\n else if (dd)\n sc_1 = -1;\n i += es;\n this_1.c = sc_1;\n var d_1;\n var file_1 = {\n name: fn_1,\n compression: cmp_1,\n start: function () {\n if (!file_1.ondata)\n err(5);\n if (!sc_1)\n file_1.ondata(null, et, true);\n else {\n var ctr = _this.o[cmp_1];\n if (!ctr)\n file_1.ondata(err(14, 'unknown compression type ' + cmp_1, 1), null, false);\n d_1 = sc_1 < 0 ? new ctr(fn_1) : new ctr(fn_1, sc_1, su_1);\n d_1.ondata = function (err, dat, final) { file_1.ondata(err, dat, final); };\n for (var _i = 0, chks_4 = chks_3; _i < chks_4.length; _i++) {\n var dat = chks_4[_i];\n d_1.push(dat, false);\n }\n if (_this.k[0] == chks_3 && _this.c)\n _this.d = d_1;\n else\n d_1.push(et, true);\n }\n },\n terminate: function () {\n if (d_1 && d_1.terminate)\n d_1.terminate();\n }\n };\n if (sc_1 >= 0)\n file_1.size = sc_1, file_1.originalSize = su_1;\n this_1.onfile(file_1);\n }\n return \"break\";\n }\n else if (oc) {\n if (sig == 0x8074B50) {\n is = i += 12 + (oc == -2 && 8), f = 3, this_1.c = 0;\n return \"break\";\n }\n else if (sig == 0x2014B50) {\n is = i -= 4, f = 3, this_1.c = 0;\n return \"break\";\n }\n }\n };\n var this_1 = this;\n for (; i < l - 4; ++i) {\n var state_1 = _loop_2();\n if (state_1 === \"break\")\n break;\n }\n this.p = et;\n if (oc < 0) {\n var dat = f ? buf.subarray(0, is - 12 - (oc == -2 && 8) - (b4(buf, is - 16) == 0x8074B50 && 4)) : buf.subarray(0, i);\n if (add)\n add.push(dat, !!f);\n else\n this.k[+(f == 2)].push(dat);\n }\n if (f & 2)\n return this.push(buf.subarray(i), final);\n this.p = buf.subarray(i);\n }\n if (final) {\n if (this.c)\n err(13);\n this.p = null;\n }\n };\n /**\n * Registers a decoder with the stream, allowing for files compressed with\n * the compression type provided to be expanded correctly\n * @param decoder The decoder constructor\n */\n Unzip.prototype.register = function (decoder) {\n this.o[decoder.compression] = decoder;\n };\n return Unzip;\n}());\nexport { Unzip };\nvar mt = typeof queueMicrotask == 'function' ? queueMicrotask : typeof setTimeout == 'function' ? setTimeout : function (fn) { fn(); };\nexport function unzip(data, opts, cb) {\n if (!cb)\n cb = opts, opts = {};\n if (typeof cb != 'function')\n err(7);\n var term = [];\n var tAll = function () {\n for (var i = 0; i < term.length; ++i)\n term[i]();\n };\n var files = {};\n var cbd = function (a, b) {\n mt(function () { cb(a, b); });\n };\n mt(function () { cbd = cb; });\n var e = data.length - 22;\n for (; b4(data, e) != 0x6054B50; --e) {\n if (!e || data.length - e > 65558) {\n cbd(err(13, 0, 1), null);\n return tAll;\n }\n }\n ;\n var lft = b2(data, e + 8);\n if (lft) {\n var c = lft;\n var o = b4(data, e + 16);\n var z = o == 4294967295 || c == 65535;\n if (z) {\n var ze = b4(data, e - 12);\n z = b4(data, ze) == 0x6064B50;\n if (z) {\n c = lft = b4(data, ze + 32);\n o = b4(data, ze + 48);\n }\n }\n var fltr = opts && opts.filter;\n var _loop_3 = function (i) {\n var _a = zh(data, o, z), c_1 = _a[0], sc = _a[1], su = _a[2], fn = _a[3], no = _a[4], off = _a[5], b = slzh(data, off);\n o = no;\n var cbl = function (e, d) {\n if (e) {\n tAll();\n cbd(e, null);\n }\n else {\n if (d)\n files[fn] = d;\n if (!--lft)\n cbd(null, files);\n }\n };\n if (!fltr || fltr({\n name: fn,\n size: sc,\n originalSize: su,\n compression: c_1\n })) {\n if (!c_1)\n cbl(null, slc(data, b, b + sc));\n else if (c_1 == 8) {\n var infl = data.subarray(b, b + sc);\n // Synchronously decompress under 512KB, or barely-compressed data\n if (su < 524288 || sc > 0.8 * su) {\n try {\n cbl(null, inflateSync(infl, { out: new u8(su) }));\n }\n catch (e) {\n cbl(e, null);\n }\n }\n else\n term.push(inflate(infl, { size: su }, cbl));\n }\n else\n cbl(err(14, 'unknown compression type ' + c_1, 1), null);\n }\n else\n cbl(null, null);\n };\n for (var i = 0; i < c; ++i) {\n _loop_3(i);\n }\n }\n else\n cbd(null, {});\n return tAll;\n}\n/**\n * Synchronously decompresses a ZIP archive. Prefer using `unzip` for better\n * performance with more than one file.\n * @param data The raw compressed ZIP file\n * @param opts The ZIP extraction options\n * @returns The decompressed files\n */\nexport function unzipSync(data, opts) {\n var files = {};\n var e = data.length - 22;\n for (; b4(data, e) != 0x6054B50; --e) {\n if (!e || data.length - e > 65558)\n err(13);\n }\n ;\n var c = b2(data, e + 8);\n if (!c)\n return {};\n var o = b4(data, e + 16);\n var z = o == 4294967295 || c == 65535;\n if (z) {\n var ze = b4(data, e - 12);\n z = b4(data, ze) == 0x6064B50;\n if (z) {\n c = b4(data, ze + 32);\n o = b4(data, ze + 48);\n }\n }\n var fltr = opts && opts.filter;\n for (var i = 0; i < c; ++i) {\n var _a = zh(data, o, z), c_2 = _a[0], sc = _a[1], su = _a[2], fn = _a[3], no = _a[4], off = _a[5], b = slzh(data, off);\n o = no;\n if (!fltr || fltr({\n name: fn,\n size: sc,\n originalSize: su,\n compression: c_2\n })) {\n if (!c_2)\n files[fn] = slc(data, b, b + sc);\n else if (c_2 == 8)\n files[fn] = inflateSync(data.subarray(b, b + sc), { out: new u8(su) });\n else\n err(14, 'unknown compression type ' + c_2);\n }\n }\n return files;\n}\n","import { Zip, ZipPassThrough } from 'fflate';\r\n\r\n/**\r\n * Streaming ZIP writer that assembles files into a ZIP archive on the fly.\r\n * Uses fflate's store mode (no compression) for maximum speed and minimal memory usage.\r\n * Works in both Node.js and browser environments.\r\n */\r\nexport class StreamingZipWriter {\r\n private zip: InstanceType<typeof Zip>;\r\n private currentFile: InstanceType<typeof ZipPassThrough> | null = null;\r\n private onData: (chunk: Uint8Array) => void | Promise<void>;\r\n private finalized = false;\r\n private pendingWrites: Promise<void> = Promise.resolve();\r\n\r\n constructor(onData: (chunk: Uint8Array) => void | Promise<void>) {\r\n this.onData = onData;\r\n this.zip = new Zip((err, data, _final) => {\r\n if (err) throw err;\r\n // Queue data delivery to handle async consumers\r\n this.pendingWrites = this.pendingWrites.then(() => this.onData(data));\r\n });\r\n }\r\n\r\n /**\r\n * Begin a new file entry in the ZIP.\r\n * Must call endFile() before starting another file.\r\n * @param name - Filename within the ZIP archive.\r\n */\r\n startFile(name: string): void {\r\n if (this.currentFile) {\r\n throw new Error('Must call endFile() before starting a new file.');\r\n }\r\n if (this.finalized) {\r\n throw new Error('ZIP has already been finalized.');\r\n }\r\n const entry = new ZipPassThrough(name);\r\n this.zip.add(entry);\r\n this.currentFile = entry;\r\n }\r\n\r\n /**\r\n * Write a chunk of data to the current file entry.\r\n * @param data - The data chunk to write.\r\n */\r\n writeChunk(data: Uint8Array): void {\r\n if (!this.currentFile) {\r\n throw new Error('No file started. Call startFile() first.');\r\n }\r\n this.currentFile.push(data, false);\r\n }\r\n\r\n /**\r\n * End the current file entry.\r\n */\r\n endFile(): void {\r\n if (!this.currentFile) {\r\n throw new Error('No file to end.');\r\n }\r\n this.currentFile.push(new Uint8Array(0), true);\r\n this.currentFile = null;\r\n }\r\n\r\n /**\r\n * Finalize the ZIP archive. Must be called after all files are written.\r\n * Waits for all pending async writes to complete before resolving.\r\n */\r\n async finalize(): Promise<void> {\r\n if (this.currentFile) {\r\n throw new Error('Cannot finalize with an open file. Call endFile() first.');\r\n }\r\n if (this.finalized) return;\r\n this.finalized = true;\r\n this.zip.end();\r\n // Wait for all queued onData callbacks to complete\r\n await this.pendingWrites;\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","/**\r\n * P2P Protocol Definitions\r\n *\r\n * This file defines the application-level protocol for P2P file transfers.\r\n * Protocol version 2 introduces:\r\n * - Explicit handshake with version negotiation\r\n * - Chunk-level acknowledgments for flow control\r\n * - Multiple end-ack retries for reliability\r\n * - Resume capability support\r\n *\r\n * Protocol version 3 introduces:\r\n * - Multi-file transfers via file_list, file_end, file_end_ack messages\r\n * - Sequential file-by-file transfer within a single session\r\n */\r\n\r\n// Protocol version for forward compatibility\r\nexport const P2P_PROTOCOL_VERSION = 3;\r\n\r\n/**\r\n * All possible P2P message types.\r\n */\r\nexport type P2PMessageType =\r\n | 'hello' // Initial handshake with protocol version\r\n | 'file_list' // v3: List of files in multi-file transfer\r\n | 'meta' // File metadata (name, size, mime)\r\n | 'ready' // Receiver is ready to receive\r\n | 'chunk' // Data chunk with sequence number\r\n | 'chunk_ack' // Chunk acknowledgment (for flow control)\r\n | 'file_end' // v3: Current file fully sent\r\n | 'file_end_ack' // v3: Current file receipt confirmed\r\n | 'end' // All chunks/files sent\r\n | 'end_ack' // Transfer verified complete\r\n | 'ping' // Heartbeat\r\n | 'pong' // Heartbeat response\r\n | 'error' // Error occurred\r\n | 'cancelled' // User cancelled\r\n | 'resume' // Request to resume from offset\r\n | 'resume_ack'; // Resume position confirmed\r\n\r\n/**\r\n * Base interface for all P2P messages.\r\n */\r\nexport interface P2PMessageBase {\r\n t: P2PMessageType;\r\n}\r\n\r\n/**\r\n * Initial handshake message exchanged between sender and receiver.\r\n */\r\nexport interface P2PHelloMessage extends P2PMessageBase {\r\n t: 'hello';\r\n protocolVersion: number;\r\n sessionId: string;\r\n}\r\n\r\n/**\r\n * v3: File list sent by sender after handshake for multi-file transfers.\r\n */\r\nexport interface P2PFileListMessage extends P2PMessageBase {\r\n t: 'file_list';\r\n fileCount: number;\r\n files: Array<{ name: string; size: number; mime: string }>;\r\n totalSize: number;\r\n}\r\n\r\n/**\r\n * File metadata sent by sender after handshake (single file)\r\n * or before each file's chunks in multi-file mode.\r\n */\r\nexport interface P2PMetaMessage extends P2PMessageBase {\r\n t: 'meta';\r\n sessionId: string;\r\n name: string;\r\n size: number;\r\n mime: string;\r\n /** v3: File index within the file list (0-based). Absent for single-file transfers. */\r\n fileIndex?: number;\r\n}\r\n\r\n/**\r\n * Receiver signals readiness to receive data.\r\n */\r\nexport interface P2PReadyMessage extends P2PMessageBase {\r\n t: 'ready';\r\n}\r\n\r\n/**\r\n * Chunk header sent before binary data.\r\n * The actual binary data follows immediately after this message.\r\n */\r\nexport interface P2PChunkMessage extends P2PMessageBase {\r\n t: 'chunk';\r\n seq: number; // Sequence number for ordering/ack\r\n offset: number; // Byte offset in file\r\n size: number; // Size of this chunk\r\n total: number; // Total file size\r\n}\r\n\r\n/**\r\n * Acknowledgment for a received chunk.\r\n */\r\nexport interface P2PChunkAckMessage extends P2PMessageBase {\r\n t: 'chunk_ack';\r\n seq: number; // Acknowledged sequence number\r\n received: number; // Total bytes received so far\r\n}\r\n\r\n/**\r\n * v3: Sender signals all chunks for the current file have been sent.\r\n */\r\nexport interface P2PFileEndMessage extends P2PMessageBase {\r\n t: 'file_end';\r\n fileIndex: number;\r\n attempt?: number;\r\n}\r\n\r\n/**\r\n * v3: Receiver confirms current file receipt.\r\n */\r\nexport interface P2PFileEndAckMessage extends P2PMessageBase {\r\n t: 'file_end_ack';\r\n fileIndex: number;\r\n received: number;\r\n size: number;\r\n}\r\n\r\n/**\r\n * Sender signals all chunks have been sent.\r\n */\r\nexport interface P2PEndMessage extends P2PMessageBase {\r\n t: 'end';\r\n attempt?: number; // Retry attempt number\r\n}\r\n\r\n/**\r\n * Receiver confirms transfer completion.\r\n */\r\nexport interface P2PEndAckMessage extends P2PMessageBase {\r\n t: 'end_ack';\r\n received: number;\r\n total: number;\r\n}\r\n\r\n/**\r\n * Heartbeat ping.\r\n */\r\nexport interface P2PPingMessage extends P2PMessageBase {\r\n t: 'ping';\r\n timestamp: number;\r\n}\r\n\r\n/**\r\n * Heartbeat pong response.\r\n */\r\nexport interface P2PPongMessage extends P2PMessageBase {\r\n t: 'pong';\r\n timestamp: number;\r\n}\r\n\r\n/**\r\n * Error message sent when something goes wrong.\r\n */\r\nexport interface P2PErrorMessage extends P2PMessageBase {\r\n t: 'error';\r\n message: string;\r\n code?: string;\r\n}\r\n\r\n/**\r\n * Cancellation message sent when user cancels transfer.\r\n */\r\nexport interface P2PCancelledMessage extends P2PMessageBase {\r\n t: 'cancelled';\r\n reason?: string;\r\n}\r\n\r\n/**\r\n * Resume request sent by receiver to continue interrupted transfer.\r\n */\r\nexport interface P2PResumeMessage extends P2PMessageBase {\r\n t: 'resume';\r\n sessionId: string;\r\n receivedBytes: number;\r\n}\r\n\r\n/**\r\n * Resume acknowledgment from sender.\r\n */\r\nexport interface P2PResumeAckMessage extends P2PMessageBase {\r\n t: 'resume_ack';\r\n resumeFromOffset: number;\r\n accepted: boolean;\r\n}\r\n\r\n/**\r\n * Union type of all possible P2P messages.\r\n */\r\nexport type P2PMessage =\r\n | P2PHelloMessage\r\n | P2PFileListMessage\r\n | P2PMetaMessage\r\n | P2PReadyMessage\r\n | P2PChunkMessage\r\n | P2PChunkAckMessage\r\n | P2PFileEndMessage\r\n | P2PFileEndAckMessage\r\n | P2PEndMessage\r\n | P2PEndAckMessage\r\n | P2PPingMessage\r\n | P2PPongMessage\r\n | P2PErrorMessage\r\n | P2PCancelledMessage\r\n | P2PResumeMessage\r\n | P2PResumeAckMessage;\r\n\r\n/**\r\n * Type guard to check if a value is a valid P2P message.\r\n */\r\nexport function isP2PMessage(value: unknown): value is P2PMessage {\r\n if (!value || typeof value !== 'object') return false;\r\n const msg = value as Record<string, unknown>;\r\n return typeof msg.t === 'string' && [\r\n 'hello', 'file_list', 'meta', 'ready', 'chunk', 'chunk_ack',\r\n 'file_end', 'file_end_ack', 'end', 'end_ack', 'ping', 'pong',\r\n 'error', 'cancelled', 'resume', 'resume_ack'\r\n ].includes(msg.t);\r\n}\r\n\r\n/**\r\n * Check if protocol versions are compatible.\r\n */\r\nexport function isProtocolCompatible(\r\n senderVersion: number,\r\n receiverVersion: number\r\n): boolean {\r\n return senderVersion === receiverVersion;\r\n}\r\n\r\n/**\r\n * Default chunk size for P2P transfers (64KB).\r\n * Smaller than standard upload to reduce latency and improve flow control.\r\n */\r\nexport const P2P_CHUNK_SIZE = 64 * 1024;\r\n\r\n/**\r\n * Default maximum unacknowledged chunks before sender pauses.\r\n * This creates backpressure when receiver is slow.\r\n */\r\nexport const P2P_MAX_UNACKED_CHUNKS = 32;\r\n\r\n/**\r\n * Default timeout for waiting on end acknowledgment (ms).\r\n */\r\nexport const P2P_END_ACK_TIMEOUT_MS = 15000;\r\n\r\n/**\r\n * Number of times to retry sending end message.\r\n */\r\nexport const P2P_END_ACK_RETRIES = 3;\r\n\r\n/**\r\n * Delay between multiple end_ack sends from receiver (ms).\r\n */\r\nexport const P2P_END_ACK_RETRY_DELAY_MS = 100;\r\n\r\n/**\r\n * Grace period after connection close before declaring failure (ms).\r\n * Allows for brief reconnection attempts.\r\n */\r\nexport const P2P_CLOSE_GRACE_PERIOD_MS = 2000;\r\n","import { DropgateValidationError, DropgateNetworkError } from '../errors.js';\r\nimport { sleep } from '../utils/network.js';\r\nimport type {\r\n P2PSendOptions,\r\n P2PSendSession,\r\n P2PSendState,\r\n DataConnection,\r\n P2PConnectionHealthEvent,\r\n} from './types.js';\r\nimport { generateP2PCode } from './utils.js';\r\nimport { buildPeerOptions, createPeerWithRetries, resolvePeerConfig } from './helpers.js';\r\nimport type { FileSource } from '../types.js';\r\nimport {\r\n P2P_PROTOCOL_VERSION,\r\n P2P_CHUNK_SIZE,\r\n P2P_MAX_UNACKED_CHUNKS,\r\n P2P_END_ACK_TIMEOUT_MS,\r\n P2P_END_ACK_RETRIES,\r\n P2P_CLOSE_GRACE_PERIOD_MS,\r\n isP2PMessage,\r\n type P2PChunkAckMessage,\r\n type P2PEndAckMessage,\r\n type P2PFileEndAckMessage,\r\n} from './protocol.js';\r\n\r\n// Timeout for detecting stalled receivers that stop sending acks\r\nconst P2P_UNACKED_CHUNK_TIMEOUT_MS = 30000;\r\n\r\n/**\r\n * Generate a unique session ID for transfer tracking.\r\n */\r\nfunction generateSessionId(): string {\r\n return crypto.randomUUID();\r\n}\r\n\r\n/**\r\n * Allowed state transitions to prevent invalid state changes.\r\n * This enforces a strict state machine where transitions only happen\r\n * in the expected order, preventing race conditions.\r\n */\r\nconst ALLOWED_TRANSITIONS: Record<P2PSendState, P2PSendState[]> = {\r\n initializing: ['listening', 'closed'],\r\n listening: ['handshaking', 'closed', 'cancelled'],\r\n handshaking: ['negotiating', 'closed', 'cancelled'],\r\n negotiating: ['transferring', 'closed', 'cancelled'],\r\n transferring: ['finishing', 'closed', 'cancelled'],\r\n finishing: ['awaiting_ack', 'closed', 'cancelled'],\r\n awaiting_ack: ['completed', 'closed', 'cancelled'],\r\n completed: ['closed'],\r\n cancelled: ['closed'],\r\n closed: [],\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 * Protocol v2 features:\r\n * - Explicit version handshake\r\n * - Chunk-level acknowledgments for flow control\r\n * - Multiple end-ack retries for reliability\r\n * - Stream-through design for unlimited file sizes\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 = P2P_CHUNK_SIZE,\r\n endAckTimeoutMs = P2P_END_ACK_TIMEOUT_MS,\r\n bufferHighWaterMark = 8 * 1024 * 1024,\r\n bufferLowWaterMark = 2 * 1024 * 1024,\r\n heartbeatIntervalMs = 5000,\r\n chunkAcknowledgments = true,\r\n maxUnackedChunks = P2P_MAX_UNACKED_CHUNKS,\r\n onCode,\r\n onStatus,\r\n onProgress,\r\n onComplete,\r\n onError,\r\n onDisconnect,\r\n onCancel,\r\n onConnectionHealth,\r\n } = opts;\r\n\r\n // Normalize to files array\r\n const files: FileSource[] = Array.isArray(file) ? file : [file];\r\n const isMultiFile = files.length > 1;\r\n const totalSize = files.reduce((sum, f) => sum + f.size, 0);\r\n\r\n // Validate required options\r\n if (!files.length) {\r\n throw new DropgateValidationError('At least one file is required.');\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 let healthCheckTimer: ReturnType<typeof setInterval> | null = null;\r\n let lastActivityTime = Date.now();\r\n\r\n // Chunk acknowledgment tracking\r\n const unackedChunks = new Map<number, { offset: number; size: number; sentAt: number }>();\r\n let nextSeq = 0;\r\n let ackResolvers: Array<() => void> = [];\r\n\r\n // Track if transfer ever started to prevent connection replacement attacks\r\n let transferEverStarted = false;\r\n\r\n // Security: Connection rate limiting to prevent DoS attacks\r\n const connectionAttempts: number[] = []; // Timestamps of recent connection attempts\r\n const MAX_CONNECTION_ATTEMPTS = 10; // Max attempts allowed\r\n const CONNECTION_RATE_WINDOW_MS = 10000; // 10 second sliding window\r\n\r\n /**\r\n * Attempt a state transition. Returns true if transition was valid.\r\n * Logs a warning for invalid transitions but doesn't throw.\r\n */\r\n const transitionTo = (newState: P2PSendState): boolean => {\r\n if (!ALLOWED_TRANSITIONS[state].includes(newState)) {\r\n console.warn(`[P2P Send] Invalid state transition: ${state} -> ${newState}`);\r\n return false;\r\n }\r\n state = newState;\r\n return true;\r\n };\r\n\r\n const reportProgress = (data: { received: number; total: number }): void => {\r\n if (isStopped()) return;\r\n const safeTotal =\r\n Number.isFinite(data.total) && data.total > 0 ? data.total : totalSize;\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 or cancellation\r\n const safeError = (err: Error): void => {\r\n if (state === 'closed' || state === 'completed' || state === 'cancelled') return;\r\n transitionTo('closed');\r\n onError?.(err);\r\n cleanup();\r\n };\r\n\r\n // Safe complete handler - only fires from awaiting_ack state\r\n const safeComplete = (): void => {\r\n if (state !== 'awaiting_ack' && state !== 'finishing') return;\r\n transitionTo('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 // Clear health check timer\r\n if (healthCheckTimer) {\r\n clearInterval(healthCheckTimer);\r\n healthCheckTimer = null;\r\n }\r\n\r\n // Clear any pending ack resolvers\r\n ackResolvers.forEach((resolve) => resolve());\r\n ackResolvers = [];\r\n unackedChunks.clear();\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' || state === 'cancelled') return;\r\n\r\n // If already completed, just cleanup without callbacks\r\n if (state === 'completed') {\r\n cleanup();\r\n return;\r\n }\r\n\r\n const wasActive = state === 'transferring' || state === 'finishing' || state === 'awaiting_ack';\r\n transitionTo('cancelled');\r\n\r\n // Notify peer before cleanup\r\n try {\r\n // @ts-expect-error - open property may exist on PeerJS connections\r\n if (activeConn && activeConn.open) {\r\n activeConn.send({ t: 'cancelled', message: 'Sender cancelled the transfer.' });\r\n }\r\n } catch {\r\n // Best effort\r\n }\r\n\r\n if (wasActive && onCancel) {\r\n onCancel({ cancelledBy: 'sender' });\r\n }\r\n\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' || state === 'cancelled';\r\n\r\n // Connection health monitoring\r\n const startHealthMonitoring = (conn: DataConnection): void => {\r\n if (!onConnectionHealth) return;\r\n\r\n healthCheckTimer = setInterval(() => {\r\n if (isStopped()) return;\r\n const dc = conn._dc;\r\n if (!dc) return;\r\n\r\n // Note: iceConnectionState is on RTCPeerConnection, not RTCDataChannel\r\n // We can only report bufferedAmount and readyState from the data channel\r\n const health: P2PConnectionHealthEvent = {\r\n iceConnectionState: (dc.readyState === 'open' ? 'connected' : 'disconnected') as P2PConnectionHealthEvent['iceConnectionState'],\r\n bufferedAmount: dc.bufferedAmount,\r\n lastActivityMs: Date.now() - lastActivityTime,\r\n };\r\n\r\n onConnectionHealth(health);\r\n }, 2000);\r\n };\r\n\r\n // Handle chunk acknowledgment\r\n const handleChunkAck = (msg: P2PChunkAckMessage): void => {\r\n lastActivityTime = Date.now();\r\n unackedChunks.delete(msg.seq);\r\n reportProgress({ received: msg.received, total: totalSize });\r\n\r\n // Resolve any pending waitForAck promises\r\n const resolver = ackResolvers.shift();\r\n if (resolver) resolver();\r\n };\r\n\r\n // Wait for chunk acknowledgment when too many unacked\r\n const waitForAck = (): Promise<void> => {\r\n return new Promise((resolve) => {\r\n ackResolvers.push(resolve);\r\n });\r\n };\r\n\r\n // Send chunk with sequence tracking\r\n const sendChunk = async (conn: DataConnection, data: ArrayBuffer, offset: number, fileTotal?: number): Promise<void> => {\r\n // Wait if too many unacknowledged chunks (flow control)\r\n if (chunkAcknowledgments) {\r\n while (unackedChunks.size >= maxUnackedChunks) {\r\n // Security: Check for stale unacked chunks (receiver stopped responding)\r\n const now = Date.now();\r\n for (const [_seq, chunk] of unackedChunks) {\r\n if (now - chunk.sentAt > P2P_UNACKED_CHUNK_TIMEOUT_MS) {\r\n throw new DropgateNetworkError('Receiver stopped acknowledging chunks');\r\n }\r\n }\r\n\r\n await Promise.race([\r\n waitForAck(),\r\n sleep(1000), // Timeout to prevent deadlock\r\n ]);\r\n if (isStopped()) return;\r\n }\r\n }\r\n\r\n const seq = nextSeq++;\r\n if (chunkAcknowledgments) {\r\n unackedChunks.set(seq, { offset, size: data.byteLength, sentAt: Date.now() });\r\n }\r\n\r\n // Send chunk header then binary data\r\n conn.send({ t: 'chunk', seq, offset, size: data.byteLength, total: fileTotal ?? totalSize });\r\n conn.send(data);\r\n sentBytes += data.byteLength;\r\n\r\n // Buffer-based flow control using data channel thresholds\r\n const dc = conn._dc;\r\n if (dc && bufferHighWaterMark > 0) {\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 if (isStopped()) return;\r\n }\r\n }\r\n };\r\n\r\n // Robust end-ack with retries\r\n const waitForEndAck = async (\r\n conn: DataConnection,\r\n ackPromise: Promise<P2PEndAckMessage>\r\n ): Promise<P2PEndAckMessage> => {\r\n const baseTimeout = endAckTimeoutMs;\r\n\r\n for (let attempt = 0; attempt < P2P_END_ACK_RETRIES; attempt++) {\r\n conn.send({ t: 'end', attempt });\r\n\r\n const timeout = baseTimeout * Math.pow(1.5, attempt);\r\n const result = await Promise.race([\r\n ackPromise,\r\n sleep(timeout).then(() => null as P2PEndAckMessage | null),\r\n ]);\r\n\r\n if (result && result.t === 'end_ack') {\r\n return result;\r\n }\r\n\r\n // Check if connection is still alive\r\n if (isStopped()) {\r\n throw new DropgateNetworkError('Connection closed during completion.');\r\n }\r\n }\r\n\r\n throw new DropgateNetworkError('Receiver did not confirm completion after retries.');\r\n };\r\n\r\n peer.on('connection', (conn: DataConnection) => {\r\n if (isStopped()) return;\r\n\r\n // Security: Connection rate limiting\r\n const now = Date.now();\r\n // Remove old attempts outside the sliding window\r\n while (connectionAttempts.length > 0 && connectionAttempts[0] < now - CONNECTION_RATE_WINDOW_MS) {\r\n connectionAttempts.shift();\r\n }\r\n // Check if we've exceeded the rate limit\r\n if (connectionAttempts.length >= MAX_CONNECTION_ATTEMPTS) {\r\n console.warn('[P2P Send] Connection rate limit exceeded, rejecting connection');\r\n try {\r\n conn.send({ t: 'error', message: 'Too many connection attempts. Please wait.' });\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 connectionAttempts.push(now);\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\r\n try {\r\n activeConn.close();\r\n } catch {\r\n // Ignore\r\n }\r\n activeConn = null;\r\n\r\n // Security: Never allow reconnection if transfer ever started\r\n // This prevents race condition attacks where receiver disconnects briefly\r\n // and reconnects to restart transfer and corrupt data\r\n if (transferEverStarted) {\r\n try {\r\n conn.send({ t: 'error', message: 'Transfer already started with another receiver. Cannot reconnect.' });\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 // Reset state to allow new transfer (only if never started transferring)\r\n state = 'listening';\r\n sentBytes = 0;\r\n nextSeq = 0;\r\n unackedChunks.clear();\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 transitionTo('handshaking');\r\n if (!isStopped()) onStatus?.({ phase: 'connected', message: 'Receiver connected.' });\r\n lastActivityTime = Date.now();\r\n\r\n let helloResolve: ((version: number) => void) | null = null;\r\n let readyResolve: (() => void) | null = null;\r\n let endAckResolve: ((msg: P2PEndAckMessage) => void) | null = null;\r\n let fileEndAckResolve: ((msg: P2PFileEndAckMessage) => void) | null = null;\r\n\r\n const helloPromise = new Promise<number>((resolve) => {\r\n helloResolve = resolve;\r\n });\r\n\r\n const readyPromise = new Promise<void>((resolve) => {\r\n readyResolve = resolve;\r\n });\r\n\r\n const endAckPromise = new Promise<P2PEndAckMessage>((resolve) => {\r\n endAckResolve = resolve;\r\n });\r\n\r\n conn.on('data', (data: unknown) => {\r\n lastActivityTime = Date.now();\r\n\r\n // Handle binary data (we don't expect binary from receiver)\r\n if (data instanceof ArrayBuffer || ArrayBuffer.isView(data)) {\r\n return;\r\n }\r\n\r\n if (!isP2PMessage(data)) return;\r\n\r\n const msg = data;\r\n\r\n switch (msg.t) {\r\n case 'hello':\r\n helloResolve?.(msg.protocolVersion);\r\n break;\r\n\r\n case 'ready':\r\n if (!isStopped()) onStatus?.({ phase: 'transferring', message: 'Receiver accepted. Starting transfer...' });\r\n readyResolve?.();\r\n break;\r\n\r\n case 'chunk_ack':\r\n handleChunkAck(msg as P2PChunkAckMessage);\r\n break;\r\n\r\n case 'file_end_ack':\r\n fileEndAckResolve?.(msg as P2PFileEndAckMessage);\r\n break;\r\n\r\n case 'end_ack':\r\n endAckResolve?.(msg as P2PEndAckMessage);\r\n break;\r\n\r\n case 'pong':\r\n // Heartbeat response received, connection is alive\r\n break;\r\n\r\n case 'error':\r\n safeError(new DropgateNetworkError(msg.message || 'Receiver reported an error.'));\r\n break;\r\n\r\n case 'cancelled':\r\n if (state === 'cancelled' || state === 'closed' || state === 'completed') return;\r\n transitionTo('cancelled');\r\n onCancel?.({ cancelledBy: 'receiver', message: msg.reason });\r\n cleanup();\r\n break;\r\n }\r\n });\r\n\r\n conn.on('open', async () => {\r\n try {\r\n if (isStopped()) return;\r\n\r\n // Start health monitoring\r\n startHealthMonitoring(conn);\r\n\r\n // Protocol v2: Send hello first\r\n conn.send({\r\n t: 'hello',\r\n protocolVersion: P2P_PROTOCOL_VERSION,\r\n sessionId,\r\n });\r\n\r\n // Wait for receiver's hello (with timeout)\r\n const receiverVersion = await Promise.race([\r\n helloPromise,\r\n sleep(10000).then(() => null as number | null),\r\n ]);\r\n\r\n if (isStopped()) return;\r\n\r\n if (receiverVersion === null) {\r\n throw new DropgateNetworkError('Receiver did not respond to handshake.');\r\n } else if (receiverVersion !== P2P_PROTOCOL_VERSION) {\r\n throw new DropgateNetworkError(\r\n `Protocol version mismatch: sender v${P2P_PROTOCOL_VERSION}, receiver v${receiverVersion}`\r\n );\r\n }\r\n\r\n transitionTo('negotiating');\r\n if (!isStopped()) onStatus?.({ phase: 'waiting', message: 'Connected. Waiting for receiver to accept...' });\r\n\r\n // v3: Send file_list for multi-file transfers\r\n if (isMultiFile) {\r\n conn.send({\r\n t: 'file_list',\r\n fileCount: files.length,\r\n files: files.map(f => ({ name: f.name, size: f.size, mime: f.type || 'application/octet-stream' })),\r\n totalSize,\r\n });\r\n }\r\n\r\n // Send metadata for the first file (or the only file)\r\n conn.send({\r\n t: 'meta',\r\n sessionId,\r\n name: files[0].name,\r\n size: files[0].size,\r\n mime: files[0].type || 'application/octet-stream',\r\n ...(isMultiFile ? { fileIndex: 0 } : {}),\r\n });\r\n\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' || state === 'awaiting_ack') {\r\n try {\r\n conn.send({ t: 'ping', timestamp: Date.now() });\r\n } catch {\r\n // Ignore ping errors\r\n }\r\n }\r\n }, heartbeatIntervalMs);\r\n }\r\n\r\n transitionTo('transferring');\r\n transferEverStarted = true; // Security: Mark that transfer has started\r\n\r\n let overallSentBytes = 0;\r\n\r\n // Send file(s) in chunks\r\n for (let fi = 0; fi < files.length; fi++) {\r\n const currentFile = files[fi];\r\n\r\n // For multi-file (after first file), send meta for subsequent files\r\n if (isMultiFile && fi > 0) {\r\n conn.send({\r\n t: 'meta',\r\n sessionId,\r\n name: currentFile.name,\r\n size: currentFile.size,\r\n mime: currentFile.type || 'application/octet-stream',\r\n fileIndex: fi,\r\n });\r\n }\r\n\r\n // Send this file's chunks\r\n for (let offset = 0; offset < currentFile.size; offset += chunkSize) {\r\n if (isStopped()) return;\r\n\r\n const slice = currentFile.slice(offset, offset + chunkSize);\r\n const buf = await slice.arrayBuffer();\r\n if (isStopped()) return;\r\n\r\n await sendChunk(conn, buf, offset, currentFile.size);\r\n overallSentBytes += buf.byteLength;\r\n reportProgress({ received: overallSentBytes, total: totalSize });\r\n }\r\n\r\n if (isStopped()) return;\r\n\r\n // For multi-file: send file_end and wait for file_end_ack\r\n if (isMultiFile) {\r\n const fileEndAckPromise = new Promise<P2PFileEndAckMessage>((resolve) => {\r\n fileEndAckResolve = resolve;\r\n });\r\n\r\n conn.send({ t: 'file_end', fileIndex: fi });\r\n\r\n const feAck = await Promise.race([\r\n fileEndAckPromise,\r\n sleep(endAckTimeoutMs).then(() => null as P2PFileEndAckMessage | null),\r\n ]);\r\n\r\n if (isStopped()) return;\r\n\r\n if (!feAck) {\r\n throw new DropgateNetworkError(`Receiver did not confirm receipt of file ${fi + 1}/${files.length}.`);\r\n }\r\n }\r\n }\r\n\r\n if (isStopped()) return;\r\n\r\n transitionTo('finishing');\r\n transitionTo('awaiting_ack');\r\n\r\n // Wait for end acknowledgment with retries\r\n const ackResult = await waitForEndAck(conn, endAckPromise);\r\n\r\n if (isStopped()) return;\r\n\r\n const ackTotal = Number(ackResult.total) || totalSize;\r\n const ackReceived = Number(ackResult.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' || state === 'cancelled') {\r\n // Clean shutdown or already cancelled, ensure full cleanup\r\n cleanup();\r\n return;\r\n }\r\n\r\n // Special handling for awaiting_ack state - give grace period\r\n if (state === 'awaiting_ack') {\r\n // Connection closed while waiting for end_ack\r\n // Give a grace period for the ack to have been processed\r\n setTimeout(() => {\r\n if (state === 'awaiting_ack') {\r\n // Still waiting, treat as failure\r\n safeError(new DropgateNetworkError('Connection closed while awaiting confirmation.'));\r\n }\r\n }, P2P_CLOSE_GRACE_PERIOD_MS);\r\n return;\r\n }\r\n\r\n if (state === 'transferring' || state === 'finishing') {\r\n // Connection closed during active transfer — the receiver either cancelled\r\n // or disconnected. Treat as a receiver-initiated cancellation so the UI\r\n // can reset cleanly instead of showing a raw error.\r\n transitionTo('cancelled');\r\n onCancel?.({ cancelledBy: 'receiver' });\r\n cleanup();\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 nextSeq = 0;\r\n unackedChunks.clear();\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 { sleep } from '../utils/network.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\nimport {\r\n P2P_PROTOCOL_VERSION,\r\n P2P_END_ACK_RETRY_DELAY_MS,\r\n isP2PMessage,\r\n type P2PChunkMessage,\r\n type P2PFileListMessage,\r\n} from './protocol.js';\r\n\r\n/**\r\n * Allowed state transitions to prevent invalid state changes.\r\n */\r\nconst ALLOWED_TRANSITIONS: Record<P2PReceiveState, P2PReceiveState[]> = {\r\n initializing: ['connecting', 'closed'],\r\n connecting: ['handshaking', 'closed', 'cancelled'],\r\n handshaking: ['negotiating', 'closed', 'cancelled'],\r\n negotiating: ['transferring', 'closed', 'cancelled'],\r\n transferring: ['completed', 'closed', 'cancelled'],\r\n completed: ['closed'],\r\n cancelled: ['closed'],\r\n closed: [],\r\n};\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 * Protocol v2 features:\r\n * - Explicit version handshake\r\n * - Chunk-level acknowledgments for flow control\r\n * - Multiple end-ack sends for reliability\r\n * - Stream-through design for unlimited file sizes\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 onFileStart,\r\n onFileEnd,\r\n onComplete,\r\n onError,\r\n onDisconnect,\r\n onCancel,\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 writeQueue = Promise.resolve();\r\n let watchdogTimer: ReturnType<typeof setTimeout> | null = null;\r\n let activeConn: DataConnection | null = null;\r\n\r\n let pendingChunk: P2PChunkMessage | null = null;\r\n\r\n // Multi-file tracking (v3)\r\n let fileList: P2PFileListMessage | null = null;\r\n let currentFileReceived = 0;\r\n let totalReceivedAllFiles = 0;\r\n\r\n // Security: Chunk sequence validation\r\n let expectedChunkSeq = 0;\r\n\r\n // Security: Write queue depth limiting to prevent memory exhaustion\r\n let writeQueueDepth = 0;\r\n const MAX_WRITE_QUEUE_DEPTH = 100;\r\n\r\n // Security: Maximum file count for multi-file transfers\r\n const MAX_FILE_COUNT = 10000;\r\n\r\n /**\r\n * Attempt a state transition. Returns true if transition was valid.\r\n */\r\n const transitionTo = (newState: P2PReceiveState): boolean => {\r\n if (!ALLOWED_TRANSITIONS[state].includes(newState)) {\r\n console.warn(`[P2P Receive] Invalid state transition: ${state} -> ${newState}`);\r\n return false;\r\n }\r\n state = newState;\r\n return true;\r\n };\r\n\r\n // Helper to check if session is stopped\r\n const isStopped = (): boolean => state === 'closed' || state === 'cancelled';\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 or cancellation\r\n const safeError = (err: Error): void => {\r\n if (state === 'closed' || state === 'completed' || state === 'cancelled') return;\r\n transitionTo('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 transitionTo('completed');\r\n onComplete?.(completeData);\r\n // Don't immediately cleanup - let acks be sent first\r\n // The sender will close the connection after receiving ack\r\n // Our close handler will call cleanup when that happens\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' || state === 'cancelled') return;\r\n\r\n // If already completed, just cleanup without callbacks\r\n if (state === 'completed') {\r\n cleanup();\r\n return;\r\n }\r\n\r\n const wasActive = state === 'transferring';\r\n transitionTo('cancelled');\r\n\r\n // Notify peer before cleanup\r\n try {\r\n // @ts-expect-error - open property may exist on PeerJS connections\r\n if (activeConn && activeConn.open) {\r\n activeConn.send({ t: 'cancelled', reason: 'Receiver cancelled the transfer.' });\r\n }\r\n } catch {\r\n // Best effort\r\n }\r\n\r\n if (wasActive && onCancel) {\r\n onCancel({ cancelledBy: 'receiver' });\r\n }\r\n\r\n cleanup();\r\n };\r\n\r\n // Send chunk acknowledgment\r\n const sendChunkAck = (conn: DataConnection, seq: number): void => {\r\n try {\r\n conn.send({ t: 'chunk_ack', seq, received });\r\n } catch {\r\n // Ignore send errors\r\n }\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 transitionTo('connecting');\r\n const conn = peer.connect(normalizedCode, { reliable: true });\r\n activeConn = conn;\r\n\r\n conn.on('open', () => {\r\n transitionTo('handshaking');\r\n onStatus?.({ phase: 'connected', message: 'Connected.' });\r\n\r\n // Send our hello immediately\r\n conn.send({\r\n t: 'hello',\r\n protocolVersion: P2P_PROTOCOL_VERSION,\r\n sessionId: '',\r\n });\r\n });\r\n\r\n conn.on('data', async (data: unknown) => {\r\n try {\r\n // Note: Watchdog is reset only on actual binary data, not control messages\r\n // This prevents attackers from keeping connections alive with just pings\r\n\r\n // Handle binary data - this is file content\r\n if (data instanceof ArrayBuffer || ArrayBuffer.isView(data) ||\r\n (typeof Blob !== 'undefined' && data instanceof Blob)) {\r\n\r\n // CRITICAL SECURITY: Only accept binary data if we're in 'transferring' state\r\n // This ensures the receiver has:\r\n // 1. Received file metadata (meta message)\r\n // 2. Consented to receive (sent 'ready' signal)\r\n // Without this check, a malicious sender could force data onto the receiver\r\n if (state !== 'transferring') {\r\n throw new DropgateValidationError(\r\n 'Received binary data before transfer was accepted. Possible malicious sender.'\r\n );\r\n }\r\n\r\n // Security: Only reset watchdog on actual binary data (prevents keep-alive attacks)\r\n resetWatchdog();\r\n\r\n // Security: Check write queue depth to prevent memory exhaustion\r\n if (writeQueueDepth >= MAX_WRITE_QUEUE_DEPTH) {\r\n throw new DropgateNetworkError('Write queue overflow - receiver cannot keep up');\r\n }\r\n\r\n // Process the binary chunk\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 // Queue the write operation\r\n const chunkSeq = pendingChunk?.seq ?? -1;\r\n const expectedSize = pendingChunk?.size;\r\n pendingChunk = null;\r\n\r\n writeQueueDepth++;\r\n writeQueue = writeQueue\r\n .then(async () => {\r\n const buf = await bufPromise;\r\n\r\n // Security: Validate chunk size matches declared size\r\n if (expectedSize !== undefined && buf.byteLength !== expectedSize) {\r\n throw new DropgateValidationError(\r\n `Chunk size mismatch: expected ${expectedSize}, got ${buf.byteLength}`\r\n );\r\n }\r\n\r\n // Security: Validate we don't receive more than declared total\r\n const newReceived = received + buf.byteLength;\r\n if (total > 0 && newReceived > total) {\r\n throw new DropgateValidationError(\r\n `Received more data than expected: ${newReceived} > ${total}`\r\n );\r\n }\r\n\r\n // Call consumer's onData handler (stream-through, no buffering)\r\n if (onData) {\r\n await onData(buf);\r\n }\r\n\r\n received += buf.byteLength;\r\n currentFileReceived += buf.byteLength;\r\n const progressReceived = fileList ? (totalReceivedAllFiles + currentFileReceived) : received;\r\n const progressTotal = fileList ? fileList.totalSize : total;\r\n const percent = progressTotal ? Math.min(100, (progressReceived / progressTotal) * 100) : 0;\r\n if (!isStopped()) onProgress?.({ processedBytes: progressReceived, totalBytes: progressTotal, percent });\r\n\r\n // Send chunk acknowledgment\r\n if (chunkSeq >= 0) {\r\n sendChunkAck(conn, chunkSeq);\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 .finally(() => {\r\n writeQueueDepth--;\r\n });\r\n\r\n return;\r\n }\r\n\r\n // Handle control messages\r\n if (!isP2PMessage(data)) return;\r\n\r\n const msg = data;\r\n\r\n switch (msg.t) {\r\n case 'hello':\r\n currentSessionId = msg.sessionId || null;\r\n transitionTo('negotiating');\r\n onStatus?.({ phase: 'waiting', message: 'Waiting for file details...' });\r\n break;\r\n\r\n case 'file_list': {\r\n // v3: Store file list for multi-file transfer\r\n const fileListMsg = msg as P2PFileListMessage;\r\n\r\n // Security: Validate file count\r\n if (fileListMsg.fileCount > MAX_FILE_COUNT) {\r\n throw new DropgateValidationError(`Too many files: ${fileListMsg.fileCount}`);\r\n }\r\n\r\n // Security: Validate total size matches sum of file sizes\r\n const sumSize = fileListMsg.files.reduce((sum, f) => sum + f.size, 0);\r\n if (sumSize !== fileListMsg.totalSize) {\r\n throw new DropgateValidationError(\r\n `File list size mismatch: declared ${fileListMsg.totalSize}, actual sum ${sumSize}`\r\n );\r\n }\r\n\r\n fileList = fileListMsg;\r\n total = fileListMsg.totalSize;\r\n break;\r\n }\r\n\r\n case 'meta': {\r\n // For multi-file: meta comes for each file (first triggers ready, subsequent auto-transition)\r\n if (state !== 'negotiating' && !(state === 'transferring' && fileList)) {\r\n return;\r\n }\r\n\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 const fileSize = Number(msg.size) || 0;\r\n const fi = msg.fileIndex;\r\n\r\n // For multi-file subsequent files, reset per-file tracking\r\n if (fileList && typeof fi === 'number' && fi > 0) {\r\n currentFileReceived = 0;\r\n // Don't reset writeQueue or received - they accumulate\r\n onFileStart?.({ fileIndex: fi, name, size: fileSize });\r\n break; // Already transferring, no need for ready signal\r\n }\r\n\r\n // First file (or single file transfer)\r\n received = 0;\r\n currentFileReceived = 0;\r\n totalReceivedAllFiles = 0;\r\n if (!fileList) {\r\n total = fileSize;\r\n }\r\n writeQueue = Promise.resolve();\r\n\r\n // Function to send ready signal\r\n const sendReady = (): void => {\r\n transitionTo('transferring');\r\n // Start watchdog once we're ready to receive data\r\n resetWatchdog();\r\n // Notify consumer about first file start (for multi-file ZIP assembly)\r\n if (fileList) {\r\n onFileStart?.({ fileIndex: 0, name, size: fileSize });\r\n }\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 // Build metadata event\r\n const metaEvt: Parameters<NonNullable<typeof onMeta>>[0] = { name, total };\r\n if (fileList) {\r\n metaEvt.fileCount = fileList.fileCount;\r\n metaEvt.files = fileList.files.map(f => ({ name: f.name, size: f.size }));\r\n metaEvt.totalSize = fileList.totalSize;\r\n }\r\n\r\n if (autoReady) {\r\n if (!isStopped()) {\r\n onMeta?.(metaEvt);\r\n onProgress?.({ processedBytes: received, totalBytes: total, percent: 0 });\r\n }\r\n sendReady();\r\n } else {\r\n // Pass sendReady function to callback so consumer can trigger transfer start\r\n metaEvt.sendReady = sendReady;\r\n if (!isStopped()) {\r\n onMeta?.(metaEvt);\r\n onProgress?.({ processedBytes: received, totalBytes: total, percent: 0 });\r\n }\r\n }\r\n break;\r\n }\r\n\r\n case 'chunk': {\r\n const chunkMsg = msg as P2PChunkMessage;\r\n\r\n // Security: Only accept chunk messages if we're in 'transferring' state\r\n if (state !== 'transferring') {\r\n throw new DropgateValidationError(\r\n 'Received chunk message before transfer was accepted.'\r\n );\r\n }\r\n\r\n // Security: Validate chunk sequence (must be in order)\r\n if (chunkMsg.seq !== expectedChunkSeq) {\r\n throw new DropgateValidationError(\r\n `Chunk sequence error: expected ${expectedChunkSeq}, got ${chunkMsg.seq}`\r\n );\r\n }\r\n expectedChunkSeq++;\r\n\r\n pendingChunk = chunkMsg;\r\n break;\r\n }\r\n\r\n case 'ping':\r\n // Respond to heartbeat - keeps watchdog alive and confirms we're active\r\n try {\r\n conn.send({ t: 'pong', timestamp: Date.now() });\r\n } catch {\r\n // Ignore send errors\r\n }\r\n break;\r\n\r\n case 'file_end': {\r\n // v3: Current file complete, ack it\r\n clearWatchdog();\r\n await writeQueue;\r\n\r\n const feIdx = msg.fileIndex;\r\n onFileEnd?.({ fileIndex: feIdx, receivedBytes: currentFileReceived });\r\n\r\n try {\r\n conn.send({ t: 'file_end_ack', fileIndex: feIdx, received: currentFileReceived, size: currentFileReceived });\r\n } catch {\r\n // Ignore send errors\r\n }\r\n\r\n totalReceivedAllFiles += currentFileReceived;\r\n currentFileReceived = 0;\r\n\r\n // Restart watchdog for next file\r\n resetWatchdog();\r\n break;\r\n }\r\n\r\n case 'end':\r\n clearWatchdog();\r\n await writeQueue;\r\n\r\n // For multi-file, use totalReceivedAllFiles + any remaining\r\n const finalReceived = fileList ? (totalReceivedAllFiles + currentFileReceived) : received;\r\n const finalTotal = fileList ? fileList.totalSize : total;\r\n\r\n if (finalTotal && finalReceived < finalTotal) {\r\n const err = new DropgateNetworkError(\r\n 'Transfer ended before all data 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 // Send end_ack immediately so sender can complete\r\n try {\r\n conn.send({ t: 'end_ack', received: finalReceived, total: finalTotal });\r\n } catch {\r\n // Ignore send errors\r\n }\r\n\r\n // Mark as completed - protects against close handler race\r\n safeComplete({ received: finalReceived, total: finalTotal });\r\n\r\n // Send additional acks for reliability (fire-and-forget, best effort)\r\n (async () => {\r\n for (let i = 0; i < 2; i++) {\r\n await sleep(P2P_END_ACK_RETRY_DELAY_MS);\r\n try {\r\n conn.send({ t: 'end_ack', received: finalReceived, total: finalTotal });\r\n } catch {\r\n break; // Connection closed\r\n }\r\n }\r\n })().catch(() => { });\r\n break;\r\n\r\n case 'error':\r\n throw new DropgateNetworkError(msg.message || 'Sender reported an error.');\r\n\r\n case 'cancelled':\r\n if (state === 'cancelled' || state === 'closed' || state === 'completed') return;\r\n transitionTo('cancelled');\r\n onCancel?.({ cancelledBy: 'sender', message: msg.reason });\r\n cleanup();\r\n break;\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' || state === 'cancelled') {\r\n // Clean shutdown or already cancelled, ensure full cleanup\r\n cleanup();\r\n return;\r\n }\r\n\r\n // Sender disconnected or cancelled before transfer completed\r\n if (state === 'transferring') {\r\n // Connection closed during active transfer — the sender either cancelled\r\n // or disconnected. Treat as a sender-initiated cancellation so the UI\r\n // can show a clean message instead of a raw error.\r\n transitionTo('cancelled');\r\n onCancel?.({ cancelledBy: 'sender' });\r\n cleanup();\r\n } else if (state === 'negotiating') {\r\n // We had metadata but transfer hadn't started\r\n transitionTo('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","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 ServerInfo,\r\n ServerTarget,\r\n CompatibilityResult,\r\n ShareTargetResult,\r\n UploadResult,\r\n UploadSession,\r\n UploadProgressEvent,\r\n DropgateClientOptions,\r\n UploadFilesOptions,\r\n GetServerInfoOptions,\r\n ConnectOptions,\r\n ValidateUploadOptions,\r\n FileSource,\r\n Base64Adapter,\r\n DownloadFilesOptions,\r\n DownloadResult,\r\n DownloadProgressEvent,\r\n FileMetadata,\r\n BundleMetadata,\r\n} from '../types.js';\r\nimport type {\r\n P2PSendFileOptions,\r\n P2PReceiveFileOptions,\r\n P2PSendSession,\r\n P2PReceiveSession,\r\n} from '../p2p/types.js';\r\nimport { getDefaultCrypto, getDefaultFetch, getDefaultBase64 } from '../adapters/defaults.js';\r\nimport { makeAbortSignal, fetchJson, sleep, buildBaseUrl, parseServerUrl } 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\nimport { startP2PSend } from '../p2p/send.js';\r\nimport { startP2PReceive } from '../p2p/receive.js';\r\nimport { resolvePeerConfig } from '../p2p/helpers.js';\r\nimport { StreamingZipWriter } from '../zip/stream-zip.js';\r\n\r\n/**\r\n * Resolve a server option (URL string or ServerTarget) to a base URL string.\r\n */\r\nfunction resolveServerToBaseUrl(server: string | ServerTarget): string {\r\n if (typeof server === 'string') {\r\n return buildBaseUrl(parseServerUrl(server));\r\n }\r\n return buildBaseUrl(server);\r\n}\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 { server, 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 = resolveServerToBaseUrl(server);\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 operations.\r\n * Handles server communication, encryption, chunked uploads, downloads, and P2P transfers.\r\n *\r\n * Server connection is configured once in the constructor — all methods use\r\n * the stored server URL and cached server info automatically.\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\r\n /** Resolved base URL (e.g. 'https://dropgate.link'). May change during HTTP fallback. */\r\n baseUrl: string;\r\n\r\n /** Whether to automatically retry with HTTP when HTTPS fails. */\r\n private _fallbackToHttp: boolean;\r\n /** Cached compatibility result (null until first connect()). */\r\n private _compat: (CompatibilityResult & { serverInfo: ServerInfo; baseUrl: string }) | null = null;\r\n /** In-flight connect promise to deduplicate concurrent calls. */\r\n private _connectPromise: Promise<CompatibilityResult & { serverInfo: ServerInfo; baseUrl: string }> | null = null;\r\n\r\n /**\r\n * Create a new DropgateClient instance.\r\n * @param opts - Client configuration options including server URL.\r\n * @throws {DropgateValidationError} If clientVersion or server 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 if (!opts.server) {\r\n throw new DropgateValidationError(\r\n 'DropgateClient requires server (URL string or ServerTarget object).'\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._fallbackToHttp = Boolean(opts.fallbackToHttp);\r\n\r\n // Resolve server to baseUrl\r\n this.baseUrl = resolveServerToBaseUrl(opts.server);\r\n }\r\n\r\n /**\r\n * Get the server target (host, port, secure) derived from the current baseUrl.\r\n * Useful for passing to standalone functions that still need a ServerTarget.\r\n */\r\n get serverTarget(): ServerTarget {\r\n const url = new URL(this.baseUrl);\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 * Connect to the server: fetch server info and check version compatibility.\r\n * Results are cached — subsequent calls return instantly without network requests.\r\n * Concurrent calls are deduplicated.\r\n *\r\n * @param opts - Optional timeout and abort signal.\r\n * @returns Compatibility result with 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 connect(\r\n opts?: ConnectOptions\r\n ): Promise<CompatibilityResult & { serverInfo: ServerInfo; baseUrl: string }> {\r\n // Return cached result if available\r\n if (this._compat) return this._compat;\r\n\r\n // Deduplicate concurrent connect calls\r\n if (!this._connectPromise) {\r\n this._connectPromise = this._fetchAndCheckCompat(opts).finally(() => {\r\n this._connectPromise = null;\r\n });\r\n }\r\n\r\n return this._connectPromise;\r\n }\r\n\r\n private async _fetchAndCheckCompat(\r\n opts?: ConnectOptions\r\n ): Promise<CompatibilityResult & { serverInfo: ServerInfo; baseUrl: string }> {\r\n const { timeoutMs = 5000, signal } = opts ?? {};\r\n\r\n let baseUrl = this.baseUrl;\r\n let serverInfo: ServerInfo;\r\n\r\n try {\r\n const result = await getServerInfo({\r\n server: baseUrl,\r\n timeoutMs,\r\n signal,\r\n fetchFn: this.fetchFn,\r\n });\r\n baseUrl = result.baseUrl;\r\n serverInfo = result.serverInfo;\r\n } catch (err) {\r\n // HTTP fallback: if HTTPS failed and fallback is enabled, retry with HTTP\r\n if (this._fallbackToHttp && this.baseUrl.startsWith('https://')) {\r\n const httpBaseUrl = this.baseUrl.replace('https://', 'http://');\r\n try {\r\n const result = await getServerInfo({\r\n server: httpBaseUrl,\r\n timeoutMs,\r\n signal,\r\n fetchFn: this.fetchFn,\r\n });\r\n // HTTP worked — update stored baseUrl\r\n this.baseUrl = httpBaseUrl;\r\n baseUrl = result.baseUrl;\r\n serverInfo = result.serverInfo;\r\n } catch {\r\n // Both failed — throw the original HTTPS error\r\n if (err instanceof DropgateError) throw err;\r\n throw new DropgateNetworkError('Could not connect to the server.', { cause: err });\r\n }\r\n } else {\r\n if (err instanceof DropgateError) throw err;\r\n throw new DropgateNetworkError('Could not connect to the server.', { cause: err });\r\n }\r\n }\r\n\r\n const compat = this._checkVersionCompat(serverInfo!);\r\n this._compat = { ...compat, serverInfo: serverInfo!, baseUrl };\r\n return this._compat;\r\n }\r\n\r\n /**\r\n * Pure version compatibility check (no network calls).\r\n */\r\n private _checkVersionCompat(serverInfo: ServerInfo): CompatibilityResult {\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 };\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 };\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 };\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 - Optional timeout and abort signal.\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?: ConnectOptions\r\n ): Promise<ShareTargetResult> {\r\n const { timeoutMs = 5000, signal } = opts ?? {};\r\n\r\n // Check server compatibility (uses cache)\r\n const compat = await this.connect(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 * Fetch metadata for a single file from the server.\r\n * @param fileId - The file ID to fetch metadata for.\r\n * @param opts - Optional connection options (timeout, signal).\r\n * @returns File metadata including size, filename, and encryption status.\r\n * @throws {DropgateNetworkError} If the server cannot be reached.\r\n * @throws {DropgateProtocolError} If the file is not found or server returns an error.\r\n */\r\n async getFileMetadata(\r\n fileId: string,\r\n opts?: ConnectOptions\r\n ): Promise<FileMetadata> {\r\n if (!fileId || typeof fileId !== 'string') {\r\n throw new DropgateValidationError('File ID is required.');\r\n }\r\n\r\n const { timeoutMs = 5000, signal } = opts ?? {};\r\n\r\n const url = `${this.baseUrl}/api/file/${encodeURIComponent(fileId)}/meta`;\r\n const { res, json } = await fetchJson(this.fetchFn, url, {\r\n method: 'GET',\r\n timeoutMs,\r\n signal,\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) || `Failed to fetch file metadata (status ${res.status}).`;\r\n throw new DropgateProtocolError(msg, { details: json });\r\n }\r\n\r\n return json as FileMetadata;\r\n }\r\n\r\n /**\r\n * Fetch metadata for a bundle from the server and derive computed fields.\r\n * For sealed bundles, decrypts the manifest to extract file list.\r\n * Automatically derives totalSizeBytes and fileCount from the files array.\r\n * @param bundleId - The bundle ID to fetch metadata for.\r\n * @param keyB64 - Base64-encoded decryption key (required for encrypted bundles).\r\n * @param opts - Optional connection options (timeout, signal).\r\n * @returns Complete bundle metadata with all files and computed fields.\r\n * @throws {DropgateNetworkError} If the server cannot be reached.\r\n * @throws {DropgateProtocolError} If the bundle is not found or server returns an error.\r\n * @throws {DropgateValidationError} If decryption key is missing for encrypted bundle.\r\n */\r\n async getBundleMetadata(\r\n bundleId: string,\r\n keyB64?: string,\r\n opts?: ConnectOptions\r\n ): Promise<BundleMetadata> {\r\n if (!bundleId || typeof bundleId !== 'string') {\r\n throw new DropgateValidationError('Bundle ID is required.');\r\n }\r\n\r\n const { timeoutMs = 5000, signal } = opts ?? {};\r\n\r\n const url = `${this.baseUrl}/api/bundle/${encodeURIComponent(bundleId)}/meta`;\r\n const { res, json } = await fetchJson(this.fetchFn, url, {\r\n method: 'GET',\r\n timeoutMs,\r\n signal,\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) || `Failed to fetch bundle metadata (status ${res.status}).`;\r\n throw new DropgateProtocolError(msg, { details: json });\r\n }\r\n\r\n const serverMeta = json as {\r\n isEncrypted: boolean;\r\n sealed?: boolean;\r\n encryptedManifest?: string;\r\n files?: Array<{\r\n fileId: string;\r\n sizeBytes: number;\r\n filename?: string;\r\n encryptedFilename?: string;\r\n }>;\r\n };\r\n\r\n let files: Array<{\r\n fileId: string;\r\n sizeBytes: number;\r\n filename?: string;\r\n encryptedFilename?: string;\r\n }> = [];\r\n\r\n // Handle sealed bundles: decrypt manifest to get file list\r\n if (serverMeta.sealed && serverMeta.encryptedManifest) {\r\n if (!keyB64) {\r\n throw new DropgateValidationError(\r\n 'Decryption key (keyB64) is required for encrypted sealed bundles.'\r\n );\r\n }\r\n\r\n const key = await importKeyFromBase64(this.cryptoObj, keyB64);\r\n const encryptedBytes = this.base64.decode(serverMeta.encryptedManifest);\r\n const decryptedBuffer = await decryptChunk(this.cryptoObj, encryptedBytes, key);\r\n const manifestJson = new TextDecoder().decode(decryptedBuffer);\r\n const manifest = JSON.parse(manifestJson) as {\r\n files: Array<{ fileId: string; sizeBytes: number; name: string }>\r\n };\r\n\r\n // Map manifest files to consistent format (name -> filename for consistency)\r\n files = manifest.files.map(f => ({\r\n fileId: f.fileId,\r\n sizeBytes: f.sizeBytes,\r\n filename: f.name,\r\n }));\r\n } else if (serverMeta.files) {\r\n // Unsealed bundle: use files from server response\r\n files = serverMeta.files;\r\n } else {\r\n throw new DropgateProtocolError('Invalid bundle metadata: missing files or manifest.');\r\n }\r\n\r\n // Derive totalSizeBytes and fileCount from files array\r\n const totalSizeBytes = files.reduce((sum, f) => sum + (f.sizeBytes || 0), 0);\r\n const fileCount = files.length;\r\n\r\n return {\r\n isEncrypted: serverMeta.isEncrypted,\r\n sealed: serverMeta.sealed,\r\n encryptedManifest: serverMeta.encryptedManifest,\r\n files,\r\n totalSizeBytes,\r\n fileCount,\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 { files: rawFiles, 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 const files = Array.isArray(rawFiles) ? rawFiles : [rawFiles];\r\n if (files.length === 0) {\r\n throw new DropgateValidationError('At least one file is required.');\r\n }\r\n\r\n // Validate each file and check size limits\r\n for (let i = 0; i < files.length; i++) {\r\n const file = files[i];\r\n const fileSize = Number(file?.size || 0);\r\n if (!file || !Number.isFinite(fileSize) || fileSize <= 0) {\r\n throw new DropgateValidationError(`File at index ${i} is missing or invalid.`);\r\n }\r\n\r\n // maxSizeMB: 0 means unlimited (per-file check)\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 validationChunkSize = (Number.isFinite(caps.chunkSize) && caps.chunkSize! > 0)\r\n ? caps.chunkSize!\r\n : this.chunkSize;\r\n const totalChunks = Math.ceil(fileSize / validationChunkSize);\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 at index ${i} too large once encryption overhead is included. Server limit: ${maxMB} MB.`\r\n : `File at index ${i} too large. Server limit: ${maxMB} MB.`;\r\n throw new DropgateValidationError(msg);\r\n }\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 'End-to-end encryption is not supported on this server.'\r\n );\r\n }\r\n\r\n return true;\r\n }\r\n\r\n /**\r\n * Upload one or more files to the server with optional encryption.\r\n * Single files use the standard upload protocol.\r\n * Multiple files use the bundle protocol, grouping files under a single download link.\r\n *\r\n * @param opts - Upload options including file(s) and settings.\r\n * @returns Upload session with result promise and cancellation support.\r\n */\r\n async uploadFiles(opts: UploadFilesOptions): Promise<UploadSession> {\r\n const {\r\n files: rawFiles,\r\n lifetimeMs,\r\n encrypt,\r\n maxDownloads,\r\n filenameOverrides,\r\n onProgress,\r\n onCancel,\r\n signal,\r\n timeouts = {},\r\n retry = {},\r\n } = opts;\r\n\r\n const files = Array.isArray(rawFiles) ? rawFiles : [rawFiles];\r\n if (files.length === 0) {\r\n throw new DropgateValidationError('At least one file is required.');\r\n }\r\n\r\n const internalController = signal ? null : new AbortController();\r\n const effectiveSignal = signal || internalController?.signal;\r\n\r\n let uploadState: 'initializing' | 'uploading' | 'completing' | 'completed' | 'cancelled' | 'error' = 'initializing';\r\n const currentUploadIds: string[] = [];\r\n\r\n const totalSizeBytes = files.reduce((sum, f) => sum + f.size, 0);\r\n\r\n const uploadPromise = (async (): Promise<UploadResult> => {\r\n try {\r\n const progress = (evt: UploadProgressEvent): void => {\r\n try { if (onProgress) onProgress(evt); } catch { /* Ignore */ }\r\n };\r\n\r\n // 0) Get server info + compat (uses cache)\r\n progress({ phase: 'server-info', text: 'Checking server...', percent: 0, processedBytes: 0, totalBytes: totalSizeBytes });\r\n\r\n const compat = await this.connect({\r\n timeoutMs: timeouts.serverInfoMs ?? 5000,\r\n signal: effectiveSignal,\r\n });\r\n\r\n const { baseUrl, serverInfo } = compat;\r\n progress({ phase: 'server-compat', text: compat.message, percent: 0, processedBytes: 0, totalBytes: totalSizeBytes });\r\n if (!compat.compatible) {\r\n throw new DropgateValidationError(compat.message);\r\n }\r\n\r\n // 1) Resolve filenames\r\n const filenames = files.map((f, i) => filenameOverrides?.[i] ?? f.name ?? 'file');\r\n\r\n // Resolve encrypt option: default to true if server supports E2EE\r\n const serverSupportsE2EE = Boolean(serverInfo?.capabilities?.upload?.e2ee);\r\n const effectiveEncrypt = encrypt ?? serverSupportsE2EE;\r\n\r\n if (!effectiveEncrypt) {\r\n for (const name of filenames) validatePlainFilename(name);\r\n }\r\n\r\n this.validateUploadInputs({ files, lifetimeMs, encrypt: effectiveEncrypt, serverInfo });\r\n\r\n // 2) Encryption prep (single key for all files)\r\n let cryptoKey: CryptoKey | null = null;\r\n let keyB64: string | null = null;\r\n const transmittedFilenames: string[] = [];\r\n\r\n if (effectiveEncrypt) {\r\n if (!this.cryptoObj?.subtle) {\r\n throw new DropgateValidationError(\r\n 'Web Crypto API not available (crypto.subtle). Encryption requires a secure context (HTTPS or localhost).'\r\n );\r\n }\r\n progress({ phase: 'crypto', text: 'Generating encryption key...', percent: 0, processedBytes: 0, totalBytes: totalSizeBytes });\r\n try {\r\n cryptoKey = await generateAesGcmKey(this.cryptoObj);\r\n keyB64 = await exportKeyBase64(this.cryptoObj, cryptoKey);\r\n for (const name of filenames) {\r\n transmittedFilenames.push(\r\n await encryptFilenameToBase64(this.cryptoObj, name, cryptoKey)\r\n );\r\n }\r\n } catch (err) {\r\n throw new DropgateError('Failed to prepare encryption.', { code: 'CRYPTO_PREP_FAILED', cause: err });\r\n }\r\n } else {\r\n transmittedFilenames.push(...filenames);\r\n }\r\n\r\n // 3) Compute chunk sizes\r\n const serverChunkSize = serverInfo?.capabilities?.upload?.chunkSize;\r\n const effectiveChunkSize = (Number.isFinite(serverChunkSize) && serverChunkSize! > 0)\r\n ? serverChunkSize!\r\n : this.chunkSize;\r\n\r\n const retries = Number.isFinite(retry.retries) ? retry.retries! : 5;\r\n const baseBackoffMs = Number.isFinite(retry.backoffMs) ? retry.backoffMs! : 1000;\r\n const maxBackoffMs = Number.isFinite(retry.maxBackoffMs) ? retry.maxBackoffMs! : 30000;\r\n\r\n // ========== SINGLE FILE ==========\r\n if (files.length === 1) {\r\n const file = files[0];\r\n const totalChunks = Math.ceil(file.size / effectiveChunkSize);\r\n const totalUploadSize = estimateTotalUploadSizeBytes(file.size, totalChunks, effectiveEncrypt);\r\n\r\n // Init\r\n progress({ phase: 'init', text: 'Reserving server storage...', percent: 0, processedBytes: 0, totalBytes: file.size });\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: effectiveSignal,\r\n headers: { 'Content-Type': 'application/json', Accept: 'application/json' },\r\n body: JSON.stringify({\r\n filename: transmittedFilenames[0],\r\n lifetime: lifetimeMs,\r\n isEncrypted: effectiveEncrypt,\r\n totalSize: totalUploadSize,\r\n totalChunks,\r\n ...(maxDownloads !== undefined ? { maxDownloads } : {}),\r\n }),\r\n });\r\n\r\n if (!initRes.res.ok) {\r\n const errorJson = initRes.json as { error?: string } | null;\r\n throw new DropgateProtocolError(errorJson?.error || `Server initialisation failed: ${initRes.res.status}`, { details: initRes.json || initRes.text });\r\n }\r\n\r\n const uploadId = (initRes.json as { uploadId?: string })?.uploadId;\r\n if (!uploadId) throw new DropgateProtocolError('Server did not return a valid uploadId.');\r\n currentUploadIds.push(uploadId);\r\n uploadState = 'uploading';\r\n\r\n // Chunks\r\n await this._uploadFileChunks({\r\n file, uploadId, cryptoKey, effectiveChunkSize, totalChunks, totalUploadSize,\r\n baseOffset: 0, totalBytesAllFiles: file.size,\r\n progress, signal: effectiveSignal, baseUrl,\r\n retries, backoffMs: baseBackoffMs, maxBackoffMs,\r\n chunkTimeoutMs: timeouts.chunkMs ?? 60000,\r\n });\r\n\r\n // Complete\r\n progress({ phase: 'complete', text: 'Finalising upload...', percent: 100, processedBytes: file.size, totalBytes: file.size });\r\n uploadState = 'completing';\r\n\r\n const completeRes = await fetchJson(this.fetchFn, `${baseUrl}/upload/complete`, {\r\n method: 'POST',\r\n timeoutMs: timeouts.completeMs ?? 30000,\r\n signal: effectiveSignal,\r\n headers: { 'Content-Type': 'application/json', Accept: 'application/json' },\r\n body: JSON.stringify({ uploadId }),\r\n });\r\n\r\n if (!completeRes.res.ok) {\r\n const errorJson = completeRes.json as { error?: string } | null;\r\n throw new DropgateProtocolError(errorJson?.error || 'Finalisation failed.', { details: completeRes.json || completeRes.text });\r\n }\r\n\r\n const fileId = (completeRes.json as { id?: string })?.id;\r\n if (!fileId) throw new DropgateProtocolError('Server did not return a valid file id.');\r\n\r\n let downloadUrl = `${baseUrl}/${fileId}`;\r\n if (effectiveEncrypt && keyB64) downloadUrl += `#${keyB64}`;\r\n\r\n progress({ phase: 'done', text: 'Upload successful!', percent: 100, processedBytes: file.size, totalBytes: file.size });\r\n uploadState = 'completed';\r\n\r\n return {\r\n downloadUrl, fileId, uploadId, baseUrl,\r\n ...(effectiveEncrypt && keyB64 ? { keyB64 } : {}),\r\n };\r\n }\r\n\r\n // ========== MULTI-FILE (BUNDLE) ==========\r\n // Prepare per-file metadata\r\n const fileManifest = files.map((f, i) => {\r\n const totalChunks = Math.ceil(f.size / effectiveChunkSize);\r\n const totalUploadSize = estimateTotalUploadSizeBytes(f.size, totalChunks, effectiveEncrypt);\r\n return { filename: transmittedFilenames[i], totalSize: totalUploadSize, totalChunks };\r\n });\r\n\r\n // Init bundle\r\n progress({ phase: 'init', text: `Reserving server storage for ${files.length} files...`, percent: 0, processedBytes: 0, totalBytes: totalSizeBytes, totalFiles: files.length });\r\n\r\n const initBundleRes = await fetchJson(this.fetchFn, `${baseUrl}/upload/init-bundle`, {\r\n method: 'POST',\r\n timeoutMs: timeouts.initMs ?? 15000,\r\n signal: effectiveSignal,\r\n headers: { 'Content-Type': 'application/json', Accept: 'application/json' },\r\n body: JSON.stringify({\r\n fileCount: files.length,\r\n files: fileManifest,\r\n lifetime: lifetimeMs,\r\n isEncrypted: effectiveEncrypt,\r\n ...(maxDownloads !== undefined ? { maxDownloads } : {}),\r\n }),\r\n });\r\n\r\n if (!initBundleRes.res.ok) {\r\n const errorJson = initBundleRes.json as { error?: string } | null;\r\n throw new DropgateProtocolError(errorJson?.error || `Bundle initialisation failed: ${initBundleRes.res.status}`, { details: initBundleRes.json || initBundleRes.text });\r\n }\r\n\r\n const bundleInitJson = initBundleRes.json as { bundleUploadId?: string; fileUploadIds?: string[] } | null;\r\n const bundleUploadId = bundleInitJson?.bundleUploadId;\r\n const fileUploadIds = bundleInitJson?.fileUploadIds;\r\n if (!bundleUploadId || !fileUploadIds || fileUploadIds.length !== files.length) {\r\n throw new DropgateProtocolError('Server did not return valid bundle upload IDs.');\r\n }\r\n currentUploadIds.push(...fileUploadIds);\r\n uploadState = 'uploading';\r\n\r\n // Upload each file sequentially\r\n const fileResults: Array<{ fileId: string; name: string; size: number }> = [];\r\n let cumulativeBytes = 0;\r\n\r\n for (let fi = 0; fi < files.length; fi++) {\r\n const file = files[fi];\r\n const uploadId = fileUploadIds[fi];\r\n const totalChunks = fileManifest[fi].totalChunks;\r\n const totalUploadSize = fileManifest[fi].totalSize;\r\n\r\n progress({\r\n phase: 'file-start', text: `Uploading file ${fi + 1} of ${files.length}: ${filenames[fi]}`,\r\n percent: totalSizeBytes > 0 ? (cumulativeBytes / totalSizeBytes) * 100 : 0,\r\n processedBytes: cumulativeBytes, totalBytes: totalSizeBytes,\r\n fileIndex: fi, totalFiles: files.length, currentFileName: filenames[fi],\r\n });\r\n\r\n await this._uploadFileChunks({\r\n file, uploadId, cryptoKey, effectiveChunkSize, totalChunks, totalUploadSize,\r\n baseOffset: cumulativeBytes, totalBytesAllFiles: totalSizeBytes,\r\n progress, signal: effectiveSignal, baseUrl,\r\n retries, backoffMs: baseBackoffMs, maxBackoffMs,\r\n chunkTimeoutMs: timeouts.chunkMs ?? 60000,\r\n fileIndex: fi, totalFiles: files.length, currentFileName: filenames[fi],\r\n });\r\n\r\n // Complete individual file\r\n const completeRes = await fetchJson(this.fetchFn, `${baseUrl}/upload/complete`, {\r\n method: 'POST',\r\n timeoutMs: timeouts.completeMs ?? 30000,\r\n signal: effectiveSignal,\r\n headers: { 'Content-Type': 'application/json', Accept: 'application/json' },\r\n body: JSON.stringify({ uploadId }),\r\n });\r\n\r\n if (!completeRes.res.ok) {\r\n const errorJson = completeRes.json as { error?: string } | null;\r\n throw new DropgateProtocolError(errorJson?.error || `File ${fi + 1} finalisation failed.`, { details: completeRes.json || completeRes.text });\r\n }\r\n\r\n const fileId = (completeRes.json as { id?: string })?.id;\r\n if (!fileId) throw new DropgateProtocolError(`Server did not return a valid file id for file ${fi + 1}.`);\r\n\r\n fileResults.push({ fileId, name: filenames[fi], size: file.size });\r\n cumulativeBytes += file.size;\r\n\r\n progress({\r\n phase: 'file-complete', text: `File ${fi + 1} of ${files.length} uploaded.`,\r\n percent: totalSizeBytes > 0 ? (cumulativeBytes / totalSizeBytes) * 100 : 0,\r\n processedBytes: cumulativeBytes, totalBytes: totalSizeBytes,\r\n fileIndex: fi, totalFiles: files.length, currentFileName: filenames[fi],\r\n });\r\n }\r\n\r\n // Complete bundle\r\n progress({ phase: 'complete', text: 'Finalising bundle...', percent: 100, processedBytes: totalSizeBytes, totalBytes: totalSizeBytes });\r\n uploadState = 'completing';\r\n\r\n // For encrypted bundles, build and encrypt the manifest client-side.\r\n // The server stores only the opaque blob and cannot read which files belong to the bundle.\r\n let encryptedManifestB64: string | undefined;\r\n if (effectiveEncrypt && cryptoKey) {\r\n const manifest = JSON.stringify({\r\n files: fileResults.map(r => ({\r\n fileId: r.fileId,\r\n name: r.name,\r\n sizeBytes: r.size,\r\n })),\r\n });\r\n const manifestBytes = new TextEncoder().encode(manifest);\r\n const encryptedBlob = await encryptToBlob(this.cryptoObj, manifestBytes.buffer, cryptoKey);\r\n const encryptedBuffer = new Uint8Array(await encryptedBlob.arrayBuffer());\r\n encryptedManifestB64 = this.base64.encode(encryptedBuffer);\r\n }\r\n\r\n const completeBundleRes = await fetchJson(this.fetchFn, `${baseUrl}/upload/complete-bundle`, {\r\n method: 'POST',\r\n timeoutMs: timeouts.completeMs ?? 30000,\r\n signal: effectiveSignal,\r\n headers: { 'Content-Type': 'application/json', Accept: 'application/json' },\r\n body: JSON.stringify({\r\n bundleUploadId,\r\n ...(encryptedManifestB64 ? { encryptedManifest: encryptedManifestB64 } : {}),\r\n }),\r\n });\r\n\r\n if (!completeBundleRes.res.ok) {\r\n const errorJson = completeBundleRes.json as { error?: string } | null;\r\n throw new DropgateProtocolError(errorJson?.error || 'Bundle finalisation failed.', { details: completeBundleRes.json || completeBundleRes.text });\r\n }\r\n\r\n const bundleId = (completeBundleRes.json as { bundleId?: string })?.bundleId;\r\n if (!bundleId) throw new DropgateProtocolError('Server did not return a valid bundle id.');\r\n\r\n let downloadUrl = `${baseUrl}/b/${bundleId}`;\r\n if (effectiveEncrypt && keyB64) downloadUrl += `#${keyB64}`;\r\n\r\n progress({ phase: 'done', text: 'Upload successful!', percent: 100, processedBytes: totalSizeBytes, totalBytes: totalSizeBytes });\r\n uploadState = 'completed';\r\n\r\n return {\r\n downloadUrl, bundleId, baseUrl, files: fileResults,\r\n ...(effectiveEncrypt && keyB64 ? { keyB64 } : {}),\r\n };\r\n\r\n } catch (err) {\r\n if (err instanceof Error && (err.name === 'AbortError' || err.message?.includes('abort'))) {\r\n uploadState = 'cancelled';\r\n onCancel?.();\r\n } else {\r\n uploadState = 'error';\r\n }\r\n throw err;\r\n }\r\n })();\r\n\r\n const callCancelEndpoint = async (uploadId: string): Promise<void> => {\r\n try {\r\n await fetchJson(this.fetchFn, `${this.baseUrl}/upload/cancel`, {\r\n method: 'POST', timeoutMs: 5000,\r\n headers: { 'Content-Type': 'application/json', Accept: 'application/json' },\r\n body: JSON.stringify({ uploadId }),\r\n });\r\n } catch { /* Best effort */ }\r\n };\r\n\r\n return {\r\n result: uploadPromise,\r\n cancel: (reason?: string) => {\r\n if (uploadState === 'completed' || uploadState === 'cancelled') return;\r\n uploadState = 'cancelled';\r\n for (const id of currentUploadIds) {\r\n callCancelEndpoint(id).catch(() => { });\r\n }\r\n internalController?.abort(new DropgateAbortError(reason || 'Upload cancelled by user.'));\r\n },\r\n getStatus: () => uploadState,\r\n };\r\n }\r\n\r\n /**\r\n * Upload a single file's chunks to the server. Used internally by uploadFiles().\r\n */\r\n private async _uploadFileChunks(params: {\r\n file: FileSource;\r\n uploadId: string;\r\n cryptoKey: CryptoKey | null;\r\n effectiveChunkSize: number;\r\n totalChunks: number;\r\n totalUploadSize: number;\r\n baseOffset: number;\r\n totalBytesAllFiles: number;\r\n progress: (evt: UploadProgressEvent) => void;\r\n signal?: AbortSignal;\r\n baseUrl: string;\r\n retries: number;\r\n backoffMs: number;\r\n maxBackoffMs: number;\r\n chunkTimeoutMs: number;\r\n fileIndex?: number;\r\n totalFiles?: number;\r\n currentFileName?: string;\r\n }): Promise<void> {\r\n const {\r\n file, uploadId, cryptoKey, effectiveChunkSize, totalChunks,\r\n baseOffset, totalBytesAllFiles, progress, signal, baseUrl,\r\n retries, backoffMs, maxBackoffMs, chunkTimeoutMs,\r\n fileIndex, totalFiles, currentFileName,\r\n } = params;\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 * effectiveChunkSize;\r\n const end = Math.min(start + effectiveChunkSize, file.size);\r\n const chunkSlice = file.slice(start, end);\r\n\r\n const processedBytes = baseOffset + start;\r\n const percent = totalBytesAllFiles > 0 ? (processedBytes / totalBytesAllFiles) * 100 : 0;\r\n progress({\r\n phase: 'chunk',\r\n text: `Uploading chunk ${i + 1} of ${totalChunks}...`,\r\n percent, processedBytes, totalBytes: totalBytesAllFiles,\r\n chunkIndex: i, totalChunks,\r\n ...(fileIndex !== undefined ? { fileIndex, totalFiles, currentFileName } : {}),\r\n });\r\n\r\n const chunkBuffer = await chunkSlice.arrayBuffer();\r\n\r\n let uploadBlob: Blob;\r\n if (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 if (uploadBlob.size > effectiveChunkSize + 1024) {\r\n throw new DropgateValidationError('Chunk too large (client-side). Check chunk size settings.');\r\n }\r\n\r\n const toHash = await uploadBlob.arrayBuffer();\r\n const hashHex = await sha256Hex(this.cryptoObj, toHash);\r\n\r\n await this._attemptChunkUpload(\r\n `${baseUrl}/upload/chunk`,\r\n { method: 'POST', headers: { 'Content-Type': 'application/octet-stream', 'X-Upload-ID': uploadId, 'X-Chunk-Index': String(i), 'X-Chunk-Hash': hashHex }, body: uploadBlob },\r\n { retries, backoffMs, maxBackoffMs, timeoutMs: chunkTimeoutMs, signal, progress, chunkIndex: i, totalChunks, chunkSize: effectiveChunkSize, fileSizeBytes: totalBytesAllFiles }\r\n );\r\n }\r\n }\r\n\r\n /**\r\n * Download one or more files from the server with optional decryption.\r\n *\r\n * For single files, use `fileId`. For bundles, use `bundleId`.\r\n * With `asZip: true` on bundles, streams a ZIP archive via `onData`.\r\n * Without `asZip`, delivers files individually via `onFileStart`/`onFileData`/`onFileEnd`.\r\n *\r\n * @param opts - Download options including file/bundle ID and optional key.\r\n * @returns Download result containing filename(s) and received bytes.\r\n */\r\n async downloadFiles(opts: DownloadFilesOptions): Promise<DownloadResult> {\r\n const {\r\n fileId,\r\n bundleId,\r\n keyB64,\r\n asZip,\r\n zipFilename: _zipFilename,\r\n onProgress,\r\n onData,\r\n onFileStart,\r\n onFileData,\r\n onFileEnd,\r\n signal,\r\n timeoutMs = 60000,\r\n } = opts;\r\n\r\n const progress = (evt: DownloadProgressEvent): void => {\r\n try { if (onProgress) onProgress(evt); } catch { /* Ignore */ }\r\n };\r\n\r\n if (!fileId && !bundleId) {\r\n throw new DropgateValidationError('Either fileId or bundleId is required.');\r\n }\r\n\r\n // 0) Connect\r\n progress({ phase: 'server-info', text: 'Checking server...', processedBytes: 0, totalBytes: 0, percent: 0 });\r\n const compat = await this.connect({ timeoutMs, signal });\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) throw new DropgateValidationError(compat.message);\r\n\r\n // ========== SINGLE FILE ==========\r\n if (fileId) {\r\n return this._downloadSingleFile({ fileId, keyB64, onProgress, onData, signal, timeoutMs, baseUrl, compat });\r\n }\r\n\r\n // ========== BUNDLE ==========\r\n progress({ phase: 'metadata', text: 'Fetching bundle info...', processedBytes: 0, totalBytes: 0, percent: 0 });\r\n\r\n // Use getBundleMetadata to fetch metadata with proper derivation\r\n let bundleMeta: BundleMetadata;\r\n try {\r\n bundleMeta = await this.getBundleMetadata(bundleId!, keyB64, { timeoutMs, signal });\r\n } catch (err) {\r\n if (err instanceof DropgateError) throw err;\r\n if (err instanceof Error && err.name === 'AbortError') throw new DropgateAbortError('Download cancelled.');\r\n throw new DropgateNetworkError('Could not fetch bundle metadata.', { cause: err });\r\n }\r\n\r\n const isEncrypted = Boolean(bundleMeta.isEncrypted);\r\n const totalBytes = bundleMeta.totalSizeBytes || 0;\r\n\r\n // Decrypt filenames (and manifest for sealed bundles)\r\n let cryptoKey: CryptoKey | undefined;\r\n const filenames: string[] = [];\r\n\r\n if (isEncrypted) {\r\n if (!keyB64) throw new DropgateValidationError('Decryption key is required for encrypted bundles.');\r\n if (!this.cryptoObj?.subtle) throw new DropgateValidationError('Web Crypto API not available for decryption.');\r\n\r\n try {\r\n cryptoKey = await importKeyFromBase64(this.cryptoObj, keyB64, this.base64);\r\n\r\n if (bundleMeta.sealed && bundleMeta.encryptedManifest) {\r\n // Sealed bundle: decrypt the manifest to get the file list\r\n const encryptedBytes = this.base64.decode(bundleMeta.encryptedManifest);\r\n const decryptedBuffer = await decryptChunk(this.cryptoObj, encryptedBytes, cryptoKey);\r\n const manifestJson = new TextDecoder().decode(decryptedBuffer);\r\n const manifest = JSON.parse(manifestJson) as { files: Array<{ fileId: string; name: string; sizeBytes: number }> };\r\n\r\n // Populate bundleMeta.files from the decrypted manifest\r\n bundleMeta.files = manifest.files.map(f => ({\r\n fileId: f.fileId,\r\n sizeBytes: f.sizeBytes,\r\n filename: f.name,\r\n }));\r\n bundleMeta.fileCount = bundleMeta.files.length;\r\n\r\n for (const f of bundleMeta.files) {\r\n filenames.push(f.filename || 'file');\r\n }\r\n } else {\r\n // Non-sealed encrypted bundle: decrypt individual filenames\r\n for (const f of bundleMeta.files) {\r\n filenames.push(await decryptFilenameFromBase64(this.cryptoObj, f.encryptedFilename!, cryptoKey, this.base64));\r\n }\r\n }\r\n } catch (err) {\r\n throw new DropgateError('Failed to decrypt bundle manifest.', { code: 'DECRYPT_MANIFEST_FAILED', cause: err });\r\n }\r\n } else {\r\n for (const f of bundleMeta.files) {\r\n filenames.push(f.filename || 'file');\r\n }\r\n }\r\n\r\n let totalReceivedBytes = 0;\r\n\r\n if (asZip && onData) {\r\n // ===== BUNDLE AS ZIP =====\r\n const zipWriter = new StreamingZipWriter(onData);\r\n\r\n for (let fi = 0; fi < bundleMeta.files.length; fi++) {\r\n const fileMeta = bundleMeta.files[fi];\r\n const name = filenames[fi];\r\n\r\n progress({\r\n phase: 'zipping', text: `Downloading ${name}...`,\r\n percent: totalBytes > 0 ? (totalReceivedBytes / totalBytes) * 100 : 0,\r\n processedBytes: totalReceivedBytes, totalBytes,\r\n fileIndex: fi, totalFiles: bundleMeta.files.length, currentFileName: name,\r\n });\r\n\r\n zipWriter.startFile(name);\r\n\r\n // Download and stream this file into the ZIP\r\n const baseReceivedBytes = totalReceivedBytes;\r\n const bytesReceived = await this._streamFileIntoCallback(\r\n baseUrl, fileMeta.fileId, isEncrypted, cryptoKey, compat,\r\n signal, timeoutMs,\r\n (chunk) => { zipWriter.writeChunk(chunk); },\r\n (fileBytes) => {\r\n const current = baseReceivedBytes + fileBytes;\r\n progress({\r\n phase: 'zipping', text: `Downloading ${name}...`,\r\n percent: totalBytes > 0 ? (current / totalBytes) * 100 : 0,\r\n processedBytes: current, totalBytes,\r\n fileIndex: fi, totalFiles: bundleMeta.files.length, currentFileName: name,\r\n });\r\n },\r\n );\r\n\r\n zipWriter.endFile();\r\n totalReceivedBytes += bytesReceived;\r\n }\r\n\r\n await zipWriter.finalize();\r\n\r\n // Notify server of bundle download\r\n try {\r\n await fetchJson(this.fetchFn, `${baseUrl}/api/bundle/${bundleId}/downloaded`, {\r\n method: 'POST', timeoutMs: 5000,\r\n headers: { 'Content-Type': 'application/json', Accept: 'application/json' },\r\n body: '{}',\r\n });\r\n } catch { /* Best effort */ }\r\n\r\n progress({ phase: 'complete', text: 'Download complete!', percent: 100, processedBytes: totalReceivedBytes, totalBytes });\r\n\r\n return { filenames, receivedBytes: totalReceivedBytes, wasEncrypted: isEncrypted };\r\n\r\n } else {\r\n // ===== BUNDLE AS INDIVIDUAL FILES =====\r\n const dataCallback = onFileData || onData;\r\n\r\n for (let fi = 0; fi < bundleMeta.files.length; fi++) {\r\n const fileMeta = bundleMeta.files[fi];\r\n const name = filenames[fi];\r\n\r\n progress({\r\n phase: 'downloading', text: `Downloading ${name}...`,\r\n percent: totalBytes > 0 ? (totalReceivedBytes / totalBytes) * 100 : 0,\r\n processedBytes: totalReceivedBytes, totalBytes,\r\n fileIndex: fi, totalFiles: bundleMeta.files.length, currentFileName: name,\r\n });\r\n\r\n onFileStart?.({ name, size: fileMeta.sizeBytes, index: fi });\r\n\r\n const baseReceivedBytes = totalReceivedBytes;\r\n const bytesReceived = await this._streamFileIntoCallback(\r\n baseUrl, fileMeta.fileId, isEncrypted, cryptoKey, compat,\r\n signal, timeoutMs,\r\n dataCallback ? (chunk) => dataCallback(chunk) : undefined,\r\n (fileBytes) => {\r\n const current = baseReceivedBytes + fileBytes;\r\n progress({\r\n phase: 'downloading', text: `Downloading ${name}...`,\r\n percent: totalBytes > 0 ? (current / totalBytes) * 100 : 0,\r\n processedBytes: current, totalBytes,\r\n fileIndex: fi, totalFiles: bundleMeta.files.length, currentFileName: name,\r\n });\r\n },\r\n );\r\n\r\n onFileEnd?.({ name, index: fi });\r\n totalReceivedBytes += bytesReceived;\r\n }\r\n\r\n progress({ phase: 'complete', text: 'Download complete!', percent: 100, processedBytes: totalReceivedBytes, totalBytes });\r\n\r\n return { filenames, receivedBytes: totalReceivedBytes, wasEncrypted: isEncrypted };\r\n }\r\n }\r\n\r\n /**\r\n * Download a single file, handling encryption/decryption internally.\r\n * Preserves the original downloadFile() behavior.\r\n */\r\n private async _downloadSingleFile(params: {\r\n fileId: string;\r\n keyB64?: string;\r\n onProgress?: (evt: DownloadProgressEvent) => void;\r\n onData?: (chunk: Uint8Array) => Promise<void> | void;\r\n signal?: AbortSignal;\r\n timeoutMs: number;\r\n baseUrl: string;\r\n compat: CompatibilityResult & { serverInfo: ServerInfo; baseUrl: string };\r\n }): Promise<DownloadResult> {\r\n const { fileId, keyB64, onProgress, onData, signal, timeoutMs, baseUrl, compat } = params;\r\n\r\n const progress = (evt: DownloadProgressEvent): void => {\r\n try { if (onProgress) onProgress(evt); } catch { /* Ignore */ }\r\n };\r\n\r\n // Fetch metadata\r\n progress({ phase: 'metadata', text: 'Fetching file info...', processedBytes: 0, totalBytes: 0, percent: 0 });\r\n\r\n // Use getFileMetadata for consistent metadata fetching\r\n let metadata: FileMetadata;\r\n try {\r\n metadata = await this.getFileMetadata(fileId, { timeoutMs, signal });\r\n } catch (err) {\r\n if (err instanceof DropgateError) throw err;\r\n if (err instanceof Error && err.name === 'AbortError') throw new DropgateAbortError('Download cancelled.');\r\n throw new DropgateNetworkError('Could not fetch file metadata.', { cause: err });\r\n }\r\n\r\n const isEncrypted = Boolean(metadata.isEncrypted);\r\n const totalBytes = metadata.sizeBytes || 0;\r\n\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. Provide an onData callback to stream files larger than ${limitMB}MB.`\r\n );\r\n }\r\n\r\n // Decrypt filename\r\n let filename: string;\r\n let cryptoKey: CryptoKey | undefined;\r\n\r\n if (isEncrypted) {\r\n if (!keyB64) throw new DropgateValidationError('Decryption key is required for encrypted files.');\r\n if (!this.cryptoObj?.subtle) throw new DropgateValidationError('Web Crypto API not available for decryption.');\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(this.cryptoObj, metadata.encryptedFilename!, cryptoKey, this.base64);\r\n } catch (err) {\r\n throw new DropgateError('Failed to decrypt filename.', { code: 'DECRYPT_FILENAME_FAILED', cause: err });\r\n }\r\n } else {\r\n filename = metadata.filename || 'file';\r\n }\r\n\r\n // Download\r\n progress({ phase: 'downloading', text: 'Starting download...', percent: 0, processedBytes: 0, totalBytes });\r\n\r\n const dataChunks: Uint8Array[] = [];\r\n const collectData = !onData;\r\n\r\n const receivedBytes = await this._streamFileIntoCallback(\r\n baseUrl, fileId, isEncrypted, cryptoKey, compat, signal, timeoutMs,\r\n async (chunk) => {\r\n if (collectData) {\r\n dataChunks.push(chunk);\r\n } else {\r\n await onData!(chunk);\r\n }\r\n },\r\n (bytes) => {\r\n progress({\r\n phase: 'downloading', text: 'Downloading...',\r\n percent: totalBytes > 0 ? (bytes / totalBytes) * 100 : 0,\r\n processedBytes: bytes, totalBytes,\r\n });\r\n },\r\n );\r\n\r\n progress({ phase: 'complete', text: 'Download complete!', percent: 100, processedBytes: receivedBytes, totalBytes });\r\n\r\n let data: Uint8Array | undefined;\r\n if (collectData && dataChunks.length > 0) {\r\n const totalLength = dataChunks.reduce((sum, c) => sum + c.length, 0);\r\n data = new Uint8Array(totalLength);\r\n let offset = 0;\r\n for (const c of dataChunks) { data.set(c, offset); offset += c.length; }\r\n }\r\n\r\n return {\r\n filename, receivedBytes, wasEncrypted: isEncrypted,\r\n ...(data ? { data } : {}),\r\n };\r\n }\r\n\r\n /**\r\n * Stream a single file's content into a callback, handling decryption if needed.\r\n * Returns total bytes received from the network (encrypted size).\r\n */\r\n private async _streamFileIntoCallback(\r\n baseUrl: string,\r\n fileId: string,\r\n isEncrypted: boolean,\r\n cryptoKey: CryptoKey | undefined,\r\n compat: CompatibilityResult & { serverInfo: ServerInfo; baseUrl: string },\r\n signal: AbortSignal | undefined,\r\n timeoutMs: number,\r\n onChunk?: (chunk: Uint8Array) => void | Promise<void>,\r\n onBytesReceived?: (receivedBytes: number) => void,\r\n ): Promise<number> {\r\n const { signal: downloadSignal, cleanup: downloadCleanup } = makeAbortSignal(signal, timeoutMs);\r\n let receivedBytes = 0;\r\n\r\n try {\r\n const downloadRes = await this.fetchFn(`${baseUrl}/api/file/${fileId}`, {\r\n method: 'GET', signal: downloadSignal,\r\n });\r\n\r\n if (!downloadRes.ok) throw new DropgateProtocolError(`Download failed (status ${downloadRes.status}).`);\r\n if (!downloadRes.body) throw new DropgateProtocolError('Streaming response not available.');\r\n\r\n const reader = downloadRes.body.getReader();\r\n\r\n if (isEncrypted && cryptoKey) {\r\n const downloadChunkSize = (Number.isFinite(compat.serverInfo?.capabilities?.upload?.chunkSize) && compat.serverInfo.capabilities!.upload!.chunkSize! > 0)\r\n ? compat.serverInfo.capabilities!.upload!.chunkSize!\r\n : this.chunkSize;\r\n const ENCRYPTED_CHUNK_SIZE = downloadChunkSize + ENCRYPTION_OVERHEAD_PER_CHUNK;\r\n const pendingChunks: Uint8Array[] = [];\r\n let pendingLength = 0;\r\n\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) { result.set(chunk, offset); offset += chunk.length; }\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) throw new DropgateAbortError('Download cancelled.');\r\n const { done, value } = await reader.read();\r\n if (done) break;\r\n\r\n pendingChunks.push(value);\r\n pendingLength += value.length;\r\n receivedBytes += value.length;\r\n if (onBytesReceived) onBytesReceived(receivedBytes);\r\n\r\n while (pendingLength >= ENCRYPTED_CHUNK_SIZE) {\r\n const buffer = flushPending();\r\n const encryptedChunk = buffer.subarray(0, ENCRYPTED_CHUNK_SIZE);\r\n if (buffer.length > ENCRYPTED_CHUNK_SIZE) {\r\n pendingChunks.push(buffer.subarray(ENCRYPTED_CHUNK_SIZE));\r\n pendingLength = buffer.length - ENCRYPTED_CHUNK_SIZE;\r\n }\r\n\r\n const decryptedBuffer = await decryptChunk(this.cryptoObj, encryptedChunk, cryptoKey);\r\n if (onChunk) await onChunk(new Uint8Array(decryptedBuffer));\r\n }\r\n }\r\n\r\n if (pendingLength > 0) {\r\n const buffer = flushPending();\r\n const decryptedBuffer = await decryptChunk(this.cryptoObj, buffer, cryptoKey);\r\n if (onChunk) await onChunk(new Uint8Array(decryptedBuffer));\r\n }\r\n } else {\r\n while (true) {\r\n if (signal?.aborted) throw new DropgateAbortError('Download cancelled.');\r\n const { done, value } = await reader.read();\r\n if (done) break;\r\n receivedBytes += value.length;\r\n if (onBytesReceived) onBytesReceived(receivedBytes);\r\n if (onChunk) await onChunk(value);\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') throw new DropgateAbortError('Download cancelled.');\r\n throw new DropgateNetworkError('Download failed.', { cause: err });\r\n } finally {\r\n downloadCleanup();\r\n }\r\n\r\n return receivedBytes;\r\n }\r\n\r\n /**\r\n * Start a P2P send session. Connects to the signalling server and waits for a receiver.\r\n *\r\n * Server info, peerjsPath, iceServers, and cryptoObj are provided automatically\r\n * from the client's cached server info and configuration.\r\n *\r\n * @param opts - P2P send options (file, Peer constructor, callbacks, tuning).\r\n * @returns P2P send session with control methods.\r\n * @throws {DropgateValidationError} If P2P is not enabled on the server.\r\n * @throws {DropgateNetworkError} If the signalling server cannot be reached.\r\n */\r\n async p2pSend(opts: P2PSendFileOptions): Promise<P2PSendSession> {\r\n const compat = await this.connect();\r\n if (!compat.compatible) {\r\n throw new DropgateValidationError(compat.message);\r\n }\r\n\r\n const { serverInfo } = compat;\r\n const p2pCaps = serverInfo?.capabilities?.p2p;\r\n if (!p2pCaps?.enabled) {\r\n throw new DropgateValidationError('Direct transfer is disabled on this server.');\r\n }\r\n\r\n const { host, port, secure } = this.serverTarget;\r\n const { path: peerjsPath, iceServers } = resolvePeerConfig({}, p2pCaps);\r\n\r\n return startP2PSend({\r\n ...opts,\r\n host,\r\n port,\r\n secure,\r\n peerjsPath,\r\n iceServers,\r\n serverInfo,\r\n cryptoObj: this.cryptoObj,\r\n });\r\n }\r\n\r\n /**\r\n * Start a P2P receive session. Connects to a sender via their sharing code.\r\n *\r\n * Server info, peerjsPath, and iceServers are provided automatically\r\n * from the client's cached server info.\r\n *\r\n * @param opts - P2P receive options (code, Peer constructor, callbacks, tuning).\r\n * @returns P2P receive session with control methods.\r\n * @throws {DropgateValidationError} If P2P is not enabled on the server.\r\n * @throws {DropgateNetworkError} If the signalling server cannot be reached.\r\n */\r\n async p2pReceive(opts: P2PReceiveFileOptions): Promise<P2PReceiveSession> {\r\n const compat = await this.connect();\r\n if (!compat.compatible) {\r\n throw new DropgateValidationError(compat.message);\r\n }\r\n\r\n const { serverInfo } = compat;\r\n const p2pCaps = serverInfo?.capabilities?.p2p;\r\n if (!p2pCaps?.enabled) {\r\n throw new DropgateValidationError('Direct transfer is disabled on this server.');\r\n }\r\n\r\n const { host, port, secure } = this.serverTarget;\r\n const { path: peerjsPath, iceServers } = resolvePeerConfig({}, p2pCaps);\r\n\r\n return startP2PReceive({\r\n ...opts,\r\n host,\r\n port,\r\n secure,\r\n peerjsPath,\r\n iceServers,\r\n serverInfo,\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"],"mappings":"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","names":["fl","ZipPassThrough","Zip","err","_a","_b","err","crypto","err","err","ALLOWED_TRANSITIONS","err","err","downloadUrl","result"]}
|