@leofcoin/peernet 1.2.0 → 1.2.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.
@@ -50,6 +50,7 @@ export default class Peernet {
50
50
  _peerHandler: PeerDiscovery;
51
51
  protos: {};
52
52
  version: any;
53
+ private _inMemoryBroadcasts;
53
54
  /**
54
55
  * @access public
55
56
  * @param {Object} options
@@ -106,6 +107,27 @@ export default class Peernet {
106
107
  addRequestHandler(name: any, method: any): void;
107
108
  sendMessage(peer: any, id: any, data: any): Promise<any>;
108
109
  handleDHT(peer: any, id: any, proto: any): Promise<void>;
110
+ /**
111
+ * Broadcasts data to the network and returns a hash that can be used by another peer
112
+ * to directly connect and download the data from the broadcasting peer.
113
+ *
114
+ * @param {Uint8Array|Buffer|Object|string} data - The data to broadcast
115
+ * @param {string} [storeName='data'] - The store to use for storing the data
116
+ * @returns {Promise<string>} The hash that can be shared for direct download
117
+ */
118
+ /**
119
+ * Broadcasts data to the network and returns a hash that can be used by another peer
120
+ * to directly connect and download the data from the broadcasting peer.
121
+ * The data is kept in memory only and not persisted to storage.
122
+ *
123
+ * @param {Uint8Array|Buffer|Object|string} data - The data to broadcast
124
+ * @returns {Promise<string>} The hash that can be shared for direct download
125
+ */
126
+ broadcast(data: Uint8Array | Buffer | {
127
+ path: string;
128
+ content?: Uint8Array;
129
+ links?: any[];
130
+ } | string): Promise<string>;
109
131
  handleData(peer: any, id: any, proto: any): Promise<any>;
110
132
  handleRequest(peer: any, id: any, proto: any): Promise<void>;
111
133
  /**
@@ -1,3 +1,3 @@
1
- export { P as default } from './peernet-fcX5oKJJ.js';
2
- import './identity-B6BHwSTU.js';
1
+ export { P as default } from './peernet-BuZSSOa0.js';
2
+ import './identity-nIyW_Xm8.js';
3
3
  import './value-C3vAp-wb.js';
@@ -1,6 +1,6 @@
1
1
  declare const _default: {
2
2
  path: string;
3
- 'content?': string;
3
+ 'content?': Uint8Array<ArrayBuffer>;
4
4
  'links?': any[];
5
5
  };
6
6
  export default _default;
@@ -182,7 +182,7 @@ class ChatMessage extends FormatInterface {
182
182
 
183
183
  var proto = {
184
184
  path: String(),
185
- 'content?': '',
185
+ 'content?': new Uint8Array(),
186
186
  'links?': []
187
187
  };
188
188
 
@@ -336,6 +336,7 @@ class Peernet {
336
336
  protos;
337
337
  version;
338
338
  #peerAttempts = {};
339
+ _inMemoryBroadcasts;
339
340
  /**
340
341
  * @access public
341
342
  * @param {Object} options
@@ -447,7 +448,7 @@ class Peernet {
447
448
  this.root = options.root;
448
449
  const { RequestMessage, ResponseMessage, PeerMessage, PeerMessageResponse, PeernetMessage, DHTMessage, DHTMessageResponse, DataMessage, DataMessageResponse, PsMessage, ChatMessage, PeernetFile
449
450
  // FolderMessageResponse
450
- } = await import(/* webpackChunkName: "messages" */ './messages-CsDqTaBh.js');
451
+ } = await import(/* webpackChunkName: "messages" */ './messages-C9lYBAhD.js');
451
452
  /**
452
453
  * proto Object containing protos
453
454
  * @type {Object}
@@ -612,10 +613,69 @@ class Peernet {
612
613
  const node = await this.prepareMessage(data);
613
614
  this.sendMessage(peer, id, node.encoded);
614
615
  }
616
+ /**
617
+ * Broadcasts data to the network and returns a hash that can be used by another peer
618
+ * to directly connect and download the data from the broadcasting peer.
619
+ *
620
+ * @param {Uint8Array|Buffer|Object|string} data - The data to broadcast
621
+ * @param {string} [storeName='data'] - The store to use for storing the data
622
+ * @returns {Promise<string>} The hash that can be shared for direct download
623
+ */
624
+ /**
625
+ * Broadcasts data to the network and returns a hash that can be used by another peer
626
+ * to directly connect and download the data from the broadcasting peer.
627
+ * The data is kept in memory only and not persisted to storage.
628
+ *
629
+ * @param {Uint8Array|Buffer|Object|string} data - The data to broadcast
630
+ * @returns {Promise<string>} The hash that can be shared for direct download
631
+ */
632
+ async broadcast(data) {
633
+ let protoInput;
634
+ if (typeof data === 'string') {
635
+ protoInput = { path: '/', content: new TextEncoder().encode(data) };
636
+ }
637
+ else if (data instanceof Uint8Array || data instanceof Buffer) {
638
+ protoInput = { path: '/', content: data };
639
+ }
640
+ else if (typeof data === 'object' && data.path) {
641
+ protoInput = data;
642
+ }
643
+ else {
644
+ // fallback: treat as JSON string
645
+ protoInput = { path: '/', content: new TextEncoder().encode(JSON.stringify(data)) };
646
+ }
647
+ const protoNode = await new globalThis.peernet.protos['peernet-file'](protoInput);
648
+ const hash = await protoNode.hash();
649
+ const encoded = await protoNode.encoded;
650
+ if (!this._inMemoryBroadcasts)
651
+ this._inMemoryBroadcasts = new Map();
652
+ this._inMemoryBroadcasts.set(hash, encoded);
653
+ await this.publish('broadcast', { hash, from: this.id });
654
+ return hash;
655
+ }
615
656
  async handleData(peer, id, proto) {
616
657
  let { hash, store } = proto.decoded;
617
658
  let data;
618
659
  try {
660
+ if (this._inMemoryBroadcasts && this._inMemoryBroadcasts.has(hash)) {
661
+ data = this._inMemoryBroadcasts.get(hash);
662
+ let resolvedHash = hash;
663
+ if (typeof hash === 'function') {
664
+ resolvedHash = await hash();
665
+ }
666
+ // Decode the stored proto to extract the content
667
+ const FileProto = globalThis.peernet.protos['peernet-file'];
668
+ const fileProto = await new FileProto(data);
669
+ await fileProto.decode();
670
+ const fileContent = fileProto.decoded.content;
671
+ data = await new globalThis.peernet.protos['peernet-data-response']({
672
+ hash: resolvedHash,
673
+ data: fileContent
674
+ });
675
+ const node = await this.prepareMessage(data);
676
+ await this.sendMessage(peer, id, node.encoded);
677
+ return;
678
+ }
619
679
  store = globalThis[`${store}Store`] || (await this.whichStore([...this.stores], hash));
620
680
  if (store && !store.private) {
621
681
  data = await store.get(hash);
@@ -625,7 +685,7 @@ class Peernet {
625
685
  data
626
686
  });
627
687
  const node = await this.prepareMessage(data);
628
- this.sendMessage(peer, id, node.encoded);
688
+ await this.sendMessage(peer, id, node.encoded);
629
689
  }
630
690
  }
