@spider-mesh/core 2.0.41 → 2.0.43

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
68
-
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 |
75
-
76
- ### Recommended imports
77
-
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
22
+ For TypeScript projects using decorators, enable decorator metadata in your compiler settings.
86
23
 
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.
24
+ ## Runtime Setup
93
25
 
94
- ### Do and don't
26
+ Create a registry when you need remote peer discovery and RPC routing.
95
27
 
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,209 +69,217 @@ 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'
150
+ import { firstValueFrom } from 'rxjs'
277
151
 
278
- type UserServiceContract = {
279
- getUser(id: string): Promise<{ id: string; name: string }>
280
- }
152
+ await users.set({ timeout: 3000, retry: 2 }).getUser('42')
281
153
 
282
- const users = RemoteServiceLinker.link<UserServiceContract>(mesh, {
154
+ await firstValueFrom(mesh.callRemoteService({
283
155
  service: 'UserService',
284
- })
285
-
286
- await users.wait()
287
-
288
- const user = await users.getUser('42')
289
- console.log(user.name)
156
+ method: 'getUser',
157
+ args: ['42'],
158
+ transporter: 'Http2Rpc',
159
+ }))
290
160
  ```
291
161
 
292
- Remote methods are exposed through a typed Proxy.
162
+ `transporter` may be:
293
163
 
294
- ## RPC Behavior
164
+ - a registered transporter name string
165
+ - a class or object with a `name`
295
166
 
296
- ### Awaitable observable calls
167
+ ## Events
297
168
 
298
- Remote method calls return an RxJS observable that is also awaitable.
169
+ Use `SpiderMesh.linkEvent()` to bind a topic by event class name.
299
170
 
300
171
  ```ts
