@friggframework/devtools 2.0.0--canary.623.71305f2.0 → 2.0.0--canary.622.fe9da2d.0

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.
@@ -1,20 +1,42 @@
1
1
  /**
2
2
  * Environment Builder Service
3
- *
3
+ *
4
4
  * Domain Service - Hexagonal Architecture
5
- *
5
+ *
6
6
  * Builds Lambda environment variable configuration from:
7
7
  * 1. AppDefinition environment flags
8
8
  * 2. Discovered AWS resources (VPC IDs, KMS keys, etc.)
9
9
  * 3. Generated resource references
10
10
  */
11
11
 
12
+ // OTLP-family exporters read their endpoint/headers from these standard env
13
+ // vars (ADR-011). When such an exporter is configured we auto-register them as
14
+ // Serverless passthroughs so the deployed Lambda inherits them from the deploy
15
+ // environment — no need for the adopter to also list them under `environment`.
16
+ //
17
+ // NOTE (VPC egress): a Lambda in a private subnet needs a NAT gateway or a VPC
18
+ // endpoint to reach an external OTLP backend (Honeycomb/Datadog). Without egress
19
+ // the exporter fails silently within its flush timeout — see the deploy docs.
20
+ const OTLP_EXPORTER_TYPES = new Set(['otlp', 'honeycomb', 'datadog']);
21
+ const OTEL_PASSTHROUGH_VARS = [
22
+ 'OTEL_EXPORTER_OTLP_ENDPOINT',
23
+ 'OTEL_EXPORTER_OTLP_HEADERS',
24
+ ];
25
+
26
+ function addTelemetryEnvPassthrough(appDefinition, envVars) {
27
+ const exporterType = appDefinition?.telemetry?.exporter?.type;
28
+ if (!OTLP_EXPORTER_TYPES.has(exporterType)) return;
29
+ for (const key of OTEL_PASSTHROUGH_VARS) {
30
+ envVars[key] = `\${env:${key}, ''}`;
31
+ }
32
+ }
33
+
12
34
  /**
13
35
  * Get environment variables from AppDefinition
14
- *
36
+ *
15
37
  * Extracts environment variable definitions where value is true,
16
38
  * and creates Serverless variable references.
17
- *
39
+ *
18
40
  * @param {Object} appDefinition - Application definition
19
41
  * @returns {Object} Environment variable mappings
20
42
  */
@@ -38,6 +60,8 @@ function getAppEnvironmentVars(appDefinition) {
38
60
  'AWS_SESSION_TOKEN',
39
61
  ]);
40
62
 
63
+ addTelemetryEnvPassthrough(appDefinition, envVars);
64
+
41
65
  if (!appDefinition.environment) {
42
66
  return envVars;
43
67
  }
@@ -65,7 +89,8 @@ function getAppEnvironmentVars(appDefinition) {
65
89
  }
