@terreno/api 0.19.0 → 0.20.0

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.
@@ -84,8 +84,19 @@ var __read = (this && this.__read) || function (o, n) {
84
84
  }
85
85
  return ar;
86
86
  };
87
+ var __values = (this && this.__values) || function(o) {
88
+ var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
89
+ if (m) return m.call(o);
90
+ if (o && typeof o.length === "number") return {
91
+ next: function () {
92
+ if (o && i >= o.length) o = void 0;
93
+ return { value: o && o[i++], done: !o };
94
+ }
95
+ };
96
+ throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
97
+ };
87
98
  Object.defineProperty(exports, "__esModule", { value: true });
88
- exports.GcpSecretProvider = exports.EnvSecretProvider = void 0;
99
+ exports.CachingSecretProvider = exports.CompositeSecretProvider = exports.GcpSecretProvider = exports.EnvSecretProvider = void 0;
89
100
  var errors_1 = require("./errors");
90
101
  var logger_1 = require("./logger");
91
102
  /**
@@ -106,7 +117,11 @@ var EnvSecretProvider = /** @class */ (function () {
106
117
  function EnvSecretProvider() {
107
118
  this.name = "env";
108
119
  }
109
- EnvSecretProvider.prototype.getSecret = function (secretName) {
120
+ /**
121
+ * Resolve a secret from an environment variable. Environment variables have no
122
+ * versions, so the optional `version` parameter is ignored.
123
+ */
124
+ EnvSecretProvider.prototype.getSecret = function (secretName, _version) {
110
125
  return __awaiter(this, void 0, void 0, function () {
111
126
  var envKey, value;
112
127
  var _a;
@@ -180,30 +195,42 @@ var GcpSecretProvider = /** @class */ (function () {
180
195
  });
181
196
  });
182
197
  };
183
- GcpSecretProvider.prototype.getSecret = function (secretName) {
198
+ /**
199
+ * Resolve a secret from Google Cloud Secret Manager.
200
+ *
201
+ * @param secretName - A short secret id (e.g. "openai-api-key") or a full
202
+ * resource path (e.g. "projects/p/secrets/s" or
203
+ * "projects/p/secrets/s/versions/3").
204
+ * @param version - Optional version to resolve when `secretName` is a short id
205
+ * (e.g. "3"). Defaults to "latest". Ignored when `secretName` already
206
+ * contains an explicit `/versions/...` suffix.
207
+ */
208
+ GcpSecretProvider.prototype.getSecret = function (secretName, version) {
184
209
  return __awaiter(this, void 0, void 0, function () {
185
- var client, resourceName, _a, version, payload, error_1;
210
+ var client, resolvedVersion, resourceName, _a, version_1, payload, error_1;
186
211
  var _b;
187
212
  return __generator(this, function (_c) {
188
213
  switch (_c.label) {
189
214
  case 0: return [4 /*yield*/, this.getClient()];
190
215
  case 1:
191
216
  client = _c.sent();
217
+ resolvedVersion = version !== null && version !== void 0 ? version : "latest";
192
218
  if (secretName.startsWith("projects/")) {
193
- resourceName = secretName.endsWith("/versions/latest")
219
+ // Honor a full resource path. Only append a version when one is not present.
220
+ resourceName = secretName.includes("/versions/")
194
221
  ? secretName
195
- : "".concat(secretName, "/versions/latest");
222
+ : "".concat(secretName, "/versions/").concat(resolvedVersion);
196
223
  }
197
224
  else {
198
- resourceName = "projects/".concat(this.projectId, "/secrets/").concat(secretName, "/versions/latest");
225
+ resourceName = "projects/".concat(this.projectId, "/secrets/").concat(secretName, "/versions/").concat(resolvedVersion);
199
226
  }
200
227
  _c.label = 2;
201
228
  case 2:
202
229
  _c.trys.push([2, 4, , 5]);
203
230
  return [4 /*yield*/, client.accessSecretVersion({ name: resourceName })];
204
231
  case 3:
205
- _a = __read.apply(void 0, [_c.sent(), 1]), version = _a[0];
206
- payload = (_b = version.payload) === null || _b === void 0 ? void 0 : _b.data;
232
+ _a = __read.apply(void 0, [_c.sent(), 1]), version_1 = _a[0];
233
+ payload = (_b = version_1.payload) === null || _b === void 0 ? void 0 : _b.data;
207
234
  if (!payload) {
208
235
  logger_1.logger.warn("GcpSecretProvider: secret ".concat(secretName, " has no payload"));
209
236
  return [2 /*return*/, null];
@@ -225,3 +252,144 @@ var GcpSecretProvider = /** @class */ (function () {
225
252
  return GcpSecretProvider;
226
253
  }());
227
254
  exports.GcpSecretProvider = GcpSecretProvider;
255
+ /**
256
+ * Secret provider that delegates to an ordered list of providers, returning the
257
+ * first non-null result.
258
+ *
259
+ * A provider that throws is warn-logged (secret name only — never the value) and
260
+ * resolution falls through to the next provider. This makes it easy to compose a
261
+ * primary provider with a fallback, e.g. GCP with an environment-variable
262
+ * fallback:
263
+ *
264
+ * @example
265
+ * ```typescript
266
+ * const provider = new CompositeSecretProvider([
267
+ * new GcpSecretProvider({projectId: "my-project"}),
268
+ * new EnvSecretProvider(),
269
+ * ]);
270
+ * const key = await provider.getSecret("openai-api-key");
271
+ * ```
272
+ */
273
+ var CompositeSecretProvider = /** @class */ (function () {
274
+ function CompositeSecretProvider(providers) {
275
+ if (!providers || providers.length === 0) {
276
+ throw new errors_1.APIError({
277
+ status: 500,
278
+ title: "CompositeSecretProvider requires at least one provider",
279
+ });
280
+ }
281
+ this.providers = providers;
282
+ this.name = "composite(".concat(providers.map(function (p) { return p.name; }).join(","), ")");
283
+ }
284
+ CompositeSecretProvider.prototype.getSecret = function (secretName, version) {
285
+ return __awaiter(this, void 0, void 0, function () {
286
+ var _a, _b, provider, value, error_2, message, e_1_1;
287
+ var e_1, _c;
288
+ return __generator(this, function (_d) {
289
+ switch (_d.label) {
290
+ case 0:
291
+ _d.trys.push([0, 7, 8, 9]);
292
+ _a = __values(this.providers), _b = _a.next();
293
+ _d.label = 1;
294
+ case 1:
295
+ if (!!_b.done) return [3 /*break*/, 6];
296
+ provider = _b.value;
297
+ _d.label = 2;
298
+ case 2:
299
+ _d.trys.push([2, 4, , 5]);
300
+ return [4 /*yield*/, provider.getSecret(secretName, version)];
301
+ case 3:
302
+ value = _d.sent();
303
+ if (value !== null) {
304
+ return [2 /*return*/, value];
305
+ }
306
+ return [3 /*break*/, 5];
307
+ case 4:
308
+ error_2 = _d.sent();
309
+ message = error_2 instanceof Error ? error_2.message : String(error_2);
310
+ logger_1.logger.warn("CompositeSecretProvider: provider ".concat(provider.name, " failed for secret ").concat(secretName, ": ").concat(message));
311
+ return [3 /*break*/, 5];
312
+ case 5:
313
+ _b = _a.next();
314
+ return [3 /*break*/, 1];
315
+ case 6: return [3 /*break*/, 9];
316
+ case 7:
317
+ e_1_1 = _d.sent();
318
+ e_1 = { error: e_1_1 };
319
+ return [3 /*break*/, 9];
320
+ case 8:
321
+ try {
322
+ if (_b && !_b.done && (_c = _a.return)) _c.call(_a);
323
+ }
324
+ finally { if (e_1) throw e_1.error; }
325
+ return [7 /*endfinally*/];
326
+ case 9: return [2 /*return*/, null];
327
+ }
328
+ });
329
+ });
330
+ };
331
+ return CompositeSecretProvider;
332
+ }());
333
+ exports.CompositeSecretProvider = CompositeSecretProvider;
334
+ /**
335
+ * Secret provider that wraps any provider with an in-memory TTL cache.
336
+ *
337
+ * Cache entries are keyed by `secretName@version` so that pinned versions are
338
+ * cached independently. `null` results (secret not found) are cached too, to
339
+ * avoid hammering the underlying provider for missing secrets. Secret values are
340
+ * never logged.
341
+ *
342
+ * Use `clear()` to drop the entire cache (e.g. on rotation) or `clearKey()` to
343
+ * invalidate a single secret.
344
+ *
345
+ * @example
346
+ * ```typescript
347
+ * const provider = new CachingSecretProvider(
348
+ * new CompositeSecretProvider([gcp, env]),
349
+ * {ttlMs: 30_000}
350
+ * );
351
+ * ```
352
+ */
353
+ var CachingSecretProvider = /** @class */ (function () {
354
+ function CachingSecretProvider(provider, options) {
355
+ var _a;
356
+ this.cache = new Map();
357
+ this.provider = provider;
358
+ this.ttlMs = (_a = options === null || options === void 0 ? void 0 : options.ttlMs) !== null && _a !== void 0 ? _a : 60000;
359
+ this.name = "caching(".concat(provider.name, ")");
360
+ }
361
+ CachingSecretProvider.prototype.cacheKey = function (secretName, version) {
362
+ return "".concat(secretName, "@").concat(version !== null && version !== void 0 ? version : "latest");
363
+ };
364
+ CachingSecretProvider.prototype.getSecret = function (secretName, version) {
365
+ return __awaiter(this, void 0, void 0, function () {
366
+ var key, now, cached, value;
367
+ return __generator(this, function (_a) {
368
+ switch (_a.label) {
369
+ case 0:
370
+ key = this.cacheKey(secretName, version);
371
+ now = Date.now();
372
+ cached = this.cache.get(key);
373
+ if (cached && cached.expiresAt > now) {
374
+ return [2 /*return*/, cached.value];
375
+ }
376
+ return [4 /*yield*/, this.provider.getSecret(secretName, version)];
377
+ case 1:
378
+ value = _a.sent();
379
+ this.cache.set(key, { expiresAt: now + this.ttlMs, value: value });
380
+ return [2 /*return*/, value];
381
+ }
382
+ });
383
+ });
384
+ };
385
+ /** Clears the entire cache. Useful on secret rotation and in tests. */
386
+ CachingSecretProvider.prototype.clear = function () {
387
+ this.cache.clear();
388
+ };
389
+ /** Invalidates a single cached secret by name (and optional version). */
390
+ CachingSecretProvider.prototype.clearKey = function (secretName, version) {
391
+ this.cache.delete(this.cacheKey(secretName, version));
392
+ };
393
+ return CachingSecretProvider;
394
+ }());
395
+ exports.CachingSecretProvider = CachingSecretProvider;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,391 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __generator = (this && this.__generator) || function (thisArg, body) {
12
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
13
+ return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
14
+ function verb(n) { return function (v) { return step([n, v]); }; }
15
+ function step(op) {
16
+ if (f) throw new TypeError("Generator is already executing.");
17
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
18
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
19
+ if (y = 0, t) op = [op[0] & 2, t.value];
20
+ switch (op[0]) {
21
+ case 0: case 1: t = op; break;
22
+ case 4: _.label++; return { value: op[1], done: false };
23
+ case 5: _.label++; y = op[1]; op = [0]; continue;
24
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
25
+ default:
26
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
27
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
28
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
29
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
30
+ if (t[2]) _.ops.pop();
31
+ _.trys.pop(); continue;
32
+ }
33
+ op = body.call(thisArg, _);
34
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
35
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
36
+ }
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ var bun_test_1 = require("bun:test");
40
+ var secretProviders_1 = require("./secretProviders");
41
+ (0, bun_test_1.describe)("EnvSecretProvider", function () {
42
+ (0, bun_test_1.beforeEach)(function () {
43
+ delete process.env.MY_SECRET_KEY;
44
+ });
45
+ (0, bun_test_1.it)("resolves from a SCREAMING_SNAKE_CASE env var", function () { return __awaiter(void 0, void 0, void 0, function () {
46
+ var provider, _a;
47
+ return __generator(this, function (_b) {
48
+ switch (_b.label) {
49
+ case 0:
50
+ process.env.MY_SECRET_KEY = "from-env";
51
+ provider = new secretProviders_1.EnvSecretProvider();
52
+ _a = bun_test_1.expect;
53
+ return [4 /*yield*/, provider.getSecret("my-secret-key")];
54
+ case 1:
55
+ _a.apply(void 0, [_b.sent()]).toBe("from-env");
56
+ return [2 /*return*/];
57
+ }
58
+ });
59
+ }); });
60
+ (0, bun_test_1.it)("returns null when the env var is missing", function () { return __awaiter(void 0, void 0, void 0, function () {
61
+ var provider, _a;
62
+ return __generator(this, function (_b) {
63
+ switch (_b.label) {
64
+ case 0:
65
+ provider = new secretProviders_1.EnvSecretProvider();
66
+ _a = bun_test_1.expect;
67
+ return [4 /*yield*/, provider.getSecret("my-secret-key")];
68
+ case 1:
69
+ _a.apply(void 0, [_b.sent()]).toBeNull();
70
+ return [2 /*return*/];
71
+ }
72
+ });
73
+ }); });
74
+ (0, bun_test_1.it)("ignores the version parameter", function () { return __awaiter(void 0, void 0, void 0, function () {
75
+ var provider, _a;
76
+ return __generator(this, function (_b) {
77
+ switch (_b.label) {
78
+ case 0:
79
+ process.env.MY_SECRET_KEY = "value";
80
+ provider = new secretProviders_1.EnvSecretProvider();
81
+ _a = bun_test_1.expect;
82
+ return [4 /*yield*/, provider.getSecret("my-secret-key", "5")];
83
+ case 1:
84
+ _a.apply(void 0, [_b.sent()]).toBe("value");
85
+ return [2 /*return*/];
86
+ }
87
+ });
88
+ }); });
89
+ });
90
+ (0, bun_test_1.describe)("CompositeSecretProvider", function () {
91
+ (0, bun_test_1.it)("throws when constructed with no providers", function () {
92
+ (0, bun_test_1.expect)(function () { return new secretProviders_1.CompositeSecretProvider([]); }).toThrow();
93
+ });
94
+ (0, bun_test_1.it)("returns the first non-null result", function () { return __awaiter(void 0, void 0, void 0, function () {
95
+ var a, b, c, provider, _a;
96
+ return __generator(this, function (_b) {
97
+ switch (_b.label) {
98
+ case 0:
99
+ a = { getSecret: function () { return __awaiter(void 0, void 0, void 0, function () { return __generator(this, function (_a) {
100
+ return [2 /*return*/, null];
101
+ }); }); }, name: "a" };
102
+ b = { getSecret: function () { return __awaiter(void 0, void 0, void 0, function () { return __generator(this, function (_a) {
103
+ return [2 /*return*/, "from-b"];
104
+ }); }); }, name: "b" };
105
+ c = { getSecret: function () { return __awaiter(void 0, void 0, void 0, function () { return __generator(this, function (_a) {
106
+ return [2 /*return*/, "from-c"];
107
+ }); }); }, name: "c" };
108
+ provider = new secretProviders_1.CompositeSecretProvider([a, b, c]);
109
+ _a = bun_test_1.expect;
110
+ return [4 /*yield*/, provider.getSecret("x")];
111
+ case 1:
112
+ _a.apply(void 0, [_b.sent()]).toBe("from-b");
113
+ return [2 /*return*/];
114
+ }
115
+ });
116
+ }); });
117
+ (0, bun_test_1.it)("falls through to the next provider when one throws", function () { return __awaiter(void 0, void 0, void 0, function () {
118
+ var failing, fallback, provider, _a;
119
+ return __generator(this, function (_b) {
120
+ switch (_b.label) {
121
+ case 0:
122
+ failing = {
123
+ getSecret: function () { return __awaiter(void 0, void 0, void 0, function () {
124
+ return __generator(this, function (_a) {
125
+ throw new Error("provider down");
126
+ });
127
+ }); },
128
+ name: "failing",
129
+ };
130
+ fallback = { getSecret: function () { return __awaiter(void 0, void 0, void 0, function () { return __generator(this, function (_a) {
131
+ return [2 /*return*/, "from-fallback"];
132
+ }); }); }, name: "fallback" };
133
+ provider = new secretProviders_1.CompositeSecretProvider([failing, fallback]);
134
+ _a = bun_test_1.expect;
135
+ return [4 /*yield*/, provider.getSecret("x")];
136
+ case 1:
137
+ _a.apply(void 0, [_b.sent()]).toBe("from-fallback");
138
+ return [2 /*return*/];
139
+ }
140
+ });
141
+ }); });
142
+ (0, bun_test_1.it)("returns null when every provider yields null", function () { return __awaiter(void 0, void 0, void 0, function () {
143
+ var a, b, provider, _a;
144
+ return __generator(this, function (_b) {
145
+ switch (_b.label) {
146
+ case 0:
147
+ a = { getSecret: function () { return __awaiter(void 0, void 0, void 0, function () { return __generator(this, function (_a) {
148
+ return [2 /*return*/, null];
149
+ }); }); }, name: "a" };
150
+ b = { getSecret: function () { return __awaiter(void 0, void 0, void 0, function () { return __generator(this, function (_a) {
151
+ return [2 /*return*/, null];
152
+ }); }); }, name: "b" };
153
+ provider = new secretProviders_1.CompositeSecretProvider([a, b]);
154
+ _a = bun_test_1.expect;
155
+ return [4 /*yield*/, provider.getSecret("x")];
156
+ case 1:
157
+ _a.apply(void 0, [_b.sent()]).toBeNull();
158
+ return [2 /*return*/];
159
+ }
160
+ });
161
+ }); });
162
+ (0, bun_test_1.it)("forwards the version parameter to each provider", function () { return __awaiter(void 0, void 0, void 0, function () {
163
+ var seen, a, b, provider;
164
+ return __generator(this, function (_a) {
165
+ switch (_a.label) {
166
+ case 0:
167
+ seen = [];
168
+ a = {
169
+ getSecret: function (_name, version) { return __awaiter(void 0, void 0, void 0, function () {
170
+ return __generator(this, function (_a) {
171
+ seen.push(version);
172
+ return [2 /*return*/, null];
173
+ });
174
+ }); },
175
+ name: "a",
176
+ };
177
+ b = {
178
+ getSecret: function (_name, version) { return __awaiter(void 0, void 0, void 0, function () {
179
+ return __generator(this, function (_a) {
180
+ seen.push(version);
181
+ return [2 /*return*/, "value"];
182
+ });
183
+ }); },
184
+ name: "b",
185
+ };
186
+ provider = new secretProviders_1.CompositeSecretProvider([a, b]);
187
+ return [4 /*yield*/, provider.getSecret("x", "7")];
188
+ case 1:
189
+ _a.sent();
190
+ (0, bun_test_1.expect)(seen).toEqual(["7", "7"]);
191
+ return [2 /*return*/];
192
+ }
193
+ });
194
+ }); });
195
+ (0, bun_test_1.it)("builds a composite name from the underlying providers", function () {
196
+ var provider = new secretProviders_1.CompositeSecretProvider([
197
+ { getSecret: function () { return __awaiter(void 0, void 0, void 0, function () { return __generator(this, function (_a) {
198
+ return [2 /*return*/, null];
199
+ }); }); }, name: "gcp" },
200
+ { getSecret: function () { return __awaiter(void 0, void 0, void 0, function () { return __generator(this, function (_a) {
201
+ return [2 /*return*/, null];
202
+ }); }); }, name: "env" },
203
+ ]);
204
+ (0, bun_test_1.expect)(provider.name).toBe("composite(gcp,env)");
205
+ });
206
+ });
207
+ (0, bun_test_1.describe)("CachingSecretProvider", function () {
208
+ (0, bun_test_1.it)("memoizes a value within the TTL (single underlying call)", function () { return __awaiter(void 0, void 0, void 0, function () {
209
+ var calls, underlying, provider, _a, _b;
210
+ return __generator(this, function (_c) {
211
+ switch (_c.label) {
212
+ case 0:
213
+ calls = 0;
214
+ underlying = {
215
+ getSecret: function () { return __awaiter(void 0, void 0, void 0, function () {
216
+ return __generator(this, function (_a) {
217
+ calls++;
218
+ return [2 /*return*/, "value"];
219
+ });
220
+ }); },
221
+ name: "underlying",
222
+ };
223
+ provider = new secretProviders_1.CachingSecretProvider(underlying, { ttlMs: 10000 });
224
+ _a = bun_test_1.expect;
225
+ return [4 /*yield*/, provider.getSecret("x")];
226
+ case 1:
227
+ _a.apply(void 0, [_c.sent()]).toBe("value");
228
+ _b = bun_test_1.expect;
229
+ return [4 /*yield*/, provider.getSecret("x")];
230
+ case 2:
231
+ _b.apply(void 0, [_c.sent()]).toBe("value");
232
+ (0, bun_test_1.expect)(calls).toBe(1);
233
+ return [2 /*return*/];
234
+ }
235
+ });
236
+ }); });
237
+ (0, bun_test_1.it)("re-fetches after clear()", function () { return __awaiter(void 0, void 0, void 0, function () {
238
+ var calls, underlying, provider, _a, _b;
239
+ return __generator(this, function (_c) {
240
+ switch (_c.label) {
241
+ case 0:
242
+ calls = 0;
243
+ underlying = {
244
+ getSecret: function () { return __awaiter(void 0, void 0, void 0, function () {
245
+ return __generator(this, function (_a) {
246
+ calls++;
247
+ return [2 /*return*/, "value-".concat(calls)];
248
+ });
249
+ }); },
250
+ name: "underlying",
251
+ };
252
+ provider = new secretProviders_1.CachingSecretProvider(underlying, { ttlMs: 10000 });
253
+ _a = bun_test_1.expect;
254
+ return [4 /*yield*/, provider.getSecret("x")];
255
+ case 1:
256
+ _a.apply(void 0, [_c.sent()]).toBe("value-1");
257
+ provider.clear();
258
+ _b = bun_test_1.expect;
259
+ return [4 /*yield*/, provider.getSecret("x")];
260
+ case 2:
261
+ _b.apply(void 0, [_c.sent()]).toBe("value-2");
262
+ (0, bun_test_1.expect)(calls).toBe(2);
263
+ return [2 /*return*/];
264
+ }
265
+ });
266
+ }); });
267
+ (0, bun_test_1.it)("re-fetches after the TTL expires", function () { return __awaiter(void 0, void 0, void 0, function () {
268
+ var calls, underlying, provider, _a, _b;
269
+ return __generator(this, function (_c) {
270
+ switch (_c.label) {
271
+ case 0:
272
+ calls = 0;
273
+ underlying = {
274
+ getSecret: function () { return __awaiter(void 0, void 0, void 0, function () {
275
+ return __generator(this, function (_a) {
276
+ calls++;
277
+ return [2 /*return*/, "value-".concat(calls)];
278
+ });
279
+ }); },
280
+ name: "underlying",
281
+ };
282
+ provider = new secretProviders_1.CachingSecretProvider(underlying, { ttlMs: 1 });
283
+ _a = bun_test_1.expect;
284
+ return [4 /*yield*/, provider.getSecret("x")];
285
+ case 1:
286
+ _a.apply(void 0, [_c.sent()]).toBe("value-1");
287
+ return [4 /*yield*/, new Promise(function (resolve) { return setTimeout(resolve, 5); })];
288
+ case 2:
289
+ _c.sent();
290
+ _b = bun_test_1.expect;
291
+ return [4 /*yield*/, provider.getSecret("x")];
292
+ case 3:
293
+ _b.apply(void 0, [_c.sent()]).toBe("value-2");
294
+ (0, bun_test_1.expect)(calls).toBe(2);
295
+ return [2 /*return*/];
296
+ }
297
+ });
298
+ }); });
299
+ (0, bun_test_1.it)("caches different versions independently", function () { return __awaiter(void 0, void 0, void 0, function () {
300
+ var seen, underlying, provider, _a, _b, _c;
301
+ return __generator(this, function (_d) {
302
+ switch (_d.label) {
303
+ case 0:
304
+ seen = [];
305
+ underlying = {
306
+ getSecret: function (_name, version) { return __awaiter(void 0, void 0, void 0, function () {
307
+ return __generator(this, function (_a) {
308
+ seen.push(version);
309
+ return [2 /*return*/, "v-".concat(version !== null && version !== void 0 ? version : "latest")];
310
+ });
311
+ }); },
312
+ name: "underlying",
313
+ };
314
+ provider = new secretProviders_1.CachingSecretProvider(underlying, { ttlMs: 10000 });
315
+ _a = bun_test_1.expect;
316
+ return [4 /*yield*/, provider.getSecret("x", "1")];
317
+ case 1:
318
+ _a.apply(void 0, [_d.sent()]).toBe("v-1");
319
+ _b = bun_test_1.expect;
320
+ return [4 /*yield*/, provider.getSecret("x", "2")];
321
+ case 2:
322
+ _b.apply(void 0, [_d.sent()]).toBe("v-2");
323
+ // Cached hits, no additional underlying calls.
324
+ _c = bun_test_1.expect;
325
+ return [4 /*yield*/, provider.getSecret("x", "1")];
326
+ case 3:
327
+ // Cached hits, no additional underlying calls.
328
+ _c.apply(void 0, [_d.sent()]).toBe("v-1");
329
+ (0, bun_test_1.expect)(seen).toEqual(["1", "2"]);
330
+ return [2 /*return*/];
331
+ }
332
+ });
333
+ }); });
334
+ (0, bun_test_1.it)("clearKey invalidates a single secret", function () { return __awaiter(void 0, void 0, void 0, function () {
335
+ var calls, underlying, provider;
336
+ return __generator(this, function (_a) {
337
+ switch (_a.label) {
338
+ case 0:
339
+ calls = 0;
340
+ underlying = {
341
+ getSecret: function () { return __awaiter(void 0, void 0, void 0, function () {
342
+ return __generator(this, function (_a) {
343
+ calls++;
344
+ return [2 /*return*/, "value-".concat(calls)];
345
+ });
346
+ }); },
347
+ name: "underlying",
348
+ };
349
+ provider = new secretProviders_1.CachingSecretProvider(underlying, { ttlMs: 10000 });
350
+ return [4 /*yield*/, provider.getSecret("x")];
351
+ case 1:
352
+ _a.sent();
353
+ provider.clearKey("x");
354
+ return [4 /*yield*/, provider.getSecret("x")];
355
+ case 2:
356
+ _a.sent();
357
+ (0, bun_test_1.expect)(calls).toBe(2);
358
+ return [2 /*return*/];
359
+ }
360
+ });
361
+ }); });
362
+ (0, bun_test_1.it)("caches null results", function () { return __awaiter(void 0, void 0, void 0, function () {
363
+ var calls, underlying, provider, _a, _b;
364
+ return __generator(this, function (_c) {
365
+ switch (_c.label) {
366
+ case 0:
367
+ calls = 0;
368
+ underlying = {
369
+ getSecret: function () { return __awaiter(void 0, void 0, void 0, function () {
370
+ return __generator(this, function (_a) {
371
+ calls++;
372
+ return [2 /*return*/, null];
373
+ });
374
+ }); },
375
+ name: "underlying",
376
+ };
377
+ provider = new secretProviders_1.CachingSecretProvider(underlying, { ttlMs: 10000 });
378
+ _a = bun_test_1.expect;
379
+ return [4 /*yield*/, provider.getSecret("missing")];
380
+ case 1:
381
+ _a.apply(void 0, [_c.sent()]).toBeNull();
382
+ _b = bun_test_1.expect;
383
+ return [4 /*yield*/, provider.getSecret("missing")];
384
+ case 2:
385
+ _b.apply(void 0, [_c.sent()]).toBeNull();
386
+ (0, bun_test_1.expect)(calls).toBe(1);
387
+ return [2 /*return*/];
388
+ }
389
+ });
390
+ }); });
391
+ });
package/package.json CHANGED
@@ -109,5 +109,5 @@
109
109
  "updateSnapshot": "bun test --update-snapshots"
110
110
  },
111
111
  "types": "dist/index.d.ts",
112
- "version": "0.19.0"
112
+ "version": "0.20.0"
113
113
  }