@forwardimpact/librpc 0.1.102 → 0.1.104

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.102",
3
+ "version": "0.1.104",
4
4
  "description": "gRPC server and client framework — ship service endpoints without reimplementing transport.",
5
5
  "keywords": [
6
6
  "grpc",
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 */
@@ -170,10 +176,11 @@ export class Server extends Rpc {
170
176
  );
171
177
  callback(null, response);
172
178
  } catch (error) {
173
- callback({
174
- code: this.grpc().status.INTERNAL,
175
- message: error?.message || String(error),
176
- });
179
+ const code =
180
+ typeof error?.code === "number"
181
+ ? error.code
182
+ : this.grpc().status.INTERNAL;
183
+ callback({ code, message: error?.message || String(error) });
177
184
  }
178
185
  };
179
186
  }
@@ -203,6 +210,7 @@ export class Server extends Rpc {
203
210
 
204
211
  /** Sets up graceful shutdown handlers */
205
212
  #setupShutdown() {
213
+ const runtime = this.#runtime;
206
214
  const shutdown = async () => {
207
215
  this.observer().logger()?.info("Server", "Shutting down...");
208
216
 
@@ -211,7 +219,7 @@ export class Server extends Rpc {
211
219
  await this.#service.shutdown();
212
220
  }
213
221
 
214
- this.#server.tryShutdown(() => process.exit(0));
222
+ this.#server.tryShutdown(() => runtime.proc.exit(0));
215
223
  };
216
224
  process.on("SIGINT", shutdown);
217
225
  process.on("SIGTERM", shutdown);