@aspan-corporation/ac-shared 1.2.35 → 1.2.36

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.
@@ -14,7 +14,11 @@ export { MetricUnit } from "@aws-lambda-powertools/metrics";
14
14
  export const tracer = new Tracer();
15
15
  export const metrics = new Metrics({ namespace: "aspan-corporation" });
16
16
  export const withMiddlewares = (handler) => middy(handler)
17
- .use(injectLambdaContext(logger, { logEvent: true }))
17
+ // logEvent is deliberately false: several handlers are fronted by API
18
+ // Gateway, whose proxy event carries the caller's `Authorization` header
19
+ // (a live Cognito token). Logging the full event would persist those
20
+ // tokens to CloudWatch. Handlers log the specific fields they need.
21
+ .use(injectLambdaContext(logger, { logEvent: false }))
18
22
  .use(captureLambdaHandler(tracer))
19
23
  .use(logMetrics(metrics, { captureColdStartMetric: true }))
20
24
  .use({
@@ -1,5 +1,10 @@
1
1
  import { Logger } from "@aws-lambda-powertools/logger";
2
- import { AssumeRoleCommandInput, STSClient } from "@aws-sdk/client-sts";
2
+ import { AssumeRoleCommandInput, AssumeRoleCommandOutput, STSClient } from "@aws-sdk/client-sts";
3
+ /**
4
+ * Clear the module-scoped assume-role credential cache. Intended for tests
5
+ * only, so each case starts from a known state.
6
+ */
7
+ export declare const clearAssumeRoleCache: () => void;
3
8
  export declare class STSService {
4
9
  logger: Logger;
5
10
  client: STSClient;
@@ -9,6 +14,6 @@ export declare class STSService {
9
14
  client?: STSClient;
10
15
  });
11
16
  getCallerIdentity(): Promise<import("@aws-sdk/client-sts").GetCallerIdentityCommandOutput>;
12
- assumeRole(assumeRoleCommandInput: AssumeRoleCommandInput): Promise<import("@aws-sdk/client-sts").AssumeRoleCommandOutput>;
17
+ assumeRole(assumeRoleCommandInput: AssumeRoleCommandInput): Promise<AssumeRoleCommandOutput>;
13
18
  }
14
19
  export type { AssumeRoleCommandInput, AssumeRoleCommandOutput } from "@aws-sdk/client-sts";
@@ -1,6 +1,34 @@
1
1
  import { Logger } from "@aws-lambda-powertools/logger";
2
2
  import { AssumeRoleCommand, GetCallerIdentityCommand, STSClient } from "@aws-sdk/client-sts";
3
3
  import assert from "node:assert/strict";
4
+ // Module-scoped cache of assumed-role credentials, shared across every
5
+ // STSService instance in the same execution environment. Lambda handlers
6
+ // construct a fresh STSService per invocation (usually in a middy `before`
7
+ // hook), so an instance-level cache would never hit. Keying at module scope
8
+ // means a warm container assumes the role once and reuses the credentials
9
+ // until they are close to expiry, removing an STS round-trip (~50-150ms) from
10
+ // every invocation.
11
+ const REFRESH_BUFFER_MS = 5 * 60 * 1000;
12
+ const credentialCache = new Map();
13
+ const cacheKey = (input) => [
14
+ input.RoleArn,
15
+ input.RoleSessionName,
16
+ input.ExternalId ?? "",
17
+ input.DurationSeconds ?? "",
18
+ input.SerialNumber ?? ""
19
+ ].join("|");
20
+ const isFresh = (output) => {
21
+ const expiration = output.Credentials?.Expiration;
22
+ return (expiration instanceof Date &&
23
+ expiration.getTime() - REFRESH_BUFFER_MS > Date.now());
24
+ };
25
+ /**
26
+ * Clear the module-scoped assume-role credential cache. Intended for tests
27
+ * only, so each case starts from a known state.
28
+ */
29
+ export const clearAssumeRoleCache = () => {
30
+ credentialCache.clear();
31
+ };
4
32
  export class STSService {
5
33
  logger;
6
34
  client;
@@ -27,7 +55,20 @@ export class STSService {
27
55
  return await this.client.send(new GetCallerIdentityCommand({}));
28
56
  }
29
57
  async assumeRole(assumeRoleCommandInput) {
58
+ const key = cacheKey(assumeRoleCommandInput);
59
+ const cached = credentialCache.get(key);
60
+ if (cached && isFresh(cached)) {
61
+ this.logger.debug("assumeRole cache hit", {
62
+ roleArn: assumeRoleCommandInput.RoleArn
63
+ });
64
+ return cached;
65
+ }
30
66
  this.logger.debug("assumeRole", { assumeRoleCommandInput });
31
- return await this.client.send(new AssumeRoleCommand(assumeRoleCommandInput));
67
+ const output = await this.client.send(new AssumeRoleCommand(assumeRoleCommandInput));
68
+ // Only cache credentials that carry an expiry we can reason about.
69
+ if (output.Credentials?.Expiration) {
70
+ credentialCache.set(key, output);
71
+ }
72
+ return output;
32
73
  }
33
74
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aspan-corporation/ac-shared",
3
- "version": "1.2.35",
3
+ "version": "1.2.36",
4
4
  "description": "",
5
5
  "keywords": [],
6
6
  "exports": {
@@ -36,12 +36,15 @@
36
36
  "clean": "rm -rf lib",
37
37
  "build": "npm run clean && npm run build:code && npm run build:types",
38
38
  "build:code": "tsc --declaration false --emitDeclarationOnly false",
39
- "build:types": "tsc --emitDeclarationOnly --declaration"
39
+ "build:types": "tsc --emitDeclarationOnly --declaration",
40
+ "test": "vitest run",
41
+ "test:watch": "vitest"
40
42
  },
41
43
  "devDependencies": {
42
44
  "@types/aws-lambda": "^8.10.137",
43
45
  "@types/node": "^20.12.5",
44
- "typescript": "^5.4.4"
46
+ "typescript": "^5.4.4",
47
+ "vitest": "^4.1.9"
45
48
  },
46
49
  "dependencies": {
47
50
  "@aws-lambda-powertools/batch": "^2.30.2",