@griffin-app/griffin-executor 0.1.4 → 0.1.6

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 (52) hide show
  1. package/dist/executor.d.ts.map +1 -1
  2. package/dist/executor.js +41 -24
  3. package/dist/executor.js.map +1 -1
  4. package/dist/executor.test.js +80 -80
  5. package/dist/executor.test.js.map +1 -1
  6. package/dist/index.d.ts +1 -1
  7. package/dist/index.d.ts.map +1 -1
  8. package/dist/index.js +1 -1
  9. package/dist/index.js.map +1 -1
  10. package/dist/secrets/index.d.ts +1 -1
  11. package/dist/secrets/index.d.ts.map +1 -1
  12. package/dist/secrets/index.js +1 -1
  13. package/dist/secrets/index.js.map +1 -1
  14. package/dist/secrets/providers/hub.d.ts +2 -1
  15. package/dist/secrets/providers/hub.d.ts.map +1 -1
  16. package/dist/secrets/providers/hub.js +8 -2
  17. package/dist/secrets/providers/hub.js.map +1 -1
  18. package/dist/secrets/resolver.d.ts +8 -6
  19. package/dist/secrets/resolver.d.ts.map +1 -1
  20. package/dist/secrets/resolver.js +78 -38
  21. package/dist/secrets/resolver.js.map +1 -1
  22. package/dist/secrets/secrets.test.js +12 -42
  23. package/dist/secrets/secrets.test.js.map +1 -1
  24. package/dist/secrets/types.d.ts +3 -2
  25. package/dist/secrets/types.d.ts.map +1 -1
  26. package/dist/secrets/types.js.map +1 -1
  27. package/package.json +3 -3
  28. package/src/executor.test.ts +80 -80
  29. package/src/executor.ts +46 -31
  30. package/src/index.ts +0 -1
  31. package/src/secrets/index.ts +0 -1
  32. package/src/secrets/providers/hub.ts +8 -1
  33. package/src/secrets/resolver.ts +109 -45
  34. package/src/secrets/secrets.test.ts +11 -47
  35. package/src/secrets/types.ts +1 -2
  36. package/dist/secrets/providers/aws.d.ts +0 -61
  37. package/dist/secrets/providers/aws.d.ts.map +0 -1
  38. package/dist/secrets/providers/aws.js +0 -96
  39. package/dist/secrets/providers/aws.js.map +0 -1
  40. package/dist/secrets/providers/vault.d.ts +0 -75
  41. package/dist/secrets/providers/vault.d.ts.map +0 -1
  42. package/dist/secrets/providers/vault.js +0 -143
  43. package/dist/secrets/providers/vault.js.map +0 -1
  44. package/dist/secrets/registry.d.ts +0 -39
  45. package/dist/secrets/registry.d.ts.map +0 -1
  46. package/dist/secrets/registry.js +0 -134
  47. package/dist/secrets/registry.js.map +0 -1
  48. package/dist/test-plan-types.d.ts +0 -43
  49. package/dist/test-plan-types.d.ts.map +0 -1
  50. package/dist/test-plan-types.js +0 -2
  51. package/dist/test-plan-types.js.map +0 -1
  52. package/src/secrets/providers/vault.ts +0 -257
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Secret resolution utilities for test monitors.
3
3
  */
4
- import { type MonitorV1 } from "@griffin-app/griffin-hub-sdk";
4
+ import { SecretOrString, type MonitorV1 } from "@griffin-app/griffin-hub-sdk";
5
5
  import type { SecretProvider } from "./types.js";
6
6
  import type { SecretRef, SecretRefData } from "./types.js";
7
7
  import {
@@ -10,6 +10,29 @@ import {
10
10
  SecretResolutionError,
11
11
  } from "./types.js";
12
12
 
13
+ interface TemplateRef {
14
+ $template: {
15
+ strings: string[];
16
+ values: unknown[];
17
+ };
18
+ }
19
+
20
+ function isTemplateRef(value: unknown): value is TemplateRef {
21
+ if (typeof value !== "object" || value === null) {
22
+ return false;
23
+ }
24
+ const obj = value as Record<string, unknown>;
25
+ if (
26
+ !("$template" in obj) ||
27
+ typeof obj.$template !== "object" ||
28
+ obj.$template === null
29
+ ) {
30
+ return false;
31
+ }
32
+ const tmpl = obj.$template as Record<string, unknown>;
33
+ return Array.isArray(tmpl.strings) && Array.isArray(tmpl.values);
34
+ }
35
+
13
36
  /**
14
37
  * Collected secret references and literals from a monitor.
15
38
  */
@@ -26,6 +49,10 @@ interface CollectedSecrets {
26
49
  path: (string | number)[];
27
50
  value: string;
28
51
  }>;
52
+ /** Paths to templates containing secrets (for partial resolution) */
53
+ templatePaths: Array<{
54
+ path: (string | number)[];
55
+ }>;
29
56
  }
