@griffin-app/griffin-plan-executor 0.1.13 → 0.1.15

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 (68) hide show
  1. package/README.md +14 -14
  2. package/dist/events/adapters/in-memory.test.js +25 -23
  3. package/dist/events/adapters/in-memory.test.js.map +1 -1
  4. package/dist/events/adapters/kinesis.d.ts.map +1 -1
  5. package/dist/events/adapters/kinesis.js.map +1 -1
  6. package/dist/events/adapters/kinesis.test.js +22 -20
  7. package/dist/events/adapters/kinesis.test.js.map +1 -1
  8. package/dist/events/emitter.test.js +15 -15
  9. package/dist/events/emitter.test.js.map +1 -1
  10. package/dist/events/types.d.ts +12 -12
  11. package/dist/events/types.d.ts.map +1 -1
  12. package/dist/events/types.js +1 -1
  13. package/dist/executor.d.ts +2 -2
  14. package/dist/executor.d.ts.map +1 -1
  15. package/dist/executor.js +33 -43
  16. package/dist/executor.js.map +1 -1
  17. package/dist/executor.test.js +102 -102
  18. package/dist/executor.test.js.map +1 -1
  19. package/dist/index.d.ts +4 -4
  20. package/dist/index.d.ts.map +1 -1
  21. package/dist/index.js +2 -2
  22. package/dist/index.js.map +1 -1
  23. package/dist/secrets/index.d.ts +4 -4
  24. package/dist/secrets/index.d.ts.map +1 -1
  25. package/dist/secrets/index.js +4 -4
  26. package/dist/secrets/index.js.map +1 -1
  27. package/dist/secrets/providers/aws.d.ts.map +1 -1
  28. package/dist/secrets/providers/aws.js +4 -5
  29. package/dist/secrets/providers/aws.js.map +1 -1
  30. package/dist/secrets/providers/env.js +1 -1
  31. package/dist/secrets/providers/env.js.map +1 -1
  32. package/dist/secrets/providers/vault.js +7 -7
  33. package/dist/secrets/providers/vault.js.map +1 -1
  34. package/dist/secrets/registry.d.ts +11 -33
  35. package/dist/secrets/registry.d.ts.map +1 -1
  36. package/dist/secrets/registry.js +65 -113
  37. package/dist/secrets/registry.js.map +1 -1
  38. package/dist/secrets/resolver.d.ts +12 -12
  39. package/dist/secrets/resolver.d.ts.map +1 -1
  40. package/dist/secrets/resolver.js +21 -21
  41. package/dist/secrets/resolver.js.map +1 -1
  42. package/dist/secrets/secrets.test.js +96 -120
  43. package/dist/secrets/secrets.test.js.map +1 -1
  44. package/dist/secrets/types.d.ts +2 -5
  45. package/dist/secrets/types.d.ts.map +1 -1
  46. package/dist/secrets/types.js +1 -4
  47. package/dist/secrets/types.js.map +1 -1
  48. package/dist/types.d.ts +2 -2
  49. package/package.json +4 -4
  50. package/src/events/adapters/README.md +7 -7
  51. package/src/events/adapters/in-memory.test.ts +27 -23
  52. package/src/events/adapters/kinesis.test.ts +23 -21
  53. package/src/events/adapters/kinesis.ts +6 -3
  54. package/src/events/emitter.test.ts +15 -15
  55. package/src/events/types.ts +13 -13
  56. package/src/executor.test.ts +103 -103
  57. package/src/executor.ts +40 -48
  58. package/src/index.ts +7 -7
  59. package/src/secrets/index.ts +5 -5
  60. package/src/secrets/providers/aws.ts +4 -5
  61. package/src/secrets/providers/env.ts +1 -1
  62. package/src/secrets/providers/vault.ts +7 -7
  63. package/src/secrets/registry.ts +75 -142
  64. package/src/secrets/resolver.ts +28 -26
  65. package/src/secrets/secrets.test.ts +124 -155
  66. package/src/secrets/types.ts +4 -13
  67. package/src/{test-plan-types.ts → test-monitor-types.ts} +1 -1
  68. package/src/types.ts +2 -2
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Secret provider registry for managing multiple secret providers.
2
+ * Secret provider registry for managing the configured secret provider.
3
3
  */
