typed-settings 0.1.4 → 0.2.1

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.
Files changed (81) hide show
  1. package/README.md +14 -7
  2. package/bin/typed-settings.mjs +5 -0
  3. package/dist/adapters/joi.cjs +1 -26
  4. package/dist/adapters/joi.d.cts +2 -1
  5. package/dist/adapters/joi.d.ts +2 -1
  6. package/dist/adapters/joi.js +1 -1
  7. package/dist/adapters/superstruct.cjs +1 -26
  8. package/dist/adapters/superstruct.js +1 -1
  9. package/dist/adapters/yup.cjs +1 -25
  10. package/dist/adapters/yup.js +1 -1
  11. package/dist/adapters/zod.cjs +3 -36
  12. package/dist/adapters/zod.js +1 -33
  13. package/dist/check-XVFZEVNV.js +1 -0
  14. package/dist/chunk-4EAMKDNK.js +3 -0
  15. package/dist/chunk-5D2AQWDG.js +1 -0
  16. package/dist/chunk-C6CG2D4B.js +2 -0
  17. package/dist/chunk-FQYD5ZKE.js +1 -0
  18. package/dist/chunk-NQDE3WLR.js +3 -0
  19. package/dist/chunk-RURPDXIJ.js +1 -0
  20. package/dist/cli/main.cjs +36 -2225
  21. package/dist/cli/main.js +32 -806
  22. package/dist/errors-JkkOM0u-.d.cts +25 -0
  23. package/dist/errors-JkkOM0u-.d.ts +25 -0
  24. package/dist/framework/next.cjs +5 -1158
  25. package/dist/framework/next.d.cts +3 -3
  26. package/dist/framework/next.d.ts +3 -3
  27. package/dist/framework/next.js +1 -15
  28. package/dist/framework/vite.cjs +5 -1156
  29. package/dist/framework/vite.d.cts +8 -3
  30. package/dist/framework/vite.d.ts +8 -3
  31. package/dist/framework/vite.js +1 -14
  32. package/dist/index.cjs +5 -1181
  33. package/dist/index.d.cts +38 -7
  34. package/dist/index.d.ts +38 -7
  35. package/dist/index.js +1 -6
  36. package/dist/node.cjs +6 -1241
  37. package/dist/node.d.cts +23 -9
  38. package/dist/node.d.ts +23 -9
  39. package/dist/node.js +1 -7
  40. package/dist/{types-B_Dc2L4T.d.cts → types-CcsYDAdO.d.cts} +23 -1
  41. package/dist/{types-B_Dc2L4T.d.ts → types-CcsYDAdO.d.ts} +23 -1
  42. package/dist/vault/aws.cjs +4 -217
  43. package/dist/vault/aws.d.cts +1 -1
  44. package/dist/vault/aws.d.ts +1 -1
  45. package/dist/vault/aws.js +2 -167
  46. package/dist/vault/hashicorp.cjs +4 -183
  47. package/dist/vault/hashicorp.d.cts +1 -1
  48. package/dist/vault/hashicorp.d.ts +1 -1
  49. package/dist/vault/hashicorp.js +2 -77
  50. package/dist/vault/http.cjs +4 -185
  51. package/dist/vault/http.d.cts +1 -1
  52. package/dist/vault/http.d.ts +1 -1
  53. package/dist/vault/http.js +2 -79
  54. package/package.json +2 -3
  55. package/dist/chunk-33M5GIKQ.js +0 -13
  56. package/dist/chunk-A5TCQJ7L.js +0 -65
  57. package/dist/chunk-DEP5SD4J.js +0 -233
  58. package/dist/chunk-EE6LXNJV.js +0 -34
  59. package/dist/chunk-F6DX3HXZ.js +0 -101
  60. package/dist/chunk-OVFMKC2O.js +0 -15
  61. package/dist/chunk-RQC6OMVV.js +0 -86
  62. package/dist/chunk-UFUHHR2J.js +0 -38
  63. package/dist/chunk-YVJEZPPZ.js +0 -989
  64. package/dist/errors-itGaU4Q8.d.cts +0 -17
  65. package/dist/errors-itGaU4Q8.d.ts +0 -17
  66. package/dist/formats/env.cjs +0 -103
  67. package/dist/formats/env.d.cts +0 -8
  68. package/dist/formats/env.d.ts +0 -8
  69. package/dist/formats/env.js +0 -1
  70. package/dist/formats/json.cjs +0 -15
  71. package/dist/formats/json.d.cts +0 -7
  72. package/dist/formats/json.d.ts +0 -7
  73. package/dist/formats/json.js +0 -1
  74. package/dist/formats/toml.cjs +0 -17
  75. package/dist/formats/toml.d.cts +0 -7
  76. package/dist/formats/toml.d.ts +0 -7
  77. package/dist/formats/toml.js +0 -1
  78. package/dist/formats/yaml.cjs +0 -40
  79. package/dist/formats/yaml.d.cts +0 -8
  80. package/dist/formats/yaml.d.ts +0 -8
  81. package/dist/formats/yaml.js +0 -1
package/dist/node.d.cts CHANGED
@@ -1,21 +1,35 @@
1
- import { F as Flat, S as SettingsOptions } from './types-B_Dc2L4T.cjs';
1
+ import { F as Flat, S as SettingsOptions } from './types-CcsYDAdO.cjs';
2
2
  import { StandardSchemaV1 } from '@standard-schema/spec';
3
- import { C as ConfigError } from './errors-itGaU4Q8.cjs';
3
+ import { C as ConfigError } from './errors-JkkOM0u-.cjs';
4
4
 
5
5
  /**
6
6
  * Reads dotenv files and joins them with newlines for `{ text }` sources.
7
- * Missing files are skipped; anything else (e.g. `EACCES`) rethrows.
7
+ * Missing files are skipped; anything else (e.g. `EACCES`) rethrows. Total
8
+ * size is capped (default 2 MiB, same as `loadSecretsDir`) so a hostile or
9
+ * symlinked `.env` cannot balloon memory — exceed it and `E_FILE_TOO_LARGE`.
8
10
  */
