service-bridge 1.0.9 → 1.0.10-dev.23
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 +58 -51
- package/dist/index.js +709 -33064
- package/package.json +13 -2
- package/biome.json +0 -28
- package/bun.lock +0 -249
- package/http/dist/express.d.ts +0 -51
- package/http/dist/express.d.ts.map +0 -1
- package/http/dist/express.test.d.ts +0 -2
- package/http/dist/express.test.d.ts.map +0 -1
- package/http/dist/fastify.d.ts +0 -43
- package/http/dist/fastify.d.ts.map +0 -1
- package/http/dist/fastify.test.d.ts +0 -2
- package/http/dist/fastify.test.d.ts.map +0 -1
- package/http/dist/index.d.ts +0 -7
- package/http/dist/index.d.ts.map +0 -1
- package/http/dist/trace.d.ts +0 -19
- package/http/dist/trace.d.ts.map +0 -1
- package/http/dist/trace.test.d.ts +0 -2
- package/http/dist/trace.test.d.ts.map +0 -1
- package/http/package.json +0 -49
- package/http/src/express.test.ts +0 -125
- package/http/src/express.ts +0 -209
- package/http/src/fastify.test.ts +0 -142
- package/http/src/fastify.ts +0 -159
- package/http/src/index.ts +0 -10
- package/http/src/sdk-augment.d.ts +0 -11
- package/http/src/servicebridge.d.ts +0 -23
- package/http/src/trace.test.ts +0 -97
- package/http/src/trace.ts +0 -56
- package/http/tsconfig.json +0 -17
- package/http/tsconfig.test.json +0 -6
- package/sdk/dist/generated/servicebridge-package-definition.d.ts +0 -1
- package/sdk/dist/grpc-client.d.ts +0 -344
- package/sdk/dist/grpc-client.test.d.ts +0 -1
- package/sdk/dist/index.d.ts +0 -2
- package/sdk/package.json +0 -31
- package/sdk/scripts/generate-proto.ts +0 -34
- package/sdk/src/generated/servicebridge-package-definition.ts +0 -4
- package/sdk/src/grpc-client.d.ts +0 -332
- package/sdk/src/grpc-client.d.ts.map +0 -1
- package/sdk/src/grpc-client.test.ts +0 -422
- package/sdk/src/grpc-client.ts +0 -3101
- package/sdk/src/index.d.ts +0 -3
- package/sdk/src/index.d.ts.map +0 -1
- package/sdk/src/index.ts +0 -31
- package/sdk/tsconfig.json +0 -13
package/README.md
CHANGED
|
@@ -95,16 +95,15 @@ bun add service-bridge
|
|
|
95
95
|
import { servicebridge } from "service-bridge";
|
|
96
96
|
|
|
97
97
|
const sb = servicebridge(
|
|
98
|
-
process.env.SERVICEBRIDGE_URL ?? "
|
|
98
|
+
process.env.SERVICEBRIDGE_URL ?? "localhost:14445",
|
|
99
99
|
process.env.SERVICEBRIDGE_SERVICE_KEY!,
|
|
100
|
-
"payments",
|
|
101
100
|
);
|
|
102
101
|
|
|
103
102
|
sb.handleRpc("charge", async (payload: { orderId: string; amount: number }) => {
|
|
104
103
|
return { ok: true, txId: `tx_${Date.now()}`, orderId: payload.orderId };
|
|
105
104
|
});
|
|
106
105
|
|
|
107
|
-
await sb.serve({ host: "
|
|
106
|
+
await sb.serve({ host: "localhost" });
|
|
108
107
|
```
|
|
109
108
|
|
|
110
109
|
### 3. Call it from another service
|
|
@@ -113,9 +112,8 @@ await sb.serve({ host: "0.0.0.0" });
|
|
|
113
112
|
import { servicebridge } from "service-bridge";
|
|
114
113
|
|
|
115
114
|
const sb = servicebridge(
|
|
116
|
-
process.env.SERVICEBRIDGE_URL ?? "
|
|
115
|
+
process.env.SERVICEBRIDGE_URL ?? "localhost:14445",
|
|
117
116
|
process.env.SERVICEBRIDGE_SERVICE_KEY!,
|
|
118
|
-
"orders",
|
|
119
117
|
);
|
|
120
118
|
|
|
121
119
|
const result = await sb.rpc<{ ok: boolean; txId: string }>("payments/charge", {
|
|
@@ -138,7 +136,7 @@ The SDK connects to a ServiceBridge runtime. The fastest way to start:
|
|
|
138
136
|
bash <(curl -fsSL https://servicebridge.dev/install.sh)
|
|
139
137
|
```
|
|
140
138
|
|
|
141
|
-
This installs ServiceBridge + PostgreSQL via Docker Compose and generates an admin password automatically. After install, the dashboard is at `http://localhost:14444` and the gRPC control plane at `
|
|
139
|
+
This installs ServiceBridge + PostgreSQL via Docker Compose and generates an admin password automatically. After install, the dashboard is at `http://localhost:14444` and the gRPC control plane at `localhost:14445`.
|
|
142
140
|
|
|
143
141
|
For manual Docker Compose setup, configuration reference, and all runtime environment variables, see the **[Runtime Setup](../README.md#runtime-setup)** section in the main SDK README.
|
|
144
142
|
|
|
@@ -153,7 +151,7 @@ import { servicebridge } from "service-bridge";
|
|
|
153
151
|
|
|
154
152
|
// --- Payments service (worker) ---
|
|
155
153
|
|
|
156
|
-
const payments = servicebridge("
|
|
154
|
+
const payments = servicebridge("localhost:14445", process.env.SERVICEBRIDGE_SERVICE_KEY!);
|
|
157
155
|
|
|
158
156
|
payments.handleRpc("charge", async (payload: { orderId: string; amount: number }, ctx) => {
|
|
159
157
|
await ctx?.stream.write({ status: "charging", orderId: payload.orderId }, "progress");
|
|
@@ -164,13 +162,13 @@ payments.handleRpc("charge", async (payload: { orderId: string; amount: number }
|
|
|
164
162
|
return { ok: true, txId: `tx_${Date.now()}` };
|
|
165
163
|
});
|
|
166
164
|
|
|
167
|
-
await payments.serve({ host: "
|
|
165
|
+
await payments.serve({ host: "localhost" });
|
|
168
166
|
```
|
|
169
167
|
|
|
170
168
|
```ts
|
|
171
169
|
// --- Orders service (caller + event publisher) ---
|
|
172
170
|
|
|
173
|
-
const orders = servicebridge("
|
|
171
|
+
const orders = servicebridge("localhost:14445", process.env.SERVICEBRIDGE_SERVICE_KEY!);
|
|
174
172
|
|
|
175
173
|
// Call payments, then publish event
|
|
176
174
|
const charge = await orders.rpc<{ ok: boolean; txId: string }>("payments/charge", {
|
|
@@ -190,7 +188,7 @@ await orders.event("orders.completed", {
|
|
|
190
188
|
```ts
|
|
191
189
|
// --- Notifications service (event consumer) ---
|
|
192
190
|
|
|
193
|
-
const notifications = servicebridge("
|
|
191
|
+
const notifications = servicebridge("localhost:14445", process.env.SERVICEBRIDGE_SERVICE_KEY!);
|
|
194
192
|
|
|
195
193
|
notifications.handleEvent("orders.*", async (payload, ctx) => {
|
|
196
194
|
const body = payload as { orderId: string; txId: string };
|
|
@@ -198,7 +196,7 @@ notifications.handleEvent("orders.*", async (payload, ctx) => {
|
|
|
198
196
|
// ... send email ...
|
|
199
197
|
});
|
|
200
198
|
|
|
201
|
-
await notifications.serve({ host: "
|
|
199
|
+
await notifications.serve({ host: "localhost" });
|
|
202
200
|
```
|
|
203
201
|
|
|
204
202
|
```ts
|
|
@@ -230,7 +228,7 @@ Every step above — RPC, event publish, event delivery, workflow execution —
|
|
|
230
228
|
- **Jobs** — cron, delayed, and workflow-triggered scheduling
|
|
231
229
|
|
|
232
230
|
### Security
|
|
233
|
-
- **
|
|
231
|
+
- **TLS by default** — control plane TLS + worker mTLS with gRPC certificate provisioning
|
|
234
232
|
- **Access Policy** — service-level caller/target restrictions and RBAC
|
|
235
233
|
|
|
236
234
|
### Observability
|
|
@@ -267,44 +265,46 @@ ServiceBridge keeps the core API shape consistent across Node.js, Go, and Python
|
|
|
267
265
|
constructor, RPC, events, jobs, workflows, `runWorkflow`, streams, serve/stop, and `ServiceBridgeError`.
|
|
268
266
|
|
|
269
267
|
Constructor-level defaults for `timeout`, `retries`, and `retryDelay` are available
|
|
270
|
-
across all three SDKs.
|
|
268
|
+
across all three SDKs. Parity differences are naming-only (language idioms):
|
|
271
269
|
|
|
272
|
-
-
|
|
273
|
-
-
|
|
274
|
-
-
|
|
270
|
+
- Constructor TLS overrides: `workerTLS`/`caCert` (Node), `WorkerTLS`/`CACert` (Go), `worker_tls`/`ca_cert` (Python)
|
|
271
|
+
- Handler hints: timeout/retryable/concurrency/prefetch are advisory in all SDKs
|
|
272
|
+
- Shared `serve()` fields across SDKs: host, max in-flight, instance ID, weight, and per-serve TLS override
|
|
275
273
|
|
|
276
|
-
### `servicebridge(url, serviceKey,
|
|
274
|
+
### `servicebridge(url, serviceKey, opts?)`
|
|
277
275
|
|
|
278
276
|
```ts
|
|
279
277
|
function servicebridge(
|
|
280
278
|
url: string,
|
|
281
279
|
serviceKey: string,
|
|
282
|
-
|
|
283
|
-
|
|
280
|
+
serviceOrOpts?: string | ServiceBridgeOpts,
|
|
281
|
+
maybeGlobalOpts?: ServiceBridgeOpts,
|
|
284
282
|
): ServiceBridgeService
|
|
285
283
|
```
|
|
286
284
|
|
|
287
285
|
Creates an SDK client instance.
|
|
286
|
+
Service identity is resolved by the runtime from `serviceKey`; passing a third `service` argument is legacy-only.
|
|
288
287
|
|
|
289
288
|
`ServiceBridgeOpts`:
|
|
290
289
|
|
|
291
290
|
| Option | Type | Default | Description |
|
|
292
291
|
|---|---|---|---|
|
|
293
|
-
| `timeout` | `number` | `30000` | Default timeout (ms)
|
|
292
|
+
| `timeout` | `number` | `30000` | Default hard timeout per RPC attempt (ms). |
|
|
294
293
|
| `retries` | `number` | `3` | Default retry count for `rpc()`. |
|
|
295
294
|
| `retryDelay` | `number` | `300` | Base backoff delay (ms) for `rpc()`. |
|
|
296
295
|
| `discoveryRefreshMs` | `number` | `10000` | Discovery refresh period for endpoint updates. |
|
|
297
296
|
| `queueMaxSize` | `number` | `1000` | Max offline queue size for control-plane writes. |
|
|
298
297
|
| `queueOverflow` | `"drop-oldest" \| "drop-newest" \| "error"` | `"drop-oldest"` | Overflow strategy for offline queue. |
|
|
299
298
|
| `heartbeatIntervalMs` | `number` | `10000` | Base heartbeat period for worker registrations. |
|
|
300
|
-
| `workerTransport` | `"tls"` | `"tls"` | Worker server transport. |
|
|
301
|
-
| `workerTLS` | `WorkerTLSOpts` | auto | Explicit cert/key/CA for worker mTLS. |
|
|
302
|
-
| `adminUrl` | `string` | derived from `url` | HTTP admin base URL (TLS provisioning and management API calls). |
|
|
303
|
-
| `adminSessionCookie` | `string` | `undefined` | Admin session cookie for browser-authenticated endpoints (e.g. `cancelWorkflowRun`). |
|
|
304
|
-
| `adminCsrfToken` | `string` | `undefined` | CSRF token paired with `adminSessionCookie` for unsafe HTTP methods. |
|
|
305
|
-
| `adminOrigin` | `string` | `undefined` | Origin header required by admin CSRF/origin checks. |
|
|
306
299
|
| `captureLogs` | `boolean` | `true` | Forward `console.*` logs to ServiceBridge. |
|
|
307
300
|
|
|
301
|
+
### Advanced TLS overrides
|
|
302
|
+
|
|
303
|
+
| Option | Type | Default | Description |
|
|
304
|
+
|---|---|---|---|
|
|
305
|
+
| `workerTLS` | `WorkerTLSOpts` | auto | Explicit cert/key/CA for worker mTLS. |
|
|
306
|
+
| `caCert` | `string \| Buffer` | from `serviceKey` | Optional control-plane CA override. By default SDK reads CA from sbv2 service key. |
|
|
307
|
+
|
|
308
308
|
`WorkerTLSOpts`:
|
|
309
309
|
|
|
310
310
|
```ts
|
|
@@ -343,6 +343,10 @@ const user = await sb.rpc<{ id: string; name: string }>("users/get", { id: "u_1"
|
|
|
343
343
|
});
|
|
344
344
|
```
|
|
345
345
|
|
|
346
|
+
`rpc()` is bounded even when a downstream worker is silent:
|
|
347
|
+
each attempt has a hard local timeout, retries are finite (`retries + 1` total attempts),
|
|
348
|
+
and after the final failed attempt the root RPC span is closed with `error`.
|
|
349
|
+
|
|
346
350
|
---
|
|
347
351
|
|
|
348
352
|
### `event(topic, payload?, opts?)`
|
|
@@ -503,9 +507,9 @@ Registers an RPC handler. Chainable.
|
|
|
503
507
|
|
|
504
508
|
| Option | Type | Description |
|
|
505
509
|
|---|---|---|
|
|
506
|
-
| `timeout` | `number` |
|
|
507
|
-
| `retryable` | `boolean` |
|
|
508
|
-
| `concurrency` | `number` |
|
|
510
|
+
| `timeout` | `number` | Advisory timeout hint (currently metadata-level, not hard-enforced by runtime). |
|
|
511
|
+
| `retryable` | `boolean` | Advisory retry hint (currently metadata-level, not a strict policy switch). |
|
|
512
|
+
| `concurrency` | `number` | Advisory concurrency hint (currently not hard-enforced). |
|
|
509
513
|
| `schema` | `RpcSchemaOpts` | Inline protobuf schema for binary encode/decode. |
|
|
510
514
|
| `allowedCallers` | `string[]` | Allow-list of caller service names. |
|
|
511
515
|
|
|
@@ -535,9 +539,9 @@ Registers an event consumer handler. Chainable.
|
|
|
535
539
|
|
|
536
540
|
| Option | Type | Description |
|
|
537
541
|
|---|---|---|
|
|
538
|
-
| `groupName` | `string` | Consumer group name. Default: `<service>.<pattern>`. |
|
|
539
|
-
| `concurrency` | `number` |
|
|
540
|
-
| `prefetch` | `number` |
|
|
542
|
+
| `groupName` | `string` | Consumer group name. Default: `<service-key-id>.<pattern>`. |
|
|
543
|
+
| `concurrency` | `number` | Advisory concurrency hint (currently not hard-enforced). |
|
|
544
|
+
| `prefetch` | `number` | Advisory prefetch hint (currently not hard-enforced). |
|
|
541
545
|
| `retryPolicyJson` | `string` | Retry policy JSON string. |
|
|
542
546
|
| `filterExpr` | `string` | Server-side filter expression. |
|
|
543
547
|
|
|
@@ -577,15 +581,15 @@ the Node.js process).
|
|
|
577
581
|
|
|
578
582
|
| Option | Type | Description |
|
|
579
583
|
|---|---|---|
|
|
580
|
-
| `host` | `string` | Bind host. Default: `0.0.0.0
|
|
581
|
-
| `
|
|
582
|
-
| `
|
|
583
|
-
| `
|
|
584
|
-
| `tls` | `WorkerTLSOpts` | Per-serve TLS override
|
|
584
|
+
| `host` | `string` | Bind host. Default: `localhost`. Use `0.0.0.0` in Docker/Kubernetes so ServiceBridge can reach the worker. |
|
|
585
|
+
| `maxInFlight` | `number` | Max in-flight runtime-originated commands over `OpenWorkerSession`. Default: `128`. |
|
|
586
|
+
| `instanceId` | `string` | Stable worker instance identifier. |
|
|
587
|
+
| `weight` | `number` | Scheduling/discovery weight hint. |
|
|
588
|
+
| `tls` | `WorkerTLSOpts` | Per-serve worker TLS override. |
|
|
585
589
|
|
|
586
590
|
```ts
|
|
587
591
|
await sb.serve({
|
|
588
|
-
host: "
|
|
592
|
+
host: "localhost",
|
|
589
593
|
instanceId: process.env.HOSTNAME,
|
|
590
594
|
});
|
|
591
595
|
```
|
|
@@ -693,6 +697,13 @@ identifier used by `ctx.stream.write(...)` (typically a trace ID).
|
|
|
693
697
|
| `data` | `unknown` | JSON-decoded chunk payload. |
|
|
694
698
|
| `runStatus` | `string \| undefined` | Final status on `run_complete`. |
|
|
695
699
|
|
|
700
|
+
Behavior:
|
|
701
|
+
|
|
702
|
+
- Auto-reconnect with exponential backoff (`500ms` → `5000ms`) on retryable stream failures.
|
|
703
|
+
- Deduplicates by `sequence` across reconnects.
|
|
704
|
+
- Enforces strict JSON for `type="chunk"` payloads (non-JSON chunk terminates stream with fatal error).
|
|
705
|
+
- Enforces internal queue limit `256`; overflow is fatal (consumer must drain promptly).
|
|
706
|
+
|
|
696
707
|
```ts
|
|
697
708
|
for await (const evt of sb.watchRun(runId, { key: "output", fromSequence: 0 })) {
|
|
698
709
|
if (evt.type === "chunk") {
|
|
@@ -754,7 +765,7 @@ import express from "express";
|
|
|
754
765
|
import { servicebridge } from "service-bridge";
|
|
755
766
|
import { servicebridgeMiddleware, registerExpressRoutes } from "service-bridge/express";
|
|
756
767
|
|
|
757
|
-
const sb = servicebridge(process.env.SERVICEBRIDGE_URL!, process.env.SERVICEBRIDGE_SERVICE_KEY
|
|
768
|
+
const sb = servicebridge(process.env.SERVICEBRIDGE_URL!, process.env.SERVICEBRIDGE_SERVICE_KEY!);
|
|
758
769
|
const app = express();
|
|
759
770
|
|
|
760
771
|
app.use(servicebridgeMiddleware({
|
|
@@ -810,7 +821,7 @@ import Fastify from "fastify";
|
|
|
810
821
|
import { servicebridge } from "service-bridge";
|
|
811
822
|
import { servicebridgePlugin, wrapHandler } from "service-bridge/fastify";
|
|
812
823
|
|
|
813
|
-
const sb = servicebridge(process.env.SERVICEBRIDGE_URL!, process.env.SERVICEBRIDGE_SERVICE_KEY
|
|
824
|
+
const sb = servicebridge(process.env.SERVICEBRIDGE_URL!, process.env.SERVICEBRIDGE_SERVICE_KEY!);
|
|
814
825
|
const app = Fastify();
|
|
815
826
|
|
|
816
827
|
await app.register(servicebridgePlugin, {
|
|
@@ -859,7 +870,9 @@ Runs a Fastify handler inside the current trace context so downstream SDK calls
|
|
|
859
870
|
### TLS behavior
|
|
860
871
|
|
|
861
872
|
- Worker transport is TLS-only.
|
|
862
|
-
-
|
|
873
|
+
- Control plane is TLS-only. Trust source is embedded into sbv2 service key by default.
|
|
874
|
+
- Embedded/explicit CA PEM is validated with strict x509 parsing.
|
|
875
|
+
- If `workerTLS` is not provided, SDK auto-provisions worker certs via gRPC `ProvisionWorkerCertificate`.
|
|
863
876
|
- `workerTLS.cert` and `workerTLS.key` must be provided together.
|
|
864
877
|
- `serve({ tls })` overrides global `workerTLS` for a specific worker instance.
|
|
865
878
|
|
|
@@ -879,19 +892,13 @@ The SDK requires values you pass into `servicebridge(...)`. Common setup:
|
|
|
879
892
|
|
|
880
893
|
| Variable | Required | Example | Description |
|
|
881
894
|
|---|---|---|---|
|
|
882
|
-
| `SERVICEBRIDGE_URL` | yes | `
|
|
883
|
-
| `SERVICEBRIDGE_SERVICE_KEY` | yes | `
|
|
884
|
-
| `SERVICEBRIDGE_SERVICE` | yes (worker mode) | `orders` | Service name in registry |
|
|
885
|
-
| `SERVICEBRIDGE_ADMIN_URL` | optional | `http://0.0.0.0:14444` | Explicit admin API base URL |
|
|
895
|
+
| `SERVICEBRIDGE_URL` | yes | `localhost:14445` | gRPC control plane URL |
|
|
896
|
+
| `SERVICEBRIDGE_SERVICE_KEY` | yes | `sbv2.<id>.<secret>.<ca>` | Service authentication key (sbv2 only) |
|
|
886
897
|
|
|
887
898
|
```ts
|
|
888
899
|
const sb = servicebridge(
|
|
889
|
-
process.env.SERVICEBRIDGE_URL ?? "
|
|
900
|
+
process.env.SERVICEBRIDGE_URL ?? "localhost:14445",
|
|
890
901
|
process.env.SERVICEBRIDGE_SERVICE_KEY!,
|
|
891
|
-
process.env.SERVICEBRIDGE_SERVICE ?? "orders",
|
|
892
|
-
{
|
|
893
|
-
adminUrl: process.env.SERVICEBRIDGE_ADMIN_URL,
|
|
894
|
-
},
|
|
895
902
|
);
|
|
896
903
|
```
|
|
897
904
|
|
|
@@ -949,7 +956,7 @@ try {
|
|
|
949
956
|
## FAQ
|
|
950
957
|
|
|
951
958
|
**How does ServiceBridge handle service failures?**
|
|
952
|
-
RPC calls have configurable retries with exponential backoff. Events are durable (PostgreSQL-backed) with at-least-once delivery per consumer group. Failed deliveries are retried according to policy, then moved to DLQ. Workflows track step state and can be resumed.
|
|
959
|
+
RPC calls have configurable retries with exponential backoff and hard per-attempt timeouts, so a silent downstream service cannot keep a call pending forever. Events are durable (PostgreSQL-backed) with at-least-once delivery per consumer group. Failed deliveries are retried according to policy, then moved to DLQ. Workflows track step state and can be resumed.
|
|
953
960
|
|
|
954
961
|
**Is there vendor lock-in?**
|
|
955
962
|
ServiceBridge is self-hosted. The runtime is a single Go binary + PostgreSQL. SDK calls map to standard patterns (RPC, pub/sub, cron) — migrating away means replacing SDK calls with equivalent library calls.
|