4
4
 
5
5
  import type {
@@ -10,79 +10,34 @@ import type {
10
10
  import { SecretResolutionError } from "./types.js";
11
11
 
12
12
  /**
13
- * Registry for managing and accessing secret providers.
14
- * Supports multiple providers simultaneously (e.g., env + aws + vault).
13
+ * Registry for the single configured secret provider.
15
14
  */
16
15
  export class SecretProviderRegistry {
17
- private providers = new Map<string, SecretProvider>();
16
+ private provider: SecretProvider | null = null;
18
17
 
19
18
  /**
20
- * Register a secret provider.
21
- * @param provider - The provider to register
22
- * @throws Error if a provider with the same name is already registered
19
+ * Set the secret provider.
20
+ * @param provider - The provider to use for resolution
23
21
  */
24
- register(provider: SecretProvider): void {
25
- if (this.providers.has(provider.name)) {
26
- throw new Error(
27
- `Secret provider "${provider.name}" is already registered`,
28
- );
29
- }
30
- this.providers.set(provider.name, provider);
22
+ setProvider(provider: SecretProvider): void {
23
+ this.provider = provider;
31
24
  }
32
25
 
33
26
  /**
34
- * Unregister a secret provider by name.
35
- * @param name - The provider name to remove
36
- * @returns true if the provider was removed, false if it wasn't registered
37
- */
38
- unregister(name: string): boolean {
39
- return this.providers.delete(name);
40
- }
41
-
42
- /**
43
- * Get a registered provider by name.
44
- * @param name - The provider name
45
- * @throws Error if the provider is not registered
46
- */
47
- get(name: string): SecretProvider {
48
- const provider = this.providers.get(name);
49
- if (!provider) {
50
- const available = [...this.providers.keys()];
51
- throw new SecretResolutionError(
52
- `Secret provider "${name}" is not configured. Available providers: ${
53
- available.length > 0 ? available.join(", ") : "(none)"
54
- }`,
55
- { provider: name, ref: "" },
56
- );
57
- }
58
- return provider;
59
- }
60
-
61
- /**
62
- * Check if a provider is registered.
63
- */
64
- has(name: string): boolean {
65
- return this.providers.has(name);
66
- }
67
-
68
- /**
69
- * Get all registered provider names.
70
- */
71
- getProviderNames(): string[] {
72
- return [...this.providers.keys()];
73
- }
74
-
75
- /**
76
- * Resolve a secret reference using the appropriate provider.
27
+ * Resolve a secret reference using the configured provider.
77
28
  * @param secretRef - The secret reference data
78
29
  * @returns The resolved secret value
79
30
  * @throws SecretResolutionError if resolution fails
80
31
  */
81
32
  async resolve(secretRef: SecretRefData): Promise<string> {
82
- const provider = this.get(secretRef.provider);
33
+ if (!this.provider) {
34
+ throw new SecretResolutionError("No secret provider configured", {
35
+ ref: secretRef.ref,
36
+ });
37
+ }
83
38
 
84
39
  try {
85
- return await provider.resolve(secretRef.ref, {
40
+ return await this.provider.resolve(secretRef.ref, {
86
41
  version: secretRef.version,
87
42
  field: secretRef.field,
88
43
  });
@@ -91,11 +46,10 @@ export class SecretProviderRegistry {
91
46
  throw error;
92
47
  }
93
48
  throw new SecretResolutionError(
94
- `Failed to resolve secret "${secretRef.provider}:${secretRef.ref}": ${
49
+ `Failed to resolve secret "${secretRef.ref}": ${
95
50
  error instanceof Error ? error.message : String(error)
96
51
  }`,
97
52
  {
98
- provider: secretRef.provider,
99
53
  ref: secretRef.ref,
100
54
  cause: error,
101
55
  },
@@ -104,9 +58,9 @@ export class SecretProviderRegistry {
104
58
  }
105
59
 
106
60
  /**
107
- * Resolve multiple secrets, grouped by provider for efficiency.
61
+ * Resolve multiple secrets using the configured provider.
108
62
  * @param refs - Array of secret reference data
109
- * @returns Map of "provider:ref" to resolved value
63
+ * @returns Map of key to resolved value
110
64
  * @throws SecretResolutionError if any resolution fails (fail-fast)
111
65
  */
112
66
  async resolveMany(refs: SecretRefData[]): Promise<Map<string, string>> {
@@ -114,88 +68,69 @@ export class SecretProviderRegistry {
114
68
  return new Map();
115
69
  }
116
70
 
117
- // Group refs by provider
118
- const byProvider = new Map<
119
- string,
120
- Array<{ ref: string; options?: SecretResolveOptions; key: string }>
121
- >();
122
-
123
- for (const secretRef of refs) {
124
- const key = this.makeKey(secretRef);
125
- const group = byProvider.get(secretRef.provider) || [];
126
- group.push({
127
- ref: secretRef.ref,
128
- options: {
129
- version: secretRef.version,
130
- field: secretRef.field,
131
- },
132
- key,
71
+ if (!this.provider) {
72
+ throw new SecretResolutionError("No secret provider configured", {
73
+ ref: refs[0].ref,
133
74
  });
134
- byProvider.set(secretRef.provider, group);
135
75
  }
136
76
 
137
- // Resolve each provider's secrets
138
77
  const results = new Map<string, string>();
139
78
 
140
- for (const [providerName, providerRefs] of byProvider) {
141
- const provider = this.get(providerName);
142
-
143
- // Use batch resolution if available, otherwise resolve individually
144
- if (provider.resolveMany) {
145
- const batchRefs = providerRefs.map((r) => ({
146
- ref: r.ref,
147
- options: r.options,
148
- }));
79
+ if (this.provider.resolveMany) {
80
+ const batchRefs = refs.map((r) => ({
81
+ ref: r.ref,
82
+ options: {
83
+ version: r.version,
84
+ field: r.field,
85
+ } as SecretResolveOptions,
86
+ }));
149
87
 
150
- try {
151
- const batchResults = await provider.resolveMany(batchRefs);
88
+ try {
89
+ const batchResults = await this.provider.resolveMany(batchRefs);
152
90
 
153
- for (const providerRef of providerRefs) {
154
- const value = batchResults.get(providerRef.ref);
155
- if (value === undefined) {
156
- throw new SecretResolutionError(
157
- `Secret "${providerName}:${providerRef.ref}" not found in batch results`,
158
- { provider: providerName, ref: providerRef.ref },
159
- );
160
- }
161
- results.set(providerRef.key, value);
91
+ for (const secretRef of refs) {
92
+ const value = batchResults.get(secretRef.ref);
93
+ if (value === undefined) {
94
+ throw new SecretResolutionError(
95
+ `Secret "${secretRef.ref}" not found in batch results`,
96
+ { ref: secretRef.ref },
97
+ );
162
98
  }
99
+ results.set(this.makeKey(secretRef), value);
100
+ }
101
+ } catch (error) {
102
+ if (error instanceof SecretResolutionError) {
103
+ throw error;
104
+ }
105
+ throw new SecretResolutionError(
106
+ `Batch resolution failed: ${
107
+ error instanceof Error ? error.message : String(error)
108
+ }`,
109
+ {
110
+ ref: refs[0].ref,
111
+ cause: error,
112
+ },
113
+ );
114
+ }
115
+ } else {
116
+ for (const secretRef of refs) {
117
+ try {
118
+ const value = await this.provider.resolve(secretRef.ref, {
119
+ version: secretRef.version,
120
+ field: secretRef.field,
121
+ });
122
+ results.set(this.makeKey(secretRef), value);
163
123
  } catch (error) {
164
124
  if (error instanceof SecretResolutionError) {
165
125
  throw error;
166
126
  }
167
127
  throw new SecretResolutionError(
168
- `Batch resolution failed for provider "${providerName}": ${
128
+ `Failed to resolve secret "${secretRef.ref}": ${
169
129
  error instanceof Error ? error.message : String(error)
170
130
  }`,
171
- {
172
- provider: providerName,
173
- ref: providerRefs[0]?.ref || "",
174
- cause: error,
175
- },
131
+ { ref: secretRef.ref, cause: error },
176
132
  );
177
133
  }
178
- } else {
179
- // Resolve individually (fail-fast on first error)
180
- for (const providerRef of providerRefs) {
181
- try {
182
- const value = await provider.resolve(
183
- providerRef.ref,
184
- providerRef.options,
185
- );
186
- results.set(providerRef.key, value);
187
- } catch (error) {
188
- if (error instanceof SecretResolutionError) {
189
- throw error;
190
- }
191
- throw new SecretResolutionError(
192
- `Failed to resolve secret "${providerName}:${providerRef.ref}": ${
193
- error instanceof Error ? error.message : String(error)
194
- }`,
195
- { provider: providerName, ref: providerRef.ref, cause: error },
196
- );
197
- }
198
- }
199
134
  }
200
135
  }
201
136
 
@@ -203,21 +138,19 @@ export class SecretProviderRegistry {
203
138
  }
204
139
 
205
140
  /**
206
- * Validate all registered providers.
207
- * @throws Error if any provider validation fails
141
+ * Validate the configured provider.
142
+ * @throws Error if provider validation fails
208
143
  */
209
144
  async validateAll(): Promise<void> {
210
- for (const [name, provider] of this.providers) {
211
- if (provider.validate) {
212
- try {
213
- await provider.validate();
214
- } catch (error) {
215
- throw new Error(
216
- `Provider "${name}" validation failed: ${
217
- error instanceof Error ? error.message : String(error)
218
- }`,
219
- );
220
- }
145
+ if (this.provider?.validate) {
146
+ try {
147
+ await this.provider.validate();
148
+ } catch (error) {
149
+ throw new Error(
150
+ `Provider validation failed: ${
151
+ error instanceof Error ? error.message : String(error)
152
+ }`,
153
+ );
221
154
  }
222
155
  }
223
156
  }
@@ -226,7 +159,7 @@ export class SecretProviderRegistry {
226
159
  * Create a unique key for a secret reference (for caching/deduplication).
227
160
  */
228
161
  makeKey(secretRef: SecretRefData): string {
229
- const parts = [secretRef.provider, secretRef.ref];
162
+ const parts = [secretRef.ref];
230
163
  if (secretRef.version) parts.push(`v:${secretRef.version}`);
231
164
  if (secretRef.field) parts.push(`f:${secretRef.field}`);
232
165
  return parts.join(":");
@@ -1,13 +1,13 @@
1
1
  /**
2
- * Secret resolution utilities for test plans.
2
+ * Secret resolution utilities for test monitors.
3
3
  */
4
- import { type PlanV1 } from "@griffin-app/griffin-hub-sdk";
4
+ import { type MonitorV1 } from "@griffin-app/griffin-hub-sdk";
5
5
  import type { SecretProviderRegistry } from "./registry.js";
6
6
  import type { SecretRef, SecretRefData } from "./types.js";
7
7
  import { isSecretRef, isStringLiteral } from "./types.js";
8
8
 
9
9
  /**
10
- * Collected secret references and literals from a plan.
10
+ * Collected secret references and literals from a monitor.
11
11
  */
12
12
  interface CollectedSecrets {
13
13
  /** All unique secret references found */
@@ -71,18 +71,20 @@ function collectSecretsFromValue(
71
71
  }
72
72
 
73
73
  /**
74
- * Collect all secret references and string literals from a test plan.
74
+ * Collect all secret references and string literals from a test monitor.
75
75
  * Scans endpoint headers and bodies for $secret markers and $literal wrappers.
76
76
  */
77
- export function collectSecretsFromPlan(plan: PlanV1): CollectedSecrets {
77
+ export function collectSecretsFromMonitor(
78
+ monitor: MonitorV1,
79
+ ): CollectedSecrets {
78
80
  const collected: CollectedSecrets = {
79
81
  refs: [],
80
82
  paths: [],
81
83
  literalPaths: [],
82
84
  };
83
85
 
84
- for (let nodeIndex = 0; nodeIndex < plan.nodes.length; nodeIndex++) {
85
- const node = plan.nodes[nodeIndex];
86
+ for (let nodeIndex = 0; nodeIndex < monitor.nodes.length; nodeIndex++) {
87
+ const node = monitor.nodes[nodeIndex];
86
88
 
87
89
  // Only endpoints can have secrets (in headers and body)
88
90
  if (node.type !== "HTTP_REQUEST") {
@@ -117,7 +119,7 @@ export function collectSecretsFromPlan(plan: PlanV1): CollectedSecrets {
117
119
  const uniqueRefs: SecretRefData[] = [];
118
120
 
119
121
  for (const ref of collected.refs) {
120
- const key = `${ref.provider}:${ref.ref}:${ref.version || ""}:${ref.field || ""}`;
122
+ const key = `${ref.ref}:${ref.version || ""}:${ref.field || ""}`;
121
123
  if (!seen.has(key)) {
122
124
  seen.add(key);
123
125
  uniqueRefs.push(ref);
@@ -165,24 +167,24 @@ function deepClone<T>(value: T): T {
165
167
  }
166
168
 
167
169
  /**
168
- * Resolve all secrets and unwrap string literals in a plan and return a new plan with substituted values.
169
- * The original plan is not modified.
170
+ * Resolve all secrets and unwrap string literals in a monitor and return a new monitor with substituted values.
171
+ * The original monitor is not modified.
170
172
  *
171
- * @param plan - The test plan containing secret references and string literals
173
+ * @param monitor - The test monitor containing secret references and string literals
172
174
  * @param registry - The secret provider registry
173
- * @returns A new plan with all secrets resolved to their values and literals unwrapped
175
+ * @returns A new monitor with all secrets resolved to their values and literals unwrapped
174
176
  * @throws SecretResolutionError if any secret cannot be resolved (fail-fast)
175
177
  */
176
- export async function resolveSecretsInPlan(
177
- plan: PlanV1,
178
+ export async function resolveSecretsInMonitor(
179
+ monitor: MonitorV1,
178
180
  registry: SecretProviderRegistry,
179
- ): Promise<PlanV1> {
181
+ ): Promise<MonitorV1> {
180
182
  // Collect all secret references and string literals
181
- const collected = collectSecretsFromPlan(plan);
183
+ const collected = collectSecretsFromMonitor(monitor);
182
184
 
183
185
  if (collected.refs.length === 0 && collected.literalPaths.length === 0) {
184
186
  // No secrets or literals to resolve
185
- return plan;
187
+ return monitor;
186
188
  }
187
189
 
188
190
  // Resolve all secrets (fail-fast on any error)
@@ -191,8 +193,8 @@ export async function resolveSecretsInPlan(
191
193
  ? await registry.resolveMany(collected.refs)
192
194
  : new Map();
193
195
 
194
- // Clone the plan for modification
195
- const resolvedPlan = deepClone(plan);
196
+ // Clone the monitor for modification
197
+ const resolvedMonitor = deepClone(monitor);
196
198
 
197
199
  // Substitute resolved secret values at each path
198
200
  for (const { path, secretRef } of collected.paths) {
@@ -202,27 +204,27 @@ export async function resolveSecretsInPlan(
202
204
  if (value === undefined) {
203
205
  // This shouldn't happen if resolveMany worked correctly
204
206
  throw new Error(
205
- `Internal error: resolved value not found for secret "${secretRef.provider}:${secretRef.ref}"`,
207
+ `Internal error: resolved value not found for secret "${secretRef.ref}"`,
206
208
  );
207
209
  }
208
210
 
209
- setAtPath(resolvedPlan, path, value);
211
+ setAtPath(resolvedMonitor, path, value);
210
212
  }
211
213
 
212
214
  // Unwrap string literals at each path
213
215
  for (const { path, value } of collected.literalPaths) {
214
- setAtPath(resolvedPlan, path, value);
216
+ setAtPath(resolvedMonitor, path, value);
215
217
  }
216
218
 
217
- return resolvedPlan;
219
+ return resolvedMonitor;
218
220
  }
219
221
 
220
222
  /**
221
- * Check if a plan contains any secret references or string literals that need resolution.
223
+ * Check if a monitor contains any secret references or string literals that need resolution.
222
224
  * Useful for short-circuiting resolution when no secrets or literals are present.
223
225
  */
224
- export function planHasSecrets(plan: PlanV1): boolean {
225
- for (const node of plan.nodes) {
226
+ export function planHasSecrets(monitor: MonitorV1): boolean {
227
+ for (const node of monitor.nodes) {
226
228
  if (node.type !== "HTTP_REQUEST") {
227
229
  continue;
228
230
  }