@friggframework/core 2.0.0--canary.625.27a7e7a.0 → 2.0.0--canary.625.8f60019.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.
- package/core/parameters-to-env.js +45 -2
- package/core/ssm-preload.mjs +39 -0
- package/package.json +5 -5
|
@@ -38,11 +38,11 @@ const parseKeys = (raw) =>
|
|
|
38
38
|
.map((key) => key.trim())
|
|
39
39
|
.filter(Boolean);
|
|
40
40
|
|
|
41
|
-
const sendWithRetry = async (command) => {
|
|
41
|
+
const sendWithRetry = async (ssmClient, command) => {
|
|
42
42
|
let attempt = 0;
|
|
43
43
|
for (;;) {
|
|
44
44
|
try {
|
|
45
|
-
return await
|
|
45
|
+
return await ssmClient.send(command);
|
|
46
46
|
} catch (err) {
|
|
47
47
|
if (
|
|
48
48
|
err.name === 'ThrottlingException' &&
|
|
@@ -57,6 +57,47 @@ const sendWithRetry = async (command) => {
|
|
|
57
57
|
}
|
|
58
58
|
};
|
|
59
59
|
|
|
60
|
+
/**
|
|
61
|
+
* Fetch offloaded parameters and return a plain { key: value } map. Fetch-only:
|
|
62
|
+
* no process.env mutation, no caching, no ownership tracking. Shared by the
|
|
63
|
+
* runtime loader and the INIT-phase preload (ssm-preload.js). Throws (listing
|
|
64
|
+
* every missing name) if any requested key is absent from SSM.
|
|
65
|
+
*/
|
|
66
|
+
const fetchOffloadedParameters = async (prefix, keys, { region } = {}) => {
|
|
67
|
+
const { SSMClient, GetParametersCommand } = require('@aws-sdk/client-ssm');
|
|
68
|
+
const ssmClient = new SSMClient({
|
|
69
|
+
region: region || process.env.AWS_REGION,
|
|
70
|
+
});
|
|
71
|
+
const nameFor = (key) => `${prefix}/${key}`;
|
|
72
|
+
|
|
73
|
+
const found = new Map();
|
|
74
|
+
for (const batch of chunk(keys, BATCH_SIZE)) {
|
|
75
|
+
const { Parameters = [] } = await sendWithRetry(
|
|
76
|
+
ssmClient,
|
|
77
|
+
new GetParametersCommand({
|
|
78
|
+
Names: batch.map(nameFor),
|
|
79
|
+
WithDecryption: true,
|
|
80
|
+
})
|
|
81
|
+
);
|
|
82
|
+
for (const param of Parameters) {
|
|
83
|
+
found.set(param.Name, param);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const missing = keys.map(nameFor).filter((name) => !found.has(name));
|
|
88
|
+
if (missing.length > 0) {
|
|
89
|
+
throw new Error(
|
|
90
|
+
`missing SSM parameters under ${prefix}: ${missing.join(', ')}`
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const values = {};
|
|
95
|
+
for (const key of keys) {
|
|
96
|
+
values[key] = found.get(nameFor(key)).Value;
|
|
97
|
+
}
|
|
98
|
+
return values;
|
|
99
|
+
};
|
|
100
|
+
|
|
60
101
|
const loadParameters = async (prefix, keys) => {
|
|
61
102
|
const { SSMClient, GetParametersCommand } = require('@aws-sdk/client-ssm');
|
|
62
103
|
if (!client) {
|
|
@@ -80,6 +121,7 @@ const loadParameters = async (prefix, keys) => {
|
|
|
80
121
|
try {
|
|
81
122
|
for (const batch of chunk(keysToFetch, BATCH_SIZE)) {
|
|
82
123
|
const { Parameters = [] } = await sendWithRetry(
|
|
124
|
+
client,
|
|
83
125
|
new GetParametersCommand({
|
|
84
126
|
Names: batch.map(nameFor),
|
|
85
127
|
WithDecryption: true,
|
|
@@ -166,5 +208,6 @@ const _resetCache = () => {
|
|
|
166
208
|
|
|
167
209
|
module.exports = {
|
|
168
210
|
parametersToEnv,
|
|
211
|
+
fetchOffloadedParameters,
|
|
169
212
|
_resetCache,
|
|
170
213
|
};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// INIT-phase SSM loader (ADR-027).
|
|
2
|
+
//
|
|
3
|
+
// Loaded via NODE_OPTIONS=--import BEFORE the Lambda handler and any api-module
|
|
4
|
+
// is required, so SSM-offloaded values are present in process.env at
|
|
5
|
+
// module-load time — when api-modules capture their OAuth client credentials
|
|
6
|
+
// in a top-level `const Definition = { env: { client_secret: process.env.X } }`.
|
|
7
|
+
// The runtime loader (parameters-to-env.js) runs inside the handler, which is
|
|
8
|
+
// too late for those module-load reads; this preload closes that gap.
|
|
9
|
+
//
|
|
10
|
+
// Top-level await here is awaited by Node before the entry module loads, so the
|
|
11
|
+
// fetch completes first. A fetch failure rejects the preload, failing Lambda
|
|
12
|
+
// INIT loudly (fail-fast) rather than starting with undefined credentials.
|
|
13
|
+
|
|
14
|
+
import { createRequire } from 'module';
|
|
15
|
+
|
|
16
|
+
const require = createRequire(import.meta.url);
|
|
17
|
+
|
|
18
|
+
const prefix = process.env.SSM_PARAMETER_PREFIX;
|
|
19
|
+
const rawKeys = process.env.FRIGG_SSM_OFFLOADED_KEYS;
|
|
20
|
+
|
|
21
|
+
if (prefix && rawKeys) {
|
|
22
|
+
const keys = rawKeys
|
|
23
|
+
.split(',')
|
|
24
|
+
.map((key) => key.trim())
|
|
25
|
+
.filter(Boolean);
|
|
26
|
+
|
|
27
|
+
if (keys.length > 0) {
|
|
28
|
+
const { fetchOffloadedParameters } = require('./parameters-to-env');
|
|
29
|
+
const values = await fetchOffloadedParameters(prefix, keys);
|
|
30
|
+
|
|
31
|
+
// Real env vars win over SSM (console-override debugging escape hatch).
|
|
32
|
+
for (const [key, value] of Object.entries(values)) {
|
|
33
|
+
if (process.env[key] === undefined) {
|
|
34
|
+
process.env[key] = value;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
console.log(`frigg-ssm-preload: loaded ${keys.length} parameter(s) at INIT under ${prefix}`);
|
|
38
|
+
}
|
|
39
|
+
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@friggframework/core",
|
|
3
3
|
"prettier": "@friggframework/prettier-config",
|
|
4
|
-
"version": "2.0.0--canary.625.
|
|
4
|
+
"version": "2.0.0--canary.625.8f60019.0",
|
|
5
5
|
"dependencies": {
|
|
6
6
|
"@aws-sdk/client-apigatewaymanagementapi": "^3.588.0",
|
|
7
7
|
"@aws-sdk/client-kms": "^3.588.0",
|
|
@@ -39,9 +39,9 @@
|
|
|
39
39
|
}
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
|
-
"@friggframework/eslint-config": "2.0.0--canary.625.
|
|
43
|
-
"@friggframework/prettier-config": "2.0.0--canary.625.
|
|
44
|
-
"@friggframework/test": "2.0.0--canary.625.
|
|
42
|
+
"@friggframework/eslint-config": "2.0.0--canary.625.8f60019.0",
|
|
43
|
+
"@friggframework/prettier-config": "2.0.0--canary.625.8f60019.0",
|
|
44
|
+
"@friggframework/test": "2.0.0--canary.625.8f60019.0",
|
|
45
45
|
"@prisma/client": "^6.19.3",
|
|
46
46
|
"@types/lodash": "4.17.15",
|
|
47
47
|
"@typescript-eslint/eslint-plugin": "^8.0.0",
|
|
@@ -81,5 +81,5 @@
|
|
|
81
81
|
"publishConfig": {
|
|
82
82
|
"access": "public"
|
|
83
83
|
},
|
|
84
|
-
"gitHead": "
|
|
84
|
+
"gitHead": "8f600193664963446e2c59c4b05ee076b0fbdc2a"
|
|
85
85
|
}
|