@terreno/api 0.18.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.
Files changed (41) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/dist/api.test.js +18 -8
  3. package/dist/auth.d.ts +5 -5
  4. package/dist/auth.js +123 -131
  5. package/dist/configuration.test.js +289 -10
  6. package/dist/configurationApp.d.ts +72 -5
  7. package/dist/configurationApp.js +168 -48
  8. package/dist/configurationPlugin.d.ts +64 -7
  9. package/dist/configurationPlugin.js +161 -39
  10. package/dist/configurationPlugin.test.js +238 -1
  11. package/dist/expressServer.test.js +0 -1
  12. package/dist/openApi.d.ts +6 -6
  13. package/dist/openApi.js +21 -21
  14. package/dist/populate.test.js +23 -0
  15. package/dist/realtime/queryMatcher.js +0 -6
  16. package/dist/realtime/queryStore.js +3 -11
  17. package/dist/realtime/realtime.test.js +41 -34
  18. package/dist/secretProviders.d.ts +79 -2
  19. package/dist/secretProviders.js +177 -9
  20. package/dist/secretProviders.test.d.ts +1 -0
  21. package/dist/secretProviders.test.js +391 -0
  22. package/package.json +1 -1
  23. package/src/actions.openApi.test.ts +1 -1
  24. package/src/actions.ts +0 -1
  25. package/src/api.test.ts +10 -2
  26. package/src/auth.ts +19 -19
  27. package/src/configuration.test.ts +171 -7
  28. package/src/configurationApp.ts +213 -30
  29. package/src/configurationPlugin.test.ts +174 -2
  30. package/src/configurationPlugin.ts +157 -28
  31. package/src/expressServer.test.ts +0 -1
  32. package/src/openApi.ts +21 -21
  33. package/src/populate.test.ts +25 -0
  34. package/src/realtime/queryMatcher.ts +0 -6
  35. package/src/realtime/queryStore.ts +1 -10
  36. package/src/realtime/realtime.test.ts +24 -24
  37. package/src/realtime/realtimeApp.ts +0 -1
  38. package/src/realtime/registry.ts +0 -1
  39. package/src/realtime/types.ts +0 -4
  40. package/src/secretProviders.test.ts +186 -0
  41. package/src/secretProviders.ts +145 -5
package/dist/openApi.js CHANGED
@@ -39,13 +39,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
39
39
  return (mod && mod.__esModule) ? mod : { "default": mod };
40
40
  };
41
41
  Object.defineProperty(exports, "__esModule", { value: true });
42
- exports.defaultOpenApiErrorResponses = exports.apiErrorContent = void 0;
43
- exports.getOpenApiMiddleware = getOpenApiMiddleware;
44
- exports.listOpenApiMiddleware = listOpenApiMiddleware;
45
- exports.createOpenApiMiddleware = createOpenApiMiddleware;
46
- exports.patchOpenApiMiddleware = patchOpenApiMiddleware;
47
- exports.deleteOpenApiMiddleware = deleteOpenApiMiddleware;
48
- exports.readOpenApiMiddleware = readOpenApiMiddleware;
42
+ exports.readOpenApiMiddleware = exports.deleteOpenApiMiddleware = exports.patchOpenApiMiddleware = exports.createOpenApiMiddleware = exports.listOpenApiMiddleware = exports.getOpenApiMiddleware = exports.defaultOpenApiErrorResponses = exports.apiErrorContent = void 0;
49
43
  var flatten_1 = __importDefault(require("lodash/flatten"));
50
44
  var merge_1 = __importDefault(require("lodash/merge"));
51
45
  var mongoose_to_swagger_1 = __importDefault(require("mongoose-to-swagger"));
@@ -83,7 +77,7 @@ exports.defaultOpenApiErrorResponses = {
83
77
  },
84
78
  };
85
79
  // We repeat this constantly, so we make it a component so we only have to define it once.
86
- function createAPIErrorComponent(openApi) {
80
+ var createAPIErrorComponent = function (openApi) {
87
81
  // Create a schema component called APIError
88
82
  openApi === null || openApi === void 0 ? void 0 : openApi.component("schemas", "APIError", {
89
83
  properties: {
@@ -144,8 +138,8 @@ function createAPIErrorComponent(openApi) {
144
138
  },
145
139
  type: "object",
146
140
  });
147
- }
148
- function getOpenApiMiddleware(model, options) {
141
+ };
142
+ var getOpenApiMiddleware = function (model, options) {
149
143
  var _c, _d, _e, _f, _g;
150
144
  createAPIErrorComponent(options.openApi);
151
145
  if (!((_c = options.openApi) === null || _c === void 0 ? void 0 : _c.path)) {
@@ -175,8 +169,9 @@ function getOpenApiMiddleware(model, options) {
175
169
  } }, exports.defaultOpenApiErrorResponses),
176
170
  tags: [model.collection.collectionName],
177
171
  }, (_g = (_f = options.openApiOverwrite) === null || _f === void 0 ? void 0 : _f.get) !== null && _g !== void 0 ? _g : {}));
