@spider-mesh/core 2.0.40 → 2.0.42

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,47 +1,17 @@
1
1
  # Spider Mesh Core
2
2
 
3
- `@spider-mesh/core` is a lightweight TypeScript microservice core for service-to-service RPC, pubsub events, and node discovery with pluggable transporters.
3
+ `@spider-mesh/core` is the runtime-agnostic Spider Mesh package.
4
4
 
5
5
  It provides:
6
6
 
7
- - Decorator-based local service registration
8
- - Typed remote service linking through a Proxy
9
- - Stream-first RPC over transporter-managed byte delivery
10
- - Discovery and pubsub integration through simple transporter contracts
11
- - Transporter contracts that can be implemented directly or consumed through companion transport packages such as `@spider-mesh/tcp` and `@spider-mesh/ws`
12
- - NestJS adapters for exposing and consuming services
7
+ - local microservice registration with decorators
8
+ - typed remote service linking through proxies
9
+ - RPC, discovery, and pubsub transporter contracts
10
+ - a `Registry` for remote peer state and RPC routing
11
+ - a `SpiderMesh` runtime for local services, transporters, and node metadata
12
+ - NestJS helper adapters
13
13
 
14
- ## Package Boundary
15
-
16
- Spider Mesh is now split into two main package layers:
17
-
18
- - `@spider-mesh/core`: runtime-agnostic service runtime, decorators, linking, and shared contracts
19
- - companion transport packages: concrete transport implementations such as `@spider-mesh/tcp` and `@spider-mesh/ws`
20
-
21
- Keep companion packages on the same published version as `@spider-mesh/core` to avoid contract drift between the runtime and concrete transports.
22
-
23
- ## Metadata Contract
24
-
25
- `SpiderMeshNode` is intentionally minimal.
26
-
27
- - Node metadata includes `host`, `namespace`, `node_id`, `version`, `services`, `nodes`, and `transporters`.
28
- - Node metadata no longer includes `ips`.
29
- - Node metadata no longer includes `online`.
30
-
31
- Discovery transporters should treat discovery as a node announcement stream, not as an interface-IP inventory.
32
-
33
- The discovery transporter contract is:
34
-
35
- ```ts
36
- broadcast(data: MdnsMessage<NodeMetadata>): Promise<void>
37
- ```
38
-
39
- `@spider-mesh/core` no longer passes a separate IP list into discovery transporters.
40
-
41
- If you are reading this package first, the practical rule is simple:
42
-
43
- - stay in `@spider-mesh/core` for service code and runtime code
44
- - add a companion transport package only when you explicitly need a concrete transport implementation
14
+ Concrete transport implementations live in companion packages such as `@spider-mesh/tcp` and `@spider-mesh/ws`, or in your own custom transporters.
45
15
 
46
16
  ## Install
47
17
 
@@ -49,124 +19,38 @@ If you are reading this package first, the practical rule is simple:
49
19
  bun add @spider-mesh/core rxjs reflect-metadata
