@terreno/api 0.0.17 → 0.1.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 (77) hide show
  1. package/.claude/CLAUDE.local.md +204 -0
  2. package/.cursor/rules/00-root.mdc +338 -0
  3. package/.github/copilot-instructions.md +333 -0
  4. package/AGENTS.md +333 -0
  5. package/README.md +76 -7
  6. package/biome.jsonc +1 -1
  7. package/dist/api.d.ts +68 -1
  8. package/dist/api.js +140 -5
  9. package/dist/api.query.test.js +1 -1
  10. package/dist/api.test.js +222 -484
  11. package/dist/auth.js +3 -1
  12. package/dist/errors.js +15 -12
  13. package/dist/example.js +7 -7
  14. package/dist/expressServer.d.ts +8 -2
  15. package/dist/expressServer.js +8 -1
  16. package/dist/githubAuth.d.ts +64 -0
  17. package/dist/githubAuth.js +293 -0
  18. package/dist/githubAuth.test.d.ts +1 -0
  19. package/dist/githubAuth.test.js +351 -0
  20. package/dist/index.d.ts +3 -0
  21. package/dist/index.js +3 -0
  22. package/dist/logger.js +1 -1
  23. package/dist/middleware.js +1 -1
  24. package/dist/notifiers/googleChatNotifier.js +1 -1
  25. package/dist/notifiers/googleChatNotifier.test.js +1 -1
  26. package/dist/notifiers/slackNotifier.js +1 -1
  27. package/dist/notifiers/slackNotifier.test.js +1 -1
  28. package/dist/notifiers/zoomNotifier.js +1 -1
  29. package/dist/notifiers/zoomNotifier.test.js +1 -1
  30. package/dist/openApi.test.js +8 -5
  31. package/dist/openApiBuilder.d.ts +69 -1
  32. package/dist/openApiBuilder.js +109 -5
  33. package/dist/openApiValidator.d.ts +296 -0
  34. package/dist/openApiValidator.js +698 -0
  35. package/dist/openApiValidator.test.d.ts +1 -0
  36. package/dist/openApiValidator.test.js +346 -0
  37. package/dist/permissions.js +1 -1
  38. package/dist/plugins.test.js +3 -3
  39. package/dist/terrenoPlugin.d.ts +4 -0
  40. package/dist/terrenoPlugin.js +2 -0
  41. package/dist/tests/bunSetup.js +2 -2
  42. package/dist/tests.js +34 -24
  43. package/package.json +7 -2
  44. package/src/__snapshots__/openApi.test.ts.snap +399 -0
  45. package/src/__snapshots__/openApiBuilder.test.ts.snap +108 -0
  46. package/src/api.query.test.ts +1 -1
  47. package/src/api.test.ts +161 -374
  48. package/src/api.ts +210 -4
  49. package/src/auth.ts +3 -1
  50. package/src/errors.ts +15 -12
  51. package/src/example.ts +7 -7
  52. package/src/expressServer.ts +18 -2
  53. package/src/githubAuth.test.ts +223 -0
  54. package/src/githubAuth.ts +335 -0
  55. package/src/index.ts +3 -0
  56. package/src/logger.ts +1 -1
  57. package/src/middleware.ts +1 -1
  58. package/src/notifiers/googleChatNotifier.test.ts +1 -1
  59. package/src/notifiers/googleChatNotifier.ts +1 -1
  60. package/src/notifiers/slackNotifier.test.ts +1 -1
  61. package/src/notifiers/slackNotifier.ts +1 -1
  62. package/src/notifiers/zoomNotifier.test.ts +1 -1
  63. package/src/notifiers/zoomNotifier.ts +1 -1
  64. package/src/openApi.test.ts +8 -5
  65. package/src/openApiBuilder.ts +188 -15
  66. package/src/openApiValidator.test.ts +241 -0
  67. package/src/openApiValidator.ts +860 -0
  68. package/src/permissions.ts +1 -1
  69. package/src/plugins.test.ts +3 -3
  70. package/src/terrenoPlugin.ts +5 -0
  71. package/src/tests/bunSetup.ts +2 -2
  72. package/src/tests.ts +34 -24
  73. package/CLAUDE.md +0 -107
  74. package/dist/response.d.ts +0 -0
  75. package/dist/response.js +0 -1
  76. package/index.ts +0 -1
  77. package/src/response.ts +0 -0
