service-bridge 1.8.4-dev.45 → 1.8.4-dev.46
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/dist/index.js +60 -60
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -841,11 +841,11 @@ function ensureSbResolverRegistered() {
|
|
|
841
841
|
}
|
|
842
842
|
registerResolver("sb", SbResolver);
|
|
843
843
|
}
|
|
844
|
-
function servicebridge(url, serviceKey,
|
|
845
|
-
const service = typeof serviceOrOpts === "string" ? serviceOrOpts.trim() : "";
|
|
846
|
-
const globalOpts = typeof serviceOrOpts === "string" ? maybeGlobalOpts : serviceOrOpts ?? {};
|
|
844
|
+
function servicebridge(url, serviceKey, opts = {}) {
|
|
847
845
|
const parsedServiceKey = parseServiceKeyV2(serviceKey);
|
|
848
|
-
const
|
|
846
|
+
const service = parsedServiceKey.keyId || "";
|
|
847
|
+
const globalOpts = opts;
|
|
848
|
+
const defaultConsumerPrefix = service || "consumer";
|
|
849
849
|
const meta = new grpc.Metadata;
|
|
850
850
|
meta.add("x-service-key", serviceKey);
|
|
851
851
|
const rawUrl = url.trim();
|
|
@@ -2097,7 +2097,7 @@ function servicebridge(url, serviceKey, serviceOrOpts = {}, maybeGlobalOpts = {}
|
|
|
2097
2097
|
}
|
|
2098
2098
|
}
|
|
2099
2099
|
const svc = {
|
|
2100
|
-
async rpc(targetService, fn, payload,
|
|
2100
|
+
async rpc(targetService, fn, payload, opts2) {
|
|
2101
2101
|
try {
|
|
2102
2102
|
await _controlReady;
|
|
2103
2103
|
} catch (err) {
|
|
@@ -2110,10 +2110,10 @@ function servicebridge(url, serviceKey, serviceOrOpts = {}, maybeGlobalOpts = {}
|
|
|
2110
2110
|
}
|
|
2111
2111
|
const lookupKey = `${svcName}/${fname}`;
|
|
2112
2112
|
const tc = traceStorage.getStore();
|
|
2113
|
-
const traceId =
|
|
2114
|
-
const maxRetries = normalizeNonNegativeInt(
|
|
2115
|
-
const baseDelay = normalizePositiveInt(
|
|
2116
|
-
const timeout = normalizePositiveInt(
|
|
2113
|
+
const traceId = opts2?.traceId ?? tc?.traceId ?? crypto.randomUUID();
|
|
2114
|
+
const maxRetries = normalizeNonNegativeInt(opts2?.retries ?? globalOpts.retries ?? 3, 3);
|
|
2115
|
+
const baseDelay = normalizePositiveInt(opts2?.retryDelay ?? globalOpts.retryDelay ?? 300, 300);
|
|
2116
|
+
const timeout = normalizePositiveInt(opts2?.timeout ?? globalOpts.timeout ?? 30000, 30000);
|
|
2117
2117
|
let functionKey = resolveFunctionKey(lookupKey);
|
|
2118
2118
|
if (!functionKey) {
|
|
2119
2119
|
await doLookupFunction(lookupKey);
|
|
@@ -2132,12 +2132,12 @@ function servicebridge(url, serviceKey, serviceOrOpts = {}, maybeGlobalOpts = {}
|
|
|
2132
2132
|
const rpcFnName = fname;
|
|
2133
2133
|
const rpcServiceName = svcName;
|
|
2134
2134
|
const rootSpanId = crypto.randomUUID();
|
|
2135
|
-
const parentSpanId =
|
|
2135
|
+
const parentSpanId = opts2?.parentSpanId ?? tc?.spanId ?? "";
|
|
2136
2136
|
const rootStartedAt = Date.now();
|
|
2137
2137
|
const hasRetries = maxRetries > 0;
|
|
2138
2138
|
reportCallStartAsync(traceId, rootSpanId, parentSpanId, rpcFnName, rootStartedAt, telemetryInputBuf, 1, "rpc", rpcServiceName);
|
|
2139
2139
|
return traceStorage.run({ traceId, spanId: rootSpanId }, async () => {
|
|
2140
|
-
if (
|
|
2140
|
+
if (opts2?.mode === "proxy") {
|
|
2141
2141
|
return await new Promise((resolve, reject) => {
|
|
2142
2142
|
stub.ProxyCall({
|
|
2143
2143
|
function_name: functionKey,
|
|
@@ -2248,9 +2248,9 @@ function servicebridge(url, serviceKey, serviceOrOpts = {}, maybeGlobalOpts = {}
|
|
|
2248
2248
|
throw normalizeServiceError(lastError, lookupKey, "worker");
|
|
2249
2249
|
});
|
|
2250
2250
|
},
|
|
2251
|
-
event(topic, payload,
|
|
2251
|
+
event(topic, payload, opts2) {
|
|
2252
2252
|
if (!isOnline) {
|
|
2253
|
-
enqueueOffline({ type: "event", topic, payload, opts });
|
|
2253
|
+
enqueueOffline({ type: "event", topic, payload, opts: opts2 });
|
|
2254
2254
|
return Promise.resolve("");
|
|
2255
2255
|
}
|
|
2256
2256
|
const tc = traceStorage.getStore();
|
|
@@ -2258,15 +2258,15 @@ function servicebridge(url, serviceKey, serviceOrOpts = {}, maybeGlobalOpts = {}
|
|
|
2258
2258
|
stub.Publish({
|
|
2259
2259
|
topic,
|
|
2260
2260
|
payload: toJsonBuffer(payload),
|
|
2261
|
-
headers: toWireStringMap(
|
|
2262
|
-
trace_id:
|
|
2263
|
-
parent_span_id:
|
|
2261
|
+
headers: toWireStringMap(opts2?.headers),
|
|
2262
|
+
trace_id: opts2?.traceId ?? tc?.traceId ?? "",
|
|
2263
|
+
parent_span_id: opts2?.parentSpanId ?? tc?.spanId ?? "",
|
|
2264
2264
|
producer_service: defaultConsumerPrefix,
|
|
2265
|
-
idempotency_key:
|
|
2265
|
+
idempotency_key: opts2?.idempotencyKey ?? ""
|
|
2266
2266
|
}, meta, unaryDeadlineOptions(), (err, res) => err ? reject(normalizeServiceError(err, `publish:${topic}`)) : resolve(res?.message_id ?? ""));
|
|
2267
2267
|
});
|
|
2268
2268
|
},
|
|
2269
|
-
publishEvent(topic, payload,
|
|
2269
|
+
publishEvent(topic, payload, opts2) {
|
|
2270
2270
|
const stream = workerSessionStream;
|
|
2271
2271
|
if (!stream) {
|
|
2272
2272
|
return Promise.reject(new Error("publishEvent requires an active worker session (call serve() first)"));
|
|
@@ -2277,9 +2277,9 @@ function servicebridge(url, serviceKey, serviceOrOpts = {}, maybeGlobalOpts = {}
|
|
|
2277
2277
|
request_id: requestId,
|
|
2278
2278
|
topic,
|
|
2279
2279
|
payload: toJsonBuffer(payload),
|
|
2280
|
-
headers: toWireStringMap(
|
|
2281
|
-
trace_id:
|
|
2282
|
-
parent_span_id:
|
|
2280
|
+
headers: toWireStringMap(opts2?.headers),
|
|
2281
|
+
trace_id: opts2?.traceId ?? tc?.traceId ?? "",
|
|
2282
|
+
parent_span_id: opts2?.parentSpanId ?? tc?.spanId ?? ""
|
|
2283
2283
|
};
|
|
2284
2284
|
return new Promise((resolve, reject) => {
|
|
2285
2285
|
const timer = setTimeout(() => {
|
|
@@ -2296,12 +2296,12 @@ function servicebridge(url, serviceKey, serviceOrOpts = {}, maybeGlobalOpts = {}
|
|
|
2296
2296
|
}
|
|
2297
2297
|
});
|
|
2298
2298
|
},
|
|
2299
|
-
handleRpc(fn, handler,
|
|
2300
|
-
fnHandlers.set(fn, { handler, opts:
|
|
2299
|
+
handleRpc(fn, handler, opts2) {
|
|
2300
|
+
fnHandlers.set(fn, { handler, opts: opts2 ?? {} });
|
|
2301
2301
|
return svc;
|
|
2302
2302
|
},
|
|
2303
|
-
handleEvent(pattern, handler,
|
|
2304
|
-
const normalizedOpts =
|
|
2303
|
+
handleEvent(pattern, handler, opts2) {
|
|
2304
|
+
const normalizedOpts = opts2 ?? {};
|
|
2305
2305
|
const groupName = `${defaultConsumerPrefix}.${pattern}`;
|
|
2306
2306
|
if (eventHandlers.has(groupName)) {
|
|
2307
2307
|
throw new Error(`Duplicate event consumer group "${groupName}". ` + "Use handleEvent() with a unique topic pattern per handler.");
|
|
@@ -2314,13 +2314,13 @@ function servicebridge(url, serviceKey, serviceOrOpts = {}, maybeGlobalOpts = {}
|
|
|
2314
2314
|
});
|
|
2315
2315
|
return svc;
|
|
2316
2316
|
},
|
|
2317
|
-
async serve(
|
|
2317
|
+
async serve(opts2 = {}) {
|
|
2318
2318
|
if (workerServer)
|
|
2319
2319
|
throw new Error("serve() already called");
|
|
2320
2320
|
if (fnHandlers.size === 0 && eventHandlers.size === 0) {
|
|
2321
2321
|
throw new Error("No handlers registered. Call handleRpc() or handleEvent() before serve().");
|
|
2322
2322
|
}
|
|
2323
|
-
if (
|
|
2323
|
+
if (opts2.maxInFlight != null && (!Number.isInteger(opts2.maxInFlight) || opts2.maxInFlight < 1)) {
|
|
2324
2324
|
throw new Error("serve() maxInFlight must be a positive integer");
|
|
2325
2325
|
}
|
|
2326
2326
|
try {
|
|
@@ -2329,10 +2329,10 @@ function servicebridge(url, serviceKey, serviceOrOpts = {}, maybeGlobalOpts = {}
|
|
|
2329
2329
|
Handle: handleRpc,
|
|
2330
2330
|
Deliver: handleDeliver
|
|
2331
2331
|
});
|
|
2332
|
-
const host =
|
|
2332
|
+
const host = opts2.host || "localhost";
|
|
2333
2333
|
const isWildcardBind = host === "0.0.0.0" || host === "::";
|
|
2334
2334
|
const advertiseHost = isWildcardBind ? await outboundIP() || host : host;
|
|
2335
|
-
const tlsOpts =
|
|
2335
|
+
const tlsOpts = opts2.tls ?? effectiveWorkerTLS ?? undefined;
|
|
2336
2336
|
let effectiveCert = toPemBuffer(tlsOpts?.cert);
|
|
2337
2337
|
let effectiveKey = toPemBuffer(tlsOpts?.key);
|
|
2338
2338
|
const explicitWorkerCACert = toPemBuffer(tlsOpts?.caCert);
|
|
@@ -2344,7 +2344,7 @@ function servicebridge(url, serviceKey, serviceOrOpts = {}, maybeGlobalOpts = {}
|
|
|
2344
2344
|
validateWorkerCertAndKeyPair(effectiveCert, effectiveKey);
|
|
2345
2345
|
}
|
|
2346
2346
|
if (!effectiveCert || !effectiveKey) {
|
|
2347
|
-
const instanceIdForCert =
|
|
2347
|
+
const instanceIdForCert = opts2.instanceId || Array.from(crypto.getRandomValues(new Uint8Array(3))).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
2348
2348
|
const provisioned = await provisionWorkerTLS(stub, meta, service, instanceIdForCert, isWildcardBind && advertiseHost !== host ? advertiseHost : undefined, isWildcardBind && advertiseHost !== host ? [advertiseHost] : undefined);
|
|
2349
2349
|
effectiveCert = provisioned.cert;
|
|
2350
2350
|
effectiveKey = provisioned.key;
|
|
@@ -2368,13 +2368,13 @@ function servicebridge(url, serviceKey, serviceOrOpts = {}, maybeGlobalOpts = {}
|
|
|
2368
2368
|
server.bindAsync(`${host}:0`, serverCreds, (err, p) => err ? reject(err) : resolve(p));
|
|
2369
2369
|
});
|
|
2370
2370
|
const endpoint = `${advertiseHost}:${port}`;
|
|
2371
|
-
const instanceId =
|
|
2371
|
+
const instanceId = opts2.instanceId || Array.from(crypto.getRandomValues(new Uint8Array(3))).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
2372
2372
|
serveState = {
|
|
2373
2373
|
endpoint,
|
|
2374
2374
|
transport: "tls",
|
|
2375
|
-
opts,
|
|
2375
|
+
opts: opts2,
|
|
2376
2376
|
instanceId,
|
|
2377
|
-
maxInFlight: Math.max(1, Math.trunc(
|
|
2377
|
+
maxInFlight: Math.max(1, Math.trunc(opts2.maxInFlight ?? 128))
|
|
2378
2378
|
};
|
|
2379
2379
|
if (effectiveCACert && effectiveCert && effectiveKey) {
|
|
2380
2380
|
effectiveWorkerTLS = {
|
|
@@ -2430,47 +2430,47 @@ function servicebridge(url, serviceKey, serviceOrOpts = {}, maybeGlobalOpts = {}
|
|
|
2430
2430
|
},
|
|
2431
2431
|
async job(serviceOrTarget, fnOrOpts, optsOrUndefined) {
|
|
2432
2432
|
let target2;
|
|
2433
|
-
let
|
|
2433
|
+
let opts2;
|
|
2434
2434
|
if (typeof fnOrOpts === "string") {
|
|
2435
2435
|
const service2 = serviceOrTarget.trim();
|
|
2436
2436
|
const fn = fnOrOpts.trim();
|
|
2437
|
-
|
|
2437
|
+
opts2 = optsOrUndefined ?? { via: "rpc" };
|
|
2438
2438
|
if (!service2 || !fn) {
|
|
2439
2439
|
throw new Error("job: service and fn are required for RPC jobs");
|
|
2440
2440
|
}
|
|
2441
|
-
if (
|
|
2441
|
+
if (opts2.via !== "rpc") {
|
|
2442
2442
|
throw new Error("job: via must be 'rpc' when using (service, fn) form");
|
|
2443
2443
|
}
|
|
2444
2444
|
target2 = `${service2}/${fn}`;
|
|
2445
2445
|
} else {
|
|
2446
2446
|
target2 = serviceOrTarget;
|
|
2447
|
-
|
|
2448
|
-
if (
|
|
2447
|
+
opts2 = fnOrOpts;
|
|
2448
|
+
if (opts2.via === "rpc") {
|
|
2449
2449
|
throw new Error("job: use job(service, fn, opts) form for RPC jobs");
|
|
2450
2450
|
}
|
|
2451
2451
|
}
|
|
2452
|
-
jobRegistrations.set(target2, { target: target2, opts });
|
|
2452
|
+
jobRegistrations.set(target2, { target: target2, opts: opts2 });
|
|
2453
2453
|
if (isOnline) {
|
|
2454
2454
|
await reconcileRegistrations(`job:${target2}`).catch((err) => {
|
|
2455
2455
|
reportSDKError(`register-job:${target2}`, err);
|
|
2456
2456
|
});
|
|
2457
2457
|
} else {
|
|
2458
|
-
enqueueOffline({ type: "job", target: target2, opts });
|
|
2458
|
+
enqueueOffline({ type: "job", target: target2, opts: opts2 });
|
|
2459
2459
|
}
|
|
2460
2460
|
return target2;
|
|
2461
2461
|
},
|
|
2462
|
-
async workflow(name, steps,
|
|
2463
|
-
workflowRegistrations.set(name, { name, steps, opts });
|
|
2462
|
+
async workflow(name, steps, opts2) {
|
|
2463
|
+
workflowRegistrations.set(name, { name, steps, opts: opts2 });
|
|
2464
2464
|
if (isOnline) {
|
|
2465
2465
|
await reconcileRegistrations(`workflow:${name}`).catch((err) => {
|
|
2466
2466
|
reportSDKError(`register-workflow:${name}`, err);
|
|
2467
2467
|
});
|
|
2468
2468
|
} else {
|
|
2469
|
-
enqueueOffline({ type: "workflow", name, steps, opts });
|
|
2469
|
+
enqueueOffline({ type: "workflow", name, steps, opts: opts2 });
|
|
2470
2470
|
}
|
|
2471
2471
|
return name;
|
|
2472
2472
|
},
|
|
2473
|
-
async executeWorkflow(service2, name, input,
|
|
2473
|
+
async executeWorkflow(service2, name, input, opts2) {
|
|
2474
2474
|
await _controlReady;
|
|
2475
2475
|
const svc2 = service2.trim();
|
|
2476
2476
|
const wfName = name.trim();
|
|
@@ -2510,14 +2510,14 @@ function servicebridge(url, serviceKey, serviceOrOpts = {}, maybeGlobalOpts = {}
|
|
|
2510
2510
|
});
|
|
2511
2511
|
});
|
|
2512
2512
|
},
|
|
2513
|
-
startHttpSpan(
|
|
2513
|
+
startHttpSpan(opts2) {
|
|
2514
2514
|
const tc = traceStorage.getStore();
|
|
2515
|
-
const traceId =
|
|
2515
|
+
const traceId = opts2.traceId ?? tc?.traceId ?? crypto.randomUUID();
|
|
2516
2516
|
const spanId = crypto.randomUUID();
|
|
2517
|
-
const parentSpanId =
|
|
2518
|
-
const fn = `${
|
|
2517
|
+
const parentSpanId = opts2.parentSpanId ?? tc?.spanId ?? "";
|
|
2518
|
+
const fn = `${opts2.method} ${opts2.path}`;
|
|
2519
2519
|
const startedAt = Date.now();
|
|
2520
|
-
const inputBuf = toJsonBuffer({ method:
|
|
2520
|
+
const inputBuf = toJsonBuffer({ method: opts2.method, path: opts2.path });
|
|
2521
2521
|
reportCallStartAsync(traceId, spanId, parentSpanId, fn, startedAt, inputBuf, 1, "http");
|
|
2522
2522
|
return {
|
|
2523
2523
|
traceId,
|
|
@@ -2532,17 +2532,17 @@ function servicebridge(url, serviceKey, serviceOrOpts = {}, maybeGlobalOpts = {}
|
|
|
2532
2532
|
}
|
|
2533
2533
|
};
|
|
2534
2534
|
},
|
|
2535
|
-
async registerHttpEndpoint(
|
|
2535
|
+
async registerHttpEndpoint(opts2) {
|
|
2536
2536
|
await _controlReady;
|
|
2537
2537
|
if (serveState) {
|
|
2538
|
-
const existing = httpRegistrations.get(`${
|
|
2538
|
+
const existing = httpRegistrations.get(`${opts2.method}:${opts2.route}`);
|
|
2539
2539
|
if (!existing) {
|
|
2540
|
-
httpRegistrations.set(`${
|
|
2541
|
-
method:
|
|
2542
|
-
route:
|
|
2543
|
-
allowedCallers:
|
|
2544
|
-
requestSchemaJson:
|
|
2545
|
-
responseSchemaJson:
|
|
2540
|
+
httpRegistrations.set(`${opts2.method}:${opts2.route}`, {
|
|
2541
|
+
method: opts2.method,
|
|
2542
|
+
route: opts2.route,
|
|
2543
|
+
allowedCallers: opts2.allowedCallers,
|
|
2544
|
+
requestSchemaJson: opts2.requestSchemaJson,
|
|
2545
|
+
responseSchemaJson: opts2.responseSchemaJson
|
|
2546
2546
|
});
|
|
2547
2547
|
reconcileRegistrations("http-register").catch((err) => {
|
|
2548
2548
|
reportSDKError("register-http-endpoint", err);
|
|
@@ -2550,9 +2550,9 @@ function servicebridge(url, serviceKey, serviceOrOpts = {}, maybeGlobalOpts = {}
|
|
|
2550
2550
|
}
|
|
2551
2551
|
}
|
|
2552
2552
|
},
|
|
2553
|
-
watchTrace(traceId,
|
|
2554
|
-
const key =
|
|
2555
|
-
const fromSeq =
|
|
2553
|
+
watchTrace(traceId, opts2) {
|
|
2554
|
+
const key = opts2?.key ?? "";
|
|
2555
|
+
const fromSeq = opts2?.fromSequence ?? 0;
|
|
2556
2556
|
const WATCH_TRACE_QUEUE_LIMIT = 256;
|
|
2557
2557
|
const WATCH_TRACE_RETRY_MIN_MS = 500;
|
|
2558
2558
|
const WATCH_TRACE_RETRY_MAX_MS = 5000;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "service-bridge",
|
|
3
|
-
"version": "1.8.4-dev.
|
|
3
|
+
"version": "1.8.4-dev.46",
|
|
4
4
|
"type": "module",
|
|
5
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
6
|
"keywords": [
|