50
20
  ```
51
21
 
52
- If you also install a companion transport package, keep it on the same release version as `@spider-mesh/core`.
53
-
54
- This package is ESM-only.
55
-
56
- Practical implications:
57
-
58
- - published output is loaded as ESM through the root export map
59
- - repository TypeScript uses `module: NodeNext` and `moduleResolution: NodeNext`
60
- - internal relative imports use emitted `.js` specifiers, for example `./SpiderMesh.js`
61
- - avoid CommonJS patterns such as `require()` when adding new runtime code
62
-
63
- ## AI Usage Guide
64
-
65
- Use this section as the canonical decision guide when generating code with AI tools.
66
-
67
- ### Environment support matrix
22
+ For TypeScript projects using decorators, enable decorator metadata in your compiler settings.
68
23
 
69
- | Runtime | `@spider-mesh/core` | `@spider-mesh/tcp` | `@spider-mesh/ws` |
70
- | --- | --- | --- | --- |
71
- | Node.js | Supported | Supported | Supported |
72
- | Bun | Supported | Supported | Supported |
73
- | Browser | Partially supported, depends on your custom transporter | Not recommended | Not recommended |
74
- | React Native | Supported for the core entry only | Not recommended | Not recommended |
24
+ ## Runtime Setup
75
25
 
76
- ### Recommended imports
26
+ Create a registry when you need remote peer discovery and RPC routing.
77
27
 
78
- Use these imports exactly.
79
-
80
- - Core runtime, decorators, RPC linking, NestJS helpers: `import { ... } from '@spider-mesh/core'`
81
- - Shared transporter contracts and runtime APIs: `import { ... } from '@spider-mesh/core'`
82
- - Companion TCP transport implementation when needed: `import { Http2Rpc, Http2Pubsub, UdpDiscovery } from '@spider-mesh/tcp'`
83
- - Companion WebSocket transport implementation when needed: `import { WebsocketTransporter } from '@spider-mesh/ws'`
84
-
85
- ### When to use what
86
-
87
- - Use `SpiderMesh` when you need a local runtime that can expose services, discover nodes, publish events, or call remote services.
88
- - Use `@Microservice()` on local classes that should be callable remotely.
89
- - Use `RemoteServiceLinker.link()` when you need a typed remote proxy for another service.
90
- - Use transporter contracts from `@spider-mesh/core` when you are implementing or typing your own transport.
91
- - Use `@spider-mesh/tcp` or `@spider-mesh/ws` when you want a companion transport package.
92
- - Use a custom transporter when the runtime is not Node.js or Bun, or when you need a different protocol.
93
-
94
- ### Do and don't
95
-
96
- Do:
97
-
98
- - Import runtime-agnostic APIs from `@spider-mesh/core`.
99
- - Import transporter contracts from `@spider-mesh/core` when you need transport typing.
100
- - Use a companion transport package such as `@spider-mesh/tcp` or `@spider-mesh/ws` when you need a ready-made transport implementation.
101
- - Wait for remote service availability with `wait()` before making calls in startup flows.
102
- - Treat companion package examples and e2e tests as canonical usage references for concrete transport behavior.
103
-
104
- Don't:
105
-
106
- - Do not assume `@spider-mesh/core` exports every concrete transporter.
107
- - Do not assume `@spider-mesh/tcp` is React Native-ready.
108
- - Do not assume `@spider-mesh/ws` is React Native-ready.
109
- - Do not couple service code to a single transport package unless that is intentional.
110
- - Do not call remote services before the target service has been discovered unless you intentionally rely on retry behavior.
111
-
112
- ### Canonical recipes
113
-
114
- Provider recipe:
115
-
116
- 1. Import `Microservice` and `SpiderMesh` from `@spider-mesh/core`.
117
- 2. Choose a concrete transporter implementation, for example from `@spider-mesh/tcp` or `@spider-mesh/ws`.
118
- 3. Define a class and decorate it with `@Microservice()`.
119
- 4. Instantiate the service class.
120
- 5. Create `new SpiderMesh({ transporters: [transporter] })`.
121
-
122
- Client recipe:
123
-
124
- 1. Import `SpiderMesh` and `RemoteServiceLinker` from `@spider-mesh/core`.
125
- 2. Choose a concrete transporter implementation, for example from `@spider-mesh/tcp` or `@spider-mesh/ws`.
126
- 3. Create `new SpiderMesh({ transporters: [transporter] })`.
127
- 4. Create a typed remote proxy with `RemoteServiceLinker.link()`.
128
- 5. Call `await proxy.wait()` before the first RPC call.
129
-
130
- Companion transport package recipes:
131
-
132
- TCP:
133
-
134
- 1. Import `UdpDiscovery`, `Http2Rpc`, and `Http2Pubsub` from `@spider-mesh/tcp`.
135
- 2. Add those transporters to `new SpiderMesh({ transporters: [...] })`.
136
- 3. Let the TCP package handle discovery, RPC, and pubsub transport.
137
-
138
- WebSocket:
139
-
140
- 1. Import `WebsocketTransporter` from `@spider-mesh/ws`.
141
- 2. Add that transporter to `new SpiderMesh({ transporters: [...] })`.
142
- 3. Run `WebsocketRelayServer` from `@spider-mesh/ws/relay-server` when using the WebSocket companion package.
143
-
144
- ### AI-safe assumptions
28
+ ```ts
29
+ import { Registry, SpiderMesh } from '@spider-mesh/core'
145
30
 
