service-bridge 1.8.5-dev.49 → 2.0.0-alpha

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/package.json CHANGED
@@ -1,125 +1,109 @@
1
1
  {
2
- "name": "service-bridge",
3
- "version": "1.8.5-dev.49",
4
- "type": "module",
5
- "description": "ServiceBridge SDK for Node.js — one self-hosted runtime for RPC, events, workflows, and jobs without a service mesh or sidecars. Direct gRPC between workers; durable events, jobs, tracing, auto mTLS. One Go runtime + PostgreSQL.",
6
- "keywords": [
7
- "servicebridge",
8
- "service-bridge",
9
- "microservices",
10
- "rpc",
11
- "grpc",
12
- "event-bus",
13
- "event-driven",
14
- "distributed-tracing",
15
- "workflow",
16
- "orchestration",
17
- "background-jobs",
18
- "cron",
19
- "mtls",
20
- "service-mesh",
21
- "service-discovery",
22
- "distributed-systems",
23
- "zero-sidecar",
24
- "proxyless",
25
- "istio-alternative",
26
- "rabbitmq-alternative",
27
- "temporal-alternative",
28
- "jaeger-alternative",
29
- "postgresql",
30
- "docker",
31
- "kubernetes",
32
- "dead-letter-queue",
33
- "dlq",
34
- "saga",
35
- "distributed-transactions",
36
- "ai-agent-orchestration",
37
- "express",
38
- "fastify",
39
- "http-middleware",
40
- "observability",
41
- "prometheus",
42
- "tracing",
43
- "typescript",
44
- "nodejs",
45
- "production-ready",
46
- "self-hosted"
47
- ],
48
- "homepage": "https://servicebridge.dev",
49
- "repository": {
50
- "type": "git",
51
- "url": "https://github.com/service-bridge/sdk"
52
- },
53
- "private": false,
54
- "license": "SEE LICENSE IN LICENSE",
55
- "publishConfig": {
56
- "access": "public"
57
- },
58
- "files": [
59
- "dist/index.js",
60
- "dist/index.d.ts",
61
- "dist/express.js",
62
- "dist/express.d.ts",
63
- "dist/fastify.js",
64
- "dist/fastify.d.ts",
65
- "dist/trace.d.ts",
66
- "README.md",
67
- "LICENSE"
68
- ],
69
- "main": "dist/index.js",
70
- "types": "dist/index.d.ts",
71
- "exports": {
72
- ".": {
73
- "import": "./dist/index.js",
74
- "types": "./dist/index.d.ts"
75
- },
76
- "./express": {
77
- "import": "./dist/express.js",
78
- "types": "./dist/express.d.ts"
79
- },
80
- "./fastify": {
81
- "import": "./dist/fastify.js",
82
- "types": "./dist/fastify.d.ts"
83
- }
84
- },
85
- "scripts": {
86
- "generate:proto": "bun run sdk/scripts/generate-proto.ts",
87
- "build:core": "bun build sdk/src/index.ts --outdir=dist --target=node -e @grpc/grpc-js -e @grpc/proto-loader -e protobufjs -e long",
88
- "build:types": "bunx tsc -p sdk/tsconfig.json --emitDeclarationOnly --outDir dist && bunx tsc -p http/tsconfig.json --emitDeclarationOnly --outDir http/dist && cp http/dist/express.d.ts http/dist/fastify.d.ts http/dist/trace.d.ts dist/",
89
- "build:express": "bun build ./http/src/express.ts --outfile=dist/express.js --target=node -e service-bridge",
90
- "build:fastify": "bun build ./http/src/fastify.ts --outfile=dist/fastify.js --target=node -e service-bridge",
91
- "build": "bun run build:core && bun run build:express && bun run build:fastify && bun run build:types",
92
- "test:sdk": "bun test sdk",
93
- "test:http": "bun test --tsconfig-override http/tsconfig.test.json http",
94
- "test": "bun run test:sdk && bun run test:http",
95
- "typecheck": "bunx tsc -p sdk/tsconfig.json --noEmit && bunx tsc -p http/tsconfig.json --noEmit",
96
- "lint": "bunx biome check .",
97
- "lint:fix": "bunx biome check . --write"
98
- },
99
- "dependencies": {
100
- "@grpc/grpc-js": "^1.9.0",
101
- "@grpc/proto-loader": "^0.7.10",
102
- "protobufjs": "^8.0.0"
103
- },
104
- "peerDependencies": {
105
- "express": "",
106
- "fastify": ">=4.0.0"
107
- },
108
- "peerDependenciesMeta": {
109
- "express": {
110
- "optional": true
111
- },
112
- "fastify": {
113
- "optional": true
114
- }
115
- },
116
- "devDependencies": {
117
- "@biomejs/biome": "^2.4.5",
118
- "@types/bun": "latest",
119
- "@types/express": "^5.0.6",
120
- "@types/node": "^20.0.0",
121
- "express": "^4.21.2",
122
- "fastify": "^4.29.0",
123
- "typescript": "^5.3.0"
124
- }
2
+ "name": "service-bridge",
3
+ "version": "2.0.0-alpha",
4
+ "description": "TypeScript SDK for ServiceBridge — RPC, durable events, workflows, jobs and observability over a single self-hosted gRPC runtime with mTLS.",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "main": "./dist/index.js",
8
+ "module": "./dist/index.js",
9
+ "types": "./dist/index.d.ts",
10
+ "repository": {
11
+ "type": "git",
12
+ "url": "git+https://github.com/service-bridge/sdk.git",
13
+ "directory": "node"
14
+ },
15
+ "homepage": "https://servicebridge.dev",
16
+ "publishConfig": {
17
+ "tag": "alpha",
18
+ "access": "public",
19
+ "provenance": true
20
+ },
21
+ "keywords": [
22
+ "microservices",
23
+ "rpc",
24
+ "events",
25
+ "workflows",
26
+ "grpc",
27
+ "mtls",
28
+ "observability"
29
+ ],
30
+ "engines": {
31
+ "node": ">=18",
32
+ "bun": ">=1.0"
33
+ },
34
+ "files": [
35
+ "dist",
36
+ "README.md",
37
+ "LICENSE"
38
+ ],
39
+ "scripts": {
40
+ "dev": "bun run --hot index.ts",
41
+ "start": "bun run index.ts",
42
+ "build": "tsup",
43
+ "prepack": "bun run build",
44
+ "lint": "biome check .",
45
+ "format": "biome format --write .",
46
+ "typecheck": "tsc --noEmit",
47
+ "check": "biome check --write . && tsc --noEmit"
48
+ },
49
+ "exports": {
50
+ ".": {
51
+ "types": "./dist/index.d.ts",
52
+ "import": "./dist/index.js"
53
+ },
54
+ "./express": {
55
+ "types": "./dist/http/express/index.d.ts",
56
+ "import": "./dist/http/express/index.js"
57
+ },
58
+ "./fastify": {
59
+ "types": "./dist/http/fastify/index.d.ts",
60
+ "import": "./dist/http/fastify/index.js"
61
+ },
62
+ "./hono": {
63
+ "types": "./dist/http/hono/index.d.ts",
64
+ "import": "./dist/http/hono/index.js"
65
+ }
66
+ },
67
+ "devDependencies": {
68
+ "@biomejs/biome": "^2.4.15",
69
+ "@hono/node-server": "^2.0.4",
70
+ "@types/better-sqlite3": "^7.6.13",
71
+ "@types/bun": "latest",
72
+ "@types/express": "^5.0.6",
73
+ "@types/pg": "^8.20.0",
74
+ "express": "^5.2.1",
75
+ "fast-check": "^4.8.0",
76
+ "fastify": "^5",
77
+ "hono": "^4",
78
+ "pg": "^8.21.0",
79
+ "ts-proto": "^2.11.8",
80
+ "tsup": "^8.5.1"
81
+ },
82
+ "peerDependencies": {
83
+ "express": "^4 || ^5",
84
+ "fastify": "^4 || ^5",
85
+ "hono": "^4",
86
+ "typescript": "^5"
87
+ },
88
+ "peerDependenciesMeta": {
89
+ "express": {
90
+ "optional": true
91
+ },
92
+ "fastify": {
93
+ "optional": true
94
+ },
95
+ "hono": {
96
+ "optional": true
97
+ }
98
+ },
99
+ "dependencies": {
100
+ "@bufbuild/protobuf": "^2.12.0",
101
+ "@grpc/grpc-js": "1.14.3",
102
+ "@peculiar/x509": "^2.0.0",
103
+ "better-sqlite3": "^12.10.0",
104
+ "fastify-plugin": "^5.0.1",
105
+ "protobufjs": "^8.4.0",
106
+ "reflect-metadata": "^0.2.2",
107
+ "uuidv7": "^1.2.1"
108
+ }
125
109
  }