66
90
  if (skippedKeys.length > 0) {
67
91
  console.log(
68
- ` ⚠️ Skipped ${skippedKeys.length
92
+ ` ⚠️ Skipped ${
93
+ skippedKeys.length
69
94
  } reserved AWS Lambda variables: ${skippedKeys.join(', ')}`
70
95
  );
71
96
  }
@@ -75,9 +100,9 @@ function getAppEnvironmentVars(appDefinition) {
75
100
 
76
101
  /**
77
102
  * Build complete environment configuration for Lambda functions
78
- *
103
+ *
79
104
  * Combines app environment vars with discovered AWS resource references
80
- *
105
+ *
81
106
  * @param {Object} appEnvironmentVars - Environment vars from AppDefinition
82
107
  * @param {Object} discoveredResources - Discovered AWS resources
83
108
  * @returns {Object} Complete environment configuration
@@ -85,7 +110,7 @@ function getAppEnvironmentVars(appDefinition) {
85
110
  function buildEnvironment(appEnvironmentVars, discoveredResources) {
86
111
  const environment = {
87
112
  ...appEnvironmentVars,
88
- STAGE: '${self:provider.stage}', // Used by encryption bypass logic
113
+ STAGE: '${self:provider.stage}', // Used by encryption bypass logic
89
114
  FRIGG_STACK: '${self:service}',
90
115
  FRIGG_STAGE: '${self:provider.stage}',
91
116
  FRIGG_REGION: '${self:provider.region}',
@@ -101,7 +126,9 @@ function buildEnvironment(appEnvironmentVars, discoveredResources) {
101
126
  // Add database connection info if discovered
102
127
  if (discoveredResources.auroraClusterEndpoint) {
103
128
  environment.DATABASE_HOST = discoveredResources.auroraClusterEndpoint;
104
- environment.DATABASE_PORT = String(discoveredResources.auroraPort || 5432);
129
+ environment.DATABASE_PORT = String(
130
+ discoveredResources.auroraPort || 5432
131
+ );
105
132
  }
106
133
 
107
134
  // Add secrets manager secret ARN if discovered
@@ -116,4 +143,3 @@ module.exports = {
116
143
  getAppEnvironmentVars,
117
144
  buildEnvironment,
118
145
  };
119
-
@@ -1,10 +1,13 @@
1
1
  /**
2
2
  * Tests for Environment Builder Service
3
- *
3
+ *
4
4
  * Tests environment variable extraction and building
5
5
  */
6
6
 
7
- const { getAppEnvironmentVars, buildEnvironment } = require('./environment-builder');
7
+ const {
8
+ getAppEnvironmentVars,
9
+ buildEnvironment,
10
+ } = require('./environment-builder');
8
11
 
9
12
  describe('Environment Builder', () => {
10
13
  describe('getAppEnvironmentVars()', () => {
@@ -108,6 +111,42 @@ describe('Environment Builder', () => {
108
111
  });
109
112
  });
110
113
 
114
+ describe('telemetry env passthrough (ADR-011)', () => {
115
+ it('auto-adds OTLP env passthroughs when an OTLP-family exporter is configured', () => {
116
+ const result = getAppEnvironmentVars({
117
+ telemetry: { exporter: { type: 'otlp' } },
118
+ });
119
+
120
+ expect(result.OTEL_EXPORTER_OTLP_ENDPOINT).toBe(
121
+ "${env:OTEL_EXPORTER_OTLP_ENDPOINT, ''}"
122
+ );
123
+ expect(result.OTEL_EXPORTER_OTLP_HEADERS).toBe(
124
+ "${env:OTEL_EXPORTER_OTLP_HEADERS, ''}"
125
+ );
126
+ });
127
+
128
+ it.each(['honeycomb', 'datadog'])(
129
+ 'adds OTLP passthroughs for the "%s" preset',
130
+ (type) => {
131
+ const result = getAppEnvironmentVars({
132
+ telemetry: { exporter: { type } },
133
+ });
134
+ expect(result.OTEL_EXPORTER_OTLP_ENDPOINT).toBeDefined();
135
+ }
136
+ );
137
+
138
+ it('adds no OTLP env vars for console/none exporters or absent telemetry', () => {
139
+ expect(
140
+ getAppEnvironmentVars({
141
+ telemetry: { exporter: { type: 'console' } },
142
+ }).OTEL_EXPORTER_OTLP_ENDPOINT
143
+ ).toBeUndefined();
144
+ expect(
145
+ getAppEnvironmentVars({}).OTEL_EXPORTER_OTLP_ENDPOINT
146
+ ).toBeUndefined();
147
+ });
148
+ });
149
+
111
150
  describe('buildEnvironment()', () => {
112
151
  it('should combine app vars with standard Frigg variables', () => {
113
152
  const appEnvironmentVars = {
@@ -115,7 +154,10 @@ describe('Environment Builder', () => {
115
154
  };
116
155
  const discoveredResources = {};
117
156
 
118
- const result = buildEnvironment(appEnvironmentVars, discoveredResources);
157
+ const result = buildEnvironment(
158
+ appEnvironmentVars,
159
+ discoveredResources
160
+ );
119
161
 
120
162
  expect(result.API_KEY).toBe("${env:API_KEY, ''}");
121
163
  expect(result.STAGE).toBe('${self:provider.stage}');
@@ -130,9 +172,14 @@ describe('Environment Builder', () => {
130
172
  kmsKeyId: 'arn:aws:kms:us-east-1:123456:key/abc-123',
131
173
  };
132
174
 
133
- const result = buildEnvironment(appEnvironmentVars, discoveredResources);
175
+ const result = buildEnvironment(
176
+ appEnvironmentVars,
177
+ discoveredResources
178
+ );
134
179
 
135
- expect(result.KMS_KEY_ARN).toBe('arn:aws:kms:us-east-1:123456:key/abc-123');
180
+ expect(result.KMS_KEY_ARN).toBe(
181
+ 'arn:aws:kms:us-east-1:123456:key/abc-123'
182
+ );
136
183
  });
137
184
 
138
185
  it('should prefer kmsKeyId over kmsKeyArn if both present', () => {
@@ -142,22 +189,33 @@ describe('Environment Builder', () => {
142
189
  kmsKeyArn: 'arn:aws:kms:us-east-1:123456:key/secondary',
143
190
  };
144
191
 
145
- const result = buildEnvironment(appEnvironmentVars, discoveredResources);
192
+ const result = buildEnvironment(
193
+ appEnvironmentVars,
194
+ discoveredResources
195
+ );
146
196
 
147
197
  // Implementation uses if/else-if, so kmsKeyId takes priority
148
- expect(result.KMS_KEY_ARN).toBe('arn:aws:kms:us-east-1:123456:key/primary');
198
+ expect(result.KMS_KEY_ARN).toBe(
199
+ 'arn:aws:kms:us-east-1:123456:key/primary'
200
+ );
149
201
  });
150
202
 
151
203
  it('should add database connection info if discovered', () => {
152
204
  const appEnvironmentVars = {};
153
205
  const discoveredResources = {
154
- auroraClusterEndpoint: 'cluster.abc.us-east-1.rds.amazonaws.com',
206
+ auroraClusterEndpoint:
207
+ 'cluster.abc.us-east-1.rds.amazonaws.com',
155
208
  auroraPort: 5432,
156
209
  };
157
210
 
158
- const result = buildEnvironment(appEnvironmentVars, discoveredResources);
211
+ const result = buildEnvironment(
212
+ appEnvironmentVars,
213
+ discoveredResources
214
+ );
159
215
 
160
- expect(result.DATABASE_HOST).toBe('cluster.abc.us-east-1.rds.amazonaws.com');
216
+ expect(result.DATABASE_HOST).toBe(
217
+ 'cluster.abc.us-east-1.rds.amazonaws.com'
218
+ );
161
219
  expect(result.DATABASE_PORT).toBe('5432');
162
220
  });
163
221
 
@@ -167,7 +225,10 @@ describe('Environment Builder', () => {
167
225
  auroraClusterEndpoint: 'cluster.example.com',
168
226
  };
169
227
 
170
- const result = buildEnvironment(appEnvironmentVars, discoveredResources);
228
+ const result = buildEnvironment(
229
+ appEnvironmentVars,
230
+ discoveredResources
231
+ );
171
232
 
172
233
  expect(result.DATABASE_HOST).toBe('cluster.example.com');
173
234
  expect(result.DATABASE_PORT).toBe('5432');
@@ -176,12 +237,18 @@ describe('Environment Builder', () => {
176
237
  it('should add database secret ARN if discovered', () => {
177
238
  const appEnvironmentVars = {};
178
239
  const discoveredResources = {
179
- databaseSecretArn: 'arn:aws:secretsmanager:us-east-1:123456:secret:db-secret',
240
+ databaseSecretArn:
241
+ 'arn:aws:secretsmanager:us-east-1:123456:secret:db-secret',
180
242
  };
181
243
 
182
- const result = buildEnvironment(appEnvironmentVars, discoveredResources);
244
+ const result = buildEnvironment(
245
+ appEnvironmentVars,
246
+ discoveredResources
247
+ );
183
248
 
184
- expect(result.DATABASE_SECRET_ARN).toBe('arn:aws:secretsmanager:us-east-1:123456:secret:db-secret');
249
+ expect(result.DATABASE_SECRET_ARN).toBe(
250
+ 'arn:aws:secretsmanager:us-east-1:123456:secret:db-secret'
251
+ );
185
252
  });
186
253
 
187
254
  it('should combine all discovered resources', () => {
@@ -192,19 +259,27 @@ describe('Environment Builder', () => {
192
259
  kmsKeyArn: 'arn:aws:kms:us-east-1:123456:key/abc',
193
260
  auroraClusterEndpoint: 'db.example.com',
194
261
  auroraPort: 3306,
195
- databaseSecretArn: 'arn:aws:secretsmanager:us-east-1:123456:secret:db',
262
+ databaseSecretArn:
263
+ 'arn:aws:secretsmanager:us-east-1:123456:secret:db',
196
264
  };
197
265
 
198
- const result = buildEnvironment(appEnvironmentVars, discoveredResources);
266
+ const result = buildEnvironment(
267
+ appEnvironmentVars,
268
+ discoveredResources
269
+ );
199
270
 
200
271
  expect(result.CUSTOM_VAR).toBe("${env:CUSTOM_VAR, ''}");
201
272
  expect(result.FRIGG_STACK).toBe('${self:service}');
202
273
  expect(result.FRIGG_STAGE).toBe('${self:provider.stage}');
203
274
  expect(result.FRIGG_REGION).toBe('${self:provider.region}');
204
- expect(result.KMS_KEY_ARN).toBe('arn:aws:kms:us-east-1:123456:key/abc');
275
+ expect(result.KMS_KEY_ARN).toBe(
276
+ 'arn:aws:kms:us-east-1:123456:key/abc'
277
+ );
205
278
  expect(result.DATABASE_HOST).toBe('db.example.com');
206
279
  expect(result.DATABASE_PORT).toBe('3306');
207
- expect(result.DATABASE_SECRET_ARN).toBe('arn:aws:secretsmanager:us-east-1:123456:secret:db');
280
+ expect(result.DATABASE_SECRET_ARN).toBe(
281
+ 'arn:aws:secretsmanager:us-east-1:123456:secret:db'
282
+ );
208
283
  });
209
284
 
210
285
  it('should handle empty discoveredResources', () => {
@@ -213,7 +288,10 @@ describe('Environment Builder', () => {
213
288
  };
214
289
  const discoveredResources = {};
215
290
 
216
- const result = buildEnvironment(appEnvironmentVars, discoveredResources);
291
+ const result = buildEnvironment(
292
+ appEnvironmentVars,
293
+ discoveredResources
294
+ );
217
295
 
218
296
  expect(result.API_KEY).toBe("${env:API_KEY, ''}");
219
297
  expect(result.FRIGG_STACK).toBe('${self:service}');
@@ -237,11 +315,13 @@ describe('Environment Builder', () => {
237
315
  auroraPort: 3306, // Number
238
316
  };
239
317
 
240
- const result = buildEnvironment(appEnvironmentVars, discoveredResources);
318
+ const result = buildEnvironment(
319
+ appEnvironmentVars,
320
+ discoveredResources
321
+ );
241
322
 
242
323
  expect(result.DATABASE_PORT).toBe('3306'); // String
243
324
  expect(typeof result.DATABASE_PORT).toBe('string');
244
325
  });
245
326
  });
246
327
  });
247
-
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@friggframework/devtools",
3
3
  "prettier": "@friggframework/prettier-config",
4
- "version": "2.0.0--canary.623.71305f2.0",
4
+ "version": "2.0.0--canary.622.fe9da2d.0",
5
5
  "bin": {
6
6
  "frigg": "./frigg-cli/index.js"
7
7
  },
@@ -25,9 +25,9 @@
25
25
  "@babel/eslint-parser": "^7.18.9",
26
26
  "@babel/parser": "^7.25.3",
27
27
  "@babel/traverse": "^7.25.3",
28
- "@friggframework/core": "2.0.0--canary.623.71305f2.0",
29
- "@friggframework/schemas": "2.0.0--canary.623.71305f2.0",
30
- "@friggframework/test": "2.0.0--canary.623.71305f2.0",
28
+ "@friggframework/core": "2.0.0--canary.622.fe9da2d.0",
29
+ "@friggframework/schemas": "2.0.0--canary.622.fe9da2d.0",
30
+ "@friggframework/test": "2.0.0--canary.622.fe9da2d.0",
31
31
  "@hapi/boom": "^10.0.1",
32
32
  "@inquirer/prompts": "^5.3.8",
33
33
  "axios": "^1.18.0",
@@ -55,8 +55,8 @@
55
55
  "validate-npm-package-name": "^5.0.0"
56
56
  },
57
57
  "devDependencies": {
58
- "@friggframework/eslint-config": "2.0.0--canary.623.71305f2.0",
59
- "@friggframework/prettier-config": "2.0.0--canary.623.71305f2.0",
58
+ "@friggframework/eslint-config": "2.0.0--canary.622.fe9da2d.0",
59
+ "@friggframework/prettier-config": "2.0.0--canary.622.fe9da2d.0",
60
60
  "aws-sdk-client-mock": "^4.1.0",
61
61
  "aws-sdk-client-mock-jest": "^4.1.0",
62
62
  "jest": "^30.1.3",
@@ -88,5 +88,5 @@
88
88
  "publishConfig": {
89
89
  "access": "public"
90
90
  },
91
- "gitHead": "71305f2dfed42de7be90e012a383437a46404159"
91
+ "gitHead": "fe9da2d26354e6118a6f14350cdf18394408b3a4"
92
92
  }