@forwardimpact/librpc 0.1.101 → 0.1.103

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@forwardimpact/librpc",
3
- "version": "0.1.101",
3
+ "version": "0.1.103",
4
4
  "description": "gRPC server and client framework — ship service endpoints without reimplementing transport.",
5
5
  "keywords": [
6
6
  "grpc",
@@ -55,7 +55,7 @@
55
55
  "@grpc/grpc-js": "^1.14.4"
56
56
  },
57
57
  "devDependencies": {
58
- "@forwardimpact/libharness": "^0.1.14"
58
+ "@forwardimpact/libmock": "^0.1.0"
59
59
  },
60
60
  "engines": {
61
61
  "bun": ">=1.2.0",
package/src/auth.js CHANGED
@@ -8,14 +8,17 @@ import grpc from "@grpc/grpc-js";
8
8
  export class HmacAuth {
9
9
  #secret;
10
10
  #tokenLifetimeMs;
11
+ #now;
11
12
 
12
13
  /**
13
14
  * Creates a new HMAC authenticator instance
14
15
  * @param {string} secret - Shared secret key for HMAC generation (minimum 32 characters)
15
16
  * @param {number} tokenLifetimeSeconds - Token lifetime in seconds (default: 60)
17
+ * @param {object} [options] - Optional collaborators
18
+ * @param {() => number} [options.now] - Injectable clock (default: Date.now)
16
19
  * @throws {Error} When secret is too short or invalid
17
20
  */
18
- constructor(secret, tokenLifetimeSeconds = 60) {
21
+ constructor(secret, tokenLifetimeSeconds = 60, { now = Date.now } = {}) {
19
22
  if (!secret || typeof secret !== "string") {
20
23
  throw new Error("Secret must be a non-empty string");
21
24
  }
@@ -28,6 +31,7 @@ export class HmacAuth {
28
31
 
29
32
  this.#secret = secret;
30
33
  this.#tokenLifetimeMs = tokenLifetimeSeconds * 1000;
34
+ this.#now = now;
31
35
  }
32
36
 
33
37
  /**
@@ -41,7 +45,7 @@ export class HmacAuth {
41
45
  throw new Error("Service ID must be a non-empty string");
42
46
  }
43
47
 
44
- const timestamp = Date.now();
48
+ const timestamp = this.#now();
45
49
  const payload = `${serviceId}:${timestamp}`;
46
50
  const signature = crypto
47
51
  .createHmac("sha256", this.#secret)
@@ -92,7 +96,7 @@ export class HmacAuth {
92
96
  }
93
97
 
94
98
  // Check token expiration
95
- const now = Date.now();
99
+ const now = this.#now();
96
100
  if (now - timestamp > this.#tokenLifetimeMs) {
97
101
  return {
98
102
  isValid: false,
package/src/server.js CHANGED
@@ -170,10 +170,11 @@ export class Server extends Rpc {
170
170
  );
171
171
  callback(null, response);
172
172
  } catch (error) {
173
- callback({
174
- code: this.grpc().status.INTERNAL,
175
- message: error?.message || String(error),
176
- });
173
+ const code =
174
+ typeof error?.code === "number"
175
+ ? error.code
176
+ : this.grpc().status.INTERNAL;
177
+ callback({ code, message: error?.message || String(error) });
177
178
  }
178
179
  };
179
180
  }