@forwardimpact/librpc 0.1.104 → 0.1.106
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/bin/fit-unary.js +5 -15
- package/package.json +3 -3
- package/src/base.js +20 -10
- package/src/client.js +4 -2
- package/src/index.js +14 -3
- package/src/server.js +20 -21
package/bin/fit-unary.js
CHANGED
|
@@ -8,21 +8,8 @@ import { createDefaultRuntime } from "@forwardimpact/libutil/runtime";
|
|
|
8
8
|
|
|
9
9
|
const runtime = createDefaultRuntime();
|
|
10
10
|
|
|
11
|
-
// `bun build --compile` injects FIT_UNARY_VERSION via --define, eliminating
|
|
12
|
-
// the readFileSync branch in the compiled binary (which would ENOENT against
|
|
13
|
-
// the bunfs virtual mount). Source execution falls through to package.json.
|
|
14
|
-
const VERSION =
|
|
15
|
-
runtime.proc.env.FIT_UNARY_VERSION ||
|
|
16
|
-
JSON.parse(
|
|
17
|
-
runtime.fsSync.readFileSync(
|
|
18
|
-
new URL("../package.json", import.meta.url),
|
|
19
|
-
"utf8",
|
|
20
|
-
),
|
|
21
|
-
).version;
|
|
22
|
-
|
|
23
11
|
const definition = {
|
|
24
12
|
name: "fit-unary",
|
|
25
|
-
version: VERSION,
|
|
26
13
|
description: "Make a unary gRPC call to a service",
|
|
27
14
|
usage: "fit-unary <service> <method> [json-request]",
|
|
28
15
|
globalOptions: {
|
|
@@ -33,8 +20,11 @@ const definition = {
|
|
|
33
20
|
examples: ['fit-unary memory GetWindow \'{"resource_id":"..."}\''],
|
|
34
21
|
};
|
|
35
22
|
|
|
36
|
-
const cli = createCli(definition, {
|
|
37
|
-
|
|
23
|
+
const cli = createCli(definition, {
|
|
24
|
+
runtime,
|
|
25
|
+
packageJsonUrl: new URL("../package.json", import.meta.url),
|
|
26
|
+
});
|
|
27
|
+
const logger = createLogger("cli", runtime);
|
|
38
28
|
|
|
39
29
|
/**
|
|
40
30
|
* Makes a unary gRPC call to a service
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@forwardimpact/librpc",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.106",
|
|
4
4
|
"description": "gRPC server and client framework — ship service endpoints without reimplementing transport.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"grpc",
|
|
@@ -20,9 +20,9 @@
|
|
|
20
20
|
"jobs": [
|
|
21
21
|
{
|
|
22
22
|
"user": "Platform Builders",
|
|
23
|
-
"goal": "
|
|
23
|
+
"goal": "Ship Service Endpoints Without Boilerplate",
|
|
24
24
|
"trigger": "Starting a new service and reaching for last project's copy-pasted transport boilerplate.",
|
|
25
|
-
"bigHire": "ship a service endpoint without reimplementing transport.",
|
|
25
|
+
"bigHire": "ship a gRPC service endpoint without reimplementing transport.",
|
|
26
26
|
"littleHire": "call a service without managing connections or retries.",
|
|
27
27
|
"competesWith": "copy-pasting boilerplate; hand-writing protobuf clients; tolerating the duplication"
|
|
28
28
|
}
|
package/src/base.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import grpc from "@grpc/grpc-js";
|
|
2
2
|
|
|
3
3
|
import { createObserver } from "@forwardimpact/libtelemetry";
|
|
4
|
-
import { createDefaultProc } from "@forwardimpact/libutil/runtime";
|
|
5
4
|
|
|
6
5
|
import { Interceptor, HmacAuth } from "./auth.js";
|
|
7
6
|
import { definitions } from "./generated/definitions/exports.js";
|
|
@@ -24,15 +23,22 @@ export function createGrpc() {
|
|
|
24
23
|
}
|
|
25
24
|
|
|
26
25
|
/**
|
|
27
|
-
* Default auth factory that creates authentication interceptor.
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
26
|
+
* Default auth factory that creates an authentication interceptor. Reads
|
|
27
|
+
* `SERVICE_SECRET` from the injected `runtime.proc.env` rather than
|
|
28
|
+
* constructing its own process collaborator — the runtime is threaded from the
|
|
29
|
+
* entry point through `Server`/`Client` (no leaf-collaborator construction in
|
|
30
|
+
* src, Success Criterion 9).
|
|
31
31
|
* @param {string} serviceName - Name of the service for the interceptor
|
|
32
|
+
* @param {import("@forwardimpact/libutil/runtime").Runtime} runtime - Injected runtime bag
|
|
32
33
|
* @returns {Interceptor} Configured interceptor instance
|
|
33
34
|
*/
|
|
34
|
-
export function createAuth(serviceName) {
|
|
35
|
-
|
|
35
|
+
export function createAuth(serviceName, runtime) {
|
|
36
|
+
if (!runtime?.proc) {
|
|
37
|
+
throw new Error(
|
|
38
|
+
`createAuth requires an injected runtime for service ${serviceName}`,
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
const secret = runtime.proc.env.SERVICE_SECRET;
|
|
36
42
|
if (!secret) {
|
|
37
43
|
throw new Error(
|
|
38
44
|
`SERVICE_SECRET environment variable is required for service ${serviceName}`,
|
|
@@ -54,14 +60,16 @@ export class Rpc {
|
|
|
54
60
|
/**
|
|
55
61
|
* Creates a new Rpc instance
|
|
56
62
|
* @param {object} config - Configuration object
|
|
63
|
+
* @param {import("@forwardimpact/libutil/runtime").Runtime} runtime - Injected runtime bag (required), threaded to `authFn`
|
|
57
64
|
* @param {object} [logger] - Optional logger instance
|
|
58
65
|
* @param {import("@forwardimpact/libtelemetry").Tracer} [tracer] - Optional tracer for distributed tracing
|
|
59
66
|
* @param {(serviceName: string, logger: object, tracer: object) => object} observerFn - Observer factory
|
|
60
67
|
* @param {() => {grpc: object}} grpcFn - gRPC factory
|
|
61
|
-
* @param {(serviceName: string) => object} authFn - Auth factory
|
|
68
|
+
* @param {(serviceName: string, runtime: object) => object} authFn - Auth factory
|
|
62
69
|
*/
|
|
63
70
|
constructor(
|
|
64
71
|
config,
|
|
72
|
+
runtime,
|
|
65
73
|
logger = null,
|
|
66
74
|
tracer = null,
|
|
67
75
|
observerFn = createObserver,
|
|
@@ -69,6 +77,7 @@ export class Rpc {
|
|
|
69
77
|
authFn = createAuth,
|
|
70
78
|
) {
|
|
71
79
|
if (!config) throw new Error("config is required");
|
|
80
|
+
if (!runtime) throw new Error("runtime is required");
|
|
72
81
|
if (typeof observerFn !== "function")
|
|
73
82
|
throw new Error("observerFn must be a function");
|
|
74
83
|
if (typeof grpcFn !== "function")
|
|
@@ -82,8 +91,9 @@ export class Rpc {
|
|
|
82
91
|
const { grpc } = grpcFn();
|
|
83
92
|
this.#grpc = grpc;
|
|
84
93
|
|
|
85
|
-
// Setup authentication
|
|
86
|
-
|
|
94
|
+
// Setup authentication (the default factory reads SERVICE_SECRET off the
|
|
95
|
+
// injected runtime; a mock authFn ignores it)
|
|
96
|
+
this.#auth = authFn(this.config.name, runtime);
|
|
87
97
|
|
|
88
98
|
// Create observer with logger and tracer
|
|
89
99
|
this.#observer = observerFn(this.config.name, logger, tracer);
|
package/src/client.js
CHANGED
|
@@ -19,15 +19,17 @@ export class Client extends Rpc {
|
|
|
19
19
|
/**
|
|
20
20
|
* Creates a new Client instance
|
|
21
21
|
* @param {object} config - Configuration object
|
|
22
|
+
* @param {import("@forwardimpact/libutil/runtime").Runtime} runtime - Injected runtime bag (required), threaded to `authFn`
|
|
22
23
|
* @param {object} [logger] - Optional logger instance
|
|
23
24
|
* @param {import("@forwardimpact/libtelemetry").Tracer} [tracer] - Optional tracer for distributed tracing
|
|
24
25
|
* @param {(serviceName: string, logger: object, tracer: object) => object} observerFn - Observer factory
|
|
25
26
|
* @param {() => {grpc: object}} grpcFn - gRPC factory
|
|
26
|
-
* @param {(serviceName: string) => object} authFn - Auth factory
|
|
27
|
+
* @param {(serviceName: string, runtime: object) => object} authFn - Auth factory
|
|
27
28
|
* @param {import("@forwardimpact/libutil").Retry} [retry] - Optional retry instance for handling transient errors
|
|
28
29
|
*/
|
|
29
30
|
constructor(
|
|
30
31
|
config,
|
|
32
|
+
runtime,
|
|
31
33
|
logger = null,
|
|
32
34
|
tracer = null,
|
|
33
35
|
observerFn = createObserver,
|
|
@@ -35,7 +37,7 @@ export class Client extends Rpc {
|
|
|
35
37
|
authFn = createAuth,
|
|
36
38
|
retry = null,
|
|
37
39
|
) {
|
|
38
|
-
super(config, logger, tracer, observerFn, grpcFn, authFn);
|
|
40
|
+
super(config, runtime, logger, tracer, observerFn, grpcFn, authFn);
|
|
39
41
|
this.#retry = retry || createRetry({ retries: 10, delay: 1000 });
|
|
40
42
|
this.#setupClient();
|
|
41
43
|
}
|
package/src/index.js
CHANGED
|
@@ -2,6 +2,7 @@ import grpc from "@grpc/grpc-js";
|
|
|
2
2
|
|
|
3
3
|
import { createServiceConfig } from "@forwardimpact/libconfig";
|
|
4
4
|
import { Tracer } from "@forwardimpact/libtelemetry/tracer.js";
|
|
5
|
+
import { createDefaultRuntime } from "@forwardimpact/libutil/runtime";
|
|
5
6
|
|
|
6
7
|
import { capitalizeFirstLetter } from "./base.js";
|
|
7
8
|
import * as exports from "./generated/services/exports.js";
|
|
@@ -30,11 +31,17 @@ export const clients = exports.clients || {};
|
|
|
30
31
|
export async function createTracer(serviceName) {
|
|
31
32
|
const traceConfig = await createServiceConfig("trace");
|
|
32
33
|
const { TraceClient } = clients;
|
|
33
|
-
|
|
34
|
+
// createTracer is a composition-root factory; it builds the production
|
|
35
|
+
// runtime as its DI root, threads it into the TraceClient (so the client's
|
|
36
|
+
// auth reads SERVICE_SECRET off the bag) and into the Tracer (and thus every
|
|
37
|
+
// Span) via its clock.
|
|
38
|
+
const runtime = createDefaultRuntime();
|
|
39
|
+
const traceClient = new TraceClient(traceConfig, runtime);
|
|
34
40
|
return new Tracer({
|
|
35
41
|
serviceName,
|
|
36
42
|
traceClient,
|
|
37
43
|
grpcMetadata: grpc.Metadata,
|
|
44
|
+
clock: runtime.clock,
|
|
38
45
|
});
|
|
39
46
|
}
|
|
40
47
|
|
|
@@ -60,6 +67,10 @@ export async function createClient(name, logger = null, tracer = null) {
|
|
|
60
67
|
// Create config for the service
|
|
61
68
|
const config = await createServiceConfig(name);
|
|
62
69
|
|
|
63
|
-
//
|
|
64
|
-
|
|
70
|
+
// createClient is a composition-root factory; build the production runtime
|
|
71
|
+
// here and thread it so the client's auth reads SERVICE_SECRET off the bag.
|
|
72
|
+
const runtime = createDefaultRuntime();
|
|
73
|
+
|
|
74
|
+
// Create and return the client instance (runtime is the required 2nd arg)
|
|
75
|
+
return new ClientClass(config, runtime, logger, tracer);
|
|
65
76
|
}
|
package/src/server.js
CHANGED
|
@@ -6,7 +6,6 @@ import {
|
|
|
6
6
|
capitalizeFirstLetter,
|
|
7
7
|
} from "./base.js";
|
|
8
8
|
import { healthDefinition, createHealthHandlers } from "./health.js";
|
|
9
|
-
import { createDefaultRuntime } from "@forwardimpact/libutil/runtime";
|
|
10
9
|
|
|
11
10
|
/**
|
|
12
11
|
* gRPC Server class using pre-compiled service definitions
|
|
@@ -21,29 +20,29 @@ export class Server extends Rpc {
|
|
|
21
20
|
* Creates a gRPC server for a service
|
|
22
21
|
* @param {object} service - Service instance with business logic
|
|
23
22
|
* @param {object} config - Server configuration
|
|
24
|
-
* @param {object}
|
|
25
|
-
* @param {
|
|
26
|
-
* @param {(
|
|
27
|
-
* @param {()
|
|
28
|
-
* @param {(serviceName: string) => object}
|
|
29
|
-
* @param {
|
|
30
|
-
*
|
|
23
|
+
* @param {object} options - Injected collaborators and optional factory overrides
|
|
24
|
+
* @param {object} [options.logger] - Optional logger instance
|
|
25
|
+
* @param {import("@forwardimpact/libtelemetry").Tracer} [options.tracer] - Optional tracer for distributed tracing
|
|
26
|
+
* @param {import("@forwardimpact/libutil/runtime").Runtime} options.runtime - Injected runtime bag
|
|
27
|
+
* @param {(serviceName: string, logger: object, tracer: object) => object} [options.observerFn] - Observer factory
|
|
28
|
+
* @param {() => {grpc: object}} [options.grpcFn] - gRPC factory
|
|
29
|
+
* @param {(serviceName: string) => object} [options.authFn] - Auth factory
|
|
31
30
|
*/
|
|
32
|
-
constructor(
|
|
33
|
-
service,
|
|
34
|
-
config,
|
|
35
|
-
logger = null,
|
|
36
|
-
tracer = null,
|
|
37
|
-
observerFn = createObserver,
|
|
38
|
-
grpcFn = createGrpc,
|
|
39
|
-
authFn = createAuth,
|
|
40
|
-
runtime = null,
|
|
41
|
-
) {
|
|
31
|
+
constructor(service, config, options = {}) {
|
|
42
32
|
if (!service) throw new Error("service is required");
|
|
43
|
-
|
|
44
|
-
|
|
33
|
+
const {
|
|
34
|
+
logger = null,
|
|
35
|
+
tracer = null,
|
|
36
|
+
runtime,
|
|
37
|
+
observerFn = createObserver,
|
|
38
|
+
grpcFn = createGrpc,
|
|
39
|
+
authFn = createAuth,
|
|
40
|
+
} = options;
|
|
41
|
+
if (!runtime) throw new Error("runtime is required");
|
|
42
|
+
|
|
43
|
+
super(config, runtime, logger, tracer, observerFn, grpcFn, authFn);
|
|
45
44
|
this.#service = service;
|
|
46
|
-
this.#runtime = runtime
|
|
45
|
+
this.#runtime = runtime;
|
|
47
46
|
}
|
|
48
47
|
|
|
49
48
|
/** Starts the gRPC server */
|