@digitraffic/common 2026.8.31-2 → 2026.9.4-1

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.
Files changed (30) hide show
  1. package/dist/__test__/imports.test.js +304 -82
  2. package/dist/__test__/infra/acl-builder.test.js +0 -18
  3. package/dist/__test__/stack/dt-function.test.js +121 -3
  4. package/dist/aws/infra/acl-builder.d.ts +2 -21
  5. package/dist/aws/infra/acl-builder.js +6 -54
  6. package/dist/aws/infra/canaries/database-checker.js +3 -2
  7. package/dist/aws/infra/stack/dt-function.d.ts +11 -13
  8. package/dist/aws/infra/stack/dt-function.js +49 -23
  9. package/dist/aws/infra/stack/stack-checking-aspect.js +2 -1
  10. package/dist/aws/infra/stack/stack.d.ts +0 -2
  11. package/dist/aws/infra/stack/stack.js +8 -9
  12. package/dist/aws/runtime/dt-logger.d.ts +0 -1
  13. package/dist/aws/runtime/dt-logger.js +0 -5
  14. package/dist/aws/runtime/environment.d.ts +0 -1
  15. package/dist/aws/runtime/environment.js +0 -1
  16. package/dist/aws/runtime/secrets/proxy-holder.d.ts +1 -6
  17. package/dist/aws/runtime/secrets/proxy-holder.js +4 -6
  18. package/dist/aws/runtime/secrets/rds-holder.d.ts +1 -4
  19. package/dist/aws/runtime/secrets/rds-holder.js +4 -6
  20. package/dist/database/database.d.ts +1 -1
  21. package/dist/database/database.js +1 -1
  22. package/package.json +1 -1
  23. package/dist/__test__/infra/stack/dt-function.test.d.ts +0 -1
  24. package/dist/__test__/infra/stack/dt-function.test.js +0 -70
  25. package/dist/__test__/infra/stack/stack.test.d.ts +0 -1
  26. package/dist/__test__/infra/stack/stack.test.js +0 -33
  27. package/dist/__test__/utils/lead-time-logging.test.d.ts +0 -1
  28. package/dist/__test__/utils/lead-time-logging.test.js +0 -153
  29. package/dist/utils/lead-time-logging.d.ts +0 -10
  30. package/dist/utils/lead-time-logging.js +0 -33
@@ -91,18 +91,7 @@ export class AclBuilder {
91
91
  });
92
92
  return this;
93
93
  }
94
- /**
95
- * Adds a rate-based WAF rule for either blocking or counting requests.
96
- *
97
- * @param name WAF rule name and CloudWatch metric name.
98
- * @param limit Maximum requests allowed in the WAF evaluation window.
99
- * @param isHeaderRequired Whether the rule applies when the digitraffic-user header is present.
100
- * @param isBasedOnIpAndUriPath Whether requests are aggregated by IP address and URI path.
101
- * @param customResponseBodyKey Custom response body key; when provided, matching requests are blocked instead of counted.
102
- * @param path Optional URI path pattern that limits which requests are included.
103
- * @param excludedPath Optional URI path pattern that excludes matching requests from the rule.
104
- */
105
- withThrottleRule(name, limit, isHeaderRequired, isBasedOnIpAndUriPath, customResponseBodyKey, path, excludedPath) {
94
+ withThrottleRule(name, limit, isHeaderRequired, isBasedOnIpAndUriPath, customResponseBodyKey, path) {
106
95
  const isBlockRule = !!customResponseBodyKey;
107
96
  const rules = isBlockRule ? this._blockRules : this._countRules;
108
97
  const action = isBlockRule
@@ -125,7 +114,7 @@ export class AclBuilder {
125
114
  metricName: name,
126
115
  },
127
116
  action,
128
- statement: createThrottleStatement(limit, isHeaderRequired, isBasedOnIpAndUriPath, path, excludedPath),
117
+ statement: createThrottleStatement(limit, isHeaderRequired, isBasedOnIpAndUriPath, path),
129
118
  });
130
119
  return this;
131
120
  }
@@ -171,21 +160,13 @@ export class AclBuilder {
171
160
  this._addThrottleResponseBody(customResponseBodyKey, limit);
172
161
  return this.withThrottleRule("ThrottleRuleWithAnonymousUserByPath", limit, false, false, customResponseBodyKey, path);
173
162
  }