@@ -0,0 +1,351 @@
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 __importDefault = (this && this.__importDefault) || function (mod) {
72
+ return (mod && mod.__esModule) ? mod : { "default": mod };
73
+ };
74
+ Object.defineProperty(exports, "__esModule", { value: true });
75
+ var bun_test_1 = require("bun:test");
76
+ var mongoose_1 = __importStar(require("mongoose"));
77
+ var passport_local_mongoose_1 = __importDefault(require("passport-local-mongoose"));
78
+ var supertest_1 = __importDefault(require("supertest"));
79
+ var expressServer_1 = require("./expressServer");
80
+ var githubAuth_1 = require("./githubAuth");
81
+ var logger_1 = require("./logger");
82
+ var plugins_1 = require("./plugins");
83
+ // Create schema for GitHub-enabled user
84
+ var testUserSchema = new mongoose_1.Schema({
85
+ admin: { default: false, description: "Whether the user has admin privileges", type: Boolean },
86
+ name: { description: "The user's display name", type: String },
87
+ username: { description: "The user's username", type: String },
88
+ });
89
+ testUserSchema.plugin(passport_local_mongoose_1.default, {
90
+ attemptsField: "attempts",
91
+ interval: 1,
92
+ limitAttempts: true,
93
+ maxAttempts: 3,
94
+ maxInterval: 1,
95
+ usernameCaseInsensitive: true,
96
+ usernameField: "email",
97
+ });
98
+ testUserSchema.plugin(plugins_1.createdUpdatedPlugin);
99
+ testUserSchema.plugin(plugins_1.isDisabledPlugin);
100
+ testUserSchema.plugin(githubAuth_1.githubUserPlugin);
101
+ // Get or create model to avoid model redefinition errors
102
+ var GitHubTestUserModel = mongoose_1.default.models.GitHubTestUser || (0, mongoose_1.model)("GitHubTestUser", testUserSchema);
103
+ // Connect to database before tests
104
+ var connectDb = function () { return __awaiter(void 0, void 0, void 0, function () {
105
+ return __generator(this, function (_a) {
106
+ switch (_a.label) {
107
+ case 0:
108
+ if (!(mongoose_1.default.connection.readyState === 0)) return [3 /*break*/, 2];
109
+ return [4 /*yield*/, mongoose_1.default
110
+ .connect("mongodb://127.0.0.1/terreno?&connectTimeoutMS=360000")
111
+ .catch(logger_1.logger.catch)];
112
+ case 1:
113
+ _a.sent();
114
+ _a.label = 2;
115
+ case 2:
116
+ process.env.REFRESH_TOKEN_SECRET = "refresh_secret";
117
+ process.env.TOKEN_SECRET = "secret";
118
+ process.env.TOKEN_EXPIRES_IN = "30m";
119
+ process.env.TOKEN_ISSUER = "example.com";
120
+ process.env.SESSION_SECRET = "session";
121
+ return [2 /*return*/];
122
+ }
123
+ });
124
+ }); };
125
+ (0, bun_test_1.describe)("githubUserPlugin", function () {
126
+ (0, bun_test_1.it)("adds GitHub fields to schema", function () {
127
+ var paths = testUserSchema.paths;
128
+ (0, bun_test_1.expect)(paths.githubId).toBeDefined();
129
+ (0, bun_test_1.expect)(paths.githubUsername).toBeDefined();
130
+ (0, bun_test_1.expect)(paths.githubProfileUrl).toBeDefined();
131
+ (0, bun_test_1.expect)(paths.githubAvatarUrl).toBeDefined();
132
+ });
133
+ (0, bun_test_1.it)("githubId is indexed and sparse", function () {
134
+ var githubIdPath = testUserSchema.path("githubId");
135
+ (0, bun_test_1.expect)(githubIdPath.options.index).toBe(true);
136
+ (0, bun_test_1.expect)(githubIdPath.options.sparse).toBe(true);
137
+ (0, bun_test_1.expect)(githubIdPath.options.unique).toBe(true);
138
+ });
139
+ });
140
+ (0, bun_test_1.describe)("GitHub auth routes", function () {
141
+ var app;
142
+ var agent;
143
+ (0, bun_test_1.beforeEach)(function () { return __awaiter(void 0, void 0, void 0, function () {
144
+ function addRoutes(router) {
145
+ router.get("/test", function (_req, res) { return res.json({ ok: true }); });
146
+ }
147
+ var testUser;
148
+ return __generator(this, function (_a) {
149
+ switch (_a.label) {
150
+ case 0:
151
+ (0, bun_test_1.setSystemTime)();
152
+ return [4 /*yield*/, connectDb()];
153
+ case 1:
154
+ _a.sent();
155
+ return [4 /*yield*/, GitHubTestUserModel.deleteMany({})];
156
+ case 2:
157
+ _a.sent();
158
+ return [4 /*yield*/, GitHubTestUserModel.create({
159
+ admin: false,
160
+ email: "test@example.com",
161
+ name: "Test User",
162
+ })];
163
+ case 3:
164
+ testUser = _a.sent();
165
+ return [4 /*yield*/, testUser.setPassword("password123")];
166
+ case 4:
167
+ _a.sent();
168
+ return [4 /*yield*/, testUser.save()];
169
+ case 5:
170
+ _a.sent();
171
+ app = (0, expressServer_1.setupServer)({
172
+ addRoutes: addRoutes,
173
+ githubAuth: {
174
+ allowAccountLinking: true,
175
+ callbackURL: "http://localhost:9000/auth/github/callback",
176
+ clientId: "test-client-id",
177
+ clientSecret: "test-client-secret",
178
+ },
179
+ skipListen: true,
180
+ userModel: GitHubTestUserModel,
181
+ });
182
+ agent = supertest_1.default.agent(app);
183
+ return [2 /*return*/];
184
+ }
185
+ });
186
+ }); });
187
+ (0, bun_test_1.afterEach)(function () { return __awaiter(void 0, void 0, void 0, function () {
188
+ return __generator(this, function (_a) {
189
+ (0, bun_test_1.setSystemTime)();
190
+ return [2 /*return*/];
191
+ });
192
+ }); });
193
+ (0, bun_test_1.it)("GET /auth/github redirects to GitHub OAuth", function () { return __awaiter(void 0, void 0, void 0, function () {
194
+ var res;
195
+ return __generator(this, function (_a) {
196
+ switch (_a.label) {
197
+ case 0: return [4 /*yield*/, agent.get("/auth/github").expect(302)];
198
+ case 1:
199
+ res = _a.sent();
200
+ (0, bun_test_1.expect)(res.headers.location).toContain("github.com");
201
+ (0, bun_test_1.expect)(res.headers.location).toContain("client_id=test-client-id");
202
+ return [2 /*return*/];
203
+ }
204
+ });
205
+ }); });
206
+ (0, bun_test_1.it)("GET /auth/github/failure returns 401", function () { return __awaiter(void 0, void 0, void 0, function () {
207
+ var res;
208
+ return __generator(this, function (_a) {
209
+ switch (_a.label) {
210
+ case 0: return [4 /*yield*/, agent.get("/auth/github/failure").expect(401)];
211
+ case 1:
212
+ res = _a.sent();
213
+ (0, bun_test_1.expect)(res.body.message).toBe("GitHub authentication failed");
214
+ return [2 /*return*/];
215
+ }
216
+ });
217
+ }); });
218
+ (0, bun_test_1.it)("DELETE /auth/github/unlink requires authentication", function () { return __awaiter(void 0, void 0, void 0, function () {
219
+ var res;
220
+ return __generator(this, function (_a) {
221
+ switch (_a.label) {
222
+ case 0: return [4 /*yield*/, agent.delete("/auth/github/unlink").expect(401)];
223
+ case 1:
224
+ res = _a.sent();
225
+ (0, bun_test_1.expect)(res.body).toBeDefined();
226
+ return [2 /*return*/];
227
+ }
228
+ });
229
+ }); });
230
+ (0, bun_test_1.it)("DELETE /auth/github/unlink works when authenticated with password", function () { return __awaiter(void 0, void 0, void 0, function () {
231
+ var loginRes, user, res, updatedUser;
232
+ return __generator(this, function (_a) {
233
+ switch (_a.label) {
234
+ case 0: return [4 /*yield*/, agent
235
+ .post("/auth/login")
236
+ .send({ email: "test@example.com", password: "password123" })
237
+ .expect(200)];
238
+ case 1:
239
+ loginRes = _a.sent();
240
+ return [4 /*yield*/, GitHubTestUserModel.findOne({ email: "test@example.com" })];
241
+ case 2:
242
+ user = _a.sent();
243
+ if (!user) return [3 /*break*/, 4];
244
+ user.githubId = "99999";
245
+ user.githubUsername = "testghuser";
246
+ return [4 /*yield*/, user.save()];
247
+ case 3:
248
+ _a.sent();
249
+ _a.label = 4;
250
+ case 4: return [4 /*yield*/, agent
251
+ .delete("/auth/github/unlink")
252
+ .set("authorization", "Bearer ".concat(loginRes.body.data.token))
253
+ .expect(200)];
254
+ case 5:
255
+ res = _a.sent();
256
+ (0, bun_test_1.expect)(res.body.data.message).toBe("GitHub account unlinked successfully");
257
+ return [4 /*yield*/, GitHubTestUserModel.findOne({ email: "test@example.com" })];
258
+ case 6:
259
+ updatedUser = _a.sent();
260
+ (0, bun_test_1.expect)(updatedUser.githubId).toBeUndefined();
261
+ (0, bun_test_1.expect)(updatedUser.githubUsername).toBeUndefined();
262
+ return [2 /*return*/];
263
+ }
264
+ });
265
+ }); });
266
+ (0, bun_test_1.it)("user can have both password and GitHub auth", function () { return __awaiter(void 0, void 0, void 0, function () {
267
+ var user, res, updatedUser;
268
+ return __generator(this, function (_a) {
269
+ switch (_a.label) {
270
+ case 0: return [4 /*yield*/, GitHubTestUserModel.findOne({ email: "test@example.com" })];
271
+ case 1:
272
+ user = _a.sent();
273
+ (0, bun_test_1.expect)(user).toBeDefined();
274
+ if (!user) {
275
+ return [2 /*return*/];
276
+ }
277
+ // Link GitHub
278
+ user.githubId = "88888";
279
+ user.githubUsername = "linkeduser";
280
+ return [4 /*yield*/, user.save()];
281
+ case 2:
282
+ _a.sent();
283
+ return [4 /*yield*/, agent
284
+ .post("/auth/login")
285
+ .send({ email: "test@example.com", password: "password123" })
286
+ .expect(200)];
287
+ case 3:
288
+ res = _a.sent();
289
+ (0, bun_test_1.expect)(res.body.data.token).toBeDefined();
290
+ return [4 /*yield*/, GitHubTestUserModel.findOne({ email: "test@example.com" })];
291
+ case 4:
292
+ updatedUser = _a.sent();
293
+ (0, bun_test_1.expect)(updatedUser).toBeDefined();
294
+ (0, bun_test_1.expect)(updatedUser.githubId).toBe("88888");
295
+ (0, bun_test_1.expect)(updatedUser.githubUsername).toBe("linkeduser");
296
+ return [2 /*return*/];
297
+ }
298
+ });
299
+ }); });
300
+ });
301
+ (0, bun_test_1.describe)("GitHub auth disabled", function () {
302
+ var app;
303
+ var agent;
304
+ (0, bun_test_1.beforeEach)(function () { return __awaiter(void 0, void 0, void 0, function () {
305
+ function addRoutes(router) {
306
+ router.get("/test", function (_req, res) { return res.json({ ok: true }); });
307
+ }
308
+ return __generator(this, function (_a) {
309
+ switch (_a.label) {
310
+ case 0:
311
+ (0, bun_test_1.setSystemTime)();
312
+ return [4 /*yield*/, connectDb()];
313
+ case 1:
314
+ _a.sent();
315
+ return [4 /*yield*/, GitHubTestUserModel.deleteMany({})];
316
+ case 2:
317
+ _a.sent();
318
+ // Setup server WITHOUT GitHub auth
319
+ app = (0, expressServer_1.setupServer)({
320
+ addRoutes: addRoutes,
321
+ skipListen: true,
322
+ userModel: GitHubTestUserModel,
323
+ });
324
+ agent = supertest_1.default.agent(app);
325
+ return [2 /*return*/];
326
+ }
327
+ });
328
+ }); });
329
+ (0, bun_test_1.afterEach)(function () { return __awaiter(void 0, void 0, void 0, function () {
330
+ return __generator(this, function (_a) {
331
+ (0, bun_test_1.setSystemTime)();
332
+ return [2 /*return*/];
333
+ });
334
+ }); });
335
+ (0, bun_test_1.it)("GitHub routes are not available when githubAuth is not configured", function () { return __awaiter(void 0, void 0, void 0, function () {
336
+ return __generator(this, function (_a) {
337
+ switch (_a.label) {
338
+ case 0: return [4 /*yield*/, agent.get("/auth/github").expect(404)];
339
+ case 1:
340
+ _a.sent();
341
+ return [4 /*yield*/, agent.get("/auth/github/callback").expect(404)];
342
+ case 2:
343
+ _a.sent();
344
+ return [4 /*yield*/, agent.delete("/auth/github/unlink").expect(404)];
345
+ case 3:
346
+ _a.sent();
347
+ return [2 /*return*/];
348
+ }
349
+ });
350
+ }); });
351
+ });
package/dist/index.d.ts CHANGED
@@ -2,13 +2,16 @@ export * from "./api";
2
2
  export * from "./auth";
