@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.
- package/README.md +14 -14
- package/dist/events/adapters/in-memory.test.js +25 -23
- package/dist/events/adapters/in-memory.test.js.map +1 -1
- package/dist/events/adapters/kinesis.d.ts.map +1 -1
- package/dist/events/adapters/kinesis.js.map +1 -1
- package/dist/events/adapters/kinesis.test.js +22 -20
- package/dist/events/adapters/kinesis.test.js.map +1 -1
- package/dist/events/emitter.test.js +15 -15
- package/dist/events/emitter.test.js.map +1 -1
- package/dist/events/types.d.ts +12 -12
- package/dist/events/types.d.ts.map +1 -1
- package/dist/events/types.js +1 -1
- package/dist/executor.d.ts +2 -2
- package/dist/executor.d.ts.map +1 -1
- package/dist/executor.js +33 -43
- package/dist/executor.js.map +1 -1
- package/dist/executor.test.js +102 -102
- package/dist/executor.test.js.map +1 -1
- package/dist/index.d.ts +4 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/secrets/index.d.ts +4 -4
- package/dist/secrets/index.d.ts.map +1 -1
- package/dist/secrets/index.js +4 -4
- package/dist/secrets/index.js.map +1 -1
- package/dist/secrets/providers/aws.d.ts.map +1 -1
- package/dist/secrets/providers/aws.js +4 -5
- package/dist/secrets/providers/aws.js.map +1 -1
- package/dist/secrets/providers/env.js +1 -1
- package/dist/secrets/providers/env.js.map +1 -1
- package/dist/secrets/providers/vault.js +7 -7
- package/dist/secrets/providers/vault.js.map +1 -1
- package/dist/secrets/registry.d.ts +11 -33
- package/dist/secrets/registry.d.ts.map +1 -1
- package/dist/secrets/registry.js +65 -113
- package/dist/secrets/registry.js.map +1 -1
- package/dist/secrets/resolver.d.ts +12 -12
- package/dist/secrets/resolver.d.ts.map +1 -1
- package/dist/secrets/resolver.js +21 -21
- package/dist/secrets/resolver.js.map +1 -1
- package/dist/secrets/secrets.test.js +96 -120
- package/dist/secrets/secrets.test.js.map +1 -1
- package/dist/secrets/types.d.ts +2 -5
- package/dist/secrets/types.d.ts.map +1 -1
- package/dist/secrets/types.js +1 -4
- package/dist/secrets/types.js.map +1 -1
- package/dist/types.d.ts +2 -2
- package/package.json +4 -4
- package/src/events/adapters/README.md +7 -7
- package/src/events/adapters/in-memory.test.ts +27 -23
- package/src/events/adapters/kinesis.test.ts +23 -21
- package/src/events/adapters/kinesis.ts +6 -3
- package/src/events/emitter.test.ts +15 -15
- package/src/events/types.ts +13 -13
- package/src/executor.test.ts +103 -103
- package/src/executor.ts +40 -48
- package/src/index.ts +7 -7
- package/src/secrets/index.ts +5 -5
- package/src/secrets/providers/aws.ts +4 -5
- package/src/secrets/providers/env.ts +1 -1
- package/src/secrets/providers/vault.ts +7 -7
- package/src/secrets/registry.ts +75 -142
- package/src/secrets/resolver.ts +28 -26
- package/src/secrets/secrets.test.ts +124 -155
- package/src/secrets/types.ts +4 -13
- package/src/{test-plan-types.ts → test-monitor-types.ts} +1 -1
- package/src/types.ts +2 -2
package/src/secrets/registry.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Secret provider registry for managing
|
|
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
|
|
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
|
|
16
|
+
private provider: SecretProvider | null = null;
|
|
18
17
|
|
|
19
18
|
/**
|
|
20
|
-
*
|
|
21
|
-
* @param provider - The provider to
|
|
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
|
-
|
|
25
|
-
|
|
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
|
-
*
|
|
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
|
-
|
|
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.
|
|
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
|
|
61
|
+
* Resolve multiple secrets using the configured provider.
|
|
108
62
|
* @param refs - Array of secret reference data
|
|
109
|
-
* @returns Map of
|
|
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
|
-
|
|
118
|
-
|
|
119
|
-
|
|
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
|
-
|
|
141
|
-
const
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
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
|
-
|
|
151
|
-
|
|
88
|
+
try {
|
|
89
|
+
const batchResults = await this.provider.resolveMany(batchRefs);
|
|
152
90
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
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
|
-
`
|
|
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
|
|
207
|
-
* @throws Error if
|
|
141
|
+
* Validate the configured provider.
|
|
142
|
+
* @throws Error if provider validation fails
|
|
208
143
|
*/
|
|
209
144
|
async validateAll(): Promise<void> {
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
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.
|
|
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(":");
|
package/src/secrets/resolver.ts
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Secret resolution utilities for test
|
|
2
|
+
* Secret resolution utilities for test monitors.
|
|
3
3
|
*/
|
|
4
|
-
import { type
|
|
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
|
|
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
|
|
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
|
|
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 <
|
|
85
|
-
const node =
|
|
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.
|
|
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
|
|
169
|
-
* The original
|
|
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
|
|
173
|
+
* @param monitor - The test monitor containing secret references and string literals
|
|
172
174
|
* @param registry - The secret provider registry
|
|
173
|
-
* @returns A new
|
|
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
|
|
177
|
-
|
|
178
|
+
export async function resolveSecretsInMonitor(
|
|
179
|
+
monitor: MonitorV1,
|
|
178
180
|
registry: SecretProviderRegistry,
|
|
179
|
-
): Promise<
|
|
181
|
+
): Promise<MonitorV1> {
|
|
180
182
|
// Collect all secret references and string literals
|
|
181
|
-
const collected =
|
|
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
|
|
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
|
|
195
|
-
const
|
|
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.
|
|
207
|
+
`Internal error: resolved value not found for secret "${secretRef.ref}"`,
|
|
206
208
|
);
|
|
207
209
|
}
|
|
208
210
|
|
|
209
|
-
setAtPath(
|
|
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(
|
|
216
|
+
setAtPath(resolvedMonitor, path, value);
|
|
215
217
|
}
|
|
216
218
|
|
|
217
|
-
return
|
|
219
|
+
return resolvedMonitor;
|
|
218
220
|
}
|
|
219
221
|
|
|
220
222
|
/**
|
|
221
|
-
* Check if a
|
|
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(
|
|
225
|
-
for (const node of
|
|
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
|
}
|