146
- An AI agent should assume the following unless the codebase says otherwise:
31
+ const registry = new Registry()
32
+ const mesh = new SpiderMesh(registry)
33
+ ```
147
34
 
148
- - The root package entry is the safe default for shared runtime APIs.
149
- - Concrete transports are opt-in through companion packages or custom implementations.
150
- - Transporter contracts come from the core package.
151
- - The companion TCP and WebSocket packages are sibling transport options for Node.js and Bun runtimes.
152
- - If the target runtime is React Native, prefer the core APIs and a runtime-appropriate custom transporter.
35
+ Register transporter instances on the runtime:
153
36
 
154
- ## Core Model
37
+ ```ts
38
+ import { Http2Pubsub, Http2Rpc, UdpDiscovery } from '@spider-mesh/tcp'
155
39
 
156
- `SpiderMesh` is the runtime coordinator.
40
+ mesh.registerTransporter(new UdpDiscovery())
41
+ mesh.registerTransporter(new Http2Rpc())
42
+ mesh.registerTransporter(new Http2Pubsub())
43
+ ```
157
44
 
158
- It keeps track of:
45
+ Transporter capability is inferred by instance shape:
159
46
 
160
- - The current node metadata
161
- - Local service instances registered in the process
162
- - Remote nodes discovered from discovery transporters
163
- - RPC-capable nodes for each service
164
- - RPC pending and running stream state
165
- - Pubsub, RPC, and discovery transporters
47
+ - `send()` => RPC transporter
48
+ - `publish()` => pubsub transporter
49
+ - `broadcast()` => discovery transporter
166
50
 
167
- ## Quick Start
51
+ ## Local Services
168
52
 
169
- ### 1. Define a local microservice
53
+ Use `@Microservice()` on local service classes and instantiate them normally.
170
54
 
171
55
  ```ts
172
56
  import { BeforeMicroserviceOnline, Microservice } from '@spider-mesh/core'
@@ -185,203 +69,222 @@ export class UserService {
185
69
  return { id, name: 'Ada' }
186
70
  }
187
71
  }
72
+
73
+ new UserService()
188
74
  ```
189
75
 
190
- `@Microservice()` registers the instance into Spider Mesh when the class is constructed.
76
+ `@Microservice()` emits the constructed instance into the shared `LOCAL_SERVICES$` stream. `SpiderMesh` subscribes to that stream and adds the service to local node metadata.
191
77
 
192
- `@BeforeMicroserviceOnline()` marks async setup methods that must complete before the service is exposed in local metadata.
78
+ ## Remote Services
193
79
 
194
- ### 2. Create the runtime
80
+ Create a typed remote client with `RemoteServiceLinker.link()`.
195
81
 
196
82
  ```ts