3
3
  export * from "./errors";
4
4
  export * from "./expressServer";
5
+ export * from "./githubAuth";
5
6
  export * from "./logger";
6
7
  export * from "./middleware";
7
8
  export * from "./notifiers";
8
9
  export * from "./openApiBuilder";
9
10
  export * from "./openApiEtag";
11
+ export * from "./openApiValidator";
10
12
  export * from "./permissions";
11
13
  export * from "./plugins";
12
14
  export * from "./populate";
15
+ export * from "./terrenoPlugin";
13
16
  export * from "./transformers";
14
17
  export * from "./utils";
package/dist/index.js CHANGED
@@ -18,13 +18,16 @@ __exportStar(require("./api"), exports);
18
18
  __exportStar(require("./auth"), exports);
19
19
  __exportStar(require("./errors"), exports);
20
20
  __exportStar(require("./expressServer"), exports);
21
+ __exportStar(require("./githubAuth"), exports);
21
22
  __exportStar(require("./logger"), exports);
22
23
  __exportStar(require("./middleware"), exports);
23
24
  __exportStar(require("./notifiers"), exports);
24
25
  __exportStar(require("./openApiBuilder"), exports);
25
26
  __exportStar(require("./openApiEtag"), exports);
27
+ __exportStar(require("./openApiValidator"), exports);
26
28
  __exportStar(require("./permissions"), exports);