178
- }
179
- function listOpenApiMiddleware(model, options) {
172
+ };
173
+ exports.getOpenApiMiddleware = getOpenApiMiddleware;
174
+ var listOpenApiMiddleware = function (model, options) {
180
175
  var _c, _d, _e, _f, _g, _h;
181
176
  if (!((_c = options.openApi) === null || _c === void 0 ? void 0 : _c.path)) {
182
177
  return noop;
@@ -316,8 +311,9 @@ function listOpenApiMiddleware(model, options) {
316
311
  } }, exports.defaultOpenApiErrorResponses),
317
312
  tags: [model.collection.collectionName],
318
313
  }, (_h = (_g = options.openApiOverwrite) === null || _g === void 0 ? void 0 : _g.list) !== null && _h !== void 0 ? _h : {}));
319
- }
320
- function createOpenApiMiddleware(model, options) {
314
+ };
315
+ exports.listOpenApiMiddleware = listOpenApiMiddleware;
316
+ var createOpenApiMiddleware = function (model, options) {
321
317
  var _c, _d, _e, _f, _g;
322
318
  if (!((_c = options.openApi) === null || _c === void 0 ? void 0 : _c.path)) {
323
319
  return noop;
@@ -355,8 +351,9 @@ function createOpenApiMiddleware(model, options) {
355
351
  } }, exports.defaultOpenApiErrorResponses),
356
352
  tags: [model.collection.collectionName],
357
353
  }, (_g = (_f = options.openApiOverwrite) === null || _f === void 0 ? void 0 : _f.create) !== null && _g !== void 0 ? _g : {}));
358
- }
359
- function patchOpenApiMiddleware(model, options) {
354
+ };
355
+ exports.createOpenApiMiddleware = createOpenApiMiddleware;
356
+ var patchOpenApiMiddleware = function (model, options) {
360
357
  var _c, _d, _e, _f, _g;
361
358
  if (!((_c = options.openApi) === null || _c === void 0 ? void 0 : _c.path)) {
362
359
  return noop;
@@ -394,8 +391,9 @@ function patchOpenApiMiddleware(model, options) {
394
391
  } }, exports.defaultOpenApiErrorResponses),
395
392
  tags: [model.collection.collectionName],
396
393
  }, (_g = (_f = options.openApiOverwrite) === null || _f === void 0 ? void 0 : _f.update) !== null && _g !== void 0 ? _g : {}));
397
- }
398
- function deleteOpenApiMiddleware(model, options) {
394
+ };
395
+ exports.patchOpenApiMiddleware = patchOpenApiMiddleware;
396
+ var deleteOpenApiMiddleware = function (model, options) {
399
397
  var _c, _d, _e, _f, _g;
400
398
  if (!((_c = options.openApi) === null || _c === void 0 ? void 0 : _c.path)) {
401
399
  return noop;
@@ -409,10 +407,11 @@ function deleteOpenApiMiddleware(model, options) {
409
407
  } }, exports.defaultOpenApiErrorResponses),
410
408
  tags: [model.collection.collectionName],
411
409
  }, (_g = (_f = options.openApiOverwrite) === null || _f === void 0 ? void 0 : _f.delete) !== null && _g !== void 0 ? _g : {}));
412
- }
410
+ };
411
+ exports.deleteOpenApiMiddleware = deleteOpenApiMiddleware;
413
412
  // This is a generic OpenAPI wrapper for a read that returns any object described by `properties`.
414
413
  // Useful for endpoints that don't directly map to a model.
415
- function readOpenApiMiddleware(options, properties, required, queryParameters) {
414
+ var readOpenApiMiddleware = function (options, properties, required, queryParameters) {
416
415
  var _c, _d, _e, _f, _g;
417
416
  if (!((_c = options.openApi) === null || _c === void 0 ? void 0 : _c.path)) {
418
417
  // Just log this once rather than for each middleware.
@@ -438,4 +437,5 @@ function readOpenApiMiddleware(options, properties, required, queryParameters) {
438
437
  } }, exports.defaultOpenApiErrorResponses),
439
438
  tags: [],
440
439
  }, (_g = (_f = options.openApiOverwrite) === null || _f === void 0 ? void 0 : _f.get) !== null && _g !== void 0 ? _g : {}));