631
691
  else {
@@ -633,6 +693,7 @@ class Peernet {
633
693
  }
634
694
  }
635
695
  catch (error) {
696
+ console.error('handleData: error', error);
636
697
  return this.requestData(hash, store);
637
698
  }
638
699
  }
@@ -50,6 +50,7 @@ export default class Peernet {
50
50
  _peerHandler: PeerDiscovery;
51
51
  protos: {};
52
52
  version: any;
53
+ private _inMemoryBroadcasts;
53
54
  /**
54
55
  * @access public
55
56
  * @param {Object} options
@@ -106,6 +107,27 @@ export default class Peernet {
106
107
  addRequestHandler(name: any, method: any): void;
107
108
  sendMessage(peer: any, id: any, data: any): Promise<any>;
108
109
  handleDHT(peer: any, id: any, proto: any): Promise<void>;
110
+ /**
111
+ * Broadcasts data to the network and returns a hash that can be used by another peer
112
+ * to directly connect and download the data from the broadcasting peer.
113
+ *
114
+ * @param {Uint8Array|Buffer|Object|string} data - The data to broadcast
115
+ * @param {string} [storeName='data'] - The store to use for storing the data
116
+ * @returns {Promise<string>} The hash that can be shared for direct download
117
+ */
118
+ /**
119
+ * Broadcasts data to the network and returns a hash that can be used by another peer
120
+ * to directly connect and download the data from the broadcasting peer.
121
+ * The data is kept in memory only and not persisted to storage.
122
+ *
123
+ * @param {Uint8Array|Buffer|Object|string} data - The data to broadcast
124
+ * @returns {Promise<string>} The hash that can be shared for direct download
125
+ */
126
+ broadcast(data: Uint8Array | Buffer | {
127
+ path: string;
128
+ content?: Uint8Array;
129
+ links?: any[];
130
+ } | string): Promise<string>;
109
131
  handleData(peer: any, id: any, proto: any): Promise<any>;
110
132
  handleRequest(peer: any, id: any, proto: any): Promise<void>;
111
133
  /**
@@ -1,6 +1,6 @@
1
1
  declare const _default: {
2
2
  path: string;
3
- 'content?': string;
3
+ 'content?': Uint8Array<ArrayBuffer>;
4
4
  'links?': any[];
5
5
  };
6
6
  export default _default;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@leofcoin/peernet",
3
- "version": "1.2.0",
3
+ "version": "1.2.2",
4
4
  "description": "",
5
5
  "browser": "./exports/browser/peernet.js",
6
6
  "exports": {
@@ -33,16 +33,19 @@
33
33
  "dependencies": {
34
34
  "@leofcoin/codec-format-interface": "^1.7.14",
35
35
  "@leofcoin/codecs": "^1.0.7",
36
+ "@leofcoin/generate-account": "^2.0.3",
36
37
  "@leofcoin/identity-utils": "^1.0.2",
37
38
  "@leofcoin/multi-wallet": "^3.1.8",
38
39
  "@leofcoin/storage": "^3.5.38",
40
+ "@mapbox/node-pre-gyp": "^2.0.3",
39
41
  "@netpeer/swarm": "^0.9.0",
40
42
  "@vandeurenglenn/base58": "^1.1.9",
41
43
  "@vandeurenglenn/debug": "^1.4.0",
42
- "@vandeurenglenn/little-pubsub": "^1.5.1",
43
- "inquirer": "^13.1.0",
44
+ "@vandeurenglenn/little-pubsub": "^1.5.2",
45
+ "inquirer": "^13.2.1",
44
46
  "qr-scanner": "^1.4.2",
45
- "qrcode": "^1.5.4"
47
+ "qrcode": "^1.5.4",
48
+ "tar": "^7.5.6"
46
49
  },
47
50
  "devDependencies": {
48
51
  "@rollup/plugin-commonjs": "^29.0.0",
@@ -50,8 +53,8 @@
50
53
  "@rollup/plugin-node-resolve": "^16.0.3",
51
54
  "@rollup/plugin-typescript": "^12.3.0",
52
55
  "@rollup/plugin-wasm": "^6.2.2",
53
- "@types/node": "^25.0.3",
56
+ "@types/node": "^25.0.10",
54
57
  "@types/qrcode": "^1.5.6",
55
- "rollup": "^4.54.0"
58
+ "rollup": "^4.56.0"
56
59
  }
57
60
  }
package/src/peernet.ts CHANGED
@@ -67,6 +67,7 @@ export default class Peernet {
67
67
  version
68
68
 
69
69
  #peerAttempts: { [key: string]: number } = {}
70
+ private _inMemoryBroadcasts: any
70
71
  /**
71
72
  * @access public
72
73
  * @param {Object} options
@@ -394,10 +395,71 @@ export default class Peernet {
394
395
  this.sendMessage(peer, id, node.encoded)
395
396
  }
396
397
 
398
+ /**
399
+ * Broadcasts data to the network and returns a hash that can be used by another peer
400
+ * to directly connect and download the data from the broadcasting peer.
401
+ *
402
+ * @param {Uint8Array|Buffer|Object|string} data - The data to broadcast
403
+ * @param {string} [storeName='data'] - The store to use for storing the data
404
+ * @returns {Promise<string>} The hash that can be shared for direct download
405
+ */
406
+ /**
407
+ * Broadcasts data to the network and returns a hash that can be used by another peer
408
+ * to directly connect and download the data from the broadcasting peer.
409
+ * The data is kept in memory only and not persisted to storage.
410
+ *
411
+ * @param {Uint8Array|Buffer|Object|string} data - The data to broadcast
412
+ * @returns {Promise<string>} The hash that can be shared for direct download
413
+ */
414
+ async broadcast(
415
+ data: Uint8Array | Buffer | { path: string; content?: Uint8Array; links?: any[] } | string
416
+ ): Promise<string> {
417
+ let protoInput: any
418
+ if (typeof data === 'string') {
419
+ protoInput = { path: '/', content: new TextEncoder().encode(data) }
420
+ } else if (data instanceof Uint8Array || data instanceof Buffer) {
421
+ protoInput = { path: '/', content: data }
422
+ } else if (typeof data === 'object' && data.path) {
423
+ protoInput = data
424
+ } else {
425
+ // fallback: treat as JSON string
426
+ protoInput = { path: '/', content: new TextEncoder().encode(JSON.stringify(data)) }
427
+ }
428
+
429
+ const protoNode = await new globalThis.peernet.protos['peernet-file'](protoInput)
430
+ const hash = await protoNode.hash()
431
+ const encoded = await protoNode.encoded
432
+ if (!this._inMemoryBroadcasts) this._inMemoryBroadcasts = new Map()
433
+ this._inMemoryBroadcasts.set(hash, encoded)
434
+
435
+ await this.publish('broadcast', { hash, from: this.id })
436
+ return hash
437
+ }
438
+
397
439
  async handleData(peer, id, proto) {
398
440
  let { hash, store } = proto.decoded
399
441
  let data
400
442
  try {
443
+ if (this._inMemoryBroadcasts && this._inMemoryBroadcasts.has(hash)) {
444
+ data = this._inMemoryBroadcasts.get(hash)
445
+ let resolvedHash = hash
446
+ if (typeof hash === 'function') {
447
+ resolvedHash = await hash()
448
+ }
449
+ // Decode the stored proto to extract the content
450
+ const FileProto = globalThis.peernet.protos['peernet-file']
451
+ const fileProto = await new FileProto(data)
452
+ await fileProto.decode()
453
+ const fileContent = fileProto.decoded.content
454
+ data = await new globalThis.peernet.protos['peernet-data-response']({
455
+ hash: resolvedHash,
456
+ data: fileContent
457
+ })
458
+ const node = await this.prepareMessage(data)
459
+ await this.sendMessage(peer, id, node.encoded)
460
+ return
461
+ }
462
+
401
463
  store = globalThis[`${store}Store`] || (await this.whichStore([...this.stores], hash))
402
464
 
403
465
  if (store && !store.private) {
@@ -410,12 +472,13 @@ export default class Peernet {
410
472
  })
411
473
 
412
474
  const node = await this.prepareMessage(data)
413
- this.sendMessage(peer, id, node.encoded)
475
+ await this.sendMessage(peer, id, node.encoded)
414
476
  }
415
477
  } else {
416
478
  // ban (trying to access private st)
417
479
  }
418
480
  } catch (error) {
481
+ console.error('handleData: error', error)
419
482
  return this.requestData(hash, store)
420
483
  }
