@terreno/api 0.3.0 → 0.4.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 (45) hide show
  1. package/dist/api.js +9 -8
  2. package/dist/auth.d.ts +1 -1
  3. package/dist/auth.js +7 -1
  4. package/dist/betterAuthSetup.js +15 -9
  5. package/dist/configuration.test.d.ts +1 -0
  6. package/dist/configuration.test.js +699 -0
  7. package/dist/configurationApp.d.ts +91 -0
  8. package/dist/configurationApp.js +407 -0
  9. package/dist/configurationPlugin.d.ts +102 -0
  10. package/dist/configurationPlugin.js +285 -0
  11. package/dist/configurationPlugin.test.d.ts +1 -0
  12. package/dist/configurationPlugin.test.js +509 -0
  13. package/dist/example.js +1 -1
  14. package/dist/expressServer.js +5 -1
  15. package/dist/githubAuth.js +2 -2
  16. package/dist/index.d.ts +5 -0
  17. package/dist/index.js +5 -0
  18. package/dist/openApiCompat.d.ts +23 -0
  19. package/dist/openApiCompat.js +198 -0
  20. package/dist/scriptRunner.d.ts +52 -0
  21. package/dist/scriptRunner.js +231 -0
  22. package/dist/secretProviders.d.ts +47 -0
  23. package/dist/secretProviders.js +214 -0
  24. package/dist/terrenoApp.d.ts +25 -0
  25. package/dist/terrenoApp.js +50 -3
  26. package/dist/tests.d.ts +27 -9
  27. package/dist/tests.js +10 -1
  28. package/package.json +13 -13
  29. package/src/api.ts +9 -8
  30. package/src/auth.ts +7 -1
  31. package/src/betterAuthSetup.ts +5 -3
  32. package/src/configuration.test.ts +398 -0
  33. package/src/configurationApp.ts +359 -0
  34. package/src/configurationPlugin.test.ts +299 -0
  35. package/src/configurationPlugin.ts +288 -0
  36. package/src/example.ts +1 -1
  37. package/src/expressServer.ts +6 -1
  38. package/src/githubAuth.ts +4 -4
  39. package/src/index.ts +5 -0
  40. package/src/openApiCompat.ts +147 -0
  41. package/src/permissions.ts +1 -1
  42. package/src/scriptRunner.ts +219 -0
  43. package/src/secretProviders.ts +109 -0
  44. package/src/terrenoApp.ts +45 -3
  45. package/src/tests.ts +12 -1
