@libp2p/interface-compliance-tests 3.0.7-ea8a0637 → 3.0.7-eabf6f36

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.
Files changed (183) hide show
  1. package/README.md +9 -0
  2. package/dist/src/connection/index.d.ts +5 -0
  3. package/dist/src/connection/index.d.ts.map +1 -0
  4. package/dist/src/connection/index.js +150 -0
  5. package/dist/src/connection/index.js.map +1 -0
  6. package/dist/src/connection-encryption/index.d.ts +5 -0
  7. package/dist/src/connection-encryption/index.d.ts.map +1 -0
  8. package/dist/src/connection-encryption/index.js +71 -0
  9. package/dist/src/connection-encryption/index.js.map +1 -0
  10. package/dist/src/connection-encryption/utils/index.d.ts +3 -0
  11. package/dist/src/connection-encryption/utils/index.d.ts.map +1 -0
  12. package/dist/src/connection-encryption/utils/index.js +19 -0
  13. package/dist/src/connection-encryption/utils/index.js.map +1 -0
  14. package/dist/src/index.d.ts.map +1 -1
  15. package/dist/src/is-valid-tick.d.ts.map +1 -1
  16. package/dist/src/is-valid-tick.js.map +1 -1
  17. package/dist/src/mocks/connection-encrypter.d.ts +3 -0
  18. package/dist/src/mocks/connection-encrypter.d.ts.map +1 -0
  19. package/dist/src/mocks/connection-encrypter.js +98 -0
  20. package/dist/src/mocks/connection-encrypter.js.map +1 -0
  21. package/dist/src/mocks/connection-gater.d.ts +3 -0
  22. package/dist/src/mocks/connection-gater.d.ts.map +1 -0
  23. package/dist/src/mocks/connection-gater.js +17 -0
  24. package/dist/src/mocks/connection-gater.js.map +1 -0
  25. package/dist/src/mocks/connection-manager.d.ts +29 -0
  26. package/dist/src/mocks/connection-manager.d.ts.map +1 -0
  27. package/dist/src/mocks/connection-manager.js +145 -0
  28. package/dist/src/mocks/connection-manager.js.map +1 -0
  29. package/dist/src/mocks/connection.d.ts +32 -0
  30. package/dist/src/mocks/connection.d.ts.map +1 -0
  31. package/dist/src/mocks/connection.js +167 -0
  32. package/dist/src/mocks/connection.js.map +1 -0
  33. package/dist/src/mocks/duplex.d.ts +3 -0
  34. package/dist/src/mocks/duplex.d.ts.map +1 -0
  35. package/dist/src/mocks/duplex.js +9 -0
  36. package/dist/src/mocks/duplex.js.map +1 -0
  37. package/dist/src/mocks/index.d.ts +13 -0
  38. package/dist/src/mocks/index.d.ts.map +1 -0
  39. package/dist/src/mocks/index.js +11 -0
  40. package/dist/src/mocks/index.js.map +1 -0
  41. package/dist/src/mocks/metrics.d.ts +3 -0
  42. package/dist/src/mocks/metrics.d.ts.map +1 -0
  43. package/dist/src/mocks/metrics.js +115 -0
  44. package/dist/src/mocks/metrics.js.map +1 -0
  45. package/dist/src/mocks/multiaddr-connection.d.ts +17 -0
  46. package/dist/src/mocks/multiaddr-connection.d.ts.map +1 -0
  47. package/dist/src/mocks/multiaddr-connection.js +60 -0
  48. package/dist/src/mocks/multiaddr-connection.js.map +1 -0
  49. package/dist/src/mocks/muxer.d.ts +36 -0
  50. package/dist/src/mocks/muxer.d.ts.map +1 -0
  51. package/dist/src/mocks/muxer.js +213 -0
  52. package/dist/src/mocks/muxer.js.map +1 -0
  53. package/dist/src/mocks/peer-discovery.d.ts +22 -0
  54. package/dist/src/mocks/peer-discovery.d.ts.map +1 -0
  55. package/dist/src/mocks/peer-discovery.js +47 -0
  56. package/dist/src/mocks/peer-discovery.js.map +1 -0
  57. package/dist/src/mocks/registrar.d.ts +18 -0
  58. package/dist/src/mocks/registrar.d.ts.map +1 -0
  59. package/dist/src/mocks/registrar.js +66 -0
  60. package/dist/src/mocks/registrar.js.map +1 -0
  61. package/dist/src/mocks/upgrader.d.ts +10 -0
  62. package/dist/src/mocks/upgrader.d.ts.map +1 -0
  63. package/dist/src/mocks/upgrader.js +31 -0
  64. package/dist/src/mocks/upgrader.js.map +1 -0
  65. package/dist/src/peer-discovery/index.d.ts +5 -0
  66. package/dist/src/peer-discovery/index.d.ts.map +1 -0
  67. package/dist/src/peer-discovery/index.js +66 -0
  68. package/dist/src/peer-discovery/index.js.map +1 -0
  69. package/dist/src/pubsub/api.d.ts +6 -0
  70. package/dist/src/pubsub/api.d.ts.map +1 -0
  71. package/dist/src/pubsub/api.js +87 -0
  72. package/dist/src/pubsub/api.js.map +1 -0
  73. package/dist/src/pubsub/connection-handlers.d.ts +6 -0
  74. package/dist/src/pubsub/connection-handlers.d.ts.map +1 -0
  75. package/dist/src/pubsub/connection-handlers.js +329 -0
  76. package/dist/src/pubsub/connection-handlers.js.map +1 -0
  77. package/dist/src/pubsub/emit-self.d.ts +6 -0
  78. package/dist/src/pubsub/emit-self.d.ts.map +1 -0
  79. package/dist/src/pubsub/emit-self.js +80 -0
  80. package/dist/src/pubsub/emit-self.js.map +1 -0
  81. package/dist/src/pubsub/index.d.ts +18 -0
  82. package/dist/src/pubsub/index.d.ts.map +1 -0
  83. package/dist/src/pubsub/index.js +17 -0
  84. package/dist/src/pubsub/index.js.map +1 -0
  85. package/dist/src/pubsub/messages.d.ts +6 -0
  86. package/dist/src/pubsub/messages.d.ts.map +1 -0
  87. package/dist/src/pubsub/messages.js +48 -0
  88. package/dist/src/pubsub/messages.js.map +1 -0
  89. package/dist/src/pubsub/multiple-nodes.d.ts +6 -0
  90. package/dist/src/pubsub/multiple-nodes.d.ts.map +1 -0
  91. package/dist/src/pubsub/multiple-nodes.js +350 -0
  92. package/dist/src/pubsub/multiple-nodes.js.map +1 -0
  93. package/dist/src/pubsub/two-nodes.d.ts +6 -0
  94. package/dist/src/pubsub/two-nodes.d.ts.map +1 -0
  95. package/dist/src/pubsub/two-nodes.js +217 -0
  96. package/dist/src/pubsub/two-nodes.js.map +1 -0
  97. package/dist/src/pubsub/utils.d.ts +6 -0
  98. package/dist/src/pubsub/utils.d.ts.map +1 -0
  99. package/dist/src/pubsub/utils.js +22 -0
  100. package/dist/src/pubsub/utils.js.map +1 -0
  101. package/dist/src/stream-muxer/base-test.d.ts +5 -0
  102. package/dist/src/stream-muxer/base-test.d.ts.map +1 -0
  103. package/dist/src/stream-muxer/base-test.js +153 -0
  104. package/dist/src/stream-muxer/base-test.js.map +1 -0
  105. package/dist/src/stream-muxer/close-test.d.ts +5 -0
  106. package/dist/src/stream-muxer/close-test.d.ts.map +1 -0
  107. package/dist/src/stream-muxer/close-test.js +357 -0
  108. package/dist/src/stream-muxer/close-test.js.map +1 -0
  109. package/dist/src/stream-muxer/fixtures/pb/message.d.ts +13 -0
  110. package/dist/src/stream-muxer/fixtures/pb/message.d.ts.map +1 -0
  111. package/dist/src/stream-muxer/fixtures/pb/message.js +67 -0
  112. package/dist/src/stream-muxer/fixtures/pb/message.js.map +1 -0
  113. package/dist/src/stream-muxer/index.d.ts +5 -0
  114. package/dist/src/stream-muxer/index.d.ts.map +1 -0
  115. package/dist/src/stream-muxer/index.js +13 -0
  116. package/dist/src/stream-muxer/index.js.map +1 -0
  117. package/dist/src/stream-muxer/mega-stress-test.d.ts +5 -0
  118. package/dist/src/stream-muxer/mega-stress-test.d.ts.map +1 -0
  119. package/dist/src/stream-muxer/mega-stress-test.js +11 -0
  120. package/dist/src/stream-muxer/mega-stress-test.js.map +1 -0
  121. package/dist/src/stream-muxer/spawner.d.ts +4 -0
  122. package/dist/src/stream-muxer/spawner.d.ts.map +1 -0
  123. package/dist/src/stream-muxer/spawner.js +37 -0
  124. package/dist/src/stream-muxer/spawner.js.map +1 -0
  125. package/dist/src/stream-muxer/stress-test.d.ts +5 -0
  126. package/dist/src/stream-muxer/stress-test.d.ts.map +1 -0
  127. package/dist/src/stream-muxer/stress-test.js +23 -0
  128. package/dist/src/stream-muxer/stress-test.js.map +1 -0
  129. package/dist/src/transport/dial-test.d.ts +5 -0
  130. package/dist/src/transport/dial-test.d.ts.map +1 -0
  131. package/dist/src/transport/dial-test.js +98 -0
  132. package/dist/src/transport/dial-test.js.map +1 -0
  133. package/dist/src/transport/filter-test.d.ts +5 -0
  134. package/dist/src/transport/filter-test.d.ts.map +1 -0
  135. package/dist/src/transport/filter-test.js +18 -0
  136. package/dist/src/transport/filter-test.js.map +1 -0
  137. package/dist/src/transport/index.d.ts +15 -0
  138. package/dist/src/transport/index.d.ts.map +1 -0
  139. package/dist/src/transport/index.js +11 -0
  140. package/dist/src/transport/index.js.map +1 -0
  141. package/dist/src/transport/listen-test.d.ts +5 -0
  142. package/dist/src/transport/listen-test.d.ts.map +1 -0
  143. package/dist/src/transport/listen-test.js +152 -0
  144. package/dist/src/transport/listen-test.js.map +1 -0
  145. package/package.json +72 -5
  146. package/src/connection/index.ts +182 -0
  147. package/src/connection-encryption/index.ts +97 -0
  148. package/src/connection-encryption/utils/index.ts +24 -0
  149. package/src/index.ts +0 -1
  150. package/src/is-valid-tick.ts +0 -1
  151. package/src/mocks/connection-encrypter.ts +113 -0
  152. package/src/mocks/connection-gater.ts +18 -0
  153. package/src/mocks/connection-manager.ts +211 -0
  154. package/src/mocks/connection.ts +226 -0
  155. package/src/mocks/duplex.ts +10 -0
  156. package/src/mocks/index.ts +12 -0
  157. package/src/mocks/metrics.ts +162 -0
  158. package/src/mocks/multiaddr-connection.ts +76 -0
  159. package/src/mocks/muxer.ts +303 -0
  160. package/src/mocks/peer-discovery.ts +60 -0
  161. package/src/mocks/registrar.ts +88 -0
  162. package/src/mocks/upgrader.ts +49 -0
  163. package/src/peer-discovery/index.ts +90 -0
  164. package/src/pubsub/api.ts +114 -0
  165. package/src/pubsub/connection-handlers.ts +413 -0
  166. package/src/pubsub/emit-self.ts +99 -0
  167. package/src/pubsub/index.ts +34 -0
  168. package/src/pubsub/messages.ts +59 -0
  169. package/src/pubsub/multiple-nodes.ts +440 -0
  170. package/src/pubsub/two-nodes.ts +273 -0
  171. package/src/pubsub/utils.ts +29 -0
  172. package/src/stream-muxer/base-test.ts +196 -0
  173. package/src/stream-muxer/close-test.ts +442 -0
  174. package/src/stream-muxer/fixtures/pb/message.proto +7 -0
  175. package/src/stream-muxer/fixtures/pb/message.ts +87 -0
  176. package/src/stream-muxer/index.ts +15 -0
  177. package/src/stream-muxer/mega-stress-test.ts +14 -0
  178. package/src/stream-muxer/spawner.ts +55 -0
  179. package/src/stream-muxer/stress-test.ts +27 -0
  180. package/src/transport/dial-test.ts +124 -0
  181. package/src/transport/filter-test.ts +25 -0
  182. package/src/transport/index.ts +25 -0
  183. package/src/transport/listen-test.ts +191 -0