9
- declare function loadEnvFiles(paths: string[]): {
11
+ declare function loadEnvFiles(paths: string[], opts?: {
12
+ maxBytes?: number;
13
+ }): {
10
14
  text: string;
11
15
  };
16
+ /** Options for {@link loadSecretsDir}. */
17
+ interface LoadSecretsDirOptions {
18
+ /** Keep empty files as `''` instead of skipping them. Default false (skip). */
19
+ preserveEmpty?: boolean;
20
+ /** Per-file read cap in UTF-8 bytes. Default 2 MiB (matches core `maxBytes`). */
21
+ maxBytes?: number;
22
+ }
12
23
  /**
13
24
  * Reads a secrets dir (Docker/K8s `/run/secrets`) into a flat map keyed by
14
- * uppercased filenames. Skips subdirectories, `..*` entries, and empties; strips
15
- * one trailing newline but otherwise preserves whitespace verbatim. Missing dir
16
- * reads as `{}`; unreadable files throw (`EACCES` fails closed).
25
+ * uppercased filenames. Skips subdirectories, `..*` entries, and (by default)
26
+ * empties; strips one trailing newline but otherwise preserves whitespace
27
+ * verbatim. Missing dir reads as `{}`; unreadable files throw (`EACCES` fails
28
+ * closed). Symlinks are confined to the dir (K8s `..data` links resolve inside);
29
+ * links escaping it throw. Case collisions (`Token` vs `TOKEN`) throw instead
30
+ * of depending on readdir order.
17
31
  */
18
- declare function loadSecretsDir(dir: string): Flat;
32
+ declare function loadSecretsDir(dir: string, opts?: LoadSecretsDirOptions): Flat;
19
33
  /**
20
34
  * Parses one dotenv file into a flat map. Missing file reads as `{}`;
21
35
  * anything else (including `EACCES` and syntax errors) rethrows.
@@ -51,4 +65,4 @@ interface WatchHandle<T = unknown> {
51
65
  */
52
66
  declare function watchSettings<S extends StandardSchemaV1>(opts: SettingsOptions<S>, cb?: WatchCallbacks): WatchHandle<StandardSchemaV1.InferOutput<S>>;
53
67
 
54
- export { type WatchCallbacks, type WatchHandle, loadEnvFiles, loadSecretsDir, parseEnvFile, watchSettings };
68
+ export { type LoadSecretsDirOptions, type WatchCallbacks, type WatchHandle, loadEnvFiles, loadSecretsDir, parseEnvFile, watchSettings };
package/dist/node.d.ts CHANGED
@@ -1,21 +1,35 @@
1
- import { F as Flat, S as SettingsOptions } from './types-B_Dc2L4T.js';
1
+ import { F as Flat, S as SettingsOptions } from './types-CcsYDAdO.js';
2
2
  import { StandardSchemaV1 } from '@standard-schema/spec';
3
- import { C as ConfigError } from './errors-itGaU4Q8.js';
3
+ import { C as ConfigError } from './errors-JkkOM0u-.js';
4
4
 
5
5
  /**
6
6
  * Reads dotenv files and joins them with newlines for `{ text }` sources.
7
- * Missing files are skipped; anything else (e.g. `EACCES`) rethrows.
7
+ * Missing files are skipped; anything else (e.g. `EACCES`) rethrows. Total
8
+ * size is capped (default 2 MiB, same as `loadSecretsDir`) so a hostile or
9
+ * symlinked `.env` cannot balloon memory — exceed it and `E_FILE_TOO_LARGE`.
8
10
  */
9
- declare function loadEnvFiles(paths: string[]): {
11
+ declare function loadEnvFiles(paths: string[], opts?: {
12
+ maxBytes?: number;
13
+ }): {
10
14
  text: string;
11
15
  };
16
+ /** Options for {@link loadSecretsDir}. */
17
+ interface LoadSecretsDirOptions {
18
+ /** Keep empty files as `''` instead of skipping them. Default false (skip). */
19
+ preserveEmpty?: boolean;
20
+ /** Per-file read cap in UTF-8 bytes. Default 2 MiB (matches core `maxBytes`). */
21
+ maxBytes?: number;
22
+ }
12
23
  /**
13
24
  * Reads a secrets dir (Docker/K8s `/run/secrets`) into a flat map keyed by
14
- * uppercased filenames. Skips subdirectories, `..*` entries, and empties; strips
15
- * one trailing newline but otherwise preserves whitespace verbatim. Missing dir
16
- * reads as `{}`; unreadable files throw (`EACCES` fails closed).
25
+ * uppercased filenames. Skips subdirectories, `..*` entries, and (by default)
26
+ * empties; strips one trailing newline but otherwise preserves whitespace
27
+ * verbatim. Missing dir reads as `{}`; unreadable files throw (`EACCES` fails
28
+ * closed). Symlinks are confined to the dir (K8s `..data` links resolve inside);
29
+ * links escaping it throw. Case collisions (`Token` vs `TOKEN`) throw instead
30
+ * of depending on readdir order.
17
31
  */
18
- declare function loadSecretsDir(dir: string): Flat;
32
+ declare function loadSecretsDir(dir: string, opts?: LoadSecretsDirOptions): Flat;
19
33
  /**
20
34
  * Parses one dotenv file into a flat map. Missing file reads as `{}`;
21
35
  * anything else (including `EACCES` and syntax errors) rethrows.
@@ -51,4 +65,4 @@ interface WatchHandle<T = unknown> {
51
65
  */
52
66
  declare function watchSettings<S extends StandardSchemaV1>(opts: SettingsOptions<S>, cb?: WatchCallbacks): WatchHandle<StandardSchemaV1.InferOutput<S>>;
53
67
 
54
- export { type WatchCallbacks, type WatchHandle, loadEnvFiles, loadSecretsDir, parseEnvFile, watchSettings };
68
+ export { type LoadSecretsDirOptions, type WatchCallbacks, type WatchHandle, loadEnvFiles, loadSecretsDir, parseEnvFile, watchSettings };
package/dist/node.js CHANGED
@@ -1,7 +1 @@
1
- export { loadEnvFiles, loadSecretsDir, parseEnvFile, watchSettings } from './chunk-DEP5SD4J.js';
2
- import './chunk-YVJEZPPZ.js';
3
- import './chunk-33M5GIKQ.js';
4
- import './chunk-OVFMKC2O.js';
5
- import './chunk-UFUHHR2J.js';
6
- import './chunk-EE6LXNJV.js';
7
- import './chunk-F6DX3HXZ.js';
1
+ export{a as loadEnvFiles,b as loadSecretsDir,c as parseEnvFile,d as watchSettings}from'./chunk-C6CG2D4B.js';import'./chunk-4EAMKDNK.js';import'./chunk-NQDE3WLR.js';
@@ -40,7 +40,11 @@ interface SettingsOptions<S extends StandardSchemaV1 = StandardSchemaV1> {
40
40
  sources?: SourceInput[];
41
41
  /** Global prefix strip (case-insensitive, once). */
42
42
  prefix?: string;
43
- /** Fixed to `'__'`; only double underscores split keys. */
43
+ /**
44
+ * Fixed to `'__'`; only double underscores split keys. Not configurable —
45
+ * the field exists so the constraint is visible (and greppable) at the type
46
+ * level rather than hidden in the key-expansion code.
47
+ */
44
48
  separator?: '__';
45
49
  /** Best-effort leaf coercion before validation. Default true. */
46
50
  coerce?: boolean;
@@ -64,6 +68,24 @@ interface SettingsOptions<S extends StandardSchemaV1 = StandardSchemaV1> {
64
68
  allowUnresolved?: boolean;
65
69
  /** Injectable env snapshot (tests, Edge). Defaults to process.env when available. */
66
70
  env?: Flat;
71
+ /**
72
+ * Extra secret matchers beyond the default (`key|secret|token|password|private`,
73
+ * case-insensitive substring). Strings match case-insensitively as substrings,
74
+ * RegExps test as given. Secret-looking error paths get value redaction.
75
+ */
76
+ secretKeys?: (string | RegExp)[];
77
+ /**
78
+ * Injectable filesystem for file sources (tests, bundlers, Edge shims).
79
+ * Takes precedence over the `typed-settings/node` global hook and the
80
+ * `require('node:fs')` fallback. `statSync` enables a pre-read size check
81
+ * against `maxBytes` so oversized files never fully materialize.
82
+ */
83
+ fs?: {
84
+ readFileSync(path: string, encoding: string): string;
85
+ statSync?(path: string): {
86
+ size: number;
87
+ };
88
+ };
67
89
  }
68
90
 
69
91
  export type { ArrayStrategy as A, Flat as F, SettingsOptions as S, UnknownKeys as U, SecretProvider as a, SourceInput as b };
@@ -40,7 +40,11 @@ interface SettingsOptions<S extends StandardSchemaV1 = StandardSchemaV1> {
40
40
  sources?: SourceInput[];
41
41
  /** Global prefix strip (case-insensitive, once). */
42
42
  prefix?: string;
43
- /** Fixed to `'__'`; only double underscores split keys. */
43
+ /**
44
+ * Fixed to `'__'`; only double underscores split keys. Not configurable —
45
+ * the field exists so the constraint is visible (and greppable) at the type
46
+ * level rather than hidden in the key-expansion code.
47
+ */
44
48
  separator?: '__';
45
49
  /** Best-effort leaf coercion before validation. Default true. */
46
50
  coerce?: boolean;
@@ -64,6 +68,24 @@ interface SettingsOptions<S extends StandardSchemaV1 = StandardSchemaV1> {
64
68
  allowUnresolved?: boolean;
65
69
  /** Injectable env snapshot (tests, Edge). Defaults to process.env when available. */
66
70
  env?: Flat;
71
+ /**
72
+ * Extra secret matchers beyond the default (`key|secret|token|password|private`,
73
+ * case-insensitive substring). Strings match case-insensitively as substrings,
74
+ * RegExps test as given. Secret-looking error paths get value redaction.
75
+ */
76
+ secretKeys?: (string | RegExp)[];
77
+ /**
78
+ * Injectable filesystem for file sources (tests, bundlers, Edge shims).
79
+ * Takes precedence over the `typed-settings/node` global hook and the
80
+ * `require('node:fs')` fallback. `statSync` enables a pre-read size check
81
+ * against `maxBytes` so oversized files never fully materialize.
82
+ */
83
+ fs?: {
84
+ readFileSync(path: string, encoding: string): string;
85
+ statSync?(path: string): {
86
+ size: number;
87
+ };
88
+ };
67
89
  }
68
90
 
69
91
  export type { ArrayStrategy as A, Flat as F, SettingsOptions as S, UnknownKeys as U, SecretProvider as a, SourceInput as b };
@@ -1,217 +1,4 @@
1
- 'use strict';
2
-
3
- // src/errors.ts
4
- function byteLengthUtf8(text) {
5
- try {
6
- const g = globalThis;
7
- if (g.TextEncoder) return new g.TextEncoder().encode(text).length;
8
- if (g.Buffer) return g.Buffer.byteLength(text, "utf8");
9
- } catch {
10
- }
11
- return text.length;
12
- }
13
- var ConfigError = class extends Error {
14
- code = "E_INVALID_CONFIG";
15
- issues;
16
- constructor(issues, code = "E_INVALID_CONFIG") {
17
- const lines = issues.map(
18
- (i) => `- ${i.path} (from ${i.from}): ${i.message}`
19
- );
20
- super(`Invalid config (${issues.length} issue${issues.length === 1 ? "" : "s"}):
21
- ${lines.join("\n")}`);
22
- this.name = "ConfigError";
23
- this.code = code;
24
- this.issues = issues;
25
- }
26
- };
27
-
28
- // src/vault/shared.ts
29
- function normalizeSecretMap(raw, providerName, opts) {
30
- const clean = (v) => {
31
- if (v === null || v === void 0) return void 0;
32
- let s = typeof v === "string" ? v : String(v);
33
- s = s.replace(/\r?\n$/, "");
34
- return s;
35
- };
36
- const out = {};
37
- if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
38
- throw new ConfigError(
39
- [{ path: "<root>", from: providerName, message: "E_SHAPE: provider returned non-object" }],
40
- "E_SHAPE"
41
- );
42
- }
43
- for (const [k, v] of Object.entries(raw)) {
44
- if (v !== null && typeof v === "object" && !Array.isArray(v)) {
45
- out[k] = v;
46
- continue;
47
- }
48
- const c = clean(v);
49
- if (c !== void 0) out[k] = c;
50
- }
51
- return out;
52
- }
53
-
54
- // src/vault/aws.ts
55
- async function withTimeout(p, ms, name) {
56
- let timer;
57
- const timeout = new Promise((_, rej) => {
58
- timer = setTimeout(
59
- () => rej(new ConfigError([{ path: name, from: name, message: "E_TIMEOUT" }], "E_TIMEOUT")),
60
- ms
61
- );
62
- });
63
- try {
64
- return await Promise.race([p, timeout]);
65
- } finally {
66
- clearTimeout(timer);
67
- }
68
- }
69
- function deferred(fn) {
70
- return Promise.resolve().then(fn);
71
- }
72
- function wrapClientError(e, path, from) {
73
- if (e instanceof ConfigError) throw e;
74
- const err = e;
75
- throw new ConfigError(
76
- [{ path, from, message: `E_UPSTREAM: ${err?.message ?? String(e)}` }],
77
- "E_UPSTREAM"
78
- );
79
- }
80
- var BASE64_RE = /^[A-Za-z0-9+/]*={0,2}$/;
81
- function decodeBinary(b, path, from) {
82
- if (typeof b === "string") {
83
- const compact = b.replace(/\s/g, "");
84
- if (!BASE64_RE.test(compact) || compact.length % 4 !== 0) {
85
- throw new ConfigError([{ path, from, message: "E_SHAPE: SecretBinary is not valid base64" }], "E_SHAPE");
86
- }
87
- return Buffer.from(compact, "base64").toString("utf8");
88
- }
89
- return Buffer.from(b).toString("utf8");
90
- }
91
- function awsProvider(opts) {
92
- const name = `aws://${(opts.secrets ?? []).join(",")}${(opts.params ?? []).join(",") || "ssm"}`;
93
- const timeoutMs = opts.timeoutMs ?? 5e3;
94
- const maxSecret = opts.maxBytesSecret ?? 64 * 1024;
95
- const maxParam = opts.maxBytesParam ?? 8 * 1024;
96
- let inflight = null;
97
- async function loadSecrets() {
98
- const out = {};
99
- if (!opts.secrets?.length) return out;
100
- if (!opts.secretsClient) {
101
- throw new ConfigError(
102
- [{ path: "<aws>", from: name, message: "E_NO_SDK: inject secretsClient (peer @aws-sdk/client-secrets-manager)" }],
103
- "E_NO_SDK"
104
- );
105
- }
106
- for (const id of opts.secrets) {
107
- let raw;
108
- try {
109
- raw = await withTimeout(deferred(() => opts.secretsClient.getSecretValue(id)), timeoutMs, name);
110
- } catch (e) {
111
- wrapClientError(e, id, name);
112
- }
113
- if (typeof raw === "string") {
114
- if (byteLengthUtf8(raw) > maxSecret) throw new ConfigError([{ path: id, from: name, message: "E_TOO_LARGE" }], "E_TOO_LARGE");
115
- try {
116
- const parsed = JSON.parse(raw);
117
- if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
118
- Object.assign(out, normalizeSecretMap(parsed, `${name}/${id}`));
119
- continue;
120
- }
121
- } catch {
122
- }
123
- out[lastSegment(id)] = raw.replace(/\r?\n$/, "");
124
- continue;
125
- }
126
- const { SecretString, SecretBinary } = raw;
127
- if (SecretString !== void 0 && SecretBinary !== void 0) {
128
- throw new ConfigError([{ path: id, from: name, message: "E_SHAPE: SecretString XOR SecretBinary" }], "E_SHAPE");
129
- }
130
- if (SecretString === void 0 && SecretBinary === void 0) {
131
- throw new ConfigError([{ path: id, from: name, message: "E_SHAPE: neither SecretString nor SecretBinary present" }], "E_SHAPE");
132
- }
133
- if (SecretString !== void 0) {
134
- if (byteLengthUtf8(SecretString) > maxSecret) throw new ConfigError([{ path: id, from: name, message: "E_TOO_LARGE" }], "E_TOO_LARGE");
135
- try {
136
- const parsed = JSON.parse(SecretString);
137
- if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
138
- Object.assign(out, normalizeSecretMap(parsed, `${name}/${id}`));
139
- continue;
140
- }
141
- } catch {
142
- }
143
- out[lastSegment(id)] = SecretString.replace(/\r?\n$/, "");
144
- } else if (SecretBinary !== void 0) {
145
- const decoded = decodeBinary(SecretBinary, id, name);
146
- if (byteLengthUtf8(decoded) > maxSecret) throw new ConfigError([{ path: id, from: name, message: "E_TOO_LARGE" }], "E_TOO_LARGE");
147
- out[lastSegment(id)] = decoded.replace(/\r?\n$/, "");
148
- }
149
- }
150
- return out;
151
- }
152
- function lastSegment(id) {
153
- const segs = id.split("/").filter((s) => s.length > 0);
154
- return (segs.length > 0 ? segs[segs.length - 1] : id).toUpperCase();
155
- }
156
- function stripParamPrefix(paramName, requested) {
157
- if (!paramName) return "";
158
- let best = "";
159
- for (const r of requested) {
160
- if (!r) continue;
161
- if (paramName === r) {
162
- if (r.endsWith("/")) return "";
163
- return r.split("/").filter((s) => s.length > 0).pop();
164
- }
165
- const dirPrefix = r.endsWith("/") ? r : `${r}/`;
166
- if (paramName.startsWith(dirPrefix) && dirPrefix.length > best.length) best = dirPrefix;
167
- }
168
- const rest = best ? paramName.slice(best.length) : paramName.split("/").filter((s) => s.length > 0).pop() ?? paramName;
169
- return rest.replace(/\//g, "__");
170
- }
171
- async function loadParams() {
172
- const out = {};
173
- if (!opts.params?.length) return out;
174
- if (!opts.ssmClient) {
175
- throw new ConfigError(
176
- [{ path: "<aws>", from: name, message: "E_NO_SDK: inject ssmClient (peer @aws-sdk/client-ssm)" }],
177
- "E_NO_SDK"
178
- );
179
- }
180
- let map;
181
- try {
182
- map = await withTimeout(
183
- deferred(() => opts.ssmClient.getParameters(opts.params, true)),
184
- timeoutMs,
185
- name
186
- );
187
- } catch (e) {
188
- wrapClientError(e, "<ssm>", name);
189
- }
190
- for (const [fullName, value] of Object.entries(map)) {
191
- if (byteLengthUtf8(value) > maxParam) throw new ConfigError([{ path: fullName, from: name, message: "E_TOO_LARGE" }], "E_TOO_LARGE");
192
- const key = stripParamPrefix(fullName, opts.params).toUpperCase();
193
- if (!key) continue;
194
- out[key] = value.replace(/\r?\n$/, "");
195
- }
196
- return out;
197
- }
198
- async function doLoad() {
199
- const [s, p] = await Promise.all([loadSecrets(), loadParams()]);
200
- return { ...s, ...p };
201
- }
202
- void opts.region;
203
- void opts.versionId;
204
- return {
205
- name,
206
- load() {
207
- if (!inflight) {
208
- inflight = doLoad().finally(() => {
209
- inflight = null;
210
- });
211
- }
212
- return inflight;
213
- }
214
- };
215
- }
216
-
217
- exports.awsProvider = awsProvider;
1
+ 'use strict';function h(t){try{let e=globalThis;if(e.TextEncoder)return new e.TextEncoder().encode(t).length;if(e.Buffer)return e.Buffer.byteLength(t,"utf8")}catch{}return t.length}var o=class extends Error{code="E_INVALID_CONFIG";issues;static[Symbol.hasInstance](e){return typeof e=="object"&&e!==null&&e.name==="ConfigError"&&typeof e.code=="string"&&Array.isArray(e.issues)}constructor(e=[],a="E_INVALID_CONFIG"){let f=e.map(u=>`- ${u.path} (from ${u.from}): ${u.message}`);super(e.length===0?"Invalid config: unknown error (no issues attached)":`Invalid config (${e.length} issue${e.length===1?"":"s"}):
2
+ ${f.join(`
3
+ `)}`),this.name="ConfigError",this.code=a,this.issues=e;}};function R(t){return t.toLowerCase()==="__proto__"}function A(t,e,a){let u=l=>{if(l==null)return;let m=typeof l=="string"?l:String(l);return (m=m.replace(/\r?\n$/,"")),m},d={};if(!t||typeof t!="object"||Array.isArray(t))throw new o([{path:"<root>",from:e,message:"E_SHAPE: provider returned non-object"}],"E_SHAPE");for(let[l,m]of Object.entries(t)){if(R(l))throw new o([{path:l,from:e,message:"E_PROTO: forbidden key"}],"E_PROTO");if(m!==null&&typeof m=="object"&&!Array.isArray(m)){d[l]=m;continue}let w=u(m);w!==void 0&&(d[l]=w);}return d}function p(t,e,a){if(!Number.isFinite(t)||t<=0)throw new o([{path:e,from:a,message:`E_USAGE: ${e} must be a positive finite number`}],"E_USAGE")}async function S(t,e,a){let f,u=new Promise((d,l)=>{f=setTimeout(()=>l(new o([{path:a,from:a,message:"E_TIMEOUT"}],"E_TIMEOUT")),e);});t.catch(()=>{});try{return await Promise.race([t,u])}finally{clearTimeout(f);}}function O(t){return Promise.resolve().then(t)}function T(t,e,a){if(t instanceof o)throw t;let f=t;throw new o([{path:e,from:a,message:`E_UPSTREAM: ${f?.message??String(t)}`}],"E_UPSTREAM")}var C=/^[A-Za-z0-9+/]*={0,2}$/;function I(t,e,a){if(typeof t=="string"){let u=t.replace(/\s/g,"");if(!C.test(u)||u.length%4!==0)throw new o([{path:e,from:a,message:"E_SHAPE: SecretBinary is not valid base64"}],"E_SHAPE");return $(u)}let f=globalThis;if(f.Buffer)return f.Buffer.from(t,"binary").toString("utf8");try{return new TextDecoder("utf-8",{fatal:!0}).decode(t)}catch{throw new o([{path:e,from:a,message:"E_SHAPE: SecretBinary is not valid UTF-8"}],"E_SHAPE")}}function $(t){let e=globalThis;if(e.Buffer)return e.Buffer.from(t,"base64").toString("utf8");if(e.atob){let a=e.atob(t),f=new Uint8Array(a.length);for(let u=0;u<a.length;u++)f[u]=a.charCodeAt(u);return new TextDecoder().decode(f)}throw new o([{path:"<aws>",from:"aws",message:"E_NO_SDK: no base64 decoder (Buffer/atob) available"}],"E_NO_SDK")}function N(t){let e=`aws://${(t.secrets??[]).join(",")}${(t.params??[]).join(",")||"ssm"}`,a=t.timeoutMs??5e3,f=t.maxBytesSecret??64*1024,u=t.maxBytesParam??8*1024;p(a,"timeoutMs",e),p(f,"maxBytesSecret",e),p(u,"maxBytesParam",e);let d=null,l;async function m(){let r={};if(!t.secrets?.length)return r;if(!t.secretsClient)throw new o([{path:"<aws>",from:e,message:"E_NO_SDK: inject secretsClient (peer @aws-sdk/client-secrets-manager)"}],"E_NO_SDK");for(let n of t.secrets){let s;try{s=await S(O(()=>t.secretsClient.getSecretValue(n)),l(),e);}catch(i){T(i,n,e);}if(typeof s=="string"){if(h(s)>f)throw new o([{path:n,from:e,message:"E_TOO_LARGE"}],"E_TOO_LARGE");let i;try{i=JSON.parse(s);}catch{i=void 0;}if(i&&typeof i=="object"&&!Array.isArray(i)){y(r,A(i,`${e}/${n}`),`${e}/${n}`);continue}E(r,n,s);continue}let{SecretString:c,SecretBinary:g}=s;if(c!==void 0&&g!==void 0)throw new o([{path:n,from:e,message:"E_SHAPE: SecretString XOR SecretBinary"}],"E_SHAPE");if(c===void 0&&g===void 0)throw new o([{path:n,from:e,message:"E_SHAPE: neither SecretString nor SecretBinary present"}],"E_SHAPE");if(c!==void 0){if(h(c)>f)throw new o([{path:n,from:e,message:"E_TOO_LARGE"}],"E_TOO_LARGE");let i;try{i=JSON.parse(c);}catch{i=void 0;}if(i&&typeof i=="object"&&!Array.isArray(i)){y(r,A(i,`${e}/${n}`),`${e}/${n}`);continue}E(r,n,c);}else if(g!==void 0){let i=I(g,n,e);if(h(i)>f)throw new o([{path:n,from:e,message:"E_TOO_LARGE"}],"E_TOO_LARGE");E(r,n,i);}}return r}function w(r){let n=r.split("/").filter(s=>s.length>0);return (n.length>0?n[n.length-1]:r).toUpperCase()}function E(r,n,s){let c=w(n);c&&_(r,c,s.replace(/\r?\n$/,""),n);}function _(r,n,s,c){if(n in r)throw new o([{path:n,from:c,message:"E_COLLISION: two secrets/params map to the same key \u2014 rename one or narrow the params prefix"}],"E_COLLISION");r[n]=s;}function y(r,n,s){for(let[c,g]of Object.entries(n))_(r,c,g,s);}function P(r,n){if(!r)return "";let s="";for(let g of n){if(!g)continue;if(r===g)return g.endsWith("/")?"":g.split("/").filter(x=>x.length>0).pop();let i=g.endsWith("/")?g:`${g}/`;r.startsWith(i)&&i.length>s.length&&(s=i);}return (s?r.slice(s.length):r.split("/").filter(g=>g.length>0).pop()??r).replace(/\//g,"__")}async function b(){let r={};if(!t.params?.length)return r;if(!t.ssmClient)throw new o([{path:"<aws>",from:e,message:"E_NO_SDK: inject ssmClient (peer @aws-sdk/client-ssm)"}],"E_NO_SDK");let n;try{n=await S(O(()=>t.ssmClient.getParameters(t.params,!0)),l(),e);}catch(s){T(s,"<ssm>",e);}for(let[s,c]of Object.entries(n)){if(h(c)>u)throw new o([{path:s,from:e,message:"E_TOO_LARGE"}],"E_TOO_LARGE");let g=P(s,t.params).toUpperCase();g&&_(r,g,c.replace(/\r?\n$/,""),s);}return r}async function k(){let r=Date.now()+a;l=()=>Math.max(1,r-Date.now());let[n,s]=await Promise.all([m(),b()]),c={...n};return y(c,s,e),c}return t.region,t.versionId,{name:e,load(){return d||(d=k().finally(()=>{d=null;})),d}}}
4
+ exports.awsProvider=N;
@@ -1,4 +1,4 @@
1
- import { a as SecretProvider } from '../types-B_Dc2L4T.cjs';
1
+ import { a as SecretProvider } from '../types-CcsYDAdO.cjs';
2
2
  import '@standard-schema/spec';
3
3
 
4
4
  /**
@@ -1,4 +1,4 @@
1
- import { a as SecretProvider } from '../types-B_Dc2L4T.js';
1
+ import { a as SecretProvider } from '../types-CcsYDAdO.js';
2
2
  import '@standard-schema/spec';
3
3
 
4
4
  /**
package/dist/vault/aws.js CHANGED
@@ -1,167 +1,2 @@
1
- import { normalizeSecretMap } from '../chunk-RQC6OMVV.js';
2
- import { ConfigError, byteLengthUtf8 } from '../chunk-EE6LXNJV.js';
3
-
4
- // src/vault/aws.ts
5
- async function withTimeout(p, ms, name) {
6
- let timer;
7
- const timeout = new Promise((_, rej) => {
8
- timer = setTimeout(
9
- () => rej(new ConfigError([{ path: name, from: name, message: "E_TIMEOUT" }], "E_TIMEOUT")),
10
- ms
11
- );
12
- });
13
- try {
14
- return await Promise.race([p, timeout]);
15
- } finally {
16
- clearTimeout(timer);
17
- }
18
- }
19
- function deferred(fn) {
20
- return Promise.resolve().then(fn);
21
- }
22
- function wrapClientError(e, path, from) {
23
- if (e instanceof ConfigError) throw e;
24
- const err = e;
25
- throw new ConfigError(
26
- [{ path, from, message: `E_UPSTREAM: ${err?.message ?? String(e)}` }],
27
- "E_UPSTREAM"
28
- );
29
- }
30
- var BASE64_RE = /^[A-Za-z0-9+/]*={0,2}$/;
31
- function decodeBinary(b, path, from) {
32
- if (typeof b === "string") {
33
- const compact = b.replace(/\s/g, "");
34
- if (!BASE64_RE.test(compact) || compact.length % 4 !== 0) {
35
- throw new ConfigError([{ path, from, message: "E_SHAPE: SecretBinary is not valid base64" }], "E_SHAPE");
36
- }
37
- return Buffer.from(compact, "base64").toString("utf8");
38
- }
39
- return Buffer.from(b).toString("utf8");
40
- }
41
- function awsProvider(opts) {
42
- const name = `aws://${(opts.secrets ?? []).join(",")}${(opts.params ?? []).join(",") || "ssm"}`;
43
- const timeoutMs = opts.timeoutMs ?? 5e3;
44
- const maxSecret = opts.maxBytesSecret ?? 64 * 1024;
45
- const maxParam = opts.maxBytesParam ?? 8 * 1024;
46
- let inflight = null;
47
- async function loadSecrets() {
48
- const out = {};
49
- if (!opts.secrets?.length) return out;
50
- if (!opts.secretsClient) {
51
- throw new ConfigError(
52
- [{ path: "<aws>", from: name, message: "E_NO_SDK: inject secretsClient (peer @aws-sdk/client-secrets-manager)" }],
53
- "E_NO_SDK"
54
- );
55
- }
56
- for (const id of opts.secrets) {
57
- let raw;
58
- try {
59
- raw = await withTimeout(deferred(() => opts.secretsClient.getSecretValue(id)), timeoutMs, name);
60
- } catch (e) {
61
- wrapClientError(e, id, name);
62
- }
63
- if (typeof raw === "string") {
64
- if (byteLengthUtf8(raw) > maxSecret) throw new ConfigError([{ path: id, from: name, message: "E_TOO_LARGE" }], "E_TOO_LARGE");
65
- try {
66
- const parsed = JSON.parse(raw);
67
- if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
68
- Object.assign(out, normalizeSecretMap(parsed, `${name}/${id}`));
69
- continue;
70
- }
71
- } catch {
72
- }
73
- out[lastSegment(id)] = raw.replace(/\r?\n$/, "");
74
- continue;
75
- }
76
- const { SecretString, SecretBinary } = raw;
77
- if (SecretString !== void 0 && SecretBinary !== void 0) {
78
- throw new ConfigError([{ path: id, from: name, message: "E_SHAPE: SecretString XOR SecretBinary" }], "E_SHAPE");
79
- }
80
- if (SecretString === void 0 && SecretBinary === void 0) {
81
- throw new ConfigError([{ path: id, from: name, message: "E_SHAPE: neither SecretString nor SecretBinary present" }], "E_SHAPE");
82
- }
83
- if (SecretString !== void 0) {
84
- if (byteLengthUtf8(SecretString) > maxSecret) throw new ConfigError([{ path: id, from: name, message: "E_TOO_LARGE" }], "E_TOO_LARGE");
85
- try {
86
- const parsed = JSON.parse(SecretString);
87
- if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
88
- Object.assign(out, normalizeSecretMap(parsed, `${name}/${id}`));
89
- continue;
90
- }
91
- } catch {
92
- }
93
- out[lastSegment(id)] = SecretString.replace(/\r?\n$/, "");
94
- } else if (SecretBinary !== void 0) {
95
- const decoded = decodeBinary(SecretBinary, id, name);
96
- if (byteLengthUtf8(decoded) > maxSecret) throw new ConfigError([{ path: id, from: name, message: "E_TOO_LARGE" }], "E_TOO_LARGE");
97
- out[lastSegment(id)] = decoded.replace(/\r?\n$/, "");
98
- }
99
- }
100
- return out;
101
- }
102
- function lastSegment(id) {
103
- const segs = id.split("/").filter((s) => s.length > 0);
104
- return (segs.length > 0 ? segs[segs.length - 1] : id).toUpperCase();
105
- }
106
- function stripParamPrefix(paramName, requested) {
107
- if (!paramName) return "";
108
- let best = "";
109
- for (const r of requested) {
110
- if (!r) continue;
111
- if (paramName === r) {
112
- if (r.endsWith("/")) return "";
113
- return r.split("/").filter((s) => s.length > 0).pop();
114
- }
115
- const dirPrefix = r.endsWith("/") ? r : `${r}/`;
116
- if (paramName.startsWith(dirPrefix) && dirPrefix.length > best.length) best = dirPrefix;
117
- }
118
- const rest = best ? paramName.slice(best.length) : paramName.split("/").filter((s) => s.length > 0).pop() ?? paramName;
119
- return rest.replace(/\//g, "__");
120
- }
121
- async function loadParams() {
122
- const out = {};
123
- if (!opts.params?.length) return out;
124
- if (!opts.ssmClient) {
125
- throw new ConfigError(
126
- [{ path: "<aws>", from: name, message: "E_NO_SDK: inject ssmClient (peer @aws-sdk/client-ssm)" }],
127
- "E_NO_SDK"
128
- );
129
- }
130
- let map;
131
- try {
132
- map = await withTimeout(
133
- deferred(() => opts.ssmClient.getParameters(opts.params, true)),
134
- timeoutMs,
135
- name
136
- );
137
- } catch (e) {
138
- wrapClientError(e, "<ssm>", name);
139
- }
140
- for (const [fullName, value] of Object.entries(map)) {
141
- if (byteLengthUtf8(value) > maxParam) throw new ConfigError([{ path: fullName, from: name, message: "E_TOO_LARGE" }], "E_TOO_LARGE");
142
- const key = stripParamPrefix(fullName, opts.params).toUpperCase();
143
- if (!key) continue;
144
- out[key] = value.replace(/\r?\n$/, "");
145
- }
146
- return out;
147
- }
148
- async function doLoad() {
149
- const [s, p] = await Promise.all([loadSecrets(), loadParams()]);
150
- return { ...s, ...p };
151
- }
152
- void opts.region;
153
- void opts.versionId;
154
- return {
155
- name,
156
- load() {
157
- if (!inflight) {
158
- inflight = doLoad().finally(() => {
159
- inflight = null;
160
- });
161
- }
162
- return inflight;
163
- }
164
- };
165
- }
166
-
167
- export { awsProvider };
1
+ import {f,c as c$1}from'../chunk-5D2AQWDG.js';import {d,c}from'../chunk-NQDE3WLR.js';async function _(t,r,g){let f,m=new Promise((l,u)=>{f=setTimeout(()=>u(new d([{path:g,from:g,message:"E_TIMEOUT"}],"E_TIMEOUT")),r);});t.catch(()=>{});try{return await Promise.race([t,m])}finally{clearTimeout(f);}}function p(t){return Promise.resolve().then(t)}function P(t,r,g){if(t instanceof d)throw t;let f=t;throw new d([{path:r,from:g,message:`E_UPSTREAM: ${f?.message??String(t)}`}],"E_UPSTREAM")}var C=/^[A-Za-z0-9+/]*={0,2}$/;function x(t,r,g){if(typeof t=="string"){let m=t.replace(/\s/g,"");if(!C.test(m)||m.length%4!==0)throw new d([{path:r,from:g,message:"E_SHAPE: SecretBinary is not valid base64"}],"E_SHAPE");return F(m)}let f=globalThis;if(f.Buffer)return f.Buffer.from(t,"binary").toString("utf8");try{return new TextDecoder("utf-8",{fatal:!0}).decode(t)}catch{throw new d([{path:r,from:g,message:"E_SHAPE: SecretBinary is not valid UTF-8"}],"E_SHAPE")}}function F(t){let r=globalThis;if(r.Buffer)return r.Buffer.from(t,"base64").toString("utf8");if(r.atob){let g=r.atob(t),f=new Uint8Array(g.length);for(let m=0;m<g.length;m++)f[m]=g.charCodeAt(m);return new TextDecoder().decode(f)}throw new d([{path:"<aws>",from:"aws",message:"E_NO_SDK: no base64 decoder (Buffer/atob) available"}],"E_NO_SDK")}function L(t){let r=`aws://${(t.secrets??[]).join(",")}${(t.params??[]).join(",")||"ssm"}`,g=t.timeoutMs??5e3,f$1=t.maxBytesSecret??64*1024,m=t.maxBytesParam??8*1024;f(g,"timeoutMs",r),f(f$1,"maxBytesSecret",r),f(m,"maxBytesParam",r);let l=null,u;async function O(){let n={};if(!t.secrets?.length)return n;if(!t.secretsClient)throw new d([{path:"<aws>",from:r,message:"E_NO_SDK: inject secretsClient (peer @aws-sdk/client-secrets-manager)"}],"E_NO_SDK");for(let e of t.secrets){let s;try{s=await _(p(()=>t.secretsClient.getSecretValue(e)),u(),r);}catch(i){P(i,e,r);}if(typeof s=="string"){if(c(s)>f$1)throw new d([{path:e,from:r,message:"E_TOO_LARGE"}],"E_TOO_LARGE");let i;try{i=JSON.parse(s);}catch{i=void 0;}if(i&&typeof i=="object"&&!Array.isArray(i)){E(n,c$1(i,`${r}/${e}`),`${r}/${e}`);continue}h(n,e,s);continue}let{SecretString:o,SecretBinary:a}=s;if(o!==void 0&&a!==void 0)throw new d([{path:e,from:r,message:"E_SHAPE: SecretString XOR SecretBinary"}],"E_SHAPE");if(o===void 0&&a===void 0)throw new d([{path:e,from:r,message:"E_SHAPE: neither SecretString nor SecretBinary present"}],"E_SHAPE");if(o!==void 0){if(c(o)>f$1)throw new d([{path:e,from:r,message:"E_TOO_LARGE"}],"E_TOO_LARGE");let i;try{i=JSON.parse(o);}catch{i=void 0;}if(i&&typeof i=="object"&&!Array.isArray(i)){E(n,c$1(i,`${r}/${e}`),`${r}/${e}`);continue}h(n,e,o);}else if(a!==void 0){let i=x(a,e,r);if(c(i)>f$1)throw new d([{path:e,from:r,message:"E_TOO_LARGE"}],"E_TOO_LARGE");h(n,e,i);}}return n}function A(n){let e=n.split("/").filter(s=>s.length>0);return (e.length>0?e[e.length-1]:n).toUpperCase()}function h(n,e,s){let o=A(e);o&&S(n,o,s.replace(/\r?\n$/,""),e);}function S(n,e,s,o){if(e in n)throw new d([{path:e,from:o,message:"E_COLLISION: two secrets/params map to the same key \u2014 rename one or narrow the params prefix"}],"E_COLLISION");n[e]=s;}function E(n,e,s){for(let[o,a]of Object.entries(e))S(n,o,a,s);}function T(n,e){if(!n)return "";let s="";for(let a of e){if(!a)continue;if(n===a)return a.endsWith("/")?"":a.split("/").filter(v=>v.length>0).pop();let i=a.endsWith("/")?a:`${a}/`;n.startsWith(i)&&i.length>s.length&&(s=i);}return (s?n.slice(s.length):n.split("/").filter(a=>a.length>0).pop()??n).replace(/\//g,"__")}async function b(){let n={};if(!t.params?.length)return n;if(!t.ssmClient)throw new d([{path:"<aws>",from:r,message:"E_NO_SDK: inject ssmClient (peer @aws-sdk/client-ssm)"}],"E_NO_SDK");let e;try{e=await _(p(()=>t.ssmClient.getParameters(t.params,!0)),u(),r);}catch(s){P(s,"<ssm>",r);}for(let[s,o]of Object.entries(e)){if(c(o)>m)throw new d([{path:s,from:r,message:"E_TOO_LARGE"}],"E_TOO_LARGE");let a=T(s,t.params).toUpperCase();a&&S(n,a,o.replace(/\r?\n$/,""),s);}return n}async function B(){let n=Date.now()+g;u=()=>Math.max(1,n-Date.now());let[e,s]=await Promise.all([O(),b()]),o={...e};return E(o,s,r),o}return t.region,t.versionId,{name:r,load(){return l||(l=B().finally(()=>{l=null;})),l}}}
2
+ export{L as awsProvider};