@serfab/cadre-core 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/README.md +252 -0
  2. package/dist/arachnode-stub.d.ts +60 -0
  3. package/dist/arachnode-stub.js +120 -0
  4. package/dist/arachnode-stub.js.map +1 -0
  5. package/dist/cadre-node.d.ts +252 -0
  6. package/dist/cadre-node.js +753 -0
  7. package/dist/cadre-node.js.map +1 -0
  8. package/dist/control-database.d.ts +62 -0
  9. package/dist/control-database.js +295 -0
  10. package/dist/control-database.js.map +1 -0
  11. package/dist/enrollment.d.ts +76 -0
  12. package/dist/enrollment.js +126 -0
  13. package/dist/enrollment.js.map +1 -0
  14. package/dist/hibernation-manager.d.ts +68 -0
  15. package/dist/hibernation-manager.js +212 -0
  16. package/dist/hibernation-manager.js.map +1 -0
  17. package/dist/index.d.ts +12 -0
  18. package/dist/index.js +24 -0
  19. package/dist/index.js.map +1 -0
  20. package/dist/seed-bootstrap.d.ts +164 -0
  21. package/dist/seed-bootstrap.js +554 -0
  22. package/dist/seed-bootstrap.js.map +1 -0
  23. package/dist/strand-database.d.ts +39 -0
  24. package/dist/strand-database.js +108 -0
  25. package/dist/strand-database.js.map +1 -0
  26. package/dist/strand-formation-manager.d.ts +87 -0
  27. package/dist/strand-formation-manager.js +168 -0
  28. package/dist/strand-formation-manager.js.map +1 -0
  29. package/dist/strand-instance-manager.d.ts +60 -0
  30. package/dist/strand-instance-manager.js +209 -0
  31. package/dist/strand-instance-manager.js.map +1 -0
  32. package/dist/strand-solicitation.d.ts +145 -0
  33. package/dist/strand-solicitation.js +180 -0
  34. package/dist/strand-solicitation.js.map +1 -0
  35. package/dist/strand-watcher.d.ts +70 -0
  36. package/dist/strand-watcher.js +136 -0
  37. package/dist/strand-watcher.js.map +1 -0
  38. package/dist/types.d.ts +484 -0
  39. package/dist/types.js +26 -0
  40. package/dist/types.js.map +1 -0
  41. package/package.json +67 -0
  42. package/src/arachnode-stub.ts +160 -0
  43. package/src/cadre-node.ts +923 -0
  44. package/src/control-database.ts +376 -0
  45. package/src/enrollment.ts +199 -0
  46. package/src/hibernation-manager.ts +268 -0
  47. package/src/index.ts +71 -0
  48. package/src/seed-bootstrap.ts +753 -0
  49. package/src/strand-database.ts +168 -0
  50. package/src/strand-formation-manager.ts +277 -0
  51. package/src/strand-instance-manager.ts +281 -0
  52. package/src/strand-solicitation.ts +318 -0
  53. package/src/strand-watcher.ts +197 -0
  54. package/src/types.ts +553 -0
