@optimystic/db-p2p 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (189) hide show
  1. package/dist/index.min.js +52 -0
  2. package/dist/index.min.js.map +7 -0
  3. package/dist/src/cluster/client.d.ts +12 -0
  4. package/dist/src/cluster/client.d.ts.map +1 -0
  5. package/dist/src/cluster/client.js +65 -0
  6. package/dist/src/cluster/client.js.map +1 -0
  7. package/dist/src/cluster/cluster-repo.d.ts +79 -0
  8. package/dist/src/cluster/cluster-repo.d.ts.map +1 -0
  9. package/dist/src/cluster/cluster-repo.js +613 -0
  10. package/dist/src/cluster/cluster-repo.js.map +1 -0
  11. package/dist/src/cluster/partition-detector.d.ts +59 -0
  12. package/dist/src/cluster/partition-detector.d.ts.map +1 -0
  13. package/dist/src/cluster/partition-detector.js +129 -0
  14. package/dist/src/cluster/partition-detector.js.map +1 -0
  15. package/dist/src/cluster/service.d.ts +49 -0
  16. package/dist/src/cluster/service.d.ts.map +1 -0
  17. package/dist/src/cluster/service.js +107 -0
  18. package/dist/src/cluster/service.js.map +1 -0
  19. package/dist/src/index.d.ts +29 -0
  20. package/dist/src/index.d.ts.map +1 -0
  21. package/dist/src/index.js +29 -0
  22. package/dist/src/index.js.map +1 -0
  23. package/dist/src/it-utility.d.ts +4 -0
  24. package/dist/src/it-utility.d.ts.map +1 -0
  25. package/dist/src/it-utility.js +32 -0
  26. package/dist/src/it-utility.js.map +1 -0
  27. package/dist/src/libp2p-key-network.d.ts +59 -0
  28. package/dist/src/libp2p-key-network.d.ts.map +1 -0
  29. package/dist/src/libp2p-key-network.js +278 -0
  30. package/dist/src/libp2p-key-network.js.map +1 -0
  31. package/dist/src/libp2p-node.d.ts +28 -0
  32. package/dist/src/libp2p-node.d.ts.map +1 -0
  33. package/dist/src/libp2p-node.js +270 -0
  34. package/dist/src/libp2p-node.js.map +1 -0
  35. package/dist/src/logger.d.ts +3 -0
  36. package/dist/src/logger.d.ts.map +1 -0
  37. package/dist/src/logger.js +6 -0
  38. package/dist/src/logger.js.map +1 -0
  39. package/dist/src/network/get-network-manager.d.ts +4 -0
  40. package/dist/src/network/get-network-manager.d.ts.map +1 -0
  41. package/dist/src/network/get-network-manager.js +17 -0
  42. package/dist/src/network/get-network-manager.js.map +1 -0
  43. package/dist/src/network/network-manager-service.d.ts +82 -0
  44. package/dist/src/network/network-manager-service.d.ts.map +1 -0
  45. package/dist/src/network/network-manager-service.js +283 -0
  46. package/dist/src/network/network-manager-service.js.map +1 -0
  47. package/dist/src/peer-utils.d.ts +2 -0
  48. package/dist/src/peer-utils.d.ts.map +1 -0
  49. package/dist/src/peer-utils.js +28 -0
  50. package/dist/src/peer-utils.js.map +1 -0
  51. package/dist/src/protocol-client.d.ts +12 -0
  52. package/dist/src/protocol-client.d.ts.map +1 -0
  53. package/dist/src/protocol-client.js +34 -0
  54. package/dist/src/protocol-client.js.map +1 -0
  55. package/dist/src/repo/client.d.ts +17 -0
  56. package/dist/src/repo/client.d.ts.map +1 -0
  57. package/dist/src/repo/client.js +82 -0
  58. package/dist/src/repo/client.js.map +1 -0
  59. package/dist/src/repo/cluster-coordinator.d.ts +59 -0
  60. package/dist/src/repo/cluster-coordinator.d.ts.map +1 -0
  61. package/dist/src/repo/cluster-coordinator.js +539 -0
  62. package/dist/src/repo/cluster-coordinator.js.map +1 -0
  63. package/dist/src/repo/coordinator-repo.d.ts +29 -0
  64. package/dist/src/repo/coordinator-repo.d.ts.map +1 -0
  65. package/dist/src/repo/coordinator-repo.js +102 -0
  66. package/dist/src/repo/coordinator-repo.js.map +1 -0
  67. package/dist/src/repo/redirect.d.ts +14 -0
  68. package/dist/src/repo/redirect.d.ts.map +1 -0
  69. package/dist/src/repo/redirect.js +9 -0
  70. package/dist/src/repo/redirect.js.map +1 -0
  71. package/dist/src/repo/service.d.ts +52 -0
  72. package/dist/src/repo/service.d.ts.map +1 -0
  73. package/dist/src/repo/service.js +181 -0
  74. package/dist/src/repo/service.js.map +1 -0
  75. package/dist/src/repo/types.d.ts +7 -0
  76. package/dist/src/repo/types.d.ts.map +1 -0
  77. package/dist/src/repo/types.js +2 -0
  78. package/dist/src/repo/types.js.map +1 -0
  79. package/dist/src/routing/libp2p-known-peers.d.ts +4 -0
  80. package/dist/src/routing/libp2p-known-peers.d.ts.map +1 -0
  81. package/dist/src/routing/libp2p-known-peers.js +19 -0
  82. package/dist/src/routing/libp2p-known-peers.js.map +1 -0
  83. package/dist/src/routing/responsibility.d.ts +14 -0
  84. package/dist/src/routing/responsibility.d.ts.map +1 -0
  85. package/dist/src/routing/responsibility.js +45 -0
  86. package/dist/src/routing/responsibility.js.map +1 -0
  87. package/dist/src/routing/simple-cluster-coordinator.d.ts +23 -0
  88. package/dist/src/routing/simple-cluster-coordinator.d.ts.map +1 -0
  89. package/dist/src/routing/simple-cluster-coordinator.js +59 -0
  90. package/dist/src/routing/simple-cluster-coordinator.js.map +1 -0
  91. package/dist/src/storage/arachnode-fret-adapter.d.ts +65 -0
  92. package/dist/src/storage/arachnode-fret-adapter.d.ts.map +1 -0
  93. package/dist/src/storage/arachnode-fret-adapter.js +93 -0
  94. package/dist/src/storage/arachnode-fret-adapter.js.map +1 -0
  95. package/dist/src/storage/block-storage.d.ts +31 -0
  96. package/dist/src/storage/block-storage.d.ts.map +1 -0
  97. package/dist/src/storage/block-storage.js +154 -0
  98. package/dist/src/storage/block-storage.js.map +1 -0
  99. package/dist/src/storage/file-storage.d.ts +30 -0
  100. package/dist/src/storage/file-storage.d.ts.map +1 -0
  101. package/dist/src/storage/file-storage.js +127 -0
  102. package/dist/src/storage/file-storage.js.map +1 -0
  103. package/dist/src/storage/helpers.d.ts +3 -0
  104. package/dist/src/storage/helpers.d.ts.map +1 -0
  105. package/dist/src/storage/helpers.js +28 -0
  106. package/dist/src/storage/helpers.js.map +1 -0
  107. package/dist/src/storage/i-block-storage.d.ts +32 -0
  108. package/dist/src/storage/i-block-storage.d.ts.map +1 -0
  109. package/dist/src/storage/i-block-storage.js +2 -0
  110. package/dist/src/storage/i-block-storage.js.map +1 -0
  111. package/dist/src/storage/i-raw-storage.d.ts +20 -0
  112. package/dist/src/storage/i-raw-storage.d.ts.map +1 -0
  113. package/dist/src/storage/i-raw-storage.js +2 -0
  114. package/dist/src/storage/i-raw-storage.js.map +1 -0
  115. package/dist/src/storage/memory-storage.d.ts +27 -0
  116. package/dist/src/storage/memory-storage.d.ts.map +1 -0
  117. package/dist/src/storage/memory-storage.js +87 -0
  118. package/dist/src/storage/memory-storage.js.map +1 -0
  119. package/dist/src/storage/restoration-coordinator-v2.d.ts +63 -0
  120. package/dist/src/storage/restoration-coordinator-v2.d.ts.map +1 -0
  121. package/dist/src/storage/restoration-coordinator-v2.js +157 -0
  122. package/dist/src/storage/restoration-coordinator-v2.js.map +1 -0
  123. package/dist/src/storage/ring-selector.d.ts +56 -0
  124. package/dist/src/storage/ring-selector.d.ts.map +1 -0
  125. package/dist/src/storage/ring-selector.js +118 -0
  126. package/dist/src/storage/ring-selector.js.map +1 -0
  127. package/dist/src/storage/storage-monitor.d.ts +23 -0
  128. package/dist/src/storage/storage-monitor.d.ts.map +1 -0
  129. package/dist/src/storage/storage-monitor.js +40 -0
  130. package/dist/src/storage/storage-monitor.js.map +1 -0
  131. package/dist/src/storage/storage-repo.d.ts +17 -0
  132. package/dist/src/storage/storage-repo.d.ts.map +1 -0
  133. package/dist/src/storage/storage-repo.js +267 -0
  134. package/dist/src/storage/storage-repo.js.map +1 -0
  135. package/dist/src/storage/struct.d.ts +29 -0
  136. package/dist/src/storage/struct.d.ts.map +1 -0
  137. package/dist/src/storage/struct.js +2 -0
  138. package/dist/src/storage/struct.js.map +1 -0
  139. package/dist/src/sync/client.d.ts +27 -0
  140. package/dist/src/sync/client.d.ts.map +1 -0
  141. package/dist/src/sync/client.js +32 -0
  142. package/dist/src/sync/client.js.map +1 -0
  143. package/dist/src/sync/protocol.d.ts +58 -0
  144. package/dist/src/sync/protocol.d.ts.map +1 -0
  145. package/dist/src/sync/protocol.js +12 -0
  146. package/dist/src/sync/protocol.js.map +1 -0
  147. package/dist/src/sync/service.d.ts +62 -0
  148. package/dist/src/sync/service.d.ts.map +1 -0
  149. package/dist/src/sync/service.js +168 -0
  150. package/dist/src/sync/service.js.map +1 -0
  151. package/package.json +73 -0
  152. package/readme.md +497 -0
  153. package/src/cluster/client.ts +63 -0
  154. package/src/cluster/cluster-repo.ts +711 -0
  155. package/src/cluster/partition-detector.ts +158 -0
  156. package/src/cluster/service.ts +156 -0
  157. package/src/index.ts +30 -0
  158. package/src/it-utility.ts +36 -0
  159. package/src/libp2p-key-network.ts +334 -0
  160. package/src/libp2p-node.ts +335 -0
  161. package/src/logger.ts +9 -0
  162. package/src/network/get-network-manager.ts +17 -0
  163. package/src/network/network-manager-service.ts +334 -0
  164. package/src/peer-utils.ts +24 -0
  165. package/src/protocol-client.ts +54 -0
  166. package/src/repo/client.ts +112 -0
  167. package/src/repo/cluster-coordinator.ts +592 -0
  168. package/src/repo/coordinator-repo.ts +137 -0
  169. package/src/repo/redirect.ts +17 -0
  170. package/src/repo/service.ts +219 -0
  171. package/src/repo/types.ts +7 -0
  172. package/src/routing/libp2p-known-peers.ts +26 -0
  173. package/src/routing/responsibility.ts +63 -0
  174. package/src/routing/simple-cluster-coordinator.ts +70 -0
  175. package/src/storage/arachnode-fret-adapter.ts +128 -0
  176. package/src/storage/block-storage.ts +182 -0
  177. package/src/storage/file-storage.ts +163 -0
  178. package/src/storage/helpers.ts +29 -0
  179. package/src/storage/i-block-storage.ts +40 -0
  180. package/src/storage/i-raw-storage.ts +30 -0
  181. package/src/storage/memory-storage.ts +108 -0
  182. package/src/storage/restoration-coordinator-v2.ts +191 -0
  183. package/src/storage/ring-selector.ts +155 -0
  184. package/src/storage/storage-monitor.ts +59 -0
  185. package/src/storage/storage-repo.ts +320 -0
  186. package/src/storage/struct.ts +34 -0
  187. package/src/sync/client.ts +42 -0
  188. package/src/sync/protocol.ts +71 -0
  189. package/src/sync/service.ts +229 -0
