aws-secrets-manager-env-loader 0.1.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/LICENSE +21 -0
- package/README.md +143 -0
- package/dist/index.cjs +197 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +94 -0
- package/dist/index.d.ts +94 -0
- package/dist/index.js +156 -0
- package/dist/index.js.map +1 -0
- package/package.json +70 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 steel8rat
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
# aws-secrets-manager-env-loader
|
|
2
|
+
|
|
3
|
+
Load a map of AWS Secrets Manager secrets into `process.env` at boot.
|
|
4
|
+
|
|
5
|
+
- **Env wins.** An env var already set to a non-empty value is kept, not fetched.
|
|
6
|
+
Local dev supplies values via the environment and never calls AWS.
|
|
7
|
+
- **Fail fast.** Missing secrets are fetched in parallel; if any fetch or JSON
|
|
8
|
+
parse fails, the call rejects and `process.env` is left unmodified. No
|
|
9
|
+
partial-load mode.
|
|
10
|
+
- **JSON blobs.** A value is either a secret ID (whole `SecretString`) or
|
|
11
|
+
`{ secretId, key }` (one property of a JSON secret). Env vars sharing a
|
|
12
|
+
`secretId` fetch and parse it once.
|
|
13
|
+
- **Peer dependency.** `@aws-sdk/client-secrets-manager` is not bundled; you
|
|
14
|
+
choose and upgrade the version. Same convention as
|
|
15
|
+
[`@aws-lambda-powertools/parameters`][powertools].
|
|
16
|
+
|
|
17
|
+
[powertools]: https://www.npmjs.com/package/@aws-lambda-powertools/parameters
|
|
18
|
+
|
|
19
|
+
## Install
|
|
20
|
+
|
|
21
|
+
```sh
|
|
22
|
+
npm install aws-secrets-manager-env-loader @aws-sdk/client-secrets-manager
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
The peer dependency (`>=3.0.0`) is only loaded when a fetch actually happens, so
|
|
26
|
+
it can be skipped if every mapped var is always pre-set in the environment.
|
|
27
|
+
|
|
28
|
+
## Usage
|
|
29
|
+
|
|
30
|
+
```ts
|
|
31
|
+
import { loadSecrets } from "aws-secrets-manager-env-loader";
|
|
32
|
+
|
|
33
|
+
await loadSecrets({
|
|
34
|
+
secrets: {
|
|
35
|
+
CHAT_BOT_TOKEN: "example/internal/chat/bot-token/sample-service",
|
|
36
|
+
SERVICE_API_KEY: "example/internal/service/api-key/sample-service",
|
|
37
|
+
},
|
|
38
|
+
});
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Call it once and `await` it before the rest of the app reads those vars.
|
|
42
|
+
|
|
43
|
+
### Ordering
|
|
44
|
+
|
|
45
|
+
The loader reads `process.env` only — it does not parse `.env` files. For "env
|
|
46
|
+
wins" to apply locally, the values must be in `process.env` *before* the call.
|
|
47
|
+
Load them first:
|
|
48
|
+
|
|
49
|
+
```sh
|
|
50
|
+
node --env-file=.env dist/app.js
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
```ts
|
|
54
|
+
import "dotenv/config"; // before importing anything that calls loadSecrets
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Otherwise the var looks missing and the loader tries to fetch it (and throws
|
|
58
|
+
without credentials).
|
|
59
|
+
|
|
60
|
+
## API
|
|
61
|
+
|
|
62
|
+
```ts
|
|
63
|
+
function loadSecrets(options: LoadSecretsOptions): Promise<void>;
|
|
64
|
+
|
|
65
|
+
interface LoadSecretsOptions {
|
|
66
|
+
secrets: Record<string, SecretSource>;
|
|
67
|
+
/**
|
|
68
|
+
* Pre-constructed client, for control over region, credentials, retry, or SDK
|
|
69
|
+
* version. Default: constructed lazily on first fetch (region from
|
|
70
|
+
* AWS_REGION, else us-east-1; default credential chain).
|
|
71
|
+
*/
|
|
72
|
+
client?: SecretsManagerClient | SecretsManagerClientLike;
|
|
73
|
+
/** Progress messages. Default: console.log. Pass `() => {}` to silence. */
|
|
74
|
+
onLog?: (message: string) => void;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
type SecretSource =
|
|
78
|
+
| string // secret ID; whole SecretString
|
|
79
|
+
| { secretId: string; key?: string }; // one key of a JSON secret
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Supplying a client:
|
|
83
|
+
|
|
84
|
+
```ts
|
|
85
|
+
import { SecretsManagerClient } from "@aws-sdk/client-secrets-manager";
|
|
86
|
+
|
|
87
|
+
await loadSecrets({
|
|
88
|
+
secrets: { SERVICE_API_KEY: "example/internal/service/api-key/sample-service" },
|
|
89
|
+
client: new SecretsManagerClient({ region: "eu-west-1", maxAttempts: 5 }),
|
|
90
|
+
});
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### JSON-blob secrets
|
|
94
|
+
|
|
95
|
+
For a secret holding a JSON object (the console's key/value editor, RDS-managed
|
|
96
|
+
secrets), point multiple vars at keys of one `secretId`:
|
|
97
|
+
|
|
98
|
+
```ts
|
|
99
|
+
await loadSecrets({
|
|
100
|
+
secrets: {
|
|
101
|
+
DB_USERNAME: { secretId: "example/db/creds", key: "username" },
|
|
102
|
+
DB_PASSWORD: { secretId: "example/db/creds", key: "password" },
|
|
103
|
+
DB_PORT: { secretId: "example/db/creds", key: "port" }, // 5432 -> "5432"
|
|
104
|
+
SERVICE_API_KEY: "example/internal/service/api-key/sample-service",
|
|
105
|
+
},
|
|
106
|
+
});
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Non-string values are `JSON.stringify`-d before writing.
|
|
110
|
+
|
|
111
|
+
### Semantics
|
|
112
|
+
|
|
113
|
+
| Case | Behavior |
|
|
114
|
+
| --- | --- |
|
|
115
|
+
| Env var set to a non-empty value | Kept; not fetched or parsed |
|
|
116
|
+
| Env var unset or empty string | Fetched and written |
|
|
117
|
+
| Several env vars, one `secretId` | Fetched once, parsed once, in parallel with other secrets |
|
|
118
|
+
| Any fetch or JSON parse fails | Call rejects; `process.env` unmodified |
|
|
119
|
+
| Secret has no `SecretString` | `MissingSecretStringError` |
|
|
120
|
+
| Keyed source, key not in the JSON object | `SecretKeyNotFoundError` |
|
|
121
|
+
| Keyed source, `SecretString` not a JSON object | `SecretJsonParseError` |
|
|
122
|
+
| Default client needed, peer dep not installed | `SdkNotInstalledError` |
|
|
123
|
+
| Nothing missing | Returns immediately; SDK not loaded |
|
|
124
|
+
|
|
125
|
+
Exports: `loadSecrets`, `MissingSecretStringError`, `SecretJsonParseError`,
|
|
126
|
+
`SecretKeyNotFoundError`, `SdkNotInstalledError`, and the types
|
|
127
|
+
`LoadSecretsOptions`, `SecretSource`, `SecretsManagerClientLike`.
|
|
128
|
+
|
|
129
|
+
## Development
|
|
130
|
+
|
|
131
|
+
```sh
|
|
132
|
+
npm install
|
|
133
|
+
npm run typecheck
|
|
134
|
+
npm test # node --test
|
|
135
|
+
npm run build # tsup -> dist/ (ESM + CJS + .d.ts)
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
Tests use Node's runner with native type stripping (Node >=22.18 / >=23.6). The
|
|
139
|
+
published package is compiled JavaScript and requires Node >=18.
|
|
140
|
+
|
|
141
|
+
## License
|
|
142
|
+
|
|
143
|
+
MIT
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/index.ts
|
|
31
|
+
var index_exports = {};
|
|
32
|
+
__export(index_exports, {
|
|
33
|
+
MissingSecretStringError: () => MissingSecretStringError,
|
|
34
|
+
SdkNotInstalledError: () => SdkNotInstalledError,
|
|
35
|
+
SecretJsonParseError: () => SecretJsonParseError,
|
|
36
|
+
SecretKeyNotFoundError: () => SecretKeyNotFoundError,
|
|
37
|
+
loadSecrets: () => loadSecrets
|
|
38
|
+
});
|
|
39
|
+
module.exports = __toCommonJS(index_exports);
|
|
40
|
+
|
|
41
|
+
// src/sdk.ts
|
|
42
|
+
var SdkNotInstalledError = class extends Error {
|
|
43
|
+
constructor(cause) {
|
|
44
|
+
super(
|
|
45
|
+
`aws-secrets-manager-env-loader: could not load '@aws-sdk/client-secrets-manager'. It is a peer dependency - install it in your app to use the default client, or pass an explicit \`client\`. (If every mapped env var is already set, no fetch happens and the SDK is never needed.) Underlying error: ${cause instanceof Error ? cause.message : String(cause)}`,
|
|
46
|
+
{ cause }
|
|
47
|
+
);
|
|
48
|
+
this.name = "SdkNotInstalledError";
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
var realImport = () => import("@aws-sdk/client-secrets-manager");
|
|
52
|
+
var importSdk = realImport;
|
|
53
|
+
var sdkPromise;
|
|
54
|
+
var defaultClientPromise;
|
|
55
|
+
async function loadSdk() {
|
|
56
|
+
if (!sdkPromise) {
|
|
57
|
+
sdkPromise = importSdk().catch((err) => {
|
|
58
|
+
sdkPromise = void 0;
|
|
59
|
+
throw new SdkNotInstalledError(err);
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
return sdkPromise;
|
|
63
|
+
}
|
|
64
|
+
async function getDefaultClient() {
|
|
65
|
+
if (!defaultClientPromise) {
|
|
66
|
+
defaultClientPromise = (async () => {
|
|
67
|
+
const { SecretsManagerClient } = await loadSdk();
|
|
68
|
+
return new SecretsManagerClient({
|
|
69
|
+
region: process.env.AWS_REGION ?? "us-east-1"
|
|
70
|
+
});
|
|
71
|
+
})().catch((err) => {
|
|
72
|
+
defaultClientPromise = void 0;
|
|
73
|
+
throw err;
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
return defaultClientPromise;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// src/index.ts
|
|
80
|
+
var MissingSecretStringError = class extends Error {
|
|
81
|
+
secretId;
|
|
82
|
+
constructor(secretId) {
|
|
83
|
+
super(
|
|
84
|
+
`aws-secrets-manager-env-loader: secret "${secretId}" has no SecretString`
|
|
85
|
+
);
|
|
86
|
+
this.name = "MissingSecretStringError";
|
|
87
|
+
this.secretId = secretId;
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
var SecretJsonParseError = class extends Error {
|
|
91
|
+
secretId;
|
|
92
|
+
constructor(secretId, cause) {
|
|
93
|
+
super(
|
|
94
|
+
`aws-secrets-manager-env-loader: secret "${secretId}" is not a JSON object`,
|
|
95
|
+
{ cause }
|
|
96
|
+
);
|
|
97
|
+
this.name = "SecretJsonParseError";
|
|
98
|
+
this.secretId = secretId;
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
var SecretKeyNotFoundError = class extends Error {
|
|
102
|
+
secretId;
|
|
103
|
+
key;
|
|
104
|
+
envVar;
|
|
105
|
+
constructor(secretId, key, envVar) {
|
|
106
|
+
super(
|
|
107
|
+
`aws-secrets-manager-env-loader: key "${key}" (for env var ${envVar}) is not present in JSON secret "${secretId}"`
|
|
108
|
+
);
|
|
109
|
+
this.name = "SecretKeyNotFoundError";
|
|
110
|
+
this.secretId = secretId;
|
|
111
|
+
this.key = key;
|
|
112
|
+
this.envVar = envVar;
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
async function loadSecrets(options) {
|
|
116
|
+
const { secrets, onLog = defaultLog } = options;
|
|
117
|
+
const missing = Object.keys(secrets).filter(
|
|
118
|
+
(envVar) => !process.env[envVar]
|
|
119
|
+
);
|
|
120
|
+
if (missing.length === 0) {
|
|
121
|
+
onLog("[secrets] all secrets already in env, skipping fetch");
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
onLog(`[secrets] fetching from Secrets Manager: ${missing.join(", ")}`);
|
|
125
|
+
const { GetSecretValueCommand } = await loadSdk();
|
|
126
|
+
const client = options.client ?? await getDefaultClient();
|
|
127
|
+
const sources = new Map(
|
|
128
|
+
missing.map((envVar) => [envVar, normalize(secrets[envVar])])
|
|
129
|
+
);
|
|
130
|
+
const distinctIds = [
|
|
131
|
+
...new Set([...sources.values()].map((source) => source.secretId))
|
|
132
|
+
];
|
|
133
|
+
const rawById = /* @__PURE__ */ new Map();
|
|
134
|
+
await Promise.all(
|
|
135
|
+
distinctIds.map(async (secretId) => {
|
|
136
|
+
const res = await client.send(
|
|
137
|
+
new GetSecretValueCommand({ SecretId: secretId })
|
|
138
|
+
);
|
|
139
|
+
if (!res.SecretString) throw new MissingSecretStringError(secretId);
|
|
140
|
+
rawById.set(secretId, res.SecretString);
|
|
141
|
+
})
|
|
142
|
+
);
|
|
143
|
+
const jsonById = /* @__PURE__ */ new Map();
|
|
144
|
+
const parseJsonSecret = (secretId) => {
|
|
145
|
+
const cached = jsonById.get(secretId);
|
|
146
|
+
if (cached) return cached;
|
|
147
|
+
let value;
|
|
148
|
+
try {
|
|
149
|
+
value = JSON.parse(rawById.get(secretId));
|
|
150
|
+
} catch (err) {
|
|
151
|
+
throw new SecretJsonParseError(secretId, err);
|
|
152
|
+
}
|
|
153
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
154
|
+
throw new SecretJsonParseError(
|
|
155
|
+
secretId,
|
|
156
|
+
new Error("SecretString is not a JSON object")
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
const parsed = value;
|
|
160
|
+
jsonById.set(secretId, parsed);
|
|
161
|
+
return parsed;
|
|
162
|
+
};
|
|
163
|
+
const resolved = missing.map((envVar) => {
|
|
164
|
+
const { secretId, key } = sources.get(envVar);
|
|
165
|
+
if (key === void 0) {
|
|
166
|
+
return [envVar, rawById.get(secretId)];
|
|
167
|
+
}
|
|
168
|
+
const parsed = parseJsonSecret(secretId);
|
|
169
|
+
if (!(key in parsed)) {
|
|
170
|
+
throw new SecretKeyNotFoundError(secretId, key, envVar);
|
|
171
|
+
}
|
|
172
|
+
const value = parsed[key];
|
|
173
|
+
return [
|
|
174
|
+
envVar,
|
|
175
|
+
typeof value === "string" ? value : JSON.stringify(value)
|
|
176
|
+
];
|
|
177
|
+
});
|
|
178
|
+
for (const [envVar, value] of resolved) {
|
|
179
|
+
process.env[envVar] = value;
|
|
180
|
+
}
|
|
181
|
+
onLog(`[secrets] provisioned into env: ${missing.join(", ")}`);
|
|
182
|
+
}
|
|
183
|
+
function normalize(source) {
|
|
184
|
+
return typeof source === "string" ? { secretId: source } : source;
|
|
185
|
+
}
|
|
186
|
+
function defaultLog(message) {
|
|
187
|
+
console.log(message);
|
|
188
|
+
}
|
|
189
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
190
|
+
0 && (module.exports = {
|
|
191
|
+
MissingSecretStringError,
|
|
192
|
+
SdkNotInstalledError,
|
|
193
|
+
SecretJsonParseError,
|
|
194
|
+
SecretKeyNotFoundError,
|
|
195
|
+
loadSecrets
|
|
196
|
+
});
|
|
197
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/sdk.ts"],"sourcesContent":["import type { SecretsManagerClient } from \"@aws-sdk/client-secrets-manager\";\nimport { getDefaultClient, loadSdk, SdkNotInstalledError } from \"./sdk.ts\";\n\nexport { SdkNotInstalledError };\n\n/**\n * The minimal shape this package needs from a Secrets Manager client: a `send`\n * method that resolves a `GetSecretValueCommand` to something carrying a\n * `SecretString`. The real `SecretsManagerClient` satisfies this, and so does\n * any stub with a compatible `send` (handy for tests).\n */\nexport interface SecretsManagerClientLike {\n send(command: unknown): Promise<{ SecretString?: string }>;\n}\n\n/**\n * How to source one env var's value:\n *\n * - a plain **string** is a Secrets Manager secret ID whose entire\n * `SecretString` becomes the value;\n * - **`{ secretId, key }`** fetches `secretId`, parses its `SecretString` as a\n * JSON object, and writes property `key` (non-string values are\n * `JSON.stringify`-d). Several env vars may read different keys of the same\n * `secretId` - it is fetched and parsed exactly once.\n */\nexport type SecretSource =\n | string\n | {\n /** Secrets Manager secret ID. */\n secretId: string;\n /**\n * Property to read from the secret's JSON object. Omit to use the whole\n * `SecretString` verbatim.\n */\n key?: string;\n };\n\nexport interface LoadSecretsOptions {\n /**\n * Map of env var name -> where to get its value. See {@link SecretSource}.\n *\n * @example\n * {\n * // whole SecretString\n * SERVICE_API_KEY: \"example/internal/service/api-key/sample-service\",\n * // two keys out of one JSON secret, fetched once\n * DB_USERNAME: { secretId: \"example/db/creds\", key: \"username\" },\n * DB_PASSWORD: { secretId: \"example/db/creds\", key: \"password\" },\n * }\n */\n secrets: Record<string, SecretSource>;\n /**\n * Pre-constructed client. Supply this to control region, credentials,\n * retry/backoff, or the exact SDK version in play. If omitted, a\n * `SecretsManagerClient` is constructed lazily on first fetch (region from\n * `AWS_REGION`, else `us-east-1`; default credential chain).\n */\n client?: SecretsManagerClient | SecretsManagerClientLike;\n /**\n * Called with human-readable progress messages. Defaults to `console.log`.\n * Pass `() => {}` to silence, or route through your own logger.\n */\n onLog?: (message: string) => void;\n}\n\n/** Thrown when a fetched secret exists but carries no `SecretString`. */\nexport class MissingSecretStringError extends Error {\n readonly secretId: string;\n constructor(secretId: string) {\n super(\n `aws-secrets-manager-env-loader: secret \"${secretId}\" has no SecretString`,\n );\n this.name = \"MissingSecretStringError\";\n this.secretId = secretId;\n }\n}\n\n/** Thrown when a keyed source points at a secret whose `SecretString` is not a JSON object. */\nexport class SecretJsonParseError extends Error {\n readonly secretId: string;\n constructor(secretId: string, cause: unknown) {\n super(\n `aws-secrets-manager-env-loader: secret \"${secretId}\" is not a JSON object`,\n { cause },\n );\n this.name = \"SecretJsonParseError\";\n this.secretId = secretId;\n }\n}\n\n/** Thrown when a keyed source names a property missing from the secret's JSON object. */\nexport class SecretKeyNotFoundError extends Error {\n readonly secretId: string;\n readonly key: string;\n readonly envVar: string;\n constructor(secretId: string, key: string, envVar: string) {\n super(\n `aws-secrets-manager-env-loader: key \"${key}\" (for env var ${envVar}) is not present in JSON secret \"${secretId}\"`,\n );\n this.name = \"SecretKeyNotFoundError\";\n this.secretId = secretId;\n this.key = key;\n this.envVar = envVar;\n }\n}\n\n/**\n * Load the mapped secrets into `process.env`.\n *\n * - Env vars already set to a non-empty value are kept and never fetched. Only\n * `process.env` is consulted; `.env` files must be loaded beforehand.\n * - Each distinct secret ID is fetched once, in parallel, even when several env\n * vars read different keys from it.\n * - `Promise.all` semantics: any failed fetch or JSON parse rejects the whole\n * call and leaves `process.env` unmodified. No partial-load mode.\n */\nexport async function loadSecrets(options: LoadSecretsOptions): Promise<void> {\n const { secrets, onLog = defaultLog } = options;\n\n const missing = Object.keys(secrets).filter(\n (envVar) => !process.env[envVar],\n );\n if (missing.length === 0) {\n onLog(\"[secrets] all secrets already in env, skipping fetch\");\n return;\n }\n\n onLog(`[secrets] fetching from Secrets Manager: ${missing.join(\", \")}`);\n\n const { GetSecretValueCommand } = await loadSdk();\n const client = options.client ?? (await getDefaultClient());\n\n const sources = new Map(\n missing.map((envVar) => [envVar, normalize(secrets[envVar]!)] as const),\n );\n const distinctIds = [\n ...new Set([...sources.values()].map((source) => source.secretId)),\n ];\n\n const rawById = new Map<string, string>();\n await Promise.all(\n distinctIds.map(async (secretId) => {\n const res = await client.send(\n new GetSecretValueCommand({ SecretId: secretId }),\n );\n if (!res.SecretString) throw new MissingSecretStringError(secretId);\n rawById.set(secretId, res.SecretString);\n }),\n );\n\n const jsonById = new Map<string, Record<string, unknown>>();\n const parseJsonSecret = (secretId: string): Record<string, unknown> => {\n const cached = jsonById.get(secretId);\n if (cached) return cached;\n let value: unknown;\n try {\n value = JSON.parse(rawById.get(secretId)!);\n } catch (err) {\n throw new SecretJsonParseError(secretId, err);\n }\n if (value === null || typeof value !== \"object\" || Array.isArray(value)) {\n throw new SecretJsonParseError(\n secretId,\n new Error(\"SecretString is not a JSON object\"),\n );\n }\n const parsed = value as Record<string, unknown>;\n jsonById.set(secretId, parsed);\n return parsed;\n };\n\n const resolved = missing.map((envVar) => {\n const { secretId, key } = sources.get(envVar)!;\n if (key === undefined) {\n return [envVar, rawById.get(secretId)!] as const;\n }\n const parsed = parseJsonSecret(secretId);\n if (!(key in parsed)) {\n throw new SecretKeyNotFoundError(secretId, key, envVar);\n }\n const value = parsed[key];\n return [\n envVar,\n typeof value === \"string\" ? value : JSON.stringify(value),\n ] as const;\n });\n\n for (const [envVar, value] of resolved) {\n process.env[envVar] = value;\n }\n onLog(`[secrets] provisioned into env: ${missing.join(\", \")}`);\n}\n\nfunction normalize(source: SecretSource): { secretId: string; key?: string } {\n return typeof source === \"string\" ? { secretId: source } : source;\n}\n\nfunction defaultLog(message: string): void {\n console.log(message);\n}\n","import type { SecretsManagerClient } from \"@aws-sdk/client-secrets-manager\";\n\n/** Thrown when the AWS SDK client peer dependency cannot be resolved at runtime. */\nexport class SdkNotInstalledError extends Error {\n constructor(cause: unknown) {\n super(\n \"aws-secrets-manager-env-loader: could not load '@aws-sdk/client-secrets-manager'. \" +\n \"It is a peer dependency - install it in your app to use the default client, \" +\n \"or pass an explicit `client`. (If every mapped env var is already set, no fetch \" +\n \"happens and the SDK is never needed.) \" +\n `Underlying error: ${cause instanceof Error ? cause.message : String(cause)}`,\n { cause },\n );\n this.name = \"SdkNotInstalledError\";\n }\n}\n\nexport type SecretsManagerSdk = typeof import(\"@aws-sdk/client-secrets-manager\");\n\nconst realImport = (): Promise<SecretsManagerSdk> =>\n import(\"@aws-sdk/client-secrets-manager\");\n\nlet importSdk: () => Promise<SecretsManagerSdk> = realImport;\nlet sdkPromise: Promise<SecretsManagerSdk> | undefined;\nlet defaultClientPromise: Promise<SecretsManagerClient> | undefined;\n\n/**\n * Load `@aws-sdk/client-secrets-manager` lazily and only once. Kept out of\n * module scope so that importing this package never requires the peer\n * dependency - only an actual fetch does.\n */\nexport async function loadSdk(): Promise<SecretsManagerSdk> {\n if (!sdkPromise) {\n sdkPromise = importSdk().catch((err: unknown) => {\n sdkPromise = undefined;\n throw new SdkNotInstalledError(err);\n });\n }\n return sdkPromise;\n}\n\n/**\n * A `SecretsManagerClient` constructed lazily on first use, with its region from\n * `AWS_REGION` (falling back to `us-east-1`) and default-chain credentials.\n */\nexport async function getDefaultClient(): Promise<SecretsManagerClient> {\n if (!defaultClientPromise) {\n defaultClientPromise = (async () => {\n const { SecretsManagerClient } = await loadSdk();\n return new SecretsManagerClient({\n region: process.env.AWS_REGION ?? \"us-east-1\",\n });\n })().catch((err: unknown) => {\n defaultClientPromise = undefined;\n throw err;\n });\n }\n return defaultClientPromise;\n}\n\n/**\n * Test seam. Deliberately NOT re-exported from `index.ts`, so it never reaches\n * the published entry point or type definitions. Swaps how the AWS SDK is\n * imported and clears the memoized SDK + default client. Pass `undefined` to\n * restore the real dynamic import.\n *\n * @internal\n */\nexport function __setSdkImporterForTests(\n fn: (() => Promise<SecretsManagerSdk>) | undefined,\n): void {\n importSdk = fn ?? realImport;\n sdkPromise = undefined;\n defaultClientPromise = undefined;\n}\n"],"mappings":"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","names":[]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { SecretsManagerClient } from '@aws-sdk/client-secrets-manager';
|
|
2
|
+
|
|
3
|
+
/** Thrown when the AWS SDK client peer dependency cannot be resolved at runtime. */
|
|
4
|
+
declare class SdkNotInstalledError extends Error {
|
|
5
|
+
constructor(cause: unknown);
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* The minimal shape this package needs from a Secrets Manager client: a `send`
|
|
10
|
+
* method that resolves a `GetSecretValueCommand` to something carrying a
|
|
11
|
+
* `SecretString`. The real `SecretsManagerClient` satisfies this, and so does
|
|
12
|
+
* any stub with a compatible `send` (handy for tests).
|
|
13
|
+
*/
|
|
14
|
+
interface SecretsManagerClientLike {
|
|
15
|
+
send(command: unknown): Promise<{
|
|
16
|
+
SecretString?: string;
|
|
17
|
+
}>;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* How to source one env var's value:
|
|
21
|
+
*
|
|
22
|
+
* - a plain **string** is a Secrets Manager secret ID whose entire
|
|
23
|
+
* `SecretString` becomes the value;
|
|
24
|
+
* - **`{ secretId, key }`** fetches `secretId`, parses its `SecretString` as a
|
|
25
|
+
* JSON object, and writes property `key` (non-string values are
|
|
26
|
+
* `JSON.stringify`-d). Several env vars may read different keys of the same
|
|
27
|
+
* `secretId` - it is fetched and parsed exactly once.
|
|
28
|
+
*/
|
|
29
|
+
type SecretSource = string | {
|
|
30
|
+
/** Secrets Manager secret ID. */
|
|
31
|
+
secretId: string;
|
|
32
|
+
/**
|
|
33
|
+
* Property to read from the secret's JSON object. Omit to use the whole
|
|
34
|
+
* `SecretString` verbatim.
|
|
35
|
+
*/
|
|
36
|
+
key?: string;
|
|
37
|
+
};
|
|
38
|
+
interface LoadSecretsOptions {
|
|
39
|
+
/**
|
|
40
|
+
* Map of env var name -> where to get its value. See {@link SecretSource}.
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* {
|
|
44
|
+
* // whole SecretString
|
|
45
|
+
* SERVICE_API_KEY: "example/internal/service/api-key/sample-service",
|
|
46
|
+
* // two keys out of one JSON secret, fetched once
|
|
47
|
+
* DB_USERNAME: { secretId: "example/db/creds", key: "username" },
|
|
48
|
+
* DB_PASSWORD: { secretId: "example/db/creds", key: "password" },
|
|
49
|
+
* }
|
|
50
|
+
*/
|
|
51
|
+
secrets: Record<string, SecretSource>;
|
|
52
|
+
/**
|
|
53
|
+
* Pre-constructed client. Supply this to control region, credentials,
|
|
54
|
+
* retry/backoff, or the exact SDK version in play. If omitted, a
|
|
55
|
+
* `SecretsManagerClient` is constructed lazily on first fetch (region from
|
|
56
|
+
* `AWS_REGION`, else `us-east-1`; default credential chain).
|
|
57
|
+
*/
|
|
58
|
+
client?: SecretsManagerClient | SecretsManagerClientLike;
|
|
59
|
+
/**
|
|
60
|
+
* Called with human-readable progress messages. Defaults to `console.log`.
|
|
61
|
+
* Pass `() => {}` to silence, or route through your own logger.
|
|
62
|
+
*/
|
|
63
|
+
onLog?: (message: string) => void;
|
|
64
|
+
}
|
|
65
|
+
/** Thrown when a fetched secret exists but carries no `SecretString`. */
|
|
66
|
+
declare class MissingSecretStringError extends Error {
|
|
67
|
+
readonly secretId: string;
|
|
68
|
+
constructor(secretId: string);
|
|
69
|
+
}
|
|
70
|
+
/** Thrown when a keyed source points at a secret whose `SecretString` is not a JSON object. */
|
|
71
|
+
declare class SecretJsonParseError extends Error {
|
|
72
|
+
readonly secretId: string;
|
|
73
|
+
constructor(secretId: string, cause: unknown);
|
|
74
|
+
}
|
|
75
|
+
/** Thrown when a keyed source names a property missing from the secret's JSON object. */
|
|
76
|
+
declare class SecretKeyNotFoundError extends Error {
|
|
77
|
+
readonly secretId: string;
|
|
78
|
+
readonly key: string;
|
|
79
|
+
readonly envVar: string;
|
|
80
|
+
constructor(secretId: string, key: string, envVar: string);
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Load the mapped secrets into `process.env`.
|
|
84
|
+
*
|
|
85
|
+
* - Env vars already set to a non-empty value are kept and never fetched. Only
|
|
86
|
+
* `process.env` is consulted; `.env` files must be loaded beforehand.
|
|
87
|
+
* - Each distinct secret ID is fetched once, in parallel, even when several env
|
|
88
|
+
* vars read different keys from it.
|
|
89
|
+
* - `Promise.all` semantics: any failed fetch or JSON parse rejects the whole
|
|
90
|
+
* call and leaves `process.env` unmodified. No partial-load mode.
|
|
91
|
+
*/
|
|
92
|
+
declare function loadSecrets(options: LoadSecretsOptions): Promise<void>;
|
|
93
|
+
|
|
94
|
+
export { type LoadSecretsOptions, MissingSecretStringError, SdkNotInstalledError, SecretJsonParseError, SecretKeyNotFoundError, type SecretSource, type SecretsManagerClientLike, loadSecrets };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { SecretsManagerClient } from '@aws-sdk/client-secrets-manager';
|
|
2
|
+
|
|
3
|
+
/** Thrown when the AWS SDK client peer dependency cannot be resolved at runtime. */
|
|
4
|
+
declare class SdkNotInstalledError extends Error {
|
|
5
|
+
constructor(cause: unknown);
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* The minimal shape this package needs from a Secrets Manager client: a `send`
|
|
10
|
+
* method that resolves a `GetSecretValueCommand` to something carrying a
|
|
11
|
+
* `SecretString`. The real `SecretsManagerClient` satisfies this, and so does
|
|
12
|
+
* any stub with a compatible `send` (handy for tests).
|
|
13
|
+
*/
|
|
14
|
+
interface SecretsManagerClientLike {
|
|
15
|
+
send(command: unknown): Promise<{
|
|
16
|
+
SecretString?: string;
|
|
17
|
+
}>;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* How to source one env var's value:
|
|
21
|
+
*
|
|
22
|
+
* - a plain **string** is a Secrets Manager secret ID whose entire
|
|
23
|
+
* `SecretString` becomes the value;
|
|
24
|
+
* - **`{ secretId, key }`** fetches `secretId`, parses its `SecretString` as a
|
|
25
|
+
* JSON object, and writes property `key` (non-string values are
|
|
26
|
+
* `JSON.stringify`-d). Several env vars may read different keys of the same
|
|
27
|
+
* `secretId` - it is fetched and parsed exactly once.
|
|
28
|
+
*/
|
|
29
|
+
type SecretSource = string | {
|
|
30
|
+
/** Secrets Manager secret ID. */
|
|
31
|
+
secretId: string;
|
|
32
|
+
/**
|
|
33
|
+
* Property to read from the secret's JSON object. Omit to use the whole
|
|
34
|
+
* `SecretString` verbatim.
|
|
35
|
+
*/
|
|
36
|
+
key?: string;
|
|
37
|
+
};
|
|
38
|
+
interface LoadSecretsOptions {
|
|
39
|
+
/**
|
|
40
|
+
* Map of env var name -> where to get its value. See {@link SecretSource}.
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* {
|
|
44
|
+
* // whole SecretString
|
|
45
|
+
* SERVICE_API_KEY: "example/internal/service/api-key/sample-service",
|
|
46
|
+
* // two keys out of one JSON secret, fetched once
|
|
47
|
+
* DB_USERNAME: { secretId: "example/db/creds", key: "username" },
|
|
48
|
+
* DB_PASSWORD: { secretId: "example/db/creds", key: "password" },
|
|
49
|
+
* }
|
|
50
|
+
*/
|
|
51
|
+
secrets: Record<string, SecretSource>;
|
|
52
|
+
/**
|
|
53
|
+
* Pre-constructed client. Supply this to control region, credentials,
|
|
54
|
+
* retry/backoff, or the exact SDK version in play. If omitted, a
|
|
55
|
+
* `SecretsManagerClient` is constructed lazily on first fetch (region from
|
|
56
|
+
* `AWS_REGION`, else `us-east-1`; default credential chain).
|
|
57
|
+
*/
|
|
58
|
+
client?: SecretsManagerClient | SecretsManagerClientLike;
|
|
59
|
+
/**
|
|
60
|
+
* Called with human-readable progress messages. Defaults to `console.log`.
|
|
61
|
+
* Pass `() => {}` to silence, or route through your own logger.
|
|
62
|
+
*/
|
|
63
|
+
onLog?: (message: string) => void;
|
|
64
|
+
}
|
|
65
|
+
/** Thrown when a fetched secret exists but carries no `SecretString`. */
|
|
66
|
+
declare class MissingSecretStringError extends Error {
|
|
67
|
+
readonly secretId: string;
|
|
68
|
+
constructor(secretId: string);
|
|
69
|
+
}
|
|
70
|
+
/** Thrown when a keyed source points at a secret whose `SecretString` is not a JSON object. */
|
|
71
|
+
declare class SecretJsonParseError extends Error {
|
|
72
|
+
readonly secretId: string;
|
|
73
|
+
constructor(secretId: string, cause: unknown);
|
|
74
|
+
}
|
|
75
|
+
/** Thrown when a keyed source names a property missing from the secret's JSON object. */
|
|
76
|
+
declare class SecretKeyNotFoundError extends Error {
|
|
77
|
+
readonly secretId: string;
|
|
78
|
+
readonly key: string;
|
|
79
|
+
readonly envVar: string;
|
|
80
|
+
constructor(secretId: string, key: string, envVar: string);
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Load the mapped secrets into `process.env`.
|
|
84
|
+
*
|
|
85
|
+
* - Env vars already set to a non-empty value are kept and never fetched. Only
|
|
86
|
+
* `process.env` is consulted; `.env` files must be loaded beforehand.
|
|
87
|
+
* - Each distinct secret ID is fetched once, in parallel, even when several env
|
|
88
|
+
* vars read different keys from it.
|
|
89
|
+
* - `Promise.all` semantics: any failed fetch or JSON parse rejects the whole
|
|
90
|
+
* call and leaves `process.env` unmodified. No partial-load mode.
|
|
91
|
+
*/
|
|
92
|
+
declare function loadSecrets(options: LoadSecretsOptions): Promise<void>;
|
|
93
|
+
|
|
94
|
+
export { type LoadSecretsOptions, MissingSecretStringError, SdkNotInstalledError, SecretJsonParseError, SecretKeyNotFoundError, type SecretSource, type SecretsManagerClientLike, loadSecrets };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
// src/sdk.ts
|
|
2
|
+
var SdkNotInstalledError = class extends Error {
|
|
3
|
+
constructor(cause) {
|
|
4
|
+
super(
|
|
5
|
+
`aws-secrets-manager-env-loader: could not load '@aws-sdk/client-secrets-manager'. It is a peer dependency - install it in your app to use the default client, or pass an explicit \`client\`. (If every mapped env var is already set, no fetch happens and the SDK is never needed.) Underlying error: ${cause instanceof Error ? cause.message : String(cause)}`,
|
|
6
|
+
{ cause }
|
|
7
|
+
);
|
|
8
|
+
this.name = "SdkNotInstalledError";
|
|
9
|
+
}
|
|
10
|
+
};
|
|
11
|
+
var realImport = () => import("@aws-sdk/client-secrets-manager");
|
|
12
|
+
var importSdk = realImport;
|
|
13
|
+
var sdkPromise;
|
|
14
|
+
var defaultClientPromise;
|
|
15
|
+
async function loadSdk() {
|
|
16
|
+
if (!sdkPromise) {
|
|
17
|
+
sdkPromise = importSdk().catch((err) => {
|
|
18
|
+
sdkPromise = void 0;
|
|
19
|
+
throw new SdkNotInstalledError(err);
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
return sdkPromise;
|
|
23
|
+
}
|
|
24
|
+
async function getDefaultClient() {
|
|
25
|
+
if (!defaultClientPromise) {
|
|
26
|
+
defaultClientPromise = (async () => {
|
|
27
|
+
const { SecretsManagerClient } = await loadSdk();
|
|
28
|
+
return new SecretsManagerClient({
|
|
29
|
+
region: process.env.AWS_REGION ?? "us-east-1"
|
|
30
|
+
});
|
|
31
|
+
})().catch((err) => {
|
|
32
|
+
defaultClientPromise = void 0;
|
|
33
|
+
throw err;
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
return defaultClientPromise;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// src/index.ts
|
|
40
|
+
var MissingSecretStringError = class extends Error {
|
|
41
|
+
secretId;
|
|
42
|
+
constructor(secretId) {
|
|
43
|
+
super(
|
|
44
|
+
`aws-secrets-manager-env-loader: secret "${secretId}" has no SecretString`
|
|
45
|
+
);
|
|
46
|
+
this.name = "MissingSecretStringError";
|
|
47
|
+
this.secretId = secretId;
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
var SecretJsonParseError = class extends Error {
|
|
51
|
+
secretId;
|
|
52
|
+
constructor(secretId, cause) {
|
|
53
|
+
super(
|
|
54
|
+
`aws-secrets-manager-env-loader: secret "${secretId}" is not a JSON object`,
|
|
55
|
+
{ cause }
|
|
56
|
+
);
|
|
57
|
+
this.name = "SecretJsonParseError";
|
|
58
|
+
this.secretId = secretId;
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
var SecretKeyNotFoundError = class extends Error {
|
|
62
|
+
secretId;
|
|
63
|
+
key;
|
|
64
|
+
envVar;
|
|
65
|
+
constructor(secretId, key, envVar) {
|
|
66
|
+
super(
|
|
67
|
+
`aws-secrets-manager-env-loader: key "${key}" (for env var ${envVar}) is not present in JSON secret "${secretId}"`
|
|
68
|
+
);
|
|
69
|
+
this.name = "SecretKeyNotFoundError";
|
|
70
|
+
this.secretId = secretId;
|
|
71
|
+
this.key = key;
|
|
72
|
+
this.envVar = envVar;
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
async function loadSecrets(options) {
|
|
76
|
+
const { secrets, onLog = defaultLog } = options;
|
|
77
|
+
const missing = Object.keys(secrets).filter(
|
|
78
|
+
(envVar) => !process.env[envVar]
|
|
79
|
+
);
|
|
80
|
+
if (missing.length === 0) {
|
|
81
|
+
onLog("[secrets] all secrets already in env, skipping fetch");
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
onLog(`[secrets] fetching from Secrets Manager: ${missing.join(", ")}`);
|
|
85
|
+
const { GetSecretValueCommand } = await loadSdk();
|
|
86
|
+
const client = options.client ?? await getDefaultClient();
|
|
87
|
+
const sources = new Map(
|
|
88
|
+
missing.map((envVar) => [envVar, normalize(secrets[envVar])])
|
|
89
|
+
);
|
|
90
|
+
const distinctIds = [
|
|
91
|
+
...new Set([...sources.values()].map((source) => source.secretId))
|
|
92
|
+
];
|
|
93
|
+
const rawById = /* @__PURE__ */ new Map();
|
|
94
|
+
await Promise.all(
|
|
95
|
+
distinctIds.map(async (secretId) => {
|
|
96
|
+
const res = await client.send(
|
|
97
|
+
new GetSecretValueCommand({ SecretId: secretId })
|
|
98
|
+
);
|
|
99
|
+
if (!res.SecretString) throw new MissingSecretStringError(secretId);
|
|
100
|
+
rawById.set(secretId, res.SecretString);
|
|
101
|
+
})
|
|
102
|
+
);
|
|
103
|
+
const jsonById = /* @__PURE__ */ new Map();
|
|
104
|
+
const parseJsonSecret = (secretId) => {
|
|
105
|
+
const cached = jsonById.get(secretId);
|
|
106
|
+
if (cached) return cached;
|
|
107
|
+
let value;
|
|
108
|
+
try {
|
|
109
|
+
value = JSON.parse(rawById.get(secretId));
|
|
110
|
+
} catch (err) {
|
|
111
|
+
throw new SecretJsonParseError(secretId, err);
|
|
112
|
+
}
|
|
113
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
114
|
+
throw new SecretJsonParseError(
|
|
115
|
+
secretId,
|
|
116
|
+
new Error("SecretString is not a JSON object")
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
const parsed = value;
|
|
120
|
+
jsonById.set(secretId, parsed);
|
|
121
|
+
return parsed;
|
|
122
|
+
};
|
|
123
|
+
const resolved = missing.map((envVar) => {
|
|
124
|
+
const { secretId, key } = sources.get(envVar);
|
|
125
|
+
if (key === void 0) {
|
|
126
|
+
return [envVar, rawById.get(secretId)];
|
|
127
|
+
}
|
|
128
|
+
const parsed = parseJsonSecret(secretId);
|
|
129
|
+
if (!(key in parsed)) {
|
|
130
|
+
throw new SecretKeyNotFoundError(secretId, key, envVar);
|
|
131
|
+
}
|
|
132
|
+
const value = parsed[key];
|
|
133
|
+
return [
|
|
134
|
+
envVar,
|
|
135
|
+
typeof value === "string" ? value : JSON.stringify(value)
|
|
136
|
+
];
|
|
137
|
+
});
|
|
138
|
+
for (const [envVar, value] of resolved) {
|
|
139
|
+
process.env[envVar] = value;
|
|
140
|
+
}
|
|
141
|
+
onLog(`[secrets] provisioned into env: ${missing.join(", ")}`);
|
|
142
|
+
}
|
|
143
|
+
function normalize(source) {
|
|
144
|
+
return typeof source === "string" ? { secretId: source } : source;
|
|
145
|
+
}
|
|
146
|
+
function defaultLog(message) {
|
|
147
|
+
console.log(message);
|
|
148
|
+
}
|
|
149
|
+
export {
|
|
150
|
+
MissingSecretStringError,
|
|
151
|
+
SdkNotInstalledError,
|
|
152
|
+
SecretJsonParseError,
|
|
153
|
+
SecretKeyNotFoundError,
|
|
154
|
+
loadSecrets
|
|
155
|
+
};
|
|
156
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/sdk.ts","../src/index.ts"],"sourcesContent":["import type { SecretsManagerClient } from \"@aws-sdk/client-secrets-manager\";\n\n/** Thrown when the AWS SDK client peer dependency cannot be resolved at runtime. */\nexport class SdkNotInstalledError extends Error {\n constructor(cause: unknown) {\n super(\n \"aws-secrets-manager-env-loader: could not load '@aws-sdk/client-secrets-manager'. \" +\n \"It is a peer dependency - install it in your app to use the default client, \" +\n \"or pass an explicit `client`. (If every mapped env var is already set, no fetch \" +\n \"happens and the SDK is never needed.) \" +\n `Underlying error: ${cause instanceof Error ? cause.message : String(cause)}`,\n { cause },\n );\n this.name = \"SdkNotInstalledError\";\n }\n}\n\nexport type SecretsManagerSdk = typeof import(\"@aws-sdk/client-secrets-manager\");\n\nconst realImport = (): Promise<SecretsManagerSdk> =>\n import(\"@aws-sdk/client-secrets-manager\");\n\nlet importSdk: () => Promise<SecretsManagerSdk> = realImport;\nlet sdkPromise: Promise<SecretsManagerSdk> | undefined;\nlet defaultClientPromise: Promise<SecretsManagerClient> | undefined;\n\n/**\n * Load `@aws-sdk/client-secrets-manager` lazily and only once. Kept out of\n * module scope so that importing this package never requires the peer\n * dependency - only an actual fetch does.\n */\nexport async function loadSdk(): Promise<SecretsManagerSdk> {\n if (!sdkPromise) {\n sdkPromise = importSdk().catch((err: unknown) => {\n sdkPromise = undefined;\n throw new SdkNotInstalledError(err);\n });\n }\n return sdkPromise;\n}\n\n/**\n * A `SecretsManagerClient` constructed lazily on first use, with its region from\n * `AWS_REGION` (falling back to `us-east-1`) and default-chain credentials.\n */\nexport async function getDefaultClient(): Promise<SecretsManagerClient> {\n if (!defaultClientPromise) {\n defaultClientPromise = (async () => {\n const { SecretsManagerClient } = await loadSdk();\n return new SecretsManagerClient({\n region: process.env.AWS_REGION ?? \"us-east-1\",\n });\n })().catch((err: unknown) => {\n defaultClientPromise = undefined;\n throw err;\n });\n }\n return defaultClientPromise;\n}\n\n/**\n * Test seam. Deliberately NOT re-exported from `index.ts`, so it never reaches\n * the published entry point or type definitions. Swaps how the AWS SDK is\n * imported and clears the memoized SDK + default client. Pass `undefined` to\n * restore the real dynamic import.\n *\n * @internal\n */\nexport function __setSdkImporterForTests(\n fn: (() => Promise<SecretsManagerSdk>) | undefined,\n): void {\n importSdk = fn ?? realImport;\n sdkPromise = undefined;\n defaultClientPromise = undefined;\n}\n","import type { SecretsManagerClient } from \"@aws-sdk/client-secrets-manager\";\nimport { getDefaultClient, loadSdk, SdkNotInstalledError } from \"./sdk.ts\";\n\nexport { SdkNotInstalledError };\n\n/**\n * The minimal shape this package needs from a Secrets Manager client: a `send`\n * method that resolves a `GetSecretValueCommand` to something carrying a\n * `SecretString`. The real `SecretsManagerClient` satisfies this, and so does\n * any stub with a compatible `send` (handy for tests).\n */\nexport interface SecretsManagerClientLike {\n send(command: unknown): Promise<{ SecretString?: string }>;\n}\n\n/**\n * How to source one env var's value:\n *\n * - a plain **string** is a Secrets Manager secret ID whose entire\n * `SecretString` becomes the value;\n * - **`{ secretId, key }`** fetches `secretId`, parses its `SecretString` as a\n * JSON object, and writes property `key` (non-string values are\n * `JSON.stringify`-d). Several env vars may read different keys of the same\n * `secretId` - it is fetched and parsed exactly once.\n */\nexport type SecretSource =\n | string\n | {\n /** Secrets Manager secret ID. */\n secretId: string;\n /**\n * Property to read from the secret's JSON object. Omit to use the whole\n * `SecretString` verbatim.\n */\n key?: string;\n };\n\nexport interface LoadSecretsOptions {\n /**\n * Map of env var name -> where to get its value. See {@link SecretSource}.\n *\n * @example\n * {\n * // whole SecretString\n * SERVICE_API_KEY: \"example/internal/service/api-key/sample-service\",\n * // two keys out of one JSON secret, fetched once\n * DB_USERNAME: { secretId: \"example/db/creds\", key: \"username\" },\n * DB_PASSWORD: { secretId: \"example/db/creds\", key: \"password\" },\n * }\n */\n secrets: Record<string, SecretSource>;\n /**\n * Pre-constructed client. Supply this to control region, credentials,\n * retry/backoff, or the exact SDK version in play. If omitted, a\n * `SecretsManagerClient` is constructed lazily on first fetch (region from\n * `AWS_REGION`, else `us-east-1`; default credential chain).\n */\n client?: SecretsManagerClient | SecretsManagerClientLike;\n /**\n * Called with human-readable progress messages. Defaults to `console.log`.\n * Pass `() => {}` to silence, or route through your own logger.\n */\n onLog?: (message: string) => void;\n}\n\n/** Thrown when a fetched secret exists but carries no `SecretString`. */\nexport class MissingSecretStringError extends Error {\n readonly secretId: string;\n constructor(secretId: string) {\n super(\n `aws-secrets-manager-env-loader: secret \"${secretId}\" has no SecretString`,\n );\n this.name = \"MissingSecretStringError\";\n this.secretId = secretId;\n }\n}\n\n/** Thrown when a keyed source points at a secret whose `SecretString` is not a JSON object. */\nexport class SecretJsonParseError extends Error {\n readonly secretId: string;\n constructor(secretId: string, cause: unknown) {\n super(\n `aws-secrets-manager-env-loader: secret \"${secretId}\" is not a JSON object`,\n { cause },\n );\n this.name = \"SecretJsonParseError\";\n this.secretId = secretId;\n }\n}\n\n/** Thrown when a keyed source names a property missing from the secret's JSON object. */\nexport class SecretKeyNotFoundError extends Error {\n readonly secretId: string;\n readonly key: string;\n readonly envVar: string;\n constructor(secretId: string, key: string, envVar: string) {\n super(\n `aws-secrets-manager-env-loader: key \"${key}\" (for env var ${envVar}) is not present in JSON secret \"${secretId}\"`,\n );\n this.name = \"SecretKeyNotFoundError\";\n this.secretId = secretId;\n this.key = key;\n this.envVar = envVar;\n }\n}\n\n/**\n * Load the mapped secrets into `process.env`.\n *\n * - Env vars already set to a non-empty value are kept and never fetched. Only\n * `process.env` is consulted; `.env` files must be loaded beforehand.\n * - Each distinct secret ID is fetched once, in parallel, even when several env\n * vars read different keys from it.\n * - `Promise.all` semantics: any failed fetch or JSON parse rejects the whole\n * call and leaves `process.env` unmodified. No partial-load mode.\n */\nexport async function loadSecrets(options: LoadSecretsOptions): Promise<void> {\n const { secrets, onLog = defaultLog } = options;\n\n const missing = Object.keys(secrets).filter(\n (envVar) => !process.env[envVar],\n );\n if (missing.length === 0) {\n onLog(\"[secrets] all secrets already in env, skipping fetch\");\n return;\n }\n\n onLog(`[secrets] fetching from Secrets Manager: ${missing.join(\", \")}`);\n\n const { GetSecretValueCommand } = await loadSdk();\n const client = options.client ?? (await getDefaultClient());\n\n const sources = new Map(\n missing.map((envVar) => [envVar, normalize(secrets[envVar]!)] as const),\n );\n const distinctIds = [\n ...new Set([...sources.values()].map((source) => source.secretId)),\n ];\n\n const rawById = new Map<string, string>();\n await Promise.all(\n distinctIds.map(async (secretId) => {\n const res = await client.send(\n new GetSecretValueCommand({ SecretId: secretId }),\n );\n if (!res.SecretString) throw new MissingSecretStringError(secretId);\n rawById.set(secretId, res.SecretString);\n }),\n );\n\n const jsonById = new Map<string, Record<string, unknown>>();\n const parseJsonSecret = (secretId: string): Record<string, unknown> => {\n const cached = jsonById.get(secretId);\n if (cached) return cached;\n let value: unknown;\n try {\n value = JSON.parse(rawById.get(secretId)!);\n } catch (err) {\n throw new SecretJsonParseError(secretId, err);\n }\n if (value === null || typeof value !== \"object\" || Array.isArray(value)) {\n throw new SecretJsonParseError(\n secretId,\n new Error(\"SecretString is not a JSON object\"),\n );\n }\n const parsed = value as Record<string, unknown>;\n jsonById.set(secretId, parsed);\n return parsed;\n };\n\n const resolved = missing.map((envVar) => {\n const { secretId, key } = sources.get(envVar)!;\n if (key === undefined) {\n return [envVar, rawById.get(secretId)!] as const;\n }\n const parsed = parseJsonSecret(secretId);\n if (!(key in parsed)) {\n throw new SecretKeyNotFoundError(secretId, key, envVar);\n }\n const value = parsed[key];\n return [\n envVar,\n typeof value === \"string\" ? value : JSON.stringify(value),\n ] as const;\n });\n\n for (const [envVar, value] of resolved) {\n process.env[envVar] = value;\n }\n onLog(`[secrets] provisioned into env: ${missing.join(\", \")}`);\n}\n\nfunction normalize(source: SecretSource): { secretId: string; key?: string } {\n return typeof source === \"string\" ? { secretId: source } : source;\n}\n\nfunction defaultLog(message: string): void {\n console.log(message);\n}\n"],"mappings":"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","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "aws-secrets-manager-env-loader",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Load a map of AWS Secrets Manager secrets into process.env at boot. Env vars already set win over the fetch, missing ones are fetched in parallel, and it fails fast. The AWS SDK client is a peer dependency.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"aws",
|
|
7
|
+
"secrets-manager",
|
|
8
|
+
"secrets",
|
|
9
|
+
"env",
|
|
10
|
+
"environment-variables",
|
|
11
|
+
"process.env",
|
|
12
|
+
"dotenv",
|
|
13
|
+
"bootstrap",
|
|
14
|
+
"config",
|
|
15
|
+
"12-factor"
|
|
16
|
+
],
|
|
17
|
+
"license": "MIT",
|
|
18
|
+
"author": "steel8rat",
|
|
19
|
+
"repository": {
|
|
20
|
+
"type": "git",
|
|
21
|
+
"url": "git+https://github.com/steel8rat/aws-secrets-manager-env-loader.git"
|
|
22
|
+
},
|
|
23
|
+
"homepage": "https://github.com/steel8rat/aws-secrets-manager-env-loader#readme",
|
|
24
|
+
"bugs": {
|
|
25
|
+
"url": "https://github.com/steel8rat/aws-secrets-manager-env-loader/issues"
|
|
26
|
+
},
|
|
27
|
+
"type": "module",
|
|
28
|
+
"main": "./dist/index.cjs",
|
|
29
|
+
"module": "./dist/index.js",
|
|
30
|
+
"types": "./dist/index.d.ts",
|
|
31
|
+
"exports": {
|
|
32
|
+
".": {
|
|
33
|
+
"import": {
|
|
34
|
+
"types": "./dist/index.d.ts",
|
|
35
|
+
"default": "./dist/index.js"
|
|
36
|
+
},
|
|
37
|
+
"require": {
|
|
38
|
+
"types": "./dist/index.d.cts",
|
|
39
|
+
"default": "./dist/index.cjs"
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
"files": [
|
|
44
|
+
"dist",
|
|
45
|
+
"README.md",
|
|
46
|
+
"LICENSE"
|
|
47
|
+
],
|
|
48
|
+
"engines": {
|
|
49
|
+
"node": ">=18"
|
|
50
|
+
},
|
|
51
|
+
"scripts": {
|
|
52
|
+
"build": "tsup",
|
|
53
|
+
"typecheck": "tsc --noEmit",
|
|
54
|
+
"test": "node --test",
|
|
55
|
+
"test:watch": "node --test --watch",
|
|
56
|
+
"prepublishOnly": "npm run typecheck && npm run test && npm run build"
|
|
57
|
+
},
|
|
58
|
+
"peerDependencies": {
|
|
59
|
+
"@aws-sdk/client-secrets-manager": ">=3.0.0"
|
|
60
|
+
},
|
|
61
|
+
"overrides": {
|
|
62
|
+
"esbuild": "^0.28.2"
|
|
63
|
+
},
|
|
64
|
+
"devDependencies": {
|
|
65
|
+
"@aws-sdk/client-secrets-manager": "^3.699.0",
|
|
66
|
+
"@types/node": "^22.20.1",
|
|
67
|
+
"tsup": "^8.3.5",
|
|
68
|
+
"typescript": "^5.7.2"
|
|
69
|
+
}
|
|
70
|
+
}
|