@friggframework/devtools 2.0.0--canary.625.eaeb79b.0 → 2.0.0--canary.622.44c5bcb.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,15 +1,52 @@
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
- const { isSsmOffloadActive, getOffloadedKeys } = require('../parameters/offload-utils');
12
+ const {
13
+ isSsmOffloadActive,
14
+ getOffloadedKeys,
15
+ } = require('../parameters/offload-utils');
16
+
17
+ // OTLP-family exporters read their endpoint/headers from these standard env
18
+ // vars (ADR-011). When such an exporter is configured we auto-register them as
19
+ // Serverless passthroughs so the deployed Lambda inherits them from the deploy
20
+ // environment — no need for the adopter to also list them under `environment`.
21
+ //
22
+ // NOTE (VPC egress): a Lambda in a private subnet needs a NAT gateway or a VPC
23
+ // endpoint to reach an external OTLP backend (Honeycomb/Datadog). Without egress
24
+ // the exporter fails silently within its flush timeout — see the deploy docs.
25
+ const OTLP_EXPORTER_TYPES = new Set(['otlp', 'honeycomb', 'datadog']);
26
+ const OTEL_PASSTHROUGH_VARS = [
27
+ 'OTEL_EXPORTER_OTLP_ENDPOINT',
28
+ 'OTEL_EXPORTER_OTLP_HEADERS',
29
+ ];
30
+
31
+ function addTelemetryEnvPassthrough(appDefinition, envVars) {
32
+ const exporterType = appDefinition?.telemetry?.exporter?.type;
33
+ if (!OTLP_EXPORTER_TYPES.has(exporterType)) return;
34
+
35
+ // A passthrough var that is also marked for SSM offload must NOT be baked
36
+ // as a direct `${env:KEY, ''}` Lambda var: the deploy shell has no such var
37
+ // (it lives only in Parameter Store), so it resolves to '' at package time,
38
+ // and at runtime '' !== undefined blocks the SSM loader from ever populating
39
+ // it. Leave it out here so it lives only in FRIGG_SSM_OFFLOADED_KEYS. When
40
+ // offload is inactive (local mode / ssm disabled) it still gets the
41
+ // passthrough, matching the local-fallback contract.
42
+ const offloadedKeys = isSsmOffloadActive(appDefinition)
43
+ ? new Set(getOffloadedKeys(appDefinition))
44
+ : null;
45
+ for (const key of OTEL_PASSTHROUGH_VARS) {
46
+ if (offloadedKeys?.has(key)) continue;
47
+ envVars[key] = `\${env:${key}, ''}`;
48
+ }
49
+ }
13
50
 
