@secrefs/node 0.1.0 → 0.2.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/README.md +1 -1
- package/dist/index.cjs +331 -10
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +178 -1
- package/dist/index.d.ts +178 -1
- package/dist/index.js +326 -10
- package/dist/index.js.map +1 -1
- package/dist/secrefs.cjs +361 -18
- package/dist/secrefs.cjs.map +1 -1
- package/package.json +5 -4
package/README.md
CHANGED
|
@@ -121,4 +121,4 @@ MIT. See [LICENSE](./LICENSE).
|
|
|
121
121
|
|
|
122
122
|
The client libraries are MIT permanently and unconditionally — the SecRefs
|
|
123
123
|
control plane is licensed separately. See
|
|
124
|
-
[LICENSING.md](https://github.com/
|
|
124
|
+
[LICENSING.md](https://github.com/secrefs/secrefs/blob/main/LICENSING.md).
|
package/dist/index.cjs
CHANGED
|
@@ -33,6 +33,8 @@ __export(src_exports, {
|
|
|
33
33
|
AwsSecretsManagerProvider: () => AwsSecretsManagerProvider,
|
|
34
34
|
BaseSecretProvider: () => BaseSecretProvider,
|
|
35
35
|
BitwardenProvider: () => BitwardenProvider,
|
|
36
|
+
CONFIG_FILENAME: () => CONFIG_FILENAME,
|
|
37
|
+
ConfigError: () => ConfigError,
|
|
36
38
|
ControlPlaneClient: () => ControlPlaneClient,
|
|
37
39
|
ControlPlaneRequestError: () => ControlPlaneRequestError,
|
|
38
40
|
LocalProvider: () => LocalProvider,
|
|
@@ -42,12 +44,15 @@ __export(src_exports, {
|
|
|
42
44
|
SecretFetchError: () => SecretFetchError,
|
|
43
45
|
TtlCache: () => TtlCache,
|
|
44
46
|
VaultProvider: () => VaultProvider,
|
|
47
|
+
buildProviders: () => buildProviders,
|
|
45
48
|
checkReferences: () => checkReferences,
|
|
46
49
|
createDefaultProviders: () => createDefaultProviders,
|
|
47
50
|
expandKeyValueMap: () => expandKeyValueMap,
|
|
48
51
|
expandProcessEnv: () => expandProcessEnv,
|
|
49
52
|
extractField: () => extractField,
|
|
50
53
|
isSecretRef: () => isSecretRef,
|
|
54
|
+
loadConfigFrom: () => loadConfigFrom,
|
|
55
|
+
parseConfig: () => parseConfig,
|
|
51
56
|
parseEnvFileText: () => parseEnvFileText,
|
|
52
57
|
parseSecretRef: () => parseSecretRef,
|
|
53
58
|
recoverTruncatedSecRefs: () => recoverTruncatedSecRefs,
|
|
@@ -59,16 +64,143 @@ module.exports = __toCommonJS(src_exports);
|
|
|
59
64
|
// src/providers/aws.ts
|
|
60
65
|
var import_client_secrets_manager = require("@aws-sdk/client-secrets-manager");
|
|
61
66
|
|
|
67
|
+
// src/providers/errors.ts
|
|
68
|
+
var AUTH_NAMES = /* @__PURE__ */ new Set([
|
|
69
|
+
"CredentialsProviderError",
|
|
70
|
+
"TokenProviderError",
|
|
71
|
+
"ExpiredToken",
|
|
72
|
+
"ExpiredTokenException",
|
|
73
|
+
"InvalidClientTokenId",
|
|
74
|
+
"UnrecognizedClientException",
|
|
75
|
+
"InvalidIdentityToken",
|
|
76
|
+
"AuthFailure",
|
|
77
|
+
"SSOTokenProviderFailure"
|
|
78
|
+
]);
|
|
79
|
+
var NOT_FOUND_NAMES = /* @__PURE__ */ new Set([
|
|
80
|
+
"ResourceNotFoundException",
|
|
81
|
+
"NoSuchEntity",
|
|
82
|
+
"SecretNotFound"
|
|
83
|
+
]);
|
|
84
|
+
var DENIED_NAMES = /* @__PURE__ */ new Set([
|
|
85
|
+
"AccessDeniedException",
|
|
86
|
+
"AccessDenied",
|
|
87
|
+
"AuthorizationError",
|
|
88
|
+
"UnauthorizedOperation"
|
|
89
|
+
]);
|
|
90
|
+
var TRANSIENT_NAMES = /* @__PURE__ */ new Set([
|
|
91
|
+
"TimeoutError",
|
|
92
|
+
"NetworkingError",
|
|
93
|
+
"RequestTimeout",
|
|
94
|
+
"RequestTimeoutException",
|
|
95
|
+
"ThrottlingException",
|
|
96
|
+
"TooManyRequestsException",
|
|
97
|
+
"InternalServiceError",
|
|
98
|
+
"InternalServerError",
|
|
99
|
+
"ServiceUnavailable",
|
|
100
|
+
"ServiceUnavailableException",
|
|
101
|
+
"AbortError",
|
|
102
|
+
"ECONNRESET",
|
|
103
|
+
"ECONNREFUSED",
|
|
104
|
+
"ETIMEDOUT",
|
|
105
|
+
"EAI_AGAIN"
|
|
106
|
+
]);
|
|
107
|
+
var AUTH_FRAGMENTS = [
|
|
108
|
+
"could not load credentials",
|
|
109
|
+
"sso session associated with this profile has expired",
|
|
110
|
+
"security token included in the request is expired",
|
|
111
|
+
"unable to locate credentials",
|
|
112
|
+
"token is expired",
|
|
113
|
+
"credentials have expired",
|
|
114
|
+
"is expired"
|
|
115
|
+
];
|
|
116
|
+
var TRANSIENT_FRAGMENTS = [
|
|
117
|
+
"socket hang up",
|
|
118
|
+
"network error",
|
|
119
|
+
"timed out",
|
|
120
|
+
"timeout",
|
|
121
|
+
"econnreset",
|
|
122
|
+
"econnrefused",
|
|
123
|
+
"getaddrinfo"
|
|
124
|
+
];
|
|
125
|
+
function nameOf(err) {
|
|
126
|
+
if (typeof err !== "object" || err === null) return "";
|
|
127
|
+
const e = err;
|
|
128
|
+
for (const candidate of [e.name, e.code, e.__type]) {
|
|
129
|
+
if (typeof candidate === "string" && candidate) return candidate;
|
|
130
|
+
}
|
|
131
|
+
return "";
|
|
132
|
+
}
|
|
133
|
+
function statusOf(err) {
|
|
134
|
+
if (typeof err !== "object" || err === null) return void 0;
|
|
135
|
+
const meta = err.$metadata;
|
|
136
|
+
if (typeof meta?.httpStatusCode === "number") return meta.httpStatusCode;
|
|
137
|
+
const status = err.status ?? err.statusCode;
|
|
138
|
+
return typeof status === "number" ? status : void 0;
|
|
139
|
+
}
|
|
140
|
+
function classifyError(err) {
|
|
141
|
+
const name = nameOf(err);
|
|
142
|
+
if (AUTH_NAMES.has(name)) return "auth";
|
|
143
|
+
if (NOT_FOUND_NAMES.has(name)) return "not_found";
|
|
144
|
+
if (DENIED_NAMES.has(name)) return "denied";
|
|
145
|
+
if (TRANSIENT_NAMES.has(name)) return "transient";
|
|
146
|
+
const status = statusOf(err);
|
|
147
|
+
if (status === 401) return "auth";
|
|
148
|
+
if (status === 403) return "denied";
|
|
149
|
+
if (status === 404) return "not_found";
|
|
150
|
+
if (status === 408 || status === 429) return "transient";
|
|
151
|
+
if (status !== void 0 && status >= 500) return "transient";
|
|
152
|
+
const message = (err instanceof Error ? err.message : String(err ?? "")).toLowerCase();
|
|
153
|
+
if (AUTH_FRAGMENTS.some((f) => message.includes(f))) return "auth";
|
|
154
|
+
if (TRANSIENT_FRAGMENTS.some((f) => message.includes(f))) return "transient";
|
|
155
|
+
return "unknown";
|
|
156
|
+
}
|
|
157
|
+
function isStaleServable(kind) {
|
|
158
|
+
return kind === "transient";
|
|
159
|
+
}
|
|
160
|
+
function remedyFor(kind, provider, err) {
|
|
161
|
+
if (kind !== "auth") return void 0;
|
|
162
|
+
const message = err instanceof Error ? err.message : "";
|
|
163
|
+
if (/sso session/i.test(message)) {
|
|
164
|
+
const profile = process.env.AWS_PROFILE;
|
|
165
|
+
return profile ? `Run: aws sso login --profile ${profile}` : "Run: aws sso login --profile <your-profile>";
|
|
166
|
+
}
|
|
167
|
+
switch (provider) {
|
|
168
|
+
case "aws": {
|
|
169
|
+
const profile = process.env.AWS_PROFILE;
|
|
170
|
+
return profile ? `Check credentials for AWS profile "${profile}" - if it uses SSO, run: aws sso login --profile ${profile}` : "No AWS credentials found. Set AWS_PROFILE, export static keys, or attach an instance role.";
|
|
171
|
+
}
|
|
172
|
+
case "bitwarden":
|
|
173
|
+
return "Set BWS_ACCESS_TOKEN to a valid Bitwarden machine account token.";
|
|
174
|
+
case "vault":
|
|
175
|
+
return "Set VAULT_ADDR and VAULT_TOKEN, or renew the token if it has expired.";
|
|
176
|
+
default:
|
|
177
|
+
return void 0;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
62
181
|
// src/providers/base.ts
|
|
63
182
|
var SecretFetchError = class extends Error {
|
|
64
183
|
constructor(provider, path2, cause) {
|
|
65
|
-
|
|
184
|
+
const kind = classifyError(cause);
|
|
185
|
+
super(
|
|
186
|
+
kind === "auth" ? `[${provider}] cannot authenticate: ${errorMessage(cause)}` : `[${provider}] failed to fetch secret at "${path2}": ${errorMessage(cause)}`
|
|
187
|
+
);
|
|
66
188
|
this.provider = provider;
|
|
67
189
|
this.path = path2;
|
|
190
|
+
this.cause = cause;
|
|
68
191
|
this.name = "SecretFetchError";
|
|
192
|
+
this.kind = kind;
|
|
193
|
+
this.remedy = remedyFor(kind, provider, cause);
|
|
69
194
|
}
|
|
70
195
|
provider;
|
|
71
196
|
path;
|
|
197
|
+
cause;
|
|
198
|
+
/** Whose problem this is - see {@link SecretErrorKind}. Classified from
|
|
199
|
+
* `cause` so every existing throw site is categorised without having to
|
|
200
|
+
* know about categories. */
|
|
201
|
+
kind;
|
|
202
|
+
/** The action that fixes it, when there is one (auth failures). */
|
|
203
|
+
remedy;
|
|
72
204
|
};
|
|
73
205
|
var BaseSecretProvider = class {
|
|
74
206
|
async fetchBatch(requests) {
|
|
@@ -113,6 +245,9 @@ function extractField(raw, field, context) {
|
|
|
113
245
|
return typeof current === "object" ? JSON.stringify(current) : String(current);
|
|
114
246
|
}
|
|
115
247
|
|
|
248
|
+
// src/providers/aws.ts
|
|
249
|
+
var import_credential_providers = require("@aws-sdk/credential-providers");
|
|
250
|
+
|
|
116
251
|
// src/ttlCache.ts
|
|
117
252
|
var TtlCache = class {
|
|
118
253
|
/** Settled values, only populated when a TTL is configured. */
|
|
@@ -123,9 +258,15 @@ var TtlCache = class {
|
|
|
123
258
|
* stays correct even with caching fully disabled. */
|
|
124
259
|
inFlight = /* @__PURE__ */ new Map();
|
|
125
260
|
ttlMs;
|
|
261
|
+
staleGraceMs;
|
|
262
|
+
isStaleServable;
|
|
263
|
+
onStale;
|
|
126
264
|
now;
|
|
127
265
|
constructor(options = {}) {
|
|
128
266
|
this.ttlMs = options.ttlMs ?? 0;
|
|
267
|
+
this.staleGraceMs = options.staleGraceMs ?? 0;
|
|
268
|
+
this.isStaleServable = options.isStaleServable ?? (() => false);
|
|
269
|
+
this.onStale = options.onStale;
|
|
129
270
|
this.now = options.now ?? Date.now;
|
|
130
271
|
}
|
|
131
272
|
/**
|
|
@@ -149,11 +290,19 @@ var TtlCache = class {
|
|
|
149
290
|
this.inFlight.set(key, pending);
|
|
150
291
|
try {
|
|
151
292
|
const value = await pending;
|
|
152
|
-
if (this.ttlMs > 0) {
|
|
293
|
+
if (this.ttlMs > 0 || this.staleGraceMs > 0) {
|
|
153
294
|
this.entries.set(key, { value: Promise.resolve(value), storedAt: this.now() });
|
|
154
295
|
}
|
|
155
296
|
return value;
|
|
156
297
|
} catch (err) {
|
|
298
|
+
const previous = this.entries.get(key);
|
|
299
|
+
if (previous && this.staleGraceMs > 0 && this.isStaleServable(err)) {
|
|
300
|
+
const ageMs = this.now() - previous.storedAt;
|
|
301
|
+
if (ageMs <= this.staleGraceMs) {
|
|
302
|
+
this.onStale?.(key, ageMs, err);
|
|
303
|
+
return previous.value;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
157
306
|
this.entries.delete(key);
|
|
158
307
|
throw err;
|
|
159
308
|
} finally {
|
|
@@ -217,6 +366,7 @@ var AwsSecretsManagerProvider = class extends BaseSecretProvider {
|
|
|
217
366
|
name = "aws";
|
|
218
367
|
explicitClient;
|
|
219
368
|
region;
|
|
369
|
+
profile;
|
|
220
370
|
controlPlane;
|
|
221
371
|
controlPlaneClient;
|
|
222
372
|
ambientClient = null;
|
|
@@ -225,8 +375,14 @@ var AwsSecretsManagerProvider = class extends BaseSecretProvider {
|
|
|
225
375
|
super();
|
|
226
376
|
this.explicitClient = options.client;
|
|
227
377
|
this.region = options.region;
|
|
378
|
+
this.profile = options.profile;
|
|
228
379
|
this.controlPlane = options.controlPlane;
|
|
229
|
-
this.rawCache = new TtlCache({
|
|
380
|
+
this.rawCache = new TtlCache({
|
|
381
|
+
ttlMs: options.cacheTtlMs,
|
|
382
|
+
staleGraceMs: options.staleGraceMs,
|
|
383
|
+
isStaleServable: (err) => isStaleServable(classifyError(err)),
|
|
384
|
+
onStale: options.onStaleValue
|
|
385
|
+
});
|
|
230
386
|
if (this.controlPlane) {
|
|
231
387
|
this.controlPlaneClient = this.controlPlane.client ?? new ControlPlaneClient({ baseUrl: this.controlPlane.baseUrl, token: this.controlPlane.token });
|
|
232
388
|
}
|
|
@@ -245,7 +401,15 @@ var AwsSecretsManagerProvider = class extends BaseSecretProvider {
|
|
|
245
401
|
}
|
|
246
402
|
return this.buildClientFromMintedCredentials(minted.credentials);
|
|
247
403
|
}
|
|
248
|
-
if (!this.ambientClient)
|
|
404
|
+
if (!this.ambientClient) {
|
|
405
|
+
this.ambientClient = new import_client_secrets_manager.SecretsManagerClient({
|
|
406
|
+
region: this.region,
|
|
407
|
+
// fromNodeProviderChain honours the same precedence as the
|
|
408
|
+
// ambient default, just pinned to one profile - so instance
|
|
409
|
+
// roles and env vars still work when no profile is named.
|
|
410
|
+
...this.profile ? { credentials: (0, import_credential_providers.fromNodeProviderChain)({ profile: this.profile }) } : {}
|
|
411
|
+
});
|
|
412
|
+
}
|
|
249
413
|
return this.ambientClient;
|
|
250
414
|
}
|
|
251
415
|
buildClientFromMintedCredentials(credentials) {
|
|
@@ -271,7 +435,8 @@ var AwsSecretsManagerProvider = class extends BaseSecretProvider {
|
|
|
271
435
|
}
|
|
272
436
|
throw new Error(`secret "${path2}" has no SecretString or SecretBinary payload`);
|
|
273
437
|
} catch (err) {
|
|
274
|
-
|
|
438
|
+
if (err instanceof SecretFetchError) throw err;
|
|
439
|
+
throw new SecretFetchError(this.name, path2, err);
|
|
275
440
|
}
|
|
276
441
|
});
|
|
277
442
|
}
|
|
@@ -613,16 +778,37 @@ function tryParseSecretRef(raw) {
|
|
|
613
778
|
}
|
|
614
779
|
|
|
615
780
|
// src/resolver.ts
|
|
781
|
+
function formatFailures(errors) {
|
|
782
|
+
const auth = errors.filter((e) => e.kind === "auth");
|
|
783
|
+
const rest = errors.filter((e) => e.kind !== "auth");
|
|
784
|
+
const lines = [];
|
|
785
|
+
for (const provider of [...new Set(auth.map((e) => e.provider ?? "unknown"))]) {
|
|
786
|
+
const group = auth.filter((e) => (e.provider ?? "unknown") === provider);
|
|
787
|
+
lines.push(`Cannot authenticate to provider "${provider}".`);
|
|
788
|
+
lines.push(` ${group[0].message}`);
|
|
789
|
+
const remedy = group.find((e) => e.remedy)?.remedy;
|
|
790
|
+
if (remedy) lines.push(` ${remedy}`);
|
|
791
|
+
lines.push(` Not resolved: ${group.map((e) => e.key).join(", ")}`);
|
|
792
|
+
}
|
|
793
|
+
if (rest.length > 0) {
|
|
794
|
+
if (lines.length > 0) lines.push("");
|
|
795
|
+
lines.push(`Failed to resolve ${rest.length} secret reference(s):`);
|
|
796
|
+
for (const e of rest) lines.push(` - ${e.key}: ${e.ref} -> ${e.message}`);
|
|
797
|
+
}
|
|
798
|
+
return lines.join("\n");
|
|
799
|
+
}
|
|
616
800
|
var SecRefsResolutionError = class extends Error {
|
|
617
801
|
constructor(errors) {
|
|
618
|
-
super(
|
|
619
|
-
`Failed to resolve ${errors.length} secret reference(s):
|
|
620
|
-
` + errors.map((e) => ` - ${e.key}: ${e.ref} -> ${e.message}`).join("\n")
|
|
621
|
-
);
|
|
802
|
+
super(formatFailures(errors));
|
|
622
803
|
this.errors = errors;
|
|
623
804
|
this.name = "SecRefsResolutionError";
|
|
624
805
|
}
|
|
625
806
|
errors;
|
|
807
|
+
/** True when every failure was an environment/auth problem, so a caller
|
|
808
|
+
* can tell "your credentials lapsed" from "your references are wrong". */
|
|
809
|
+
get isAuthOnly() {
|
|
810
|
+
return this.errors.length > 0 && this.errors.every((e) => e.kind === "auth");
|
|
811
|
+
}
|
|
626
812
|
};
|
|
627
813
|
async function resolveOne(ref, providers) {
|
|
628
814
|
const provider = providers[ref.provider];
|
|
@@ -662,7 +848,15 @@ async function expandKeyValueMap(input, options) {
|
|
|
662
848
|
if (result.status === "fulfilled") {
|
|
663
849
|
output[key] = result.value;
|
|
664
850
|
} else {
|
|
665
|
-
|
|
851
|
+
const reason = result.reason;
|
|
852
|
+
errors.push({
|
|
853
|
+
key,
|
|
854
|
+
ref: ref.raw,
|
|
855
|
+
message: errorMessage(reason),
|
|
856
|
+
kind: reason instanceof SecretFetchError ? reason.kind : void 0,
|
|
857
|
+
provider: reason instanceof SecretFetchError ? reason.provider : ref.provider,
|
|
858
|
+
remedy: reason instanceof SecretFetchError ? reason.remedy : void 0
|
|
859
|
+
});
|
|
666
860
|
}
|
|
667
861
|
});
|
|
668
862
|
if (errors.length > 0) {
|
|
@@ -727,6 +921,128 @@ function parseEnvFileText(rawText) {
|
|
|
727
921
|
return recoverTruncatedSecRefs(rawText, parsed);
|
|
728
922
|
}
|
|
729
923
|
|
|
924
|
+
// src/config.ts
|
|
925
|
+
var import_node_fs = require("fs");
|
|
926
|
+
var import_node_path2 = require("path");
|
|
927
|
+
var CONFIG_FILENAME = "secrefs.config.json";
|
|
928
|
+
var ConfigError = class extends Error {
|
|
929
|
+
constructor(message) {
|
|
930
|
+
super(message);
|
|
931
|
+
this.name = "ConfigError";
|
|
932
|
+
}
|
|
933
|
+
};
|
|
934
|
+
var FORBIDDEN_KEYS = /* @__PURE__ */ new Set([
|
|
935
|
+
"token",
|
|
936
|
+
"accessToken",
|
|
937
|
+
"access_token",
|
|
938
|
+
"secret",
|
|
939
|
+
"secretKey",
|
|
940
|
+
"secretAccessKey",
|
|
941
|
+
"password",
|
|
942
|
+
"apiKey",
|
|
943
|
+
"credential",
|
|
944
|
+
"credentials"
|
|
945
|
+
]);
|
|
946
|
+
function assertNoInlineSecrets(alias, config) {
|
|
947
|
+
for (const key of Object.keys(config)) {
|
|
948
|
+
if (FORBIDDEN_KEYS.has(key)) {
|
|
949
|
+
throw new ConfigError(
|
|
950
|
+
`${CONFIG_FILENAME}: provider "${alias}" sets "${key}". This file is meant to be committed and must never contain a credential. Reference the environment variable that holds it instead - e.g. "tokenEnv": "BWS_ACCESS_TOKEN".`
|
|
951
|
+
);
|
|
952
|
+
}
|
|
953
|
+
}
|
|
954
|
+
}
|
|
955
|
+
function requireEnv(alias, varName, env) {
|
|
956
|
+
const value = env[varName];
|
|
957
|
+
if (!value) {
|
|
958
|
+
throw new ConfigError(
|
|
959
|
+
`${CONFIG_FILENAME}: provider "${alias}" expects the credential in ${varName}, but that environment variable is not set.`
|
|
960
|
+
);
|
|
961
|
+
}
|
|
962
|
+
return value;
|
|
963
|
+
}
|
|
964
|
+
function parseConfig(raw, source = CONFIG_FILENAME) {
|
|
965
|
+
let parsed;
|
|
966
|
+
try {
|
|
967
|
+
parsed = JSON.parse(raw);
|
|
968
|
+
} catch (err) {
|
|
969
|
+
throw new ConfigError(`${source} is not valid JSON: ${err.message}`);
|
|
970
|
+
}
|
|
971
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
972
|
+
throw new ConfigError(`${source} must contain a JSON object.`);
|
|
973
|
+
}
|
|
974
|
+
const providers = parsed.providers;
|
|
975
|
+
if (typeof providers !== "object" || providers === null) {
|
|
976
|
+
throw new ConfigError(`${source} must have a "providers" object.`);
|
|
977
|
+
}
|
|
978
|
+
for (const [alias, value] of Object.entries(providers)) {
|
|
979
|
+
if (typeof value !== "object" || value === null) {
|
|
980
|
+
throw new ConfigError(`${source}: provider "${alias}" must be an object.`);
|
|
981
|
+
}
|
|
982
|
+
const type = value.type;
|
|
983
|
+
if (type !== "aws" && type !== "bitwarden" && type !== "vault" && type !== "local") {
|
|
984
|
+
throw new ConfigError(
|
|
985
|
+
`${source}: provider "${alias}" has type ${JSON.stringify(type)}; expected one of "aws", "bitwarden", "vault", "local".`
|
|
986
|
+
);
|
|
987
|
+
}
|
|
988
|
+
assertNoInlineSecrets(alias, value);
|
|
989
|
+
}
|
|
990
|
+
return parsed;
|
|
991
|
+
}
|
|
992
|
+
function buildProviders(config, options = {}) {
|
|
993
|
+
const env = options.env ?? process.env;
|
|
994
|
+
const configDir = options.configDir ?? process.cwd();
|
|
995
|
+
const registry = {};
|
|
996
|
+
for (const [alias, entry] of Object.entries(config.providers)) {
|
|
997
|
+
switch (entry.type) {
|
|
998
|
+
case "aws":
|
|
999
|
+
registry[alias] = new AwsSecretsManagerProvider({
|
|
1000
|
+
region: entry.region,
|
|
1001
|
+
cacheTtlMs: entry.cacheTtlMs,
|
|
1002
|
+
staleGraceMs: entry.staleGraceMs,
|
|
1003
|
+
profile: entry.profile
|
|
1004
|
+
});
|
|
1005
|
+
break;
|
|
1006
|
+
case "bitwarden":
|
|
1007
|
+
registry[alias] = new BitwardenProvider({
|
|
1008
|
+
accessToken: requireEnv(alias, entry.tokenEnv ?? "BWS_ACCESS_TOKEN", env),
|
|
1009
|
+
organizationId: entry.organizationIdEnv ? requireEnv(alias, entry.organizationIdEnv, env) : env.BWS_ORGANIZATION_ID,
|
|
1010
|
+
apiUrl: entry.apiUrl,
|
|
1011
|
+
identityUrl: entry.identityUrl
|
|
1012
|
+
});
|
|
1013
|
+
break;
|
|
1014
|
+
case "vault":
|
|
1015
|
+
registry[alias] = new VaultProvider({
|
|
1016
|
+
endpoint: entry.addr ?? env.VAULT_ADDR,
|
|
1017
|
+
token: requireEnv(alias, entry.tokenEnv ?? "VAULT_TOKEN", env)
|
|
1018
|
+
});
|
|
1019
|
+
break;
|
|
1020
|
+
case "local":
|
|
1021
|
+
registry[alias] = new LocalProvider({
|
|
1022
|
+
filePath: entry.file ? (0, import_node_path2.resolve)(configDir, entry.file) : void 0
|
|
1023
|
+
});
|
|
1024
|
+
break;
|
|
1025
|
+
}
|
|
1026
|
+
}
|
|
1027
|
+
return registry;
|
|
1028
|
+
}
|
|
1029
|
+
function loadConfigFrom(dir = process.cwd()) {
|
|
1030
|
+
let current = (0, import_node_path2.resolve)(dir);
|
|
1031
|
+
for (; ; ) {
|
|
1032
|
+
const candidate = (0, import_node_path2.resolve)(current, CONFIG_FILENAME);
|
|
1033
|
+
let raw;
|
|
1034
|
+
try {
|
|
1035
|
+
raw = (0, import_node_fs.readFileSync)(candidate, "utf8");
|
|
1036
|
+
} catch {
|
|
1037
|
+
const parent = (0, import_node_path2.dirname)(current);
|
|
1038
|
+
if (parent === current) return void 0;
|
|
1039
|
+
current = parent;
|
|
1040
|
+
continue;
|
|
1041
|
+
}
|
|
1042
|
+
return { config: parseConfig(raw, candidate), path: candidate };
|
|
1043
|
+
}
|
|
1044
|
+
}
|
|
1045
|
+
|
|
730
1046
|
// src/index.ts
|
|
731
1047
|
function createDefaultProviders() {
|
|
732
1048
|
return {
|
|
@@ -780,6 +1096,8 @@ var secRefs = new SecRefs();
|
|
|
780
1096
|
AwsSecretsManagerProvider,
|
|
781
1097
|
BaseSecretProvider,
|
|
782
1098
|
BitwardenProvider,
|
|
1099
|
+
CONFIG_FILENAME,
|
|
1100
|
+
ConfigError,
|
|
783
1101
|
ControlPlaneClient,
|
|
784
1102
|
ControlPlaneRequestError,
|
|
785
1103
|
LocalProvider,
|
|
@@ -789,12 +1107,15 @@ var secRefs = new SecRefs();
|
|
|
789
1107
|
SecretFetchError,
|
|
790
1108
|
TtlCache,
|
|
791
1109
|
VaultProvider,
|
|
1110
|
+
buildProviders,
|
|
792
1111
|
checkReferences,
|
|
793
1112
|
createDefaultProviders,
|
|
794
1113
|
expandKeyValueMap,
|
|
795
1114
|
expandProcessEnv,
|
|
796
1115
|
extractField,
|
|
797
1116
|
isSecretRef,
|
|
1117
|
+
loadConfigFrom,
|
|
1118
|
+
parseConfig,
|
|
798
1119
|
parseEnvFileText,
|
|
799
1120
|
parseSecretRef,
|
|
800
1121
|
recoverTruncatedSecRefs,
|