service-bridge 1.6.0-dev.33 → 1.8.0-dev.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 +54 -31
- package/dist/express.js +2 -2
- package/dist/fastify.js +2 -2
- package/dist/index.js +203 -87
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -73,7 +73,7 @@ Node.js SDK for [ServiceBridge](https://servicebridge.dev) — production-ready
|
|
|
73
73
|
|
|
74
74
|
**Saga / distributed transactions** — DAG workflows with typed steps (`rpc`, `event`, `event_wait`, `sleep`, child workflow). Compensations and rollbacks via workflow step dependencies.
|
|
75
75
|
|
|
76
|
-
**AI agent orchestration** — Stream LLM tokens via realtime
|
|
76
|
+
**AI agent orchestration** — Stream LLM tokens via realtime trace streams with replay. Orchestrate multi-step AI pipelines as workflows.
|
|
77
77
|
|
|
78
78
|
**Full-stack observability** — Every RPC call, event delivery, workflow step, and HTTP request traced automatically. One timeline, one dashboard. Prometheus metrics and Loki-compatible log API included.
|
|
79
79
|
|
|
@@ -236,7 +236,7 @@ Every step above — RPC, event publish, event delivery, workflow execution —
|
|
|
236
236
|
- **Metrics** — Prometheus-compatible `/metrics` endpoint (30+ metric families)
|
|
237
237
|
- **Logs** — structured log ingest with Loki-compatible query API
|
|
238
238
|
- **Alerts** — runtime alerts for delivery failures, errors, and service health
|
|
239
|
-
- **Dashboard** — realtime web UI for
|
|
239
|
+
- **Dashboard** — realtime web UI for traces, events, workflows, jobs, DLQ, service map, and service keys
|
|
240
240
|
|
|
241
241
|
---
|
|
242
242
|
|
|
@@ -262,7 +262,7 @@ Every step above — RPC, event publish, event delivery, workflow execution —
|
|
|
262
262
|
### Cross-SDK parity notes
|
|
263
263
|
|
|
264
264
|
ServiceBridge keeps the core API shape consistent across Node.js, Go, and Python:
|
|
265
|
-
constructor, RPC, events, jobs, workflows, `
|
|
265
|
+
constructor, RPC, events, jobs, workflows, `executeWorkflow`, streams, serve/stop, and `ServiceBridgeError`.
|
|
266
266
|
|
|
267
267
|
Constructor-level defaults for `timeout`, `retries`, and `retryDelay` are available
|
|
268
268
|
across all three SDKs. Parity differences are naming-only (language idioms):
|
|
@@ -326,6 +326,15 @@ rpc<T = unknown>(fn: string, payload?: unknown, opts?: RpcOpts): Promise<T>
|
|
|
326
326
|
|
|
327
327
|
Calls a registered RPC handler on another service. Direct gRPC path, no proxy.
|
|
328
328
|
|
|
329
|
+
**Function name formats** — `fn` accepts two formats:
|
|
330
|
+
|
|
331
|
+
| Format | Example | When to use |
|
|
332
|
+
|---|---|---|
|
|
333
|
+
| Plain name | `"charge"` | Function name is globally unique across all services. Resolved automatically via service discovery. |
|
|
334
|
+
| Canonical name | `"payments/charge"` | Multiple services expose a function with the same plain name, or you want to be explicit about the target service. |
|
|
335
|
+
|
|
336
|
+
Both formats are interchangeable when the name is unique globally. Canonical format is recommended in production for clarity and to avoid ambiguity as your service count grows.
|
|
337
|
+
|
|
329
338
|
`RpcOpts`:
|
|
330
339
|
|
|
331
340
|
| Option | Type | Description |
|
|
@@ -338,6 +347,10 @@ Calls a registered RPC handler on another service. Direct gRPC path, no proxy.
|
|
|
338
347
|
| `mode` | `"direct" \| "proxy"` | Transport mode. `"direct"` (default) connects directly to the worker. `"proxy"` routes through the control plane when direct connection is unavailable. |
|
|
339
348
|
|
|
340
349
|
```ts
|
|
350
|
+
// plain name — works when "get" is unique across services
|
|
351
|
+
const user = await sb.rpc<{ id: string; name: string }>("get", { id: "u_1" });
|
|
352
|
+
|
|
353
|
+
// canonical name — explicit service target, always unambiguous
|
|
341
354
|
const user = await sb.rpc<{ id: string; name: string }>("users/get", { id: "u_1" }, {
|
|
342
355
|
timeout: 5000,
|
|
343
356
|
retries: 2,
|
|
@@ -378,6 +391,16 @@ await sb.event("orders.created", { orderId: "ord_42" }, {
|
|
|
378
391
|
|
|
379
392
|
---
|
|
380
393
|
|
|
394
|
+
### `publishEvent(topic, payload?, opts?)`
|
|
395
|
+
|
|
396
|
+
```ts
|
|
397
|
+
publishEvent(topic: string, payload?: unknown, opts?: PublishEventOpts): Promise<string>
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
Publishes an event via the established worker session stream. Requires an active worker session — call after `serve()`. Resolves with `messageId` once the server confirms with `publish_ack`. Times out after 30 s if no ack. Use `event()` when not serving (e.g. caller-only services); use `publishEvent()` from within a worker for lower-latency publishing over the existing session.
|
|
401
|
+
|
|
402
|
+
---
|
|
403
|
+
|
|
381
404
|
### `job(target, opts)`
|
|
382
405
|
|
|
383
406
|
```ts
|
|
@@ -458,44 +481,44 @@ await sb.workflow("checkout.flow", steps, { stepTimeoutMs: 60_000 });
|
|
|
458
481
|
|
|
459
482
|
---
|
|
460
483
|
|
|
461
|
-
### `
|
|
484
|
+
### `executeWorkflow(name, input?, opts?)`
|
|
462
485
|
|
|
463
486
|
```ts
|
|
464
|
-
|
|
487
|
+
executeWorkflow(name: string, input?: unknown, opts?: ExecuteWorkflowOpts): Promise<{ traceId: string; groupTraceId: string }>
|
|
465
488
|
```
|
|
466
489
|
|
|
467
|
-
Starts a workflow
|
|
468
|
-
An alternative to scheduling via `job(target, { via: "workflow" })` — triggers the
|
|
490
|
+
Starts a workflow execution on demand. The workflow must be registered first via `workflow()`.
|
|
491
|
+
An alternative to scheduling via `job(target, { via: "workflow" })` — triggers the execution immediately.
|
|
469
492
|
|
|
470
493
|
| Parameter | Type | Default | Description |
|
|
471
494
|
|---|---|---|---|
|
|
472
495
|
| `name` | `string` | required | Name of a previously registered workflow. |
|
|
473
496
|
| `input` | `unknown` | `undefined` | Optional JSON-serializable input payload. |
|
|
474
497
|
|
|
475
|
-
Returns `{
|
|
498
|
+
Returns `{ traceId, groupTraceId }`. Use `traceId` with `watchTrace()` to observe execution in real time.
|
|
476
499
|
|
|
477
|
-
`
|
|
500
|
+
`ExecuteWorkflowOpts`:
|
|
478
501
|
|
|
479
502
|
| Option | Type | Description |
|
|
480
503
|
|---|---|---|
|
|
481
|
-
| `traceId` | `string` | Override trace ID for this workflow
|
|
504
|
+
| `traceId` | `string` | Override trace ID for this workflow execution. |
|
|
482
505
|
|
|
483
506
|
```ts
|
|
484
|
-
const {
|
|
507
|
+
const { traceId, groupTraceId } = await sb.executeWorkflow("user.onboarding", { userId: "u_123" });
|
|
485
508
|
```
|
|
486
509
|
|
|
487
510
|
---
|
|
488
511
|
|
|
489
|
-
### `
|
|
512
|
+
### `cancelWorkflow(traceId)`
|
|
490
513
|
|
|
491
514
|
```ts
|
|
492
|
-
|
|
515
|
+
cancelWorkflow(traceId: string): Promise<void>
|
|
493
516
|
```
|
|
494
517
|
|
|
495
518
|
Cancels a running workflow instance.
|
|
496
519
|
|
|
497
520
|
```ts
|
|
498
|
-
await sb.
|
|
521
|
+
await sb.cancelWorkflow("trace_01HQ...XYZ");
|
|
499
522
|
```
|
|
500
523
|
|
|
501
524
|
---
|
|
@@ -542,7 +565,7 @@ sb.handleRpc("ai/generate", async (payload: { prompt: string }, ctx) => {
|
|
|
542
565
|
|
|
543
566
|
| Method | Signature | Description |
|
|
544
567
|
|---|---|---|
|
|
545
|
-
| `write` | `write(data: unknown, key?: string): Promise<void>` | Append a real-time chunk to the
|
|
568
|
+
| `write` | `write(data: unknown, key?: string): Promise<void>` | Append a real-time chunk to the trace stream. |
|
|
546
569
|
| `end` | `end(key?: string): Promise<void>` | No-op placeholder for API symmetry (lifecycle managed by runtime). |
|
|
547
570
|
|
|
548
571
|
---
|
|
@@ -584,7 +607,7 @@ a consumer, the delivery moves to DLQ with reason `delivery_ttl_exceeded`.
|
|
|
584
607
|
- `ctx.retry(delayMs?)` — ask for redelivery with optional delay
|
|
585
608
|
- `ctx.reject(reason)` — move to DLQ immediately, bypassing remaining retries
|
|
586
609
|
- `ctx.refs` — metadata (`topic`, `groupName`, `messageId`, `attempt`, `headers`)
|
|
587
|
-
- `ctx.stream.write(...)` — append real-time chunks to
|
|
610
|
+
- `ctx.stream.write(...)` — append real-time chunks to trace stream
|
|
588
611
|
|
|
589
612
|
```ts
|
|
590
613
|
sb.handleEvent("orders.*", async (payload, ctx) => {
|
|
@@ -702,32 +725,32 @@ await sb.registerHttpEndpoint({
|
|
|
702
725
|
|
|
703
726
|
---
|
|
704
727
|
|
|
705
|
-
### `
|
|
728
|
+
### `watchTrace(traceId, opts?)`
|
|
706
729
|
|
|
707
730
|
```ts
|
|
708
|
-
|
|
731
|
+
watchTrace(traceId: string, opts?: WatchTraceOpts): AsyncIterable<TraceStreamEvent>
|
|
709
732
|
```
|
|
710
733
|
|
|
711
|
-
Subscribes to a
|
|
712
|
-
identifier used by `ctx.stream.write(...)
|
|
734
|
+
Subscribes to a trace stream with replay and live updates. `traceId` is the stream
|
|
735
|
+
identifier used by `ctx.stream.write(...)`.
|
|
713
736
|
|
|
714
|
-
`
|
|
737
|
+
`WatchTraceOpts`:
|
|
715
738
|
|
|
716
739
|
| Option | Type | Default | Description |
|
|
717
740
|
|---|---|---|---|
|
|
718
741
|
| `key` | `string` | `""` | Stream key filter (`""` = all keys). |
|
|
719
742
|
| `fromSequence` | `number` | `0` | Replay from sequence cursor. |
|
|
720
743
|
|
|
721
|
-
`
|
|
744
|
+
`TraceStreamEvent`:
|
|
722
745
|
|
|
723
746
|
| Field | Type | Description |
|
|
724
747
|
|---|---|---|
|
|
725
|
-
| `type` | `"chunk" \| "
|
|
726
|
-
| `
|
|
748
|
+
| `type` | `"chunk" \| "trace_complete"` | Event kind. |
|
|
749
|
+
| `traceId` | `string` | Trace identifier being watched. |
|
|
727
750
|
| `key` | `string` | Stream lane key. |
|
|
728
751
|
| `sequence` | `number` | Monotonic sequence number. |
|
|
729
752
|
| `data` | `unknown` | JSON-decoded chunk payload. |
|
|
730
|
-
| `
|
|
753
|
+
| `traceStatus` | `string \| undefined` | Final status on `trace_complete`. |
|
|
731
754
|
|
|
732
755
|
Behavior:
|
|
733
756
|
|
|
@@ -737,11 +760,11 @@ Behavior:
|
|
|
737
760
|
- Enforces internal queue limit `256`; overflow is fatal (consumer must drain promptly).
|
|
738
761
|
|
|
739
762
|
```ts
|
|
740
|
-
for await (const evt of sb.
|
|
763
|
+
for await (const evt of sb.watchTrace(traceId, { key: "output", fromSequence: 0 })) {
|
|
741
764
|
if (evt.type === "chunk") {
|
|
742
765
|
process.stdout.write(String((evt.data as { token?: string }).token ?? ""));
|
|
743
766
|
}
|
|
744
|
-
if (evt.type === "
|
|
767
|
+
if (evt.type === "trace_complete") break;
|
|
745
768
|
}
|
|
746
769
|
```
|
|
747
770
|
|
|
@@ -766,18 +789,18 @@ if (tc) {
|
|
|
766
789
|
}
|
|
767
790
|
```
|
|
768
791
|
|
|
769
|
-
#### `
|
|
792
|
+
#### `withTraceContext(ctx, fn)`
|
|
770
793
|
|
|
771
794
|
```ts
|
|
772
|
-
|
|
795
|
+
withTraceContext<T>(ctx: { traceId: string; spanId: string }, fn: () => T): T
|
|
773
796
|
```
|
|
774
797
|
|
|
775
798
|
Runs a function inside an explicit trace context.
|
|
776
799
|
|
|
777
800
|
```ts
|
|
778
|
-
import {
|
|
801
|
+
import { withTraceContext } from "service-bridge";
|
|
779
802
|
|
|
780
|
-
|
|
803
|
+
withTraceContext({ traceId: "trace-1", spanId: "span-1" }, async () => {
|
|
781
804
|
await sb.event("audit.log", { action: "user.login" });
|
|
782
805
|
});
|
|
783
806
|
```
|
package/dist/express.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// http/src/express.ts
|
|
2
|
-
import {
|
|
2
|
+
import { withTraceContext } from "service-bridge";
|
|
3
3
|
|
|
4
4
|
// http/src/trace.ts
|
|
5
5
|
function parseTraceparent(traceparent) {
|
|
@@ -86,7 +86,7 @@ function servicebridgeMiddleware(options) {
|
|
|
86
86
|
};
|
|
87
87
|
res.on("finish", onFinish);
|
|
88
88
|
res.on("close", onFinish);
|
|
89
|
-
|
|
89
|
+
withTraceContext({ traceId: span.traceId, spanId: span.spanId }, () => {
|
|
90
90
|
next();
|
|
91
91
|
});
|
|
92
92
|
};
|
package/dist/fastify.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// http/src/fastify.ts
|
|
2
|
-
import {
|
|
2
|
+
import { withTraceContext } from "service-bridge";
|
|
3
3
|
|
|
4
4
|
// http/src/trace.ts
|
|
5
5
|
function parseTraceparent(traceparent) {
|
|
@@ -111,7 +111,7 @@ function wrapHandler(handler) {
|
|
|
111
111
|
const traceId = request.traceId;
|
|
112
112
|
const spanId = request.spanId;
|
|
113
113
|
if (traceId && spanId) {
|
|
114
|
-
return
|
|
114
|
+
return withTraceContext({ traceId, spanId }, () => handler(request, reply));
|
|
115
115
|
}
|
|
116
116
|
return handler(request, reply);
|
|
117
117
|
};
|