14
51
  /**
15
52
  * Get environment variables from AppDefinition
@@ -45,6 +82,8 @@ function getAppEnvironmentVars(appDefinition) {
45
82
  'AWS_SESSION_TOKEN',
46
83
  ]);
47
84
 
85
+ addTelemetryEnvPassthrough(appDefinition, envVars);
86
+
48
87
  const environment = appDefinition.environment || {};
49
88
 
50
89
  console.log('📋 Loading environment variables from appDefinition...');
@@ -94,15 +133,16 @@ function getAppEnvironmentVars(appDefinition) {
94
133
  }
95
134
  if (skippedKeys.length > 0) {
96
135
  console.log(
97
- ` ⚠️ Skipped ${skippedKeys.length
136
+ ` ⚠️ Skipped ${
137
+ skippedKeys.length
98
138
  } reserved AWS Lambda variables: ${skippedKeys.join(', ')}`
99
139
  );
100
140
  }
101
141
  if (offloadedKeys.length > 0) {
102
142
  console.log(
103
- ` 🔒 Offloaded ${offloadedKeys.length} variables to SSM: ${offloadedKeys.join(
104
- ', '
105
- )}`
143
+ ` 🔒 Offloaded ${
144
+ offloadedKeys.length
145
+ } variables to SSM: ${offloadedKeys.join(', ')}`
106
146
  );
107
147
  }
108
148
 
@@ -111,9 +151,9 @@ function getAppEnvironmentVars(appDefinition) {
111
151
 
112
152
  /**
113
153
  * Build complete environment configuration for Lambda functions
114
- *
154
+ *
115
155
  * Combines app environment vars with discovered AWS resource references
116
- *
156
+ *
117
157
  * @param {Object} appEnvironmentVars - Environment vars from AppDefinition
118
158
  * @param {Object} discoveredResources - Discovered AWS resources
119
159
  * @returns {Object} Complete environment configuration
@@ -121,7 +161,7 @@ function getAppEnvironmentVars(appDefinition) {
121
161
  function buildEnvironment(appEnvironmentVars, discoveredResources) {
122
162
  const environment = {
123
163
  ...appEnvironmentVars,
124
- STAGE: '${self:provider.stage}', // Used by encryption bypass logic
164
+ STAGE: '${self:provider.stage}', // Used by encryption bypass logic
125
165
  FRIGG_STACK: '${self:service}',
126
166
  FRIGG_STAGE: '${self:provider.stage}',
127
167
  FRIGG_REGION: '${self:provider.region}',
@@ -137,7 +177,9 @@ function buildEnvironment(appEnvironmentVars, discoveredResources) {
137
177
  // Add database connection info if discovered
138
178
  if (discoveredResources.auroraClusterEndpoint) {
139
179
  environment.DATABASE_HOST = discoveredResources.auroraClusterEndpoint;
140
- environment.DATABASE_PORT = String(discoveredResources.auroraPort || 5432);
180
+ environment.DATABASE_PORT = String(
181
+ discoveredResources.auroraPort || 5432
182
+ );
141
183
  }
142
184
 
143
185
  // Add secrets manager secret ARN if discovered
@@ -152,4 +194,3 @@ module.exports = {
152
194
  getAppEnvironmentVars,
153
195
  buildEnvironment,
154
196
  };
155
-
@@ -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("getAppEnvironmentVars() - 'ssm' offload", () => {
112
151
  const originalSkipDiscovery = process.env.FRIGG_SKIP_AWS_DISCOVERY;
113
152
 
@@ -161,13 +200,17 @@ describe('Environment Builder', () => {
161
200
  const appDefinition = {
162
201
  ssm: {
163
202
  enable: true,
164
- parameters: { HUBSPOT_CLIENT_SECRET: { type: 'SecureString' } },
203
+ parameters: {
204
+ HUBSPOT_CLIENT_SECRET: { type: 'SecureString' },
205
+ },
165
206
  },
166
207
  };
167
208
 
168
209
  const result = getAppEnvironmentVars(appDefinition);
169
210
 
170
- expect(result.HUBSPOT_CLIENT_SECRET).toBe("${env:HUBSPOT_CLIENT_SECRET, ''}");
211
+ expect(result.HUBSPOT_CLIENT_SECRET).toBe(
212
+ "${env:HUBSPOT_CLIENT_SECRET, ''}"
213
+ );
171
214
  });
172
215
 
173
216
  it('excludes a key declared only in ssm.parameters when offload is active', () => {
@@ -175,7 +218,9 @@ describe('Environment Builder', () => {
175
218
  const appDefinition = {
176
219
  ssm: {
177
220
  enable: true,
178
- parameters: { HUBSPOT_CLIENT_SECRET: { type: 'SecureString' } },
221
+ parameters: {
222
+ HUBSPOT_CLIENT_SECRET: { type: 'SecureString' },
223
+ },
179
224
  },
180
225
  };
181
226
 
@@ -204,6 +249,106 @@ describe('Environment Builder', () => {
204
249
  });
205
250
  });
206
251
 
252
+ describe('telemetry env passthrough + SSM offload interaction', () => {
253
+ const originalSkipDiscovery = process.env.FRIGG_SKIP_AWS_DISCOVERY;
254
+
255
+ afterEach(() => {
256
+ if (originalSkipDiscovery === undefined) {
257
+ delete process.env.FRIGG_SKIP_AWS_DISCOVERY;
258
+ } else {
259
+ process.env.FRIGG_SKIP_AWS_DISCOVERY = originalSkipDiscovery;
260
+ }
261
+ });
262
+
263
+ it("does not bake an OTEL passthrough var that is marked 'ssm' when offload is active", () => {
264
+ delete process.env.FRIGG_SKIP_AWS_DISCOVERY;
265
+ const appDefinition = {
266
+ telemetry: { exporter: { type: 'datadog' } },
267
+ ssm: { enable: true },
268
+ environment: {
269
+ OTEL_EXPORTER_OTLP_ENDPOINT: 'ssm',
270
+ OTEL_EXPORTER_OTLP_HEADERS: 'ssm',
271
+ },
272
+ };
273
+
274
+ const result = getAppEnvironmentVars(appDefinition);
275
+
276
+ expect(result.OTEL_EXPORTER_OTLP_ENDPOINT).toBeUndefined();
277
+ expect(result.OTEL_EXPORTER_OTLP_HEADERS).toBeUndefined();
278
+ });
279
+
280
+ it("keeps the direct passthrough for an OTEL var not marked 'ssm' while offloading its sibling", () => {
281
+ delete process.env.FRIGG_SKIP_AWS_DISCOVERY;
282
+ const appDefinition = {
283
+ telemetry: { exporter: { type: 'otlp' } },
284
+ ssm: { enable: true },
285
+ environment: {
286
+ OTEL_EXPORTER_OTLP_ENDPOINT: 'ssm',
287
+ },
288
+ };
289
+
290
+ const result = getAppEnvironmentVars(appDefinition);
291
+
292
+ expect(result.OTEL_EXPORTER_OTLP_ENDPOINT).toBeUndefined();
293
+ expect(result.OTEL_EXPORTER_OTLP_HEADERS).toBe(
294
+ "${env:OTEL_EXPORTER_OTLP_HEADERS, ''}"
295
+ );
296
+ });
297
+
298
+ it("falls back to the env passthrough for an 'ssm'-marked OTEL var in local mode", () => {
299
+ process.env.FRIGG_SKIP_AWS_DISCOVERY = 'true';
300
+ const appDefinition = {
301
+ telemetry: { exporter: { type: 'datadog' } },
302
+ ssm: { enable: true },
303
+ environment: {
304
+ OTEL_EXPORTER_OTLP_ENDPOINT: 'ssm',
305
+ },
306
+ };
307
+
308
+ const result = getAppEnvironmentVars(appDefinition);
309
+
310
+ expect(result.OTEL_EXPORTER_OTLP_ENDPOINT).toBe(
311
+ "${env:OTEL_EXPORTER_OTLP_ENDPOINT, ''}"
312
+ );
313
+ });
314
+
315
+ it("leaves a 'true'-marked OTEL var as a direct passthrough even when offload is active for another key", () => {
316
+ delete process.env.FRIGG_SKIP_AWS_DISCOVERY;
317
+ const appDefinition = {
318
+ telemetry: { exporter: { type: 'datadog' } },
319
+ ssm: { enable: true },
320
+ environment: {
321
+ OTEL_EXPORTER_OTLP_ENDPOINT: true,
322
+ SOME_OFFLOADED_SECRET: 'ssm',
323
+ },
324
+ };
325
+
326
+ const result = getAppEnvironmentVars(appDefinition);
327
+
328
+ expect(result.OTEL_EXPORTER_OTLP_ENDPOINT).toBe(
329
+ "${env:OTEL_EXPORTER_OTLP_ENDPOINT, ''}"
330
+ );
331
+ expect(result.SOME_OFFLOADED_SECRET).toBeUndefined();
332
+ });
333
+
334
+ it('does not bake an OTEL passthrough var offloaded only via ssm.parameters', () => {
335
+ delete process.env.FRIGG_SKIP_AWS_DISCOVERY;
336
+ const appDefinition = {
337
+ telemetry: { exporter: { type: 'datadog' } },
338
+ ssm: {
339
+ enable: true,
340
+ parameters: {
341
+ OTEL_EXPORTER_OTLP_ENDPOINT: { type: 'SecureString' },
342
+ },
343
+ },
344
+ };
345
+
346
+ const result = getAppEnvironmentVars(appDefinition);
347
+
348
+ expect(result.OTEL_EXPORTER_OTLP_ENDPOINT).toBeUndefined();
349
+ });
350
+ });
351
+
207
352
  describe('buildEnvironment()', () => {
208
353
  it('should combine app vars with standard Frigg variables', () => {
209
354
  const appEnvironmentVars = {
@@ -211,7 +356,10 @@ describe('Environment Builder', () => {
211
356
  };
212
357
  const discoveredResources = {};
213
358
 
214
- const result = buildEnvironment(appEnvironmentVars, discoveredResources);
359
+ const result = buildEnvironment(
360
+ appEnvironmentVars,
361
+ discoveredResources
362
+ );
215
363
 
216
364
  expect(result.API_KEY).toBe("${env:API_KEY, ''}");
217
365
  expect(result.STAGE).toBe('${self:provider.stage}');
@@ -226,9 +374,14 @@ describe('Environment Builder', () => {
226
374
  kmsKeyId: 'arn:aws:kms:us-east-1:123456:key/abc-123',
227
375
  };
228
376
 
229
- const result = buildEnvironment(appEnvironmentVars, discoveredResources);
377
+ const result = buildEnvironment(
378
+ appEnvironmentVars,
379
+ discoveredResources
380
+ );
230
381
 
231
- expect(result.KMS_KEY_ARN).toBe('arn:aws:kms:us-east-1:123456:key/abc-123');
382
+ expect(result.KMS_KEY_ARN).toBe(
383
+ 'arn:aws:kms:us-east-1:123456:key/abc-123'
384
+ );
232
385
  });
233
386
 
234
387
  it('should prefer kmsKeyId over kmsKeyArn if both present', () => {
@@ -238,22 +391,33 @@ describe('Environment Builder', () => {
238
391
  kmsKeyArn: 'arn:aws:kms:us-east-1:123456:key/secondary',
239
392
  };
240
393
 
241
- const result = buildEnvironment(appEnvironmentVars, discoveredResources);
394
+ const result = buildEnvironment(
395
+ appEnvironmentVars,
396
+ discoveredResources
397
+ );
242
398
 
243
399
  // Implementation uses if/else-if, so kmsKeyId takes priority
244
- expect(result.KMS_KEY_ARN).toBe('arn:aws:kms:us-east-1:123456:key/primary');
400
+ expect(result.KMS_KEY_ARN).toBe(
401
+ 'arn:aws:kms:us-east-1:123456:key/primary'
402
+ );
245
403
  });
246
404
 
247
405
  it('should add database connection info if discovered', () => {
248
406
  const appEnvironmentVars = {};
249
407
  const discoveredResources = {
250
- auroraClusterEndpoint: 'cluster.abc.us-east-1.rds.amazonaws.com',
408
+ auroraClusterEndpoint:
409
+ 'cluster.abc.us-east-1.rds.amazonaws.com',
251
410
  auroraPort: 5432,
252
411
  };
253
412
 
254
- const result = buildEnvironment(appEnvironmentVars, discoveredResources);
413
+ const result = buildEnvironment(
414
+ appEnvironmentVars,
415
+ discoveredResources
416
+ );
255
417
 
256
- expect(result.DATABASE_HOST).toBe('cluster.abc.us-east-1.rds.amazonaws.com');
418
+ expect(result.DATABASE_HOST).toBe(
419
+ 'cluster.abc.us-east-1.rds.amazonaws.com'
420
+ );
257
421
  expect(result.DATABASE_PORT).toBe('5432');
258
422
  });
259
423
 
@@ -263,7 +427,10 @@ describe('Environment Builder', () => {
263
427
  auroraClusterEndpoint: 'cluster.example.com',
264
428
  };
265
429
 
266
- const result = buildEnvironment(appEnvironmentVars, discoveredResources);
430
+ const result = buildEnvironment(
431
+ appEnvironmentVars,
432
+ discoveredResources
433
+ );
267
434
 
268
435
  expect(result.DATABASE_HOST).toBe('cluster.example.com');
269
436
  expect(result.DATABASE_PORT).toBe('5432');
@@ -272,12 +439,18 @@ describe('Environment Builder', () => {
272
439
  it('should add database secret ARN if discovered', () => {
273
440
  const appEnvironmentVars = {};
274
441
  const discoveredResources = {
275
- databaseSecretArn: 'arn:aws:secretsmanager:us-east-1:123456:secret:db-secret',
442
+ databaseSecretArn:
443
+ 'arn:aws:secretsmanager:us-east-1:123456:secret:db-secret',
276
444
  };
277
445
 
278
- const result = buildEnvironment(appEnvironmentVars, discoveredResources);
446
+ const result = buildEnvironment(
447
+ appEnvironmentVars,
448
+ discoveredResources
449
+ );
279
450
 
280
- expect(result.DATABASE_SECRET_ARN).toBe('arn:aws:secretsmanager:us-east-1:123456:secret:db-secret');
451
+ expect(result.DATABASE_SECRET_ARN).toBe(
452
+ 'arn:aws:secretsmanager:us-east-1:123456:secret:db-secret'
453
+ );
281
454
  });
282
455
 
283
456
  it('should combine all discovered resources', () => {
@@ -288,19 +461,27 @@ describe('Environment Builder', () => {
288
461
  kmsKeyArn: 'arn:aws:kms:us-east-1:123456:key/abc',
289
462
  auroraClusterEndpoint: 'db.example.com',
290
463
  auroraPort: 3306,
291
- databaseSecretArn: 'arn:aws:secretsmanager:us-east-1:123456:secret:db',
464
+ databaseSecretArn:
465
+ 'arn:aws:secretsmanager:us-east-1:123456:secret:db',
292
466
  };
293
467
 
294
- const result = buildEnvironment(appEnvironmentVars, discoveredResources);
468
+ const result = buildEnvironment(
469
+ appEnvironmentVars,
470
+ discoveredResources
471
+ );
295
472
 
296
473
  expect(result.CUSTOM_VAR).toBe("${env:CUSTOM_VAR, ''}");
297
474
  expect(result.FRIGG_STACK).toBe('${self:service}');
298
475
  expect(result.FRIGG_STAGE).toBe('${self:provider.stage}');
299
476
  expect(result.FRIGG_REGION).toBe('${self:provider.region}');
300
- expect(result.KMS_KEY_ARN).toBe('arn:aws:kms:us-east-1:123456:key/abc');
477
+ expect(result.KMS_KEY_ARN).toBe(
478
+ 'arn:aws:kms:us-east-1:123456:key/abc'
479
+ );
301
480
  expect(result.DATABASE_HOST).toBe('db.example.com');
302
481
  expect(result.DATABASE_PORT).toBe('3306');
303
- expect(result.DATABASE_SECRET_ARN).toBe('arn:aws:secretsmanager:us-east-1:123456:secret:db');
482
+ expect(result.DATABASE_SECRET_ARN).toBe(
483
+ 'arn:aws:secretsmanager:us-east-1:123456:secret:db'
484
+ );
304
485
  });
305
486
 
306
487
  it('should handle empty discoveredResources', () => {
@@ -309,7 +490,10 @@ describe('Environment Builder', () => {
309
490
  };
310
491
  const discoveredResources = {};
311
492
 
312
- const result = buildEnvironment(appEnvironmentVars, discoveredResources);
493
+ const result = buildEnvironment(
494
+ appEnvironmentVars,
495
+ discoveredResources
496
+ );
313
497
 
314
498
  expect(result.API_KEY).toBe("${env:API_KEY, ''}");
315
499
  expect(result.FRIGG_STACK).toBe('${self:service}');
@@ -333,11 +517,13 @@ describe('Environment Builder', () => {
333
517
  auroraPort: 3306, // Number
334
518
  };
335
519
 
336
- const result = buildEnvironment(appEnvironmentVars, discoveredResources);
520
+ const result = buildEnvironment(
521
+ appEnvironmentVars,
522
+ discoveredResources
523
+ );
337
524
 
338
525
  expect(result.DATABASE_PORT).toBe('3306'); // String
339
526
  expect(typeof result.DATABASE_PORT).toBe('string');
340
527
  });
341
528
  });
342
529
  });
343
-
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.625.eaeb79b.0",
4
+ "version": "2.0.0--canary.622.44c5bcb.0",
5
5
  "bin": {
6
6
  "frigg": "./frigg-cli/index.js"
7
7
  },
@@ -26,9 +26,9 @@
26
26
  "@babel/eslint-parser": "^7.18.9",
27
27
  "@babel/parser": "^7.25.3",
28
28
  "@babel/traverse": "^7.25.3",
29
- "@friggframework/core": "2.0.0--canary.625.eaeb79b.0",
30
- "@friggframework/schemas": "2.0.0--canary.625.eaeb79b.0",
31
- "@friggframework/test": "2.0.0--canary.625.eaeb79b.0",
29
+ "@friggframework/core": "2.0.0--canary.622.44c5bcb.0",
30
+ "@friggframework/schemas": "2.0.0--canary.622.44c5bcb.0",
31
+ "@friggframework/test": "2.0.0--canary.622.44c5bcb.0",
32
32
  "@hapi/boom": "^10.0.1",
33
33
  "@inquirer/prompts": "^5.3.8",
34
34
  "axios": "^1.18.0",
@@ -56,8 +56,8 @@
56
56
  "validate-npm-package-name": "^5.0.0"
57
57
  },
58
58
  "devDependencies": {
59
- "@friggframework/eslint-config": "2.0.0--canary.625.eaeb79b.0",
60
- "@friggframework/prettier-config": "2.0.0--canary.625.eaeb79b.0",
59
+ "@friggframework/eslint-config": "2.0.0--canary.622.44c5bcb.0",
60
+ "@friggframework/prettier-config": "2.0.0--canary.622.44c5bcb.0",
61
61
  "aws-sdk-client-mock": "^4.1.0",
62
62
  "aws-sdk-client-mock-jest": "^4.1.0",
63
63
  "jest": "^30.1.3",
@@ -89,5 +89,5 @@
89
89
  "publishConfig": {
90
90
  "access": "public"
91
91
  },
92
- "gitHead": "eaeb79b0e8f8bbe466ac40f389d603e86bd806b7"
92
+ "gitHead": "44c5bcb5ac98478fe902170b7e5c0cf8c0315ddf"
93
93
  }