27
29
  __exportStar(require("./plugins"), exports);
28
30
  __exportStar(require("./populate"), exports);
31
+ __exportStar(require("./terrenoPlugin"), exports);
29
32
  __exportStar(require("./transformers"), exports);
30
33
  __exportStar(require("./utils"), exports);
package/dist/logger.js CHANGED
@@ -87,7 +87,7 @@ exports.logger = exports.winstonLogger = void 0;
87
87
  exports.setupLogging = setupLogging;
88
88
  var node_fs_1 = __importDefault(require("node:fs"));
89
89
  var node_util_1 = require("node:util");
90
- var Sentry = __importStar(require("@sentry/node"));
90
+ var Sentry = __importStar(require("@sentry/bun"));
91
91
  var winston_1 = __importDefault(require("winston"));
92
92
  function isPrimitive(val) {
93
93
  return val === null || (typeof val !== "object" && typeof val !== "function");
@@ -34,7 +34,7 @@ var __importStar = (this && this.__importStar) || (function () {
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.sentryAppVersionMiddleware = sentryAppVersionMiddleware;
37
- var Sentry = __importStar(require("@sentry/node"));
37
+ var Sentry = __importStar(require("@sentry/bun"));
38
38
  /**
39
39
  * Express middleware that captures the app version from the request header
40
40
  * and adds it as a tag to the current Sentry scope.
@@ -73,7 +73,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
73
73
  };
74
74
  Object.defineProperty(exports, "__esModule", { value: true });
75
75
  exports.sendToGoogleChat = sendToGoogleChat;
76
- var Sentry = __importStar(require("@sentry/node"));
76
+ var Sentry = __importStar(require("@sentry/bun"));
77
77
  var axios_1 = __importDefault(require("axios"));
78
78
  var errors_1 = require("../errors");
79
79
  var logger_1 = require("../logger");
@@ -100,7 +100,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
100
100
  };
101
101
  Object.defineProperty(exports, "__esModule", { value: true });
102
102
  var bun_test_1 = require("bun:test");
103
- var Sentry = __importStar(require("@sentry/node"));
103
+ var Sentry = __importStar(require("@sentry/bun"));
104
104
  var axios_1 = __importDefault(require("axios"));
105
105
  var googleChatNotifier_1 = require("./googleChatNotifier");
106
106
  (0, bun_test_1.describe)("sendToGoogleChat", function () {
@@ -73,7 +73,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
73
73
  };
74
74
  Object.defineProperty(exports, "__esModule", { value: true });
75
75
  exports.sendToSlack = sendToSlack;
76
- var Sentry = __importStar(require("@sentry/node"));
76
+ var Sentry = __importStar(require("@sentry/bun"));
77
77
  var axios_1 = __importDefault(require("axios"));
78
78
  var errors_1 = require("../errors");
79
79
  var logger_1 = require("../logger");
@@ -100,7 +100,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
100
100
  };
101
101
  Object.defineProperty(exports, "__esModule", { value: true });
102
102
  var bun_test_1 = require("bun:test");
103
- var Sentry = __importStar(require("@sentry/node"));
103
+ var Sentry = __importStar(require("@sentry/bun"));
104
104
  var axios_1 = __importDefault(require("axios"));
105
105
  var slackNotifier_1 = require("./slackNotifier");
106
106
  (0, bun_test_1.describe)("sendToSlack", function () {
@@ -73,7 +73,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
73
73
  };
74
74
  Object.defineProperty(exports, "__esModule", { value: true });
75
75
  exports.sendToZoom = sendToZoom;
76
- var Sentry = __importStar(require("@sentry/node"));
76
+ var Sentry = __importStar(require("@sentry/bun"));
77
77
  var axios_1 = __importDefault(require("axios"));
78
78
  var errors_1 = require("../errors");
79
79
  var logger_1 = require("../logger");
@@ -100,7 +100,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
100
100
  };
101
101
  Object.defineProperty(exports, "__esModule", { value: true });
102
102
  var bun_test_1 = require("bun:test");
103
- var Sentry = __importStar(require("@sentry/node"));
103
+ var Sentry = __importStar(require("@sentry/bun"));
104
104
  var axios_1 = __importDefault(require("axios"));
105
105
  var zoomNotifier_1 = require("./zoomNotifier");
106
106
  (0, bun_test_1.describe)("sendToZoom", function () {
@@ -273,13 +273,13 @@ function addRoutes(router, options) {
273
273
  // Ensure that a Number query field supports gt/gte/lt/lte and just a Number
274
274
  (0, bun_test_1.expect)(foodQuery.schema).toEqual({
275
275
  oneOf: [
276
- { type: "number" },
276
+ { description: "Number of calories in the food", type: "number" },
277
277
  {
278
278
  properties: {
279
- $gt: { type: "number" },
280
- $gte: { type: "number" },
281
- $lt: { type: "number" },
282
- $lte: { type: "number" },
279
+ $gt: { description: "Number of calories in the food", type: "number" },
280
+ $gte: { description: "Number of calories in the food", type: "number" },
281
+ $lt: { description: "Number of calories in the food", type: "number" },
282
+ $lte: { description: "Number of calories in the food", type: "number" },
283
283
  },
284
284
  type: "object",
285
285
  },
@@ -393,6 +393,7 @@ function addRoutesPopulate(router, options) {
393
393
  type: "string",
394
394
  },
395
395
  name: {
396
+ description: "The user's display name",
396
397
  type: "string",
397
398
  },
398
399
  },
@@ -406,12 +407,14 @@ function addRoutesPopulate(router, options) {
406
407
  type: "array",
407
408
  });
408
409
  (0, bun_test_1.expect)(properties.likesIds).toEqual({
410
+ description: "User likes for this food",
409
411
  items: {
410
412
  properties: {
411
413
  _id: {
412
414
  type: "string",
413
415
  },
414
416
  likes: {
417
+ description: "Whether the user liked the item",
415
418
  type: "boolean",
416
419
  },
417
420
  userId: {
@@ -153,6 +153,20 @@ export type OpenApiResponse = {
153
153
  };
154
154
  };
155
155
  };
156
+ /**
157
+ * Result from building OpenAPI middleware with schemas exposed.
158
+ * Useful when you want to use the schemas with asyncHandler's validation.
159
+ */
160
+ export interface OpenApiBuildResult {
161
+ /** The OpenAPI documentation middleware */
162
+ middleware: any;
163
+ /** Request body schema if defined */
164
+ bodySchema?: Record<string, OpenApiSchemaProperty>;
165
+ /** Query parameter schemas if defined */
166
+ querySchema?: Record<string, OpenApiSchemaProperty>;
167
+ /** Whether validation was enabled on this builder */
168
+ validationEnabled: boolean;
169
+ }
156
170
  /**
157
171
  * A fluent builder for constructing OpenAPI middleware.
158
172
  *
@@ -184,6 +198,12 @@ export declare class OpenApiMiddlewareBuilder {
184
198
  private options;
185
199
  /** Accumulated OpenAPI configuration from builder methods */
186
200
  private config;
201
+ /** Validation configuration */
202
+ private validationConfig;
203
+ /** Store the raw request body schema for validation */
204
+ private requestBodySchema?;
205
+ /** Store the raw query parameter schemas for validation */
206
+ private queryParamSchemas;
187
207
  /**
188
208
  * Creates a new OpenApiMiddlewareBuilder instance.
189
209
  *
@@ -365,6 +385,51 @@ export declare class OpenApiMiddlewareBuilder {
365
385
  withPathParameter(name: string, schema: OpenApiSchemaProperty, options?: {
366
386
  description?: string;
367
387
  }): this;
388
+ /**
389
+ * Enables runtime validation for this route.
390
+ *
391
+ * When enabled, the built middleware will validate incoming requests
392
+ * against the documented schema before the handler runs.
393
+ *
394
+ * @param options - Optional configuration for validation
395
+ * @param options.body - Enable body validation (default: true if request body is defined)
396
+ * @param options.query - Enable query parameter validation (default: true if query params are defined)
397
+ * @param options.enabled - Override the global validation enabled setting
398
+ * @returns The builder instance for chaining
399
+ *
400
+ * @example
401
+ * ```typescript
402
+ * createOpenApiBuilder(options)
403
+ * .withRequestBody<{name: string}>({name: {type: "string", required: true}})
404
+ * .withValidation() // Enable validation
405
+ * .build();
406
+ * ```
407
+ */
408
+ withValidation(options?: {
409
+ body?: boolean;
410
+ query?: boolean;
411
+ enabled?: boolean;
412
+ }): this;
413
+ /**
414
+ * Builds and returns the OpenAPI middleware along with schemas.
415
+ *
416
+ * This method is useful when you want to use asyncHandler's integrated
417
+ * validation instead of separate validation middleware.
418
+ *
419
+ * @returns Object containing middleware and schemas
420
+ *
421
+ * @example
422
+ * ```typescript
423
+ * const {middleware, bodySchema} = createOpenApiBuilder(options)
424
+ * .withRequestBody<{name: string}>({name: {type: "string", required: true}})
425
+ * .buildWithSchemas();
426
+ *
427
+ * router.post("/users", middleware, asyncHandler(async (req, res) => {
428
+ * // handler code
429
+ * }, {bodySchema, validate: true}));
430
+ * ```
431
+ */
432
+ buildWithSchemas(): OpenApiBuildResult;
368
433
  /**
369
434
  * Builds and returns the OpenAPI middleware.
370
435
  *
@@ -372,10 +437,13 @@ export declare class OpenApiMiddlewareBuilder {
372
437
  * that integrates with the OpenAPI documentation system. If no OpenAPI
373
438
  * path is configured in options, returns a no-op middleware.
374
439
  *
440
+ * If validation was enabled via `withValidation()`, returns an array
441
+ * of middleware: [openApiDocMiddleware, validationMiddleware].
442
+ *
375
443
  * Default error responses (400, 401, 403, 404, 405) are automatically
376
444
  * merged with the configured responses.
377
445
  *
378
- * @returns Express middleware function for OpenAPI documentation
446
+ * @returns Express middleware function(s) for OpenAPI documentation and optional validation
379
447
  *
380
448
  * @example
381
449
  * ```typescript