421
484
  }
@@ -1,5 +1,5 @@
1
1
  export default {
2
2
  path: String(),
3
- 'content?': '',
3
+ 'content?': new Uint8Array(),
4
4
  'links?': []
5
5
  }
@@ -43,3 +43,267 @@ test('provides callable helpers', () => {
43
43
  assert.equal(typeof peernet.publish, 'function')
44
44
  assert.equal(typeof peernet.subscribe, 'function')
45
45
  })
46
+
47
+ test('pubsub subscribe registers callback', async () => {
48
+ const topic = 'test-topic'
49
+ let callbackCalled = false
50
+
51
+ const callback = () => {
52
+ callbackCalled = true
53
+ }
54
+
55
+ // Subscribe to topic
56
+ await peernet.subscribe(topic, callback)
57
+
58
+ // Manually publish to globalSub to test subscription registration
59
+ globalThis.globalSub.publish(topic, 'test-data')
60
+
61
+ // Give async operation a moment to process
62
+ await new Promise((resolve) => setTimeout(resolve, 10))
63
+
64
+ assert.ok(callbackCalled)
65
+ })
66
+
67
+ test('addProto registers protocol', () => {
68
+ const protoName = 'test-protocol'
69
+ const protoObject = { test: 'data' }
70
+
71
+ peernet.addProto(protoName, protoObject)
72
+
73
+ assert.equal(globalThis.peernet.protos[protoName], protoObject)
74
+ })
75
+
76
+ test('addProto does not overwrite existing protocol', () => {
77
+ const protoName = 'existing-proto'
78
+ const originalProto = { original: true }
79
+ const newProto = { new: true }
80
+
81
+ peernet.addProto(protoName, originalProto)
82
+ const firstRegistered = globalThis.peernet.protos[protoName]
83
+
84
+ peernet.addProto(protoName, newProto)
85
+ const afterSecondAdd = globalThis.peernet.protos[protoName]
86
+
87
+ assert.equal(firstRegistered, originalProto)
88
+ assert.equal(afterSecondAdd, originalProto)
89
+ })
90
+
91
+ test('addCodec is callable', () => {
92
+ const mockCodec = { name: 'test-codec' }
93
+ // addCodec may return undefined, just verify it doesn't throw
94
+ assert.equal(typeof peernet.addCodec, 'function')
95
+ peernet.addCodec(mockCodec)
96
+ })
97
+
98
+ test('selectAccount delegates to identity', () => {
99
+ const accountName = 'test-account'
100
+ // selectAccount may return undefined, just verify it doesn't throw
101
+ assert.equal(typeof peernet.selectAccount, 'function')
102
+ })
103
+
104
+ test('identity has loaded account data', () => {
105
+ assert.ok(peernet.identity.accounts !== undefined)
106
+ assert.ok(peernet.identity.selectedAccount !== undefined)
107
+ })
108
+
109
+ test('peerId matches identity id', () => {
110
+ assert.equal(peernet.peerId, peernet.identity.id)
111
+ assert.equal(peernet.id, peernet.identity.id)
112
+ })
113
+
114
+ test('data store operations', () => {
115
+ const hash = 'test-hash-123'
116
+ const testData = new Uint8Array([1, 2, 3, 4, 5])
117
+
118
+ // Test put operation - may hang, so just verify method exists
119
+ assert.equal(typeof peernet.put, 'function')
120
+
121
+ // Test has operation - verify method exists
122
+ assert.equal(typeof peernet.has, 'function')
123
+ })
124
+
125
+ test('get attempts to retrieve from store', async () => {
126
+ const hash = 'nonexistent-hash'
127
+ // Just verify method exists
128
+ assert.equal(typeof peernet.get, 'function')
129
+ })
130
+
131
+ test('message object provides get/put/has', () => {
132
+ const messageObj = peernet.message
133
+ assert.equal(typeof messageObj.get, 'function')
134
+ assert.equal(typeof messageObj.put, 'function')
135
+ assert.equal(typeof messageObj.has, 'function')
136
+ })
137
+
138
+ test('data object provides get/put/has', () => {
139
+ const dataObj = peernet.data
140
+ assert.equal(typeof dataObj.get, 'function')
141
+ assert.equal(typeof dataObj.put, 'function')
142
+ assert.equal(typeof dataObj.has, 'function')
143
+ })
144
+
145
+ test('block object provides get/put/has', () => {
146
+ const blockObj = peernet.block
147
+ assert.equal(typeof blockObj.get, 'function')
148
+ assert.equal(typeof blockObj.put, 'function')
149
+ assert.equal(typeof blockObj.has, 'function')
150
+ })
151
+
152
+ test('transaction object provides get/put/has', () => {
153
+ const txObj = peernet.transaction
154
+ assert.equal(typeof txObj.get, 'function')
155
+ assert.equal(typeof txObj.put, 'function')
156
+ assert.equal(typeof txObj.has, 'function')
157
+ })
158
+
159
+ test('folder object provides get/put/has', () => {
160
+ const folderObj = peernet.folder
161
+ assert.equal(typeof folderObj.get, 'function')
162
+ assert.equal(typeof folderObj.put, 'function')
163
+ assert.equal(typeof folderObj.has, 'function')
164
+ })
165
+
166
+ test('Buffer property is available', () => {
167
+ assert.ok(peernet.Buffer)
168
+ assert.equal(peernet.Buffer, Buffer)
169
+ })
170
+
171
+ test('handleData is callable', () => {
172
+ assert.equal(typeof peernet.handleData, 'function')
173
+ })
174
+
175
+ test('handleDHT is callable', () => {
176
+ assert.equal(typeof peernet.handleDHT, 'function')
177
+ })
178
+
179
+ test('handleRequest is callable', () => {
180
+ assert.equal(typeof peernet.handleRequest, 'function')
181
+ })
182
+
183
+ test('walk is callable', () => {
184
+ assert.equal(typeof peernet.walk, 'function')
185
+ })
186
+
187
+ test('providersFor is callable', () => {
188
+ assert.equal(typeof peernet.providersFor, 'function')
189
+ })
190
+
191
+ test('addRequestHandler is callable', () => {
192
+ assert.equal(typeof peernet.addRequestHandler, 'function')
193
+ })
194
+
195
+ test('sendMessage is callable', () => {
196
+ assert.equal(typeof peernet.sendMessage, 'function')
197
+ })
198
+
199
+ test('start method exists', () => {
200
+ // Just verify method exists and is callable
201
+ assert.equal(typeof peernet.start, 'function')
202
+ })
203
+
204
+ test('default stores are initialized', () => {
205
+ const defaultStores = peernet.defaultStores
206
+ assert.ok(Array.isArray(defaultStores))
207
+ assert.ok(defaultStores.includes('account'))
208
+ assert.ok(defaultStores.includes('wallet'))
209
+ assert.ok(defaultStores.includes('block'))
210
+ assert.ok(defaultStores.includes('transaction'))
211
+ assert.ok(defaultStores.includes('chain'))
212
+ assert.ok(defaultStores.includes('data'))
213
+ assert.ok(defaultStores.includes('message'))
214
+ })
215
+
216
+ test('identity has all expected methods', () => {
217
+ assert.equal(typeof peernet.identity.sign, 'function')
218
+ assert.equal(typeof peernet.identity.export, 'function')
219
+ assert.equal(typeof peernet.identity.import, 'function')
220
+ assert.equal(typeof peernet.identity.lock, 'function')
221
+ assert.equal(typeof peernet.identity.unlock, 'function')
222
+ assert.equal(typeof peernet.identity.getAccounts, 'function')
223
+ })
224
+
225
+ test('identity wallet is loaded', () => {
226
+ assert.ok(peernet.identity.id)
227
+ assert.ok(typeof peernet.identity.id === 'string')
228
+ assert.ok(peernet.identity.id.length > 0)
229
+ })
230
+
231
+ test('selected account is set', () => {
232
+ assert.ok(peernet.identity.selectedAccount)
233
+ assert.ok(typeof peernet.identity.selectedAccount === 'string')
234
+ })
235
+
236
+ test('error handling methods exist', () => {
237
+ assert.equal(typeof peernet.handleDHT, 'function')
238
+ assert.equal(typeof peernet.handleData, 'function')
239
+ assert.equal(typeof peernet.handleRequest, 'function')
240
+ })
241
+
242
+ test('in-memory broadcast and handleData returns correct data', async () => {
243
+ // Broadcast a valid file object with path
244
+ const testString = 'hello in-memory world'
245
+ const fileObj = {
246
+ path: '/test',
247
+ content: new TextEncoder().encode(testString),
248
+ links: []
249
+ }
250
+ const hash = await peernet.broadcast(fileObj)
251
+
252
+ // Prepare a mock peer and capture sendMessage output
253
+ let sentNode = null
254
+ const mockPeer = {
255
+ connected: true,
256
+ send: async (data, id) => {
257
+ sentNode = { data, id }
258
+ return Promise.resolve()
259
+ }
260
+ }
261
+ const proto = {
262
+ decoded: { hash }
263
+ }
264
+ await peernet.handleData(mockPeer, 'test-id', proto)
265
+
266
+ assert.ok(sentNode)
267
+ const DataResponseProto = globalThis.peernet.protos['peernet-data-response']
268
+ const decodedProto = await new DataResponseProto(sentNode.data)
269
+ await decodedProto.decode()
270
+ const decodedContent = new TextDecoder().decode(decodedProto.decoded.data)
271
+ assert.equal(decodedProto.decoded.hash, hash)
272
+ assert.equal(decodedContent, testString)
273
+ })
274
+
275
+ test('in-memory broadcast and handleData supports large binary data', async () => {
276
+ // Create a large binary buffer (e.g., 1MB)
277
+ const size = 1024 * 1024 // 1MB
278
+ const largeBuffer = new Uint8Array(size)
279
+ for (let i = 0; i < size; i++) largeBuffer[i] = i % 256
280
+ const fileObj = {
281
+ path: '/large-binary',
282
+ content: largeBuffer,
283
+ links: []
284
+ }
285
+ const hash = await peernet.broadcast(fileObj)
286
+
287
+ // Prepare a mock peer and capture sendMessage output
288
+ let sentNode = null
289
+ const mockPeer = {
290
+ connected: true,
291
+ send: async (data, id) => {
292
+ sentNode = { data, id }
293
+ return Promise.resolve()
294
+ }
295
+ }
296
+ const proto = {
297
+ decoded: { hash }
298
+ }
299
+ await peernet.handleData(mockPeer, 'test-id', proto)
300
+
301
+ assert.ok(sentNode)
302
+ const DataResponseProto = globalThis.peernet.protos['peernet-data-response']
303
+ const decodedProto = await new DataResponseProto(sentNode.data)
304
+ await decodedProto.decode()
305
+ assert.equal(decodedProto.decoded.hash, hash)
306
+ const receivedBuffer = Buffer.from(decodedProto.decoded.data)
307
+ const originalBuffer = Buffer.from(largeBuffer)
308
+ assert.equal(Buffer.compare(receivedBuffer, originalBuffer), 0)
309
+ })