@leofcoin/peernet 1.2.1 → 1.2.3
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 +225 -31
- package/exports/browser/{browser-CfYI-6aD.js → browser-CWWW0kSj.js} +17 -2
- package/exports/browser/{browser-Qcpp3EKK.js → browser-DMVbMScf.js} +17 -2
- package/exports/browser/{client-CWkdUcxK.js → client-B2RpD9kv.js} +4 -4
- package/exports/browser/{index-DTbjK0sK.js → index-BoQ4uWm2.js} +17 -2
- package/exports/browser/{messages-C507MMRx.js → messages-DiCEP9k9.js} +2 -2
- package/exports/browser/{peernet-CXod4Dva.js → peernet-BTtebeW3.js} +123 -45
- package/exports/browser/peernet.d.ts +22 -0
- package/exports/browser/peernet.js +1 -1
- package/exports/browser/proto/file.proto.d.ts +1 -1
- package/exports/{messages-CsDqTaBh.js → messages-C9lYBAhD.js} +1 -1
- package/exports/peernet.js +56 -2
- package/exports/types/peernet.d.ts +22 -0
- package/exports/types/proto/file.proto.d.ts +1 -1
- package/package.json +8 -6
- package/src/peernet.ts +58 -1
- package/src/proto/file.proto.js +1 -1
- package/test/peernet.test.js +259 -0
- package/test-output.log +84 -0
- package/tsconfig.json +2 -1
|
@@ -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
|
+
* @param {string} path - The path or identifier for the content being broadcasted
|
|
123
|
+
* @param {{content?: Uint8Array, links?: any[]}} data - The data to broadcast
|
|
124
|
+
|
|
125
|
+
* @returns {Promise<string>} The hash that can be shared for direct download
|
|
126
|
+
*/
|
|
127
|
+
broadcast(path: string, { content, links }: {
|
|
128
|
+
content?: Uint8Array;
|
|
129
|
+
links?: any[];
|
|
130
|
+
}): 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
|
/**
|
package/exports/peernet.js
CHANGED
|
@@ -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-
|
|
451
|
+
} = await import(/* webpackChunkName: "messages" */ './messages-C9lYBAhD.js');
|
|
451
452
|
/**
|
|
452
453
|
* proto Object containing protos
|
|
453
454
|
* @type {Object}
|
|
@@ -612,10 +613,62 @@ 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
|
+
* @param {string} path - The path or identifier for the content being broadcasted
|
|
629
|
+
* @param {{content?: Uint8Array, links?: any[]}} data - The data to broadcast
|
|
630
|
+
|
|
631
|
+
* @returns {Promise<string>} The hash that can be shared for direct download
|
|
632
|
+
*/
|
|
633
|
+
async broadcast(path, { content, links }) {
|
|
634
|
+
let protoInput;
|
|
635
|
+
if (content)
|
|
636
|
+
protoInput = { path, content };
|
|
637
|
+
else if (links)
|
|
638
|
+
protoInput = { path, links };
|
|
639
|
+
const protoNode = await new globalThis.peernet.protos['peernet-file'](protoInput);
|
|
640
|
+
const hash = await protoNode.hash();
|
|
641
|
+
const encoded = await protoNode.encoded;
|
|
642
|
+
if (!this._inMemoryBroadcasts)
|
|
643
|
+
this._inMemoryBroadcasts = new Map();
|
|
644
|
+
this._inMemoryBroadcasts.set(hash, encoded);
|
|
645
|
+
await this.publish('broadcast', { hash, from: this.id });
|
|
646
|
+
return hash;
|
|
647
|
+
}
|
|
615
648
|
async handleData(peer, id, proto) {
|
|
616
649
|
let { hash, store } = proto.decoded;
|
|
617
650
|
let data;
|
|
618
651
|
try {
|
|
652
|
+
if (this._inMemoryBroadcasts && this._inMemoryBroadcasts.has(hash)) {
|
|
653
|
+
data = this._inMemoryBroadcasts.get(hash);
|
|
654
|
+
let resolvedHash = hash;
|
|
655
|
+
if (typeof hash === 'function') {
|
|
656
|
+
resolvedHash = await hash();
|
|
657
|
+
}
|
|
658
|
+
// Decode the stored proto to extract the content or links
|
|
659
|
+
const FileProto = globalThis.peernet.protos['peernet-file'];
|
|
660
|
+
const fileProto = await new FileProto(data);
|
|
661
|
+
await fileProto.decode();
|
|
662
|
+
const { content, links } = fileProto.decoded;
|
|
663
|
+
console.log(links);
|
|
664
|
+
data = await new globalThis.peernet.protos['peernet-data-response']({
|
|
665
|
+
hash: resolvedHash,
|
|
666
|
+
data: links || content
|
|
667
|
+
});
|
|
668
|
+
const node = await this.prepareMessage(data);
|
|
669
|
+
await this.sendMessage(peer, id, node.encoded);
|
|
670
|
+
return;
|
|
671
|
+
}
|
|
619
672
|
store = globalThis[`${store}Store`] || (await this.whichStore([...this.stores], hash));
|
|
620
673
|
if (store && !store.private) {
|
|
621
674
|
data = await store.get(hash);
|
|
@@ -625,7 +678,7 @@ class Peernet {
|
|
|
625
678
|
data
|
|
626
679
|
});
|
|
627
680
|
const node = await this.prepareMessage(data);
|
|
628
|
-
this.sendMessage(peer, id, node.encoded);
|
|
681
|
+
await this.sendMessage(peer, id, node.encoded);
|
|
629
682
|
}
|
|
630
683
|
}
|
|
631
684
|
else {
|
|
@@ -633,6 +686,7 @@ class Peernet {
|
|
|
633
686
|
}
|
|
634
687
|
}
|
|
635
688
|
catch (error) {
|
|
689
|
+
console.error('handleData: error', error);
|
|
636
690
|
return this.requestData(hash, store);
|
|
637
691
|
}
|
|
638
692
|
}
|
|
@@ -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
|
+
* @param {string} path - The path or identifier for the content being broadcasted
|
|
123
|
+
* @param {{content?: Uint8Array, links?: any[]}} data - The data to broadcast
|
|
124
|
+
|
|
125
|
+
* @returns {Promise<string>} The hash that can be shared for direct download
|
|
126
|
+
*/
|
|
127
|
+
broadcast(path: string, { content, links }: {
|
|
128
|
+
content?: Uint8Array;
|
|
129
|
+
links?: any[];
|
|
130
|
+
}): 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
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@leofcoin/peernet",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.3",
|
|
4
4
|
"description": "",
|
|
5
5
|
"browser": "./exports/browser/peernet.js",
|
|
6
6
|
"exports": {
|
|
@@ -37,13 +37,15 @@
|
|
|
37
37
|
"@leofcoin/identity-utils": "^1.0.2",
|
|
38
38
|
"@leofcoin/multi-wallet": "^3.1.8",
|
|
39
39
|
"@leofcoin/storage": "^3.5.38",
|
|
40
|
+
"@mapbox/node-pre-gyp": "^2.0.3",
|
|
40
41
|
"@netpeer/swarm": "^0.9.0",
|
|
41
42
|
"@vandeurenglenn/base58": "^1.1.9",
|
|
42
43
|
"@vandeurenglenn/debug": "^1.4.0",
|
|
43
|
-
"@vandeurenglenn/little-pubsub": "^1.5.
|
|
44
|
-
"inquirer": "^13.1
|
|
44
|
+
"@vandeurenglenn/little-pubsub": "^1.5.2",
|
|
45
|
+
"inquirer": "^13.2.1",
|
|
45
46
|
"qr-scanner": "^1.4.2",
|
|
46
|
-
"qrcode": "^1.5.4"
|
|
47
|
+
"qrcode": "^1.5.4",
|
|
48
|
+
"tar": "^7.5.6"
|
|
47
49
|
},
|
|
48
50
|
"devDependencies": {
|
|
49
51
|
"@rollup/plugin-commonjs": "^29.0.0",
|
|
@@ -51,8 +53,8 @@
|
|
|
51
53
|
"@rollup/plugin-node-resolve": "^16.0.3",
|
|
52
54
|
"@rollup/plugin-typescript": "^12.3.0",
|
|
53
55
|
"@rollup/plugin-wasm": "^6.2.2",
|
|
54
|
-
"@types/node": "^25.0.
|
|
56
|
+
"@types/node": "^25.0.10",
|
|
55
57
|
"@types/qrcode": "^1.5.6",
|
|
56
|
-
"rollup": "^4.
|
|
58
|
+
"rollup": "^4.56.0"
|
|
57
59
|
}
|
|
58
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,65 @@ 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
|
+
* @param {string} path - The path or identifier for the content being broadcasted
|
|
411
|
+
* @param {{content?: Uint8Array, links?: any[]}} data - The data to broadcast
|
|
412
|
+
|
|
413
|
+
* @returns {Promise<string>} The hash that can be shared for direct download
|
|
414
|
+
*/
|
|
415
|
+
async broadcast(path: string, { content, links }: { content?: Uint8Array; links?: any[] }): Promise<string> {
|
|
416
|
+
let protoInput: any
|
|
417
|
+
if (content) protoInput = { path, content }
|
|
418
|
+
else if (links) protoInput = { path, links }
|
|
419
|
+
|
|
420
|
+
const protoNode = await new globalThis.peernet.protos['peernet-file'](protoInput)
|
|
421
|
+
const hash = await protoNode.hash()
|
|
422
|
+
const encoded = await protoNode.encoded
|
|
423
|
+
if (!this._inMemoryBroadcasts) this._inMemoryBroadcasts = new Map()
|
|
424
|
+
this._inMemoryBroadcasts.set(hash, encoded)
|
|
425
|
+
|
|
426
|
+
await this.publish('broadcast', { hash, from: this.id })
|
|
427
|
+
return hash
|
|
428
|
+
}
|
|
429
|
+
|
|
397
430
|
async handleData(peer, id, proto) {
|
|
398
431
|
let { hash, store } = proto.decoded
|
|
399
432
|
let data
|
|
400
433
|
try {
|
|
434
|
+
if (this._inMemoryBroadcasts && this._inMemoryBroadcasts.has(hash)) {
|
|
435
|
+
data = this._inMemoryBroadcasts.get(hash)
|
|
436
|
+
let resolvedHash = hash
|
|
437
|
+
if (typeof hash === 'function') {
|
|
438
|
+
resolvedHash = await hash()
|
|
439
|
+
}
|
|
440
|
+
// Decode the stored proto to extract the content or links
|
|
441
|
+
const FileProto = globalThis.peernet.protos['peernet-file']
|
|
442
|
+
const fileProto = await new FileProto(data)
|
|
443
|
+
await fileProto.decode()
|
|
444
|
+
const { content, links } = fileProto.decoded
|
|
445
|
+
console.log(links)
|
|
446
|
+
|
|
447
|
+
data = await new globalThis.peernet.protos['peernet-data-response']({
|
|
448
|
+
hash: resolvedHash,
|
|
449
|
+
data: links || content
|
|
450
|
+
})
|
|
451
|
+
|
|
452
|
+
const node = await this.prepareMessage(data)
|
|
453
|
+
await this.sendMessage(peer, id, node.encoded)
|
|
454
|
+
return
|
|
455
|
+
}
|
|
456
|
+
|
|
401
457
|
store = globalThis[`${store}Store`] || (await this.whichStore([...this.stores], hash))
|
|
402
458
|
|
|
403
459
|
if (store && !store.private) {
|
|
@@ -410,12 +466,13 @@ export default class Peernet {
|
|
|
410
466
|
})
|
|
411
467
|
|
|
412
468
|
const node = await this.prepareMessage(data)
|
|
413
|
-
this.sendMessage(peer, id, node.encoded)
|
|
469
|
+
await this.sendMessage(peer, id, node.encoded)
|
|
414
470
|
}
|
|
415
471
|
} else {
|
|
416
472
|
// ban (trying to access private st)
|
|
417
473
|
}
|
|
418
474
|
} catch (error) {
|
|
475
|
+
console.error('handleData: error', error)
|
|
419
476
|
return this.requestData(hash, store)
|
|
420
477
|
}
|
|
421
478
|
}
|
package/src/proto/file.proto.js
CHANGED
package/test/peernet.test.js
CHANGED
|
@@ -43,3 +43,262 @@ 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 path = '/test'
|
|
246
|
+
const content = new TextEncoder().encode(testString)
|
|
247
|
+
|
|
248
|
+
const hash = await peernet.broadcast(path, { content })
|
|
249
|
+
|
|
250
|
+
// Prepare a mock peer and capture sendMessage output
|
|
251
|
+
let sentNode = null
|
|
252
|
+
const mockPeer = {
|
|
253
|
+
connected: true,
|
|
254
|
+
send: async (data, id) => {
|
|
255
|
+
sentNode = { data, id }
|
|
256
|
+
return Promise.resolve()
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
const proto = {
|
|
260
|
+
decoded: { hash }
|
|
261
|
+
}
|
|
262
|
+
await peernet.handleData(mockPeer, 'test-id', proto)
|
|
263
|
+
|
|
264
|
+
assert.ok(sentNode)
|
|
265
|
+
const DataResponseProto = globalThis.peernet.protos['peernet-data-response']
|
|
266
|
+
const decodedProto = await new DataResponseProto(sentNode.data)
|
|
267
|
+
await decodedProto.decode()
|
|
268
|
+
const decodedContent = new TextDecoder().decode(decodedProto.decoded.data)
|
|
269
|
+
assert.equal(decodedProto.decoded.hash, hash)
|
|
270
|
+
assert.equal(decodedContent, testString)
|
|
271
|
+
})
|
|
272
|
+
|
|
273
|
+
test('in-memory broadcast and handleData supports large binary data', async () => {
|
|
274
|
+
// Create a large binary buffer (e.g., 1MB)
|
|
275
|
+
const size = 1024 * 1024 // 1MB
|
|
276
|
+
const largeBuffer = new Uint8Array(size)
|
|
277
|
+
for (let i = 0; i < size; i++) largeBuffer[i] = i % 256
|
|
278
|
+
const path = '/large-binary'
|
|
279
|
+
const content = largeBuffer
|
|
280
|
+
const hash = await peernet.broadcast(path, { content })
|
|
281
|
+
|
|
282
|
+
// Prepare a mock peer and capture sendMessage output
|
|
283
|
+
let sentNode = null
|
|
284
|
+
const mockPeer = {
|
|
285
|
+
connected: true,
|
|
286
|
+
send: async (data, id) => {
|
|
287
|
+
sentNode = { data, id }
|
|
288
|
+
return Promise.resolve()
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
const proto = {
|
|
292
|
+
decoded: { hash }
|
|
293
|
+
}
|
|
294
|
+
await peernet.handleData(mockPeer, 'test-id', proto)
|
|
295
|
+
|
|
296
|
+
assert.ok(sentNode)
|
|
297
|
+
const DataResponseProto = globalThis.peernet.protos['peernet-data-response']
|
|
298
|
+
const decodedProto = await new DataResponseProto(sentNode.data)
|
|
299
|
+
await decodedProto.decode()
|
|
300
|
+
assert.equal(decodedProto.decoded.hash, hash)
|
|
301
|
+
const receivedBuffer = Buffer.from(decodedProto.decoded.data)
|
|
302
|
+
const originalBuffer = Buffer.from(largeBuffer)
|
|
303
|
+
assert.equal(Buffer.compare(receivedBuffer, originalBuffer), 0)
|
|
304
|
+
})
|
package/test-output.log
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
✔ initializes with provided options (0.407125ms)
|
|
2
|
+
✔ exposes identity instance (0.051667ms)
|
|
3
|
+
✔ has a DHT instance (0.04425ms)
|
|
4
|
+
✔ returns peers and connections safely (0.267542ms)
|
|
5
|
+
✔ provides callable helpers (0.048958ms)
|
|
6
|
+
✔ pubsub subscribe registers callback (10.395584ms)
|
|
7
|
+
✔ addProto registers protocol (0.160417ms)
|
|
8
|
+
✔ addProto does not overwrite existing protocol (0.057709ms)
|
|
9
|
+
✔ addCodec is callable (0.063084ms)
|
|
10
|
+
✔ selectAccount delegates to identity (0.068584ms)
|
|
11
|
+
✔ identity has loaded account data (0.241875ms)
|
|
12
|
+
✔ peerId matches identity id (0.031375ms)
|
|
13
|
+
✔ data store operations (0.030125ms)
|
|
14
|
+
✔ get attempts to retrieve from store (0.035667ms)
|
|
15
|
+
✔ message object provides get/put/has (0.053917ms)
|
|
16
|
+
✔ data object provides get/put/has (0.045833ms)
|
|
17
|
+
✔ block object provides get/put/has (0.067375ms)
|
|
18
|
+
✔ transaction object provides get/put/has (0.042333ms)
|
|
19
|
+
✔ folder object provides get/put/has (0.034167ms)
|
|
20
|
+
✔ Buffer property is available (0.024417ms)
|
|
21
|
+
✔ handleData is callable (0.017709ms)
|
|
22
|
+
✔ handleDHT is callable (0.016167ms)
|
|
23
|
+
✔ handleRequest is callable (0.016417ms)
|
|
24
|
+
✔ walk is callable (0.015459ms)
|
|
25
|
+
✔ providersFor is callable (0.015583ms)
|
|
26
|
+
✔ addRequestHandler is callable (0.015375ms)
|
|
27
|
+
✔ sendMessage is callable (0.01625ms)
|
|
28
|
+
✔ start method exists (0.02675ms)
|
|
29
|
+
✔ default stores are initialized (0.0275ms)
|
|
30
|
+
✔ identity has all expected methods (0.020084ms)
|
|
31
|
+
✔ identity wallet is loaded (0.019333ms)
|
|
32
|
+
✔ selected account is set (0.016208ms)
|
|
33
|
+
✔ error handling methods exist (0.017083ms)
|
|
34
|
+
DEBUG_HANDLE_DATA: entering in-memory branch { hash: 'BA43QACTNQLBH6SW7NO7ZQ63I3USRUBTD5DOWLEUFJGMY6JN2BSUAES54X' }
|
|
35
|
+
DEBUG_HANDLE_DATA: got from _inMemoryBroadcasts {
|
|
36
|
+
type: 'object',
|
|
37
|
+
isUint8Array: true,
|
|
38
|
+
length: 34,
|
|
39
|
+
slice: [
|
|
40
|
+
230, 224, 1, 5, 47,
|
|
41
|
+
116, 101, 115, 116, 21,
|
|
42
|
+
104, 101, 108, 108, 111,
|
|
43
|
+
32
|
|
44
|
+
]
|
|
45
|
+
}
|
|
46
|
+
✔ in-memory broadcast and handleData returns correct data (4.52275ms)
|
|
47
|
+
DEBUG_HANDLE_DATA: entering in-memory branch { hash: 'BA43QACYLVOY2HRRGV4NOUDQ3GWPQ7YZVBBM5DOKGZIY4UOYMS3K3JSCVV' }
|
|
48
|
+
DEBUG_HANDLE_DATA: got from _inMemoryBroadcasts {
|
|
49
|
+
type: 'object',
|
|
50
|
+
isUint8Array: true,
|
|
51
|
+
length: 1048599,
|
|
52
|
+
slice: [
|
|
53
|
+
230, 224, 1, 13, 47, 108,
|
|
54
|
+
97, 114, 103, 101, 45, 98,
|
|
55
|
+
105, 110, 97, 114
|
|
56
|
+
]
|
|
57
|
+
}
|
|
58
|
+
DEBUG_TEST receivedBuffer {
|
|
59
|
+
type: 'object',
|
|
60
|
+
isUint8Array: true,
|
|
61
|
+
length: 1048576,
|
|
62
|
+
slice: [
|
|
63
|
+
0, 1, 2, 3, 4, 5,
|
|
64
|
+
6, 7, 8, 9, 10, 11,
|
|
65
|
+
12, 13, 14, 15
|
|
66
|
+
]
|
|
67
|
+
}
|
|
68
|
+
DEBUG_TEST originalBuffer {
|
|
69
|
+
length: 1048576,
|
|
70
|
+
slice: [
|
|
71
|
+
0, 1, 2, 3, 4, 5,
|
|
72
|
+
6, 7, 8, 9, 10, 11,
|
|
73
|
+
12, 13, 14, 15
|
|
74
|
+
]
|
|
75
|
+
}
|
|
76
|
+
✔ in-memory broadcast and handleData supports large binary data (163.195292ms)
|
|
77
|
+
ℹ tests 35
|
|
78
|
+
ℹ suites 0
|
|
79
|
+
ℹ pass 35
|
|
80
|
+
ℹ fail 0
|
|
81
|
+
ℹ cancelled 0
|
|
82
|
+
ℹ skipped 0
|
|
83
|
+
ℹ todo 0
|
|
84
|
+
ℹ duration_ms 366.132458
|