30
57
 
31
58
  /**
@@ -60,6 +87,20 @@ function collectSecretsFromValue(
60
87
  return;
61
88
  }
62
89
 
90
+ if (isTemplateRef(value)) {
91
+ let hasSecrets = false;
92
+ for (const val of value.$template.values) {
93
+ if (isSecretRef(val)) {
94
+ collected.refs.push(val.$secret);
95
+ hasSecrets = true;
96
+ }
97
+ }
98
+ if (hasSecrets) {
99
+ collected.templatePaths.push({ path: [...currentPath] });
100
+ }
101
+ return;
102
+ }
103
+
63
104
  if (Array.isArray(value)) {
64
105
  for (let i = 0; i < value.length; i++) {
65
106
  collectSecretsFromValue(value[i], [...currentPath, i], collected);
@@ -85,6 +126,7 @@ export function collectSecretsFromMonitor(
85
126
  refs: [],
86
127
  paths: [],
87
128
  literalPaths: [],
129
+ templatePaths: [],
88
130
  };
89
131
 
90
132
  for (let nodeIndex = 0; nodeIndex < monitor.nodes.length; nodeIndex++) {
@@ -95,7 +137,17 @@ export function collectSecretsFromMonitor(
95
137
  continue;
96
138
  }
97
139
 
98
- //const endpoint = node;
140
+ // Scan path and base (may contain templates with secrets)
141
+ collectSecretsFromValue(
142
+ node.path,
143
+ ["nodes", nodeIndex, "path"],
144
+ collected,
145
+ );
146
+ collectSecretsFromValue(
147
+ node.base,
148
+ ["nodes", nodeIndex, "base"],
149
+ collected,
150
+ );
99
151
 
100
152
  // Scan headers
101
153
  if (node.headers) {
@@ -182,15 +234,21 @@ function deepClone<T>(value: T): T {
182
234
  export async function resolveSecretsInMonitor(
183
235
  monitor: MonitorV1,
184
236
  provider: SecretProvider | null,
185
- ): Promise<MonitorV1> {
237
+ ): Promise<{ monitor: MonitorV1; hadSecrets: boolean }> {
186
238
  // Collect all secret references and string literals
187
239
  const collected = collectSecretsFromMonitor(monitor);
188
240
 
189
- if (collected.refs.length === 0 && collected.literalPaths.length === 0) {
190
- // No secrets or literals to resolve
191
- return monitor;
241
+ if (
242
+ collected.refs.length === 0 &&
243
+ collected.literalPaths.length === 0 &&
244
+ collected.templatePaths.length === 0
245
+ ) {
246
+ // No secrets, literals, or templates to resolve
247
+ return { monitor, hadSecrets: false };
192
248
  }
193
249
 
250
+ const hadSecrets = collected.refs.length > 0 || collected.templatePaths.length > 0;
251
+
194
252
  // Resolve secrets when we have refs (provider required)
195
253
  if (collected.refs.length > 0 && !provider) {
196
254
  throw new SecretResolutionError(
@@ -218,56 +276,62 @@ export async function resolveSecretsInMonitor(
218
276
  setAtPath(resolvedMonitor, path, value);
219
277
  }
220
278
 
221
- return resolvedMonitor;
222
- }
223
-
224
- /**
225
- * Check if a monitor contains any secret references or string literals that need resolution.
226
- * Useful for short-circuiting resolution when no secrets or literals are present.
227
- */
228
- export function planHasSecrets(monitor: MonitorV1): boolean {
229
- for (const node of monitor.nodes) {
230
- if (node.type !== "HTTP_REQUEST") {
231
- continue;
232
- }
233
-
234
- // Check headers
235
- if (node.headers) {
236
- for (const headerValue of Object.values(node.headers)) {
237
- if (isSecretRef(headerValue) || isStringLiteral(headerValue)) {
238
- return true;
239
- }
240
- }
241
- }
279
+ // Resolve templates that contain secrets
280
+ for (const { path } of collected.templatePaths) {
281
+ const tmpl = getAtPath(resolvedMonitor, path) as TemplateRef;
282
+ if (!isTemplateRef(tmpl)) continue;
242
283
 
243
- // Check body (recursive check)
244
- if (node.body !== undefined && containsSecretOrLiteral(node.body)) {
245
- return true;
246
- }
284
+ const resolved = await resolveTemplateSecrets(tmpl, provider!);
285
+ setAtPath(resolvedMonitor, path, resolved);
247
286
  }
248
287
 
249
- return false;
288
+ return { monitor: resolvedMonitor, hadSecrets };
250
289
  }
251
290
 
252
291
  /**
253
- * Recursively check if a value contains any secret references or string literals.
292
+ * Get a value at a path in an object.
254
293
  */
255
- function containsSecretOrLiteral(value: unknown): boolean {
256
- if (value === null || value === undefined) {
257
- return false;
258
- }
259
-
260
- if (isSecretRef(value) || isStringLiteral(value)) {
261
- return true;
294
+ function getAtPath(obj: unknown, path: (string | number)[]): unknown {
295
+ let current: any = obj;
296
+ for (const key of path) {
297
+ if (current === undefined || current === null) return undefined;
298
+ current = current[key];
262
299
  }
300
+ return current;
301
+ }
263
302
 
264
- if (Array.isArray(value)) {
265
- return value.some(containsSecretOrLiteral);
303
+ /**
304
+ * Resolve secrets in a template using partial application.
305
+ * Returns a plain string if all values resolved, or a reduced $template.
306
+ */
307
+ async function resolveTemplateSecrets(
308
+ tmpl: TemplateRef,
309
+ provider: SecretProvider,
310
+ ): Promise<string | TemplateRef> {
311
+ const { strings, values } = tmpl.$template;
312
+ const newStrings: string[] = [strings[0]];
313
+ const newValues: unknown[] = [];
314
+
315
+ for (let i = 0; i < values.length; i++) {
316
+ const val = values[i];
317
+ if (isSecretRef(val)) {
318
+ const resolved = await provider.resolve(val.$secret.ref, {
319
+ version: val.$secret.version,
320
+ field: val.$secret.field,
321
+ });
322
+ newStrings[newStrings.length - 1] += resolved + strings[i + 1];
323
+ } else if (isStringLiteral(val)) {
324
+ newStrings[newStrings.length - 1] += val.$literal + strings[i + 1];
325
+ } else {
326
+ newValues.push(val);
327
+ newStrings.push(strings[i + 1]);
328
+ }
266
329
  }
267
330
 
268
- if (typeof value === "object") {
269
- return Object.values(value).some(containsSecretOrLiteral);
331
+ if (newValues.length === 0) {
332
+ return newStrings[0];
270
333
  }
271
334
 
272
- return false;
335
+ return { $template: { strings: newStrings, values: newValues } };
273
336
  }
337
+
@@ -5,7 +5,6 @@ import { SecretResolutionError } from "./types.js";
5
5
  import {
6
6
  resolveSecretsInMonitor,
7
7
  collectSecretsFromMonitor,
8
- planHasSecrets,
9
8
  } from "./resolver.js";
10
9
  import { MonitorV1 } from "@griffin-app/griffin-hub-sdk";
11
10
 
@@ -104,8 +103,8 @@ describe("Monitor Secret Resolution", () => {
104
103
  id: "endpoint-1",
105
104
  type: "HTTP_REQUEST",
106
105
  method: "GET",
107
- path: "/api/test",
108
- base: "https://api.example.com",
106
+ path: { $literal: "/api/test" },
107
+ base: { $literal: "https://api.example.com" },
109
108
  response_format: "JSON",
110
109
  headers,
111
110
  body,
@@ -117,41 +116,6 @@ describe("Monitor Secret Resolution", () => {
117
116
  ],
118
117
  }) as MonitorV1;
119
118
 
120
- describe("planHasSecrets", () => {
121
- it("should return false for monitors without secrets or literals", () => {
122
- const monitor = createTestMonitor({ "Content-Type": "application/json" });
123
- expect(planHasSecrets(monitor)).toBe(false);
124
- });
125
-
126
- it("should return true for monitors with secret refs in headers", () => {
127
- const monitor = createTestMonitor({
128
- Authorization: createSecretRef("API_KEY"),
129
- });
130
- expect(planHasSecrets(monitor)).toBe(true);
131
- });
132
-
133
- it("should return true for monitors with secret refs in body", () => {
134
- const monitor = createTestMonitor(undefined, {
135
- token: createSecretRef("TOKEN"),
136
- });
137
- expect(planHasSecrets(monitor)).toBe(true);
138
- });
139
-
140
- it("should return true for monitors with string literals in headers", () => {
141
- const monitor = createTestMonitor({
142
- "Content-Type": createStringLiteral("application/json"),
143
- });
144
- expect(planHasSecrets(monitor)).toBe(true);
145
- });
146
-
147
- it("should return true for monitors with string literals in body", () => {
148
- const monitor = createTestMonitor(undefined, {
149
- type: createStringLiteral("test"),
150
- });
151
- expect(planHasSecrets(monitor)).toBe(true);
152
- });
153
- });
154
-
155
119
  describe("collectSecretsFromMonitor", () => {
156
120
  it("should collect secrets from headers", () => {
157
121
  const monitor = createTestMonitor({
@@ -202,7 +166,7 @@ describe("Monitor Secret Resolution", () => {
202
166
  const collected = collectSecretsFromMonitor(monitor);
203
167
 
204
168
  expect(collected.refs).toHaveLength(0);
205
- expect(collected.literalPaths).toHaveLength(2);
169
+ expect(collected.literalPaths).toHaveLength(4);
206
170
  expect(collected.literalPaths).toContainEqual({
207
171
  path: ["nodes", 0, "headers", "Content-Type"],
208
172
  value: "application/json",
@@ -223,7 +187,7 @@ describe("Monitor Secret Resolution", () => {
223
187
 
224
188
  expect(collected.refs).toHaveLength(1);
225
189
  expect(collected.refs).toContainEqual({ ref: "API_KEY" });
226
- expect(collected.literalPaths).toHaveLength(1);
190
+ expect(collected.literalPaths).toHaveLength(3);
227
191
  expect(collected.literalPaths).toContainEqual({
228
192
  path: ["nodes", 0, "headers", "Content-Type"],
229
193
  value: "application/json",
@@ -242,7 +206,7 @@ describe("Monitor Secret Resolution", () => {
242
206
  env: { API_KEY: "Bearer secret-token" },
243
207
  });
244
208
 
245
- const resolved = await resolveSecretsInMonitor(monitor, provider);
209
+ const { monitor: resolved } = await resolveSecretsInMonitor(monitor, provider);
246
210
 
247
211
  const endpoint = resolved.nodes[0];
248
212
  if (endpoint.type !== "HTTP_REQUEST") {
@@ -262,7 +226,7 @@ describe("Monitor Secret Resolution", () => {
262
226
  env: { TOKEN: "resolved-token" },
263
227
  });
264
228
 
265
- const resolved = await resolveSecretsInMonitor(monitor, provider);
229
+ const { monitor: resolved } = await resolveSecretsInMonitor(monitor, provider);
266
230
 
267
231
  const endpoint = resolved.nodes[0];
268
232
  if (endpoint.type !== "HTTP_REQUEST") {
@@ -281,7 +245,7 @@ describe("Monitor Secret Resolution", () => {
281
245
  env: { API_KEY: "secret" },
282
246
  });
283
247
 
284
- const resolved = await resolveSecretsInMonitor(monitor, provider);
248
+ const { monitor: resolved, hadSecrets } = await resolveSecretsInMonitor(monitor, provider);
285
249
 
286
250
  // Original should still have secret ref
287
251
  const originalEndpoint = monitor.nodes[0];
@@ -328,7 +292,7 @@ describe("Monitor Secret Resolution", () => {
328
292
  Accept: createStringLiteral("application/xml"),
329
293
  });
330
294
 
331
- const resolved = await resolveSecretsInMonitor(monitor, null);
295
+ const { monitor: resolved } = await resolveSecretsInMonitor(monitor, null);
332
296
 
333
297
  const endpoint = resolved.nodes[0];
334
298
  if (endpoint.type !== "HTTP_REQUEST") {
@@ -344,7 +308,7 @@ describe("Monitor Secret Resolution", () => {
344
308
  data: "plain-value",
345
309
  });
346
310
 
347
- const resolved = await resolveSecretsInMonitor(monitor, null);
311
+ const { monitor: resolved } = await resolveSecretsInMonitor(monitor, null);
348
312
 
349
313
  const endpoint = resolved.nodes[0];
350
314
  if (endpoint.type !== "HTTP_REQUEST") {
@@ -364,7 +328,7 @@ describe("Monitor Secret Resolution", () => {
364
328
  env: { API_KEY: "Bearer secret-token" },
365
329
  });
366
330
 
367
- const resolved = await resolveSecretsInMonitor(monitor, provider);
331
+ const { monitor: resolved } = await resolveSecretsInMonitor(monitor, provider);
368
332
 
369
333
  const endpoint = resolved.nodes[0];
370
334
  if (endpoint.type !== "HTTP_REQUEST") {
@@ -379,7 +343,7 @@ describe("Monitor Secret Resolution", () => {
379
343
  "Content-Type": createStringLiteral("application/json"),
380
344
  });
381
345
 
382
- const resolved = await resolveSecretsInMonitor(monitor, null);
346
+ const { monitor: resolved } = await resolveSecretsInMonitor(monitor, null);
383
347
 
384
348
  // Original should still have literal wrapper
385
349
  const originalEndpoint = monitor.nodes[0];
@@ -1,4 +1,3 @@
1
- import { StringLiteral } from "@griffin-app/griffin-hub-sdk";
2
1
 
3
2
  /**
4
3
  * Data structure for a secret reference as it appears in a monitor.
@@ -96,7 +95,7 @@ export function isSecretRef(value: unknown): value is SecretRef {
96
95
  /**
97
96
  * Type guard to check if a value is a string literal.
98
97
  */
99
- export function isStringLiteral(value: unknown): value is StringLiteral {
98
+ export function isStringLiteral(value: unknown): value is { $literal: string } {
100
99
  if (typeof value !== "object" || value === null) {
101
100
  return false;
102
101
  }
@@ -1,61 +0,0 @@
1
- /**
2
- * AWS Secrets Manager secret provider.
3
- *
4
- * Resolves secrets from a single consolidated AWS Secrets Manager secret
5
- * whose value is a JSON KV map. The secret is identified by `secretId`
6
- * (e.g. "orgId/environment") and individual secret names are keys in that map.
7
- *
8
- * Usage in DSL:
9
- * secret("aws:MY_SECRET")
10
- */
11
- import type { SecretProvider, SecretResolveOptions } from "../types.js";
12
- /**
13
- * Interface for AWS Secrets Manager client.
14
- * This allows dependency injection of the actual AWS SDK client.
15
- */
16
- export interface AwsSecretsManagerClient {
17
- getSecretValue(params: {
18
- SecretId: string;
19
- VersionStage?: string;
20
- }): Promise<{
21
- SecretString?: string;
22
- SecretBinary?: Uint8Array;
23
- }>;
24
- }
25
- export interface AwsSecretsManagerProviderOptions {
26
- /**
27
- * AWS Secrets Manager client instance.
28
- * Should be pre-configured with region and credentials.
29
- */
30
- client: AwsSecretsManagerClient;
31
- /**
32
- * The full Secrets Manager secret ID (e.g. "orgId/environment").
33
- * All secrets for this org+env are stored as a JSON KV map in this single secret.
34
- */
35
- secretId: string;
36
- /**
37
- * Default version stage to use if not specified.
38
- * Defaults to "AWSCURRENT".
39
- */
40
- defaultVersionStage?: string;
41
- }
42
- export declare class AwsSecretsManagerProvider implements SecretProvider {
43
- readonly name = "aws";
44
- private readonly client;
45
- private readonly secretId;
46
- private readonly defaultVersionStage;
47
- private cache;
48
- private readonly cacheTtlMs;
49
- constructor(options: AwsSecretsManagerProviderOptions);
50
- resolve(ref: string, options?: SecretResolveOptions): Promise<string>;
51
- /**
52
- * Fetch and cache the entire KV map from the consolidated SM secret.
53
- */
54
- private fetchKvMap;
55
- validate(): Promise<void>;
56
- /**
57
- * Clear the cache. Useful for testing or forced refresh.
58
- */
59
- clearCache(): void;
60
- }
61
- //# sourceMappingURL=aws.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"aws.d.ts","sourceRoot":"","sources":["../../../src/secrets/providers/aws.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAGxE;;;GAGG;AACH,MAAM,WAAW,uBAAuB;IACtC,cAAc,CAAC,MAAM,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,YAAY,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC;QAC3E,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,YAAY,CAAC,EAAE,UAAU,CAAC;KAC3B,CAAC,CAAC;CACJ;AAED,MAAM,WAAW,gCAAgC;IAC/C;;;OAGG;IACH,MAAM,EAAE,uBAAuB,CAAC;IAEhC;;;OAGG;IACH,QAAQ,EAAE,MAAM,CAAC;IAEjB;;;OAGG;IACH,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED,qBAAa,yBAA0B,YAAW,cAAc;IAC9D,QAAQ,CAAC,IAAI,SAAS;IACtB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA0B;IACjD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAS;IAG7C,OAAO,CAAC,KAAK,CACN;IACP,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAiB;gBAEhC,OAAO,EAAE,gCAAgC;IAM/C,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,oBAAoB,GAAG,OAAO,CAAC,MAAM,CAAC;IAgB3E;;OAEG;YACW,UAAU;IAoElB,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAI/B;;OAEG;IACH,UAAU,IAAI,IAAI;CAGnB"}
@@ -1,96 +0,0 @@
1
- /**
2
- * AWS Secrets Manager secret provider.
3
- *
4
- * Resolves secrets from a single consolidated AWS Secrets Manager secret
5
- * whose value is a JSON KV map. The secret is identified by `secretId`
6
- * (e.g. "orgId/environment") and individual secret names are keys in that map.
7
- *
8
- * Usage in DSL:
9
- * secret("aws:MY_SECRET")
10
- */
11
- import { SecretResolutionError } from "../types.js";
12
- export class AwsSecretsManagerProvider {
13
- name = "aws";
14
- client;
15
- secretId;
16
- defaultVersionStage;
17
- // Cache the entire KV map with TTL
18
- cache = null;
19
- cacheTtlMs = 5 * 60 * 1000; // 5 minutes
20
- constructor(options) {
21
- this.client = options.client;
22
- this.secretId = options.secretId;
23
- this.defaultVersionStage = options.defaultVersionStage ?? "AWSCURRENT";
24
- }
25
- async resolve(ref, options) {
26
- const kvMap = await this.fetchKvMap(options?.version ?? this.defaultVersionStage);
27
- const value = kvMap[ref];
28
- if (value === undefined) {
29
- throw new SecretResolutionError(`Secret key "${ref}" not found in consolidated secret "${this.secretId}"`, { ref });
30
- }
31
- return value;
32
- }
33
- /**
34
- * Fetch and cache the entire KV map from the consolidated SM secret.
35
- */
36
- async fetchKvMap(versionStage) {
37
- if (this.cache && this.cache.expires > Date.now()) {
38
- return this.cache.value;
39
- }
40
- try {
41
- const response = await this.client.getSecretValue({
42
- SecretId: this.secretId,
43
- VersionStage: versionStage,
44
- });
45
- if (!response.SecretString) {
46
- throw new SecretResolutionError(`Secret "${this.secretId}" does not contain a string value (binary secrets are not supported)`, { ref: this.secretId });
47
- }
48
- let parsed;
49
- try {
50
- parsed = JSON.parse(response.SecretString);
51
- }
52
- catch {
53
- throw new SecretResolutionError(`Secret "${this.secretId}" is not valid JSON`, { ref: this.secretId });
54
- }
55
- if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
56
- throw new SecretResolutionError(`Secret "${this.secretId}" is not a JSON object`, { ref: this.secretId });
57
- }
58
- const kvMap = parsed;
59
- this.cache = {
60
- value: kvMap,
61
- expires: Date.now() + this.cacheTtlMs,
62
- };
63
- return kvMap;
64
- }
65
- catch (error) {
66
- if (error instanceof SecretResolutionError) {
67
- throw error;
68
- }
69
- const awsError = error;
70
- let message = `Failed to retrieve secret "${this.secretId}"`;
71
- if (awsError.name === "ResourceNotFoundException") {
72
- message = `Secret "${this.secretId}" not found in AWS Secrets Manager`;
73
- }
74
- else if (awsError.name === "AccessDeniedException") {
75
- message = `Access denied to secret "${this.secretId}". Check IAM permissions.`;
76
- }
77
- else if (awsError.message) {
78
- message = `${message}: ${awsError.message}`;
79
- }
80
- throw new SecretResolutionError(message, {
81
- ref: this.secretId,
82
- cause: error,
83
- });
84
- }
85
- }
86
- async validate() {
87
- // Validation happens on first secret access
88
- }
89
- /**
90
- * Clear the cache. Useful for testing or forced refresh.
91
- */
92
- clearCache() {
93
- this.cache = null;
94
- }
95
- }
96
- //# sourceMappingURL=aws.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"aws.js","sourceRoot":"","sources":["../../../src/secrets/providers/aws.ts"],"names":[],"mappings":"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"}
@@ -1,75 +0,0 @@
1
- /**
2
- * HashiCorp Vault secret provider.
3
- *
4
- * Reads secrets from HashiCorp Vault KV secrets engine (v1 and v2).
5
- * Supports field extraction from JSON secrets.
6
- *
7
- * Usage in DSL:
8
- * secret("vault:secret/data/myapp/config")
9
- * secret("vault:secret/data/myapp/config", { field: "api_key" })
10
- * secret("vault:secret/data/myapp/config", { version: "2" })
11
- */
12
- import type { SecretProvider, SecretResolveOptions } from "../types.js";
13
- /**
14
- * HTTP client interface for Vault API calls.
15
- * This allows dependency injection without requiring a specific HTTP library.
16
- */
17
- export interface VaultHttpClient {
18
- get(url: string, options: {
19
- headers: Record<string, string>;
20
- }): Promise<{
21
- status: number;
22
- data: unknown;
23
- }>;
24
- }
25
- export interface VaultProviderOptions {
26
- /**
27
- * Vault server address (e.g., "https://vault.example.com:8200").
28
- */
29
- address: string;
30
- /**
31
- * Authentication token.
32
- */
33
- token: string;
34
- /**
35
- * HTTP client for making requests to Vault.
36
- */
37
- httpClient: VaultHttpClient;
38
- /**
39
- * Optional namespace for Vault Enterprise.
40
- */
41
- namespace?: string;
42
- /**
43
- * KV secrets engine version (1 or 2).
44
- * Defaults to 2.
45
- */
46
- kvVersion?: 1 | 2;
47
- /**
48
- * Optional prefix for secret paths.
49
- */
50
- prefix?: string;
51
- }
52
- export declare class VaultProvider implements SecretProvider {
53
- readonly name = "vault";
54
- private readonly address;
55
- private readonly token;
56
- private readonly httpClient;
57
- private readonly namespace?;
58
- private readonly kvVersion;
59
- private readonly prefix;
60
- private cache;
61
- private readonly cacheTtlMs;
62
- constructor(options: VaultProviderOptions);
63
- resolve(ref: string, options?: SecretResolveOptions): Promise<string>;
64
- /**
65
- * Extract a field from the secret data.
66
- * If no field is specified, returns the entire data as JSON string.
67
- */
68
- private extractField;
69
- validate(): Promise<void>;
70
- /**
71
- * Clear the cache. Useful for testing or forced refresh.
72
- */
73
- clearCache(): void;
74
- }
75
- //# sourceMappingURL=vault.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"vault.d.ts","sourceRoot":"","sources":["../../../src/secrets/providers/vault.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAGxE;;;GAGG;AACH,MAAM,WAAW,eAAe;IAC9B,GAAG,CACD,GAAG,EAAE,MAAM,EACX,OAAO,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;KAAE,GAC3C,OAAO,CAAC;QACT,MAAM,EAAE,MAAM,CAAC;QACf,IAAI,EAAE,OAAO,CAAC;KACf,CAAC,CAAC;CACJ;AAED,MAAM,WAAW,oBAAoB;IACnC;;OAEG;IACH,OAAO,EAAE,MAAM,CAAC;IAEhB;;OAEG;IACH,KAAK,EAAE,MAAM,CAAC;IAEd;;OAEG;IACH,UAAU,EAAE,eAAe,CAAC;IAE5B;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB;;;OAGG;IACH,SAAS,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC;IAElB;;OAEG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,qBAAa,aAAc,YAAW,cAAc;IAClD,QAAQ,CAAC,IAAI,WAAW;IACxB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;IAC/B,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAkB;IAC7C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAQ;IAClC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAGhC,OAAO,CAAC,KAAK,CAGT;IACJ,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAiB;gBAEhC,OAAO,EAAE,oBAAoB;IASnC,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,oBAAoB,GAAG,OAAO,CAAC,MAAM,CAAC;IAoG3E;;;OAGG;IACH,OAAO,CAAC,YAAY;IAuBd,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAoC/B;;OAEG;IACH,UAAU,IAAI,IAAI;CAGnB"}