@@ -0,0 +1,10 @@
1
+ import type { Duplex, Source } from 'it-stream-types'
2
+
3
+ export function mockDuplex (): Duplex<AsyncGenerator<Uint8Array>, Source<Uint8Array>, Promise<void>> {
4
+ return {
5
+ source: (async function * () {
6
+ yield * []
7
+ }()),
8
+ sink: async () => {}
9
+ }
10
+ }
@@ -0,0 +1,12 @@
1
+ export { mockConnectionEncrypter } from './connection-encrypter.js'
2
+ export { mockConnectionGater } from './connection-gater.js'
3
+ export { mockConnectionManager, mockNetwork } from './connection-manager.js'
4
+ export { mockConnection, mockStream, connectionPair } from './connection.js'
5
+ export { mockMultiaddrConnection, mockMultiaddrConnPair } from './multiaddr-connection.js'
6
+ export { mockMuxer } from './muxer.js'
7
+ export { mockRegistrar } from './registrar.js'
8
+ export { mockUpgrader } from './upgrader.js'
9
+ export { mockDuplex } from './duplex.js'
10
+ export { mockMetrics } from './metrics.js'
11
+ export type { MockUpgraderInit } from './upgrader.js'
12
+ export type { MockNetworkComponents } from './connection-manager.js'
@@ -0,0 +1,162 @@
1
+ import type { MultiaddrConnection, Stream, Connection } from '@libp2p/interface/connection'
2
+ import type { Metric, MetricGroup, StopTimer, Metrics, CalculatedMetricOptions, MetricOptions } from '@libp2p/interface/metrics'
3
+
4
+ class DefaultMetric implements Metric {
5
+ public value: number = 0
6
+
7
+ update (value: number): void {
8
+ this.value = value
9
+ }
10
+
11
+ increment (value: number = 1): void {
12
+ this.value += value
13
+ }
14
+
15
+ decrement (value: number = 1): void {
16
+ this.value -= value
17
+ }
18
+
19
+ reset (): void {
20
+ this.value = 0
21
+ }
22
+
23
+ timer (): StopTimer {
24
+ const start = Date.now()
25
+
26
+ return () => {
27
+ this.value = Date.now() - start
28
+ }
29
+ }
30
+ }
31
+
32
+ class DefaultGroupMetric implements MetricGroup {
33
+ public values: Record<string, number> = {}
34
+
35
+ update (values: Record<string, number>): void {
36
+ Object.entries(values).forEach(([key, value]) => {
37
+ this.values[key] = value
38
+ })
39
+ }
40
+
41
+ increment (values: Record<string, number | unknown>): void {
42
+ Object.entries(values).forEach(([key, value]) => {
43
+ this.values[key] = this.values[key] ?? 0
44
+ const inc = typeof value === 'number' ? value : 1
45
+
46
+ this.values[key] += Number(inc)
47
+ })
48
+ }
49
+
50
+ decrement (values: Record<string, number | unknown>): void {
51
+ Object.entries(values).forEach(([key, value]) => {
52
+ this.values[key] = this.values[key] ?? 0
53
+ const dec = typeof value === 'number' ? value : 1
54
+
55
+ this.values[key] -= Number(dec)
56
+ })
57
+ }
58
+
59
+ reset (): void {
60
+ this.values = {}
61
+ }
62
+
63
+ timer (key: string): StopTimer {
64
+ const start = Date.now()
65
+
66
+ return () => {
67
+ this.values[key] = Date.now() - start
68
+ }
69
+ }
70
+ }
71
+
72
+ class MockMetrics implements Metrics {
73
+ public metrics = new Map<string, any>()
74
+
75
+ trackMultiaddrConnection (maConn: MultiaddrConnection): void {
76
+
77
+ }
78
+
79
+ trackProtocolStream (stream: Stream, connection: Connection): void {
80
+
81
+ }
82
+
83
+ registerMetric (name: string, opts: CalculatedMetricOptions): void
84
+ registerMetric (name: string, opts?: MetricOptions): Metric
85
+ registerMetric (name: string, opts: any): any {
86
+ if (name == null ?? name.trim() === '') {
87
+ throw new Error('Metric name is required')
88
+ }
89
+
90
+ if (opts?.calculate != null) {
91
+ // calculated metric
92
+ this.metrics.set(name, opts.calculate)
93
+ return
94
+ }
95
+
96
+ const metric = new DefaultMetric()
97
+ this.metrics.set(name, metric)
98
+
99
+ return metric
100
+ }
101
+
102
+ registerCounter (name: string, opts: CalculatedMetricOptions): void
103
+ registerCounter (name: string, opts?: MetricOptions): Metric
104
+ registerCounter (name: string, opts: any): any {
105
+ if (name == null ?? name.trim() === '') {
106
+ throw new Error('Metric name is required')
107
+ }
108
+
109
+ if (opts?.calculate != null) {
110
+ // calculated metric
111
+ this.metrics.set(name, opts.calculate)
112
+ return
113
+ }
114
+
115
+ const metric = new DefaultMetric()
116
+ this.metrics.set(name, metric)
117
+
118
+ return metric
119
+ }
120
+
121
+ registerMetricGroup (name: string, opts: CalculatedMetricOptions<Record<string, number>>): void
122
+ registerMetricGroup (name: string, opts?: MetricOptions): MetricGroup
123
+ registerMetricGroup (name: string, opts: any): any {
124
+ if (name == null ?? name.trim() === '') {
125
+ throw new Error('Metric name is required')
126
+ }
127
+
128
+ if (opts?.calculate != null) {
129
+ // calculated metric
130
+ this.metrics.set(name, opts.calculate)
131
+ return
132
+ }
133
+
134
+ const metric = new DefaultGroupMetric()
135
+ this.metrics.set(name, metric)
136
+
137
+ return metric
138
+ }
139
+
140
+ registerCounterGroup (name: string, opts: CalculatedMetricOptions<Record<string, number>>): void
141
+ registerCounterGroup (name: string, opts?: MetricOptions): MetricGroup
142
+ registerCounterGroup (name: string, opts: any): any {
143
+ if (name == null ?? name.trim() === '') {
144
+ throw new Error('Metric name is required')
145
+ }
146
+
147
+ if (opts?.calculate != null) {
148
+ // calculated metric
149
+ this.metrics.set(name, opts.calculate)
150
+ return
151
+ }
152
+
153
+ const metric = new DefaultGroupMetric()
154
+ this.metrics.set(name, metric)
155
+
156
+ return metric
157
+ }
158
+ }
159
+
160
+ export function mockMetrics (): () => Metrics {
161
+ return () => new MockMetrics()
162
+ }
@@ -0,0 +1,76 @@
1
+ import { multiaddr } from '@multiformats/multiaddr'
2
+ import { abortableSource } from 'abortable-iterator'
3
+ import { duplexPair } from 'it-pair/duplex'
4
+ import type { MultiaddrConnection } from '@libp2p/interface/connection'
5
+ import type { PeerId } from '@libp2p/interface/peer-id'
6
+ import type { Multiaddr } from '@multiformats/multiaddr'
7
+ import type { Duplex } from 'it-stream-types'
8
+
9
+ export function mockMultiaddrConnection (source: Duplex<AsyncGenerator<Uint8Array>> & Partial<MultiaddrConnection>, peerId: PeerId): MultiaddrConnection {
10
+ const maConn: MultiaddrConnection = {
11
+ async close () {
12
+
13
+ },
14
+ abort: () => {},
15
+ timeline: {
16
+ open: Date.now()
17
+ },
18
+ remoteAddr: multiaddr(`/ip4/127.0.0.1/tcp/4001/p2p/${peerId.toString()}`),
19
+ ...source
20
+ }
21
+
22
+ return maConn
23
+ }
24
+
25
+ export interface MockMultiaddrConnPairOptions {
26
+ addrs: Multiaddr[]
27
+ remotePeer: PeerId
28
+ }
29
+
30
+ /**
31
+ * Returns both sides of a mocked MultiaddrConnection
32
+ */
33
+ export function mockMultiaddrConnPair (opts: MockMultiaddrConnPairOptions): { inbound: MultiaddrConnection, outbound: MultiaddrConnection } {
34
+ const { addrs, remotePeer } = opts
35
+ const controller = new AbortController()
36
+ const [localAddr, remoteAddr] = addrs
37
+ const [inboundStream, outboundStream] = duplexPair<Uint8Array>()
38
+
39
+ const outbound: MultiaddrConnection = {
40
+ ...outboundStream,
41
+ remoteAddr: remoteAddr.toString().includes(`/p2p/${remotePeer.toString()}`) ? remoteAddr : remoteAddr.encapsulate(`/p2p/${remotePeer.toString()}`),
42
+ timeline: {
43
+ open: Date.now()
44
+ },
45
+ close: async () => {
46
+ outbound.timeline.close = Date.now()
47
+ controller.abort()
48
+ },
49
+ abort: (err: Error) => {
50
+ outbound.timeline.close = Date.now()
51
+ controller.abort(err)
52
+ }
53
+ }
54
+
55
+ const inbound: MultiaddrConnection = {
56
+ ...inboundStream,
57
+ remoteAddr: localAddr,
58
+ timeline: {
59
+ open: Date.now()
60
+ },
61
+ close: async () => {
62
+ inbound.timeline.close = Date.now()
63
+ controller.abort()
64
+ },
65
+ abort: (err: Error) => {
66
+ outbound.timeline.close = Date.now()
67
+ controller.abort(err)
68
+ }
69
+ }
70
+
71
+ // Make the sources abortable so we can close them easily
72
+ inbound.source = abortableSource(inbound.source, controller.signal)
73
+ outbound.source = abortableSource(outbound.source, controller.signal)
74
+
75
+ return { inbound, outbound }
76
+ }
@@ -0,0 +1,303 @@
1
+ import { AbstractStream, type AbstractStreamInit } from '@libp2p/interface/stream-muxer/stream'
2
+ import { type Logger, logger } from '@libp2p/logger'
3
+ import { abortableSource } from 'abortable-iterator'
4
+ import map from 'it-map'
5
+ import * as ndjson from 'it-ndjson'
6
+ import { pipe } from 'it-pipe'
7
+ import { type Pushable, pushable } from 'it-pushable'
8
+ import { Uint8ArrayList } from 'uint8arraylist'
9
+ import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string'
10
+ import { toString as uint8ArrayToString } from 'uint8arrays/to-string'
11
+ import type { AbortOptions } from '@libp2p/interface'
12
+ import type { Direction, Stream } from '@libp2p/interface/connection'
13
+ import type { StreamMuxer, StreamMuxerFactory, StreamMuxerInit } from '@libp2p/interface/stream-muxer'
14
+ import type { Source } from 'it-stream-types'
15
+
16
+ let muxers = 0
17
+ let streams = 0
18
+
19
+ interface DataMessage {
20
+ id: string
21
+ type: 'data'
22
+ direction: Direction
23
+ chunk: string
24
+ }
25
+
26
+ interface ResetMessage {
27
+ id: string
28
+ type: 'reset'
29
+ direction: Direction
30
+ }
31
+
32
+ interface CloseMessage {
33
+ id: string
34
+ type: 'close'
35
+ direction: Direction
36
+ }
37
+
38
+ interface CreateMessage {
39
+ id: string
40
+ type: 'create'
41
+ direction: 'outbound'
42
+ }
43
+
44
+ type StreamMessage = DataMessage | ResetMessage | CloseMessage | CreateMessage
45
+
46
+ export interface MockMuxedStreamInit extends AbstractStreamInit {
47
+ push: Pushable<StreamMessage>
48
+ }
49
+
50
+ class MuxedStream extends AbstractStream {
51
+ private readonly push: Pushable<StreamMessage>
52
+
53
+ constructor (init: MockMuxedStreamInit) {
54
+ super(init)
55
+
56
+ this.push = init.push
57
+ }
58
+
59
+ sendNewStream (): void {
60
+ // If initiator, open a new stream
61
+ const createMsg: CreateMessage = {
62
+ id: this.id,
63
+ type: 'create',
64
+ direction: 'outbound'
65
+ }
66
+ this.push.push(createMsg)
67
+ }
68
+
69
+ sendData (data: Uint8ArrayList): void {
70
+ const dataMsg: DataMessage = {
71
+ id: this.id,
72
+ type: 'data',
73
+ chunk: uint8ArrayToString(data.subarray(), 'base64pad'),
74
+ direction: this.direction
75
+ }
76
+ this.push.push(dataMsg)
77
+ }
78
+
79
+ sendReset (): void {
80
+ const resetMsg: ResetMessage = {
81
+ id: this.id,
82
+ type: 'reset',
83
+ direction: this.direction
84
+ }
85
+ this.push.push(resetMsg)
86
+ }
87
+
88
+ sendCloseWrite (): void {
89
+ const closeMsg: CloseMessage = {
90
+ id: this.id,
91
+ type: 'close',
92
+ direction: this.direction
93
+ }
94
+ this.push.push(closeMsg)
95
+ }
96
+
97
+ sendCloseRead (): void {
98
+ // does not support close read, only close write
99
+ }
100
+ }
101
+
102
+ class MockMuxer implements StreamMuxer {
103
+ public source: AsyncGenerator<Uint8Array>
104
+ public input: Pushable<Uint8Array>
105
+ public streamInput: Pushable<StreamMessage>
106
+ public name: string
107
+ public protocol: string = '/mock-muxer/1.0.0'
108
+
109
+ private readonly closeController: AbortController
110
+ private readonly registryInitiatorStreams: Map<string, MuxedStream>
111
+ private readonly registryRecipientStreams: Map<string, MuxedStream>
112
+ private readonly options: StreamMuxerInit
113
+
114
+ private readonly log: Logger
115
+
116
+ constructor (init?: StreamMuxerInit) {
117
+ this.name = `muxer:${muxers++}`
118
+ this.log = logger(`libp2p:mock-muxer:${this.name}`)
119
+ this.registryInitiatorStreams = new Map()
120
+ this.registryRecipientStreams = new Map()
121
+ this.log('create muxer')
122
+ this.options = init ?? { direction: 'inbound' }
123
+ this.closeController = new AbortController()
124
+ // receives data from the muxer at the other end of the stream
125
+ this.source = this.input = pushable({
126
+ onEnd: () => {
127
+ for (const stream of this.registryInitiatorStreams.values()) {
128
+ stream.destroy()
129
+ }
130
+
131
+ for (const stream of this.registryRecipientStreams.values()) {
132
+ stream.destroy()
133
+ }
134
+ }
135
+ })
136
+
137
+ // receives messages from all of the muxed streams
138
+ this.streamInput = pushable<StreamMessage>({
139
+ objectMode: true
140
+ })
141
+ }
142
+
143
+ // receive incoming messages
144
+ async sink (source: Source<Uint8ArrayList | Uint8Array>): Promise<void> {
145
+ try {
146
+ await pipe(
147
+ abortableSource(source, this.closeController.signal),
148
+ (source) => map(source, buf => uint8ArrayToString(buf.subarray())),
149
+ ndjson.parse<StreamMessage>,
150
+ async (source) => {
151
+ for await (const message of source) {
152
+ this.log.trace('-> %s %s %s', message.type, message.direction, message.id)
153
+ this.handleMessage(message)
154
+ }
155
+ }
156
+ )
157
+
158
+ this.log('muxed stream ended')
159
+ this.input.end()
160
+ } catch (err: any) {
161
+ this.log('muxed stream errored', err)
162
+ this.input.end(err)
163
+ }
164
+ }
165
+
166
+ handleMessage (message: StreamMessage): void {
167
+ let muxedStream: MuxedStream | undefined
168
+
169
+ const registry = message.direction === 'outbound' ? this.registryRecipientStreams : this.registryInitiatorStreams
170
+
171
+ if (message.type === 'create') {
172
+ if (registry.has(message.id)) {
173
+ throw new Error(`Already had stream for ${message.id}`)
174
+ }
175
+
176
+ muxedStream = this.createStream(message.id, 'inbound')
177
+ registry.set(muxedStream.id, muxedStream)
178
+
179
+ if (this.options.onIncomingStream != null) {
180
+ this.options.onIncomingStream(muxedStream)
181
+ }
182
+ }
183
+
184
+ muxedStream = registry.get(message.id)
185
+
186
+ if (muxedStream == null) {
187
+ this.log.error(`No stream found for ${message.id}`)
188
+
189
+ return
190
+ }
191
+
192
+ if (message.type === 'data') {
193
+ muxedStream.sourcePush(new Uint8ArrayList(uint8ArrayFromString(message.chunk, 'base64pad')))
194
+ } else if (message.type === 'reset') {
195
+ this.log('-> reset stream %s %s', muxedStream.direction, muxedStream.id)
196
+ muxedStream.reset()
197
+ } else if (message.type === 'close') {
198
+ this.log('-> closing stream %s %s', muxedStream.direction, muxedStream.id)
199
+ muxedStream.remoteCloseWrite()
200
+ }
201
+ }
202
+
203
+ get streams (): Stream[] {
204
+ return Array.from(this.registryRecipientStreams.values())
205
+ .concat(Array.from(this.registryInitiatorStreams.values()))
206
+ }
207
+
208
+ newStream (name?: string): Stream {
209
+ if (this.closeController.signal.aborted) {
210
+ throw new Error('Muxer already closed')
211
+ }
212
+ this.log('newStream %s', name)
213
+ const storedStream = this.createStream(name, 'outbound')
214
+ this.registryInitiatorStreams.set(storedStream.id, storedStream)
215
+
216
+ return storedStream
217
+ }
218
+
219
+ createStream (name?: string, direction: Direction = 'outbound'): MuxedStream {
220
+ const id = name ?? `${streams++}`
221
+
222
+ this.log('createStream %s %s', direction, id)
223
+
224
+ const muxedStream: MuxedStream = new MuxedStream({
225
+ id,
226
+ direction,
227
+ push: this.streamInput,
228
+ onEnd: () => {
229
+ this.log('stream ended')
230
+
231
+ if (direction === 'outbound') {
232
+ this.registryInitiatorStreams.delete(muxedStream.id)
233
+ } else {
234
+ this.registryRecipientStreams.delete(muxedStream.id)
235
+ }
236
+
237
+ if (this.options.onStreamEnd != null) {
238
+ this.options.onStreamEnd(muxedStream)
239
+ }
240
+ },
241
+ log: logger(`libp2p:mock-muxer:stream:${direction}:${id}`)
242
+ })
243
+
244
+ return muxedStream
245
+ }
246
+
247
+ async close (options?: AbortOptions): Promise<void> {
248
+ if (this.closeController.signal.aborted) {
249
+ return
250
+ }
251
+
252
+ this.log('closing muxed streams')
253
+
254
+ await Promise.all(
255
+ this.streams.map(async s => s.close())
256
+ )
257
+
258
+ this.closeController.abort()
259
+ this.input.end()
260
+ }
261
+
262
+ abort (err: Error): void {
263
+ if (this.closeController.signal.aborted) {
264
+ return
265
+ }
266
+
267
+ this.log('aborting muxed streams')
268
+
269
+ this.streams.forEach(s => {
270
+ s.abort(err)
271
+ })
272
+
273
+ this.closeController.abort(err)
274
+ this.input.end(err)
275
+ }
276
+ }
277
+
278
+ class MockMuxerFactory implements StreamMuxerFactory {
279
+ public protocol: string = '/mock-muxer/1.0.0'
280
+
281
+ createStreamMuxer (init?: StreamMuxerInit): StreamMuxer {
282
+ const mockMuxer = new MockMuxer(init)
283
+
284
+ void Promise.resolve().then(async () => {
285
+ void pipe(
286
+ mockMuxer.streamInput,
287
+ ndjson.stringify,
288
+ (source) => map(source, str => new Uint8ArrayList(uint8ArrayFromString(str))),
289
+ async (source) => {
290
+ for await (const buf of source) {
291
+ mockMuxer.input.push(buf.subarray())
292
+ }
293
+ }
294
+ )
295
+ })
296
+
297
+ return mockMuxer
298
+ }
299
+ }
300
+
301
+ export function mockMuxer (): MockMuxerFactory {
302
+ return new MockMuxerFactory()
303
+ }
@@ -0,0 +1,60 @@
1
+ import { EventEmitter } from '@libp2p/interface/events'
2
+ import { peerDiscovery } from '@libp2p/interface/peer-discovery'
3
+ import * as PeerIdFactory from '@libp2p/peer-id-factory'
4
+ import { multiaddr } from '@multiformats/multiaddr'
5
+ import type { PeerDiscovery, PeerDiscoveryEvents } from '@libp2p/interface/peer-discovery'
6
+ import type { PeerInfo } from '@libp2p/interface/peer-info'
7
+
8
+ interface MockDiscoveryInit {
9
+ discoveryDelay?: number
10
+ }
11
+
12
+ /**
13
+ * Emits 'peer' events on discovery.
14
+ */
15
+ export class MockDiscovery extends EventEmitter<PeerDiscoveryEvents> implements PeerDiscovery {
16
+ public readonly options: MockDiscoveryInit
17
+ private _isRunning: boolean
18
+ private _timer: any
19
+
20
+ constructor (init = {}) {
21
+ super()
22
+
23
+ this.options = init
24
+ this._isRunning = false
25
+ }
26
+
27
+ readonly [peerDiscovery] = this
28
+
29
+ start (): void {
30
+ this._isRunning = true
31
+ this._discoverPeer()
32
+ }
33
+
34
+ stop (): void {
35
+ clearTimeout(this._timer)
36
+ this._isRunning = false
37
+ }
38
+
39
+ isStarted (): boolean {
40
+ return this._isRunning
41
+ }
42
+
43
+ _discoverPeer (): void {
44
+ if (!this._isRunning) return
45
+
46
+ PeerIdFactory.createEd25519PeerId()
47
+ .then(peerId => {
48
+ this._timer = setTimeout(() => {
49
+ this.safeDispatchEvent<PeerInfo>('peer', {
50
+ detail: {
51
+ id: peerId,
52
+ multiaddrs: [multiaddr('/ip4/127.0.0.1/tcp/8000')],
53
+ protocols: []
54
+ }
55
+ })
56
+ }, this.options.discoveryDelay ?? 1000)
57
+ })
58
+ .catch(() => {})
59
+ }
60
+ }
@@ -0,0 +1,88 @@
1
+ import merge from 'merge-options'
2
+ import type { Connection } from '@libp2p/interface/connection'
3
+ import type { PeerId } from '@libp2p/interface/peer-id'
4
+ import type { Topology } from '@libp2p/interface/topology'
5
+ import type { IncomingStreamData, Registrar, StreamHandler, StreamHandlerOptions, StreamHandlerRecord } from '@libp2p/interface-internal/registrar'
6
+
7
+ export class MockRegistrar implements Registrar {
8
+ private readonly topologies = new Map<string, Array<{ id: string, topology: Topology }>>()
9
+ private readonly handlers = new Map<string, StreamHandlerRecord>()
10
+
11
+ getProtocols (): string[] {
12
+ return Array.from(this.handlers.keys()).sort()
13
+ }
14
+
15
+ async handle (protocol: string, handler: StreamHandler, opts?: StreamHandlerOptions): Promise<void> {
16
+ const options = merge.bind({ ignoreUndefined: true })({
17
+ maxInboundStreams: 1,
18
+ maxOutboundStreams: 1
19
+ }, opts)
20
+
21
+ if (this.handlers.has(protocol)) {
22
+ throw new Error(`Handler already registered for protocol ${protocol}`)
23
+ }
24
+
25
+ this.handlers.set(protocol, {
26
+ handler,
27
+ options
28
+ })
29
+ }
30
+
31
+ async unhandle (protocol: string): Promise<void> {
32
+ this.handlers.delete(protocol)
33
+ }
34
+
35
+ getHandler (protocol: string): StreamHandlerRecord {
36
+ const handler = this.handlers.get(protocol)
37
+
38
+ if (handler == null) {
39
+ throw new Error(`No handler registered for protocol ${protocol}`)
40
+ }
41
+
42
+ return handler
43
+ }
44
+
45
+ async register (protocol: string, topology: Topology): Promise<string> {
46
+ const id = `topology-id-${Math.random()}`
47
+ let topologies = this.topologies.get(protocol)
48
+
49
+ if (topologies == null) {
50
+ topologies = []
51
+ }
52
+
53
+ topologies.push({
54
+ id,
55
+ topology
56
+ })
57
+
58
+ this.topologies.set(protocol, topologies)
59
+
60
+ return id
61
+ }
62
+
63
+ unregister (id: string | string[]): void {
64
+ if (!Array.isArray(id)) {
65
+ id = [id]
66
+ }
67
+
68
+ id.forEach(id => this.topologies.delete(id))
69
+ }
70
+
71
+ getTopologies (protocol: string): Topology[] {
72
+ return (this.topologies.get(protocol) ?? []).map(t => t.topology)
73
+ }
74
+ }
75
+
76
+ export function mockRegistrar (): Registrar {
77
+ return new MockRegistrar()
78
+ }
79
+
80
+ export async function mockIncomingStreamEvent (protocol: string, conn: Connection, remotePeer: PeerId): Promise<IncomingStreamData> {
81
+ return {
82
+ ...await conn.newStream([protocol]),
83
+ // @ts-expect-error incomplete implementation
84
+ connection: {
85
+ remotePeer
86
+ }
87
+ }
88
+ }