@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/dist/index.js
CHANGED
|
@@ -5,16 +5,143 @@ import {
|
|
|
5
5
|
SecretsManagerClient
|
|
6
6
|
} from "@aws-sdk/client-secrets-manager";
|
|
7
7
|
|
|
8
|
+
// src/providers/errors.ts
|
|
9
|
+
var AUTH_NAMES = /* @__PURE__ */ new Set([
|
|
10
|
+
"CredentialsProviderError",
|
|
11
|
+
"TokenProviderError",
|
|
12
|
+
"ExpiredToken",
|
|
13
|
+
"ExpiredTokenException",
|
|
14
|
+
"InvalidClientTokenId",
|
|
15
|
+
"UnrecognizedClientException",
|
|
16
|
+
"InvalidIdentityToken",
|
|
17
|
+
"AuthFailure",
|
|
18
|
+
"SSOTokenProviderFailure"
|
|
19
|
+
]);
|
|
20
|
+
var NOT_FOUND_NAMES = /* @__PURE__ */ new Set([
|
|
21
|
+
"ResourceNotFoundException",
|
|
22
|
+
"NoSuchEntity",
|
|
23
|
+
"SecretNotFound"
|
|
24
|
+
]);
|
|
25
|
+
var DENIED_NAMES = /* @__PURE__ */ new Set([
|
|
26
|
+
"AccessDeniedException",
|
|
27
|
+
"AccessDenied",
|
|
28
|
+
"AuthorizationError",
|
|
29
|
+
"UnauthorizedOperation"
|
|
30
|
+
]);
|
|
31
|
+
var TRANSIENT_NAMES = /* @__PURE__ */ new Set([
|
|
32
|
+
"TimeoutError",
|
|
33
|
+
"NetworkingError",
|
|
34
|
+
"RequestTimeout",
|
|
35
|
+
"RequestTimeoutException",
|
|
36
|
+
"ThrottlingException",
|
|
37
|
+
"TooManyRequestsException",
|
|
38
|
+
"InternalServiceError",
|
|
39
|
+
"InternalServerError",
|
|
40
|
+
"ServiceUnavailable",
|
|
41
|
+
"ServiceUnavailableException",
|
|
42
|
+
"AbortError",
|
|
43
|
+
"ECONNRESET",
|
|
44
|
+
"ECONNREFUSED",
|
|
45
|
+
"ETIMEDOUT",
|
|
46
|
+
"EAI_AGAIN"
|
|
47
|
+
]);
|
|
48
|
+
var AUTH_FRAGMENTS = [
|
|
49
|
+
"could not load credentials",
|
|
50
|
+
"sso session associated with this profile has expired",
|
|
51
|
+
"security token included in the request is expired",
|
|
52
|
+
"unable to locate credentials",
|
|
53
|
+
"token is expired",
|
|
54
|
+
"credentials have expired",
|
|
55
|
+
"is expired"
|
|
56
|
+
];
|
|
57
|
+
var TRANSIENT_FRAGMENTS = [
|
|
58
|
+
"socket hang up",
|
|
59
|
+
"network error",
|
|
60
|
+
"timed out",
|
|
61
|
+
"timeout",
|
|
62
|
+
"econnreset",
|
|
63
|
+
"econnrefused",
|
|
64
|
+
"getaddrinfo"
|
|
65
|
+
];
|
|
66
|
+
function nameOf(err) {
|
|
67
|
+
if (typeof err !== "object" || err === null) return "";
|
|
68
|
+
const e = err;
|
|
69
|
+
for (const candidate of [e.name, e.code, e.__type]) {
|
|
70
|
+
if (typeof candidate === "string" && candidate) return candidate;
|
|
71
|
+
}
|
|
72
|
+
return "";
|
|
73
|
+
}
|
|
74
|
+
function statusOf(err) {
|
|
75
|
+
if (typeof err !== "object" || err === null) return void 0;
|
|
76
|
+
const meta = err.$metadata;
|
|
77
|
+
if (typeof meta?.httpStatusCode === "number") return meta.httpStatusCode;
|
|
78
|
+
const status = err.status ?? err.statusCode;
|
|
79
|
+
return typeof status === "number" ? status : void 0;
|
|
80
|
+
}
|
|
81
|
+
function classifyError(err) {
|
|
82
|
+
const name = nameOf(err);
|
|
83
|
+
if (AUTH_NAMES.has(name)) return "auth";
|
|
84
|
+
if (NOT_FOUND_NAMES.has(name)) return "not_found";
|
|
85
|
+
if (DENIED_NAMES.has(name)) return "denied";
|
|
86
|
+
if (TRANSIENT_NAMES.has(name)) return "transient";
|
|
87
|
+
const status = statusOf(err);
|
|
88
|
+
if (status === 401) return "auth";
|
|
89
|
+
if (status === 403) return "denied";
|
|
90
|
+
if (status === 404) return "not_found";
|
|
91
|
+
if (status === 408 || status === 429) return "transient";
|
|
92
|
+
if (status !== void 0 && status >= 500) return "transient";
|
|
93
|
+
const message = (err instanceof Error ? err.message : String(err ?? "")).toLowerCase();
|
|
94
|
+
if (AUTH_FRAGMENTS.some((f) => message.includes(f))) return "auth";
|
|
95
|
+
if (TRANSIENT_FRAGMENTS.some((f) => message.includes(f))) return "transient";
|
|
96
|
+
return "unknown";
|
|
97
|
+
}
|
|
98
|
+
function isStaleServable(kind) {
|
|
99
|
+
return kind === "transient";
|
|
100
|
+
}
|
|
101
|
+
function remedyFor(kind, provider, err) {
|
|
102
|
+
if (kind !== "auth") return void 0;
|
|
103
|
+
const message = err instanceof Error ? err.message : "";
|
|
104
|
+
if (/sso session/i.test(message)) {
|
|
105
|
+
const profile = process.env.AWS_PROFILE;
|
|
106
|
+
return profile ? `Run: aws sso login --profile ${profile}` : "Run: aws sso login --profile <your-profile>";
|
|
107
|
+
}
|
|
108
|
+
switch (provider) {
|
|
109
|
+
case "aws": {
|
|
110
|
+
const profile = process.env.AWS_PROFILE;
|
|
111
|
+
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.";
|
|
112
|
+
}
|
|
113
|
+
case "bitwarden":
|
|
114
|
+
return "Set BWS_ACCESS_TOKEN to a valid Bitwarden machine account token.";
|
|
115
|
+
case "vault":
|
|
116
|
+
return "Set VAULT_ADDR and VAULT_TOKEN, or renew the token if it has expired.";
|
|
117
|
+
default:
|
|
118
|
+
return void 0;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
8
122
|
// src/providers/base.ts
|
|
9
123
|
var SecretFetchError = class extends Error {
|
|
10
124
|
constructor(provider, path2, cause) {
|
|
11
|
-
|
|
125
|
+
const kind = classifyError(cause);
|
|
126
|
+
super(
|
|
127
|
+
kind === "auth" ? `[${provider}] cannot authenticate: ${errorMessage(cause)}` : `[${provider}] failed to fetch secret at "${path2}": ${errorMessage(cause)}`
|
|
128
|
+
);
|
|
12
129
|
this.provider = provider;
|
|
13
130
|
this.path = path2;
|
|
131
|
+
this.cause = cause;
|
|
14
132
|
this.name = "SecretFetchError";
|
|
133
|
+
this.kind = kind;
|
|
134
|
+
this.remedy = remedyFor(kind, provider, cause);
|
|
15
135
|
}
|
|
16
136
|
provider;
|
|
17
137
|
path;
|
|
138
|
+
cause;
|
|
139
|
+
/** Whose problem this is - see {@link SecretErrorKind}. Classified from
|
|
140
|
+
* `cause` so every existing throw site is categorised without having to
|
|
141
|
+
* know about categories. */
|
|
142
|
+
kind;
|
|
143
|
+
/** The action that fixes it, when there is one (auth failures). */
|
|
144
|
+
remedy;
|
|
18
145
|
};
|
|
19
146
|
var BaseSecretProvider = class {
|
|
20
147
|
async fetchBatch(requests) {
|
|
@@ -59,6 +186,9 @@ function extractField(raw, field, context) {
|
|
|
59
186
|
return typeof current === "object" ? JSON.stringify(current) : String(current);
|
|
60
187
|
}
|
|
61
188
|
|
|
189
|
+
// src/providers/aws.ts
|
|
190
|
+
import { fromNodeProviderChain } from "@aws-sdk/credential-providers";
|
|
191
|
+
|
|
62
192
|
// src/ttlCache.ts
|
|
63
193
|
var TtlCache = class {
|
|
64
194
|
/** Settled values, only populated when a TTL is configured. */
|
|
@@ -69,9 +199,15 @@ var TtlCache = class {
|
|
|
69
199
|
* stays correct even with caching fully disabled. */
|
|
70
200
|
inFlight = /* @__PURE__ */ new Map();
|
|
71
201
|
ttlMs;
|
|
202
|
+
staleGraceMs;
|
|
203
|
+
isStaleServable;
|
|
204
|
+
onStale;
|
|
72
205
|
now;
|
|
73
206
|
constructor(options = {}) {
|
|
74
207
|
this.ttlMs = options.ttlMs ?? 0;
|
|
208
|
+
this.staleGraceMs = options.staleGraceMs ?? 0;
|
|
209
|
+
this.isStaleServable = options.isStaleServable ?? (() => false);
|
|
210
|
+
this.onStale = options.onStale;
|
|
75
211
|
this.now = options.now ?? Date.now;
|
|
76
212
|
}
|
|
77
213
|
/**
|
|
@@ -95,11 +231,19 @@ var TtlCache = class {
|
|
|
95
231
|
this.inFlight.set(key, pending);
|
|
96
232
|
try {
|
|
97
233
|
const value = await pending;
|
|
98
|
-
if (this.ttlMs > 0) {
|
|
234
|
+
if (this.ttlMs > 0 || this.staleGraceMs > 0) {
|
|
99
235
|
this.entries.set(key, { value: Promise.resolve(value), storedAt: this.now() });
|
|
100
236
|
}
|
|
101
237
|
return value;
|
|
102
238
|
} catch (err) {
|
|
239
|
+
const previous = this.entries.get(key);
|
|
240
|
+
if (previous && this.staleGraceMs > 0 && this.isStaleServable(err)) {
|
|
241
|
+
const ageMs = this.now() - previous.storedAt;
|
|
242
|
+
if (ageMs <= this.staleGraceMs) {
|
|
243
|
+
this.onStale?.(key, ageMs, err);
|
|
244
|
+
return previous.value;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
103
247
|
this.entries.delete(key);
|
|
104
248
|
throw err;
|
|
105
249
|
} finally {
|
|
@@ -163,6 +307,7 @@ var AwsSecretsManagerProvider = class extends BaseSecretProvider {
|
|
|
163
307
|
name = "aws";
|
|
164
308
|
explicitClient;
|
|
165
309
|
region;
|
|
310
|
+
profile;
|
|
166
311
|
controlPlane;
|
|
167
312
|
controlPlaneClient;
|
|
168
313
|
ambientClient = null;
|
|
@@ -171,8 +316,14 @@ var AwsSecretsManagerProvider = class extends BaseSecretProvider {
|
|
|
171
316
|
super();
|
|
172
317
|
this.explicitClient = options.client;
|
|
173
318
|
this.region = options.region;
|
|
319
|
+
this.profile = options.profile;
|
|
174
320
|
this.controlPlane = options.controlPlane;
|
|
175
|
-
this.rawCache = new TtlCache({
|
|
321
|
+
this.rawCache = new TtlCache({
|
|
322
|
+
ttlMs: options.cacheTtlMs,
|
|
323
|
+
staleGraceMs: options.staleGraceMs,
|
|
324
|
+
isStaleServable: (err) => isStaleServable(classifyError(err)),
|
|
325
|
+
onStale: options.onStaleValue
|
|
326
|
+
});
|
|
176
327
|
if (this.controlPlane) {
|
|
177
328
|
this.controlPlaneClient = this.controlPlane.client ?? new ControlPlaneClient({ baseUrl: this.controlPlane.baseUrl, token: this.controlPlane.token });
|
|
178
329
|
}
|
|
@@ -191,7 +342,15 @@ var AwsSecretsManagerProvider = class extends BaseSecretProvider {
|
|
|
191
342
|
}
|
|
192
343
|
return this.buildClientFromMintedCredentials(minted.credentials);
|
|
193
344
|
}
|
|
194
|
-
if (!this.ambientClient)
|
|
345
|
+
if (!this.ambientClient) {
|
|
346
|
+
this.ambientClient = new SecretsManagerClient({
|
|
347
|
+
region: this.region,
|
|
348
|
+
// fromNodeProviderChain honours the same precedence as the
|
|
349
|
+
// ambient default, just pinned to one profile - so instance
|
|
350
|
+
// roles and env vars still work when no profile is named.
|
|
351
|
+
...this.profile ? { credentials: fromNodeProviderChain({ profile: this.profile }) } : {}
|
|
352
|
+
});
|
|
353
|
+
}
|
|
195
354
|
return this.ambientClient;
|
|
196
355
|
}
|
|
197
356
|
buildClientFromMintedCredentials(credentials) {
|
|
@@ -217,7 +376,8 @@ var AwsSecretsManagerProvider = class extends BaseSecretProvider {
|
|
|
217
376
|
}
|
|
218
377
|
throw new Error(`secret "${path2}" has no SecretString or SecretBinary payload`);
|
|
219
378
|
} catch (err) {
|
|
220
|
-
|
|
379
|
+
if (err instanceof SecretFetchError) throw err;
|
|
380
|
+
throw new SecretFetchError(this.name, path2, err);
|
|
221
381
|
}
|
|
222
382
|
});
|
|
223
383
|
}
|
|
@@ -559,16 +719,37 @@ function tryParseSecretRef(raw) {
|
|
|
559
719
|
}
|
|
560
720
|
|
|
561
721
|
// src/resolver.ts
|
|
722
|
+
function formatFailures(errors) {
|
|
723
|
+
const auth = errors.filter((e) => e.kind === "auth");
|
|
724
|
+
const rest = errors.filter((e) => e.kind !== "auth");
|
|
725
|
+
const lines = [];
|
|
726
|
+
for (const provider of [...new Set(auth.map((e) => e.provider ?? "unknown"))]) {
|
|
727
|
+
const group = auth.filter((e) => (e.provider ?? "unknown") === provider);
|
|
728
|
+
lines.push(`Cannot authenticate to provider "${provider}".`);
|
|
729
|
+
lines.push(` ${group[0].message}`);
|
|
730
|
+
const remedy = group.find((e) => e.remedy)?.remedy;
|
|
731
|
+
if (remedy) lines.push(` ${remedy}`);
|
|
732
|
+
lines.push(` Not resolved: ${group.map((e) => e.key).join(", ")}`);
|
|
733
|
+
}
|
|
734
|
+
if (rest.length > 0) {
|
|
735
|
+
if (lines.length > 0) lines.push("");
|
|
736
|
+
lines.push(`Failed to resolve ${rest.length} secret reference(s):`);
|
|
737
|
+
for (const e of rest) lines.push(` - ${e.key}: ${e.ref} -> ${e.message}`);
|
|
738
|
+
}
|
|
739
|
+
return lines.join("\n");
|
|
740
|
+
}
|
|
562
741
|
var SecRefsResolutionError = class extends Error {
|
|
563
742
|
constructor(errors) {
|
|
564
|
-
super(
|
|
565
|
-
`Failed to resolve ${errors.length} secret reference(s):
|
|
566
|
-
` + errors.map((e) => ` - ${e.key}: ${e.ref} -> ${e.message}`).join("\n")
|
|
567
|
-
);
|
|
743
|
+
super(formatFailures(errors));
|
|
568
744
|
this.errors = errors;
|
|
569
745
|
this.name = "SecRefsResolutionError";
|
|
570
746
|
}
|
|
571
747
|
errors;
|
|
748
|
+
/** True when every failure was an environment/auth problem, so a caller
|
|
749
|
+
* can tell "your credentials lapsed" from "your references are wrong". */
|
|
750
|
+
get isAuthOnly() {
|
|
751
|
+
return this.errors.length > 0 && this.errors.every((e) => e.kind === "auth");
|
|
752
|
+
}
|
|
572
753
|
};
|
|
573
754
|
async function resolveOne(ref, providers) {
|
|
574
755
|
const provider = providers[ref.provider];
|
|
@@ -608,7 +789,15 @@ async function expandKeyValueMap(input, options) {
|
|
|
608
789
|
if (result.status === "fulfilled") {
|
|
609
790
|
output[key] = result.value;
|
|
610
791
|
} else {
|
|
611
|
-
|
|
792
|
+
const reason = result.reason;
|
|
793
|
+
errors.push({
|
|
794
|
+
key,
|
|
795
|
+
ref: ref.raw,
|
|
796
|
+
message: errorMessage(reason),
|
|
797
|
+
kind: reason instanceof SecretFetchError ? reason.kind : void 0,
|
|
798
|
+
provider: reason instanceof SecretFetchError ? reason.provider : ref.provider,
|
|
799
|
+
remedy: reason instanceof SecretFetchError ? reason.remedy : void 0
|
|
800
|
+
});
|
|
612
801
|
}
|
|
613
802
|
});
|
|
614
803
|
if (errors.length > 0) {
|
|
@@ -673,6 +862,128 @@ function parseEnvFileText(rawText) {
|
|
|
673
862
|
return recoverTruncatedSecRefs(rawText, parsed);
|
|
674
863
|
}
|
|
675
864
|
|
|
865
|
+
// src/config.ts
|
|
866
|
+
import { readFileSync } from "fs";
|
|
867
|
+
import { dirname, resolve } from "path";
|
|
868
|
+
var CONFIG_FILENAME = "secrefs.config.json";
|
|
869
|
+
var ConfigError = class extends Error {
|
|
870
|
+
constructor(message) {
|
|
871
|
+
super(message);
|
|
872
|
+
this.name = "ConfigError";
|
|
873
|
+
}
|
|
874
|
+
};
|
|
875
|
+
var FORBIDDEN_KEYS = /* @__PURE__ */ new Set([
|
|
876
|
+
"token",
|
|
877
|
+
"accessToken",
|
|
878
|
+
"access_token",
|
|
879
|
+
"secret",
|
|
880
|
+
"secretKey",
|
|
881
|
+
"secretAccessKey",
|
|
882
|
+
"password",
|
|
883
|
+
"apiKey",
|
|
884
|
+
"credential",
|
|
885
|
+
"credentials"
|
|
886
|
+
]);
|
|
887
|
+
function assertNoInlineSecrets(alias, config) {
|
|
888
|
+
for (const key of Object.keys(config)) {
|
|
889
|
+
if (FORBIDDEN_KEYS.has(key)) {
|
|
890
|
+
throw new ConfigError(
|
|
891
|
+
`${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".`
|
|
892
|
+
);
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
function requireEnv(alias, varName, env) {
|
|
897
|
+
const value = env[varName];
|
|
898
|
+
if (!value) {
|
|
899
|
+
throw new ConfigError(
|
|
900
|
+
`${CONFIG_FILENAME}: provider "${alias}" expects the credential in ${varName}, but that environment variable is not set.`
|
|
901
|
+
);
|
|
902
|
+
}
|
|
903
|
+
return value;
|
|
904
|
+
}
|
|
905
|
+
function parseConfig(raw, source = CONFIG_FILENAME) {
|
|
906
|
+
let parsed;
|
|
907
|
+
try {
|
|
908
|
+
parsed = JSON.parse(raw);
|
|
909
|
+
} catch (err) {
|
|
910
|
+
throw new ConfigError(`${source} is not valid JSON: ${err.message}`);
|
|
911
|
+
}
|
|
912
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
913
|
+
throw new ConfigError(`${source} must contain a JSON object.`);
|
|
914
|
+
}
|
|
915
|
+
const providers = parsed.providers;
|
|
916
|
+
if (typeof providers !== "object" || providers === null) {
|
|
917
|
+
throw new ConfigError(`${source} must have a "providers" object.`);
|
|
918
|
+
}
|
|
919
|
+
for (const [alias, value] of Object.entries(providers)) {
|
|
920
|
+
if (typeof value !== "object" || value === null) {
|
|
921
|
+
throw new ConfigError(`${source}: provider "${alias}" must be an object.`);
|
|
922
|
+
}
|
|
923
|
+
const type = value.type;
|
|
924
|
+
if (type !== "aws" && type !== "bitwarden" && type !== "vault" && type !== "local") {
|
|
925
|
+
throw new ConfigError(
|
|
926
|
+
`${source}: provider "${alias}" has type ${JSON.stringify(type)}; expected one of "aws", "bitwarden", "vault", "local".`
|
|
927
|
+
);
|
|
928
|
+
}
|
|
929
|
+
assertNoInlineSecrets(alias, value);
|
|
930
|
+
}
|
|
931
|
+
return parsed;
|
|
932
|
+
}
|
|
933
|
+
function buildProviders(config, options = {}) {
|
|
934
|
+
const env = options.env ?? process.env;
|
|
935
|
+
const configDir = options.configDir ?? process.cwd();
|
|
936
|
+
const registry = {};
|
|
937
|
+
for (const [alias, entry] of Object.entries(config.providers)) {
|
|
938
|
+
switch (entry.type) {
|
|
939
|
+
case "aws":
|
|
940
|
+
registry[alias] = new AwsSecretsManagerProvider({
|
|
941
|
+
region: entry.region,
|
|
942
|
+
cacheTtlMs: entry.cacheTtlMs,
|
|
943
|
+
staleGraceMs: entry.staleGraceMs,
|
|
944
|
+
profile: entry.profile
|
|
945
|
+
});
|
|
946
|
+
break;
|
|
947
|
+
case "bitwarden":
|
|
948
|
+
registry[alias] = new BitwardenProvider({
|
|
949
|
+
accessToken: requireEnv(alias, entry.tokenEnv ?? "BWS_ACCESS_TOKEN", env),
|
|
950
|
+
organizationId: entry.organizationIdEnv ? requireEnv(alias, entry.organizationIdEnv, env) : env.BWS_ORGANIZATION_ID,
|
|
951
|
+
apiUrl: entry.apiUrl,
|
|
952
|
+
identityUrl: entry.identityUrl
|
|
953
|
+
});
|
|
954
|
+
break;
|
|
955
|
+
case "vault":
|
|
956
|
+
registry[alias] = new VaultProvider({
|
|
957
|
+
endpoint: entry.addr ?? env.VAULT_ADDR,
|
|
958
|
+
token: requireEnv(alias, entry.tokenEnv ?? "VAULT_TOKEN", env)
|
|
959
|
+
});
|
|
960
|
+
break;
|
|
961
|
+
case "local":
|
|
962
|
+
registry[alias] = new LocalProvider({
|
|
963
|
+
filePath: entry.file ? resolve(configDir, entry.file) : void 0
|
|
964
|
+
});
|
|
965
|
+
break;
|
|
966
|
+
}
|
|
967
|
+
}
|
|
968
|
+
return registry;
|
|
969
|
+
}
|
|
970
|
+
function loadConfigFrom(dir = process.cwd()) {
|
|
971
|
+
let current = resolve(dir);
|
|
972
|
+
for (; ; ) {
|
|
973
|
+
const candidate = resolve(current, CONFIG_FILENAME);
|
|
974
|
+
let raw;
|
|
975
|
+
try {
|
|
976
|
+
raw = readFileSync(candidate, "utf8");
|
|
977
|
+
} catch {
|
|
978
|
+
const parent = dirname(current);
|
|
979
|
+
if (parent === current) return void 0;
|
|
980
|
+
current = parent;
|
|
981
|
+
continue;
|
|
982
|
+
}
|
|
983
|
+
return { config: parseConfig(raw, candidate), path: candidate };
|
|
984
|
+
}
|
|
985
|
+
}
|
|
986
|
+
|
|
676
987
|
// src/index.ts
|
|
677
988
|
function createDefaultProviders() {
|
|
678
989
|
return {
|
|
@@ -725,6 +1036,8 @@ export {
|
|
|
725
1036
|
AwsSecretsManagerProvider,
|
|
726
1037
|
BaseSecretProvider,
|
|
727
1038
|
BitwardenProvider,
|
|
1039
|
+
CONFIG_FILENAME,
|
|
1040
|
+
ConfigError,
|
|
728
1041
|
ControlPlaneClient,
|
|
729
1042
|
ControlPlaneRequestError,
|
|
730
1043
|
LocalProvider,
|
|
@@ -734,12 +1047,15 @@ export {
|
|
|
734
1047
|
SecretFetchError,
|
|
735
1048
|
TtlCache,
|
|
736
1049
|
VaultProvider,
|
|
1050
|
+
buildProviders,
|
|
737
1051
|
checkReferences,
|
|
738
1052
|
createDefaultProviders,
|
|
739
1053
|
expandKeyValueMap,
|
|
740
1054
|
expandProcessEnv,
|
|
741
1055
|
extractField,
|
|
742
1056
|
isSecretRef,
|
|
1057
|
+
loadConfigFrom,
|
|
1058
|
+
parseConfig,
|
|
743
1059
|
parseEnvFileText,
|
|
744
1060
|
parseSecretRef,
|
|
745
1061
|
recoverTruncatedSecRefs,
|