@forwardimpact/librpc 0.1.111 → 0.1.112
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 +15 -2
- package/bin/fit-unary.js +2 -2
- package/package.json +1 -1
- package/src/auth.js +8 -7
- package/src/base.js +10 -10
- package/src/client.js +46 -14
- package/src/health.js +4 -4
- package/src/index.js +10 -10
- package/src/interceptor.js +4 -3
- package/src/server.js +13 -11
package/README.md
CHANGED
|
@@ -13,11 +13,24 @@ transport.
|
|
|
13
13
|
import { Server, Client, createClient, createTracer } from '@forwardimpact/librpc';
|
|
14
14
|
```
|
|
15
15
|
|
|
16
|
+
## Unary deadlines
|
|
17
|
+
|
|
18
|
+
Every unary call carries one absolute gRPC deadline. The deadline spans
|
|
19
|
+
all retry attempts. The default is 60s. That default matches the slowest
|
|
20
|
+
unary in practice (embedding model inference). A hung connection fails
|
|
21
|
+
with `DEADLINE_EXCEEDED`. Retryable errors (UNAVAILABLE and friends)
|
|
22
|
+
cycle only until the call spends its budget. The first attempt past the
|
|
23
|
+
deadline fails immediately. `DEADLINE_EXCEEDED` gets no retry. Override
|
|
24
|
+
the deadline per service with the `deadline` config key (milliseconds)
|
|
25
|
+
in the service's config block. Once the key exists there, the
|
|
26
|
+
`SERVICE_{NAME}_DEADLINE` env var overrides it. Streaming calls are
|
|
27
|
+
exempt. Keepalive bounds them, and long-lived streams are legitimate.
|
|
28
|
+
|
|
16
29
|
## Documentation
|
|
17
30
|
|
|
18
31
|
- [Ship a Service Endpoint](https://www.forwardimpact.team/docs/libraries/typed-contracts/ship-endpoint/index.md)
|
|
19
32
|
— ship and consume a gRPC service with typed contracts, authentication,
|
|
20
|
-
retries, and health checks
|
|
33
|
+
retries, and health checks. `fit-unary` is the command-line client for it.
|
|
21
34
|
- [Keep Types Synced with Proto Definitions](https://www.forwardimpact.team/docs/libraries/typed-contracts/index.md)
|
|
22
|
-
— the full workflow
|
|
35
|
+
— the full workflow to define proto contracts and generate typed base
|
|
23
36
|
classes and clients.
|
package/bin/fit-unary.js
CHANGED
|
@@ -23,13 +23,13 @@ const definition = {
|
|
|
23
23
|
title: "Ship a Service Endpoint",
|
|
24
24
|
url: "https://www.forwardimpact.team/docs/libraries/typed-contracts/ship-endpoint/index.md",
|
|
25
25
|
description:
|
|
26
|
-
"Ship and consume a gRPC service with typed contracts, authentication, retries, and health checks
|
|
26
|
+
"Ship and consume a gRPC service with typed contracts, authentication, retries, and health checks. fit-unary is the command-line client for it.",
|
|
27
27
|
},
|
|
28
28
|
{
|
|
29
29
|
title: "Keep Types Synced with Proto Definitions",
|
|
30
30
|
url: "https://www.forwardimpact.team/docs/libraries/typed-contracts/index.md",
|
|
31
31
|
description:
|
|
32
|
-
"The full workflow
|
|
32
|
+
"The full workflow to define proto contracts and generate typed base classes and clients.",
|
|
33
33
|
},
|
|
34
34
|
],
|
|
35
35
|
};
|
package/package.json
CHANGED
package/src/auth.js
CHANGED
|
@@ -12,7 +12,7 @@ export class HmacAuth {
|
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
14
|
* Creates a new HMAC authenticator instance
|
|
15
|
-
* @param {string} secret - Shared secret key
|
|
15
|
+
* @param {string} secret - Shared secret key to generate the HMAC (minimum 32 characters)
|
|
16
16
|
* @param {number} tokenLifetimeSeconds - Token lifetime in seconds (default: 60)
|
|
17
17
|
* @param {object} [options] - Optional collaborators
|
|
18
18
|
* @param {() => number} [options.now] - Injectable clock (default: Date.now)
|
|
@@ -36,7 +36,7 @@ export class HmacAuth {
|
|
|
36
36
|
|
|
37
37
|
/**
|
|
38
38
|
* Generates an HMAC token for the specified service
|
|
39
|
-
* @param {string} serviceId - Identifier of the service
|
|
39
|
+
* @param {string} serviceId - Identifier of the service that requests authentication
|
|
40
40
|
* @returns {string} Base64 encoded HMAC token
|
|
41
41
|
* @throws {Error} When serviceId is invalid
|
|
42
42
|
*/
|
|
@@ -59,7 +59,7 @@ export class HmacAuth {
|
|
|
59
59
|
/**
|
|
60
60
|
* Verifies an HMAC token and extracts service information
|
|
61
61
|
* @param {string} token - Base64 encoded HMAC token to verify
|
|
62
|
-
* @returns {object} Verification result
|
|
62
|
+
* @returns {object} Verification result with serviceId and isValid
|
|
63
63
|
* @throws {Error} When token format is invalid
|
|
64
64
|
*/
|
|
65
65
|
verifyToken(token) {
|
|
@@ -95,7 +95,7 @@ export class HmacAuth {
|
|
|
95
95
|
};
|
|
96
96
|
}
|
|
97
97
|
|
|
98
|
-
// Check token
|
|
98
|
+
// Check whether the token expired
|
|
99
99
|
const now = this.#now();
|
|
100
100
|
if (now - timestamp > this.#tokenLifetimeMs) {
|
|
101
101
|
return {
|
|
@@ -152,7 +152,8 @@ export class HmacAuth {
|
|
|
152
152
|
|
|
153
153
|
/**
|
|
154
154
|
* gRPC interceptor for HMAC-based service authentication
|
|
155
|
-
*
|
|
155
|
+
* Adds a token to each outgoing request automatically and validates each
|
|
156
|
+
* incoming request
|
|
156
157
|
*/
|
|
157
158
|
export class Interceptor {
|
|
158
159
|
#authenticator;
|
|
@@ -232,14 +233,14 @@ export class Interceptor {
|
|
|
232
233
|
};
|
|
233
234
|
}
|
|
234
235
|
|
|
235
|
-
// Add service ID to call context
|
|
236
|
+
// Add the service ID to the call context so handlers can use it
|
|
236
237
|
call.serviceId = verification.serviceId;
|
|
237
238
|
};
|
|
238
239
|
}
|
|
239
240
|
|
|
240
241
|
/**
|
|
241
242
|
* Validates an incoming gRPC call's authentication
|
|
242
|
-
* This
|
|
243
|
+
* This helper method validates the authentication manually
|
|
243
244
|
* @param {object} call - gRPC call object
|
|
244
245
|
* @returns {object} Verification result with isValid, serviceId, and error properties
|
|
245
246
|
*/
|
package/src/base.js
CHANGED
|
@@ -6,7 +6,7 @@ import { Interceptor, HmacAuth } from "./auth.js";
|
|
|
6
6
|
import { definitions } from "./generated/definitions/exports.js";
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
|
-
* Capitalize first letter of a string
|
|
9
|
+
* Capitalize the first letter of a string
|
|
10
10
|
* @param {string} str - String to capitalize
|
|
11
11
|
* @returns {string} Capitalized string
|
|
12
12
|
*/
|
|
@@ -16,18 +16,18 @@ export function capitalizeFirstLetter(str) {
|
|
|
16
16
|
|
|
17
17
|
/**
|
|
18
18
|
* Default grpc factory that creates gRPC dependencies
|
|
19
|
-
* @returns {object} Object
|
|
19
|
+
* @returns {object} Object with grpc
|
|
20
20
|
*/
|
|
21
21
|
export function createGrpc() {
|
|
22
22
|
return { grpc };
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
/**
|
|
26
|
-
* Default auth factory that creates an authentication interceptor.
|
|
27
|
-
* `SERVICE_SECRET` from the injected `runtime.proc.env
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
26
|
+
* Default auth factory that creates an authentication interceptor. It reads
|
|
27
|
+
* `SERVICE_SECRET` from the injected `runtime.proc.env`. It does not construct
|
|
28
|
+
* its own process collaborator. The entry point threads the runtime through
|
|
29
|
+
* `Server`/`Client` (no leaf-collaborator construction in src, Success
|
|
30
|
+
* Criterion 9).
|
|
31
31
|
* @param {string} serviceName - Name of the service for the interceptor
|
|
32
32
|
* @param {import("@forwardimpact/libutil/runtime").Runtime} runtime - Injected runtime bag
|
|
33
33
|
* @returns {Interceptor} Configured interceptor instance
|
|
@@ -91,8 +91,8 @@ export class Rpc {
|
|
|
91
91
|
const { grpc } = grpcFn();
|
|
92
92
|
this.#grpc = grpc;
|
|
93
93
|
|
|
94
|
-
//
|
|
95
|
-
// injected runtime
|
|
94
|
+
// Set up authentication. The default factory reads SERVICE_SECRET off the
|
|
95
|
+
// injected runtime. A mock authFn ignores it.
|
|
96
96
|
this.#auth = authFn(this.config.name, runtime);
|
|
97
97
|
|
|
98
98
|
// Create observer with logger and tracer
|
|
@@ -124,7 +124,7 @@ export class Rpc {
|
|
|
124
124
|
tracer = () => this.#observer.tracer();
|
|
125
125
|
|
|
126
126
|
/**
|
|
127
|
-
* Get pre-compiled service definition
|
|
127
|
+
* Get the pre-compiled service definition
|
|
128
128
|
* @param {string} serviceName - Service name (e.g., "Agent", "Vector")
|
|
129
129
|
* @returns {object} Pre-compiled service definition
|
|
130
130
|
*/
|
package/src/client.js
CHANGED
|
@@ -9,12 +9,22 @@ import {
|
|
|
9
9
|
capitalizeFirstLetter,
|
|
10
10
|
} from "./base.js";
|
|
11
11
|
|
|
12
|
+
// No unary call waits forever. Every call carries one absolute gRPC
|
|
13
|
+
// deadline that spans all retry attempts. So a hung connection fails with
|
|
14
|
+
// DEADLINE_EXCEEDED, and so does a retryable-error grind (exponential
|
|
15
|
+
// backoff on UNAVAILABLE). Neither one blocks the caller forever. The
|
|
16
|
+
// default fits the slowest unary in practice (embedding model inference).
|
|
17
|
+
// Override it per service with the `deadline` config key.
|
|
18
|
+
const DEFAULT_UNARY_DEADLINE_MS = 60_000;
|
|
19
|
+
|
|
12
20
|
/**
|
|
13
|
-
* Creates a gRPC client with consistent API
|
|
21
|
+
* Creates a gRPC client with a consistent API from pre-compiled definitions
|
|
14
22
|
*/
|
|
15
23
|
export class Client extends Rpc {
|
|
16
24
|
#client;
|
|
17
25
|
#retry;
|
|
26
|
+
#clock;
|
|
27
|
+
#deadlineMs;
|
|
18
28
|
|
|
19
29
|
/**
|
|
20
30
|
* Creates a new Client instance
|
|
@@ -25,7 +35,7 @@ export class Client extends Rpc {
|
|
|
25
35
|
* @param {(serviceName: string, logger: object, tracer: object) => object} observerFn - Observer factory
|
|
26
36
|
* @param {() => {grpc: object}} grpcFn - gRPC factory
|
|
27
37
|
* @param {(serviceName: string, runtime: object) => object} authFn - Auth factory
|
|
28
|
-
* @param {import("@forwardimpact/libutil").Retry} [retry] - Optional retry instance
|
|
38
|
+
* @param {import("@forwardimpact/libutil").Retry} [retry] - Optional retry instance that handles transient errors
|
|
29
39
|
*/
|
|
30
40
|
constructor(
|
|
31
41
|
config,
|
|
@@ -39,18 +49,20 @@ export class Client extends Rpc {
|
|
|
39
49
|
) {
|
|
40
50
|
super(config, runtime, logger, tracer, observerFn, grpcFn, authFn);
|
|
41
51
|
this.#retry = retry || createRetry({ retries: 10, delay: 1000 });
|
|
52
|
+
this.#clock = runtime.clock;
|
|
53
|
+
this.#deadlineMs = Number(config.deadline) || DEFAULT_UNARY_DEADLINE_MS;
|
|
42
54
|
this.#setupClient();
|
|
43
55
|
}
|
|
44
56
|
|
|
45
57
|
/**
|
|
46
|
-
* Sets up the gRPC client
|
|
58
|
+
* Sets up the gRPC client from the pre-compiled definition
|
|
47
59
|
* @private
|
|
48
60
|
*/
|
|
49
61
|
#setupClient() {
|
|
50
62
|
const serviceName = capitalizeFirstLetter(this.config.name);
|
|
51
63
|
const serviceDefinition = this.getServiceDefinition(serviceName);
|
|
52
64
|
|
|
53
|
-
//
|
|
65
|
+
// If the config uses the default host, resort to a well-known service name
|
|
54
66
|
const host =
|
|
55
67
|
this.config.host === "0.0.0.0"
|
|
56
68
|
? `${this.config.name}.guide.local`
|
|
@@ -61,7 +73,7 @@ export class Client extends Rpc {
|
|
|
61
73
|
interceptors: [this.auth().createClientInterceptor()],
|
|
62
74
|
};
|
|
63
75
|
|
|
64
|
-
// Configure client with keepalive for long-running streams
|
|
76
|
+
// Configure the client with keepalive for long-running streams
|
|
65
77
|
// https://github.com/grpc/grpc-node/blob/master/doc/keepalive.md
|
|
66
78
|
const channelOptions = {
|
|
67
79
|
"grpc.keepalive_time_ms": 30000, // Send keepalive ping every 30 seconds
|
|
@@ -72,7 +84,7 @@ export class Client extends Rpc {
|
|
|
72
84
|
};
|
|
73
85
|
const clientCredentials = this.grpc().credentials.createInsecure();
|
|
74
86
|
|
|
75
|
-
// Create client
|
|
87
|
+
// Create the client from the pre-compiled service definition
|
|
76
88
|
const ClientConstructor = this.grpc().makeGenericClientConstructor(
|
|
77
89
|
serviceDefinition,
|
|
78
90
|
serviceName,
|
|
@@ -81,6 +93,15 @@ export class Client extends Rpc {
|
|
|
81
93
|
this.#client = new ClientConstructor(uri, clientCredentials, options);
|
|
82
94
|
}
|
|
83
95
|
|
|
96
|
+
/**
|
|
97
|
+
* Close the underlying gRPC channel. This releases its sockets and timers
|
|
98
|
+
* so short-lived processes (CLIs, tests) can exit promptly.
|
|
99
|
+
* @returns {void}
|
|
100
|
+
*/
|
|
101
|
+
close() {
|
|
102
|
+
this.#client.close();
|
|
103
|
+
}
|
|
104
|
+
|
|
84
105
|
/**
|
|
85
106
|
* Call a gRPC method with automatic CLIENT span tracing and observability.
|
|
86
107
|
* Supports unary calls.
|
|
@@ -199,7 +220,12 @@ export class Client extends Rpc {
|
|
|
199
220
|
}
|
|
200
221
|
|
|
201
222
|
/**
|
|
202
|
-
* Internal unary call handler with retry logic
|
|
223
|
+
* Internal unary call handler with retry logic. The handler computes the
|
|
224
|
+
* absolute deadline once and shares it with every retry attempt. So
|
|
225
|
+
* retryable errors repeat only until the call spends its budget. The
|
|
226
|
+
* first attempt past the deadline fails at once with DEADLINE_EXCEEDED,
|
|
227
|
+
* which is deliberately not retryable. Streaming calls are exempt.
|
|
228
|
+
* Keepalive bounds them, and long-lived streams are legitimate.
|
|
203
229
|
* @param {string} methodName - The name of the method
|
|
204
230
|
* @param {object} request - Request object
|
|
205
231
|
* @param {object} metadata - gRPC Metadata instance
|
|
@@ -207,15 +233,21 @@ export class Client extends Rpc {
|
|
|
207
233
|
* @private
|
|
208
234
|
*/
|
|
209
235
|
async #performUnaryCall(methodName, request, metadata) {
|
|
236
|
+
const options = { deadline: this.#clock.now() + this.#deadlineMs };
|
|
210
237
|
return await this.#retry.execute(() => {
|
|
211
238
|
return new Promise((resolve, reject) => {
|
|
212
|
-
this.#client[methodName](
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
239
|
+
this.#client[methodName](
|
|
240
|
+
request,
|
|
241
|
+
metadata,
|
|
242
|
+
options,
|
|
243
|
+
(error, response) => {
|
|
244
|
+
if (error) {
|
|
245
|
+
reject(error);
|
|
246
|
+
} else {
|
|
247
|
+
resolve(response);
|
|
248
|
+
}
|
|
249
|
+
},
|
|
250
|
+
);
|
|
219
251
|
});
|
|
220
252
|
});
|
|
221
253
|
}
|
package/src/health.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* gRPC Health Check protocol (grpc.health.v1.Health/Check)
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* Messages use raw protobuf wire encoding
|
|
6
|
-
* one enum/varint field
|
|
4
|
+
* This is a manual service definition. It needs no .proto file and no
|
|
5
|
+
* proto-loader. Messages use raw protobuf wire encoding. The encoding is
|
|
6
|
+
* trivial: one string field and one enum/varint field.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
/** Serving status enum
|
|
9
|
+
/** Serving status enum that matches grpc.health.v1 */
|
|
10
10
|
export const ServingStatus = {
|
|
11
11
|
UNKNOWN: 0,
|
|
12
12
|
SERVING: 1,
|
package/src/index.js
CHANGED
|
@@ -23,18 +23,18 @@ export const clients = exports.clients || {};
|
|
|
23
23
|
|
|
24
24
|
/**
|
|
25
25
|
* Creates a tracer instance for a service
|
|
26
|
-
*
|
|
27
|
-
* @param {string} serviceName - Name of the service
|
|
26
|
+
* Call this factory at startup in server.js files or when you create clients
|
|
27
|
+
* @param {string} serviceName - Name of the service to trace
|
|
28
28
|
* @returns {Promise<Tracer>} Configured tracer instance
|
|
29
|
-
* @throws {Error} If span service configuration
|
|
29
|
+
* @throws {Error} If the factory cannot load the span service configuration
|
|
30
30
|
*/
|
|
31
31
|
export async function createTracer(serviceName) {
|
|
32
32
|
const spanConfig = await createServiceConfig("span");
|
|
33
33
|
const { SpanClient } = clients;
|
|
34
|
-
// createTracer is a composition-root factory
|
|
35
|
-
// runtime as its DI root
|
|
36
|
-
// auth reads SERVICE_SECRET off the bag
|
|
37
|
-
//
|
|
34
|
+
// createTracer is a composition-root factory. It builds the production
|
|
35
|
+
// runtime as its DI root. It threads the runtime into the SpanClient, so the
|
|
36
|
+
// client's auth reads SERVICE_SECRET off the bag. It also threads the runtime
|
|
37
|
+
// clock into the Tracer, and so into every Span.
|
|
38
38
|
const runtime = createDefaultRuntime();
|
|
39
39
|
const spanClient = new SpanClient(spanConfig, runtime);
|
|
40
40
|
return new Tracer({
|
|
@@ -46,7 +46,7 @@ export async function createTracer(serviceName) {
|
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
/**
|
|
49
|
-
* Factory function to create a client instance with optional
|
|
49
|
+
* Factory function to create a client instance with an optional logger and tracer
|
|
50
50
|
* @param {string} name - Service name (e.g., "memory", "llm", "tool")
|
|
51
51
|
* @param {object} [logger] - Optional logger instance
|
|
52
52
|
* @param {import("@forwardimpact/libtelemetry").Tracer} [tracer] - Optional tracer instance for distributed tracing
|
|
@@ -64,10 +64,10 @@ export async function createClient(name, logger = null, tracer = null) {
|
|
|
64
64
|
);
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
-
// Create config for the service
|
|
67
|
+
// Create the config for the service
|
|
68
68
|
const config = await createServiceConfig(name);
|
|
69
69
|
|
|
70
|
-
// createClient is a composition-root factory
|
|
70
|
+
// createClient is a composition-root factory. Build the production runtime
|
|
71
71
|
// here and thread it so the client's auth reads SERVICE_SECRET off the bag.
|
|
72
72
|
const runtime = createDefaultRuntime();
|
|
73
73
|
|
package/src/interceptor.js
CHANGED
|
@@ -2,7 +2,8 @@ import grpc from "@grpc/grpc-js";
|
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* gRPC interceptor for HMAC-based service authentication
|
|
5
|
-
*
|
|
5
|
+
* Adds a token to each outgoing request automatically and validates each
|
|
6
|
+
* incoming request
|
|
6
7
|
*/
|
|
7
8
|
export class Interceptor {
|
|
8
9
|
#authenticator;
|
|
@@ -82,14 +83,14 @@ export class Interceptor {
|
|
|
82
83
|
};
|
|
83
84
|
}
|
|
84
85
|
|
|
85
|
-
// Add service ID to call context
|
|
86
|
+
// Add the service ID to the call context so handlers can use it
|
|
86
87
|
call.serviceId = verification.serviceId;
|
|
87
88
|
};
|
|
88
89
|
}
|
|
89
90
|
|
|
90
91
|
/**
|
|
91
92
|
* Validates an incoming gRPC call's authentication
|
|
92
|
-
* This
|
|
93
|
+
* This helper method validates the authentication manually
|
|
93
94
|
* @param {object} call - gRPC call object
|
|
94
95
|
* @returns {object} Verification result with isValid, serviceId, and error properties
|
|
95
96
|
*/
|
package/src/server.js
CHANGED
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
import { healthDefinition, createHealthHandlers } from "./health.js";
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
|
-
* gRPC Server class
|
|
11
|
+
* gRPC Server class that uses pre-compiled service definitions
|
|
12
12
|
* Takes a service instance and creates a gRPC server around it
|
|
13
13
|
*/
|
|
14
14
|
export class Server extends Rpc {
|
|
@@ -47,7 +47,7 @@ export class Server extends Rpc {
|
|
|
47
47
|
|
|
48
48
|
/** Starts the gRPC server */
|
|
49
49
|
async start() {
|
|
50
|
-
// Configure server with keepalive for long-running streams
|
|
50
|
+
// Configure the server with keepalive for long-running streams
|
|
51
51
|
// https://github.com/grpc/grpc-node/blob/master/doc/keepalive.md
|
|
52
52
|
this.#server = new (this.grpc().Server)({
|
|
53
53
|
"grpc.keepalive_time_ms": 30000, // Send keepalive ping every 30 seconds
|
|
@@ -57,19 +57,19 @@ export class Server extends Rpc {
|
|
|
57
57
|
"grpc.http2.max_pings_without_data": 0, // Unlimited pings without data
|
|
58
58
|
});
|
|
59
59
|
|
|
60
|
-
// Get pre-compiled service definition
|
|
60
|
+
// Get the pre-compiled service definition
|
|
61
61
|
const serviceName = capitalizeFirstLetter(this.config.name);
|
|
62
62
|
const definition = this.getServiceDefinition(serviceName);
|
|
63
63
|
|
|
64
|
-
// Get handlers from the service instance
|
|
64
|
+
// Get the handlers from the service instance
|
|
65
65
|
const handlers = this.#service.getHandlers();
|
|
66
66
|
|
|
67
|
-
// Wrap handlers
|
|
67
|
+
// Wrap the handlers so they authenticate and handle errors
|
|
68
68
|
const wrappedHandlers = this.#wrapHandlers(handlers, definition);
|
|
69
69
|
|
|
70
70
|
this.#server.addService(definition, wrappedHandlers);
|
|
71
71
|
|
|
72
|
-
// Register standard gRPC health check (no auth, no observer
|
|
72
|
+
// Register the standard gRPC health check (no auth, no observer wrap)
|
|
73
73
|
this.#server.addService(
|
|
74
74
|
healthDefinition,
|
|
75
75
|
createHealthHandlers(serviceName),
|
|
@@ -82,7 +82,7 @@ export class Server extends Rpc {
|
|
|
82
82
|
}
|
|
83
83
|
|
|
84
84
|
/**
|
|
85
|
-
* Wraps handlers
|
|
85
|
+
* Wraps the handlers so they authenticate and handle errors
|
|
86
86
|
* @param {object} handlers - Service method handlers
|
|
87
87
|
* @param {object} definition - Service definition
|
|
88
88
|
* @returns {object} Wrapped handlers
|
|
@@ -101,7 +101,8 @@ export class Server extends Rpc {
|
|
|
101
101
|
}
|
|
102
102
|
|
|
103
103
|
/**
|
|
104
|
-
* Wraps a streaming handler with tracing, authentication, and error
|
|
104
|
+
* Wraps a streaming handler with tracing, authentication, and error
|
|
105
|
+
* handling through Observer
|
|
105
106
|
* @param {string} methodName - Method name for tracing
|
|
106
107
|
* @param {Function} handler - Streaming handler function
|
|
107
108
|
* @returns {Function} Wrapped handler
|
|
@@ -142,7 +143,8 @@ export class Server extends Rpc {
|
|
|
142
143
|
}
|
|
143
144
|
|
|
144
145
|
/**
|
|
145
|
-
* Wraps a unary handler with tracing, authentication, and error handling
|
|
146
|
+
* Wraps a unary handler with tracing, authentication, and error handling
|
|
147
|
+
* through Observer
|
|
146
148
|
* @param {string} methodName - Method name for tracing
|
|
147
149
|
* @param {Function} handler - Unary handler function
|
|
148
150
|
* @returns {Function} Wrapped handler
|
|
@@ -185,7 +187,7 @@ export class Server extends Rpc {
|
|
|
185
187
|
}
|
|
186
188
|
|
|
187
189
|
/**
|
|
188
|
-
* Binds server to the specified URI
|
|
190
|
+
* Binds the server to the specified URI
|
|
189
191
|
* @param {string} uri - Server URI to bind to
|
|
190
192
|
* @returns {Promise<number>} Bound port number
|
|
191
193
|
*/
|
|
@@ -213,7 +215,7 @@ export class Server extends Rpc {
|
|
|
213
215
|
const shutdown = async () => {
|
|
214
216
|
this.observer().logger()?.info("Server", "Shutting down...");
|
|
215
217
|
|
|
216
|
-
// Call service shutdown if it exists
|
|
218
|
+
// Call the service shutdown if it exists
|
|
217
219
|
if (typeof this.#service.shutdown === "function") {
|
|
218
220
|
await this.#service.shutdown();
|
|
219
221
|
}
|