@@ -0,0 +1,158 @@
1
+ export interface PeerStatus {
2
+ peerId: string;
3
+ lastSeen: number;
4
+ lastGoodbye?: number;
5
+ consecutiveFailures: number;
6
+ }
7
+
8
+ /**
9
+ * Detects potential network partitions by tracking peer health,
10
+ * goodbye messages, and sudden mass unreachability events.
11
+ */
12
+ export class PartitionDetector {
13
+ private peerStatuses: Map<string, PeerStatus> = new Map();
14
+ private readonly unreachableThreshold = 3; // consecutive failures
15
+ private readonly rapidChurnThreshold = 5; // peers
16
+ private readonly rapidChurnWindow = 10000; // 10 seconds
17
+ private readonly peerTimeoutMs = 60000; // 1 minute
18
+
19
+ /**
20
+ * Record successful communication with a peer
21
+ */
22
+ recordSuccess(peerId: string): void {
23
+ const now = Date.now();
24
+ const status = this.peerStatuses.get(peerId);
25
+
26
+ if (status) {
27
+ status.lastSeen = now;
28
+ status.consecutiveFailures = 0;
29
+ } else {
30
+ this.peerStatuses.set(peerId, {
31
+ peerId,
32
+ lastSeen: now,
33
+ consecutiveFailures: 0
34
+ });
35
+ }
36
+
37
+ // Clean up old peer records
38
+ this.cleanupOldPeers();
39
+ }
40
+
41
+ /**
42
+ * Record failed communication attempt with a peer
43
+ */
44
+ recordFailure(peerId: string): void {
45
+ const now = Date.now();
46
+ const status = this.peerStatuses.get(peerId);
47
+
48
+ if (status) {
49
+ status.consecutiveFailures++;
50
+ status.lastSeen = now;
51
+ } else {
52
+ this.peerStatuses.set(peerId, {
53
+ peerId,
54
+ lastSeen: now,
55
+ consecutiveFailures: 1
56
+ });
57
+ }
58
+ }
59
+
60
+ /**
61
+ * Record explicit goodbye message from a peer
62
+ */
63
+ recordGoodbye(peerId: string): void {
64
+ const now = Date.now();
65
+ const status = this.peerStatuses.get(peerId);
66
+
67
+ if (status) {
68
+ status.lastGoodbye = now;
69
+ status.lastSeen = now;
70
+ } else {
71
+ this.peerStatuses.set(peerId, {
72
+ peerId,
73
+ lastSeen: now,
74
+ lastGoodbye: now,
75
+ consecutiveFailures: 0
76
+ });
77
+ }
78
+ }
79
+
80
+ /**
81
+ * Detect if we're likely in a network partition
82
+ * Returns true if sudden mass unreachability or rapid goodbye rate
83
+ */
84
+ detectPartition(): boolean {
85
+ const now = Date.now();
86
+
87
+ // Count recent goodbyes
88
+ const recentGoodbyes = this.getRecentGoodbyes(this.rapidChurnWindow);
89
+
90
+ // Count unreachable peers
91
+ const unreachable = Array.from(this.peerStatuses.values()).filter(
92
+ s => s.consecutiveFailures >= this.unreachableThreshold
93
+ && !s.lastGoodbye // Exclude peers that said goodbye
94
+ );
95
+
96
+ // Sudden mass unreachability suggests partition
97
+ const totalChurn = recentGoodbyes.length + unreachable.length;
98
+
99
+ return totalChurn >= this.rapidChurnThreshold;
100
+ }
101
+
102
+ /**
103
+ * Get list of currently unreachable peers
104
+ */
105
+ getUnreachablePeers(): string[] {
106
+ return Array.from(this.peerStatuses.values())
107
+ .filter(s => s.consecutiveFailures >= this.unreachableThreshold && !s.lastGoodbye)
108
+ .map(s => s.peerId);
109
+ }
110
+
111
+ /**
112
+ * Get recent goodbye messages within the specified window
113
+ */
114
+ private getRecentGoodbyes(windowMs: number): PeerStatus[] {
115
+ const cutoff = Date.now() - windowMs;
116
+ return Array.from(this.peerStatuses.values()).filter(
117
+ s => s.lastGoodbye && s.lastGoodbye > cutoff
118
+ );
119
+ }
120
+
121
+ /**
122
+ * Clean up peer records that haven't been seen recently
123
+ */
124
+ private cleanupOldPeers(): void {
125
+ const cutoff = Date.now() - this.peerTimeoutMs;
126
+ for (const [peerId, status] of this.peerStatuses.entries()) {
127
+ if (status.lastSeen < cutoff) {
128
+ this.peerStatuses.delete(peerId);
129
+ }
130
+ }
131
+ }
132
+
133
+ /**
134
+ * Get statistics for monitoring
135
+ */
136
+ getStatistics(): {
137
+ totalPeers: number;
138
+ unreachable: number;
139
+ recentGoodbyes: number;
140
+ } {
141
+ const unreachable = this.getUnreachablePeers().length;
142
+ const recentGoodbyes = this.getRecentGoodbyes(this.rapidChurnWindow).length;
143
+
144
+ return {
145
+ totalPeers: this.peerStatuses.size,
146
+ unreachable,
147
+ recentGoodbyes
148
+ };
149
+ }
150
+
151
+ /**
152
+ * Reset all tracked peer states (useful for testing)
153
+ */
154
+ reset(): void {
155
+ this.peerStatuses.clear();
156
+ }
157
+ }
158
+
@@ -0,0 +1,156 @@
1
+ import { pipe } from 'it-pipe';
2
+ import { decode as lpDecode, encode as lpEncode } from 'it-length-prefixed';
3
+ import type { Startable, Logger, IncomingStreamData } from '@libp2p/interface';
4
+ import type { ICluster, ClusterRecord } from '@optimystic/db-core';
5
+ import { computeResponsibility } from '../routing/responsibility.js'
6
+ import { peersEqual } from '../peer-utils.js'
7
+ import { buildKnownPeers } from '../routing/libp2p-known-peers.js'
8
+ import { encodePeers } from '../repo/redirect.js'
9
+ import type { Uint8ArrayList } from 'uint8arraylist';
10
+
11
+ interface BaseComponents {
12
+ logger: { forComponent: (name: string) => Logger },
13
+ registrar: {
14
+ handle: (protocol: string, handler: (data: IncomingStreamData) => void, options: any) => Promise<void>,
15
+ unhandle: (protocol: string) => Promise<void>
16
+ }
17
+ }
18
+
19
+ export interface ClusterServiceComponents extends BaseComponents {
20
+ cluster: ICluster
21
+ }
22
+
23
+ export interface ClusterServiceInit {
24
+ protocol?: string,
25
+ protocolPrefix?: string,
26
+ maxInboundStreams?: number,
27
+ maxOutboundStreams?: number,
28
+ logPrefix?: string,
29
+ kBucketSize?: number,
30
+ configuredClusterSize?: number,
31
+ allowClusterDownsize?: boolean,
32
+ clusterSizeTolerance?: number,
33
+ }
34
+
35
+ export function clusterService(init: ClusterServiceInit = {}): (components: ClusterServiceComponents) => ClusterService {
36
+ return (components: ClusterServiceComponents) => new ClusterService(components, init);
37
+ }
38
+
39
+ /**
40
+ * A libp2p service that handles cluster protocol messages
41
+ */
42
+ export class ClusterService implements Startable {
43
+ private readonly protocol: string;
44
+ private readonly maxInboundStreams: number;
45
+ private readonly maxOutboundStreams: number;
46
+ private readonly log: Logger;
47
+ private readonly cluster: ICluster;
48
+ private readonly components: ClusterServiceComponents;
49
+ private running: boolean;
50
+ private readonly k: number;
51
+ private readonly configuredClusterSize: number;
52
+ private readonly allowDownsize: boolean;
53
+ private readonly sizeTolerance: number;
54
+
55
+ constructor(components: ClusterServiceComponents, init: ClusterServiceInit = {}) {
56
+ this.components = components;
57
+ this.protocol = init.protocol ?? (init.protocolPrefix ?? '/db-p2p') + '/cluster/1.0.0';
58
+ this.maxInboundStreams = init.maxInboundStreams ?? 32;
59
+ this.maxOutboundStreams = init.maxOutboundStreams ?? 64;
60
+ this.log = components.logger.forComponent(init.logPrefix ?? 'db-p2p:cluster');
61
+ this.cluster = components.cluster;
62
+ this.running = false;
63
+ this.k = init.kBucketSize ?? 10;
64
+ this.configuredClusterSize = init.configuredClusterSize ?? 10;
65
+ this.allowDownsize = init.allowClusterDownsize ?? true;
66
+ this.sizeTolerance = init.clusterSizeTolerance ?? 0.5;
67
+ }
68
+
69
+ readonly [Symbol.toStringTag] = '@libp2p/cluster';
70
+
71
+ async start(): Promise<void> {
72
+ if (this.running) {
73
+ return;
74
+ }
75
+
76
+ await this.components.registrar.handle(this.protocol, this.handleIncomingStream.bind(this), {
77
+ maxInboundStreams: this.maxInboundStreams,
78
+ maxOutboundStreams: this.maxOutboundStreams
79
+ });
80
+
81
+ this.running = true;
82
+ }
83
+
84
+ async stop(): Promise<void> {
85
+ if (!this.running) {
86
+ return;
87
+ }
88
+
89
+ await this.components.registrar.unhandle(this.protocol);
90
+ this.running = false;
91
+ }
92
+
93
+ private handleIncomingStream(data: IncomingStreamData): void {
94
+ const { stream, connection } = data;
95
+ const peerId = connection.remotePeer;
96
+
97
+ const processStream = async function* (this: ClusterService, source: AsyncIterable<Uint8ArrayList>) {
98
+ for await (const msg of source) {
99
+ // Decode the message
100
+ const decoded = new TextDecoder().decode(msg.subarray());
101
+ const message = JSON.parse(decoded) as { operation: string; record: ClusterRecord };
102
+
103
+ // Process the operation
104
+ let response: any;
105
+ if (message.operation === 'update') {
106
+ // Use message.record.message as key source; this is RepoMessage carrying block IDs
107
+ const tailId = (message.record?.message as any)?.commit?.tailId ?? (message.record?.message as any)?.pend ? Object.keys((message.record as any).message.pend.transforms)[0] : undefined
108
+ // TEMPORARY: Disable cluster membership check to fix empty promises issue
109
+ // The membership check was causing peers to return redirect responses
110
+ // instead of processing cluster updates, leading to empty promise arrays.
111
+ // TODO: Re-enable and fix cluster membership logic for proper DHT routing
112
+ response = await this.cluster.update(message.record)
113
+ } else {
114
+ throw new Error(`Unknown operation: ${message.operation}`);
115
+ }
116
+
117
+ // Encode and yield the response
118
+ if (message.operation === 'update') {
119
+ const rec = response as any;
120
+ this.log('cluster-service:pre-serialize', {
121
+ messageHash: rec?.messageHash,
122
+ responseType: typeof response,
123
+ hasPromises: 'promises' in (rec ?? {}),
124
+ hasCommits: 'commits' in (rec ?? {}),
125
+ promiseKeys: Object.keys(rec?.promises ?? {}),
126
+ commitKeys: Object.keys(rec?.commits ?? {}),
127
+ promiseValues: rec?.promises,
128
+ commitValues: rec?.commits
129
+ });
130
+ }
131
+ const serialized = JSON.stringify(response);
132
+ if (message.operation === 'update') {
133
+ const deserialized = JSON.parse(serialized);
134
+ this.log('cluster-service:post-serialize', {
135
+ messageHash: (deserialized as any)?.messageHash,
136
+ promiseKeys: Object.keys((deserialized as any)?.promises ?? {}),
137
+ commitKeys: Object.keys((deserialized as any)?.commits ?? {})
138
+ });
139
+ }
140
+ yield new TextEncoder().encode(serialized);
141
+ }
142
+ };
143
+
144
+ Promise.resolve().then(async () => {
145
+ await pipe(
146
+ stream,
147
+ (source) => lpDecode(source),
148
+ processStream.bind(this),
149
+ (source) => lpEncode(source),
150
+ stream
151
+ );
152
+ }).catch((err: Error) => {
153
+ this.log.error('error handling cluster protocol message from %p - %e', peerId, err);
154
+ });
155
+ }
156
+ }
package/src/index.ts ADDED
@@ -0,0 +1,30 @@
1
+ export * from "./cluster/client.js";
2
+ export * from "./cluster/cluster-repo.js";
3
+ export * from "./cluster/service.js";
4
+ export * from "./protocol-client.js";
5
+ export * from "./repo/client.js";
6
+ export * from "./repo/cluster-coordinator.js";
7
+ export * from "./repo/coordinator-repo.js";
8
+ export * from "./repo/service.js";
9
+ export * from "./storage/block-storage.js";
10
+ export * from "./storage/file-storage.js";
11
+ export * from "./storage/memory-storage.js";
12
+ export * from "./storage/i-block-storage.js";
13
+ export * from "./storage/i-raw-storage.js";
14
+ export * from "./storage/storage-repo.js";
15
+ export * from "./storage/restoration-coordinator-v2.js";
16
+ export * from "./storage/ring-selector.js";
17
+ export * from "./storage/storage-monitor.js";
18
+ export * from "./storage/arachnode-fret-adapter.js";
19
+ export * from "./sync/protocol.js";
20
+ export * from "./sync/client.js";
21
+ export * from "./sync/service.js";
22
+ export * from "./it-utility.js";
23
+ export * from "./libp2p-key-network.js";
24
+ export * from "./libp2p-node.js";
25
+ export * from "./routing/responsibility.js";
26
+ export * from "./routing/libp2p-known-peers.js";
27
+ export * from "./network/network-manager-service.js";
28
+ export * from "./network/get-network-manager.js";
29
+
30
+
@@ -0,0 +1,36 @@
1
+ export async function first<T>(
2
+ createIterable: (signal: AbortSignal) => AsyncIterable<T>,
3
+ onEmpty: () => T = () => { throw new Error('No items found') },
4
+ timeoutMs?: number
5
+ ): Promise<T> {
6
+ const controller = new AbortController();
7
+ const timer = typeof timeoutMs === 'number' ? setTimeout(() => controller.abort(), timeoutMs) : undefined;
8
+ try {
9
+ for await (const item of createIterable(controller.signal)) {
10
+ return item;
11
+ }
12
+ return onEmpty();
13
+ } finally {
14
+ if (timer !== undefined) clearTimeout(timer);
15
+ controller.abort();
16
+ }
17
+ }
18
+
19
+ export async function asyncIteratorToArray<T>(iterator: AsyncIterable<T>): Promise<T[]> {
20
+ const result: T[] = [];
21
+ for await (const item of iterator) {
22
+ result.push(item);
23
+ }
24
+ return result;
25
+ }
26
+
27
+ export function reduce<TP, TC>(iter: IterableIterator<TC>, each: (prior: TP, current: TC, index: number) => TP, start: TP) {
28
+ let prior = start;
29
+ let i = 0;
30
+ for (let current of iter) {
31
+ prior = each(prior, current, i);
32
+ ++i;
33
+ }
34
+ return prior;
35
+ }
36
+