@@ -0,0 +1,484 @@
1
+ import type { Libp2p, PeerId } from '@libp2p/interface';
2
+ import type { IRawStorage, Libp2pTransports } from '@optimystic/db-p2p';
3
+ import type { StrandDatabase } from './strand-database.js';
4
+ /**
5
+ * Node profile determines storage participation
6
+ */
7
+ export type NodeProfile = 'transaction' | 'storage';
8
+ /**
9
+ * Strand filter configuration - determines which strands this node participates in
10
+ */
11
+ export type StrandFilter = {
12
+ mode: 'all';
13
+ } | {
14
+ mode: 'sAppId';
15
+ sAppId: string;
16
+ } | {
17
+ mode: 'strandId';
18
+ strandId: string;
19
+ } | {
20
+ mode: 'none';
21
+ };
22
+ /**
23
+ * Latency hint for strand hibernation behavior
24
+ */
25
+ export type LatencyHint = 'realtime' | 'interactive' | 'background' | 'archive';
26
+ /**
27
+ * Hibernation timeout configuration per latency hint (in milliseconds)
28
+ */
29
+ export interface HibernationTimeouts {
30
+ /** Time before transitioning from active to idle */
31
+ idleTimeout: number;
32
+ /** Time before transitioning from idle to hibernating */
33
+ hibernateTimeout: number;
34
+ /** Interval for check-ins while hibernating */
35
+ checkInInterval: number;
36
+ }
37
+ /**
38
+ * Default hibernation timeouts per latency hint
39
+ */
40
+ export declare const HIBERNATION_TIMEOUTS: Record<LatencyHint, HibernationTimeouts>;
41
+ /**
42
+ * Storage provider type - either an IRawStorage instance or a factory function.
43
+ * Factory functions are useful for creating strand-specific storage instances.
44
+ */
45
+ export type RawStorageProvider = IRawStorage | ((strandId: string) => IRawStorage);
46
+ /**
47
+ * Storage configuration for storage profile nodes
48
+ */
49
+ export interface StorageConfig {
50
+ /**
51
+ * Storage provider - either an IRawStorage instance or a factory function.
52
+ *
53
+ * For Node.js environments, use FileRawStorage from @optimystic/db-p2p-storage-fs:
54
+ * ```typescript
55
+ * import { FileRawStorage } from '@optimystic/db-p2p-storage-fs';
56
+ * storage: { provider: (strandId) => new FileRawStorage(`./data/${strandId}`) }
57
+ * ```
58
+ *
59
+ * For React Native, use the appropriate storage from @optimystic/db-p2p-storage-rn:
60
+ * ```typescript
61
+ * import { RNRawStorage } from '@optimystic/db-p2p-storage-rn';
62
+ * storage: { provider: (strandId) => new RNRawStorage(strandId) }
63
+ * ```
64
+ *
65
+ * For in-memory storage (testing):
66
+ * ```typescript
67
+ * import { MemoryRawStorage } from '@optimystic/db-p2p';
68
+ * storage: { provider: () => new MemoryRawStorage() }
69
+ * ```
70
+ */
71
+ provider: RawStorageProvider;
72
+ quotaBytes?: number;
73
+ }
74
+ /**
75
+ * Network configuration for libp2p
76
+ */
77
+ export interface NetworkConfig {
78
+ listenAddrs?: string[];
79
+ announceAddrs?: string[];
80
+ relayAddrs?: string[];
81
+ /**
82
+ * Enable circuit relay server - allows this node to relay connections for other peers.
83
+ * When undefined, defaults to true for storage profile nodes (they typically have
84
+ * better connectivity and uptime), false for transaction profile nodes.
85
+ */
86
+ enableRelay?: boolean;
87
+ /**
88
+ * Custom libp2p transports. When omitted, the default transports from
89
+ * `@optimystic/db-p2p` are used (TCP + circuit relay for Node.js).
90
+ *
91
+ * React Native apps must supply WebSocket-based transports because TCP
92
+ * is not available in the RN runtime:
93
+ * ```typescript
94
+ * import { webSockets } from '@libp2p/websockets';
95
+ * import { circuitRelayTransport } from '@libp2p/circuit-relay-v2';
96
+ *
97
+ * network: {
98
+ * transports: [webSockets(), circuitRelayTransport()],
99
+ * listenAddrs: [] // RN nodes typically cannot listen
100
+ * }
101
+ * ```
102
+ */
103
+ transports?: Libp2pTransports;
104
+ }
105
+ /**
106
+ * Hibernation configuration
107
+ */
108
+ export interface HibernationConfig {
109
+ enabled: boolean;
110
+ defaultLatencyHint?: LatencyHint;
111
+ /** Custom timeouts per latency hint (overrides defaults) */
112
+ customTimeouts?: Partial<Record<LatencyHint, Partial<HibernationTimeouts>>>;
113
+ }
114
+ /**
115
+ * Control network configuration
116
+ */
117
+ export interface ControlNetworkConfig {
118
+ partyId: string;
119
+ bootstrapNodes: string[];
120
+ /** Optional path to the control schema file (defaults to bundled schema) */
121
+ schemaPath?: string;
122
+ }
123
+ /**
124
+ * Main configuration for a CadreNode
125
+ */
126
+ export interface CadreNodeConfig {
127
+ /** If provided, use this keypair for the node identity */
128
+ privateKey?: Uint8Array;
129
+ /** Control network connection settings */
130
+ controlNetwork: ControlNetworkConfig;
131
+ /** Node profile: transaction-only or storage */
132
+ profile: NodeProfile;
133
+ /** Which strands to participate in */
134
+ strandFilter?: StrandFilter;
135
+ /** Storage configuration (only for storage profile) */
136
+ storage?: StorageConfig;
137
+ /** Network configuration */
138
+ network?: NetworkConfig;
139
+ /** Hibernation configuration */
140
+ hibernation?: HibernationConfig;
141
+ /** Polling interval for strand watcher in ms (default: 5000) */
142
+ strandWatchInterval?: number;
143
+ }
144
+ /**
145
+ * Status of a strand instance
146
+ */
147
+ export type StrandStatus = 'starting' | 'active' | 'idle' | 'hibernating' | 'stopping' | 'stopped' | 'error';
148
+ /**
149
+ * App information from strand header
150
+ */
151
+ export interface SAppInfo {
152
+ id: string;
153
+ version: string;
154
+ schema: string;
155
+ signature: string;
156
+ }
157
+ /**
158
+ * Strand instance state
159
+ */
160
+ export interface StrandInstance {
161
+ strandId: string;
162
+ status: StrandStatus;
163
+ sAppInfo?: SAppInfo;
164
+ /** The libp2p node for this strand (only when active/idle) */
165
+ libp2pNode?: Libp2p;
166
+ /** The Quereus database for this strand (only when active/idle) */
167
+ database?: StrandDatabase;
168
+ /** Membership info for closed strands */
169
+ memberKey?: string;
170
+ memberPrivateKey?: string;
171
+ /** Activity tracking */
172
+ connectedPeers: number;
173
+ lastActivity: Date;
174
+ nextCheckIn?: Date;
175
+ /** Latency hint from app or override */
176
+ latencyHint: LatencyHint;
177
+ /** Error message if status is 'error' */
178
+ error?: string;
179
+ }
180
+ /**
181
+ * Strand row from control network - basic membership info
182
+ */
183
+ export interface StrandRow {
184
+ Id: string;
185
+ MemberPrivateKey: string | null;
186
+ Type: 'o' | 'c';
187
+ }
188
+ /**
189
+ * sApp configuration provided by the hosting application when creating a strand.
190
+ * This is what the app developer provides - NOT loaded from the network.
191
+ */
192
+ export interface SAppConfig {
193
+ /** Public key of the sApp author */
194
+ id: string;
195
+ /** Version of the sApp */
196
+ version: string;
197
+ /** The declarative schema DDL */
198
+ schema: string;
199
+ /** Author's signature over the schema for verification */
200
+ signature: string;
201
+ /** Latency hint for hibernation behavior (optional, defaults to config) */
202
+ latencyHint?: LatencyHint;
203
+ }
204
+ /**
205
+ * Full strand configuration when adding a strand via the API
206
+ */
207
+ export interface StrandConfig {
208
+ /** Strand row from control network */
209
+ strandRow: StrandRow;
210
+ /** sApp configuration provided by the hosting application */
211
+ sAppConfig: SAppConfig;
212
+ }
213
+ /**
214
+ * Result of creating a new cadre peer
215
+ */
216
+ export interface CreatePeerResult {
217
+ peerId: PeerId;
218
+ privateKey: Uint8Array;
219
+ }
220
+ /**
221
+ * Registration data for joining a strand as a member.
222
+ * Sent from invited member to any cadre member to accept invitation.
223
+ */
224
+ export interface MemberRegistration {
225
+ /** The strand being joined */
226
+ strandId: string;
227
+ /** The member's public key for this strand */
228
+ key: string;
229
+ /** Peer IDs of the member's cadre nodes that will participate */
230
+ peerIds: string[];
231
+ }
232
+ /**
233
+ * Result of member registration
234
+ */
235
+ export interface MemberRegistrationResult {
236
+ success: boolean;
237
+ reason?: string;
238
+ }
239
+ /**
240
+ * Open invitation for strand formation.
241
+ * Shared out-of-band to allow strangers to form strands.
242
+ */
243
+ export interface OpenInvitation {
244
+ /** Unique token identifying this invitation */
245
+ token: string;
246
+ /** The sApp that will be used for the strand */
247
+ sAppId: string;
248
+ /** When this invitation expires */
249
+ expiration: Date;
250
+ /** Bootstrap addresses to contact the inviter's cadre */
251
+ bootstrap: string[];
252
+ }
253
+ /**
254
+ * Result of forming a strand via open invitation
255
+ */
256
+ export interface FormStrandResult {
257
+ /** The member key assigned to the initiator for this strand */
258
+ memberKey: string;
259
+ /** Private key for the invitation (for signing future messages) */
260
+ invitePrivateKey: string;
261
+ /** The strand that was created */
262
+ strandId: string;
263
+ }
264
+ /**
265
+ * Result of validating a strand formation request
266
+ */
267
+ export interface ValidateFormationResult {
268
+ /** Public key for validating this formation */
269
+ validationKey: string;
270
+ /** Signature over the formation approval */
271
+ validationSignature: string;
272
+ }
273
+ /**
274
+ * Disclosure object provided during strand formation.
275
+ * Contains identity and context information from the initiator.
276
+ */
277
+ export interface StrandFormationDisclosure {
278
+ /** Initiator's party identifier */
279
+ partyId?: string;
280
+ /** Additional identity bundle (app-specific) */
281
+ identityBundle?: unknown;
282
+ /** Human-readable purpose/reason for forming the strand */
283
+ purpose?: string;
284
+ /** Additional app-specific metadata */
285
+ metadata?: Record<string, unknown>;
286
+ }
287
+ /**
288
+ * Arachnode ring participation stub - will be implemented when arachnode is built
289
+ */
290
+ export interface ArachnodeConfig {
291
+ /** Enable Ring Zulu (transaction ring) - all nodes participate */
292
+ enableRingZulu: boolean;
293
+ /** Storage ring participation (storage profile only) */
294
+ storageRing?: {
295
+ /** Which ring to participate in based on capacity */
296
+ ring: number;
297
+ /** Partition within the ring */
298
+ partition?: number;
299
+ };
300
+ }
301
+ /**
302
+ * Events emitted by CadreNode
303
+ */
304
+ export interface CadreNodeEvents {
305
+ 'strand:started': {
306
+ strandId: string;
307
+ };
308
+ 'strand:stopped': {
309
+ strandId: string;
310
+ };
311
+ 'strand:error': {
312
+ strandId: string;
313
+ error: Error;
314
+ };
315
+ 'strand:idle': {
316
+ strandId: string;
317
+ };
318
+ 'strand:hibernating': {
319
+ strandId: string;
320
+ };
321
+ 'strand:waking': {
322
+ strandId: string;
323
+ };
324
+ 'control:connected': void;
325
+ 'control:disconnected': void;
326
+ /** Emitted when a seed is received via the seed protocol */
327
+ 'seed:received': {
328
+ partyId: string;
329
+ peerId: string;
330
+ };
331
+ /** Emitted when a seed is successfully applied */
332
+ 'seed:applied': {
333
+ partyId: string;
334
+ peersAdded: number;
335
+ };
336
+ /** Emitted when seed application fails */
337
+ 'seed:error': {
338
+ partyId: string;
339
+ error: string;
340
+ };
341
+ }
342
+ /**
343
+ * A peer entry in a control network seed.
344
+ * Contains connection information for a cadre peer.
345
+ */
346
+ export interface SeedPeer {
347
+ /** Peer ID (libp2p identity) */
348
+ peerId: string;
349
+ /** Multiaddrs for connecting to this peer */
350
+ multiaddrs: string[];
351
+ /** Whether this peer holds an authority key */
352
+ isAuthority: boolean;
353
+ }
354
+ /**
355
+ * Control network seed for bootstrapping new nodes.
356
+ * Pre-populates a new node's cache to solve the cold-start problem:
357
+ * new nodes need control data to validate connections, but can't get
358
+ * data without connecting first.
359
+ */
360
+ export interface ControlNetworkSeed {
361
+ /** Party ID this seed belongs to */
362
+ partyId: string;
363
+ /** Known peers in the cadre */
364
+ peers: SeedPeer[];
365
+ /** Optional signed transactions to pre-populate the cache */
366
+ transactions?: SignedTransaction[];
367
+ /** Signature over the seed by an authority key */
368
+ signature: string;
369
+ /** The authority key that signed this seed */
370
+ signerKey: string;
371
+ }
372
+ /**
373
+ * A signed transaction for inclusion in seeds.
374
+ * Allows pre-populating the control network cache.
375
+ */
376
+ export interface SignedTransaction {
377
+ /** Transaction ID */
378
+ id: string;
379
+ /** Serialized transaction data */
380
+ data: string;
381
+ /** Signature over the transaction */
382
+ signature: string;
383
+ }
384
+ /**
385
+ * Message sent from instigator to new node during seed delivery.
386
+ * Delivered via /sereus/seed/1.0.0 libp2p protocol.
387
+ */
388
+ export interface SeedMessage {
389
+ /** Party ID for the cadre */
390
+ partyId: string;
391
+ /** Known peers in the cadre */
392
+ peers: SeedPeer[];
393
+ /** Optional transactions to pre-populate cache */
394
+ transactions?: SignedTransaction[];
395
+ /** Signature by an authority key */
396
+ signature: string;
397
+ /** The authority key that signed this message */
398
+ signerKey: string;
399
+ }
400
+ /**
401
+ * Acknowledgment message from new node to instigator.
402
+ */
403
+ export interface SeedAckMessage {
404
+ /** Whether the seed was accepted */
405
+ accepted: boolean;
406
+ /** Reason for rejection (if not accepted) */
407
+ reason?: string;
408
+ }
409
+ /**
410
+ * Options for authorizing a new peer in the cadre.
411
+ */
412
+ export interface AuthorizePeerOptions {
413
+ /** Peer ID to authorize */
414
+ peerId: string;
415
+ /** Optional multiaddrs for the peer */
416
+ multiaddrs?: string[];
417
+ }
418
+ /**
419
+ * Result of applying a seed to a node.
420
+ */
421
+ export interface ApplySeedResult {
422
+ /** Whether the seed was successfully applied */
423
+ success: boolean;
424
+ /** Number of peers added to the peer store */
425
+ peersAdded: number;
426
+ /** Error message if not successful */
427
+ error?: string;
428
+ }
429
+ /**
430
+ * Node network topology type for seed helper scenarios.
431
+ */
432
+ export type NodeTopology = 'public' | 'nat';
433
+ /**
434
+ * Invite code for phone-to-server enrollment.
435
+ * Used when a server invites a phone to join the cadre.
436
+ */
437
+ export interface CadreInvite {
438
+ /** Party ID of the cadre */
439
+ partyId: string;
440
+ /** Multiaddrs to dial the authority */
441
+ authorityAddrs: string[];
442
+ /** Optional invite token for validation */
443
+ token?: string;
444
+ /** Timestamp when invite was created */
445
+ createdAt: number;
446
+ /** Optional expiration timestamp */
447
+ expiresAt?: number;
448
+ }
449
+ /**
450
+ * Options for adding a drone via provider API.
451
+ */
452
+ export interface AddDroneOptions {
453
+ /** Peer ID of the new drone (returned from provider) */
454
+ dronePeerId: string;
455
+ /** Multiaddrs of the drone (returned from provider) */
456
+ droneMultiaddrs: string[];
457
+ }
458
+ /**
459
+ * Options for adding a phone via invite flow.
460
+ */
461
+ export interface AddPhoneOptions {
462
+ /** Peer ID of the phone (sent by phone when it connects) */
463
+ phonePeerId: string;
464
+ /** Invite token for validation (must match issued invite) */
465
+ token?: string;
466
+ }
467
+ /**
468
+ * Result of preparing a seed for drone initialization.
469
+ */
470
+ export interface DroneInitResult {
471
+ /** The seed to send to the provider API */
472
+ seed: ControlNetworkSeed;
473
+ /** Base64url encoded seed for transport */
474
+ encodedSeed: string;
475
+ }
476
+ /**
477
+ * Result of creating an invite for a phone.
478
+ */
479
+ export interface InviteResult {
480
+ /** The invite to share out-of-band */
481
+ invite: CadreInvite;
482
+ /** Base64url encoded invite for QR/link */
483
+ encodedInvite: string;
484
+ }
package/dist/types.js ADDED
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Default hibernation timeouts per latency hint
3
+ */
4
+ export const HIBERNATION_TIMEOUTS = {
5
+ realtime: {
6
+ idleTimeout: Infinity, // Never idle
7
+ hibernateTimeout: Infinity, // Never hibernate
8
+ checkInInterval: Infinity // N/A
9
+ },
10
+ interactive: {
11
+ idleTimeout: 5 * 60 * 1000, // 5 minutes
12
+ hibernateTimeout: 15 * 60 * 1000, // 15 minutes after idle
13
+ checkInInterval: 30 * 1000 // 30 seconds
14
+ },
15
+ background: {
16
+ idleTimeout: 1 * 60 * 1000, // 1 minute
17
+ hibernateTimeout: 5 * 60 * 1000, // 5 minutes after idle
18
+ checkInInterval: 5 * 60 * 1000 // 5 minutes
19
+ },
20
+ archive: {
21
+ idleTimeout: 10 * 1000, // 10 seconds
22
+ hibernateTimeout: 30 * 1000, // 30 seconds after idle
23
+ checkInInterval: 60 * 60 * 1000 // 1 hour
24
+ }
25
+ };
26
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAmCA;;GAEG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAA6C;IAC5E,QAAQ,EAAE;QACR,WAAW,EAAE,QAAQ,EAAS,aAAa;QAC3C,gBAAgB,EAAE,QAAQ,EAAI,kBAAkB;QAChD,eAAe,EAAE,QAAQ,CAAK,MAAM;KACrC;IACD,WAAW,EAAE;QACX,WAAW,EAAE,CAAC,GAAG,EAAE,GAAG,IAAI,EAAI,YAAY;QAC1C,gBAAgB,EAAE,EAAE,GAAG,EAAE,GAAG,IAAI,EAAE,wBAAwB;QAC1D,eAAe,EAAE,EAAE,GAAG,IAAI,CAAI,aAAa;KAC5C;IACD,UAAU,EAAE;QACV,WAAW,EAAE,CAAC,GAAG,EAAE,GAAG,IAAI,EAAI,WAAW;QACzC,gBAAgB,EAAE,CAAC,GAAG,EAAE,GAAG,IAAI,EAAG,uBAAuB;QACzD,eAAe,EAAE,CAAC,GAAG,EAAE,GAAG,IAAI,CAAI,YAAY;KAC/C;IACD,OAAO,EAAE;QACP,WAAW,EAAE,EAAE,GAAG,IAAI,EAAQ,aAAa;QAC3C,gBAAgB,EAAE,EAAE,GAAG,IAAI,EAAG,wBAAwB;QACtD,eAAe,EAAE,EAAE,GAAG,EAAE,GAAG,IAAI,CAAG,SAAS;KAC5C;CACF,CAAC"}
package/package.json ADDED
@@ -0,0 +1,67 @@
1
+ {
2
+ "name": "@serfab/cadre-core",
3
+ "version": "0.0.2",
4
+ "type": "module",
5
+ "description": "Core library for Sereus cadre nodes - manages control networks and strand participation",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "files": [
9
+ "src",
10
+ "dist",
11
+ "!dist/test",
12
+ "!**/*.tsbuildinfo"
13
+ ],
14
+ "exports": {
15
+ ".": {
16
+ "types": "./dist/index.d.ts",
17
+ "import": "./dist/index.js"
18
+ }
19
+ },
20
+ "repository": {
21
+ "type": "git",
22
+ "url": "https://github.com/gotchoices/sereus.git",
23
+ "directory": "packages/cadre-core"
24
+ },
25
+ "author": "Got Choices Foundation",
26
+ "license": "MIT",
27
+ "keywords": [
28
+ "sereus",
29
+ "cadre",
30
+ "core",
31
+ "distributed",
32
+ "p2p",
33
+ "libp2p"
34
+ ],
35
+ "scripts": {
36
+ "clean": "rimraf dist",
37
+ "build": "tsc -p tsconfig.build.json",
38
+ "test": "vitest run",
39
+ "dev:test": "vitest"
40
+ },
41
+ "devDependencies": {
42
+ "@chainsafe/libp2p-noise": "^17.0.0",
43
+ "@chainsafe/libp2p-yamux": "^8.0.1",
44
+ "@libp2p/peer-id-factory": "^4.2.4",
45
+ "@libp2p/tcp": "^11.0.10",
46
+ "@types/debug": "^4.1.12",
47
+ "@types/node": "^25.0.10",
48
+ "aegir": "^45.2.1",
49
+ "rimraf": "^6.1.2",
50
+ "typescript": "^5.9.3",
51
+ "vitest": "^4.0.17"
52
+ },
53
+ "dependencies": {
54
+ "@libp2p/crypto": "^5.1.13",
55
+ "@libp2p/interface": "^3.1.0",
56
+ "@libp2p/peer-id": "^6.0.4",
57
+ "@optimystic/db-core": "^0.5.2",
58
+ "@optimystic/db-p2p": "^0.2.3",
59
+ "@optimystic/quereus-plugin-crypto": "^0.4.3",
60
+ "@optimystic/quereus-plugin-optimystic": "^0.4.2",
61
+ "@quereus/quereus": "^0.17.0",
62
+ "@serfab/strand-proto": "^0.1.0",
63
+ "debug": "^4.4.3",
64
+ "libp2p": "^3.1.3",
65
+ "uint8arrays": "^5.1.0"
66
+ }
67
+ }