@bsv/sdk 1.8.5 → 1.8.7
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/dist/cjs/package.json +1 -1
- package/dist/cjs/src/auth/Peer.js +21 -6
- package/dist/cjs/src/auth/Peer.js.map +1 -1
- package/dist/cjs/src/auth/clients/AuthFetch.js +229 -13
- package/dist/cjs/src/auth/clients/AuthFetch.js.map +1 -1
- package/dist/cjs/src/auth/clients/__tests__/AuthFetch.test.js +189 -0
- package/dist/cjs/src/auth/clients/__tests__/AuthFetch.test.js.map +1 -0
- package/dist/cjs/src/auth/transports/SimplifiedFetchTransport.js +162 -36
- package/dist/cjs/src/auth/transports/SimplifiedFetchTransport.js.map +1 -1
- package/dist/cjs/src/auth/transports/__tests__/SimplifiedFetchTransport.test.js +134 -0
- package/dist/cjs/src/auth/transports/__tests__/SimplifiedFetchTransport.test.js.map +1 -0
- package/dist/cjs/src/overlay-tools/LookupResolver.js +4 -4
- package/dist/cjs/src/overlay-tools/LookupResolver.js.map +1 -1
- package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
- package/dist/esm/src/auth/Peer.js +21 -6
- package/dist/esm/src/auth/Peer.js.map +1 -1
- package/dist/esm/src/auth/clients/AuthFetch.js +229 -13
- package/dist/esm/src/auth/clients/AuthFetch.js.map +1 -1
- package/dist/esm/src/auth/clients/__tests__/AuthFetch.test.js +187 -0
- package/dist/esm/src/auth/clients/__tests__/AuthFetch.test.js.map +1 -0
- package/dist/esm/src/auth/transports/SimplifiedFetchTransport.js +162 -36
- package/dist/esm/src/auth/transports/SimplifiedFetchTransport.js.map +1 -1
- package/dist/esm/src/auth/transports/__tests__/SimplifiedFetchTransport.test.js +109 -0
- package/dist/esm/src/auth/transports/__tests__/SimplifiedFetchTransport.test.js.map +1 -0
- package/dist/esm/src/overlay-tools/LookupResolver.js +4 -4
- package/dist/esm/src/overlay-tools/LookupResolver.js.map +1 -1
- package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
- package/dist/types/src/auth/Peer.d.ts +1 -0
- package/dist/types/src/auth/Peer.d.ts.map +1 -1
- package/dist/types/src/auth/clients/AuthFetch.d.ts +37 -0
- package/dist/types/src/auth/clients/AuthFetch.d.ts.map +1 -1
- package/dist/types/src/auth/clients/__tests__/AuthFetch.test.d.ts +2 -0
- package/dist/types/src/auth/clients/__tests__/AuthFetch.test.d.ts.map +1 -0
- package/dist/types/src/auth/transports/SimplifiedFetchTransport.d.ts +6 -0
- package/dist/types/src/auth/transports/SimplifiedFetchTransport.d.ts.map +1 -1
- package/dist/types/src/auth/transports/__tests__/SimplifiedFetchTransport.test.d.ts +2 -0
- package/dist/types/src/auth/transports/__tests__/SimplifiedFetchTransport.test.d.ts.map +1 -0
- package/dist/types/tsconfig.types.tsbuildinfo +1 -1
- package/dist/umd/bundle.js +3 -3
- package/dist/umd/bundle.js.map +1 -1
- package/package.json +1 -1
- package/src/auth/Peer.ts +25 -18
- package/src/auth/__tests/Peer.test.ts +238 -1
- package/src/auth/clients/AuthFetch.ts +327 -18
- package/src/auth/clients/__tests__/AuthFetch.test.ts +262 -0
- package/src/auth/transports/SimplifiedFetchTransport.ts +185 -35
- package/src/auth/transports/__tests__/SimplifiedFetchTransport.test.ts +126 -0
- package/src/overlay-tools/LookupResolver.ts +3 -3
package/package.json
CHANGED
package/src/auth/Peer.ts
CHANGED
|
@@ -149,13 +149,8 @@ export class Peer {
|
|
|
149
149
|
|
|
150
150
|
try {
|
|
151
151
|
await this.transport.send(generalMessage)
|
|
152
|
-
} catch (error:
|
|
153
|
-
|
|
154
|
-
`Failed to send message to peer ${peerSession.peerIdentityKey ?? 'unknown'
|
|
155
|
-
}: ${String(error.message)}`
|
|
156
|
-
)
|
|
157
|
-
e.stack = error.stack
|
|
158
|
-
throw e
|
|
152
|
+
} catch (error: unknown) {
|
|
153
|
+
this.propagateTransportError(peerSession.peerIdentityKey, error)
|
|
159
154
|
}
|
|
160
155
|
}
|
|
161
156
|
|
|
@@ -215,11 +210,8 @@ export class Peer {
|
|
|
215
210
|
|
|
216
211
|
try {
|
|
217
212
|
await this.transport.send(certRequestMessage)
|
|
218
|
-
} catch (error:
|
|
219
|
-
|
|
220
|
-
`Failed to send certificate request message to peer ${peerSession.peerIdentityKey ?? 'unknown'
|
|
221
|
-
}: ${String(error.message)}`
|
|
222
|
-
)
|
|
213
|
+
} catch (error: unknown) {
|
|
214
|
+
this.propagateTransportError(peerSession.peerIdentityKey, error)
|
|
223
215
|
}
|
|
224
216
|
}
|
|
225
217
|
|
|
@@ -424,6 +416,25 @@ export class Peer {
|
|
|
424
416
|
this.onInitialResponseReceivedCallbacks.delete(callbackID)
|
|
425
417
|
}
|
|
426
418
|
|
|
419
|
+
private propagateTransportError (peerIdentityKey: string | undefined, error: unknown): never {
|
|
420
|
+
if (error instanceof Error) {
|
|
421
|
+
if (peerIdentityKey != null) {
|
|
422
|
+
const existingDetails = (error as any).details
|
|
423
|
+
if (existingDetails != null && typeof existingDetails === 'object') {
|
|
424
|
+
if (existingDetails.peerIdentityKey == null) {
|
|
425
|
+
existingDetails.peerIdentityKey = peerIdentityKey
|
|
426
|
+
}
|
|
427
|
+
} else {
|
|
428
|
+
(error as any).details = { peerIdentityKey }
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
throw error
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
const message = `Failed to send message to peer ${peerIdentityKey ?? 'unknown'}: ${String(error)}`
|
|
435
|
+
throw new Error(message)
|
|
436
|
+
}
|
|
437
|
+
|
|
427
438
|
/**
|
|
428
439
|
* Handles incoming messages from the transport.
|
|
429
440
|
*
|
|
@@ -734,12 +745,8 @@ export class Peer {
|
|
|
734
745
|
|
|
735
746
|
try {
|
|
736
747
|
await this.transport.send(certificateResponse)
|
|
737
|
-
} catch (error:
|
|
738
|
-
|
|
739
|
-
throw new Error(
|
|
740
|
-
`Failed to send certificate response message to peer ${peerSession.peerIdentityKey ?? 'unknown'
|
|
741
|
-
}: ${errorMessage}`
|
|
742
|
-
)
|
|
748
|
+
} catch (error: unknown) {
|
|
749
|
+
this.propagateTransportError(peerSession.peerIdentityKey, error)
|
|
743
750
|
}
|
|
744
751
|
}
|
|
745
752
|
|
|
@@ -7,6 +7,7 @@ import { VerifiableCertificate } from '../../auth/certificates/VerifiableCertifi
|
|
|
7
7
|
import { MasterCertificate } from '../../auth/certificates/MasterCertificate.js'
|
|
8
8
|
import { getVerifiableCertificates } from '../../auth/utils/getVerifiableCertificates.js'
|
|
9
9
|
import { CompletedProtoWallet } from '../certificates/__tests/CompletedProtoWallet.js'
|
|
10
|
+
import { SimplifiedFetchTransport } from '../../auth/transports/SimplifiedFetchTransport.js'
|
|
10
11
|
|
|
11
12
|
const certifierPrivKey = new PrivateKey(21)
|
|
12
13
|
const alicePrivKey = new PrivateKey(22)
|
|
@@ -355,7 +356,7 @@ describe('Peer class mutual authentication and certificate exchange', () => {
|
|
|
355
356
|
// console.error(e)
|
|
356
357
|
})
|
|
357
358
|
})
|
|
358
|
-
|
|
359
|
+
})
|
|
359
360
|
|
|
360
361
|
await alice.toPeer(Utils.toArray('Hello Bob!'))
|
|
361
362
|
await bobReceivedGeneralMessage
|
|
@@ -364,6 +365,57 @@ describe('Peer class mutual authentication and certificate exchange', () => {
|
|
|
364
365
|
expect(certificatesReceivedByBob).toEqual([aliceVerifiableCertificate])
|
|
365
366
|
}, 15000)
|
|
366
367
|
|
|
368
|
+
describe('propagateTransportError', () => {
|
|
369
|
+
const createPeerInstance = (): Peer => {
|
|
370
|
+
const transport: Transport = {
|
|
371
|
+
send: jest.fn(async (_message: AuthMessage) => {}),
|
|
372
|
+
onData: async () => {}
|
|
373
|
+
}
|
|
374
|
+
return new Peer({} as WalletInterface, transport)
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
it('adds peer identity details to existing errors', () => {
|
|
378
|
+
const peer = createPeerInstance()
|
|
379
|
+
const originalError = new Error('send failed')
|
|
380
|
+
|
|
381
|
+
let thrown: Error | undefined
|
|
382
|
+
try {
|
|
383
|
+
(peer as any).propagateTransportError('peer-public-key', originalError)
|
|
384
|
+
} catch (error) {
|
|
385
|
+
thrown = error as Error
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
expect(thrown).toBe(originalError)
|
|
389
|
+
expect((thrown as any).details).toEqual({ peerIdentityKey: 'peer-public-key' })
|
|
390
|
+
})
|
|
391
|
+
|
|
392
|
+
it('preserves existing details when appending peer identity', () => {
|
|
393
|
+
const peer = createPeerInstance()
|
|
394
|
+
const originalError = new Error('existing details')
|
|
395
|
+
;(originalError as any).details = { status: 503 }
|
|
396
|
+
|
|
397
|
+
let thrown: Error | undefined
|
|
398
|
+
try {
|
|
399
|
+
(peer as any).propagateTransportError('peer-public-key', originalError)
|
|
400
|
+
} catch (error) {
|
|
401
|
+
thrown = error as Error
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
expect(thrown).toBe(originalError)
|
|
405
|
+
expect((thrown as any).details).toEqual({
|
|
406
|
+
status: 503,
|
|
407
|
+
peerIdentityKey: 'peer-public-key'
|
|
408
|
+
})
|
|
409
|
+
})
|
|
410
|
+
|
|
411
|
+
it('wraps non-error values with a helpful message', () => {
|
|
412
|
+
const peer = createPeerInstance()
|
|
413
|
+
expect(() => (peer as any).propagateTransportError(undefined, 'timeout')).toThrow(
|
|
414
|
+
'Failed to send message to peer unknown: timeout'
|
|
415
|
+
)
|
|
416
|
+
})
|
|
417
|
+
})
|
|
418
|
+
|
|
367
419
|
it('Alice requests Bob to present his library card before lending him a book', async () => {
|
|
368
420
|
const alicePubKey = (await walletA.getPublicKey({ identityKey: true }))
|
|
369
421
|
.publicKey
|
|
@@ -818,4 +870,189 @@ describe('Peer class mutual authentication and certificate exchange', () => {
|
|
|
818
870
|
expect(certificatesReceivedByAlice).toEqual([bobVerifiableCertificate])
|
|
819
871
|
expect(certificatesReceivedByBob).toEqual([aliceVerifiableCertificate])
|
|
820
872
|
}, 20000)
|
|
873
|
+
|
|
874
|
+
describe('Transport Error Handling', () => {
|
|
875
|
+
const privKey = PrivateKey.fromRandom()
|
|
876
|
+
|
|
877
|
+
test('Should trigger "Failed to send message to peer" error with network failure', async () => {
|
|
878
|
+
// Create a mock fetch that always fails
|
|
879
|
+
const failingFetch = (jest.fn() as any).mockRejectedValue(new Error('Network connection failed'))
|
|
880
|
+
|
|
881
|
+
// Create a transport that will fail
|
|
882
|
+
const transport = new SimplifiedFetchTransport('http://localhost:9999', failingFetch)
|
|
883
|
+
|
|
884
|
+
// Create a peer with the failing transport
|
|
885
|
+
const wallet = new CompletedProtoWallet(privKey)
|
|
886
|
+
const peer = new Peer(wallet, transport)
|
|
887
|
+
|
|
888
|
+
// Register a dummy onData callback (required before sending)
|
|
889
|
+
await transport.onData(async (message) => {
|
|
890
|
+
// This won't be called due to network failure
|
|
891
|
+
})
|
|
892
|
+
|
|
893
|
+
// Try to send a message to peer - this should fail and trigger the error
|
|
894
|
+
try {
|
|
895
|
+
await peer.toPeer([1, 2, 3, 4], '03abc123def456')
|
|
896
|
+
fail('Expected error to be thrown')
|
|
897
|
+
} catch (error: any) {
|
|
898
|
+
expect(error.message).toContain('Network error while sending authenticated request')
|
|
899
|
+
expect(error.message).toContain('Network connection failed')
|
|
900
|
+
}
|
|
901
|
+
}, 15000)
|
|
902
|
+
|
|
903
|
+
test('Should trigger error with connection timeout', async () => {
|
|
904
|
+
// Create a fetch that times out
|
|
905
|
+
const timeoutFetch = (jest.fn() as any).mockImplementation(() => {
|
|
906
|
+
return new Promise((_, reject) => {
|
|
907
|
+
setTimeout(() => {
|
|
908
|
+
reject(new Error('Request timeout'))
|
|
909
|
+
}, 100)
|
|
910
|
+
})
|
|
911
|
+
})
|
|
912
|
+
|
|
913
|
+
const transport = new SimplifiedFetchTransport('http://localhost:9999', timeoutFetch)
|
|
914
|
+
const wallet = new CompletedProtoWallet(privKey)
|
|
915
|
+
const peer = new Peer(wallet, transport)
|
|
916
|
+
|
|
917
|
+
await transport.onData(async (message) => {})
|
|
918
|
+
|
|
919
|
+
try {
|
|
920
|
+
await peer.toPeer([5, 6, 7, 8], '03def789abc123')
|
|
921
|
+
fail('Expected error to be thrown')
|
|
922
|
+
} catch (error: any) {
|
|
923
|
+
expect(error.message).toContain('Network error while sending authenticated request')
|
|
924
|
+
expect(error.message).toContain('Request timeout')
|
|
925
|
+
}
|
|
926
|
+
}, 15000)
|
|
927
|
+
|
|
928
|
+
test('Should trigger error with DNS resolution failure', async () => {
|
|
929
|
+
// Create a fetch that fails with DNS error
|
|
930
|
+
const dnsFetch = (jest.fn() as any).mockRejectedValue({
|
|
931
|
+
code: 'ENOTFOUND',
|
|
932
|
+
errno: -3008,
|
|
933
|
+
message: 'getaddrinfo ENOTFOUND nonexistent.domain'
|
|
934
|
+
})
|
|
935
|
+
|
|
936
|
+
const transport = new SimplifiedFetchTransport('http://nonexistent.domain:3000', dnsFetch)
|
|
937
|
+
const wallet = new CompletedProtoWallet(privKey)
|
|
938
|
+
const peer = new Peer(wallet, transport)
|
|
939
|
+
|
|
940
|
+
await transport.onData(async (message) => {})
|
|
941
|
+
|
|
942
|
+
try {
|
|
943
|
+
await peer.toPeer([9, 10, 11, 12], '03xyz987fed654')
|
|
944
|
+
fail('Expected error to be thrown')
|
|
945
|
+
} catch (error: any) {
|
|
946
|
+
expect(error.message).toContain('Network error while sending authenticated request')
|
|
947
|
+
expect(error.message).toContain('[object Object]')
|
|
948
|
+
}
|
|
949
|
+
}, 15000)
|
|
950
|
+
|
|
951
|
+
test('Should trigger error during certificate request send', async () => {
|
|
952
|
+
// Create a failing fetch
|
|
953
|
+
const failingFetch = (jest.fn() as any).mockRejectedValue(new Error('Connection reset by peer'))
|
|
954
|
+
|
|
955
|
+
const transport = new SimplifiedFetchTransport('http://localhost:9999', failingFetch)
|
|
956
|
+
const wallet = new CompletedProtoWallet(privKey)
|
|
957
|
+
const peer = new Peer(wallet, transport)
|
|
958
|
+
|
|
959
|
+
await transport.onData(async (message) => {})
|
|
960
|
+
|
|
961
|
+
try {
|
|
962
|
+
// Try to send a certificate request - this should also trigger the error
|
|
963
|
+
await peer.requestCertificates({
|
|
964
|
+
certifiers: ['03certifier123'],
|
|
965
|
+
types: { 'type1': ['field1'] }
|
|
966
|
+
}, '03abc123def456')
|
|
967
|
+
fail('Expected error to be thrown')
|
|
968
|
+
} catch (error: any) {
|
|
969
|
+
expect(error.message).toContain('Network error while sending authenticated request')
|
|
970
|
+
expect(error.message).toContain('Connection reset by peer')
|
|
971
|
+
}
|
|
972
|
+
}, 15000)
|
|
973
|
+
|
|
974
|
+
test('Should trigger error during certificate response send', async () => {
|
|
975
|
+
// Create a failing fetch
|
|
976
|
+
const failingFetch = (jest.fn() as any).mockRejectedValue(new Error('Socket hang up'))
|
|
977
|
+
|
|
978
|
+
const transport = new SimplifiedFetchTransport('http://localhost:9999', failingFetch)
|
|
979
|
+
const wallet = new CompletedProtoWallet(privKey)
|
|
980
|
+
const peer = new Peer(wallet, transport)
|
|
981
|
+
|
|
982
|
+
await transport.onData(async (message) => {})
|
|
983
|
+
|
|
984
|
+
try {
|
|
985
|
+
// Try to send a certificate response - this should also trigger the error
|
|
986
|
+
await peer.sendCertificateResponse('03verifier123', [])
|
|
987
|
+
fail('Expected error to be thrown')
|
|
988
|
+
} catch (error: any) {
|
|
989
|
+
expect(error.message).toContain('Network error while sending authenticated request')
|
|
990
|
+
expect(error.message).toContain('Socket hang up')
|
|
991
|
+
}
|
|
992
|
+
}, 15000)
|
|
993
|
+
|
|
994
|
+
test('Should propagate network errors with proper details', async () => {
|
|
995
|
+
// Create a fetch that throws a custom error
|
|
996
|
+
const customError = new Error('Custom transport error')
|
|
997
|
+
customError.stack = 'Custom stack trace'
|
|
998
|
+
const failingFetch = (jest.fn() as any).mockRejectedValue(customError)
|
|
999
|
+
|
|
1000
|
+
const transport = new SimplifiedFetchTransport('http://localhost:9999', failingFetch)
|
|
1001
|
+
const wallet = new CompletedProtoWallet(privKey)
|
|
1002
|
+
const peer = new Peer(wallet, transport)
|
|
1003
|
+
|
|
1004
|
+
await transport.onData(async (message) => {})
|
|
1005
|
+
|
|
1006
|
+
try {
|
|
1007
|
+
await peer.toPeer([13, 14, 15, 16], '03peer123456')
|
|
1008
|
+
fail('Expected error to be thrown')
|
|
1009
|
+
} catch (error: any) {
|
|
1010
|
+
// Should create a network error wrapping the original error
|
|
1011
|
+
expect(error.message).toContain('Network error while sending authenticated request')
|
|
1012
|
+
expect(error.message).toContain('Custom transport error')
|
|
1013
|
+
expect(error.cause).toBeDefined()
|
|
1014
|
+
expect(error.cause.message).toBe('Custom transport error')
|
|
1015
|
+
}
|
|
1016
|
+
}, 15000)
|
|
1017
|
+
|
|
1018
|
+
test('Should handle non-Error transport failures', async () => {
|
|
1019
|
+
// Create a fetch that throws a non-Error object
|
|
1020
|
+
const failingFetch = (jest.fn() as any).mockRejectedValue('String error message')
|
|
1021
|
+
|
|
1022
|
+
const transport = new SimplifiedFetchTransport('http://localhost:9999', failingFetch)
|
|
1023
|
+
const wallet = new CompletedProtoWallet(privKey)
|
|
1024
|
+
const peer = new Peer(wallet, transport)
|
|
1025
|
+
|
|
1026
|
+
await transport.onData(async (message) => {})
|
|
1027
|
+
|
|
1028
|
+
try {
|
|
1029
|
+
await peer.toPeer([17, 18, 19, 20], '03peer789abc')
|
|
1030
|
+
fail('Expected error to be thrown')
|
|
1031
|
+
} catch (error: any) {
|
|
1032
|
+
// Should create network error for non-Error objects
|
|
1033
|
+
expect(error.message).toContain('Network error while sending authenticated request')
|
|
1034
|
+
expect(error.message).toContain('String error message')
|
|
1035
|
+
}
|
|
1036
|
+
}, 15000)
|
|
1037
|
+
|
|
1038
|
+
test('Should handle undefined peer identity gracefully', async () => {
|
|
1039
|
+
// Create a failing fetch
|
|
1040
|
+
const failingFetch = (jest.fn() as any).mockRejectedValue('Network failure')
|
|
1041
|
+
|
|
1042
|
+
const transport = new SimplifiedFetchTransport('http://localhost:9999', failingFetch)
|
|
1043
|
+
const wallet = new CompletedProtoWallet(privKey)
|
|
1044
|
+
const peer = new Peer(wallet, transport)
|
|
1045
|
+
|
|
1046
|
+
await transport.onData(async (message) => {})
|
|
1047
|
+
|
|
1048
|
+
try {
|
|
1049
|
+
// Try to send to an undefined peer (this might happen in some edge cases)
|
|
1050
|
+
await peer.toPeer([21, 22, 23, 24], undefined as any)
|
|
1051
|
+
fail('Expected error to be thrown')
|
|
1052
|
+
} catch (error: any) {
|
|
1053
|
+
expect(error.message).toContain('Network error while sending authenticated request')
|
|
1054
|
+
expect(error.message).toContain('Network failure')
|
|
1055
|
+
}
|
|
1056
|
+
}, 15000)
|
|
1057
|
+
})
|
|
821
1058
|
})
|