@dropgate/core 2.2.1 → 3.0.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.
package/README.md CHANGED
@@ -9,7 +9,7 @@
9
9
  <div align="center">
10
10
 
11
11
  ![license](https://img.shields.io/badge/license-Apache--2.0-blue?style=flat-square)
12
- ![version](https://img.shields.io/badge/version-2.2.1-brightgreen?style=flat-square)
12
+ ![version](https://img.shields.io/badge/version-3.0.1-brightgreen?style=flat-square)
13
13
  ![typescript](https://img.shields.io/badge/TypeScript-5.0+-blue?style=flat-square)
14
14
 
15
15
  [![discord](https://img.shields.io/discord/667479986214666272?logo=discord&logoColor=white&style=flat-square)](https://diamonddigital.dev/discord)
@@ -17,7 +17,7 @@
17
17
 
18
18
  </div>
19
19
 
20
- ## 🌍 Overview
20
+ ## Overview
21
21
 
22
22
  **@dropgate/core** is the universal client library for Dropgate. It provides all the core functionality for:
23
23
 
@@ -25,17 +25,17 @@
25
25
  - Downloading files from Dropgate servers
26
26
  - Direct peer-to-peer file transfers (P2P)
27
27
  - Server capability detection and version checking
28
- - Utility functions for URL parsing/building, lifetime conversions, and more
28
+ - Utility functions for lifetime conversions, base64 encoding, and more
29
29
 
30
30
  This package is **headless** and **environment-agnostic** — it contains no DOM manipulation, no browser-specific APIs, and no Node.js-specific code. All environment-specific concerns (loading PeerJS, handling file streams, etc.) are handled by the consumer.
31
31
 
32
- ## đŸ“Ļ Installation
32
+ ## Installation
33
33
 
34
34
  ```bash
35
35
  npm install @dropgate/core
36
36
  ```
37
37
 
38
- ## 🔨 Builds
38
+ ## Builds
39
39
 
40
40
  The package ships with multiple build targets:
41
41
 
@@ -45,144 +45,174 @@ The package ships with multiple build targets:
45
45
  | CJS | `dist/index.cjs` | Legacy Node.js, CommonJS |
46
46
  | Browser IIFE | `dist/index.browser.js` | `<script>` tag, exposes `DropgateCore` global |
47
47
 
48
- ## 🚀 Quick Start
48
+ ## Quick Start
49
49
 
50
- ### âŦ†ī¸ Uploading a File
50
+ ### Configure Once, Use Everywhere
51
+
52
+ All operations go through a single `DropgateClient` instance. Server connection details are specified once in the constructor:
51
53
 
52
54
  ```javascript
53
55
  import { DropgateClient } from '@dropgate/core';
54
56
 
55
- const client = new DropgateClient({ clientVersion: '2.2.1' });
57
+ const client = new DropgateClient({
58
+ clientVersion: '3.0.1',
59
+ server: 'https://dropgate.link', // URL string or { host, port?, secure? }
60
+ fallbackToHttp: true, // auto-retry HTTP if HTTPS fails (optional)
61
+ });
62
+ ```
63
+
64
+ ### Connecting to the Server
65
+
66
+ `connect()` fetches server info, checks version compatibility, and caches the result. All methods call `connect()` internally, so explicit calls are optional — useful for "Test Connection" buttons or eager validation.
67
+
68
+ ```javascript
69
+ const { serverInfo, compatible, message } = await client.connect({ timeoutMs: 5000 });
70
+
71
+ console.log('Server version:', serverInfo.version);
72
+ console.log('Compatible:', compatible);
73
+ console.log('Upload enabled:', serverInfo.capabilities?.upload?.enabled);
74
+ console.log('P2P enabled:', serverInfo.capabilities?.p2p?.enabled);
75
+ ```
76
+
77
+ ### Uploading Files
56
78
 
57
- const result = await client.uploadFile({
58
- host: 'dropgate.link',
59
- port: 443,
60
- secure: true,
61
- file: myFile, // File or Blob
79
+ ```javascript
80
+ const session = await client.uploadFiles({
81
+ file: myFile, // File or Blob (implements FileSource)
62
82
  lifetimeMs: 3600000, // 1 hour
63
- maxDownloads: 5, // Limit number of downloads
83
+ maxDownloads: 5,
64
84
  encrypt: true,
65
85
  onProgress: ({ phase, text, percent }) => {
66
- console.log(`${phase}: ${text} (${percent ? percent : 0}%)`);
86
+ console.log(`${phase}: ${text} (${percent ?? 0}%)`);
67
87
  },
68
88
  });
69
89
 
90
+ const result = await session.result;
70
91
  console.log('Download URL:', result.downloadUrl);
92
+
93
+ // Cancel an in-progress upload:
94
+ // session.cancel('User cancelled');
71
95
  ```
72
96
 
73
- ### â„šī¸ Getting Server Info
97
+ ### Fetching File/Bundle Metadata
74
98
 
75
99
  ```javascript
76
- import { getServerInfo } from '@dropgate/core';
77
-
78
- const { serverInfo } = await getServerInfo({
79
- host: 'dropgate.link',
80
- secure: true,
81
- timeoutMs: 5000,
100
+ // Fetch file metadata (size, encryption status, filename)
101
+ const fileMeta = await client.getFileMetadata('file-id-123');
102
+ console.log('File size:', fileMeta.sizeBytes);
103
+ console.log('Encrypted:', fileMeta.isEncrypted);
104
+ console.log('Filename:', fileMeta.filename || fileMeta.encryptedFilename);
105
+
106
+ // Fetch bundle metadata with automatic derivation
107
+ const bundleMeta = await client.getBundleMetadata(
108
+ 'bundle-id-456',
109
+ 'base64-key-from-url-hash' // Required for encrypted bundles
110
+ );
111
+
112
+ console.log('Files:', bundleMeta.fileCount);
113
+ console.log('Total size:', bundleMeta.totalSizeBytes);
114
+ console.log('Sealed:', bundleMeta.sealed);
115
+
116
+ // For sealed bundles, the manifest is automatically decrypted
117
+ // and files array is populated from the decrypted manifest
118
+ bundleMeta.files.forEach(file => {
119
+ console.log(`- ${file.filename}: ${file.sizeBytes} bytes`);
82
120
  });
83
-
84
- console.log('Server version:', serverInfo.version);
85
- console.log('Upload enabled:', serverInfo.capabilities?.upload?.enabled);
86
- console.log('P2P enabled:', serverInfo.capabilities?.p2p?.enabled);
87
121
  ```
88
122
 
89
- ### 🔍 Checking Compatibility
123
+ ### Downloading Files
90
124
 
91
125
  ```javascript
92
- import { DropgateClient } from '@dropgate/core';
93
-
94
- const client = new DropgateClient({ clientVersion: '2.2.1' });
95
-
96
- // checkCompatibility fetches server info internally and compares versions
97
- const compat = await client.checkCompatibility({
98
- host: 'dropgate.link',
99
- secure: true,
100
- timeoutMs: 5000,
126
+ // Download with streaming (for large files)
127
+ const result = await client.downloadFiles({
128
+ fileId: 'abc123',
129
+ keyB64: 'base64-key-from-url-hash', // Required for encrypted files
130
+ onProgress: ({ phase, percent, processedBytes, totalBytes }) => {
131
+ console.log(`${phase}: ${percent}% (${processedBytes}/${totalBytes})`);
132
+ },
133
+ onData: async (chunk) => {
134
+ await writer.write(chunk);
135
+ },
101
136
  });
102
137
 
103
- console.log('Compatible:', compat.compatible);
104
- console.log('Message:', compat.message);
105
- console.log('Server version:', compat.serverVersion);
106
- console.log('Client version:', compat.clientVersion);
107
- // Also returns serverInfo and baseUrl for convenience
108
- console.log('Server capabilities:', compat.serverInfo.capabilities);
138
+ console.log('Downloaded:', result.filename);
139
+
140
+ // Or download to memory (for small files — omit onData)
141
+ const memoryResult = await client.downloadFiles({ fileId: 'abc123' });
142
+ console.log('File size:', memoryResult.data?.length);
109
143
  ```
110
144
 
111
- ### 📤 P2P File Transfer (Sender)
145
+ ### P2P File Transfer (Sender)
112
146
 
113
147
  ```javascript
114
- import { startP2PSend } from '@dropgate/core';
115
-
116
- // Consumer must provide PeerJS Peer constructor
117
148
  const Peer = await loadPeerJS(); // Your loader function
118
149
 
119
- const session = await startP2PSend({
150
+ const session = await client.p2pSend({
120
151
  file: myFile,
121
152
  Peer,
122
- host: 'dropgate.link',
123
- port: 443,
124
- secure: true,
125
153
  onCode: (code) => console.log('Share this code:', code),
126
154
  onProgress: ({ processedBytes, totalBytes, percent }) => {
127
155
  console.log(`Sending: ${percent.toFixed(1)}%`);
128
156
  },
129
157
  onComplete: () => console.log('Transfer complete!'),
130
158
  onError: (err) => console.error('Error:', err),
159
+ onCancel: ({ cancelledBy }) => console.log(`Cancelled by ${cancelledBy}`),
160
+ onDisconnect: () => console.log('Receiver disconnected'),
131
161
  });
132
162
 
133
- // To cancel:
134
- // session.stop();
163
+ // Session control
164
+ console.log('Status:', session.getStatus());
165
+ console.log('Bytes sent:', session.getBytesSent());
166
+ session.stop(); // Cancel
135
167
  ```
136
168
 
137
- ### đŸ“Ĩ P2P File Transfer (Receiver)
169
+ ### P2P File Transfer (Receiver)
138
170
 
139
171
  ```javascript
140
- import { startP2PReceive } from '@dropgate/core';
141
-
142
172
  const Peer = await loadPeerJS();
143
173
 
144
- const session = await startP2PReceive({
174
+ const session = await client.p2pReceive({
145
175
  code: 'ABCD-1234',
146
176
  Peer,
147
- host: 'dropgate.link',
148
- port: 443,
149
- secure: true,
150
- onMeta: ({ name, total }) => {
177
+ onMeta: ({ name, total, fileCount, files }) => {
151
178
  console.log(`Receiving: ${name} (${total} bytes)`);
179
+ if (fileCount) console.log(`Multi-file transfer: ${fileCount} files`);
152
180
  },
153
181
  onData: async (chunk) => {
154
- // Consumer handles file writing (e.g., streamSaver, fs.write)
155
182
  await writer.write(chunk);
156
183
  },
157
184
  onProgress: ({ processedBytes, totalBytes, percent }) => {
158
185
  console.log(`Receiving: ${percent.toFixed(1)}%`);
159
186
  },
160
- onComplete: () => console.log('Transfer complete!'),
187
+ // Multi-file transfers: called when each individual file starts/ends
188
+ onFileStart: ({ fileIndex, name, size }) => {
189
+ console.log(`File ${fileIndex}: ${name} (${size} bytes)`);
190
+ },
191
+ onFileEnd: ({ fileIndex, receivedBytes }) => {
192
+ console.log(`File ${fileIndex} complete (${receivedBytes} bytes)`);
193
+ },
194
+ onComplete: ({ received, total }) => console.log(`Complete! ${received}/${total}`),
195
+ onCancel: ({ cancelledBy }) => console.log(`Cancelled by ${cancelledBy}`),
196
+ onError: (err) => console.error('Error:', err),
197
+ onDisconnect: () => console.log('Sender disconnected'),
161
198
  });
199
+
200
+ session.stop(); // Cancel
162
201
  ```
163
202
 
164
- ### đŸ“Ĩ P2P File Transfer with Preview (Receiver)
203
+ ### P2P with File Preview (Receiver)
165
204
 
166
205
  Use `autoReady: false` to show a file preview before starting the transfer:
167
206
 
168
207
  ```javascript
169
- import { startP2PReceive } from '@dropgate/core';
170
-
171
- const Peer = await loadPeerJS();
172
- let writer;
173
-
174
- const session = await startP2PReceive({
208
+ const session = await client.p2pReceive({
175
209
  code: 'ABCD-1234',
176
210
  Peer,
177
- host: 'dropgate.link',
178
- secure: true,
179
- autoReady: false, // Don't start transfer automatically
211
+ autoReady: false,
180
212
  onMeta: ({ name, total, sendReady }) => {
181
- // Show file preview to user
182
213
  console.log(`File: ${name} (${total} bytes)`);
183
214
  showPreviewUI(name, total);
184
215
 
185
- // When user confirms, create writer and start transfer
186
216
  confirmButton.onclick = () => {
187
217
  writer = createWriteStream(name);
188
218
  sendReady(); // Signal sender to begin transfer
@@ -198,93 +228,182 @@ const session = await startP2PReceive({
198
228
  });
199
229
  ```
200
230
 
201
- ### âŦ‡ī¸ Downloading a File
231
+ ### Standalone Server Info
202
232
 
203
- ```javascript
204
- import { DropgateClient } from '@dropgate/core';
233
+ For one-off checks before constructing a client:
205
234
 
206
- const client = new DropgateClient({ clientVersion: '2.2.1' });
235
+ ```javascript
236
+ import { getServerInfo } from '@dropgate/core';
207
237
 
208
- // Download with streaming (for large files)
209
- const result = await client.downloadFile({
210
- host: 'dropgate.link',
211
- port: 443,
212
- secure: true,
213
- fileId: 'abc123',
214
- keyB64: 'base64-key-from-url-hash', // Required for encrypted files
215
- onProgress: ({ phase, percent, processedBytes, totalBytes }) => {
216
- console.log(`${phase}: ${percent}% (${processedBytes}/${totalBytes})`);
217
- },
218
- onData: async (chunk) => {
219
- // Consumer handles file writing (e.g., fs.write, streamSaver)
220
- await writer.write(chunk);
221
- },
238
+ const { serverInfo } = await getServerInfo({
239
+ server: 'https://dropgate.link',
240
+ timeoutMs: 5000,
222
241
  });
223
242
 
224
- console.log('Downloaded:', result.filename);
243
+ console.log('Server version:', serverInfo.version);
244
+ ```
225
245
 
226
- // Or download to memory (for small files)
227
- const memoryResult = await client.downloadFile({
228
- host: 'dropgate.link',
229
- secure: true,
230
- fileId: 'abc123',
231
- });
246
+ ## Metadata Fetching
232
247
 
233
- // memoryResult.data contains the complete file as Uint8Array
234
- console.log('File size:', memoryResult.data?.length);
248
+ The core library provides intelligent metadata fetching methods that handle all the complexity of deriving computed fields from server responses.
249
+
250
+ ### Philosophy
251
+
252
+ The server stores and sends only **minimal, essential data**:
253
+ - For files: Basic metadata (size, encryption flag, filename)
254
+ - For bundles: File list (for unsealed) or encrypted manifest (for sealed)
255
+
256
+ The core library **derives all computed fields**:
257
+ - `totalSizeBytes`: Sum of all file sizes
258
+ - `fileCount`: Length of files array
259
+ - Decrypted manifest contents (for sealed bundles)
260
+
261
+ ### Benefits
262
+
263
+ 1. **Single Source of Truth**: All derivation logic lives in one place (the core library)
264
+ 2. **Server Efficiency**: Server stores less redundant data
265
+ 3. **Client Flexibility**: Clients can compute fields in any format they need
266
+ 4. **Future-Proof**: Changes to derivation logic only require library updates
267
+
268
+ ### Usage
269
+
270
+ ```javascript
271
+ // The core library automatically:
272
+ // 1. Fetches raw metadata from the server
273
+ // 2. Decrypts sealed bundle manifests (if keyB64 provided)
274
+ // 3. Derives totalSizeBytes and fileCount from files array
275
+ // 4. Returns a complete BundleMetadata object
276
+
277
+ const meta = await client.getBundleMetadata('bundle-id', 'optional-key');
278
+ // meta.totalSizeBytes and meta.fileCount are computed client-side
235
279
  ```
236
280
 
237
- ## 📚 API Reference
281
+ ## API Reference
238
282
 
239
- ### 🔌 DropgateClient
283
+ ### DropgateClient
240
284
 
241
285
  The main client class for interacting with Dropgate servers.
242
286
 
243
- #### âš™ī¸ Constructor Options
287
+ #### Constructor Options
244
288
 
245
289
  | Option | Type | Required | Description |
246
290
  | --- | --- | --- | --- |
247
291
  | `clientVersion` | `string` | Yes | Client version for compatibility checking |
248
- | `chunkSize` | `number` | No | Upload chunk size (default: 5MB) |
292
+ | `server` | `string \| ServerTarget` | Yes | Server URL or `{ host, port?, secure? }` |
293
+ | `fallbackToHttp` | `boolean` | No | Auto-retry with HTTP if HTTPS fails in `connect()` |
294
+ | `chunkSize` | `number` | No | Upload chunk size fallback (default: 5MB). The server's configured chunk size (from `/api/info`) takes precedence when available. |
249
295
  | `fetchFn` | `FetchFn` | No | Custom fetch implementation |
250
296
  | `cryptoObj` | `CryptoAdapter` | No | Custom crypto implementation |
251
297
  | `base64` | `Base64Adapter` | No | Custom base64 encoder/decoder |
252
- | `logger` | `LoggerFn` | No | Custom logger function |
253
298
 
254
- #### đŸ› ī¸ Methods
299
+ #### Properties
300
+
301
+ | Property | Type | Description |
302
+ | --- | --- | --- |
303
+ | `baseUrl` | `string` | Resolved server base URL (may change if HTTP fallback occurs) |
304
+ | `serverTarget` | `ServerTarget` | Derived `{ host, port, secure }` from `baseUrl` |
305
+
306
+ #### Methods
255
307
 
256
308
  | Method | Description |
257
309
  | --- | --- |
258
- | `uploadFile(opts)` | Upload a file with optional encryption |
259
- | `downloadFile(opts)` | Download a file with optional decryption |
260
- | `checkCompatibility(opts)` | Fetch server info and check client/server version compatibility |
310
+ | `connect(opts?)` | Fetch server info, check compatibility, cache result |
311
+ | `getFileMetadata(fileId, opts?)` | Fetch metadata for a single file |
312
+ | `getBundleMetadata(bundleId, keyB64?, opts?)` | Fetch bundle metadata with automatic manifest decryption and field derivation |
313
+ | `uploadFiles(opts)` | Upload a file with optional encryption |
314
+ | `downloadFiles(opts)` | Download a file with optional decryption |
315
+ | `p2pSend(opts)` | Start a P2P send session |
316
+ | `p2pReceive(opts)` | Start a P2P receive session |
261
317
  | `validateUploadInputs(opts)` | Validate file and settings before upload |
262
- | `resolveShareTarget(value, opts)` | Resolve a sharing code via the server |
318
+ | `resolveShareTarget(value, opts?)` | Resolve a sharing code via the server |
263
319
 
264
- ### 🔄 P2P Functions
320
+ ### P2P Utility Functions
265
321
 
266
322
  | Function | Description |
267
323
  | --- | --- |
268
- | `startP2PSend(opts)` | Start a P2P send session |
269
- | `startP2PReceive(opts)` | Start a P2P receive session |
270
324
  | `generateP2PCode(cryptoObj?)` | Generate a secure sharing code |
271
325
  | `isP2PCodeLike(code)` | Check if a string looks like a P2P code |
272
326
  | `isSecureContextForP2P(hostname, isSecureContext)` | Check if P2P is allowed |
273
327
  | `isLocalhostHostname(hostname)` | Check if hostname is localhost |
274
328
 
275
- ### 🧰 Utility Functions
329
+ ### Utility Functions
276
330
 
277
331
  | Function | Description |
278
332
  | --- | --- |
279
- | `getServerInfo(opts)` | Fetch server info and capabilities |
280
- | `parseServerUrl(urlStr)` | Parse a URL string into host/port/secure |
281
- | `buildBaseUrl(opts)` | Build a URL from host/port/secure |
333
+ | `getServerInfo(opts)` | Fetch server info and capabilities (standalone) |
282
334
  | `lifetimeToMs(value, unit)` | Convert lifetime to milliseconds |
283
335
  | `estimateTotalUploadSizeBytes(...)` | Estimate upload size with encryption overhead |
284
336
  | `bytesToBase64(bytes)` | Convert bytes to base64 |
337
+ | `arrayBufferToBase64(buffer)` | Convert an ArrayBuffer to base64 |
285
338
  | `base64ToBytes(b64)` | Convert base64 to bytes |
339
+ | `parseSemverMajorMinor(version)` | Parse a semver string into `{ major, minor }` |
340
+ | `validatePlainFilename(name)` | Validate that a filename has no path traversal or illegal characters |
286
341
 
287
- ### âš ī¸ Error Classes
342
+ ### Crypto Functions
343
+
344
+ | Function | Description |
345
+ | --- | --- |
346
+ | `sha256Hex(data)` | Compute a SHA-256 hex digest |
347
+ | `generateAesGcmKey()` | Generate a random AES-256-GCM CryptoKey |
348
+ | `exportKeyBase64(key)` | Export a CryptoKey as a base64 string |
349
+ | `importKeyFromBase64(b64)` | Import a CryptoKey from a base64 string |
350
+ | `encryptToBlob(blob, key)` | Encrypt a Blob with AES-256-GCM |
351
+ | `encryptFilenameToBase64(name, key)` | Encrypt a filename string to base64 |
352
+ | `decryptChunk(chunk, key)` | Decrypt an AES-256-GCM encrypted chunk |
353
+ | `decryptFilenameFromBase64(b64, key)` | Decrypt a filename from base64 |
354
+
355
+ ### Adapter Defaults
356
+
357
+ | Function | Description |
358
+ | --- | --- |
359
+ | `getDefaultFetch()` | Get the default `fetch` implementation for the current environment |
360
+ | `getDefaultCrypto()` | Get the default `CryptoAdapter` (Web Crypto API) |
361
+ | `getDefaultBase64()` | Get the default `Base64Adapter` for the current environment |
362
+
363
+ ### Network Helpers (advanced)
364
+
365
+ | Function | Description |
366
+ | --- | --- |
367
+ | `buildBaseUrl(server)` | Build a base URL string from a server URL or `ServerTarget` |
368
+ | `parseServerUrl(url)` | Parse a URL string into a `ServerTarget` |
369
+ | `fetchJson(url, opts?)` | Fetch JSON with timeout and error handling |
370
+ | `sleep(ms)` | Promise-based delay |
371
+ | `makeAbortSignal(timeoutMs?)` | Create an `AbortSignal` with optional timeout |
372
+
373
+ ### Constants
374
+
375
+ | Constant | Description |
376
+ | --- | --- |
377
+ | `DEFAULT_CHUNK_SIZE` | Default upload chunk size in bytes (5 MB) |
378
+ | `AES_GCM_IV_BYTES` | AES-GCM initialisation vector length |
379
+ | `AES_GCM_TAG_BYTES` | AES-GCM authentication tag length |
380
+ | `ENCRYPTION_OVERHEAD_PER_CHUNK` | Total encryption overhead added to each chunk |
381
+
382
+ ### StreamingZipWriter
383
+
384
+ A streaming ZIP assembler for multi-file P2P transfers. Wraps [fflate](https://github.com/101arrowz/fflate) and produces a valid ZIP archive without buffering entire files in memory.
385
+
386
+ ```javascript
387
+ import { StreamingZipWriter } from '@dropgate/core';
388
+
389
+ const zipWriter = new StreamingZipWriter((zipChunk) => {
390
+ // Write each ZIP chunk to your output (e.g., StreamSaver writer)
391
+ writer.write(zipChunk);
392
+ });
393
+
394
+ zipWriter.startFile('photo.jpg');
395
+ zipWriter.writeChunk(chunk1);
396
+ zipWriter.writeChunk(chunk2);
397
+ zipWriter.endFile();
398
+
399
+ zipWriter.startFile('notes.txt');
400
+ zipWriter.writeChunk(chunk3);
401
+ zipWriter.endFile();
402
+
403
+ zipWriter.finalize(); // Flush remaining data and write ZIP footer
404
+ ```
405
+
406
+ ### Error Classes
288
407
 
289
408
  | Class | Description |
290
409
  | --- | --- |
@@ -295,14 +414,15 @@ The main client class for interacting with Dropgate servers.
295
414
  | `DropgateAbortError` | Operation aborted |
296
415
  | `DropgateTimeoutError` | Operation timed out |
297
416
 
298
- ## 🌐 Browser Usage
417
+ ## Browser Usage
299
418
 
300
419
  For browser environments, you can use the IIFE bundle:
301
420
 
302
421
  ```html
303
422
  <script src="/path/to/dropgate-core.browser.js"></script>
304
423
  <script>
305
- const { DropgateClient, getServerInfo, startP2PSend } = DropgateCore;
424
+ const { DropgateClient } = DropgateCore;
425
+ const client = new DropgateClient({ clientVersion: '3.0.1', server: location.origin });
306
426
  // ...
307
427
  </script>
308
428
  ```
@@ -311,33 +431,44 @@ Or as an ES module:
311
431
 
312
432
  ```html
313
433
  <script type="module">
314
- import { DropgateClient, getServerInfo } from '/path/to/dropgate-core.js';
434
+ import { DropgateClient } from '/path/to/dropgate-core.js';
435
+ const client = new DropgateClient({ clientVersion: '3.0.1', server: location.origin });
315
436
  // ...
316
437
  </script>
317
438
  ```
318
439
 
319
- ## 📋 P2P Consumer Responsibilities
440
+ ## P2P Consumer Responsibilities
320
441
 
321
- The P2P functions are designed to be **headless**. The consumer is responsible for:
442
+ The P2P methods are **headless**. The consumer is responsible for:
322
443
 
323
- 1. **Loading PeerJS**: Provide the `Peer` constructor to P2P functions
444
+ 1. **Loading PeerJS**: Provide the `Peer` constructor to `p2pSend`/`p2pReceive`
324
445
  2. **File Writing**: Handle received chunks via `onData` callback (e.g., using streamSaver)
325
446
  3. **UI Updates**: React to callbacks (`onProgress`, `onStatus`, etc.)
326
447
 
327
448
  This design allows the library to work in any environment (browser, Electron, Node.js with WebRTC).
328
449
 
329
- ## 📜 License
450
+ ### Large File Support
451
+
452
+ The P2P implementation is designed for **unlimited file sizes** with constant memory usage:
453
+
454
+ - **Stream-through architecture**: Chunks flow immediately to `onData`, no buffering
455
+ - **Flow control**: Sender pauses when receiver's write queue backs up
456
+ - **WebRTC reliability**: SCTP provides reliable, ordered, checksum-verified delivery
457
+
458
+ > **Note**: For large files, always use the `onData` callback approach rather than buffering in memory.
459
+
460
+ ## License
330
461
 
331
462
  Licensed under the **Apache-2.0 License**.
332
463
  See the [LICENSE](./LICENSE) file for details.
333
464
 
334
- ## 📖 Acknowledgements
465
+ ## Acknowledgements
335
466
 
336
467
  * Logo designed by [TheFuturisticIdiot](https://youtube.com/TheFuturisticIdiot)
337
468
  * Built with [TypeScript](https://www.typescriptlang.org/)
338
469
  * Inspired by the growing need for privacy-respecting, open file transfer tools
339
470
 
340
- ## 🙂 Contact Us
471
+ ## Contact Us
341
472
 
342
473
  * **Need help or want to chat?** [Join our Discord Server](https://diamonddigital.dev/discord)
343
474
  * **Found a bug?** [Open an issue](https://github.com/WillTDA/Dropgate/issues)