package/dist/express.d.ts DELETED
@@ -1,51 +0,0 @@
1
- import type { Application, NextFunction, Request, Response } from "express";
2
- import type { ServiceBridgeService } from "service-bridge";
3
- import type { IncomingTraceContext } from "./trace";
4
- import { extractTraceFromHeaders } from "./trace";
5
- export { extractTraceFromHeaders };
6
- export type { IncomingTraceContext };
7
- declare global {
8
- namespace Express {
9
- interface Request {
10
- servicebridge: ServiceBridgeService;
11
- traceId?: string;
12
- spanId?: string;
13
- }
14
- }
15
- }
16
- export interface ServiceBridgeExpressOptions {
17
- /** ServiceBridge client (from servicebridge()). Required. */
18
- client: ServiceBridgeService;
19
- /** Paths to skip tracing. Default: [] */
20
- excludePaths?: string[];
21
- /** Set x-trace-id response header for propagation. Default: true */
22
- propagateTraceHeader?: boolean;
23
- /**
24
- * Auto-register route patterns in the ServiceBridge HTTP catalog on first hit.
25
- * Routes appear in the UI after their first request. Default: true.
26
- * Use registerExpressRoutes() for eager (pre-traffic) registration.
27
- */
28
- autoRegister?: boolean;
29
- }
30
- export declare function servicebridgeMiddleware(options: ServiceBridgeExpressOptions): (req: Request, res: Response, next: NextFunction) => void;
31
- export interface RegisterExpressRoutesOptions {
32
- /** Stable identifier for this process instance used in http_instances. */
33
- instanceId?: string;
34
- /** Address where this service can be reached, e.g. "http://10.0.0.1:3000" */
35
- endpoint?: string;
36
- /** Service names allowed to call these endpoints. Default: [] */
37
- allowedCallers?: string[];
38
- /** Path prefixes to exclude from registration. Default: [] */
39
- excludePaths?: string[];
40
- }
41
- /**
42
- * Scan an Express application's route table and register every route pattern
43
- * in the ServiceBridge HTTP catalog. Safe to call multiple times (idempotent).
44
- *
45
- * @example
46
- * app.get('/users/:id', handler);
47
- * app.post('/orders', handler);
48
- * app.listen(3000, () => registerExpressRoutes(app, sb));
49
- */
50
- export declare function registerExpressRoutes(app: Application, client: ServiceBridgeService, opts?: RegisterExpressRoutesOptions): Promise<void>;
51
- //# sourceMappingURL=express.d.ts.map
package/dist/express.js DELETED
@@ -1,129 +0,0 @@
1
- // http/src/express.ts
2
- import { withTraceContext } from "service-bridge";
3
-
4
- // http/src/trace.ts
5
- function parseTraceparent(traceparent) {
6
- const parts = traceparent.trim().split("-");
7
- if (parts.length !== 4 || parts[0] !== "00")
8
- return null;
9
- const [, traceId, parentSpanId] = parts;
10
- if (!traceId || !parentSpanId || traceId.length !== 32 || parentSpanId.length !== 16)
11
- return null;
12
- return { traceId, parentSpanId };
13
- }
14
- function randomTraceId() {
15
- return crypto.randomUUID().replace(/-/g, "");
16
- }
17
- function extractTraceFromHeaders(headers) {
18
- const traceparent = headers.traceparent;
19
- const tp = Array.isArray(traceparent) ? traceparent[0] : traceparent;
20
- if (tp) {
21
- const parsed = parseTraceparent(tp);
22
- if (parsed)
23
- return parsed;
24
- }
25
- const xTraceId = headers["x-trace-id"];
26
- const traceId = Array.isArray(xTraceId) ? xTraceId[0] : xTraceId;
27
- if (traceId && typeof traceId === "string") {
28
- return { traceId: traceId.trim(), parentSpanId: "" };
29
- }
30
- return { traceId: randomTraceId(), parentSpanId: "" };
31
- }
32
-
33
- // http/src/express.ts
34
- function servicebridgeMiddleware(options) {
35
- const {
36
- client,
37
- excludePaths = [],
38
- propagateTraceHeader = true,
39
- autoRegister = true
40
- } = options;
41
- const registeredPatterns = new Set;
42
- return function middleware(req, res, next) {
43
- const path = req.path ?? req.url?.split("?")[0] ?? "/";
44
- if (excludePaths.some((p) => path.startsWith(p))) {
45
- req.servicebridge = client;
46
- next();
47
- return;
48
- }
49
- const headers = {};
50
- for (const [k, v] of Object.entries(req.headers)) {
51
- if (v != null)
52
- headers[k.toLowerCase()] = v;
53
- }
54
- const traceCtx = extractTraceFromHeaders(headers);
55
- const span = client.startHttpSpan({
56
- method: req.method,
57
- path,
58
- traceId: traceCtx.traceId,
59
- parentSpanId: traceCtx.parentSpanId
60
- });
61
- req.servicebridge = client;
62
- req.traceId = span.traceId;
63
- req.spanId = span.spanId;
64
- if (propagateTraceHeader) {
65
- res.setHeader("x-trace-id", span.traceId);
66
- }
67
- const onFinish = () => {
68
- res.off("finish", onFinish);
69
- res.off("close", onFinish);
70
- span.end({
71
- statusCode: res.statusCode,
72
- success: res.statusCode < 400
73
- });
74
- if (autoRegister) {
75
- const routePattern = req.route?.path;
76
- if (routePattern && typeof routePattern === "string") {
77
- const key = `${req.method}:${routePattern}`;
78
- if (!registeredPatterns.has(key)) {
79
- registeredPatterns.add(key);
80
- client.registerHttpEndpoint({ method: req.method, route: routePattern }).catch(() => {
81
- registeredPatterns.delete(key);
82
- });
83
- }
84
- }
85
- }
86
- };
87
- res.on("finish", onFinish);
88
- res.on("close", onFinish);
89
- withTraceContext({ traceId: span.traceId, spanId: span.spanId }, () => {
90
- next();
91
- });
92
- };
93
- }
94
- async function registerExpressRoutes(app, client, opts = {}) {
95
- const { instanceId, endpoint, allowedCallers = [], excludePaths = [] } = opts;
96
- const routes = extractExpressRoutes(app);
97
- await Promise.allSettled(routes.filter(({ path }) => !excludePaths.some((p) => path.startsWith(p))).map(({ method, path: route }) => client.registerHttpEndpoint({
98
- method,
99
- route,
100
- instanceId,
101
- endpoint,
102
- allowedCallers
103
- })));
104
- }
105
- function getLayerStack(app) {
106
- const r = app._router ?? app.router;
107
- return r?.stack ?? app.stack ?? [];
108
- }
109
- function extractExpressRoutes(app, prefix = "") {
110
- const routes = [];
111
- const stack = getLayerStack(app);
112
- for (const layer of stack) {
113
- if (layer.route) {
114
- const routePath = prefix + (layer.route.path ?? "");
115
- const methods = Object.keys(layer.route.methods ?? {}).filter((m) => m !== "_all");
116
- for (const method of methods) {
117
- routes.push({ method: method.toUpperCase(), path: routePath });
118
- }
119
- } else if (layer.name === "router" && layer.handle) {
120
- routes.push(...extractExpressRoutes(layer.handle, prefix));
121
- }
122
- }
123
- return routes;
124
- }
125
- export {
126
- servicebridgeMiddleware,
127
- registerExpressRoutes,
128
- extractTraceFromHeaders
129
- };
package/dist/fastify.d.ts DELETED
@@ -1,43 +0,0 @@
1
- import type { FastifyInstance, FastifyReply, FastifyRequest } from "fastify";
2
- import type { ServiceBridgeService } from "service-bridge";
3
- declare module "fastify" {
4
- interface FastifyRequest {
5
- servicebridge: ServiceBridgeService;
6
- traceId?: string;
7
- spanId?: string;
8
- }
9
- }
10
- export interface ServiceBridgeFastifyOptions {
11
- /** ServiceBridge client (from servicebridge()). Required. */
12
- client: ServiceBridgeService;
13
- /** Paths to skip tracing. Default: [] */
14
- excludePaths?: string[];
15
- /** Set x-trace-id response header for propagation. Default: true */
16
- propagateTraceHeader?: boolean;
17
- /**
18
- * Auto-register every route in the ServiceBridge HTTP catalog via Fastify's
19
- * onRoute hook. Routes appear in the UI as soon as the server starts (before
20
- * any traffic). Default: true.
21
- */
22
- autoRegister?: boolean;
23
- /**
24
- * Options forwarded to registerHttpEndpoint for each auto-discovered route.
25
- */
26
- register?: {
27
- /** Stable identifier for this process instance. */
28
- instanceId?: string;
29
- /** Address where this service can be reached, e.g. "http://10.0.0.1:3000" */
30
- endpoint?: string;
31
- /** Service names allowed to call these endpoints. Default: [] */
32
- allowedCallers?: string[];
33
- /** Path prefixes to exclude from registration. Default: [] */
34
- excludePaths?: string[];
35
- };
36
- }
37
- export declare function servicebridgePlugin(fastify: FastifyInstance, options: ServiceBridgeFastifyOptions): Promise<void>;
38
- /**
39
- * Wraps a route handler to run inside trace context.
40
- * Use when the handler calls req.servicebridge.rpc() or event() — ensures trace propagation.
41
- */
42
- export declare function wrapHandler<T>(handler: (request: FastifyRequest, reply: FastifyReply) => T): (request: FastifyRequest, reply: FastifyReply) => T;
43
- //# sourceMappingURL=fastify.d.ts.map
package/dist/fastify.js DELETED
@@ -1,122 +0,0 @@
1
- // http/src/fastify.ts
2
- import { withTraceContext } from "service-bridge";
3
-
4
- // http/src/trace.ts
5
- function parseTraceparent(traceparent) {
6
- const parts = traceparent.trim().split("-");
7
- if (parts.length !== 4 || parts[0] !== "00")
8
- return null;
9
- const [, traceId, parentSpanId] = parts;
10
- if (!traceId || !parentSpanId || traceId.length !== 32 || parentSpanId.length !== 16)
11
- return null;
12
- return { traceId, parentSpanId };
13
- }
14
- function randomTraceId() {
15
- return crypto.randomUUID().replace(/-/g, "");
16
- }
17
- function extractTraceFromHeaders(headers) {
18
- const traceparent = headers.traceparent;
19
- const tp = Array.isArray(traceparent) ? traceparent[0] : traceparent;
20
- if (tp) {
21
- const parsed = parseTraceparent(tp);
22
- if (parsed)
23
- return parsed;
24
- }
25
- const xTraceId = headers["x-trace-id"];
26
- const traceId = Array.isArray(xTraceId) ? xTraceId[0] : xTraceId;
27
- if (traceId && typeof traceId === "string") {
28
- return { traceId: traceId.trim(), parentSpanId: "" };
29
- }
30
- return { traceId: randomTraceId(), parentSpanId: "" };
31
- }
32
-
33
- // http/src/fastify.ts
34
- async function servicebridgePlugin(fastify, options) {
35
- const {
36
- client,
37
- excludePaths = [],
38
- propagateTraceHeader = true,
39
- autoRegister = true,
40
- register: registerOpts = {}
41
- } = options;
42
- fastify.decorateRequest("servicebridge", null);
43
- fastify.decorateRequest("traceId", null);
44
- fastify.decorateRequest("spanId", null);
45
- if (autoRegister) {
46
- const excludeReg = registerOpts.excludePaths ?? [];
47
- fastify.addHook("onRoute", (routeOptions) => {
48
- const routePath = routeOptions.url ?? routeOptions.path ?? "";
49
- if (excludeReg.some((p) => routePath.startsWith(p)))
50
- return;
51
- const methods = Array.isArray(routeOptions.method) ? routeOptions.method : [routeOptions.method];
52
- for (const method of methods) {
53
- if (method === "HEAD" || method === "OPTIONS")
54
- continue;
55
- client.registerHttpEndpoint({
56
- method,
57
- route: routePath,
58
- instanceId: registerOpts.instanceId,
59
- endpoint: registerOpts.endpoint,
60
- allowedCallers: registerOpts.allowedCallers ?? []
61
- }).catch(() => {});
62
- }
63
- });
64
- }
65
- fastify.addHook("onRequest", async (request, reply) => {
66
- request.servicebridge = client;
67
- const path = request.url?.split("?")[0] ?? "/";
68
- if (excludePaths.some((p) => path.startsWith(p))) {
69
- return;
70
- }
71
- const headers = {};
72
- for (const [k, v] of Object.entries(request.headers)) {
73
- if (typeof v === "string" || Array.isArray(v)) {
74
- headers[k.toLowerCase()] = v;
75
- }
76
- }
77
- const traceCtx = extractTraceFromHeaders(headers);
78
- const span = client.startHttpSpan({
79
- method: request.method,
80
- path,
81
- traceId: traceCtx.traceId,
82
- parentSpanId: traceCtx.parentSpanId
83
- });
84
- let ended = false;
85
- const endSpan = (opts) => {
86
- if (ended)
87
- return;
88
- ended = true;
89
- span.end(opts);
90
- };
91
- request.traceId = span.traceId;
92
- request.spanId = span.spanId;
93
- if (propagateTraceHeader) {
94
- reply.header("x-trace-id", span.traceId);
95
- }
96
- request.raw.once("close", () => {
97
- if (!reply.sent) {
98
- endSpan({ success: false, error: "connection closed" });
99
- }
100
- });
101
- reply.raw.once("finish", () => {
102
- endSpan({
103
- statusCode: reply.statusCode,
104
- success: reply.statusCode < 400
105
- });
106
- });
107
- });
108
- }
109
- function wrapHandler(handler) {
110
- return (request, reply) => {
111
- const traceId = request.traceId;
112
- const spanId = request.spanId;
113
- if (traceId && spanId) {
114
- return withTraceContext({ traceId, spanId }, () => handler(request, reply));
115
- }
116
- return handler(request, reply);
117
- };
118
- }
119
- export {
120
- wrapHandler,
121
- servicebridgePlugin
122
- };
package/dist/trace.d.ts DELETED
@@ -1,19 +0,0 @@
1
- /**
2
- * Result of extracting trace context from incoming HTTP headers.
3
- * traceId — the trace this request belongs to (carry-over or newly generated).
4
- * parentSpanId — the caller's span ID to use as parent, empty string if unknown.
5
- */
6
- export interface IncomingTraceContext {
7
- traceId: string;
8
- parentSpanId: string;
9
- }
10
- /**
11
- * Extract distributed trace context from incoming HTTP request headers.
12
- *
13
- * Priority:
14
- * 1. W3C `traceparent` — full parent info (traceId + parentSpanId)
15
- * 2. `x-trace-id` — traceId only, no parent span known
16
- * 3. Nothing — generate a fresh traceId, no parent
17
- */
18
- export declare function extractTraceFromHeaders(headers: Record<string, string | string[] | undefined>): IncomingTraceContext;
19
- //# sourceMappingURL=trace.d.ts.map