197
- import { SpiderMesh } from '@spider-mesh/core'
198
- import { AppDiscoveryTransporter } from './AppDiscoveryTransporter.js'
199
- import { AppRpcTransporter } from './AppRpcTransporter.js'
200
- import { AppPubsubTransporter } from './AppPubsubTransporter.js'
201
-
202
- const mesh = new SpiderMesh({
203
- transporters: [
204
- AppDiscoveryTransporter,
205
- AppRpcTransporter,
206
- AppPubsubTransporter,
207
- ],
208
- })
209
- ```
210
-
211
- These transporter class names are application-local examples, not exports from `@spider-mesh/core`.
212
-
213
- Transporter capability is inferred by method shape:
214
-
215
- - `send()` means RPC transporter
216
- - `publish()` means pubsub transporter
217
- - `broadcast()` means discovery transporter
83
+ import { RemoteServiceLinker } from '@spider-mesh/core'
218
84
 
219
- You can pass either transporter classes or already-created transporter instances into `transporters`.
85
+ type UserServiceContract = {
86
+ getUser(id: string): Promise<{ id: string; name: string }>
87
+ }
220
88
 
221
- ### 2a. Use companion transport packages
89
+ const users = RemoteServiceLinker.link<UserServiceContract>(mesh, {
90
+ service: 'UserService',
91
+ })
222
92
 
223
- Spider Mesh can work with companion transport packages such as `@spider-mesh/tcp` and `@spider-mesh/ws`.
93
+ await users.wait()
224
94
 
225
- The root package entry exports the runtime-agnostic APIs and shared transporter contracts. Concrete transport implementations can be imported from companion packages when needed.
95
+ const user = await users.getUser('42')
96
+ ```
226
97
 
227
- Create a runtime using the TCP package:
98
+ Remote methods return RxJS observables and can also be awaited.
228
99
 
229
100
  ```ts
230
- import { SpiderMesh } from '@spider-mesh/core'
231
- import { Http2Pubsub, Http2Rpc, UdpDiscovery } from '@spider-mesh/tcp'
101
+ const user = await users.getUser('42')
232
102
 
233
- const mesh = new SpiderMesh({
234
- transporters: [
235
- new UdpDiscovery(),
236
- new Http2Rpc(),
237
- new Http2Pubsub(),
238
- ],
103
+ users.getUser('42').subscribe(value => {
104
+ console.log(value)
239
105
  })
240
106
  ```
241
107
 
242
- Or use the WebSocket package:
108
+ ### Waiting and watching
243
109
 
244
110
  ```ts
245
- import { SpiderMesh } from '@spider-mesh/core'
246
- import { WebsocketTransporter } from '@spider-mesh/ws'
111
+ await users.wait(nodes => nodes.length > 0)
247
112
 
248
- const transporter = new WebsocketTransporter({
249
- heartbeatIntervalMs: 5000,
250
- reconnectIntervalMs: 1000,
113
+ users.watch().subscribe(nodes => {
114
+ console.log(nodes.map(node => node.node_id))
251
115
  })
252
116
 
253
- transporter.connect('ws://127.0.0.1:8787')
117
+ console.log(users.nodes)
118
+ ```
119
+
120
+ ### Fan-out calls
254
121
 