@@ -0,0 +1,509 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
36
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
37
+ return new (P || (P = Promise))(function (resolve, reject) {
38
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
39
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
40
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
41
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
42
+ });
43
+ };
44
+ var __generator = (this && this.__generator) || function (thisArg, body) {
45
+ 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);
46
+ return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
47
+ function verb(n) { return function (v) { return step([n, v]); }; }
48
+ function step(op) {
49
+ if (f) throw new TypeError("Generator is already executing.");
50
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
51
+ 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;
52
+ if (y = 0, t) op = [op[0] & 2, t.value];
53
+ switch (op[0]) {
54
+ case 0: case 1: t = op; break;
55
+ case 4: _.label++; return { value: op[1], done: false };
56
+ case 5: _.label++; y = op[1]; op = [0]; continue;
57
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
58
+ default:
59
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
60
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
61
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
62
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
63
+ if (t[2]) _.ops.pop();
64
+ _.trys.pop(); continue;
65
+ }
66
+ op = body.call(thisArg, _);
67
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
68
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
69
+ }
70
+ };
71
+ var __read = (this && this.__read) || function (o, n) {
72
+ var m = typeof Symbol === "function" && o[Symbol.iterator];
73
+ if (!m) return o;
74
+ var i = m.call(o), r, ar = [], e;
75
+ try {
76
+ while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
77
+ }
78
+ catch (error) { e = { error: error }; }
79
+ finally {
80
+ try {
81
+ if (r && !r.done && (m = i["return"])) m.call(i);
82
+ }
83
+ finally { if (e) throw e.error; }
84
+ }
85
+ return ar;
86
+ };
87
+ Object.defineProperty(exports, "__esModule", { value: true });
88
+ var bun_test_1 = require("bun:test");
89
+ var mongoose_1 = __importStar(require("mongoose"));
90
+ var configurationPlugin_1 = require("./configurationPlugin");
91
+ var testConfigSchema = new mongoose_1.Schema({
92
+ apiKey: {
93
+ default: "",
94
+ description: "External API key",
95
+ secret: true,
96
+ secretName: "ext-api-key",
97
+ type: String,
98
+ },
99
+ appName: {
100
+ default: "Test App",
101
+ description: "Application name",
102
+ type: String,
103
+ },
104
+ maintenanceMode: {
105
+ default: false,
106
+ description: "Whether maintenance mode is on",
107
+ type: Boolean,
108
+ },
109
+ nested: {
110
+ type: new mongoose_1.Schema({
111
+ secretToken: {
112
+ default: "",
113
+ description: "A nested secret token",
114
+ secret: true,
115
+ secretName: "nested-token",
116
+ secretProvider: "vault",
117
+ type: String,
118
+ },
119
+ webhookUrl: {
120
+ default: "https://example.com/hook",
121
+ description: "Webhook URL",
122
+ type: String,
123
+ },
124
+ }),
125
+ },
126
+ });
127
+ testConfigSchema.plugin(configurationPlugin_1.configurationPlugin);
128
+ var TestConfigModel = (0, mongoose_1.model)("TestConfiguration", testConfigSchema);
129
+ // --- Simple schema for singleton tests ---
130
+ var simpleSchema = new mongoose_1.Schema({
131
+ value: { default: "default", description: "A value", type: String },
132
+ });
133
+ simpleSchema.plugin(configurationPlugin_1.configurationPlugin);
134
+ var SimpleConfigModel = (0, mongoose_1.model)("SimpleConfiguration", simpleSchema);
135
+ (0, bun_test_1.describe)("configurationPlugin", function () {
136
+ (0, bun_test_1.describe)("schema setup", function () {
137
+ (0, bun_test_1.it)("adds a _singleton field with unique index", function () {
138
+ var indexes = SimpleConfigModel.schema.indexes();
139
+ var singletonIndex = indexes.find(function (_a) {
140
+ var _b = __read(_a, 1), fields = _b[0];
141
+ return fields._singleton !== undefined;
142
+ });
143
+ (0, bun_test_1.expect)(singletonIndex).toBeDefined();
144
+ (0, bun_test_1.expect)(singletonIndex[1].unique).toBe(true);
145
+ });
146
+ (0, bun_test_1.it)("adds getConfig static", function () {
147
+ (0, bun_test_1.expect)(typeof SimpleConfigModel.getConfig).toBe("function");
148
+ });
149
+ (0, bun_test_1.it)("adds updateConfig static", function () {
150
+ (0, bun_test_1.expect)(typeof SimpleConfigModel.updateConfig).toBe("function");
151
+ });
152
+ (0, bun_test_1.it)("adds getSecretFields static", function () {
153
+ (0, bun_test_1.expect)(typeof TestConfigModel.getSecretFields).toBe("function");
154
+ });
155
+ (0, bun_test_1.it)("adds resolveSecrets static", function () {
156
+ (0, bun_test_1.expect)(typeof TestConfigModel.resolveSecrets).toBe("function");
157
+ });
158
+ });
159
+ (0, bun_test_1.describe)("getSecretFields", function () {
160
+ (0, bun_test_1.it)("discovers top-level secret fields", function () {
161
+ var secrets = TestConfigModel.getSecretFields();
162
+ var apiKeySecret = secrets.find(function (s) { return s.path === "apiKey"; });
163
+ (0, bun_test_1.expect)(apiKeySecret).toBeDefined();
164
+ (0, bun_test_1.expect)(apiKeySecret.secretName).toBe("ext-api-key");
165
+ });
166
+ (0, bun_test_1.it)("discovers nested secret fields", function () {
167
+ var secrets = TestConfigModel.getSecretFields();
168
+ var nestedSecret = secrets.find(function (s) { return s.path === "nested.secretToken"; });
169
+ (0, bun_test_1.expect)(nestedSecret).toBeDefined();
170
+ (0, bun_test_1.expect)(nestedSecret.secretName).toBe("nested-token");
171
+ (0, bun_test_1.expect)(nestedSecret.secretProvider).toBe("vault");
172
+ });
173
+ (0, bun_test_1.it)("does not include non-secret fields", function () {
174
+ var secrets = TestConfigModel.getSecretFields();
175
+ var nonSecret = secrets.find(function (s) { return s.path === "appName"; });
176
+ (0, bun_test_1.expect)(nonSecret).toBeUndefined();
177
+ });
178
+ (0, bun_test_1.it)("returns the correct total count of secret fields", function () {
179
+ var secrets = TestConfigModel.getSecretFields();
180
+ (0, bun_test_1.expect)(secrets.length).toBe(2);
181
+ });
182
+ });
183
+ (0, bun_test_1.describe)("resolveSecrets", function () {
184
+ (0, bun_test_1.it)("resolves secrets from a provider", function () { return __awaiter(void 0, void 0, void 0, function () {
185
+ var provider, resolved;
186
+ return __generator(this, function (_a) {
187
+ switch (_a.label) {
188
+ case 0:
189
+ provider = {
190
+ getSecret: function (name) { return __awaiter(void 0, void 0, void 0, function () {
191
+ return __generator(this, function (_a) {
192
+ if (name === "ext-api-key") {
193
+ return [2 /*return*/, "resolved-api-key"];
194
+ }
195
+ if (name === "nested-token") {
196
+ return [2 /*return*/, "resolved-token"];
197
+ }
198
+ return [2 /*return*/, null];
199
+ });
200
+ }); },
201
+ name: "test-provider",
202
+ };
203
+ return [4 /*yield*/, TestConfigModel.resolveSecrets(provider)];
204
+ case 1:
205
+ resolved = _a.sent();
206
+ (0, bun_test_1.expect)(resolved.get("apiKey")).toBe("resolved-api-key");
207
+ (0, bun_test_1.expect)(resolved.get("nested.secretToken")).toBe("resolved-token");
208
+ return [2 /*return*/];
209
+ }
210
+ });
211
+ }); });
212
+ (0, bun_test_1.it)("handles provider failures gracefully", function () { return __awaiter(void 0, void 0, void 0, function () {
213
+ var provider, resolved;
214
+ return __generator(this, function (_a) {
215
+ switch (_a.label) {
216
+ case 0:
217
+ provider = {
218
+ getSecret: function () { return __awaiter(void 0, void 0, void 0, function () {
219
+ return __generator(this, function (_a) {
220
+ throw new Error("provider down");
221
+ });
222
+ }); },
223
+ name: "failing-provider",
224
+ };
225
+ return [4 /*yield*/, TestConfigModel.resolveSecrets(provider)];
226
+ case 1:
227
+ resolved = _a.sent();
228
+ (0, bun_test_1.expect)(resolved.size).toBe(0);
229
+ return [2 /*return*/];
230
+ }
231
+ });
232
+ }); });
233
+ (0, bun_test_1.it)("handles partial resolution", function () { return __awaiter(void 0, void 0, void 0, function () {
234
+ var provider, resolved;
235
+ return __generator(this, function (_a) {
236
+ switch (_a.label) {
237
+ case 0:
238
+ provider = {
239
+ getSecret: function (name) { return __awaiter(void 0, void 0, void 0, function () {
240
+ return __generator(this, function (_a) {
241
+ if (name === "ext-api-key") {
242
+ return [2 /*return*/, "resolved-key"];
243
+ }
244
+ return [2 /*return*/, null];
245
+ });
246
+ }); },
247
+ name: "partial-provider",
248
+ };
249
+ return [4 /*yield*/, TestConfigModel.resolveSecrets(provider)];
250
+ case 1:
251
+ resolved = _a.sent();
252
+ (0, bun_test_1.expect)(resolved.size).toBe(1);
253
+ (0, bun_test_1.expect)(resolved.get("apiKey")).toBe("resolved-key");
254
+ return [2 /*return*/];
255
+ }
256
+ });
257
+ }); });
258
+ });
259
+ (0, bun_test_1.describe)("singleton behavior (requires MongoDB)", function () {
260
+ var dbConnected = false;
261
+ (0, bun_test_1.beforeAll)(function () { return __awaiter(void 0, void 0, void 0, function () {
262
+ var _a;
263
+ return __generator(this, function (_b) {
264
+ switch (_b.label) {
265
+ case 0:
266
+ _b.trys.push([0, 4, , 5]);
267
+ if (!(mongoose_1.default.connection.readyState === 1)) return [3 /*break*/, 1];
268
+ dbConnected = true;
269
+ return [3 /*break*/, 3];
270
+ case 1: return [4 /*yield*/, mongoose_1.default.connect("mongodb://127.0.0.1/terreno-config-test", {
271
+ connectTimeoutMS: 3000,
272
+ serverSelectionTimeoutMS: 3000,
273
+ })];
274
+ case 2:
275
+ _b.sent();
276
+ dbConnected = true;
277
+ _b.label = 3;
278
+ case 3: return [3 /*break*/, 5];
279
+ case 4:
280
+ _a = _b.sent();
281
+ dbConnected = false;
282
+ return [3 /*break*/, 5];
283
+ case 5: return [2 /*return*/];
284
+ }
285
+ });
286
+ }); });
287
+ (0, bun_test_1.afterAll)(function () { return __awaiter(void 0, void 0, void 0, function () {
288
+ var _a;
289
+ var _b;
290
+ return __generator(this, function (_c) {
291
+ switch (_c.label) {
292
+ case 0:
293
+ if (!(dbConnected && mongoose_1.default.connection.readyState === 1)) return [3 /*break*/, 4];
294
+ _c.label = 1;
295
+ case 1:
296
+ _c.trys.push([1, 3, , 4]);
297
+ return [4 /*yield*/, ((_b = mongoose_1.default.connection.db) === null || _b === void 0 ? void 0 : _b.dropDatabase())];
298
+ case 2:
299
+ _c.sent();
300
+ return [3 /*break*/, 4];
301
+ case 3:
302
+ _a = _c.sent();
303
+ return [3 /*break*/, 4];
304
+ case 4: return [2 /*return*/];
305
+ }
306
+ });
307
+ }); });
308
+ (0, bun_test_1.beforeEach)(function () { return __awaiter(void 0, void 0, void 0, function () {
309
+ var _a;
310
+ return __generator(this, function (_b) {
311
+ switch (_b.label) {
312
+ case 0:
313
+ if (!dbConnected) {
314
+ return [2 /*return*/];
315
+ }
316
+ _b.label = 1;
317
+ case 1:
318
+ _b.trys.push([1, 3, , 4]);
319
+ return [4 /*yield*/, SimpleConfigModel.collection.drop()];
320
+ case 2:
321
+ _b.sent();
322
+ return [3 /*break*/, 4];
323
+ case 3:
324
+ _a = _b.sent();
325
+ return [3 /*break*/, 4];
326
+ case 4: return [4 /*yield*/, SimpleConfigModel.ensureIndexes()];
327
+ case 5:
328
+ _b.sent();
329
+ return [2 /*return*/];
330
+ }
331
+ });
332
+ }); });
333
+ (0, bun_test_1.it)("creates a document via getConfig when none exists", function () { return __awaiter(void 0, void 0, void 0, function () {
334
+ var config;
335
+ return __generator(this, function (_a) {
336
+ switch (_a.label) {
337
+ case 0:
338
+ if (!dbConnected) {
339
+ return [2 /*return*/];
340
+ }
341
+ return [4 /*yield*/, SimpleConfigModel.getConfig()];
342
+ case 1:
343
+ config = _a.sent();
344
+ (0, bun_test_1.expect)(config).toBeDefined();
345
+ (0, bun_test_1.expect)(config.value).toBe("default");
346
+ return [2 /*return*/];
347
+ }
348
+ });
349
+ }); });
350
+ (0, bun_test_1.it)("returns the same document on subsequent getConfig calls", function () { return __awaiter(void 0, void 0, void 0, function () {
351
+ var first, second;
352
+ return __generator(this, function (_a) {
353
+ switch (_a.label) {
354
+ case 0:
355
+ if (!dbConnected) {
356
+ return [2 /*return*/];
357
+ }
358
+ return [4 /*yield*/, SimpleConfigModel.getConfig()];
359
+ case 1:
360
+ first = _a.sent();
361
+ return [4 /*yield*/, SimpleConfigModel.getConfig()];
362
+ case 2:
363
+ second = _a.sent();
364
+ (0, bun_test_1.expect)(first._id.toString()).toBe(second._id.toString());
365
+ return [2 /*return*/];
366
+ }
367
+ });
368
+ }); });
369
+ (0, bun_test_1.it)("prevents creating a second document via save", function () { return __awaiter(void 0, void 0, void 0, function () {
370
+ var duplicate;
371
+ return __generator(this, function (_a) {
372
+ switch (_a.label) {
373
+ case 0:
374
+ if (!dbConnected) {
375
+ return [2 /*return*/];
376
+ }
377
+ return [4 /*yield*/, SimpleConfigModel.getConfig()];
378
+ case 1:
379
+ _a.sent();
380
+ duplicate = new SimpleConfigModel({ value: "duplicate" });
381
+ return [4 /*yield*/, (0, bun_test_1.expect)(duplicate.save()).rejects.toThrow()];
382
+ case 2:
383
+ _a.sent();
384
+ return [2 /*return*/];
385
+ }
386
+ });
387
+ }); });
388
+ (0, bun_test_1.it)("updates an existing document via updateConfig", function () { return __awaiter(void 0, void 0, void 0, function () {
389
+ var updated, count;
390
+ return __generator(this, function (_a) {
391
+ switch (_a.label) {
392
+ case 0:
393
+ if (!dbConnected) {
394
+ return [2 /*return*/];
395
+ }
396
+ return [4 /*yield*/, SimpleConfigModel.getConfig()];
397
+ case 1:
398
+ _a.sent();
399
+ return [4 /*yield*/, SimpleConfigModel.updateConfig({ value: "updated" })];
400
+ case 2:
401
+ updated = _a.sent();
402
+ (0, bun_test_1.expect)(updated.value).toBe("updated");
403
+ return [4 /*yield*/, SimpleConfigModel.countDocuments()];
404
+ case 3:
405
+ count = _a.sent();
406
+ (0, bun_test_1.expect)(count).toBe(1);
407
+ return [2 /*return*/];
408
+ }
409
+ });
410
+ }); });
411
+ (0, bun_test_1.it)("creates a document with values if none exists via updateConfig", function () { return __awaiter(void 0, void 0, void 0, function () {
412
+ var config;
413
+ return __generator(this, function (_a) {
414
+ switch (_a.label) {
415
+ case 0:
416
+ if (!dbConnected) {
417
+ return [2 /*return*/];
418
+ }
419
+ return [4 /*yield*/, SimpleConfigModel.updateConfig({ value: "custom" })];
420
+ case 1:
421
+ config = _a.sent();
422
+ (0, bun_test_1.expect)(config.value).toBe("custom");
423
+ return [2 /*return*/];
424
+ }
425
+ });
426
+ }); });
427
+ (0, bun_test_1.it)("prevents deleteOne", function () { return __awaiter(void 0, void 0, void 0, function () {
428
+ var err_1;
429
+ return __generator(this, function (_a) {
430
+ switch (_a.label) {
431
+ case 0:
432
+ if (!dbConnected) {
433
+ return [2 /*return*/];
434
+ }
435
+ return [4 /*yield*/, SimpleConfigModel.getConfig()];
436
+ case 1:
437
+ _a.sent();
438
+ _a.label = 2;
439
+ case 2:
440
+ _a.trys.push([2, 4, , 5]);
441
+ return [4 /*yield*/, SimpleConfigModel.deleteOne({}).exec()];
442
+ case 3:
443
+ _a.sent();
444
+ bun_test_1.expect.unreachable("Should have thrown");
445
+ return [3 /*break*/, 5];
446
+ case 4:
447
+ err_1 = _a.sent();
448
+ (0, bun_test_1.expect)(err_1.title).toMatch(/Cannot hard-delete the configuration document/);
449
+ return [3 /*break*/, 5];
450
+ case 5: return [2 /*return*/];
451
+ }
452
+ });
453
+ }); });
454
+ (0, bun_test_1.it)("prevents findOneAndDelete", function () { return __awaiter(void 0, void 0, void 0, function () {
455
+ var err_2;
456
+ return __generator(this, function (_a) {
457
+ switch (_a.label) {
458
+ case 0:
459
+ if (!dbConnected) {
460
+ return [2 /*return*/];
461
+ }
462
+ return [4 /*yield*/, SimpleConfigModel.getConfig()];
463
+ case 1:
464
+ _a.sent();
465
+ _a.label = 2;
466
+ case 2:
467
+ _a.trys.push([2, 4, , 5]);
468
+ return [4 /*yield*/, SimpleConfigModel.findOneAndDelete({}).exec()];
469
+ case 3:
470
+ _a.sent();
471
+ bun_test_1.expect.unreachable("Should have thrown");
472
+ return [3 /*break*/, 5];
473
+ case 4:
474
+ err_2 = _a.sent();
475
+ (0, bun_test_1.expect)(err_2.title).toMatch(/Cannot hard-delete the configuration document/);
476
+ return [3 /*break*/, 5];
477
+ case 5: return [2 /*return*/];
478
+ }
479
+ });
480
+ }); });
481
+ (0, bun_test_1.it)("prevents deleteMany", function () { return __awaiter(void 0, void 0, void 0, function () {
482
+ var err_3;
483
+ return __generator(this, function (_a) {
484
+ switch (_a.label) {
485
+ case 0:
486
+ if (!dbConnected) {
487
+ return [2 /*return*/];
488
+ }
489
+ return [4 /*yield*/, SimpleConfigModel.getConfig()];
490
+ case 1:
491
+ _a.sent();
492
+ _a.label = 2;
493
+ case 2:
494
+ _a.trys.push([2, 4, , 5]);
495
+ return [4 /*yield*/, SimpleConfigModel.deleteMany({}).exec()];
496
+ case 3:
497
+ _a.sent();
498
+ bun_test_1.expect.unreachable("Should have thrown");
499
+ return [3 /*break*/, 5];
500
+ case 4:
501
+ err_3 = _a.sent();
502
+ (0, bun_test_1.expect)(err_3.title).toMatch(/Cannot hard-delete the configuration document/);
503
+ return [3 /*break*/, 5];
504
+ case 5: return [2 /*return*/];
505
+ }
506
+ });
507
+ }); });
508
+ });
509
+ });
package/dist/example.js CHANGED
@@ -82,7 +82,7 @@ var schema = new mongoose_1.Schema({
82
82
  var FoodModel = (0, mongoose_1.model)("Food", schema);
83
83
  function getBaseServer() {
84
84
  var app = (0, express_1.default)();
85
- app.all("/*", function (req, res, next) {
85
+ app.use(function (req, res, next) {
86
86
  res.header("Access-Control-Allow-Origin", "*");
87
87
  res.header("Access-Control-Allow-Headers", "*");
88
88
  // intercepts OPTIONS method
@@ -118,6 +118,7 @@ var errors_1 = require("./errors");
118
118
  var githubAuth_1 = require("./githubAuth");
119
119
  var logger_1 = require("./logger");
120
120
  var notifiers_1 = require("./notifiers");
121
+ var openApiCompat_1 = require("./openApiCompat");
121
122
  var openApiEtag_1 = require("./openApiEtag");
122
123
  var SLOW_READ_MAX = 200;
123
124
  var SLOW_WRITE_MAX = 500;
@@ -234,6 +235,8 @@ function initializeRoutes(UserModel, addRoutes, options) {
234
235
  var _a;
235
236
  if (options === void 0) { options = {}; }
236
237
  var app = (0, express_1.default)();
238
+ // Record mount paths on layers for Express 5 → OpenAPI compat
239
+ (0, openApiCompat_1.patchAppUse)(app);
237
240
  // TODO: Log a warning when we hit the array limit.
238
241
  app.set("query parser", function (str) { var _a; return qs_1.default.parse(str, { arrayLimit: (_a = options.arrayLimit) !== null && _a !== void 0 ? _a : 200 }); });
239
242
  app.use((0, cors_1.default)({
@@ -255,7 +258,7 @@ function initializeRoutes(UserModel, addRoutes, options) {
255
258
  next();
256
259
  });
257
260
  // Add Sentry scopes for session, transaction, and userId if any are set
258
- app.all("*", function (req, _res, next) {
261
+ app.use(function (req, _res, next) {
259
262
  var _a;
260
263
  var transactionId = req.header("X-Transaction-ID");
261
264
  var sessionId = req.header("X-Session-ID");
@@ -271,6 +274,7 @@ function initializeRoutes(UserModel, addRoutes, options) {
271
274
  next();
272
275
  });
273
276
  // Add ETag middleware for OpenAPI JSON endpoint before the openapi middleware
277
+ app.use(openApiCompat_1.openApiCompatMiddleware);
274
278
  app.use(openApiEtag_1.openApiEtagMiddleware);
275
279
  var oapi = (0, openapi_1.default)({
276
280
  info: {
@@ -80,7 +80,7 @@ function setupGitHubAuth(_app, userModel, githubOptions) {
80
80
  clientSecret: githubOptions.clientSecret,
81
81
  passReqToCallback: true,
82
82
  scope: scope,
83
- }, function (req, accessToken, refreshToken, profile, done) { return __awaiter(_this, void 0, void 0, function () {
83
+ }, (function (req, accessToken, refreshToken, profile, done) { return __awaiter(_this, void 0, void 0, function () {
84
84
  var existingUser, user, githubId, existingGitHubUser, user, email, existingEmailUser, newUser, error_1;
85
85
  var _a, _b, _c, _d, _e, _f, _g, _h;
86
86
  return __generator(this, function (_j) {
@@ -166,7 +166,7 @@ function setupGitHubAuth(_app, userModel, githubOptions) {
166
166
  case 14: return [2 /*return*/];
167
167
  }
168
168
  });
169
- }); }));
169
+ }); })));
170
170
  }
171
171
  /**
172
172
  * Adds GitHub OAuth routes to the Express application.
package/dist/index.d.ts CHANGED
@@ -3,6 +3,8 @@ export * from "./auth";
3
3
  export * from "./betterAuth";
4
4
  export * from "./betterAuthApp";
5
5
  export * from "./betterAuthSetup";
6
+ export * from "./configurationApp";
7
+ export * from "./configurationPlugin";
6
8
  export * from "./errors";
7
9
  export * from "./expressServer";
8
10
  export * from "./githubAuth";
@@ -10,11 +12,14 @@ export * from "./logger";
10
12
  export * from "./middleware";
11
13
  export * from "./notifiers";
12
14
  export * from "./openApiBuilder";
15
+ export * from "./openApiCompat";
13
16
  export * from "./openApiEtag";
14
17
  export * from "./openApiValidator";
15
18
  export * from "./permissions";
16
19
  export * from "./plugins";
17
20
  export * from "./populate";
21
+ export * from "./scriptRunner";
22
+ export * from "./secretProviders";
18
23
  export * from "./terrenoApp";
19
24
  export * from "./terrenoPlugin";
20
25
  export * from "./transformers";
package/dist/index.js CHANGED
@@ -19,6 +19,8 @@ __exportStar(require("./auth"), exports);
19
19
  __exportStar(require("./betterAuth"), exports);
20
20
  __exportStar(require("./betterAuthApp"), exports);
21
21
  __exportStar(require("./betterAuthSetup"), exports);
22
+ __exportStar(require("./configurationApp"), exports);
23
+ __exportStar(require("./configurationPlugin"), exports);
22
24
  __exportStar(require("./errors"), exports);
23
25
  __exportStar(require("./expressServer"), exports);
24
26
  __exportStar(require("./githubAuth"), exports);
@@ -26,11 +28,14 @@ __exportStar(require("./logger"), exports);
26
28
  __exportStar(require("./middleware"), exports);
27
29
  __exportStar(require("./notifiers"), exports);
28
30
  __exportStar(require("./openApiBuilder"), exports);
31
+ __exportStar(require("./openApiCompat"), exports);
29
32
  __exportStar(require("./openApiEtag"), exports);
30
33
  __exportStar(require("./openApiValidator"), exports);
31
34
  __exportStar(require("./permissions"), exports);
32
35
  __exportStar(require("./plugins"), exports);
33
36
  __exportStar(require("./populate"), exports);
37
+ __exportStar(require("./scriptRunner"), exports);
38
+ __exportStar(require("./secretProviders"), exports);
34
39
  __exportStar(require("./terrenoApp"), exports);
35
40
  __exportStar(require("./terrenoPlugin"), exports);
36
41
  __exportStar(require("./transformers"), exports);
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Patches the Express router stack to add `.regexp` on layers for
3
+ * compatibility with @wesleytodd/openapi, which expects Express 4-style
4
+ * layers with `.regexp.fast_slash`.
5
+ *
6
+ * In Express 5 (router@2.x), layers use `.slash` (boolean) and `.matchers`
7
+ * (array of functions) instead of `.regexp`.
8
+ *
9
+ * @see https://github.com/wesleytodd/express-openapi/issues/70
10
+ */
11
+ /**
12
+ * Wraps an Express app's `use` method to record the mount path on each
13
+ * layer added to the router stack. This runs at setup time so that
14
+ * `patchRouterStack` can read the original path later.
15
+ *
16
+ * Must be called before any routes are registered.
17
+ */
18
+ export declare const patchAppUse: (app: any) => void;
19
+ /**
20
+ * Express middleware that patches the router stack before OpenAPI doc
21
+ * generation. Must be mounted before the openapi middleware.
22
+ */
23
+ export declare const openApiCompatMiddleware: (req: any, _res: any, next: () => void) => void;