301
- const user = await users.getUser('42')
302
- ```
172
+ class UserCreatedEvent {
173
+ constructor(
174
+ public readonly id: string,
175
+ public readonly email: string,
176
+ ) {}
177
+ }
303
178
 
304
- ```ts
305
- users.getUser('42').subscribe(user => {
306
- console.log(user)
179
+ const userCreated = mesh.linkEvent(UserCreatedEvent)
180
+
181
+ await userCreated.publish(new UserCreatedEvent('42', 'ada@example.com'))
182
+
183
+ const sub = userCreated.listen().subscribe(event => {
184
+ console.log(event.id)
307
185
  })
186
+
187
+ sub.unsubscribe()
308
188
  ```
309
189
 
310
- ### Stream-first RPC
190
+ Current event behavior:
311
191
 
312
- Spider Mesh treats every RPC response as a stream.
192
+ - `publish()` fans out through all registered pubsub transporters
193
+ - `listen()` merges all transporter listeners into one shared stream
194
+ - first subscribe adds the topic to local node metadata
195
+ - last unsubscribe removes the topic from local node metadata
196
+ - discovery transporters rebroadcast node metadata after topic changes
313
197
 
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.
198
+ ## Registry
317
199
 
318
- ### Per-link defaults
200
+ `Registry` stores remote peer and RPC routing state.
319
201
 
320
- ```ts
321
- const resilientUsers = users.set({
322
- timeout: 3000,
323
- retry: 2,
324
- fallback: { id: 'fallback', name: 'Unknown' },
325
- })
202
+ Current public methods:
326
203
 
327
- const user = await resilientUsers.getUser('42')
328
- ```
204
+ - `getPeer(nodeId)`
205
+ - `upsertPeer(node)`
206
+ - `removePeer(nodeId)`
207
+ - `listPeers(service?)`
208
+ - `watch(service?)`
209
+ - `pickRpcNode(service, { node_id? })`
210
+ - `getRpcTransporterName(service)`
211
+ - `listTopicNodes(topic)`
329
212
 
330
- Supported RPC options:
213
+ ## Transporter Contracts
331
214
 
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
215
+ The contract source of truth is `src/types.ts`.
340
216
 
341
- ### Waiting and watching
217
+ ### RPC transporter
342
218
 
343
219
  ```ts
344
- await users.wait(nodes => nodes.length >= 2)
220
+ type RpcTransporter = Observable<RpcEvent> & {
221
+ linkRegistry?(registry: Registry): void
222
+ send(data: RpcPacket, node_id?: string): Promise<void>
223
+ }
224
+ ```
345
225
 
346
- users.watch().subscribe(nodes => {
347
- console.log(nodes.map(node => node.node_id))
348
- })
226
+ ### Pubsub transporter
349
227
 
350
- console.log(users.nodes)
228
+ ```ts
229
+ type PubsubTransporter = Observable<PubsubEvent> & {
230
+ publish<T>(topic: string, data: T): Promise<void>
231
+ listen<T>(topic: string): Observable<T>
232
+ linkRegistry?(registry: Registry): void
233
+ }
234
+ ```
235
+
236
+ ### Discovery transporter
237
+
238
+ ```ts
239
+ type DiscoveryTransporter = Observable<DiscoveryEvent> & {
240
+ linkRegistry?(registry: Registry): void
241
+ broadcast(data: MdnsMessage<NodeMetadata>): Promise<void>
242
+ }
351
243
  ```
352
244
 
353
- `wait()` resolves when a service availability condition is met.
245
+ ## NestJS Helpers
354
246
 
355
- `watch()` streams matching nodes whenever topology changes.
247
+ The package exports:
356
248
 
357
- `nodes` returns the current RPC-capable node list for that service.
249
+ - `NestJSExposeMicroservice(factory, metadata?)`
250
+ - `NestJSLinkMicroservice(factory, transporter?)`
251
+ - `NestJSLinkEvent(factory)`
358
252
 
359
- ### Fan-out calls across all nodes
253
+ `NestJSLinkMicroservice(factory, transporter?)` forwards the optional transporter selector into `RemoteServiceLinker.link()`.
360
254
 
361
- ```ts
362
- users.__batch__getUser('42').subscribe(result => {
363
- console.log(result)
364
- })
365
- ```
255
+ ## Companion Packages
366
256
 
367
- Each emission is either:
257
+ Use companion packages when you need concrete transport implementations.
368
258
 
369
- - `{ node, data }`
370
- - `{ node, error }`
259
+ - `@spider-mesh/tcp`
260
+ - `@spider-mesh/ws`
371
261
 
372
- ## Events
262
+ Keep runtime logic in `@spider-mesh/core` and import concrete transporters explicitly from the companion package.
373
263
 
374
- `SpiderMesh.linkEvent()` binds a topic using the event class name.
264
+ ## Tests And Validation
375
265
 
376
- ```ts
377
- class UserCreatedEvent {
378
- constructor(
379
- public readonly id: string,
380
- public readonly email: string,
381
- ) {}
382
- }
266
+ The repository includes mock e2e coverage for:
383
267
 
384
- const userCreated = mesh.linkEvent(UserCreatedEvent)
268
+ - RPC routing by transporter selector
269
+ - async `RemoteServiceLinker.wait()` behavior
270
+ - topic metadata lifecycle for `linkEvent()`
271
+ - RPC routing across isolated child processes
385
272
 
386
- await userCreated.publish(new UserCreatedEvent('42', 'ada@example.com'))
273
+ Run tests with:
387
274
 
388
- userCreated.listen().subscribe(event => {
389
- console.log(event.id)
390
- })
275
+ ```bash
276
+ bun run test:e2e
277
+ ```
278
+
279
+ Build with:
280
+
281
+ ```bash
282
+ bun run build
391
283
  ```
392
284
 
393
285
  ## NestJS Integration
@@ -396,18 +288,24 @@ userCreated.listen().subscribe(event => {
396
288
 
397
289
  ```ts
398
290
  import { Module } from '@nestjs/common'
399
- import { SpiderMesh } from '@spider-mesh/core'
291
+ import { Registry, SpiderMesh } from '@spider-mesh/core'
400
292
  import { Http2Pubsub, Http2Rpc, UdpDiscovery } from '@spider-mesh/tcp'
401
293
 
402
294
  @Module({
403
295
  providers: [
404
- SpiderMesh.asProvider({
405
- transporters: [
406
- UdpDiscovery,
407
- Http2Rpc,
408
- Http2Pubsub,
409
- ],
410
- }),
296
+ {
297
+ provide: SpiderMesh,
298
+ useFactory: () => {
299
+ const registry = new Registry()
300
+ const mesh = new SpiderMesh(registry)
301
+
302
+ mesh.registerTransporter(new UdpDiscovery())
303
+ mesh.registerTransporter(new Http2Rpc())
304
+ mesh.registerTransporter(new Http2Pubsub())
305
+
306
+ return mesh
307
+ },
308
+ },
411
309
  ],
412
310
  exports: [SpiderMesh],
413
311
  })
@@ -469,6 +367,12 @@ export class CheckoutService {
469
367
  export class CheckoutModule {}
470
368
  ```
471
369
 
370
+ Pass a transporter only when you need to force a specific RPC transporter:
371
+
372
+ ```ts
373
+ NestJSLinkMicroservice(BillingService, 'Http2Rpc')
374
+ ```
375
+
472
376
  ### Inject an event binding in NestJS
473
377
 
474
378
  ```ts
@@ -499,85 +403,6 @@ export class AuditService {
499
403
  export class AuditModule {}
500
404
  ```
501
405
 
502
- ## Transporter Contract
503
-
504
- Concrete transport implementations can live in companion packages such as `@spider-mesh/tcp` and `@spider-mesh/ws`, or in your own application code.
505
-
506
- You can also provide your own classes that implement one or more transporter contracts exported by `@spider-mesh/core`.
507
-
508
- ### Public API map
509
-
510
- - `SpiderMesh`: runtime coordinator for services, events, discovery, and transporters
511
- - `RemoteServiceLinker.link()`: creates a typed remote proxy
512
- - `@Microservice()`: exposes a local class instance as a remote service
513
- - `SpiderMesh.linkEvent()`: creates a topic binding for publish and subscribe
514
- - `RpcTransporter`: RPC transporter contract
515
- - `DiscoveryTransporter`: discovery transporter contract
516
- - `PubsubTransporter`: pubsub transporter contract
517
-
518
- ### RPC transporter
519
-
520
- ```ts
521
- type RpcMessage = {
522
- node_id: string
523
- packet: RpcPacket
524
- }
525
-
526
- type RpcEvent = Partial<{
527
- rpc: RpcMessage
528
- offline: string
529
- endpoints: Record<string, string | boolean | number>
530
- }>
531
-
532
- type RpcTransporter = Observable<RpcEvent> & {
533
- send(data: RpcPacket, node: SpiderMeshNode): Promise<void>
534
- }
535
- ```
536
-
537
- The RPC observable can emit:
538
-
539
- - `rpc`: inbound RPC message shaped as `{ node_id, packet }`
540
- - `offline`: node offline event
541
- - `endpoints`: transporter metadata to attach to the current node
542
-
543
- The internal RPC wire protocol supports:
544
-
545
- - `request`
546
- - `response`
547
- - `cancel`
548
-
549
- `response` packets can carry:
550
-
551
- - `data`
552
- - `error`
553
- - `completed`
554
-
555
- ### Discovery transporter
556
-
557
- ```ts
558
- type DiscoveryEvent = {
559
- discovered: SpiderMeshNode
560
- }
561
-
562
- type DiscoveryTransporter = Observable<DiscoveryEvent> & {
563
- broadcast(
564
- data: MdnsMessage<NodeMetadata>,
565
- ips: string[],
566
- ): Promise<void>
567
- }
568
- ```
569
-
570
- Discovery transporters stream remote node snapshots into the mesh, and `SpiderMesh` itself calls `broadcast()` whenever local node metadata changes.
571
-
572
- ### Pubsub transporter
573
-
574
- ```ts
575
- type PubsubTransporter = {
576
- publish<T>(topic: string, data: T): Promise<void>
577
- listen<T>(topic: string): Observable<T>
578
- }
579
- ```
580
-
581
406
  ## Error Model
582
407
 
583
408
  The core defines these error codes for RPC flows:
@@ -596,21 +421,17 @@ The core defines these error codes for RPC flows:
596
421
  The package also exports:
597
422
 
598
423
  - `LimitConcurrency(limit)` and `LimitConcurrentRunning(limit)` for throttling async method execution
599
- - `randomUUID()` for Node.js, browser, and React Native compatible UUID generation using ESM-safe runtime detection
600
- - `MicroserviceException` types for common RPC error codes
601
-
602
- ## Build
603
-
604
- ```bash
605
- bun run build
606
- ```
424
+ - `MicroserviceError` for common RPC error codes
607
425
 
608
426
  ## Notes
609
427
 
428
+ - This package is ESM-only.
429
+ - Repository source uses emitted `.js` relative specifiers.
430
+ - `LOCAL_SERVICES$` is process-global inside one process.
610
431
  - Local services are registered when their class instances are constructed.
611
432
  - Service identity is based on the class name.
612
433
  - Event topic identity is based on the event class name.
613
- - RPC target selection is round-robin unless you force `node_id` or `ip`.
434
+ - RPC target selection is round-robin unless you force `node_id`.
614
435
  - `SpiderMesh` owns RPC stream lifecycle, timeout, retry, and cancel behavior.
615
436
  - Transporters focus on byte transport, pubsub topic IO, and discovery broadcasts.
616
437
  - The root package entry intentionally focuses on runtime-agnostic APIs and shared contracts.