@spider-mesh/core 2.0.30 → 2.0.35
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 +573 -0
- package/build/src/RemoteService.d.ts +1 -1
- package/build/src/RemoteService.js.map +1 -1
- package/build/src/SpiderMesh.d.ts +2 -2
- package/build/src/SpiderMesh.js +266 -115
- package/build/src/SpiderMesh.js.map +1 -1
- package/build/src/decorators/Microservice.d.ts +1 -1
- package/build/src/decorators/Microservice.js.map +1 -1
- package/build/src/helpers/GetIps.js +15 -8
- package/build/src/helpers/GetIps.js.map +1 -1
- package/build/src/helpers/LimitConcurrency.js +5 -2
- package/build/src/helpers/LimitConcurrency.js.map +1 -1
- package/build/src/index.d.ts +1 -0
- package/build/src/types.d.ts +102 -0
- package/build/src/types.js +2 -0
- package/build/src/types.js.map +1 -0
- package/build/tsconfig.tsbuildinfo +1 -1
- package/package.json +4 -4
- package/tsconfig.json +3 -1
- package/build/src/abstract/DiscoveryTransporter.d.ts +0 -5
- package/build/src/abstract/DiscoveryTransporter.js +0 -3
- package/build/src/abstract/DiscoveryTransporter.js.map +0 -1
- package/build/src/abstract/PubsubTransporter.d.ts +0 -11
- package/build/src/abstract/PubsubTransporter.js +0 -3
- package/build/src/abstract/PubsubTransporter.js.map +0 -1
- package/build/src/abstract/RemoteService.d.ts +0 -35
- package/build/src/abstract/RemoteService.js +0 -107
- package/build/src/abstract/RemoteService.js.map +0 -1
- package/build/src/abstract/RpcTransporter.d.ts +0 -30
- package/build/src/abstract/RpcTransporter.js +0 -3
- package/build/src/abstract/RpcTransporter.js.map +0 -1
- package/build/src/abstract/SpiderMeshNode.d.ts +0 -18
- package/build/src/abstract/SpiderMeshNode.js +0 -2
- package/build/src/abstract/SpiderMeshNode.js.map +0 -1
- package/build/src/abstracts/DiscoveryTransporter.d.ts +0 -5
- package/build/src/abstracts/DiscoveryTransporter.js +0 -3
- package/build/src/abstracts/DiscoveryTransporter.js.map +0 -1
- package/build/src/abstracts/PubsubTransporter.d.ts +0 -11
- package/build/src/abstracts/PubsubTransporter.js +0 -3
- package/build/src/abstracts/PubsubTransporter.js.map +0 -1
- package/build/src/abstracts/RemoteService.d.ts +0 -38
- package/build/src/abstracts/RemoteService.js +0 -105
- package/build/src/abstracts/RemoteService.js.map +0 -1
- package/build/src/abstracts/RpcTransporter.d.ts +0 -30
- package/build/src/abstracts/RpcTransporter.js +0 -3
- package/build/src/abstracts/RpcTransporter.js.map +0 -1
- package/build/src/abstracts/SpiderMeshNode.d.ts +0 -18
- package/build/src/abstracts/SpiderMeshNode.js +0 -2
- package/build/src/abstracts/SpiderMeshNode.js.map +0 -1
- package/build/src/helpers/MicroserviceNotFound copy.d.ts +0 -5
- package/build/src/helpers/MicroserviceNotFound copy.js +0 -8
- package/build/src/helpers/MicroserviceNotFound copy.js.map +0 -1
- package/build/src/helpers/MicroserviceNotFound.d.ts +0 -5
- package/build/src/helpers/MicroserviceNotFound.js +0 -8
- package/build/src/helpers/MicroserviceNotFound.js.map +0 -1
- package/build/src/helpers/MicroserviceOfflineException.d.ts +0 -5
- package/build/src/helpers/MicroserviceOfflineException.js +0 -8
- package/build/src/helpers/MicroserviceOfflineException.js.map +0 -1
- package/build/src/helpers/MicroserviceRpcTimeout.d.ts +0 -5
- package/build/src/helpers/MicroserviceRpcTimeout.js +0 -8
- package/build/src/helpers/MicroserviceRpcTimeout.js.map +0 -1
- package/build/src/interfaces/DiscoveryTransporter.d.ts +0 -5
- package/build/src/interfaces/DiscoveryTransporter.js +0 -3
- package/build/src/interfaces/DiscoveryTransporter.js.map +0 -1
- package/build/src/interfaces/PubsubTransporter.d.ts +0 -11
- package/build/src/interfaces/PubsubTransporter.js +0 -3
- package/build/src/interfaces/PubsubTransporter.js.map +0 -1
- package/build/src/interfaces/RemoteService.d.ts +0 -35
- package/build/src/interfaces/RemoteService.js +0 -107
- package/build/src/interfaces/RemoteService.js.map +0 -1
- package/build/src/interfaces/RpcTransporter.d.ts +0 -30
- package/build/src/interfaces/RpcTransporter.js +0 -3
- package/build/src/interfaces/RpcTransporter.js.map +0 -1
- package/build/src/interfaces/SpiderMeshNode.d.ts +0 -18
- package/build/src/interfaces/SpiderMeshNode.js +0 -2
- package/build/src/interfaces/SpiderMeshNode.js.map +0 -1
package/README.md
ADDED
|
@@ -0,0 +1,573 @@
|
|
|
1
|
+
# Spider Mesh Core
|
|
2
|
+
|
|
3
|
+
`@spider-mesh/core` is a lightweight TypeScript microservice core for service-to-service RPC, pubsub events, and node discovery with pluggable transporters.
|
|
4
|
+
|
|
5
|
+
It provides:
|
|
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
|
|
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
|
+
If you are reading this package first, the practical rule is simple:
|
|
22
|
+
|
|
23
|
+
- stay in `@spider-mesh/core` for service code and runtime code
|
|
24
|
+
- add a companion transport package only when you explicitly need a concrete transport implementation
|
|
25
|
+
|
|
26
|
+
## Install
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
bun add @spider-mesh/core rxjs reflect-metadata
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
This package is ESM-only.
|
|
33
|
+
|
|
34
|
+
## AI Usage Guide
|
|
35
|
+
|
|
36
|
+
Use this section as the canonical decision guide when generating code with AI tools.
|
|
37
|
+
|
|
38
|
+
### Environment support matrix
|
|
39
|
+
|
|
40
|
+
| Runtime | `@spider-mesh/core` | `@spider-mesh/tcp` | `@spider-mesh/ws` |
|
|
41
|
+
| --- | --- | --- | --- |
|
|
42
|
+
| Node.js | Supported | Supported | Supported |
|
|
43
|
+
| Bun | Supported | Supported | Supported |
|
|
44
|
+
| Browser | Partially supported, depends on your custom transporter | Not recommended | Not recommended |
|
|
45
|
+
| React Native | Supported for the core entry only | Not recommended | Not recommended |
|
|
46
|
+
|
|
47
|
+
### Recommended imports
|
|
48
|
+
|
|
49
|
+
Use these imports exactly.
|
|
50
|
+
|
|
51
|
+
- Core runtime, decorators, RPC linking, NestJS helpers: `import { ... } from '@spider-mesh/core'`
|
|
52
|
+
- Shared transporter contracts and runtime APIs: `import { ... } from '@spider-mesh/core'`
|
|
53
|
+
- Companion TCP transport implementation when needed: `import { Http2Rpc, Http2Pubsub, UdpDiscovery } from '@spider-mesh/tcp'`
|
|
54
|
+
- Companion WebSocket transport implementation when needed: `import { WebsocketTransporter } from '@spider-mesh/ws'`
|
|
55
|
+
|
|
56
|
+
### When to use what
|
|
57
|
+
|
|
58
|
+
- Use `SpiderMesh` when you need a local runtime that can expose services, discover nodes, publish events, or call remote services.
|
|
59
|
+
- Use `@Microservice()` on local classes that should be callable remotely.
|
|
60
|
+
- Use `RemoteServiceLinker.link()` when you need a typed remote proxy for another service.
|
|
61
|
+
- Use transporter contracts from `@spider-mesh/core` when you are implementing or typing your own transport.
|
|
62
|
+
- Use `@spider-mesh/tcp` or `@spider-mesh/ws` when you want a companion transport package.
|
|
63
|
+
- Use a custom transporter when the runtime is not Node.js or Bun, or when you need a different protocol.
|
|
64
|
+
|
|
65
|
+
### Do and don't
|
|
66
|
+
|
|
67
|
+
Do:
|
|
68
|
+
|
|
69
|
+
- Import runtime-agnostic APIs from `@spider-mesh/core`.
|
|
70
|
+
- Import transporter contracts from `@spider-mesh/core` when you need transport typing.
|
|
71
|
+
- Use a companion transport package such as `@spider-mesh/tcp` or `@spider-mesh/ws` when you need a ready-made transport implementation.
|
|
72
|
+
- Wait for remote service availability with `wait()` before making calls in startup flows.
|
|
73
|
+
- Treat the files under `examples/` as canonical usage references.
|
|
74
|
+
|
|
75
|
+
Don't:
|
|
76
|
+
|
|
77
|
+
- Do not assume `@spider-mesh/core` exports every concrete transporter.
|
|
78
|
+
- Do not assume `@spider-mesh/tcp` is React Native-ready.
|
|
79
|
+
- Do not assume `@spider-mesh/ws` is React Native-ready.
|
|
80
|
+
- Do not couple service code to a single transport package unless that is intentional.
|
|
81
|
+
- Do not call remote services before the target service has been discovered unless you intentionally rely on retry behavior.
|
|
82
|
+
|
|
83
|
+
### Canonical recipes
|
|
84
|
+
|
|
85
|
+
Provider recipe:
|
|
86
|
+
|
|
87
|
+
1. Import `Microservice` and `SpiderMesh` from `@spider-mesh/core`.
|
|
88
|
+
2. Choose a concrete transporter implementation, for example from `@spider-mesh/tcp` or `@spider-mesh/ws`.
|
|
89
|
+
3. Define a class and decorate it with `@Microservice()`.
|
|
90
|
+
4. Instantiate the service class.
|
|
91
|
+
5. Create `new SpiderMesh({ transporters: [transporter] })`.
|
|
92
|
+
|
|
93
|
+
Client recipe:
|
|
94
|
+
|
|
95
|
+
1. Import `SpiderMesh` and `RemoteServiceLinker` from `@spider-mesh/core`.
|
|
96
|
+
2. Choose a concrete transporter implementation, for example from `@spider-mesh/tcp` or `@spider-mesh/ws`.
|
|
97
|
+
3. Create `new SpiderMesh({ transporters: [transporter] })`.
|
|
98
|
+
4. Create a typed remote proxy with `RemoteServiceLinker.link()`.
|
|
99
|
+
5. Call `await proxy.wait()` before the first RPC call.
|
|
100
|
+
|
|
101
|
+
Companion transport package recipes:
|
|
102
|
+
|
|
103
|
+
TCP:
|
|
104
|
+
|
|
105
|
+
1. Import `UdpDiscovery`, `Http2Rpc`, and `Http2Pubsub` from `@spider-mesh/tcp`.
|
|
106
|
+
2. Add those transporters to `new SpiderMesh({ transporters: [...] })`.
|
|
107
|
+
3. Let the TCP package handle discovery, RPC, and pubsub transport.
|
|
108
|
+
|
|
109
|
+
WebSocket:
|
|
110
|
+
|
|
111
|
+
1. Import `WebsocketTransporter` from `@spider-mesh/ws`.
|
|
112
|
+
2. Add that transporter to `new SpiderMesh({ transporters: [...] })`.
|
|
113
|
+
3. Run `WebsocketRelayServer` from `@spider-mesh/ws/relay-server` when using the WebSocket companion package.
|
|
114
|
+
|
|
115
|
+
### AI-safe assumptions
|
|
116
|
+
|
|
117
|
+
An AI agent should assume the following unless the codebase says otherwise:
|
|
118
|
+
|
|
119
|
+
- The root package entry is the safe default for shared runtime APIs.
|
|
120
|
+
- Concrete transports are opt-in through companion packages or custom implementations.
|
|
121
|
+
- Transporter contracts come from the core package.
|
|
122
|
+
- The companion TCP and WebSocket packages are sibling transport options for Node.js and Bun runtimes.
|
|
123
|
+
- If the target runtime is React Native, prefer the core APIs and a runtime-appropriate custom transporter.
|
|
124
|
+
|
|
125
|
+
## Core Model
|
|
126
|
+
|
|
127
|
+
`SpiderMesh` is the runtime coordinator.
|
|
128
|
+
|
|
129
|
+
It keeps track of:
|
|
130
|
+
|
|
131
|
+
- The current node metadata
|
|
132
|
+
- Local service instances registered in the process
|
|
133
|
+
- Remote nodes discovered from discovery transporters
|
|
134
|
+
- RPC-capable nodes for each service
|
|
135
|
+
- RPC pending and running stream state
|
|
136
|
+
- Pubsub, RPC, and discovery transporters
|
|
137
|
+
|
|
138
|
+
## Quick Start
|
|
139
|
+
|
|
140
|
+
### 1. Define a local microservice
|
|
141
|
+
|
|
142
|
+
```ts
|
|
143
|
+
import { BeforeMicroserviceOnline, Microservice } from '@spider-mesh/core'
|
|
144
|
+
|
|
145
|
+
@Microservice({ version: '1.0.0' })
|
|
146
|
+
export class UserService {
|
|
147
|
+
private ready = false
|
|
148
|
+
|
|
149
|
+
@BeforeMicroserviceOnline()
|
|
150
|
+
async warmup() {
|
|
151
|
+
this.ready = true
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
async getUser(id: string) {
|
|
155
|
+
if (!this.ready) throw new Error('Service not ready')
|
|
156
|
+
return { id, name: 'Ada' }
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
`@Microservice()` registers the instance into Spider Mesh when the class is constructed.
|
|
162
|
+
|
|
163
|
+
`@BeforeMicroserviceOnline()` marks async setup methods that must complete before the service is exposed in local metadata.
|
|
164
|
+
|
|
165
|
+
### 2. Create the runtime
|
|
166
|
+
|
|
167
|
+
```ts
|
|
168
|
+
import { SpiderMesh } from '@spider-mesh/core'
|
|
169
|
+
import { MdnsDiscoveryTransporter } from './transporters/MdnsDiscoveryTransporter.js'
|
|
170
|
+
import { RedisRpcTransporter } from './transporters/RedisRpcTransporter.js'
|
|
171
|
+
import { RedisPubsubTransporter } from './transporters/RedisPubsubTransporter.js'
|
|
172
|
+
|
|
173
|
+
const mesh = new SpiderMesh({
|
|
174
|
+
transporters: [
|
|
175
|
+
MdnsDiscoveryTransporter,
|
|
176
|
+
RedisRpcTransporter,
|
|
177
|
+
RedisPubsubTransporter,
|
|
178
|
+
],
|
|
179
|
+
})
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
Transporter capability is inferred by method shape:
|
|
183
|
+
|
|
184
|
+
- `send()` means RPC transporter
|
|
185
|
+
- `publish()` means pubsub transporter
|
|
186
|
+
- `broadcast()` means discovery transporter
|
|
187
|
+
|
|
188
|
+
You can pass either transporter classes or already-created transporter instances into `transporters`.
|
|
189
|
+
|
|
190
|
+
### 2a. Use companion transport packages
|
|
191
|
+
|
|
192
|
+
Spider Mesh can work with companion transport packages such as `@spider-mesh/tcp` and `@spider-mesh/ws`.
|
|
193
|
+
|
|
194
|
+
The root package entry exports the runtime-agnostic APIs and shared transporter contracts. Concrete transport implementations can be imported from companion packages when needed.
|
|
195
|
+
|
|
196
|
+
Create a runtime using the TCP package:
|
|
197
|
+
|
|
198
|
+
```ts
|
|
199
|
+
import { SpiderMesh } from '@spider-mesh/core'
|
|
200
|
+
import { Http2Pubsub, Http2Rpc, UdpDiscovery } from '@spider-mesh/tcp'
|
|
201
|
+
|
|
202
|
+
const mesh = new SpiderMesh({
|
|
203
|
+
transporters: [
|
|
204
|
+
new UdpDiscovery(),
|
|
205
|
+
new Http2Rpc(),
|
|
206
|
+
new Http2Pubsub(),
|
|
207
|
+
],
|
|
208
|
+
})
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
Or use the WebSocket package:
|
|
212
|
+
|
|
213
|
+
```ts
|
|
214
|
+
import { SpiderMesh } from '@spider-mesh/core'
|
|
215
|
+
import { WebsocketTransporter } from '@spider-mesh/ws'
|
|
216
|
+
|
|
217
|
+
const transporter = new WebsocketTransporter({
|
|
218
|
+
heartbeatIntervalMs: 5000,
|
|
219
|
+
reconnectIntervalMs: 1000,
|
|
220
|
+
})
|
|
221
|
+
|
|
222
|
+
transporter.connect('ws://127.0.0.1:8787')
|
|
223
|
+
|
|
224
|
+
const mesh = new SpiderMesh({
|
|
225
|
+
transporters: [transporter],
|
|
226
|
+
})
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
Both patterns keep service/runtime code in `@spider-mesh/core` while delegating concrete transport behavior to a companion package.
|
|
230
|
+
|
|
231
|
+
### 2b. Minimal working startup order
|
|
232
|
+
|
|
233
|
+
When using a discovery-based transport package, the canonical startup order is:
|
|
234
|
+
|
|
235
|
+
1. Start one or more provider processes that register local `@Microservice()` classes.
|
|
236
|
+
2. Start client processes.
|
|
237
|
+
3. Wait for discovery to converge.
|
|
238
|
+
4. In clients, call `await remote.wait()` before the first remote method call.
|
|
239
|
+
|
|
240
|
+
If an AI agent needs one default operational pattern, use this startup order.
|
|
241
|
+
|
|
242
|
+
### 3. Call a remote service
|
|
243
|
+
|
|
244
|
+
```ts
|
|
245
|
+
import { RemoteServiceLinker } from '@spider-mesh/core'
|
|
246
|
+
|
|
247
|
+
type UserServiceContract = {
|
|
248
|
+
getUser(id: string): Promise<{ id: string; name: string }>
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
const users = RemoteServiceLinker.link<UserServiceContract>(mesh, {
|
|
252
|
+
service: 'UserService',
|
|
253
|
+
})
|
|
254
|
+
|
|
255
|
+
await users.wait()
|
|
256
|
+
|
|
257
|
+
const user = await users.getUser('42')
|
|
258
|
+
console.log(user.name)
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
Remote methods are exposed through a typed Proxy.
|
|
262
|
+
|
|
263
|
+
## RPC Behavior
|
|
264
|
+
|
|
265
|
+
### Awaitable observable calls
|
|
266
|
+
|
|
267
|
+
Remote method calls return an RxJS observable that is also awaitable.
|
|
268
|
+
|
|
269
|
+
```ts
|
|
270
|
+
const user = await users.getUser('42')
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
```ts
|
|
274
|
+
users.getUser('42').subscribe(user => {
|
|
275
|
+
console.log(user)
|
|
276
|
+
})
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
### Stream-first RPC
|
|
280
|
+
|
|
281
|
+
Spider Mesh treats every RPC response as a stream.
|
|
282
|
+
|
|
283
|
+
- If a local method returns an `Observable`, each emission is forwarded to the caller.
|
|
284
|
+
- If a local method returns a `Promise` or plain value, it is sent as one `data` event with `completed: true`.
|
|
285
|
+
- If the caller unsubscribes early, Spider Mesh sends a `cancel` packet so the remote node can stop the running stream.
|
|
286
|
+
|
|
287
|
+
### Per-link defaults
|
|
288
|
+
|
|
289
|
+
```ts
|
|
290
|
+
const resilientUsers = users.set({
|
|
291
|
+
timeout: 3000,
|
|
292
|
+
retry: 2,
|
|
293
|
+
fallback: { id: 'fallback', name: 'Unknown' },
|
|
294
|
+
})
|
|
295
|
+
|
|
296
|
+
const user = await resilientUsers.getUser('42')
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
Supported RPC options:
|
|
300
|
+
|
|
301
|
+
- `service`: remote service name
|
|
302
|
+
- `method`: remote method name
|
|
303
|
+
- `args`: positional arguments
|
|
304
|
+
- `timeout`: timeout per inactivity window in milliseconds
|
|
305
|
+
- `retry`: retry count for offline errors
|
|
306
|
+
- `fallback`: fallback value when the call fails
|
|
307
|
+
- `node_id`: force routing to a specific node
|
|
308
|
+
- `ip`: force routing to a specific node IP
|
|
309
|
+
|
|
310
|
+
### Waiting and watching
|
|
311
|
+
|
|
312
|
+
```ts
|
|
313
|
+
await users.wait(nodes => nodes.length >= 2)
|
|
314
|
+
|
|
315
|
+
users.watch().subscribe(nodes => {
|
|
316
|
+
console.log(nodes.map(node => node.node_id))
|
|
317
|
+
})
|
|
318
|
+
|
|
319
|
+
console.log(users.nodes)
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
`wait()` resolves when a service availability condition is met.
|
|
323
|
+
|
|
324
|
+
`watch()` streams matching nodes whenever topology changes.
|
|
325
|
+
|
|
326
|
+
`nodes` returns the current RPC-capable node list for that service.
|
|
327
|
+
|
|
328
|
+
### Fan-out calls across all nodes
|
|
329
|
+
|
|
330
|
+
```ts
|
|
331
|
+
users.__batch__getUser('42').subscribe(result => {
|
|
332
|
+
console.log(result)
|
|
333
|
+
})
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
Each emission is either:
|
|
337
|
+
|
|
338
|
+
- `{ node, data }`
|
|
339
|
+
- `{ node, error }`
|
|
340
|
+
|
|
341
|
+
## Events
|
|
342
|
+
|
|
343
|
+
`SpiderMesh.linkEvent()` binds a topic using the event class name.
|
|
344
|
+
|
|
345
|
+
```ts
|
|
346
|
+
class UserCreatedEvent {
|
|
347
|
+
constructor(
|
|
348
|
+
public readonly id: string,
|
|
349
|
+
public readonly email: string,
|
|
350
|
+
) {}
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
const userCreated = mesh.linkEvent(UserCreatedEvent)
|
|
354
|
+
|
|
355
|
+
await userCreated.publish(new UserCreatedEvent('42', 'ada@example.com'))
|
|
356
|
+
|
|
357
|
+
userCreated.listen().subscribe(event => {
|
|
358
|
+
console.log(event.id)
|
|
359
|
+
})
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
## NestJS Integration
|
|
363
|
+
|
|
364
|
+
### Register `SpiderMesh` as a provider
|
|
365
|
+
|
|
366
|
+
```ts
|
|
367
|
+
import { Module } from '@nestjs/common'
|
|
368
|
+
import { SpiderMesh } from '@spider-mesh/core'
|
|
369
|
+
import { MdnsDiscoveryTransporter } from './transporters/MdnsDiscoveryTransporter.js'
|
|
370
|
+
import { RedisRpcTransporter } from './transporters/RedisRpcTransporter.js'
|
|
371
|
+
import { RedisPubsubTransporter } from './transporters/RedisPubsubTransporter.js'
|
|
372
|
+
|
|
373
|
+
@Module({
|
|
374
|
+
providers: [
|
|
375
|
+
SpiderMesh.asProvider({
|
|
376
|
+
transporters: [
|
|
377
|
+
MdnsDiscoveryTransporter,
|
|
378
|
+
RedisRpcTransporter,
|
|
379
|
+
RedisPubsubTransporter,
|
|
380
|
+
],
|
|
381
|
+
}),
|
|
382
|
+
],
|
|
383
|
+
exports: [SpiderMesh],
|
|
384
|
+
})
|
|
385
|
+
export class MeshModule {}
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
### Expose a NestJS service as a microservice
|
|
389
|
+
|
|
390
|
+
```ts
|
|
391
|
+
import { Injectable, Module } from '@nestjs/common'
|
|
392
|
+
import { NestJSExposeMicroservice } from '@spider-mesh/core'
|
|
393
|
+
|
|
394
|
+
@Injectable()
|
|
395
|
+
export class BillingService {
|
|
396
|
+
async charge(orderId: string) {
|
|
397
|
+
return { orderId, status: 'ok' }
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
@Module({
|
|
402
|
+
providers: [
|
|
403
|
+
BillingService,
|
|
404
|
+
NestJSExposeMicroservice(BillingService, { boundedContext: 'billing' }),
|
|
405
|
+
],
|
|
406
|
+
})
|
|
407
|
+
export class BillingModule {}
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
### Inject a remote service proxy in NestJS
|
|
411
|
+
|
|
412
|
+
```ts
|
|
413
|
+
import { Inject, Injectable, Module } from '@nestjs/common'
|
|
414
|
+
import { NestJSLinkMicroservice } from '@spider-mesh/core'
|
|
415
|
+
|
|
416
|
+
class BillingService {
|
|
417
|
+
charge(orderId: string): Promise<{ orderId: string; status: string }> {
|
|
418
|
+
throw new Error('typing only')
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
@Injectable()
|
|
423
|
+
export class CheckoutService {
|
|
424
|
+
constructor(
|
|
425
|
+
@Inject(BillingService)
|
|
426
|
+
private readonly billing: BillingService,
|
|
427
|
+
) {}
|
|
428
|
+
|
|
429
|
+
async checkout(orderId: string) {
|
|
430
|
+
return this.billing.charge(orderId)
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
@Module({
|
|
435
|
+
providers: [
|
|
436
|
+
NestJSLinkMicroservice(BillingService),
|
|
437
|
+
CheckoutService,
|
|
438
|
+
],
|
|
439
|
+
})
|
|
440
|
+
export class CheckoutModule {}
|
|
441
|
+
```
|
|
442
|
+
|
|
443
|
+
### Inject an event binding in NestJS
|
|
444
|
+
|
|
445
|
+
```ts
|
|
446
|
+
import { Inject, Injectable, Module } from '@nestjs/common'
|
|
447
|
+
import { NestJSLinkEvent } from '@spider-mesh/core'
|
|
448
|
+
|
|
449
|
+
class UserCreatedEvent {
|
|
450
|
+
constructor(public readonly id: string) {}
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
@Injectable()
|
|
454
|
+
export class AuditService {
|
|
455
|
+
constructor(
|
|
456
|
+
@Inject(UserCreatedEvent)
|
|
457
|
+
private readonly userCreated: {
|
|
458
|
+
publish(data: UserCreatedEvent): Promise<void>
|
|
459
|
+
listen(): any
|
|
460
|
+
},
|
|
461
|
+
) {}
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
@Module({
|
|
465
|
+
providers: [
|
|
466
|
+
NestJSLinkEvent(UserCreatedEvent),
|
|
467
|
+
AuditService,
|
|
468
|
+
],
|
|
469
|
+
})
|
|
470
|
+
export class AuditModule {}
|
|
471
|
+
```
|
|
472
|
+
|
|
473
|
+
## Transporter Contract
|
|
474
|
+
|
|
475
|
+
Concrete transport implementations can live in companion packages such as `@spider-mesh/tcp` and `@spider-mesh/ws`, or in your own application code.
|
|
476
|
+
|
|
477
|
+
You can also provide your own classes that implement one or more transporter contracts exported by `@spider-mesh/core`.
|
|
478
|
+
|
|
479
|
+
### Public API map
|
|
480
|
+
|
|
481
|
+
- `SpiderMesh`: runtime coordinator for services, events, discovery, and transporters
|
|
482
|
+
- `RemoteServiceLinker.link()`: creates a typed remote proxy
|
|
483
|
+
- `@Microservice()`: exposes a local class instance as a remote service
|
|
484
|
+
- `SpiderMesh.linkEvent()`: creates a topic binding for publish and subscribe
|
|
485
|
+
- `RpcTransporter`: RPC transporter contract
|
|
486
|
+
- `DiscoveryTransporter`: discovery transporter contract
|
|
487
|
+
- `PubsubTransporter`: pubsub transporter contract
|
|
488
|
+
|
|
489
|
+
### RPC transporter
|
|
490
|
+
|
|
491
|
+
```ts
|
|
492
|
+
type RpcTransporter = Observable<RpcEvent> & {
|
|
493
|
+
send(data: RpcPacket, node: SpiderMeshNode): Promise<void>
|
|
494
|
+
}
|
|
495
|
+
```
|
|
496
|
+
|
|
497
|
+
The RPC observable can emit:
|
|
498
|
+
|
|
499
|
+
- `rpc`: inbound RPC packet with the source node attached
|
|
500
|
+
- `offline`: node offline event
|
|
501
|
+
- `endpoints`: transporter metadata to attach to the current node
|
|
502
|
+
|
|
503
|
+
The internal RPC wire protocol supports:
|
|
504
|
+
|
|
505
|
+
- `request`
|
|
506
|
+
- `response`
|
|
507
|
+
- `cancel`
|
|
508
|
+
|
|
509
|
+
`response` packets can carry:
|
|
510
|
+
|
|
511
|
+
- `data`
|
|
512
|
+
- `error`
|
|
513
|
+
- `completed`
|
|
514
|
+
|
|
515
|
+
### Discovery transporter
|
|
516
|
+
|
|
517
|
+
```ts
|
|
518
|
+
type DiscoveryTransporter = Observable<DiscoveryEvent> & {
|
|
519
|
+
broadcast(
|
|
520
|
+
data: MdnsMessage<NodeMetadata>,
|
|
521
|
+
ips: string[],
|
|
522
|
+
): Promise<void>
|
|
523
|
+
}
|
|
524
|
+
```
|
|
525
|
+
|
|
526
|
+
Discovery transporters stream remote node snapshots into the mesh, and `SpiderMesh` itself calls `broadcast()` whenever local node metadata changes.
|
|
527
|
+
|
|
528
|
+
### Pubsub transporter
|
|
529
|
+
|
|
530
|
+
```ts
|
|
531
|
+
type PubsubTransporter = {
|
|
532
|
+
publish<T>(topic: string, data: T): Promise<void>
|
|
533
|
+
listen<T>(topic: string): Observable<T>
|
|
534
|
+
}
|
|
535
|
+
```
|
|
536
|
+
|
|
537
|
+
## Error Model
|
|
538
|
+
|
|
539
|
+
The core defines these error codes for RPC flows:
|
|
540
|
+
|
|
541
|
+
- `MICROSERVICE_OFFLINE`
|
|
542
|
+
- `MICROSERVICE_NOT_FOUND`
|
|
543
|
+
- `MICROSERVICE_RPC_TIMEOUT`
|
|
544
|
+
|
|
545
|
+
## Environment Variables
|
|
546
|
+
|
|
547
|
+
- `SPIDERMESH_NAMESPACE`: namespace of the current node, default `default`
|
|
548
|
+
- `SPIDERMESH_NODE_HOSTNAME`: optional hostname attached to node metadata
|
|
549
|
+
|
|
550
|
+
## Helpers
|
|
551
|
+
|
|
552
|
+
The package also exports:
|
|
553
|
+
|
|
554
|
+
- `LimitConcurrency(limit)` and `LimitConcurrentRunning(limit)` for throttling async method execution
|
|
555
|
+
- `randomUUID()` for Node.js, browser, and React Native compatible UUID generation
|
|
556
|
+
- `MicroserviceException` types for common RPC error codes
|
|
557
|
+
|
|
558
|
+
## Build
|
|
559
|
+
|
|
560
|
+
```bash
|
|
561
|
+
bun run build
|
|
562
|
+
```
|
|
563
|
+
|
|
564
|
+
## Notes
|
|
565
|
+
|
|
566
|
+
- Local services are registered when their class instances are constructed.
|
|
567
|
+
- Service identity is based on the class name.
|
|
568
|
+
- Event topic identity is based on the event class name.
|
|
569
|
+
- RPC target selection is round-robin unless you force `node_id` or `ip`.
|
|
570
|
+
- `SpiderMesh` owns RPC stream lifecycle, timeout, retry, and cancel behavior.
|
|
571
|
+
- Transporters focus on byte transport, pubsub topic IO, and discovery broadcasts.
|
|
572
|
+
- The root package entry intentionally focuses on runtime-agnostic APIs and shared contracts.
|
|
573
|
+
- If an AI agent is uncertain which import to use, prefer `@spider-mesh/core` first, then opt into a companion transport package such as `@spider-mesh/tcp` or `@spider-mesh/ws` only when a concrete transport is needed.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { RpcOptions, SpiderMeshNode } from "@spider-mesh/types";
|
|
2
1
|
import { Observable } from "rxjs";
|
|
3
2
|
import { ServiceChecker, SpiderMesh } from "./SpiderMesh.js";
|
|
3
|
+
import { RpcOptions, SpiderMeshNode } from "./types.js";
|
|
4
4
|
export type IsUnknown<T, A, B> = unknown extends T ? ([T] extends [unknown] ? A : B) : B;
|
|
5
5
|
export type RemoteServiceOptions = Partial<RpcOptions<any>> & {
|
|
6
6
|
service: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RemoteService.js","sourceRoot":"","sources":["../../src/RemoteService.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"RemoteService.js","sourceRoot":"","sources":["../../src/RemoteService.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Observable } from "rxjs";
|
|
2
|
-
import { SpiderMeshNode, RpcTransporter, PubsubTransporter, DiscoveryTransporter, RpcOptions } from '
|
|
2
|
+
import { SpiderMeshNode, RpcTransporter, PubsubTransporter, DiscoveryTransporter, RpcOptions } from './types.js';
|
|
3
3
|
export type HelloEvent = SpiderMeshNode & {
|
|
4
4
|
back?: boolean;
|
|
5
5
|
};
|
|
@@ -11,7 +11,7 @@ export type NodesMap = {
|
|
|
11
11
|
export type SpiderMeshOptions = {
|
|
12
12
|
transporters: Array<{
|
|
13
13
|
new (): RpcTransporter | PubsubTransporter | DiscoveryTransporter;
|
|
14
|
-
}>;
|
|
14
|
+
} | RpcTransporter | PubsubTransporter | DiscoveryTransporter>;
|
|
15
15
|
};
|
|
16
16
|
export declare class SpiderMesh {
|
|
17
17
|
#private;
|