255
- const mesh = new SpiderMesh({
256
- transporters: [transporter],
122
+ ```ts
123
+ users.__batch__getUser('42').subscribe(result => {
124
+ console.log(result)
257
125
  })
258
126
  ```
259
127
 
260
- Both patterns keep service/runtime code in `@spider-mesh/core` while delegating concrete transport behavior to a companion package.
261
-
262
- ### 2b. Minimal working startup order
128
+ Each emission is either `{ node, data }` or `{ node, error }`.
263
129
 
264
- When using a discovery-based transport package, the canonical startup order is:
130
+ ## RPC Options
265
131
 
266
- 1. Start one or more provider processes that register local `@Microservice()` classes.
267
- 2. Start client processes.
268
- 3. Wait for discovery to converge.
269
- 4. In clients, call `await remote.wait()` before the first remote method call.
132
+ The current `RpcOptions` shape is:
270
133
 
271
- If an AI agent needs one default operational pattern, use this startup order.
134
+ ```ts
135
+ type RpcOptions<T = any> = {
136
+ service: string
137
+ method: string
138
+ args: any[]
139
+ fallback?: T
140
+ timeout?: number
141
+ retry?: number
142
+ node_id?: string
143
+ transporter?: string | { name?: string }
144
+ }
145
+ ```
272
146
 
273
- ### 3. Call a remote service
147
+ Examples:
274
148
 
275
149
  ```ts
276
- import { RemoteServiceLinker } from '@spider-mesh/core'
277
-
278
- type UserServiceContract = {
279
- getUser(id: string): Promise<{ id: string; name: string }>
280
- }
150
+ await users.set({ timeout: 3000, retry: 2 }).getUser('42')
281
151
 
282
- const users = RemoteServiceLinker.link<UserServiceContract>(mesh, {
152
+ await mesh.callRemoteService({
283
153
  service: 'UserService',
154
+ method: 'getUser',
155
+ args: ['42'],
156
+ transporter: 'Http2Rpc',
284
157
  })
285
-
286
- await users.wait()
287
-
288
- const user = await users.getUser('42')
289
- console.log(user.name)
290
158
  ```
291
159
 
292
- Remote methods are exposed through a typed Proxy.
160
+ `transporter` may be:
293
161
 
294
- ## RPC Behavior
162
+ - a registered transporter name string
163
+ - a class or object with a `name`
295
164
 
296
- ### Awaitable observable calls
165
+ ## Events
297
166
 
298
- Remote method calls return an RxJS observable that is also awaitable.
167
+ Use `SpiderMesh.linkEvent()` to bind a topic by event class name.
299
168
 
300
169
  ```ts
301
- const user = await users.getUser('42')
302
- ```
170
+ class UserCreatedEvent {
171
+ constructor(
172
+ public readonly id: string,
173
+ public readonly email: string,
174
+ ) {}
175
+ }
303
176
 
304
- ```ts
305
- users.getUser('42').subscribe(user => {
306
- console.log(user)
177
+ const userCreated = mesh.linkEvent(UserCreatedEvent)
178
+
179
+ await userCreated.publish(new UserCreatedEvent('42', 'ada@example.com'))
180
+
181
+ const sub = userCreated.listen().subscribe(event => {
182
+ console.log(event.id)
307
183
  })
184
+
185
+ sub.unsubscribe()
308
186
  ```
309
187
 
310
- ### Stream-first RPC
188
+ Current event behavior:
311
189
 
312
- Spider Mesh treats every RPC response as a stream.
190
+ - `publish()` fans out through all registered pubsub transporters
191
+ - `listen()` merges all transporter listeners into one shared stream
192
+ - first subscribe adds the topic to local node metadata
193
+ - last unsubscribe removes the topic from local node metadata
194
+ - discovery transporters rebroadcast node metadata after topic changes
313
195
 
314
- - If a local method returns an `Observable`, each emission is forwarded to the caller.
315
- - If a local method returns a `Promise` or plain value, it is sent as one `data` event with `completed: true`.
316
- - If the caller unsubscribes early, Spider Mesh sends a `cancel` packet so the remote node can stop the running stream.
196
+ ## Registry
317
197
 
318
- ### Per-link defaults
198
+ `Registry` stores remote peer and RPC routing state.
319
199
 
320
- ```ts
321
- const resilientUsers = users.set({
322
- timeout: 3000,
323
- retry: 2,
324
- fallback: { id: 'fallback', name: 'Unknown' },
325
- })
200
+ Current public methods:
326
201
 
327
- const user = await resilientUsers.getUser('42')
328
- ```
202
+ - `getPeer(nodeId)`
203
+ - `upsertPeer(node)`
204
+ - `removePeer(nodeId)`
205
+ - `listPeers({ service? })`
206
+ - `watch(service?)`
207
+ - `pickRpcNode(service, { node_id? })`
208
+ - `getRpcTransporterName(service, { node_id? })`
209
+ - `listTopicNodes(topic)`
329
210
 
330
- Supported RPC options:
211
+ ## Transporter Contracts
331
212
 
332
- - `service`: remote service name
333
- - `method`: remote method name
334
- - `args`: positional arguments
335
- - `timeout`: timeout per inactivity window in milliseconds
336
- - `retry`: retry count for offline errors
337
- - `fallback`: fallback value when the call fails
338
- - `node_id`: force routing to a specific node
339
- - `ip`: force routing to a specific node IP
213
+ The contract source of truth is `src/types.ts`.
340
214
 
341
- ### Waiting and watching
215
+ ### RPC transporter
342
216
 
343
217
  ```ts
344
- await users.wait(nodes => nodes.length >= 2)
218
+ type RpcTransporter = Observable<RpcEvent> & {
219
+ linkRegistry?(registry: Registry): void
220
+ send(data: RpcPacket, node_id?: string): Promise<void>
221
+ }
222
+ ```
345
223
 
346
- users.watch().subscribe(nodes => {
347
- console.log(nodes.map(node => node.node_id))
348
- })
224
+ ### Pubsub transporter
349
225
 
350
- console.log(users.nodes)
226
+ ```ts
227
+ type PubsubTransporter = {
228
+ publish<T>(topic: string, data: T): Promise<void>
229
+ listen<T>(topic: string): Observable<T>
230
+ linkRegistry?(registry: Registry): void
231
+ }
351
232
  ```
352
233
 
353
- `wait()` resolves when a service availability condition is met.
234
+ ### Discovery transporter
354
235
 
355
- `watch()` streams matching nodes whenever topology changes.
236
+ ```ts
237
+ type DiscoveryTransporter = Observable<DiscoveryEvent> & {
238
+ linkRegistry?(registry: Registry): void
239
+ broadcast(data: MdnsMessage<NodeMetadata>): Promise<void>
240
+ }
241
+ ```
356
242
 
357
- `nodes` returns the current RPC-capable node list for that service.
243
+ ## NestJS Helpers
358
244
 
359
- ### Fan-out calls across all nodes
245
+ The package exports:
360
246
 
361
- ```ts
362
- users.__batch__getUser('42').subscribe(result => {
363
- console.log(result)
364
- })
365
- ```
247
+ - `NestJSExposeMicroservice(factory, metadata?)`
248
+ - `NestJSLinkMicroservice(factory, transporter)`
249
+ - `NestJSLinkEvent(factory)`
366
250
 
367
- Each emission is either:
251
+ `NestJSLinkMicroservice(factory, transporter)` forwards the transporter selector into `RemoteServiceLinker.link()`.
368
252
 
369
- - `{ node, data }`
370
- - `{ node, error }`
253
+ ## Companion Packages
371
254
 
372
- ## Events
255
+ Use companion packages when you need concrete transport implementations.
373
256
 
374
- `SpiderMesh.linkEvent()` binds a topic using the event class name.
257
+ - `@spider-mesh/tcp`
258
+ - `@spider-mesh/ws`
375
259
 
376
- ```ts
377
- class UserCreatedEvent {
378
- constructor(
379
- public readonly id: string,
380
- public readonly email: string,
381
- ) {}
382
- }
260
+ Keep runtime logic in `@spider-mesh/core` and import concrete transporters explicitly from the companion package.
383
261
 
384
- const userCreated = mesh.linkEvent(UserCreatedEvent)
262
+ ## Tests And Validation
263
+
264
+ The repository includes mock e2e coverage for:
265
+
266
+ - RPC routing by transporter selector
267
+ - async `RemoteServiceLinker.wait()` behavior
268
+ - topic metadata lifecycle for `linkEvent()`
269
+ - RPC routing across isolated child processes
270
+
271
+ Run tests with:
272
+
273
+ ```bash
274
+ bun run test:e2e
275
+ ```
276
+
277
+ Build with:
278
+
279
+ ```bash
280
+ bun run build
281
+ ```
282
+
283
+ ## Notes
284
+
285
+ - This package is ESM-only.
286
+ - Repository source uses emitted `.js` relative specifiers.
287
+ - `LOCAL_SERVICES$` is process-global inside one process.
385
288
 
386
289
  await userCreated.publish(new UserCreatedEvent('42', 'ada@example.com'))
387
290
 
@@ -0,0 +1,19 @@
1
+ import { BehaviorSubject } from 'rxjs';
2
+ import type { SpiderMeshNode } from './types.js';
3
+ export type RegistryPickRpcTargetOptions = {
4
+ node_id?: string;
5
+ };
6
+ export declare class Registry {
7
+ #private;
8
+ readonly nodes$: BehaviorSubject<Map<string, SpiderMeshNode>>;
9
+ getPeer(nodeId: string): SpiderMeshNode | undefined;
10
+ upsertPeer(node: SpiderMeshNode): SpiderMeshNode;
11
+ removePeer(nodeId: string): boolean;
12
+ listPeers(options?: {
13
+ service?: string;
14
+ }): SpiderMeshNode[];
15
+ watch(service?: string): import("rxjs").Observable<SpiderMeshNode[]>;
16
+ pickRpcNode(service: string, options?: RegistryPickRpcTargetOptions): string | null;
17
+ getRpcTransporterName(service: string, options?: RegistryPickRpcTargetOptions): string | null;
18
+ listTopicNodes(topic: string): SpiderMeshNode[];
19
+ }
@@ -0,0 +1,77 @@
1
+ import { BehaviorSubject, map } from 'rxjs';
2
+ export class Registry {
3
+ nodes$ = new BehaviorSubject(new Map());
4
+ #rrIndexes = new Map();
5
+ getPeer(nodeId) {
6
+ return this.nodes$.value.get(nodeId);
7
+ }
8
+ upsertPeer(node) {
9
+ const nodes = new Map(this.nodes$.value);
10
+ const current = nodes.get(node.node_id);
11
+ nodes.set(node.node_id, {
12
+ ...current,
13
+ ...node,
14
+ topics: node.topics || current?.topics || [],
15
+ services: {
16
+ ...(current?.services || {}),
17
+ ...(node.services || {})
18
+ },
19
+ nodes: {
20
+ ...(current?.nodes || {}),
21
+ ...(node.nodes || {})
22
+ },
23
+ transporters: {
24
+ ...(current?.transporters || {}),
25
+ ...(node.transporters || {})
26
+ }
27
+ });
28
+ this.nodes$.next(nodes);
29
+ return nodes.get(node.node_id);
30
+ }
31
+ removePeer(nodeId) {
32
+ const nodes = new Map(this.nodes$.value);
33
+ const deleted = nodes.delete(nodeId);
34
+ if (deleted) {
35
+ this.nodes$.next(nodes);
36
+ }
37
+ return deleted;
38
+ }
39
+ listPeers(options = {}) {
40
+ return [...this.nodes$.value.values()].filter(node => {
41
+ if (options.service && node.services[options.service] == undefined)
42
+ return false;
43
+ return true;
44
+ });
45
+ }
46
+ watch(service) {
47
+ return this.nodes$.pipe(map(() => this.listPeers({ service })));
48
+ }
49
+ pickRpcNode(service, options = {}) {
50
+ if (options.node_id) {
51
+ const node = this.getPeer(options.node_id);
52
+ if (!node)
53
+ return null;
54
+ if (node.services[service] == undefined)
55
+ return null;
56
+ return node.node_id;
57
+ }
58
+ const targets = this.listPeers({ service });
59
+ if (targets.length === 0)
60
+ return null;
61
+ const index = this.#rrIndexes.get(service) || 0;
62
+ this.#rrIndexes.set(service, (index + 1) % targets.length);
63
+ return targets[index % targets.length]?.node_id || null;
64
+ }
65
+ getRpcTransporterName(service, options = {}) {
66
+ const nodeId = options.node_id || this.pickRpcNode(service, options);
67
+ if (!nodeId)
68
+ return null;
69
+ const node = this.getPeer(nodeId);
70
+ const transporter = node?.transporters?.rpc;
71
+ return typeof transporter === 'string' ? transporter : null;
72
+ }
73
+ listTopicNodes(topic) {
74
+ return [...this.nodes$.value.values()].filter(node => node.topics.includes(topic));
75
+ }
76
+ }
77
+ //# sourceMappingURL=Registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Registry.js","sourceRoot":"","sources":["../../src/Registry.ts"],"names":[],"mappings":"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"}