@forwardimpact/librpc 0.1.103 → 0.1.105

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 CHANGED
@@ -1,18 +1,24 @@
1
1
  #!/usr/bin/env node
2
2
  import "@forwardimpact/libpreflight/node22";
3
3
 
4
- import { readFileSync } from "node:fs";
5
4
  import { createCli } from "@forwardimpact/libcli";
6
5
  import { createClient, createTracer } from "@forwardimpact/librpc";
7
6
  import { createLogger } from "@forwardimpact/libtelemetry";
7
+ import { createDefaultRuntime } from "@forwardimpact/libutil/runtime";
8
+
9
+ const runtime = createDefaultRuntime();
8
10
 
9
11
  // `bun build --compile` injects FIT_UNARY_VERSION via --define, eliminating
10
12
  // the readFileSync branch in the compiled binary (which would ENOENT against
11
13
  // the bunfs virtual mount). Source execution falls through to package.json.
12
14
  const VERSION =
13
- process.env.FIT_UNARY_VERSION ||
14
- JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8"))
15
- .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;
16
22
 
17
23
  const definition = {
18
24
  name: "fit-unary",
@@ -27,7 +33,7 @@ const definition = {
27
33
  examples: ['fit-unary memory GetWindow \'{"resource_id":"..."}\''],
28
34
  };
29
35
 
30
- const cli = createCli(definition);
36
+ const cli = createCli(definition, { runtime });
31
37
  const logger = createLogger("cli");
32
38
 
33
39
  /**
@@ -35,13 +41,13 @@ const logger = createLogger("cli");
35
41
  * @returns {Promise<void>}
36
42
  */
37
43
  async function main() {
38
- const parsed = cli.parse(process.argv.slice(2));
39
- if (!parsed) process.exit(0);
44
+ const parsed = cli.parse(runtime.proc.argv.slice(2));
45
+ if (!parsed) return runtime.proc.exit(0);
40
46
 
41
47
  const [service, method, requestJson] = parsed.positionals;
42
48
  if (!service || !method) {
43
49
  cli.usageError("expected arguments: <service> <method> [json-request]");
44
- process.exit(2);
50
+ return runtime.proc.exit(2);
45
51
  }
46
52
 
47
53
  const request = requestJson ? JSON.parse(requestJson) : {};
@@ -49,11 +55,11 @@ async function main() {
49
55
  const client = await createClient(service, logger, tracer);
50
56
 
51
57
  const response = await client.callUnary(method, request);
52
- console.log(JSON.stringify(response, null, 2));
58
+ runtime.proc.stdout.write(JSON.stringify(response, null, 2) + "\n");
53
59
  }
54
60
 
55
61
  main().catch((error) => {
56
62
  logger.exception("main", error);
57
63
  cli.error(error.message);
58
- process.exit(1);
64
+ runtime.proc.exit(1);
59
65
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@forwardimpact/librpc",
3
- "version": "0.1.103",
3
+ "version": "0.1.105",
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": "Keep Service Contracts Typed",
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,6 +1,7 @@
1
1
  import grpc from "@grpc/grpc-js";
2
2
 
3
3
  import { createObserver } from "@forwardimpact/libtelemetry";
4
+ import { createDefaultProc } from "@forwardimpact/libutil/runtime";
4
5
 
5
6
  import { Interceptor, HmacAuth } from "./auth.js";
6
7
  import { definitions } from "./generated/definitions/exports.js";
@@ -23,12 +24,15 @@ export function createGrpc() {
23
24
  }
24
25
 
25
26
  /**
26
- * Default auth factory that creates authentication interceptor
27
+ * Default auth factory that creates authentication interceptor.
28
+ * Reads `SERVICE_SECRET` through `createDefaultProc().env` (a late-binding
29
+ * Proxy over `process.env`) rather than touching the `process` global
30
+ * directly — same read semantics as before, no ambient-dep smell.
27
31
  * @param {string} serviceName - Name of the service for the interceptor
28
32
  * @returns {Interceptor} Configured interceptor instance
29
33
  */
30
34
  export function createAuth(serviceName) {
31
- const secret = process.env.SERVICE_SECRET;
35
+ const secret = createDefaultProc().env.SERVICE_SECRET;
32
36
  if (!secret) {
33
37
  throw new Error(
34
38
  `SERVICE_SECRET environment variable is required for service ${serviceName}`,
package/src/server.js CHANGED
@@ -6,6 +6,7 @@ import {
6
6
  capitalizeFirstLetter,
7
7
  } from "./base.js";
8
8
  import { healthDefinition, createHealthHandlers } from "./health.js";
9
+ import { createDefaultRuntime } from "@forwardimpact/libutil/runtime";
9
10
 
10
11
  /**
11
12
  * gRPC Server class using pre-compiled service definitions
@@ -14,6 +15,7 @@ import { healthDefinition, createHealthHandlers } from "./health.js";
14
15
  export class Server extends Rpc {
15
16
  #server;
16
17
  #service;
18
+ #runtime;
17
19
 
18
20
  /**
19
21
  * Creates a gRPC server for a service
@@ -24,6 +26,8 @@ export class Server extends Rpc {
24
26
  * @param {(serviceName: string, logger: object, tracer: object) => object} observerFn - Observer factory
25
27
  * @param {() => {grpc: object}} grpcFn - gRPC factory
26
28
  * @param {(serviceName: string) => object} authFn - Auth factory
29
+ * @param {import("@forwardimpact/libutil/runtime").Runtime} [runtime] - Optional runtime bag;
30
+ * falls back to `createDefaultRuntime()` so existing service callers keep working.
27
31
  */
28
32
  constructor(
29
33
  service,
@@ -33,11 +37,13 @@ export class Server extends Rpc {
33
37
  observerFn = createObserver,
34
38
  grpcFn = createGrpc,
35
39
  authFn = createAuth,
40
+ runtime = null,
36
41
  ) {
37
42
  if (!service) throw new Error("service is required");
38
43
 
39
44
  super(config, logger, tracer, observerFn, grpcFn, authFn);
40
45
  this.#service = service;
46
+ this.#runtime = runtime ?? createDefaultRuntime();
41
47
  }
42
48
 
43
49
  /** Starts the gRPC server */
@@ -204,6 +210,7 @@ export class Server extends Rpc {
204
210
 
205
211
  /** Sets up graceful shutdown handlers */
206
212
  #setupShutdown() {
213
+ const runtime = this.#runtime;
207
214
  const shutdown = async () => {
208
215
  this.observer().logger()?.info("Server", "Shutting down...");
209
216
 
@@ -212,7 +219,7 @@ export class Server extends Rpc {
212
219
  await this.#service.shutdown();
213
220
  }
214
221
 
215
- this.#server.tryShutdown(() => process.exit(0));
222
+ this.#server.tryShutdown(() => runtime.proc.exit(0));
216
223
  };
217
224
  process.on("SIGINT", shutdown);
218
225
  process.on("SIGTERM", shutdown);