@griffin-app/griffin-ts 0.1.14 → 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.
@@ -3,46 +3,32 @@ import { secret, isSecretRef } from "./secrets.js";
3
3
 
4
4
  describe("Secret References", () => {
5
5
  describe("Basic Secret Creation", () => {
6
- it("should create environment variable secret", () => {
7
- const ref = secret("env:API_KEY");
6
+ it("should create secret with ref only", () => {
7
+ const ref = secret("API_KEY");
8
8
 
9
9
  expect(ref).toEqual({
10
10
  $secret: {
11
- provider: "env",
12
11
  ref: "API_KEY",
13
12
  },
14
13
  });
15
14
  });
16
15
 
17
- it("should create AWS Secrets Manager secret", () => {
18
- const ref = secret("aws:prod/api-key");
16
+ it("should create secret with alphanumeric and underscore", () => {
17
+ const ref = secret("my_secret_123");
19
18
 
20
19
  expect(ref).toEqual({
21
20
  $secret: {
22
- provider: "aws",
23
- ref: "prod/api-key",
21
+ ref: "my_secret_123",
24
22
  },
25
23
  });
26
24
  });
27
25
 
28
- it("should create Vault secret", () => {
29
- const ref = secret("vault:secret/data/api");
26
+ it("should trim ref", () => {
27
+ const ref = secret(" API_KEY ");
30
28
 
31
29
  expect(ref).toEqual({
32
30
  $secret: {
33
- provider: "vault",
34
- ref: "secret/data/api",
35
- },
36
- });
37
- });
38
-
39
- it("should create Doppler secret", () => {
40
- const ref = secret("doppler:backend/prod/API_KEY");
41
-
42
- expect(ref).toEqual({
43
- $secret: {
44
- provider: "doppler",
45
- ref: "backend/prod/API_KEY",
31
+ ref: "API_KEY",
46
32
  },
47
33
  });
48
34
  });
@@ -50,39 +36,36 @@ describe("Secret References", () => {
50
36
 
51
37
  describe("Secret Options", () => {
52
38
  it("should include version when specified", () => {
53
- const ref = secret("aws:prod/api-key", { version: "v1.2.3" });
39
+ const ref = secret("prod_api_key", { version: "v1.2.3" });
54
40
 
55
41
  expect(ref).toEqual({
56
42
  $secret: {
57
- provider: "aws",
58
- ref: "prod/api-key",
43
+ ref: "prod_api_key",
59
44
  version: "v1.2.3",
60
45
  },
61
46
  });
62
47
  });
63
48
 
64
49
  it("should include field when specified", () => {
65
- const ref = secret("aws:prod/db-credentials", { field: "password" });
50
+ const ref = secret("db_credentials", { field: "password" });
66
51
 
67
52
  expect(ref).toEqual({
68
53
  $secret: {
69
- provider: "aws",
70
- ref: "prod/db-credentials",
54
+ ref: "db_credentials",
71
55
  field: "password",
72
56
  },
73
57
  });
74
58
  });
75
59
 
76
60
  it("should include both version and field", () => {
77
- const ref = secret("aws:prod/db-credentials", {
61
+ const ref = secret("db_credentials", {
78
62
  version: "latest",
79
63
  field: "username",
80
64
  });
81
65
 
82
66
  expect(ref).toEqual({
83
67
  $secret: {
84
- provider: "aws",
85
- ref: "prod/db-credentials",
68
+ ref: "db_credentials",
86
69
  version: "latest",
87
70
  field: "username",
88
71
  },
@@ -90,83 +73,65 @@ describe("Secret References", () => {
90
73
  });
91
74
  });
92
75
 
93
- describe("Provider Formats", () => {
94
- it("should handle various provider names", () => {
95
- expect(secret("custom:my-secret")).toEqual({
96
- $secret: { provider: "custom", ref: "my-secret" },
97
- });
98
-
99
- expect(secret("gcp:my-secret")).toEqual({
100
- $secret: { provider: "gcp", ref: "my-secret" },
76
+ describe("Valid Names", () => {
77
+ it("should accept names starting with letter", () => {
78
+ expect(secret("API_KEY")).toEqual({
79
+ $secret: { ref: "API_KEY" },
101
80
  });
102
-
103
- expect(secret("azure:my-secret")).toEqual({
104
- $secret: { provider: "azure", ref: "my-secret" },
81
+ expect(secret("DatabasePassword")).toEqual({
82
+ $secret: { ref: "DatabasePassword" },
105
83
  });
106
84
  });
107
85
 
108
- it("should handle refs with multiple slashes", () => {
109
- const ref = secret("aws:path/to/nested/secret");
110
-
111
- expect(ref).toEqual({
112
- $secret: {
113
- provider: "aws",
114
- ref: "path/to/nested/secret",
115
- },
86
+ it("should accept names starting with underscore", () => {
87
+ expect(secret("_private_key")).toEqual({
88
+ $secret: { ref: "_private_key" },
116
89
  });
117
90
  });
118
91
 
119
- it("should handle refs with special characters", () => {
120
- const ref = secret("env:API_KEY_123");
121
-
122
- expect(ref).toEqual({
123
- $secret: {
124
- provider: "env",
125
- ref: "API_KEY_123",
126
- },
92
+ it("should accept alphanumeric and underscores", () => {
93
+ expect(secret("API_KEY_123")).toEqual({
94
+ $secret: { ref: "API_KEY_123" },
127
95
  });
128
96
  });
97
+ });
129
98
 
130
- it("should handle refs with hyphens and underscores", () => {
131
- const ref = secret("aws:prod/my-api_key-v2");
99
+ describe("Error Handling", () => {
100
+ it("should throw error when ref is empty", () => {
101
+ expect(() => secret("")).toThrow("Secret ref cannot be empty");
102
+ });
132
103
 
133
- expect(ref).toEqual({
134
- $secret: {
135
- provider: "aws",
136
- ref: "prod/my-api_key-v2",
137
- },
138
- });
104
+ it("should throw error when ref is only whitespace", () => {
105
+ expect(() => secret(" ")).toThrow("Secret ref cannot be empty");
139
106
  });
140
- });
141
107
 
142
- describe("Error Handling", () => {
143
- it("should throw error when missing colon", () => {
144
- expect(() => secret("no-colon-here")).toThrow(
145
- 'Secret path must include provider: "provider:path"',
108
+ it("should throw error when ref starts with number", () => {
109
+ expect(() => secret("123_KEY")).toThrow(
110
+ "Secret ref must start with a letter or underscore",
146
111
  );
147
112
  });
148
113
 
149
- it("should throw error when provider is empty", () => {
150
- expect(() => secret(":my-secret")).toThrow(
151
- "Secret path must have a provider name before the colon",
114
+ it("should throw error when ref contains hyphen", () => {
115
+ expect(() => secret("api-key")).toThrow(
116
+ "Secret ref must start with a letter or underscore",
152
117
  );
153
118
  });
154
119
 
155
- it("should throw error when ref is empty", () => {
156
- expect(() => secret("aws:")).toThrow(
157
- "Secret path must have a reference after the colon",
120
+ it("should throw error when ref contains special characters", () => {
121
+ expect(() => secret("api.key")).toThrow(
122
+ "Secret ref must start with a letter or underscore",
158
123
  );
159
124
  });
160
125
  });
161
126
 
162
127
  describe("Type Guard", () => {
163
128
  it("should identify valid secret refs", () => {
164
- const ref = secret("env:API_KEY");
129
+ const ref = secret("API_KEY");
165
130
  expect(isSecretRef(ref)).toBe(true);
166
131
  });
167
132
 
168
133
  it("should identify secret refs with options", () => {
169
- const ref = secret("aws:key", { version: "v1", field: "password" });
134
+ const ref = secret("key", { version: "v1", field: "password" });
170
135
  expect(isSecretRef(ref)).toBe(true);
171
136
  });
172
137
 
@@ -187,27 +152,16 @@ describe("Secret References", () => {
187
152
  expect(isSecretRef({ $secret: null })).toBe(false);
188
153
  expect(isSecretRef({ $secret: "string" })).toBe(false);
189
154
  expect(isSecretRef({ $secret: {} })).toBe(false);
190
- expect(isSecretRef({ $secret: { provider: "aws" } })).toBe(false);
191
- expect(isSecretRef({ $secret: { ref: "key" } })).toBe(false);
155
+ expect(isSecretRef({ $secret: { ref: "key" } })).toBe(true); // valid - only ref required
192
156
  });
193
157
 
194
158
  it("should accept valid $secret structure", () => {
159
+ expect(isSecretRef({ $secret: { ref: "key" } })).toBe(true);
160
+ expect(isSecretRef({ $secret: { ref: "VAR", version: "v1" } })).toBe(
161
+ true,
162
+ );
195
163
  expect(
196
- isSecretRef({
197
- $secret: { provider: "aws", ref: "key" },
198
- }),
199
- ).toBe(true);
200
-
201
- expect(
202
- isSecretRef({
203
- $secret: { provider: "env", ref: "VAR", version: "v1" },
204
- }),
205
- ).toBe(true);
206
-
207
- expect(
208
- isSecretRef({
209
- $secret: { provider: "vault", ref: "path", field: "password" },
210
- }),
164
+ isSecretRef({ $secret: { ref: "path", field: "password" } }),
211
165
  ).toBe(true);
212
166
  });
213
167
  });
@@ -215,31 +169,30 @@ describe("Secret References", () => {
215
169
  describe("Integration Examples", () => {
216
170
  it("should work in header configurations", () => {
217
171
  const headers = {
218
- Authorization: secret("env:API_TOKEN"),
219
- "X-API-Key": secret("aws:prod/api-key"),
172
+ Authorization: secret("API_TOKEN"),
173
+ "X-API-Key": secret("API_KEY"),
220
174
  };
221
175
 
222
176
  expect(headers["Authorization"]).toEqual({
223
- $secret: { provider: "env", ref: "API_TOKEN" },
177
+ $secret: { ref: "API_TOKEN" },
224
178
  });
225
179
  expect(headers["X-API-Key"]).toEqual({
226
- $secret: { provider: "aws", ref: "prod/api-key" },
180
+ $secret: { ref: "API_KEY" },
227
181
  });
228
182
  });
229
183
 
230
184
  it("should work in body configurations", () => {
231
185
  const body = {
232
- apiKey: secret("env:API_KEY"),
233
- dbPassword: secret("aws:prod/db-password", { field: "password" }),
186
+ apiKey: secret("API_KEY"),
187
+ dbPassword: secret("db_credentials", { field: "password" }),
234
188
  };
235
189
 
236
190
  expect(body.apiKey).toEqual({
237
- $secret: { provider: "env", ref: "API_KEY" },
191
+ $secret: { ref: "API_KEY" },
238
192
  });
239
193
  expect(body.dbPassword).toEqual({
240
194
  $secret: {
241
- provider: "aws",
242
- ref: "prod/db-password",
195
+ ref: "db_credentials",
243
196
  field: "password",
244
197
  },
245
198
  });
@@ -250,49 +203,17 @@ describe("Secret References", () => {
250
203
  database: {
251
204
  host: "localhost",
252
205
  credentials: {
253
- username: secret("vault:db/username"),
254
- password: secret("vault:db/password"),
206
+ username: secret("db_username"),
207
+ password: secret("db_password"),
255
208
  },
256
209
  },
257
210
  };
258
211
 
259
212
  expect(config.database.credentials.username).toEqual({
260
- $secret: { provider: "vault", ref: "db/username" },
213
+ $secret: { ref: "db_username" },
261
214
  });
262
215
  expect(config.database.credentials.password).toEqual({
263
- $secret: { provider: "vault", ref: "db/password" },
264
- });
265
- });
266
- });
267
-
268
- describe("Real-world Provider Examples", () => {
269
- it("should handle AWS Secrets Manager paths", () => {
270
- expect(secret("aws:production/api/key")).toEqual({
271
- $secret: { provider: "aws", ref: "production/api/key" },
272
- });
273
-
274
- expect(secret("aws:prod/rds/credentials")).toEqual({
275
- $secret: { provider: "aws", ref: "prod/rds/credentials" },
276
- });
277
- });
278
-
279
- it("should handle HashiCorp Vault paths", () => {
280
- expect(secret("vault:secret/data/api")).toEqual({
281
- $secret: { provider: "vault", ref: "secret/data/api" },
282
- });
283
-
284
- expect(secret("vault:kv/prod/database")).toEqual({
285
- $secret: { provider: "vault", ref: "kv/prod/database" },
286
- });
287
- });
288
-
289
- it("should handle environment variables", () => {
290
- expect(secret("env:DATABASE_URL")).toEqual({
291
- $secret: { provider: "env", ref: "DATABASE_URL" },
292
- });
293
-
294
- expect(secret("env:API_KEY")).toEqual({
295
- $secret: { provider: "env", ref: "API_KEY" },
216
+ $secret: { ref: "db_password" },
296
217
  });
297
218
  });
298
219
  });
package/src/secrets.ts CHANGED
@@ -7,15 +7,12 @@ export interface SecretOptions {
7
7
  field?: string;
8
8
  }
9
9
 
10
+ const SECRET_REF_REGEX = /^[A-Za-z_][A-Za-z0-9_]*$/;
11
+
10
12
  /**
11
13
  * Create a secret reference for use in endpoint headers or body.
12
14
  *
13
- * @param path - Provider-qualified path in format "provider:path"
14
- * Examples:
15
- * - "env:API_KEY" - Environment variable
16
- * - "aws:prod/api-key" - AWS Secrets Manager
17
- * - "vault:secret/data/api" - HashiCorp Vault
18
- * - "doppler:backend/prod/API_KEY" - Doppler
15
+ * @param ref - Secret name (alphanumeric and underscore only)
19
16
  *
20
17
  * @param options - Optional version pinning or field extraction
21
18
  *
@@ -25,40 +22,26 @@ export interface SecretOptions {
25
22
  * method: GET,
26
23
  * path: "/api/data",
27
24
  * headers: {
28
- * "Authorization": secret("aws:prod/api-key"),
29
- * "X-API-Key": secret("env:LOCAL_API_KEY"),
25
+ * "Authorization": secret("API_KEY"),
26
+ * "X-API-Key": secret("LOCAL_API_KEY"),
30
27
  * },
31
28
  * })
32
29
  * ```
33
30
  */
34
- export function secret(path: string, options?: SecretOptions): SecretRef {
35
- const colonIndex = path.indexOf(":");
36
-
37
- if (colonIndex === -1) {
38
- throw new Error(
39
- `Secret path must include provider: "provider:path" (e.g., "aws:my-secret", "env:API_KEY"). Got: "${path}"`,
40
- );
41
- }
42
-
43
- if (colonIndex === 0) {
44
- throw new Error(
45
- `Secret path must have a provider name before the colon. Got: "${path}"`,
46
- );
31
+ export function secret(ref: string, options?: SecretOptions): SecretRef {
32
+ const trimmed = ref.trim();
33
+ if (!trimmed.length) {
34
+ throw new Error("Secret ref cannot be empty");
47
35
  }
48
-
49
- if (colonIndex === path.length - 1) {
36
+ if (!SECRET_REF_REGEX.test(trimmed)) {
50
37
  throw new Error(
51
- `Secret path must have a reference after the colon. Got: "${path}"`,
38
+ `Secret ref must start with a letter or underscore and contain only letters, numbers, and underscores. Got: "${ref}"`,
52
39
  );
53
40
  }
54
41
 
55
- const provider = path.slice(0, colonIndex);
56
- const ref = path.slice(colonIndex + 1);
57
-
58
42
  return {
59
43
  $secret: {
60
- provider,
61
- ref,
44
+ ref: trimmed,
62
45
  ...(options?.version !== undefined && { version: options.version }),
63
46
  ...(options?.field !== undefined && { field: options.field }),
64
47
  },
@@ -83,10 +66,7 @@ export function isSecretRef(value: unknown): value is SecretRef {
83
66
  }
84
67
 
85
68
  const secretData = obj.$secret as Record<string, unknown>;
86
- return (
87
- typeof secretData.provider === "string" &&
88
- typeof secretData.ref === "string"
89
- );
69
+ return typeof secretData.ref === "string";
90
70
  }
91
71
 
92
72
  /**
@@ -399,17 +399,14 @@ describe("Sequential Test Builder", () => {
399
399
  base: "http://localhost:3000",
400
400
  response_format: "JSON",
401
401
  headers: {
402
- Authorization: secret("env:API_TOKEN"),
402
+ Authorization: secret("API_TOKEN"),
403
403
  },
404
404
  })
405
405
  .build();
406
406
 
407
407
  const endpoint = monitor.nodes[0] as any;
408
408
  expect(endpoint.headers.Authorization).toEqual({
409
- $secret: {
410
- provider: "env",
411
- ref: "API_TOKEN",
412
- },
409
+ $secret: { ref: "API_TOKEN" },
413
410
  });
414
411
  });
415
412
 
@@ -424,17 +421,14 @@ describe("Sequential Test Builder", () => {
424
421
  base: "http://localhost:3000",
425
422
  response_format: "JSON",
426
423
  body: {
427
- apiKey: secret("aws:prod/api-key"),
424
+ apiKey: secret("API_KEY"),
428
425
  },
429
426
  })
430
427
  .build();
431
428
 
432
429
  const endpoint = monitor.nodes[0] as any;
433
430
  expect(endpoint.body.apiKey).toEqual({
434
- $secret: {
435
- provider: "aws",
436
- ref: "prod/api-key",
437
- },
431
+ $secret: { ref: "API_KEY" },
438
432
  });
439
433
  });
440
434