174
- /**
175
- * Throttles anonymous requests by IP address and URI path.
176
- *
177
- * Existing callers that omit `excludedPath` retain the previous behavior.
178
- *
179
- * @param limit Maximum requests allowed for each IP and URI path combination.
180
- * @param excludedPath Optional URI path pattern excluded from this throttle rule.
181
- */
182
- withThrottleAnonymousUserIpAndUriPath(limit, excludedPath) {
163
+ withThrottleAnonymousUserIpAndUriPath(limit) {
183
164
  if (limit === undefined) {
184
165
  return this;
185
166
  }
186
167
  const customResponseBodyKey = `IP_PATH_THROTTLE_ANONYMOUS_USER_${limit}`;
187
168
  this._addThrottleResponseBody(customResponseBodyKey, limit);
188
- return this.withThrottleRule("ThrottleRuleIPQueryWithAnonymousUser", limit, false, true, customResponseBodyKey, undefined, excludedPath);
169
+ return this.withThrottleRule("ThrottleRuleIPQueryWithAnonymousUser", limit, false, true, customResponseBodyKey);
189
170
  }
190
171
  withCountDigitrafficUserIp(limit) {
191
172
  if (limit === undefined) {
@@ -308,18 +289,8 @@ function notStatement(statement) {
308
289
  },
309
290
  };
310
291
  }
311
- /**
312
- * Creates the scope-down and rate-based statement used by a throttle rule.
313
- *
314
- * @param limit Maximum requests allowed in the WAF evaluation window.
315
- * @param isHeaderRequired Whether the statement matches requests with the digitraffic-user header.
316
- * @param isBasedOnIpAndUriPath Whether requests are aggregated by IP address and URI path.
317
- * @param path Optional URI path pattern that limits which requests are included.
318
- * @param excludedPath Optional URI path pattern that is explicitly excluded from the statement.
319
- */
320
- function createThrottleStatement(limit, isHeaderRequired, isBasedOnIpAndUriPath, path, excludedPath) {
321
- // Matches requests with a non-empty digitraffic-user header. For anonymous
322
- // requests, the inverted statement matches requests without the header.
292
+ function createThrottleStatement(limit, isHeaderRequired, isBasedOnIpAndUriPath, path) {
293
+ // this statement matches empty digitraffic-user -header
323
294
  const matchStatement = {
324
295
  sizeConstraintStatement: {
325
296
  comparisonOperator: isHeaderRequired ? "GT" : "GE",
@@ -353,25 +324,6 @@ function createThrottleStatement(limit, isHeaderRequired, isBasedOnIpAndUriPath,
353
324
  },
354
325
  };
355
326
  }
356
- if (excludedPath) {
357
- const excludedPathMatchStatement = {
358
- regexMatchStatement: {
359
- fieldToMatch: {
360
- uriPath: {},
361
- },
362
- regexString: excludedPath.source,
363
- textTransformations: [{ priority: 0, type: "NONE" }],
364
- },
365
- };
366
- scopeDownStatement = {
367
- andStatement: {
368
- statements: [
369
- scopeDownStatement,
370
- notStatement(excludedPathMatchStatement),
371
- ],
372
- },
373
- };
374
- }
375
327
  if (isBasedOnIpAndUriPath) {
376
328
  return {
377
329
  rateBasedStatement: {
@@ -1,5 +1,6 @@
1
1
  import synthetics from "Synthetics";
2
2
  import { inDatabaseReadonly } from "../../../database/database.js";
3
+ import { getEnvVariable } from "../../../utils/utils.js";
3
4
  import { logger } from "../../runtime/dt-logger-default.js";
4
5
  import { ProxyHolder } from "../../runtime/secrets/proxy-holder.js";
5
6
  import { RdsHolder } from "../../runtime/secrets/rds-holder.js";
@@ -65,10 +66,10 @@ export class DatabaseCountChecker {
65
66
  synthetics.getConfiguration().withFailedCanaryMetric(true);
66
67
  }
67
68
  static createForProxy() {
68
- return new DatabaseCountChecker(() => ProxyHolder.create().setCredentials());
69
+ return new DatabaseCountChecker(() => new ProxyHolder(getEnvVariable("SECRET_ID")).setCredentials());
69
70
  }
70
71
  static createForRds() {
71
- return new DatabaseCountChecker(() => RdsHolder.create().setCredentials());
72
+ return new DatabaseCountChecker(() => new RdsHolder(getEnvVariable("SECRET_ID")).setCredentials());
72
73
  }
73
74
  /**
74
75
  * Expect that the count is 1
@@ -28,6 +28,7 @@ export declare class FunctionBuilder {
28
28
  private readonly securityGroups;
29
29
  private readonly policyStatements;
30
30
  private readonly allowedActions;
31
+ private _adotTracing;
31
32
  private readonly _features;
32
33
  constructor(stack: Stack & DigitrafficStackInterface, lambdaName: string);
33
34
  /**
@@ -40,19 +41,9 @@ export declare class FunctionBuilder {
40
41
  */
41
42
  static plain(stack: Stack & DigitrafficStackInterface, lambdaName: string): FunctionBuilder;
42
43
  /**
43
- * Sets the Lambda deployment package to the directory `dist/lambda/${path}` and configures
44
- * the handler to `basename(path).handler`.
45
- * @param path - subdirectory under `dist/lambda/` that contains the compiled Lambda code.
46
- * The handler module name is derived from `basename(path)` (e.g. `"locations.handler"`).
47
- * Defaults to `lambdaName`.
48
- * Example: `"dump/locations"` → asset dir `dist/lambda/dump/locations/`, handler `locations.handler`.
49
- * @param exclude - optional glob patterns to exclude from the asset bundle.
50
- * Patterns are relative to the asset root (`dist/lambda/${path}/`) — do NOT use a leading slash.
51
- * Use `**` to match files recursively.
52
- * Example: `["datex2-36", "datex2-37", "statuses"]` excludes sibling lambda subdirectories
53
- * that would otherwise be bundled into this lambda's deployment package.
54
- */
55
- withAssetCode(path?: string, exclude?: string[]): this;
44
+ * Use AssetCode from given path(dist/lambda/${path}). Default path is lambdaName. Also calls withHandler with the same value.
45
+ */
46
+ withAssetCode(path?: string): this;
56
47
  withCode(code: Code): this;
57
48
  /**
58
49
  * Use given handler(${name}.handler) to run the lambda. Default value is lambdaName.
@@ -86,6 +77,12 @@ export declare class FunctionBuilder {
86
77
  * Add Lambda layers.
87
78
  */
88
79
  withLayers(...layers: ILayerVersion[]): this;
80
+ /**
81
+ * Enable ADOT (AWS Distro for OpenTelemetry) tracing for this Lambda.
82
+ * Adds the ADOT Lambda layer, enables X-Ray active tracing,
83
+ * sets required environment variables, and attaches IAM permissions.
84
+ */
85
+ withAdotTracing(): this;
89
86
  /**
90
87
  * Set architecture for the lambda. Default is Architecture.ARM_64.
91
88
  */
@@ -124,6 +121,7 @@ export declare class FunctionBuilder {
124
121
  build(): AwsFunction;
125
122
  private attachPolicies;
126
123
  private validateNoWildcardActions;
124
+ private getLayers;
127
125
  private getEnvironment;
128
126
  private createAlarms;
129
127
  private createAlarm;
@@ -2,8 +2,9 @@ import { basename } from "node:path";
2
2
  import { Duration } from "aws-cdk-lib";
3
3
  import { SnsAction } from "aws-cdk-lib/aws-cloudwatch-actions";
4
4
  import { Effect, PolicyStatement } from "aws-cdk-lib/aws-iam";
5
- import { ApplicationLogLevel, Architecture, AssetCode, Function as AwsFunction, Code, LoggingFormat, Runtime, SystemLogLevel, } from "aws-cdk-lib/aws-lambda";
6
- import { camelCase, kebabCase, startCase } from "es-toolkit";
5
+ import { ApplicationLogLevel, Architecture, AssetCode, Function as AwsFunction, Code, LayerVersion, LoggingFormat, Runtime, SystemLogLevel, Tracing, } from "aws-cdk-lib/aws-lambda";
6
+ import { ManagedPolicy } from "aws-cdk-lib/aws-iam";
7
+ import { camelCase, startCase } from "es-toolkit";
7
8
  import { DatabaseEnvironmentKeys } from "../../../database/database.js";
8
9
  import { EnvKeys } from "../../runtime/environment.js";
9
10
  import { DtFunctionAlarms } from "./dt-function-alarms.js";
@@ -29,6 +30,7 @@ export class FunctionBuilder {
29
30
  securityGroups = [];
30
31
  policyStatements = [];
31
32
  allowedActions = [];
33
+ _adotTracing = false;
32
34
  _features = {
33
35
  databaseAccess: true,
34
36
  secretAccess: true,
@@ -39,7 +41,13 @@ export class FunctionBuilder {
39
41
  this.functionName = `${stack.configuration.shortName}-${startCase(camelCase(lambdaName)).replace(/\s/g, "")}`;
40
42
  this.environment = {};
41
43
  this.vpc = stack.vpc;
44
+ // this calls withHandler as well but with full path
42
45
  this.withAssetCode(lambdaName);
46
+ // Remove path from lambda to get module name.
47
+ // e.g. for lambdaName "api/charging-network/v1/operators",
48
+ // moduleName becomes "operators" and handler becomes "operators.handler"
49
+ const moduleName = basename(lambdaName);
50
+ this.withHandler(moduleName);
43
51
  }
44
52
  /**
45
53
  * Creates a new builder with defaults, using the lambdaName as a source for the lambda implementation (dist/lambdaName/lambdaName.js).
@@ -57,22 +65,11 @@ export class FunctionBuilder {
57
65
  .withoutSecretAccess();
58
66
  }
59
67
  /**
60
- * Sets the Lambda deployment package to the directory `dist/lambda/${path}` and configures
61
- * the handler to `basename(path).handler`.
62
- * @param path - subdirectory under `dist/lambda/` that contains the compiled Lambda code.
63
- * The handler module name is derived from `basename(path)` (e.g. `"locations.handler"`).
64
- * Defaults to `lambdaName`.
65
- * Example: `"dump/locations"` → asset dir `dist/lambda/dump/locations/`, handler `locations.handler`.
66
- * @param exclude - optional glob patterns to exclude from the asset bundle.
67
- * Patterns are relative to the asset root (`dist/lambda/${path}/`) — do NOT use a leading slash.
68
- * Use `**` to match files recursively.
69
- * Example: `["datex2-36", "datex2-37", "statuses"]` excludes sibling lambda subdirectories
70
- * that would otherwise be bundled into this lambda's deployment package.
68
+ * Use AssetCode from given path(dist/lambda/${path}). Default path is lambdaName. Also calls withHandler with the same value.
71
69
  */
72
- withAssetCode(path = this._name, exclude) {
73
- this.code = new AssetCode(`dist/lambda/${path}`, exclude ? { exclude } : undefined);
74
- const moduleName = basename(path);
75
- this.withHandler(moduleName);
70
+ withAssetCode(path = this._name) {
71
+ this.code = new AssetCode(`dist/lambda/${path}`);
72
+ this.withHandler(path);
76
73
  return this;
77
74
  }
78
75
  withCode(code) {
@@ -135,6 +132,15 @@ export class FunctionBuilder {
135
132
  this.layers.push(...layers);
136
133
  return this;
137
134
  }
135
+ /**
136
+ * Enable ADOT (AWS Distro for OpenTelemetry) tracing for this Lambda.
137
+ * Adds the ADOT Lambda layer, enables X-Ray active tracing,
138
+ * sets required environment variables, and attaches IAM permissions.
139
+ */
140
+ withAdotTracing() {
141
+ this._adotTracing = true;
142
+ return this;
143
+ }
138
144
  /**
139
145
  * Set architecture for the lambda. Default is Architecture.ARM_64.
140
146
  */
@@ -213,6 +219,7 @@ export class FunctionBuilder {
213
219
  }
214
220
  : undefined;
215
221
  const securityGroups = this.getSecurityGroups();
222
+ const layers = this.getLayers();
216
223
  const createdFunction = new AwsFunction(this._stack, this.functionName, {
217
224
  vpc: this.vpc,
218
225
  vpcSubnets,
@@ -232,11 +239,15 @@ export class FunctionBuilder {
232
239
  securityGroups,
233
240
  environment: this.getEnvironment(),
234
241
  description: this.description,
235
- layers: this.layers.length > 0 ? this.layers : undefined,
242
+ layers: layers.length > 0 ? layers : undefined,
243
+ tracing: this._adotTracing ? Tracing.ACTIVE : undefined,
236
244
  });
237
245
  if (this._features.secretAccess) {
238
246
  this._stack.grantSecret(createdFunction);
239
247
  }
248
+ if (this._adotTracing) {
249
+ createdFunction.role?.addManagedPolicy(ManagedPolicy.fromAwsManagedPolicyName("CloudWatchLambdaApplicationSignalsExecutionRolePolicy"));
250
+ }
240
251
  this.attachPolicies(createdFunction);
241
252
  this.createAlarms(createdFunction);
242
253
  return createdFunction;
@@ -259,22 +270,37 @@ export class FunctionBuilder {
259
270
  throw new Error(`Lambda ${this.functionName} cannot use wildcard action "*" in withAllowedActions. Please specify explicit actions.`);
260
271
  }
261
272
  }
273
+ getLayers() {
274
+ const layers = [...this.layers];
275
+ if (this._adotTracing) {
276
+ const layerName = this.runtime.name.startsWith("python")
277
+ ? "AWSOpenTelemetryDistroPython"
278
+ : "AWSOpenTelemetryDistroJs";
279
+ layers.push(LayerVersion.fromLayerVersionArn(this._stack, `${this.functionName}-AdotLayer`, `arn:aws:lambda:eu-west-1:615299751070:layer:${layerName}:1`));
280
+ }
281
+ return layers;
282
+ }
262
283
  getEnvironment() {
263
- let environment = {
264
- [EnvKeys.APP_NAME]: `${this._stack.configuration.trafficType.toLowerCase()}-${kebabCase(this._stack.configuration.shortName)}`,
265
- };
266
- if (this._features.secretAccess && this._stack.configuration.secretId) {
284
+ let environment = {};
285
+ if (this._features.secretAccess) {
267
286
  environment = {
268
287
  ...environment,
269
288
  [EnvKeys.SECRET_ID]: this._stack.configuration.secretId,
270
289
  };
271
290
  }
272
- if (this._features.databaseAccess && this._stack.configuration.shortName) {
291
+ if (this._features.databaseAccess) {
273
292
  environment = {
274
293
  ...environment,
275
294
  [DatabaseEnvironmentKeys.DB_APPLICATION]: this._stack.configuration.shortName,
276
295
  };
277
296
  }
297
+ if (this._adotTracing) {
298
+ environment = {
299
+ ...environment,
300
+ AWS_LAMBDA_EXEC_WRAPPER: "/opt/otel-instrument",
301
+ OTEL_AWS_APPLICATION_SIGNALS_ENABLED: "false",
302
+ };
303
+ }
278
304
  return { ...environment, ...this.environment };
279
305
  }
280
306
  createAlarms(lambda) {
@@ -4,7 +4,8 @@ import { CfnFunction, Runtime } from "aws-cdk-lib/aws-lambda";
4
4
  import { LogRetention } from "aws-cdk-lib/aws-logs";
5
5
  import { CfnBucket } from "aws-cdk-lib/aws-s3";
6
6
  import { CfnQueue } from "aws-cdk-lib/aws-sqs";
7
- import { kebabCase, snakeCase } from "es-toolkit";
7
+ import { kebabCase } from "change-case";
8
+ import { snakeCase } from "es-toolkit";
8
9
  import { DigitrafficStack, SOLUTION_KEY } from "./stack.js";
9
10
  const MAX_CONCURRENCY_LIMIT = 100;
10
11
  const ALLOWED_RUNTIMES = [
@@ -20,8 +20,6 @@ export interface StackConfiguration {
20
20
  readonly lambdaDbSgId?: string;
21
21
  readonly privateSubnetIds?: string[];
22
22
  readonly availabilityZones?: string[];
23
- readonly dbWriterHost?: string;
24
- readonly dbReaderHost?: string;
25
23
  readonly trafficType: TrafficType;
26
24
  readonly production: boolean;
27
25
  readonly stackProps: StackProps;
@@ -3,8 +3,6 @@ import { SecurityGroup, Vpc } from "aws-cdk-lib/aws-ec2";
3
3
  import { Secret } from "aws-cdk-lib/aws-secretsmanager";
4
4
  import { Topic } from "aws-cdk-lib/aws-sns";
5
5
  import { StringParameter } from "aws-cdk-lib/aws-ssm";
6
- import { kebabCase } from "es-toolkit";
7
- import { EnvKeys } from "../../runtime/environment.js";
8
6
  import { StackCheckingAspect } from "./stack-checking-aspect.js";
9
7
  const SSM_ROOT = "/digitraffic";
10
8
  export const SOLUTION_KEY = "Solution";
@@ -48,13 +46,14 @@ export class DigitrafficStack extends Stack {
48
46
  return this.createDefaultLambdaEnvironment(this.configuration.shortName);
49
47
  }
50
48
  createDefaultLambdaEnvironment(dbApplication) {
51
- return {
52
- [EnvKeys.APP_NAME]: `${this.configuration.trafficType.toLowerCase()}-${kebabCase(this.configuration.shortName)}`,
53
- DB_APPLICATION: dbApplication,
54
- ...(this.configuration.secretId
55
- ? { SECRET_ID: this.configuration.secretId }
56
- : {}),
57
- };
49
+ return this.configuration.secretId
50
+ ? {
51
+ SECRET_ID: this.configuration.secretId,
52
+ DB_APPLICATION: dbApplication,
53
+ }
54
+ : {
55
+ DB_APPLICATION: dbApplication,
56
+ };
58
57
  }
59
58
  getSecret() {
60
59
  if (this.secret === undefined) {
@@ -61,7 +61,6 @@ export interface LoggableType extends CustomParams {
61
61
  * * the actual message (as json or as string)
62
62
  */
63
63
  export declare class DtLogger {
64
- readonly appName?: string;
65
64
  readonly lambdaName?: string;
66
65
  readonly runtime?: string;
67
66
  readonly writeStream: Writable;
@@ -1,6 +1,5 @@
1
1
  import { lowerFirst, mapKeys } from "es-toolkit";
2
2
  import { getEnvVariableOrElse } from "../../utils/utils.js";
3
- import { EnvKeys } from "./environment.js";
4
3
  /**
5
4
  * Helper class for json-logging.
6
5
  *
@@ -11,7 +10,6 @@ import { EnvKeys } from "./environment.js";
11
10
  * * the actual message (as json or as string)
12
11
  */
13
12
  export class DtLogger {
14
- appName;
15
13
  lambdaName;
16
14
  runtime;
17
15
  writeStream;
@@ -21,7 +19,6 @@ export class DtLogger {
21
19
  * @param {LoggerConfiguration?} [config] - Accepts configuration options @see {@link LoggerConfiguration}
22
20
  */
23
21
  constructor(config) {
24
- this.appName = getEnvVariableOrElse(EnvKeys.APP_NAME, undefined);
25
22
  this.lambdaName =
26
23
  config?.lambdaName ??
27
24
  getEnvVariableOrElse("AWS_LAMBDA_FUNCTION_NAME", "unknown lambda name");
@@ -41,7 +38,6 @@ export class DtLogger {
41
38
  const logMessage = {
42
39
  message,
43
40
  level: "DEBUG",
44
- app: this.appName,
45
41
  lambdaName: this.lambdaName,
46
42
  runtime: this.runtime,
47
43
  };
@@ -116,7 +112,6 @@ export class DtLogger {
116
112
  ...messageFields,
117
113
  error,
118
114
  stack,
119
- app: this.appName,
120
115
  lambdaName: this.lambdaName,
121
116
  runtime: this.runtime,
122
117
  };
@@ -1,5 +1,4 @@
1
1
  export declare enum EnvKeys {
2
- APP_NAME = "APP_NAME",
3
2
  AWS_REGION = "AWS_REGION",
4
3
  SECRET_ID = "SECRET_ID",
5
4
  SECRET_OVERRIDE_AWS_REGION = "SECRET_OVERRIDE_AWS_REGION"
@@ -1,6 +1,5 @@
1
1
  export var EnvKeys;
2
2
  (function (EnvKeys) {
3
- EnvKeys["APP_NAME"] = "APP_NAME";
4
3
  EnvKeys["AWS_REGION"] = "AWS_REGION";
5
4
  EnvKeys["SECRET_ID"] = "SECRET_ID";
6
5
  EnvKeys["SECRET_OVERRIDE_AWS_REGION"] = "SECRET_OVERRIDE_AWS_REGION";
@@ -1,14 +1,9 @@
1
- import type { RdsProxySecret } from "./dbsecret.js";
2
- import { SecretHolder } from "./secret-holder.js";
3
1
  /**
4
2
  * Holds credentials for RDS Proxy access.
5
3
  */
6
4
  export declare class ProxyHolder {
7
5
  private readonly secretHolder;
8
- private constructor();
9
- /** Creates a new instance of ProxyHolder from given SecretHolder. */
10
- static create(secretHolder: SecretHolder<RdsProxySecret>): ProxyHolder;
11
- /** Creates a new instance of ProxyHolder with a new default SecretHolder(using env variable SECRET_ID). */
6
+ constructor(secretId: string);
12
7
  static create(): ProxyHolder;
13
8
  setCredentials(): Promise<void>;
14
9
  }
@@ -8,13 +8,11 @@ const RDS_PROXY_SECRET_KEYS = Object.values(RdsProxySecretKey);
8
8
  */
9
9
  export class ProxyHolder {
10
10
  secretHolder;
11
- constructor(secretHolder) {
12
- this.secretHolder = secretHolder;
11
+ constructor(secretId) {
12
+ this.secretHolder = new SecretHolder(secretId, "", RDS_PROXY_SECRET_KEYS);
13
13
  }
14
- static create(secretHolder) {
15
- const holder = secretHolder ??
16
- new SecretHolder(getEnvVariable("SECRET_ID"), "", RDS_PROXY_SECRET_KEYS);
17
- return new ProxyHolder(holder);
14
+ static create() {
15
+ return new ProxyHolder(getEnvVariable("SECRET_ID"));
18
16
  }
19
17
  async setCredentials() {
20
18
  const secret = await this.secretHolder.get();
@@ -1,12 +1,9 @@
1
- import type { RdsSecret } from "./dbsecret.js";
2
- import { SecretHolder } from "./secret-holder.js";
3
1
  /**
4
2
  * Holds credentials for RDS access.
5
3
  */
6
4
  export declare class RdsHolder {
7
5
  private readonly secretHolder;
8
- private constructor();
9
- static create(rdsHolder: SecretHolder<RdsSecret>): RdsHolder;
6
+ constructor(secretId: string);
10
7
  static create(): RdsHolder;
11
8
  setCredentials(): Promise<void>;
12
9
  }
@@ -8,13 +8,11 @@ const RDS_SECRET_KEYS = Object.values(RdsSecretKey);
8
8
  */
9
9
  export class RdsHolder {
10
10
  secretHolder;
11
- constructor(secretHolder) {
12
- this.secretHolder = secretHolder;
11
+ constructor(secretId) {
12
+ this.secretHolder = new SecretHolder(secretId, "", RDS_SECRET_KEYS);
13
13
  }
14
- static create(secretHolder) {
15
- const holder = secretHolder ??
16
- new SecretHolder(getEnvVariable("SECRET_ID"), "", RDS_SECRET_KEYS);
17
- return new RdsHolder(holder);
14
+ static create() {
15
+ return new RdsHolder(getEnvVariable("SECRET_ID"));
18
16
  }
19
17
  async setCredentials() {
20
18
  const secret = await this.secretHolder.get();
@@ -1,4 +1,4 @@
1
- import type { IDatabase, ITask } from "pg-promise";
1
+ import { type IDatabase, type ITask } from "pg-promise";
2
2
  export declare enum DatabaseEnvironmentKeys {
3
3
  DB_USER = "DB_USER",
4
4
  DB_PASS = "DB_PASS",
@@ -1,4 +1,4 @@
1
- import pgpImport from "pg-promise";
1
+ import pgpImport, {} from "pg-promise";
2
2
  import { logger } from "../aws/runtime/dt-logger-default.js";
3
3
  import { logException } from "../utils/logging.js";
4
4
  import { getEnvVariable, getEnvVariableOrElse } from "../utils/utils.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@digitraffic/common",
3
- "version": "2026.8.31-2",
3
+ "version": "2026.9.4-1",
4
4
  "private": false,
5
5
  "description": "",
6
6
  "repository": {
@@ -1 +0,0 @@
1
- export {};
@@ -1,70 +0,0 @@
1
- import { App } from "aws-cdk-lib";
2
- import { AssetCode } from "aws-cdk-lib/aws-lambda";
3
- import { describe, expect, test } from "vitest";
4
- import { FunctionBuilder } from "../../../aws/infra/stack/dt-function.js";
5
- import { DigitrafficStack } from "../../../aws/infra/stack/stack.js";
6
- function createStack() {
7
- const app = new App();
8
- return new DigitrafficStack(app, "TestStack", {
9
- shortName: "TEST",
10
- });
11
- }
12
- function getCode(builder) {
13
- return builder.code;
14
- }
15
- function getHandler(builder) {
16
- return builder.handler;
17
- }
18
- describe("FunctionBuilder.withAssetCode", () => {
19
- test("uses lambdaName as default path", () => {
20
- const stack = createStack();
21
- const builder = FunctionBuilder.create(stack, "feature/subfeature/lambda-implementation");
22
- const code = getCode(builder);
23
- expect(code).toBeInstanceOf(AssetCode);
24
- expect(code.path).toBe("dist/lambda/feature/subfeature/lambda-implementation");
25
- });
26
- test("uses custom path when provided", () => {
27
- const stack = createStack();
28
- const builder = FunctionBuilder.create(stack, "feature/subfeature/lambda-implementation").withAssetCode("feature/subfeature/lambda-implementation");
29
- const code = getCode(builder);
30
- expect(code.path).toBe("dist/lambda/feature/subfeature/lambda-implementation");
31
- });
32
- test("returns this for chaining when called without exclude", () => {
33
- const stack = createStack();
34
- const builder = FunctionBuilder.create(stack, "feature/subfeature/lambda-implementation");
35
- expect(builder.withAssetCode("feature/subfeature/lambda-implementation")).toBe(builder);
36
- });
37
- test("accepts exclude list and uses correct path", () => {
38
- const stack = createStack();
39
- const builder = FunctionBuilder.create(stack, "feature/shared").withAssetCode("feature/shared", [
40
- "handler-a/**",
41
- "handler-b/**",
42
- "handler-c/**",
43
- ]);
44
- const code = getCode(builder);
45
- expect(code).toBeInstanceOf(AssetCode);
46
- expect(code.path).toBe("dist/lambda/feature/shared");
47
- });
48
- test("returns this for chaining when called with exclude", () => {
49
- const stack = createStack();
50
- const builder = FunctionBuilder.create(stack, "feature/shared");
51
- expect(builder.withAssetCode("feature/shared", ["handler-a/**"])).toBe(builder);
52
- });
53
- test("exclude is optional — undefined behaves same as no exclude", () => {
54
- const stack = createStack();
55
- const builderWithout = FunctionBuilder.create(stack, "test/lambda");
56
- const builderWithUndefined = FunctionBuilder.create(stack, "test/lambda2").withAssetCode("test/lambda2", undefined);
57
- expect(getCode(builderWithout).path).toBe("dist/lambda/test/lambda");
58
- expect(getCode(builderWithUndefined).path).toBe("dist/lambda/test/lambda2");
59
- });
60
- test("handler uses basename(path), not the full path", () => {
61
- const stack = createStack();
62
- // Without explicit withAssetCode — constructor default
63
- const builderDefault = FunctionBuilder.create(stack, "feature/shared");
64
- expect(getHandler(builderDefault)).toBe("shared.handler");
65
- // With explicit withAssetCode (e.g. to pass exclude) — must also use basename
66
- const builderExplicit = FunctionBuilder.create(stack, "feature/shared2").withAssetCode("feature/shared2", ["handler-a/**"]);
67
- expect(getHandler(builderExplicit)).toBe("shared2.handler");
68
- });
69
- });
70
- //# sourceMappingURL=dt-function.test.js.map
@@ -1 +0,0 @@
1
- export {};
@@ -1,33 +0,0 @@
1
- import { App } from "aws-cdk-lib";
2
- import { describe, expect, test } from "vitest";
3
- import { DigitrafficStack } from "../../../aws/infra/stack/stack.js";
4
- import { EnvKeys } from "../../../aws/runtime/environment.js";
5
- import { TrafficType } from "../../../types/traffictype.js";
6
- function createStack(secretId) {
7
- const configuration = {
8
- shortName: "VS",
9
- trafficType: TrafficType.ROAD,
10
- secretId,
11
- alarmTopicArn: "",
12
- warningTopicArn: "",
13
- production: false,
14
- stackProps: {},
15
- };
16
- return new DigitrafficStack(new App(), "TestStack", configuration);
17
- }
18
- describe("DigitrafficStack.createDefaultLambdaEnvironment", () => {
19
- test("includes the application name without a secret", () => {
20
- expect(createStack().createDefaultLambdaEnvironment("variable-signs")).toEqual({
21
- [EnvKeys.APP_NAME]: "road-vs",
22
- DB_APPLICATION: "variable-signs",
23
- });
24
- });
25
- test("preserves the secret configuration", () => {
26
- expect(createStack("test-secret").createDefaultLambdaEnvironment("variable-signs")).toEqual({
27
- [EnvKeys.APP_NAME]: "road-vs",
28
- DB_APPLICATION: "variable-signs",
29
- SECRET_ID: "test-secret",
30
- });
31
- });
32
- });
33
- //# sourceMappingURL=stack.test.js.map
@@ -1 +0,0 @@
1
- export {};