@dropgate/core 2.0.0-beta.1

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.
@@ -0,0 +1,552 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/p2p/index.ts
21
+ var p2p_exports = {};
22
+ __export(p2p_exports, {
23
+ buildPeerOptions: () => buildPeerOptions,
24
+ createPeerWithRetries: () => createPeerWithRetries,
25
+ generateP2PCode: () => generateP2PCode,
26
+ isLocalhostHostname: () => isLocalhostHostname,
27
+ isP2PCodeLike: () => isP2PCodeLike,
28
+ isSecureContextForP2P: () => isSecureContextForP2P,
29
+ startP2PReceive: () => startP2PReceive,
30
+ startP2PSend: () => startP2PSend
31
+ });
32
+ module.exports = __toCommonJS(p2p_exports);
33
+
34
+ // src/errors.ts
35
+ var DropgateError = class extends Error {
36
+ constructor(message, opts = {}) {
37
+ super(message);
38
+ this.name = this.constructor.name;
39
+ this.code = opts.code || "DROPGATE_ERROR";
40
+ this.details = opts.details;
41
+ if (opts.cause !== void 0) {
42
+ Object.defineProperty(this, "cause", {
43
+ value: opts.cause,
44
+ writable: false,
45
+ enumerable: false,
46
+ configurable: true
47
+ });
48
+ }
49
+ }
50
+ };
51
+ var DropgateValidationError = class extends DropgateError {
52
+ constructor(message, opts = {}) {
53
+ super(message, { ...opts, code: opts.code || "VALIDATION_ERROR" });
54
+ }
55
+ };
56
+ var DropgateNetworkError = class extends DropgateError {
57
+ constructor(message, opts = {}) {
58
+ super(message, { ...opts, code: opts.code || "NETWORK_ERROR" });
59
+ }
60
+ };
61
+ var DropgateAbortError = class extends DropgateError {
62
+ constructor(message = "Operation aborted") {
63
+ super(message, { code: "ABORT_ERROR" });
64
+ this.name = "AbortError";
65
+ }
66
+ };
67
+
68
+ // src/utils/network.ts
69
+ function sleep(ms, signal) {
70
+ return new Promise((resolve, reject) => {
71
+ if (signal?.aborted) {
72
+ return reject(signal.reason || new DropgateAbortError());
73
+ }
74
+ const t = setTimeout(resolve, ms);
75
+ if (signal) {
76
+ signal.addEventListener(
77
+ "abort",
78
+ () => {
79
+ clearTimeout(t);
80
+ reject(signal.reason || new DropgateAbortError());
81
+ },
82
+ { once: true }
83
+ );
84
+ }
85
+ });
86
+ }
87
+
88
+ // src/adapters/defaults.ts
89
+ function getDefaultCrypto() {
90
+ return globalThis.crypto;
91
+ }
92
+
93
+ // src/p2p/utils.ts
94
+ function isLocalhostHostname(hostname) {
95
+ const host = String(hostname || "").toLowerCase();
96
+ return host === "localhost" || host === "127.0.0.1" || host === "::1";
97
+ }
98
+ function isSecureContextForP2P(hostname, isSecureContext) {
99
+ return Boolean(isSecureContext) || isLocalhostHostname(hostname || "");
100
+ }
101
+ function generateP2PCode(cryptoObj) {
102
+ const crypto = cryptoObj || getDefaultCrypto();
103
+ const letters = "ABCDEFGHJKLMNPQRSTUVWXYZ";
104
+ if (crypto) {
105
+ const randomBytes = new Uint8Array(8);
106
+ crypto.getRandomValues(randomBytes);
107
+ let letterPart = "";
108
+ for (let i = 0; i < 4; i++) {
109
+ letterPart += letters[randomBytes[i] % letters.length];
110
+ }
111
+ let numberPart = "";
112
+ for (let i = 4; i < 8; i++) {
113
+ numberPart += (randomBytes[i] % 10).toString();
114
+ }
115
+ return `${letterPart}-${numberPart}`;
116
+ }
117
+ let a = "";
118
+ for (let i = 0; i < 4; i++) {
119
+ a += letters[Math.floor(Math.random() * letters.length)];
120
+ }
121
+ let b = "";
122
+ for (let i = 0; i < 4; i++) {
123
+ b += Math.floor(Math.random() * 10);
124
+ }
125
+ return `${a}-${b}`;
126
+ }
127
+ function isP2PCodeLike(code) {
128
+ return /^[A-Z]{4}-\d{4}$/.test(String(code || "").trim());
129
+ }
130
+
131
+ // src/p2p/helpers.ts
132
+ function buildPeerOptions(opts = {}) {
133
+ const { host, port, peerjsPath = "/peerjs", secure = false, iceServers = [] } = opts;
134
+ const peerOpts = {
135
+ host,
136
+ path: peerjsPath,
137
+ secure,
138
+ config: { iceServers },
139
+ debug: 0
140
+ };
141
+ if (port) {
142
+ peerOpts.port = port;
143
+ }
144
+ return peerOpts;
145
+ }
146
+ async function createPeerWithRetries(opts) {
147
+ const { code, codeGenerator, maxAttempts, buildPeer, onCode } = opts;
148
+ let nextCode = code || codeGenerator();
149
+ let peer = null;
150
+ let lastError = null;
151
+ for (let attempt = 0; attempt < maxAttempts; attempt++) {
152
+ onCode?.(nextCode, attempt);
153
+ try {
154
+ peer = await new Promise((resolve, reject) => {
155
+ const instance = buildPeer(nextCode);
156
+ instance.on("open", () => resolve(instance));
157
+ instance.on("error", (err) => {
158
+ try {
159
+ instance.destroy();
160
+ } catch {
161
+ }
162
+ reject(err);
163
+ });
164
+ });
165
+ return { peer, code: nextCode };
166
+ } catch (err) {
167
+ lastError = err;
168
+ nextCode = codeGenerator();
169
+ }
170
+ }
171
+ throw lastError || new DropgateNetworkError("Could not establish PeerJS connection.");
172
+ }
173
+
174
+ // src/p2p/send.ts
175
+ async function startP2PSend(opts) {
176
+ const {
177
+ file,
178
+ Peer,
179
+ serverInfo,
180
+ host,
181
+ port,
182
+ peerjsPath,
183
+ secure = false,
184
+ iceServers,
185
+ codeGenerator,
186
+ cryptoObj,
187
+ maxAttempts = 4,
188
+ chunkSize = 256 * 1024,
189
+ readyTimeoutMs = 8e3,
190
+ endAckTimeoutMs = 15e3,
191
+ bufferHighWaterMark = 8 * 1024 * 1024,
192
+ bufferLowWaterMark = 2 * 1024 * 1024,
193
+ onCode,
194
+ onStatus,
195
+ onProgress,
196
+ onComplete,
197
+ onError
198
+ } = opts;
199
+ if (!file) {
200
+ throw new DropgateValidationError("File is missing.");
201
+ }
202
+ if (!Peer) {
203
+ throw new DropgateValidationError(
204
+ "PeerJS Peer constructor is required. Install peerjs and pass it as the Peer option."
205
+ );
206
+ }
207
+ const p2pCaps = serverInfo?.capabilities?.p2p;
208
+ if (serverInfo && !p2pCaps?.enabled) {
209
+ throw new DropgateValidationError("Direct transfer is disabled on this server.");
210
+ }
211
+ const finalPath = peerjsPath ?? p2pCaps?.peerjsPath ?? "/peerjs";
212
+ const finalIceServers = iceServers ?? p2pCaps?.iceServers ?? [];
213
+ const peerOpts = buildPeerOptions({
214
+ host,
215
+ port,
216
+ peerjsPath: finalPath,
217
+ secure,
218
+ iceServers: finalIceServers
219
+ });
220
+ const finalCodeGenerator = codeGenerator || (() => generateP2PCode(cryptoObj));
221
+ const buildPeer = (id) => new Peer(id, peerOpts);
222
+ const { peer, code } = await createPeerWithRetries({
223
+ code: null,
224
+ codeGenerator: finalCodeGenerator,
225
+ maxAttempts,
226
+ buildPeer,
227
+ onCode
228
+ });
229
+ let stopped = false;
230
+ let activeConn = null;
231
+ let transferActive = false;
232
+ let transferCompleted = false;
233
+ const reportProgress = (data) => {
234
+ const safeTotal = Number.isFinite(data.total) && data.total > 0 ? data.total : file.size;
235
+ const safeReceived = Math.min(Number(data.received) || 0, safeTotal || 0);
236
+ const percent = safeTotal ? safeReceived / safeTotal * 100 : 0;
237
+ onProgress?.({ sent: safeReceived, total: safeTotal, percent });
238
+ };
239
+ const stop = () => {
240
+ stopped = true;
241
+ try {
242
+ activeConn?.close();
243
+ } catch {
244
+ }
245
+ try {
246
+ peer.destroy();
247
+ } catch {
248
+ }
249
+ };
250
+ peer.on("connection", (conn) => {
251
+ if (stopped) return;
252
+ if (activeConn) {
253
+ try {
254
+ conn.send({ t: "error", message: "Another receiver is already connected." });
255
+ } catch {
256
+ }
257
+ try {
258
+ conn.close();
259
+ } catch {
260
+ }
261
+ return;
262
+ }
263
+ activeConn = conn;
264
+ onStatus?.({ phase: "connected", message: "Connected. Starting transfer..." });
265
+ let readyResolve = null;
266
+ let ackResolve = null;
267
+ const readyPromise = new Promise((resolve) => {
268
+ readyResolve = resolve;
269
+ });
270
+ const ackPromise = new Promise((resolve) => {
271
+ ackResolve = resolve;
272
+ });
273
+ conn.on("data", (data) => {
274
+ if (!data || typeof data !== "object" || data instanceof ArrayBuffer || ArrayBuffer.isView(data)) {
275
+ return;
276
+ }
277
+ const msg = data;
278
+ if (!msg.t) return;
279
+ if (msg.t === "ready") {
280
+ readyResolve?.();
281
+ return;
282
+ }
283
+ if (msg.t === "progress") {
284
+ reportProgress({ received: msg.received || 0, total: msg.total || 0 });
285
+ return;
286
+ }
287
+ if (msg.t === "ack" && msg.phase === "end") {
288
+ ackResolve?.(msg);
289
+ return;
290
+ }
291
+ if (msg.t === "error") {
292
+ onError?.(new DropgateNetworkError(msg.message || "Receiver reported an error."));
293
+ stop();
294
+ }
295
+ });
296
+ conn.on("open", async () => {
297
+ try {
298
+ transferActive = true;
299
+ if (stopped) return;
300
+ conn.send({
301
+ t: "meta",
302
+ name: file.name,
303
+ size: file.size,
304
+ mime: file.type || "application/octet-stream"
305
+ });
306
+ let sent = 0;
307
+ const total = file.size;
308
+ const dc = conn._dc;
309
+ if (dc && Number.isFinite(bufferLowWaterMark)) {
310
+ try {
311
+ dc.bufferedAmountLowThreshold = bufferLowWaterMark;
312
+ } catch {
313
+ }
314
+ }
315
+ await Promise.race([readyPromise, sleep(readyTimeoutMs).catch(() => null)]);
316
+ for (let offset = 0; offset < total; offset += chunkSize) {
317
+ if (stopped) return;
318
+ const slice = file.slice(offset, offset + chunkSize);
319
+ const buf = await slice.arrayBuffer();
320
+ conn.send(buf);
321
+ sent += buf.byteLength;
322
+ if (dc) {
323
+ while (dc.bufferedAmount > bufferHighWaterMark) {
324
+ await new Promise((resolve) => {
325
+ const fallback = setTimeout(resolve, 60);
326
+ try {
327
+ dc.addEventListener(
328
+ "bufferedamountlow",
329
+ () => {
330
+ clearTimeout(fallback);
331
+ resolve();
332
+ },
333
+ { once: true }
334
+ );
335
+ } catch {
336
+ }
337
+ });
338
+ }
339
+ }
340
+ }
341
+ if (stopped) return;
342
+ conn.send({ t: "end" });
343
+ const ackTimeoutMs = Number.isFinite(endAckTimeoutMs) ? Math.max(endAckTimeoutMs, Math.ceil(file.size / (1024 * 1024)) * 1e3) : null;
344
+ const ackResult = await Promise.race([
345
+ ackPromise,
346
+ sleep(ackTimeoutMs || 15e3).catch(() => null)
347
+ ]);
348
+ if (!ackResult || typeof ackResult !== "object") {
349
+ throw new DropgateNetworkError("Receiver did not confirm completion.");
350
+ }
351
+ const ackData = ackResult;
352
+ const ackTotal = Number(ackData.total) || file.size;
353
+ const ackReceived = Number(ackData.received) || 0;
354
+ if (ackTotal && ackReceived < ackTotal) {
355
+ throw new DropgateNetworkError("Receiver reported an incomplete transfer.");
356
+ }
357
+ reportProgress({ received: ackReceived || ackTotal, total: ackTotal });
358
+ transferCompleted = true;
359
+ transferActive = false;
360
+ onComplete?.();
361
+ stop();
362
+ } catch (err) {
363
+ onError?.(err);
364
+ stop();
365
+ }
366
+ });
367
+ conn.on("error", (err) => {
368
+ onError?.(err);
369
+ stop();
370
+ });
371
+ conn.on("close", () => {
372
+ if (!transferCompleted && transferActive && !stopped) {
373
+ onError?.(
374
+ new DropgateNetworkError("Receiver disconnected before transfer completed.")
375
+ );
376
+ }
377
+ stop();
378
+ });
379
+ });
380
+ return { peer, code, stop };
381
+ }
382
+
383
+ // src/p2p/receive.ts
384
+ async function startP2PReceive(opts) {
385
+ const {
386
+ code,
387
+ Peer,
388
+ serverInfo,
389
+ host,
390
+ port,
391
+ peerjsPath,
392
+ secure = false,
393
+ iceServers,
394
+ onStatus,
395
+ onMeta,
396
+ onData,
397
+ onProgress,
398
+ onComplete,
399
+ onError,
400
+ onDisconnect
401
+ } = opts;
402
+ if (!code) {
403
+ throw new DropgateValidationError("No sharing code was provided.");
404
+ }
405
+ if (!Peer) {
406
+ throw new DropgateValidationError(
407
+ "PeerJS Peer constructor is required. Install peerjs and pass it as the Peer option."
408
+ );
409
+ }
410
+ const p2pCaps = serverInfo?.capabilities?.p2p;
411
+ if (serverInfo && !p2pCaps?.enabled) {
412
+ throw new DropgateValidationError("Direct transfer is disabled on this server.");
413
+ }
414
+ const normalizedCode = String(code).trim().replace(/\s+/g, "").toUpperCase();
415
+ if (!isP2PCodeLike(normalizedCode)) {
416
+ throw new DropgateValidationError("Invalid direct transfer code.");
417
+ }
418
+ const finalPath = peerjsPath ?? p2pCaps?.peerjsPath ?? "/peerjs";
419
+ const finalIceServers = iceServers ?? p2pCaps?.iceServers ?? [];
420
+ const peerOpts = buildPeerOptions({
421
+ host,
422
+ port,
423
+ peerjsPath: finalPath,
424
+ secure,
425
+ iceServers: finalIceServers
426
+ });
427
+ const peer = new Peer(void 0, peerOpts);
428
+ let total = 0;
429
+ let received = 0;
430
+ let lastProgressSentAt = 0;
431
+ const progressIntervalMs = 120;
432
+ let writeQueue = Promise.resolve();
433
+ const stop = () => {
434
+ try {
435
+ peer.destroy();
436
+ } catch {
437
+ }
438
+ };
439
+ peer.on("error", (err) => {
440
+ onError?.(err);
441
+ stop();
442
+ });
443
+ peer.on("open", () => {
444
+ const conn = peer.connect(normalizedCode, { reliable: true });
445
+ conn.on("open", () => {
446
+ onStatus?.({ phase: "connected", message: "Waiting for file details..." });
447
+ });
448
+ conn.on("data", async (data) => {
449
+ try {
450
+ if (data && typeof data === "object" && !(data instanceof ArrayBuffer) && !ArrayBuffer.isView(data)) {
451
+ const msg = data;
452
+ if (msg.t === "meta") {
453
+ const name = String(msg.name || "file");
454
+ total = Number(msg.size) || 0;
455
+ received = 0;
456
+ writeQueue = Promise.resolve();
457
+ onMeta?.({ name, total });
458
+ onProgress?.({ received, total, percent: 0 });
459
+ try {
460
+ conn.send({ t: "ready" });
461
+ } catch {
462
+ }
463
+ return;
464
+ }
465
+ if (msg.t === "end") {
466
+ await writeQueue;
467
+ if (total && received < total) {
468
+ const err = new DropgateNetworkError(
469
+ "Transfer ended before the full file was received."
470
+ );
471
+ try {
472
+ conn.send({ t: "error", message: err.message });
473
+ } catch {
474
+ }
475
+ throw err;
476
+ }
477
+ onComplete?.({ received, total });
478
+ try {
479
+ conn.send({ t: "ack", phase: "end", received, total });
480
+ } catch {
481
+ }
482
+ return;
483
+ }
484
+ if (msg.t === "error") {
485
+ throw new DropgateNetworkError(msg.message || "Sender reported an error.");
486
+ }
487
+ return;
488
+ }
489
+ let bufPromise;
490
+ if (data instanceof ArrayBuffer) {
491
+ bufPromise = Promise.resolve(new Uint8Array(data));
492
+ } else if (ArrayBuffer.isView(data)) {
493
+ bufPromise = Promise.resolve(
494
+ new Uint8Array(data.buffer, data.byteOffset, data.byteLength)
495
+ );
496
+ } else if (typeof Blob !== "undefined" && data instanceof Blob) {
497
+ bufPromise = data.arrayBuffer().then((buffer) => new Uint8Array(buffer));
498
+ } else {
499
+ return;
500
+ }
501
+ writeQueue = writeQueue.then(async () => {
502
+ const buf = await bufPromise;
503
+ if (onData) {
504
+ await onData(buf);
505
+ }
506
+ received += buf.byteLength;
507
+ const percent = total ? Math.min(100, received / total * 100) : 0;
508
+ onProgress?.({ received, total, percent });
509
+ const now = Date.now();
510
+ if (received === total || now - lastProgressSentAt >= progressIntervalMs) {
511
+ lastProgressSentAt = now;
512
+ try {
513
+ conn.send({ t: "progress", received, total });
514
+ } catch {
515
+ }
516
+ }
517
+ }).catch((err) => {
518
+ try {
519
+ conn.send({
520
+ t: "error",
521
+ message: err?.message || "Receiver write failed."
522
+ });
523
+ } catch {
524
+ }
525
+ onError?.(err);
526
+ stop();
527
+ });
528
+ } catch (err) {
529
+ onError?.(err);
530
+ stop();
531
+ }
532
+ });
533
+ conn.on("close", () => {
534
+ if (received > 0 && total > 0 && received < total) {
535
+ onDisconnect?.();
536
+ }
537
+ });
538
+ });
539
+ return { peer, stop };
540
+ }
541
+ // Annotate the CommonJS export names for ESM import in node:
542
+ 0 && (module.exports = {
543
+ buildPeerOptions,
544
+ createPeerWithRetries,
545
+ generateP2PCode,
546
+ isLocalhostHostname,
547
+ isP2PCodeLike,
548
+ isSecureContextForP2P,
549
+ startP2PReceive,
550
+ startP2PSend
551
+ });
552
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/p2p/index.ts","../../src/errors.ts","../../src/utils/network.ts","../../src/adapters/defaults.ts","../../src/p2p/utils.ts","../../src/p2p/helpers.ts","../../src/p2p/send.ts","../../src/p2p/receive.ts"],"sourcesContent":["// P2P module exports\nexport { startP2PSend } from './send.js';\nexport { startP2PReceive } from './receive.js';\nexport { generateP2PCode, isP2PCodeLike, isLocalhostHostname, isSecureContextForP2P } from './utils.js';\nexport { buildPeerOptions, createPeerWithRetries } from './helpers.js';\n\n// Re-export types\nexport type {\n PeerConstructor,\n PeerInstance,\n PeerOptions,\n DataConnection,\n P2PSendOptions,\n P2PSendSession,\n P2PReceiveOptions,\n P2PReceiveSession,\n} from './types.js';\n","export interface DropgateErrorOptions {\n code?: string;\n details?: unknown;\n cause?: unknown;\n}\n\n/**\n * Base error class for all Dropgate errors\n */\nexport class DropgateError extends Error {\n readonly code: string;\n readonly details?: unknown;\n\n constructor(message: string, opts: DropgateErrorOptions = {}) {\n super(message);\n this.name = this.constructor.name;\n this.code = opts.code || 'DROPGATE_ERROR';\n this.details = opts.details;\n if (opts.cause !== undefined) {\n // Use Object.defineProperty for cause to maintain compatibility\n Object.defineProperty(this, 'cause', {\n value: opts.cause,\n writable: false,\n enumerable: false,\n configurable: true,\n });\n }\n }\n}\n\n/**\n * Validation error for invalid inputs\n */\nexport class DropgateValidationError extends DropgateError {\n constructor(message: string, opts: DropgateErrorOptions = {}) {\n super(message, { ...opts, code: opts.code || 'VALIDATION_ERROR' });\n }\n}\n\n/**\n * Network error for connection issues\n */\nexport class DropgateNetworkError extends DropgateError {\n constructor(message: string, opts: DropgateErrorOptions = {}) {\n super(message, { ...opts, code: opts.code || 'NETWORK_ERROR' });\n }\n}\n\n/**\n * Protocol error for server communication issues\n */\nexport class DropgateProtocolError extends DropgateError {\n constructor(message: string, opts: DropgateErrorOptions = {}) {\n super(message, { ...opts, code: opts.code || 'PROTOCOL_ERROR' });\n }\n}\n\n/**\n * Abort error - replacement for DOMException with AbortError name\n * Used when operations are cancelled\n */\nexport class DropgateAbortError extends DropgateError {\n constructor(message = 'Operation aborted') {\n super(message, { code: 'ABORT_ERROR' });\n this.name = 'AbortError';\n }\n}\n\n/**\n * Timeout error - replacement for DOMException with TimeoutError name\n * Used when operations exceed their time limit\n */\nexport class DropgateTimeoutError extends DropgateError {\n constructor(message = 'Request timed out') {\n super(message, { code: 'TIMEOUT_ERROR' });\n this.name = 'TimeoutError';\n }\n}\n","import { DropgateAbortError, DropgateTimeoutError, DropgateValidationError } from '../errors.js';\nimport type { FetchFn, ServerTarget } from '../types.js';\n\n/**\n * Parse a server URL string into host, port, and secure components.\n * If no protocol is specified, defaults to HTTPS.\n */\nexport function parseServerUrl(urlStr: string): ServerTarget {\n let normalized = urlStr.trim();\n if (!normalized.startsWith('http://') && !normalized.startsWith('https://')) {\n normalized = 'https://' + normalized;\n }\n const url = new URL(normalized);\n return {\n host: url.hostname,\n port: url.port ? Number(url.port) : undefined,\n secure: url.protocol === 'https:',\n };\n}\n\n/**\n * Build a base URL from host, port, and secure options.\n */\nexport function buildBaseUrl(opts: ServerTarget): string {\n const { host, port, secure } = opts;\n\n if (!host || typeof host !== 'string') {\n throw new DropgateValidationError('Server host is required.');\n }\n\n const protocol = secure === false ? 'http' : 'https';\n const portSuffix = port ? `:${port}` : '';\n\n return `${protocol}://${host}${portSuffix}`;\n}\n\n/**\n * Sleep for a specified duration, with optional abort signal support.\n */\nexport function sleep(ms: number, signal?: AbortSignal): Promise<void> {\n return new Promise((resolve, reject) => {\n if (signal?.aborted) {\n return reject(signal.reason || new DropgateAbortError());\n }\n\n const t = setTimeout(resolve, ms);\n\n if (signal) {\n signal.addEventListener(\n 'abort',\n () => {\n clearTimeout(t);\n reject(signal.reason || new DropgateAbortError());\n },\n { once: true }\n );\n }\n });\n}\n\nexport interface AbortSignalWithCleanup {\n signal: AbortSignal;\n cleanup: () => void;\n}\n\n/**\n * Create an AbortSignal that combines a parent signal with a timeout.\n */\nexport function makeAbortSignal(\n parentSignal?: AbortSignal | null,\n timeoutMs?: number\n): AbortSignalWithCleanup {\n const controller = new AbortController();\n let timeoutId: ReturnType<typeof setTimeout> | null = null;\n\n const abort = (reason?: unknown): void => {\n if (!controller.signal.aborted) {\n controller.abort(reason);\n }\n };\n\n if (parentSignal) {\n if (parentSignal.aborted) {\n abort(parentSignal.reason);\n } else {\n parentSignal.addEventListener('abort', () => abort(parentSignal.reason), {\n once: true,\n });\n }\n }\n\n if (Number.isFinite(timeoutMs) && timeoutMs! > 0) {\n timeoutId = setTimeout(() => {\n abort(new DropgateTimeoutError());\n }, timeoutMs);\n }\n\n return {\n signal: controller.signal,\n cleanup: () => {\n if (timeoutId) clearTimeout(timeoutId);\n },\n };\n}\n\nexport interface FetchJsonResult {\n res: Response;\n json: unknown;\n text: string;\n}\n\nexport interface FetchJsonOptions extends Omit<RequestInit, 'signal'> {\n timeoutMs?: number;\n signal?: AbortSignal;\n}\n\n/**\n * Fetch JSON from a URL with timeout and error handling.\n */\nexport async function fetchJson(\n fetchFn: FetchFn,\n url: string,\n opts: FetchJsonOptions = {}\n): Promise<FetchJsonResult> {\n const { timeoutMs, signal, ...rest } = opts;\n const { signal: s, cleanup } = makeAbortSignal(signal, timeoutMs);\n\n try {\n const res = await fetchFn(url, { ...rest, signal: s });\n const text = await res.text();\n\n let json: unknown = null;\n try {\n json = text ? JSON.parse(text) : null;\n } catch {\n // Ignore parse errors - json will remain null\n }\n\n return { res, json, text };\n } finally {\n cleanup();\n }\n}\n","import type { Base64Adapter, CryptoAdapter, FetchFn } from '../types.js';\n\n/**\n * Get the default Base64 adapter for the current environment.\n * Automatically detects Node.js Buffer vs browser btoa/atob.\n */\nexport function getDefaultBase64(): Base64Adapter {\n // Check for Node.js Buffer (works in Node.js and some bundlers)\n if (typeof Buffer !== 'undefined' && typeof Buffer.from === 'function') {\n return {\n encode(bytes: Uint8Array): string {\n return Buffer.from(bytes).toString('base64');\n },\n decode(b64: string): Uint8Array {\n return new Uint8Array(Buffer.from(b64, 'base64'));\n },\n };\n }\n\n // Browser fallback using btoa/atob\n if (typeof btoa === 'function' && typeof atob === 'function') {\n return {\n encode(bytes: Uint8Array): string {\n let binary = '';\n for (let i = 0; i < bytes.length; i++) {\n binary += String.fromCharCode(bytes[i]);\n }\n return btoa(binary);\n },\n decode(b64: string): Uint8Array {\n const binary = atob(b64);\n const out = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) {\n out[i] = binary.charCodeAt(i);\n }\n return out;\n },\n };\n }\n\n throw new Error(\n 'No Base64 implementation available. Provide a Base64Adapter via options.'\n );\n}\n\n/**\n * Get the default crypto object for the current environment.\n * Returns globalThis.crypto if available.\n */\nexport function getDefaultCrypto(): CryptoAdapter | undefined {\n return globalThis.crypto as CryptoAdapter | undefined;\n}\n\n/**\n * Get the default fetch function for the current environment.\n * Returns globalThis.fetch if available.\n */\nexport function getDefaultFetch(): FetchFn | undefined {\n return globalThis.fetch?.bind(globalThis) as FetchFn | undefined;\n}\n","import type { CryptoAdapter } from '../types.js';\nimport { getDefaultCrypto } from '../adapters/defaults.js';\n\n/**\n * Check if a hostname is localhost\n */\nexport function isLocalhostHostname(hostname: string): boolean {\n const host = String(hostname || '').toLowerCase();\n return host === 'localhost' || host === '127.0.0.1' || host === '::1';\n}\n\n/**\n * Check if the current context allows P2P (HTTPS or localhost)\n */\nexport function isSecureContextForP2P(\n hostname?: string,\n isSecureContext?: boolean\n): boolean {\n return Boolean(isSecureContext) || isLocalhostHostname(hostname || '');\n}\n\n/**\n * Generate a P2P sharing code using cryptographically secure random.\n * Format: XXXX-0000 (4 letters + 4 digits)\n */\nexport function generateP2PCode(cryptoObj?: CryptoAdapter): string {\n const crypto = cryptoObj || getDefaultCrypto();\n const letters = 'ABCDEFGHJKLMNPQRSTUVWXYZ'; // Excluded I and O to avoid confusion\n\n if (crypto) {\n const randomBytes = new Uint8Array(8);\n crypto.getRandomValues(randomBytes);\n\n let letterPart = '';\n for (let i = 0; i < 4; i++) {\n letterPart += letters[randomBytes[i] % letters.length];\n }\n\n let numberPart = '';\n for (let i = 4; i < 8; i++) {\n numberPart += (randomBytes[i] % 10).toString();\n }\n\n return `${letterPart}-${numberPart}`;\n }\n\n // Fallback to Math.random (less secure, but works everywhere)\n let a = '';\n for (let i = 0; i < 4; i++) {\n a += letters[Math.floor(Math.random() * letters.length)];\n }\n let b = '';\n for (let i = 0; i < 4; i++) {\n b += Math.floor(Math.random() * 10);\n }\n return `${a}-${b}`;\n}\n\n/**\n * Check if a string looks like a P2P sharing code\n */\nexport function isP2PCodeLike(code: string): boolean {\n return /^[A-Z]{4}-\\d{4}$/.test(String(code || '').trim());\n}\n","import { DropgateNetworkError } from '../errors.js';\nimport type { PeerInstance, PeerOptions } from './types.js';\n\nexport interface BuildPeerOptionsInput {\n host?: string;\n port?: number;\n peerjsPath?: string;\n secure?: boolean;\n iceServers?: RTCIceServer[];\n}\n\n/**\n * Build PeerJS connection options\n */\nexport function buildPeerOptions(opts: BuildPeerOptionsInput = {}): PeerOptions {\n const { host, port, peerjsPath = '/peerjs', secure = false, iceServers = [] } = opts;\n\n const peerOpts: PeerOptions = {\n host,\n path: peerjsPath,\n secure,\n config: { iceServers },\n debug: 0,\n };\n\n if (port) {\n peerOpts.port = port;\n }\n\n return peerOpts;\n}\n\nexport interface CreatePeerWithRetriesOptions {\n code?: string | null;\n codeGenerator: () => string;\n maxAttempts: number;\n buildPeer: (id: string) => PeerInstance;\n onCode?: (code: string, attempt: number) => void;\n}\n\n/**\n * Create a peer with retries if the code is already taken\n */\nexport async function createPeerWithRetries(\n opts: CreatePeerWithRetriesOptions\n): Promise<{ peer: PeerInstance; code: string }> {\n const { code, codeGenerator, maxAttempts, buildPeer, onCode } = opts;\n\n let nextCode = code || codeGenerator();\n let peer: PeerInstance | null = null;\n let lastError: Error | null = null;\n\n for (let attempt = 0; attempt < maxAttempts; attempt++) {\n onCode?.(nextCode, attempt);\n\n try {\n peer = await new Promise<PeerInstance>((resolve, reject) => {\n const instance = buildPeer(nextCode);\n instance.on('open', () => resolve(instance));\n instance.on('error', (err: Error) => {\n try {\n instance.destroy();\n } catch {\n // Ignore destroy errors\n }\n reject(err);\n });\n });\n\n return { peer, code: nextCode };\n } catch (err) {\n lastError = err as Error;\n nextCode = codeGenerator();\n }\n }\n\n throw lastError || new DropgateNetworkError('Could not establish PeerJS connection.');\n}\n","import { DropgateValidationError, DropgateNetworkError } from '../errors.js';\nimport { sleep } from '../utils/network.js';\nimport type { P2PSendOptions, P2PSendSession, DataConnection } from './types.js';\nimport { generateP2PCode } from './utils.js';\nimport { buildPeerOptions, createPeerWithRetries } from './helpers.js';\n\n/**\n * Start a direct transfer (P2P) sender session.\n *\n * IMPORTANT: Consumer must provide the PeerJS Peer constructor.\n * This removes DOM coupling (no script injection).\n *\n * Example:\n * ```js\n * import Peer from 'peerjs';\n * import { startP2PSend } from '@dropgate/core/p2p';\n *\n * const session = await startP2PSend({\n * file: myFile,\n * Peer,\n * host: 'dropgate.link',\n * secure: true,\n * onCode: (code) => console.log('Share this code:', code),\n * onProgress: (evt) => console.log(`${evt.percent}% sent`),\n * onComplete: () => console.log('Done!'),\n * });\n * ```\n */\nexport async function startP2PSend(opts: P2PSendOptions): Promise<P2PSendSession> {\n const {\n file,\n Peer,\n serverInfo,\n host,\n port,\n peerjsPath,\n secure = false,\n iceServers,\n codeGenerator,\n cryptoObj,\n maxAttempts = 4,\n chunkSize = 256 * 1024,\n readyTimeoutMs = 8000,\n endAckTimeoutMs = 15000,\n bufferHighWaterMark = 8 * 1024 * 1024,\n bufferLowWaterMark = 2 * 1024 * 1024,\n onCode,\n onStatus,\n onProgress,\n onComplete,\n onError,\n } = opts;\n\n // Validate required options\n if (!file) {\n throw new DropgateValidationError('File is missing.');\n }\n\n if (!Peer) {\n throw new DropgateValidationError(\n 'PeerJS Peer constructor is required. Install peerjs and pass it as the Peer option.'\n );\n }\n\n // Check P2P capabilities if serverInfo is provided\n const p2pCaps = serverInfo?.capabilities?.p2p;\n if (serverInfo && !p2pCaps?.enabled) {\n throw new DropgateValidationError('Direct transfer is disabled on this server.');\n }\n\n // Determine options (use serverInfo if available)\n const finalPath = peerjsPath ?? p2pCaps?.peerjsPath ?? '/peerjs';\n const finalIceServers = iceServers ?? p2pCaps?.iceServers ?? [];\n\n // Build peer options\n const peerOpts = buildPeerOptions({\n host,\n port,\n peerjsPath: finalPath,\n secure,\n iceServers: finalIceServers,\n });\n\n // Create the code generator\n const finalCodeGenerator = codeGenerator || (() => generateP2PCode(cryptoObj));\n\n // Create peer with retries\n const buildPeer = (id: string) => new Peer(id, peerOpts);\n const { peer, code } = await createPeerWithRetries({\n code: null,\n codeGenerator: finalCodeGenerator,\n maxAttempts,\n buildPeer,\n onCode,\n });\n\n let stopped = false;\n let activeConn: DataConnection | null = null;\n let transferActive = false;\n let transferCompleted = false;\n\n const reportProgress = (data: { received: number; total: number }): void => {\n const safeTotal =\n Number.isFinite(data.total) && data.total > 0 ? data.total : file.size;\n const safeReceived = Math.min(Number(data.received) || 0, safeTotal || 0);\n const percent = safeTotal ? (safeReceived / safeTotal) * 100 : 0;\n onProgress?.({ sent: safeReceived, total: safeTotal, percent });\n };\n\n const stop = (): void => {\n stopped = true;\n try {\n activeConn?.close();\n } catch {\n // Ignore close errors\n }\n try {\n peer.destroy();\n } catch {\n // Ignore destroy errors\n }\n };\n\n peer.on('connection', (conn: DataConnection) => {\n if (stopped) return;\n\n if (activeConn) {\n try {\n conn.send({ t: 'error', message: 'Another receiver is already connected.' });\n } catch {\n // Ignore send errors\n }\n try {\n conn.close();\n } catch {\n // Ignore close errors\n }\n return;\n }\n\n activeConn = conn;\n onStatus?.({ phase: 'connected', message: 'Connected. Starting transfer...' });\n\n let readyResolve: (() => void) | null = null;\n let ackResolve: ((data: unknown) => void) | null = null;\n\n const readyPromise = new Promise<void>((resolve) => {\n readyResolve = resolve;\n });\n\n const ackPromise = new Promise<unknown>((resolve) => {\n ackResolve = resolve;\n });\n\n conn.on('data', (data: unknown) => {\n if (\n !data ||\n typeof data !== 'object' ||\n data instanceof ArrayBuffer ||\n ArrayBuffer.isView(data)\n ) {\n return;\n }\n\n const msg = data as { t?: string; received?: number; total?: number; phase?: string; message?: string };\n if (!msg.t) return;\n\n if (msg.t === 'ready') {\n readyResolve?.();\n return;\n }\n\n if (msg.t === 'progress') {\n reportProgress({ received: msg.received || 0, total: msg.total || 0 });\n return;\n }\n\n if (msg.t === 'ack' && msg.phase === 'end') {\n ackResolve?.(msg);\n return;\n }\n\n if (msg.t === 'error') {\n onError?.(new DropgateNetworkError(msg.message || 'Receiver reported an error.'));\n stop();\n }\n });\n\n conn.on('open', async () => {\n try {\n transferActive = true;\n if (stopped) return;\n\n conn.send({\n t: 'meta',\n name: file.name,\n size: file.size,\n mime: file.type || 'application/octet-stream',\n });\n\n let sent = 0;\n const total = file.size;\n const dc = conn._dc;\n\n if (dc && Number.isFinite(bufferLowWaterMark)) {\n try {\n dc.bufferedAmountLowThreshold = bufferLowWaterMark;\n } catch {\n // Ignore threshold setting errors\n }\n }\n\n // Wait for ready signal\n await Promise.race([readyPromise, sleep(readyTimeoutMs).catch(() => null)]);\n\n // Send file in chunks\n for (let offset = 0; offset < total; offset += chunkSize) {\n if (stopped) return;\n\n const slice = file.slice(offset, offset + chunkSize);\n const buf = await slice.arrayBuffer();\n conn.send(buf);\n sent += buf.byteLength;\n\n // Flow control\n if (dc) {\n while (dc.bufferedAmount > bufferHighWaterMark) {\n await new Promise<void>((resolve) => {\n const fallback = setTimeout(resolve, 60);\n try {\n dc.addEventListener(\n 'bufferedamountlow',\n () => {\n clearTimeout(fallback);\n resolve();\n },\n { once: true }\n );\n } catch {\n // Fallback only\n }\n });\n }\n }\n }\n\n if (stopped) return;\n conn.send({ t: 'end' });\n\n // Wait for acknowledgment\n const ackTimeoutMs = Number.isFinite(endAckTimeoutMs)\n ? Math.max(endAckTimeoutMs, Math.ceil(file.size / (1024 * 1024)) * 1000)\n : null;\n\n const ackResult = await Promise.race([\n ackPromise,\n sleep(ackTimeoutMs || 15000).catch(() => null),\n ]);\n\n if (!ackResult || typeof ackResult !== 'object') {\n throw new DropgateNetworkError('Receiver did not confirm completion.');\n }\n\n const ackData = ackResult as { total?: number; received?: number };\n const ackTotal = Number(ackData.total) || file.size;\n const ackReceived = Number(ackData.received) || 0;\n\n if (ackTotal && ackReceived < ackTotal) {\n throw new DropgateNetworkError('Receiver reported an incomplete transfer.');\n }\n\n reportProgress({ received: ackReceived || ackTotal, total: ackTotal });\n transferCompleted = true;\n transferActive = false;\n onComplete?.();\n stop();\n } catch (err) {\n onError?.(err as Error);\n stop();\n }\n });\n\n conn.on('error', (err: Error) => {\n onError?.(err);\n stop();\n });\n\n conn.on('close', () => {\n if (!transferCompleted && transferActive && !stopped) {\n onError?.(\n new DropgateNetworkError('Receiver disconnected before transfer completed.')\n );\n }\n stop();\n });\n });\n\n return { peer, code, stop };\n}\n","import { DropgateValidationError, DropgateNetworkError } from '../errors.js';\nimport type { P2PReceiveOptions, P2PReceiveSession } from './types.js';\nimport { isP2PCodeLike } from './utils.js';\nimport { buildPeerOptions } from './helpers.js';\n\n/**\n * Start a direct transfer (P2P) receiver session.\n *\n * IMPORTANT: Consumer must provide the PeerJS Peer constructor and handle file writing.\n * This removes DOM coupling (no streamSaver).\n *\n * Example:\n * ```js\n * import Peer from 'peerjs';\n * import { startP2PReceive } from '@dropgate/core/p2p';\n *\n * let writer;\n * const session = await startP2PReceive({\n * code: 'ABCD-1234',\n * Peer,\n * host: 'dropgate.link',\n * secure: true,\n * onMeta: ({ name, total }) => {\n * // Consumer creates file writer\n * writer = createWriteStream(name);\n * },\n * onData: async (chunk) => {\n * // Consumer writes data\n * await writer.write(chunk);\n * },\n * onComplete: () => {\n * writer.close();\n * console.log('Done!');\n * },\n * });\n * ```\n */\nexport async function startP2PReceive(opts: P2PReceiveOptions): Promise<P2PReceiveSession> {\n const {\n code,\n Peer,\n serverInfo,\n host,\n port,\n peerjsPath,\n secure = false,\n iceServers,\n onStatus,\n onMeta,\n onData,\n onProgress,\n onComplete,\n onError,\n onDisconnect,\n } = opts;\n\n // Validate required options\n if (!code) {\n throw new DropgateValidationError('No sharing code was provided.');\n }\n\n if (!Peer) {\n throw new DropgateValidationError(\n 'PeerJS Peer constructor is required. Install peerjs and pass it as the Peer option.'\n );\n }\n\n // Check P2P capabilities if serverInfo is provided\n const p2pCaps = serverInfo?.capabilities?.p2p;\n if (serverInfo && !p2pCaps?.enabled) {\n throw new DropgateValidationError('Direct transfer is disabled on this server.');\n }\n\n // Validate and normalize code\n const normalizedCode = String(code).trim().replace(/\\s+/g, '').toUpperCase();\n if (!isP2PCodeLike(normalizedCode)) {\n throw new DropgateValidationError('Invalid direct transfer code.');\n }\n\n // Determine options (use serverInfo if available)\n const finalPath = peerjsPath ?? p2pCaps?.peerjsPath ?? '/peerjs';\n const finalIceServers = iceServers ?? p2pCaps?.iceServers ?? [];\n\n // Build peer options\n const peerOpts = buildPeerOptions({\n host,\n port,\n peerjsPath: finalPath,\n secure,\n iceServers: finalIceServers,\n });\n\n // Create peer (receiver doesn't need a specific ID)\n const peer = new Peer(undefined, peerOpts);\n\n let total = 0;\n let received = 0;\n let lastProgressSentAt = 0;\n const progressIntervalMs = 120;\n let writeQueue = Promise.resolve();\n\n const stop = (): void => {\n try {\n peer.destroy();\n } catch {\n // Ignore destroy errors\n }\n };\n\n peer.on('error', (err: Error) => {\n onError?.(err);\n stop();\n });\n\n peer.on('open', () => {\n const conn = peer.connect(normalizedCode, { reliable: true });\n\n conn.on('open', () => {\n onStatus?.({ phase: 'connected', message: 'Waiting for file details...' });\n });\n\n conn.on('data', async (data: unknown) => {\n try {\n // Handle control messages\n if (\n data &&\n typeof data === 'object' &&\n !(data instanceof ArrayBuffer) &&\n !ArrayBuffer.isView(data)\n ) {\n const msg = data as {\n t?: string;\n name?: string;\n size?: number;\n message?: string;\n };\n\n if (msg.t === 'meta') {\n const name = String(msg.name || 'file');\n total = Number(msg.size) || 0;\n received = 0;\n writeQueue = Promise.resolve();\n\n onMeta?.({ name, total });\n onProgress?.({ received, total, percent: 0 });\n\n try {\n conn.send({ t: 'ready' });\n } catch {\n // Ignore send errors\n }\n return;\n }\n\n if (msg.t === 'end') {\n await writeQueue;\n\n if (total && received < total) {\n const err = new DropgateNetworkError(\n 'Transfer ended before the full file was received.'\n );\n try {\n conn.send({ t: 'error', message: err.message });\n } catch {\n // Ignore send errors\n }\n throw err;\n }\n\n onComplete?.({ received, total });\n\n try {\n conn.send({ t: 'ack', phase: 'end', received, total });\n } catch {\n // Ignore send errors\n }\n return;\n }\n\n if (msg.t === 'error') {\n throw new DropgateNetworkError(msg.message || 'Sender reported an error.');\n }\n\n return;\n }\n\n // Handle binary data\n let bufPromise: Promise<Uint8Array>;\n\n if (data instanceof ArrayBuffer) {\n bufPromise = Promise.resolve(new Uint8Array(data));\n } else if (ArrayBuffer.isView(data)) {\n bufPromise = Promise.resolve(\n new Uint8Array(data.buffer, data.byteOffset, data.byteLength)\n );\n } else if (typeof Blob !== 'undefined' && data instanceof Blob) {\n bufPromise = data.arrayBuffer().then((buffer) => new Uint8Array(buffer));\n } else {\n return;\n }\n\n writeQueue = writeQueue\n .then(async () => {\n const buf = await bufPromise;\n\n // Call consumer's onData handler\n if (onData) {\n await onData(buf);\n }\n\n received += buf.byteLength;\n const percent = total ? Math.min(100, (received / total) * 100) : 0;\n onProgress?.({ received, total, percent });\n\n const now = Date.now();\n if (received === total || now - lastProgressSentAt >= progressIntervalMs) {\n lastProgressSentAt = now;\n try {\n conn.send({ t: 'progress', received, total });\n } catch {\n // Ignore send errors\n }\n }\n })\n .catch((err) => {\n try {\n conn.send({\n t: 'error',\n message: (err as Error)?.message || 'Receiver write failed.',\n });\n } catch {\n // Ignore send errors\n }\n onError?.(err as Error);\n stop();\n });\n } catch (err) {\n onError?.(err as Error);\n stop();\n }\n });\n\n conn.on('close', () => {\n if (received > 0 && total > 0 && received < total) {\n onDisconnect?.();\n }\n });\n });\n\n return { peer, stop };\n}\n"],"mappings":"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","names":[]}