441
- }
440
+ };
441
+ exports.readOpenApiMiddleware = readOpenApiMiddleware;
@@ -328,6 +328,29 @@ var tests_1 = require("./tests");
328
328
  (0, bun_test_1.expect)(detail.properties.displayAmount.type).toBe("any");
329
329
  });
330
330
  });
331
+ (0, bun_test_1.describe)("getOpenApiSpecForModel populate with existing properties", function () {
332
+ (0, bun_test_1.it)("merges populated properties into a path that already has properties", function () {
333
+ var result = (0, populate_1.getOpenApiSpecForModel)(tests_1.FoodModel, {
334
+ populatePaths: [{ path: "likesIds.userId" }],
335
+ });
336
+ // likesIds is an array subschema with its own properties already;
337
+ // populating userId should merge the user properties into the existing structure.
338
+ (0, bun_test_1.expect)(result.properties.likesIds).toBeDefined();
339
+ var likesIds = result.properties.likesIds;
340
+ var items = likesIds.items;
341
+ (0, bun_test_1.expect)(items.properties.userId).toBeDefined();
342
+ });
343
+ (0, bun_test_1.it)("creates intermediate path structure when navigating to nested populate", function () {
344
+ // eatenBy is defined as [{ ref: "User", type: ObjectId }] - an array of refs.
345
+ // When we populate eatenBy, the openApiPath resolves through items.
346
+ var result = (0, populate_1.getOpenApiSpecForModel)(tests_1.FoodModel, {
347
+ populatePaths: [{ path: "eatenBy" }],
348
+ });
349
+ (0, bun_test_1.expect)(result.properties.eatenBy).toBeDefined();
350
+ var eatenBy = result.properties.eatenBy;
351
+ (0, bun_test_1.expect)(eatenBy.items).toBeDefined();
352
+ });
353
+ });
331
354
  (0, bun_test_1.describe)("filterKeys (via getOpenApiSpecForModel populatePaths)", function () {
332
355
  (0, bun_test_1.it)("filters populated fields using dot-notation keys", function () {
333
356
  var result = (0, populate_1.getOpenApiSpecForModel)(tests_1.FoodModel, {
@@ -35,7 +35,6 @@ var __read = (this && this.__read) || function (o, n) {
35
35
  };
36
36
  Object.defineProperty(exports, "__esModule", { value: true });
37
37
  exports.matchesQuery = void 0;
38
- // biome-ignore lint/suspicious/noExplicitAny: traversing arbitrary nested document fields by user-supplied dotted path
39
38
  var getNestedValue = function (doc, path) {
40
39
  var e_1, _a;
41
40
  var parts = path.split(".");
@@ -58,7 +57,6 @@ var getNestedValue = function (doc, path) {
58
57
  }
59
58
  return current;
60
59
  };
61
- // biome-ignore lint/suspicious/noExplicitAny: value may be any document field type (string, number, ObjectId, etc.)
62
60
  var normalize = function (value) {
63
61
  var _a;
64
62
  if (value === null || value === undefined) {
@@ -73,7 +71,6 @@ var normalize = function (value) {
73
71
  }
74
72
  return value;
75
73
  };
76
- // biome-ignore lint/suspicious/noExplicitAny: rawValue is an arbitrary document field, condition is an arbitrary user query operand
77
74
  var matchesCondition = function (rawValue, condition) {
78
75
  var e_2, _a;
79
76
  var value = normalize(rawValue);
@@ -127,7 +124,6 @@ var matchesCondition = function (rawValue, condition) {
127
124
  return false;
128
125
  }
129
126
  var inValues = operand.map(normalize);
130
- // biome-ignore lint/suspicious/noExplicitAny: normalized value of arbitrary document field
131
127
  if (!inValues.some(function (v) { return v === value || String(v) === String(value); })) {
132
128
  return false;
133
129
  }
@@ -138,7 +134,6 @@ var matchesCondition = function (rawValue, condition) {
138
134
  return false;
139
135
  }
140
136
  var ninValues = operand.map(normalize);
141
- // biome-ignore lint/suspicious/noExplicitAny: normalized value of arbitrary document field
142
137
  if (ninValues.some(function (v) { return v === value || String(v) === String(value); })) {
143
138
  return false;
144
139
  }
@@ -179,7 +174,6 @@ var matchesCondition = function (rawValue, condition) {
179
174
  * @param query - MongoDB-style query object
180
175
  * @returns true if the document matches all query conditions
181
176
  */
182
- // biome-ignore lint/suspicious/noExplicitAny: doc is arbitrary; query values are arbitrary user-supplied JSON
183
177
  var matchesQuery = function (doc, query) {
184
178
  var e_3, _a, e_4, _b, e_5, _c;
185
179
  try {
@@ -40,12 +40,9 @@ exports.clearQueryStore = exports.getQuerySubscriptionsForCollection = exports.r
40
40
  * Compute a deterministic queryId from collection and query on the server side.
41
41
  * This prevents clients from hijacking other subscriptions by providing a colliding queryId.
42
42
  */
43
- var computeQueryId = function (collection,
44
- // biome-ignore lint/suspicious/noExplicitAny: MongoDB query filter values are arbitrary user-supplied JSON
45
- query) {
43
+ var computeQueryId = function (collection, query) {
46
44
  var e_1, _a;
47
45
  var sortedKeys = Object.keys(query).sort();
48
- // biome-ignore lint/suspicious/noExplicitAny: mirrors the input query value shape
49
46
  var normalized = {};
50
47
  try {
51
48
  for (var sortedKeys_1 = __values(sortedKeys), sortedKeys_1_1 = sortedKeys_1.next(); !sortedKeys_1_1.done; sortedKeys_1_1 = sortedKeys_1.next()) {
@@ -71,9 +68,7 @@ var socketQueries = new Map();
71
68
  * Register a query subscription for a socket.
72
69
  * The socket joins the `query:{queryId}` room (handled by the caller).
73
70
  */
74
- var addQuerySubscription = function (socketId, collection,
75
- // biome-ignore lint/suspicious/noExplicitAny: MongoDB query filter values are arbitrary user-supplied JSON
76
- query, queryId) {
71
+ var addQuerySubscription = function (socketId, collection, query, queryId) {
77
72
  var _a;
78
73
  querySubscriptions.set(queryId, { collection: collection, query: query, queryId: queryId });
79
74
  if (!socketQueries.has(socketId)) {
@@ -161,11 +156,8 @@ exports.removeAllSocketQueries = removeAllSocketQueries;
161
156
  * Get all unique query subscriptions for a given collection.
162
157
  * Used by the change stream watcher to evaluate which query rooms to emit to.
163
158
  */
164
- var getQuerySubscriptionsForCollection = function (collection
165
- // biome-ignore lint/suspicious/noExplicitAny: MongoDB query filter values are arbitrary user-supplied JSON
166
- ) {
159
+ var getQuerySubscriptionsForCollection = function (collection) {
167
160
  var e_5, _a;
168
- // biome-ignore lint/suspicious/noExplicitAny: MongoDB query filter values are arbitrary user-supplied JSON
169
161
  var result = [];
170
162
  try {
171
163
  for (var querySubscriptions_1 = __values(querySubscriptions), querySubscriptions_1_1 = querySubscriptions_1.next(); !querySubscriptions_1_1.done; querySubscriptions_1_1 = querySubscriptions_1.next()) {
@@ -2201,6 +2201,22 @@ var createMockSocket = function (decodedToken) {
2201
2201
  },
2202
2202
  };
2203
2203
  };
2204
+ var invokeRegisteredChangeHandler = function (mockStream, event) { return __awaiter(void 0, void 0, void 0, function () {
2205
+ var changeHandler;
2206
+ return __generator(this, function (_a) {
2207
+ switch (_a.label) {
2208
+ case 0:
2209
+ changeHandler = mockStream.listeners.get("change");
2210
+ if (!changeHandler) {
2211
+ throw new Error("expected change handler");
2212
+ }
2213
+ return [4 /*yield*/, changeHandler(event)];
2214
+ case 1:
2215
+ _a.sent();
2216
+ return [2 /*return*/];
2217
+ }
2218
+ });
2219
+ }); };
2204
2220
  var createMockIo = function () {
2205
2221
  var rooms = new Map();
2206
2222
  var sockets = new Map();
@@ -2272,7 +2288,7 @@ var createMockSocket = function (decodedToken) {
2272
2288
  });
2273
2289
  }); });
2274
2290
  (0, bun_test_1.it)("handles change events for registered models", function () { return __awaiter(void 0, void 0, void 0, function () {
2275
- var mockStream, mockDb, startChangeStreamWatcher, io, changeHandler;
2291
+ var mockStream, mockDb, startChangeStreamWatcher, io;
2276
2292
  return __generator(this, function (_a) {
2277
2293
  switch (_a.label) {
2278
2294
  case 0:
@@ -2304,22 +2320,22 @@ var createMockSocket = function (decodedToken) {
2304
2320
  startChangeStreamWatcher = (_a.sent()).startChangeStreamWatcher;
2305
2321
  io = createMockIo();
2306
2322
  startChangeStreamWatcher(io, {}, true);
2307
- changeHandler = mockStream.listeners.get("change");
2308
- (0, bun_test_1.expect)(changeHandler).toBeDefined();
2309
- return [4 /*yield*/, changeHandler({
2323
+ // Trigger an insert change event
2324
+ return [4 /*yield*/, invokeRegisteredChangeHandler(mockStream, {
2310
2325
  documentKey: { _id: "doc-1" },
2311
2326
  fullDocument: { _id: "doc-1", name: "Test Todo" },
2312
2327
  ns: { coll: "todos" },
2313
2328
  operationType: "insert",
2314
2329
  })];
2315
2330
  case 2:
2331
+ // Trigger an insert change event
2316
2332
  _a.sent();
2317
2333
  return [2 /*return*/];
2318
2334
  }
2319
2335
  });
2320
2336
  }); });
2321
2337
  (0, bun_test_1.it)("skips events for unregistered collections", function () { return __awaiter(void 0, void 0, void 0, function () {
2322
- var mockStream, mockDb, startChangeStreamWatcher, io, changeHandler;
2338
+ var mockStream, mockDb, startChangeStreamWatcher, io;
2323
2339
  return __generator(this, function (_a) {
2324
2340
  switch (_a.label) {
2325
2341
  case 0:
@@ -2333,9 +2349,8 @@ var createMockSocket = function (decodedToken) {
2333
2349
  startChangeStreamWatcher = (_a.sent()).startChangeStreamWatcher;
2334
2350
  io = createMockIo();
2335
2351
  startChangeStreamWatcher(io, {}, true);
2336
- changeHandler = mockStream.listeners.get("change");
2337
2352
  // Trigger for an unregistered collection — should not throw
2338
- return [4 /*yield*/, changeHandler({
2353
+ return [4 /*yield*/, invokeRegisteredChangeHandler(mockStream, {
2339
2354
  documentKey: { _id: "doc-1" },
2340
2355
  fullDocument: { _id: "doc-1" },
2341
2356
  ns: { coll: "unknown_collection" },
@@ -2349,7 +2364,7 @@ var createMockSocket = function (decodedToken) {
2349
2364
  });
2350
2365
  }); });
2351
2366
  (0, bun_test_1.it)("skips events when method is not enabled for the model", function () { return __awaiter(void 0, void 0, void 0, function () {
2352
- var mockStream, mockDb, startChangeStreamWatcher, io, changeHandler;
2367
+ var mockStream, mockDb, startChangeStreamWatcher, io;
2353
2368
  return __generator(this, function (_a) {
2354
2369
  switch (_a.label) {
2355
2370
  case 0:
@@ -2381,9 +2396,8 @@ var createMockSocket = function (decodedToken) {
2381
2396
  startChangeStreamWatcher = (_a.sent()).startChangeStreamWatcher;
2382
2397
  io = createMockIo();
2383
2398
  startChangeStreamWatcher(io, {}, true);
2384
- changeHandler = mockStream.listeners.get("change");
2385
2399
  // Update event should be skipped because "update" not in methods
2386
- return [4 /*yield*/, changeHandler({
2400
+ return [4 /*yield*/, invokeRegisteredChangeHandler(mockStream, {
2387
2401
  documentKey: { _id: "doc-1" },
2388
2402
  fullDocument: { _id: "doc-1", name: "Updated" },
2389
2403
  ns: { coll: "todos" },
@@ -2398,7 +2412,7 @@ var createMockSocket = function (decodedToken) {
2398
2412
  });
2399
2413
  }); });
2400
2414
  (0, bun_test_1.it)("handles delete events for owner-strategy models", function () { return __awaiter(void 0, void 0, void 0, function () {
2401
- var mockStream, mockDb, startChangeStreamWatcher, io, changeHandler;
2415
+ var mockStream, mockDb, startChangeStreamWatcher, io;
2402
2416
  return __generator(this, function (_a) {
2403
2417
  switch (_a.label) {
2404
2418
  case 0:
@@ -2430,9 +2444,8 @@ var createMockSocket = function (decodedToken) {
2430
2444
  startChangeStreamWatcher = (_a.sent()).startChangeStreamWatcher;
2431
2445
  io = createMockIo();
2432
2446
  startChangeStreamWatcher(io, {}, true);
2433
- changeHandler = mockStream.listeners.get("change");
2434
2447
  // Hard delete (no fullDocument)
2435
- return [4 /*yield*/, changeHandler({
2448
+ return [4 /*yield*/, invokeRegisteredChangeHandler(mockStream, {
2436
2449
  documentKey: { _id: "doc-1" },
2437
2450
  ns: { coll: "todos" },
2438
2451
  operationType: "delete",
@@ -2445,7 +2458,7 @@ var createMockSocket = function (decodedToken) {
2445
2458
  });
2446
2459
  }); });
2447
2460
  (0, bun_test_1.it)("handles delete events for broadcast-strategy models", function () { return __awaiter(void 0, void 0, void 0, function () {
2448
- var mockStream, mockDb, startChangeStreamWatcher, io, changeHandler;
2461
+ var mockStream, mockDb, startChangeStreamWatcher, io;
2449
2462
  return __generator(this, function (_a) {
2450
2463
  switch (_a.label) {
2451
2464
  case 0:
@@ -2477,9 +2490,8 @@ var createMockSocket = function (decodedToken) {
2477
2490
  startChangeStreamWatcher = (_a.sent()).startChangeStreamWatcher;
2478
2491
  io = createMockIo();
2479
2492
  startChangeStreamWatcher(io, {}, true);
2480
- changeHandler = mockStream.listeners.get("change");
2481
2493
  // Hard delete for broadcast strategy
2482
- return [4 /*yield*/, changeHandler({
2494
+ return [4 /*yield*/, invokeRegisteredChangeHandler(mockStream, {
2483
2495
  documentKey: { _id: "doc-1" },
2484
2496
  ns: { coll: "broadcasts" },
2485
2497
  operationType: "delete",
@@ -2492,7 +2504,7 @@ var createMockSocket = function (decodedToken) {
2492
2504
  });
2493
2505
  }); });
2494
2506
  (0, bun_test_1.it)("includes updatedFields in event for update operations", function () { return __awaiter(void 0, void 0, void 0, function () {
2495
- var mockStream, mockDb, startChangeStreamWatcher, io, changeHandler;
2507
+ var mockStream, mockDb, startChangeStreamWatcher, io;
2496
2508
  return __generator(this, function (_a) {
2497
2509
  switch (_a.label) {
2498
2510
  case 0:
@@ -2524,8 +2536,7 @@ var createMockSocket = function (decodedToken) {
2524
2536
  startChangeStreamWatcher = (_a.sent()).startChangeStreamWatcher;
2525
2537
  io = createMockIo();
2526
2538
  startChangeStreamWatcher(io, {}, true);
2527
- changeHandler = mockStream.listeners.get("change");
2528
- return [4 /*yield*/, changeHandler({
2539
+ return [4 /*yield*/, invokeRegisteredChangeHandler(mockStream, {
2529
2540
  documentKey: { _id: "doc-1" },
2530
2541
  fullDocument: { _id: "doc-1", name: "Updated", status: "done" },
2531
2542
  ns: { coll: "todos" },
@@ -2563,7 +2574,7 @@ var createMockSocket = function (decodedToken) {
2563
2574
  });
2564
2575
  }); });
2565
2576
  (0, bun_test_1.it)("respects ignoredOperations config", function () { return __awaiter(void 0, void 0, void 0, function () {
2566
- var mockStream, mockDb, startChangeStreamWatcher, io, changeHandler;
2577
+ var mockStream, mockDb, startChangeStreamWatcher, io;
2567
2578
  return __generator(this, function (_a) {
2568
2579
  switch (_a.label) {
2569
2580
  case 0:
@@ -2595,9 +2606,8 @@ var createMockSocket = function (decodedToken) {
2595
2606
  startChangeStreamWatcher = (_a.sent()).startChangeStreamWatcher;
2596
2607
  io = createMockIo();
2597
2608
  startChangeStreamWatcher(io, { ignoredOperations: ["insert"] }, true);
2598
- changeHandler = mockStream.listeners.get("change");
2599
2609
  // This insert should be skipped because "insert" is ignored
2600
- return [4 /*yield*/, changeHandler({
2610
+ return [4 /*yield*/, invokeRegisteredChangeHandler(mockStream, {
2601
2611
  documentKey: { _id: "doc-1" },
2602
2612
  fullDocument: { _id: "doc-1" },
2603
2613
  ns: { coll: "todos" },
@@ -2611,7 +2621,7 @@ var createMockSocket = function (decodedToken) {
2611
2621
  });
2612
2622
  }); });
2613
2623
  (0, bun_test_1.it)("skips events with no collectionName or docId", function () { return __awaiter(void 0, void 0, void 0, function () {
2614
- var mockStream, mockDb, startChangeStreamWatcher, io, changeHandler;
2624
+ var mockStream, mockDb, startChangeStreamWatcher, io;
2615
2625
  return __generator(this, function (_a) {
2616
2626
  switch (_a.label) {
2617
2627
  case 0:
@@ -2625,9 +2635,8 @@ var createMockSocket = function (decodedToken) {
2625
2635
  startChangeStreamWatcher = (_a.sent()).startChangeStreamWatcher;
2626
2636
  io = createMockIo();
2627
2637
  startChangeStreamWatcher(io, {}, true);
2628
- changeHandler = mockStream.listeners.get("change");
2629
2638
  // Missing ns.coll
2630
- return [4 /*yield*/, changeHandler({
2639
+ return [4 /*yield*/, invokeRegisteredChangeHandler(mockStream, {
2631
2640
  documentKey: { _id: "doc-1" },
2632
2641
  ns: {},
2633
2642
  operationType: "insert",
@@ -2636,7 +2645,7 @@ var createMockSocket = function (decodedToken) {
2636
2645
  // Missing ns.coll
2637
2646
  _a.sent();
2638
2647
  // Missing documentKey
2639
- return [4 /*yield*/, changeHandler({
2648
+ return [4 /*yield*/, invokeRegisteredChangeHandler(mockStream, {
2640
2649
  documentKey: {},
2641
2650
  ns: { coll: "todos" },
2642
2651
  operationType: "insert",
@@ -2649,7 +2658,7 @@ var createMockSocket = function (decodedToken) {
2649
2658
  });
2650
2659
  }); });
2651
2660
  (0, bun_test_1.it)("skips non-CRUD operation types", function () { return __awaiter(void 0, void 0, void 0, function () {
2652
- var mockStream, mockDb, startChangeStreamWatcher, io, changeHandler;
2661
+ var mockStream, mockDb, startChangeStreamWatcher, io;
2653
2662
  return __generator(this, function (_a) {
2654
2663
  switch (_a.label) {
2655
2664
  case 0:
@@ -2663,9 +2672,8 @@ var createMockSocket = function (decodedToken) {
2663
2672
  startChangeStreamWatcher = (_a.sent()).startChangeStreamWatcher;
2664
2673
  io = createMockIo();
2665
2674
  startChangeStreamWatcher(io, {}, true);
2666
- changeHandler = mockStream.listeners.get("change");
2667
2675
  // "drop" is not in our pipeline filter, should be skipped
2668
- return [4 /*yield*/, changeHandler({
2676
+ return [4 /*yield*/, invokeRegisteredChangeHandler(mockStream, {
2669
2677
  operationType: "drop",
2670
2678
  })];
2671
2679
  case 2:
@@ -2721,7 +2729,7 @@ var createMockSocket = function (decodedToken) {
2721
2729
  });
2722
2730
  }); });
2723
2731
  (0, bun_test_1.it)("catches errors thrown in the change handler", function () { return __awaiter(void 0, void 0, void 0, function () {
2724
- var mockStream, mockDb, startChangeStreamWatcher, emissions, mockSocket, rooms, sockets, io, changeHandler;
2732
+ var mockStream, mockDb, startChangeStreamWatcher, emissions, mockSocket, rooms, sockets, io;
2725
2733
  return __generator(this, function (_a) {
2726
2734
  switch (_a.label) {
2727
2735
  case 0:
@@ -2776,9 +2784,8 @@ var createMockSocket = function (decodedToken) {
2776
2784
  to: function () { return ({ emit: function () { } }); },
2777
2785
  };
2778
2786
  startChangeStreamWatcher(io, {}, true);
2779
- changeHandler = mockStream.listeners.get("change");
2780
2787
  // Should not throw even though permission check throws
2781
- return [4 /*yield*/, changeHandler({
2788
+ return [4 /*yield*/, invokeRegisteredChangeHandler(mockStream, {
2782
2789
  documentKey: { _id: "doc-1" },
2783
2790
  fullDocument: { _id: "doc-1", name: "Test" },
2784
2791
  ns: { coll: "todos" },
@@ -2862,7 +2869,7 @@ var createMockSocket = function (decodedToken) {
2862
2869
  });
2863
2870
  }); });
2864
2871
  var makeServer = function () {
2865
- var http = require("http");
2872
+ var http = require("node:http");
2866
2873
  var server = http.createServer();
2867
2874
  servers.push(server);
2868
2875
  return server;
@@ -2925,7 +2932,7 @@ var createMockSocket = function (decodedToken) {
2925
2932
  });
2926
2933
  }); });
2927
2934
  var makeServer = function () {
2928
- var http = require("http");
2935
+ var http = require("node:http");
2929
2936
  var server = http.createServer();
2930
2937
  servers.push(server);
2931
2938
  return server;
@@ -15,7 +15,11 @@ import type { SecretProvider } from "./configurationPlugin";
15
15
  */
16
16
  export declare class EnvSecretProvider implements SecretProvider {
17
17
  name: string;
18
- getSecret(secretName: string): Promise<string | null>;
18
+ /**
19
+ * Resolve a secret from an environment variable. Environment variables have no
20
+ * versions, so the optional `version` parameter is ignored.
21
+ */
22
+ getSecret(secretName: string, _version?: string): Promise<string | null>;
19
23
  }
20
24
  /**
21
25
  * Options for GcpSecretProvider.
@@ -43,5 +47,78 @@ export declare class GcpSecretProvider implements SecretProvider {
43
47
  private client;
44
48
  constructor(options: GcpSecretProviderOptions);
45
49
  private getClient;
46
- getSecret(secretName: string): Promise<string | null>;
50
+ /**
51
+ * Resolve a secret from Google Cloud Secret Manager.
52
+ *
53
+ * @param secretName - A short secret id (e.g. "openai-api-key") or a full
54
+ * resource path (e.g. "projects/p/secrets/s" or
55
+ * "projects/p/secrets/s/versions/3").
56
+ * @param version - Optional version to resolve when `secretName` is a short id
57
+ * (e.g. "3"). Defaults to "latest". Ignored when `secretName` already
58
+ * contains an explicit `/versions/...` suffix.
59
+ */
60
+ getSecret(secretName: string, version?: string): Promise<string | null>;
61
+ }
62
+ /**
63
+ * Secret provider that delegates to an ordered list of providers, returning the
64
+ * first non-null result.
65
+ *
66
+ * A provider that throws is warn-logged (secret name only — never the value) and
67
+ * resolution falls through to the next provider. This makes it easy to compose a
68
+ * primary provider with a fallback, e.g. GCP with an environment-variable
69
+ * fallback:
70
+ *
71
+ * @example
72
+ * ```typescript
73
+ * const provider = new CompositeSecretProvider([
74
+ * new GcpSecretProvider({projectId: "my-project"}),
75
+ * new EnvSecretProvider(),
76
+ * ]);
77
+ * const key = await provider.getSecret("openai-api-key");
78
+ * ```
79
+ */
80
+ export declare class CompositeSecretProvider implements SecretProvider {
81
+ name: string;
82
+ private providers;
83
+ constructor(providers: SecretProvider[]);
84
+ getSecret(secretName: string, version?: string): Promise<string | null>;
85
+ }
86
+ /**
87
+ * Options for CachingSecretProvider.
88
+ */
89
+ export interface CachingSecretProviderOptions {
90
+ /** Time-to-live for cached values, in milliseconds. Defaults to 60_000 (1 minute). */
91
+ ttlMs?: number;
92
+ }
93
+ /**
94
+ * Secret provider that wraps any provider with an in-memory TTL cache.
95
+ *
96
+ * Cache entries are keyed by `secretName@version` so that pinned versions are
97
+ * cached independently. `null` results (secret not found) are cached too, to
98
+ * avoid hammering the underlying provider for missing secrets. Secret values are
99
+ * never logged.
100
+ *
101
+ * Use `clear()` to drop the entire cache (e.g. on rotation) or `clearKey()` to
102
+ * invalidate a single secret.
103
+ *
104
+ * @example
105
+ * ```typescript
106
+ * const provider = new CachingSecretProvider(
107
+ * new CompositeSecretProvider([gcp, env]),
108
+ * {ttlMs: 30_000}
109
+ * );
110
+ * ```
111
+ */
112
+ export declare class CachingSecretProvider implements SecretProvider {
113
+ name: string;
114
+ private provider;
115
+ private ttlMs;
116
+ private cache;
117
+ constructor(provider: SecretProvider, options?: CachingSecretProviderOptions);
118
+ private cacheKey;
119
+ getSecret(secretName: string, version?: string): Promise<string | null>;
120
+ /** Clears the entire cache. Useful on secret rotation and in tests. */
121
+ clear(): void;
122
+ /** Invalidates a single cached secret by name (and optional version). */
123
+ clearKey(secretName: string, version?: string): void;
47
124
  }