@terreno/api 0.0.1

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 (119) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +170 -0
  3. package/biome.jsonc +22 -0
  4. package/bunfig.toml +4 -0
  5. package/dist/api.d.ts +227 -0
  6. package/dist/api.js +1024 -0
  7. package/dist/api.test.d.ts +1 -0
  8. package/dist/api.test.js +2143 -0
  9. package/dist/auth.d.ts +50 -0
  10. package/dist/auth.js +512 -0
  11. package/dist/auth.test.d.ts +1 -0
  12. package/dist/auth.test.js +778 -0
  13. package/dist/errors.d.ts +75 -0
  14. package/dist/errors.js +216 -0
  15. package/dist/example.d.ts +1 -0
  16. package/dist/example.js +118 -0
  17. package/dist/expressServer.d.ts +35 -0
  18. package/dist/expressServer.js +436 -0
  19. package/dist/index.d.ts +14 -0
  20. package/dist/index.js +30 -0
  21. package/dist/logger.d.ts +23 -0
  22. package/dist/logger.js +249 -0
  23. package/dist/middleware.d.ts +10 -0
  24. package/dist/middleware.js +52 -0
  25. package/dist/notifiers/googleChatNotifier.d.ts +5 -0
  26. package/dist/notifiers/googleChatNotifier.js +130 -0
  27. package/dist/notifiers/googleChatNotifier.test.d.ts +1 -0
  28. package/dist/notifiers/googleChatNotifier.test.js +260 -0
  29. package/dist/notifiers/index.d.ts +3 -0
  30. package/dist/notifiers/index.js +19 -0
  31. package/dist/notifiers/slackNotifier.d.ts +5 -0
  32. package/dist/notifiers/slackNotifier.js +130 -0
  33. package/dist/notifiers/slackNotifier.test.d.ts +1 -0
  34. package/dist/notifiers/slackNotifier.test.js +259 -0
  35. package/dist/notifiers/zoomNotifier.d.ts +34 -0
  36. package/dist/notifiers/zoomNotifier.js +181 -0
  37. package/dist/notifiers/zoomNotifier.test.d.ts +1 -0
  38. package/dist/notifiers/zoomNotifier.test.js +370 -0
  39. package/dist/openApi.d.ts +60 -0
  40. package/dist/openApi.js +441 -0
  41. package/dist/openApi.test.d.ts +1 -0
  42. package/dist/openApi.test.js +445 -0
  43. package/dist/openApiBuilder.d.ts +419 -0
  44. package/dist/openApiBuilder.js +424 -0
  45. package/dist/openApiBuilder.test.d.ts +1 -0
  46. package/dist/openApiBuilder.test.js +509 -0
  47. package/dist/openApiEtag.d.ts +7 -0
  48. package/dist/openApiEtag.js +38 -0
  49. package/dist/permissions.d.ts +26 -0
  50. package/dist/permissions.js +331 -0
  51. package/dist/permissions.test.d.ts +1 -0
  52. package/dist/permissions.test.js +413 -0
  53. package/dist/plugins.d.ts +67 -0
  54. package/dist/plugins.js +315 -0
  55. package/dist/plugins.test.d.ts +1 -0
  56. package/dist/plugins.test.js +639 -0
  57. package/dist/populate.d.ts +14 -0
  58. package/dist/populate.js +315 -0
  59. package/dist/populate.test.d.ts +1 -0
  60. package/dist/populate.test.js +133 -0
  61. package/dist/response.d.ts +0 -0
  62. package/dist/response.js +1 -0
  63. package/dist/tests/bunSetup.d.ts +1 -0
  64. package/dist/tests/bunSetup.js +297 -0
  65. package/dist/tests/index.d.ts +1 -0
  66. package/dist/tests/index.js +17 -0
  67. package/dist/tests.d.ts +99 -0
  68. package/dist/tests.js +273 -0
  69. package/dist/transformers.d.ts +25 -0
  70. package/dist/transformers.js +217 -0
  71. package/dist/transformers.test.d.ts +1 -0
  72. package/dist/transformers.test.js +370 -0
  73. package/dist/utils.d.ts +11 -0
  74. package/dist/utils.js +143 -0
  75. package/dist/utils.test.d.ts +1 -0
  76. package/dist/utils.test.js +14 -0
  77. package/index.ts +1 -0
  78. package/package.json +88 -0
  79. package/src/__snapshots__/openApi.test.ts.snap +4814 -0
  80. package/src/__snapshots__/openApiBuilder.test.ts.snap +1485 -0
  81. package/src/api.test.ts +1661 -0
  82. package/src/api.ts +1036 -0
  83. package/src/auth.test.ts +550 -0
  84. package/src/auth.ts +408 -0
  85. package/src/errors.ts +225 -0
  86. package/src/example.ts +99 -0
  87. package/src/express.d.ts +5 -0
  88. package/src/expressServer.ts +387 -0
  89. package/src/index.ts +14 -0
  90. package/src/logger.ts +190 -0
  91. package/src/middleware.ts +18 -0
  92. package/src/notifiers/googleChatNotifier.test.ts +114 -0
  93. package/src/notifiers/googleChatNotifier.ts +47 -0
  94. package/src/notifiers/index.ts +3 -0
  95. package/src/notifiers/slackNotifier.test.ts +113 -0
  96. package/src/notifiers/slackNotifier.ts +55 -0
  97. package/src/notifiers/zoomNotifier.test.ts +207 -0
  98. package/src/notifiers/zoomNotifier.ts +111 -0
  99. package/src/openApi.test.ts +331 -0
  100. package/src/openApi.ts +494 -0
  101. package/src/openApiBuilder.test.ts +442 -0
  102. package/src/openApiBuilder.ts +636 -0
  103. package/src/openApiEtag.ts +40 -0
  104. package/src/permissions.test.ts +219 -0
  105. package/src/permissions.ts +228 -0
  106. package/src/plugins.test.ts +390 -0
  107. package/src/plugins.ts +289 -0
  108. package/src/populate.test.ts +65 -0
  109. package/src/populate.ts +258 -0
  110. package/src/response.ts +0 -0
  111. package/src/tests/bunSetup.ts +234 -0
  112. package/src/tests/index.ts +1 -0
  113. package/src/tests.ts +218 -0
  114. package/src/transformers.test.ts +202 -0
  115. package/src/transformers.ts +170 -0
  116. package/src/utils.test.ts +14 -0
  117. package/src/utils.ts +47 -0
  118. package/tsconfig.json +60 -0
  119. package/types.d.ts +17 -0
@@ -0,0 +1,445 @@
1
+ "use strict";
2
+ var __assign = (this && this.__assign) || function () {
3
+ __assign = Object.assign || function(t) {
4
+ for (var s, i = 1, n = arguments.length; i < n; i++) {
5
+ s = arguments[i];
6
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
7
+ t[p] = s[p];
8
+ }
9
+ return t;
10
+ };
11
+ return __assign.apply(this, arguments);
12
+ };
13
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
14
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
15
+ return new (P || (P = Promise))(function (resolve, reject) {
16
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
17
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
18
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
19
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
20
+ });
21
+ };
22
+ var __generator = (this && this.__generator) || function (thisArg, body) {
23
+ 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);
24
+ return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
25
+ function verb(n) { return function (v) { return step([n, v]); }; }
26
+ function step(op) {
27
+ if (f) throw new TypeError("Generator is already executing.");
28
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
29
+ 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;
30
+ if (y = 0, t) op = [op[0] & 2, t.value];
31
+ switch (op[0]) {
32
+ case 0: case 1: t = op; break;
33
+ case 4: _.label++; return { value: op[1], done: false };
34
+ case 5: _.label++; y = op[1]; op = [0]; continue;
35
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
36
+ default:
37
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
38
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
39
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
40
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
41
+ if (t[2]) _.ops.pop();
42
+ _.trys.pop(); continue;
43
+ }
44
+ op = body.call(thisArg, _);
45
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
46
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
47
+ }
48
+ };
49
+ var __read = (this && this.__read) || function (o, n) {
50
+ var m = typeof Symbol === "function" && o[Symbol.iterator];
51
+ if (!m) return o;
52
+ var i = m.call(o), r, ar = [], e;
53
+ try {
54
+ while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
55
+ }
56
+ catch (error) { e = { error: error }; }
57
+ finally {
58
+ try {
59
+ if (r && !r.done && (m = i["return"])) m.call(i);
60
+ }
61
+ finally { if (e) throw e.error; }
62
+ }
63
+ return ar;
64
+ };
65
+ var __importDefault = (this && this.__importDefault) || function (mod) {
66
+ return (mod && mod.__esModule) ? mod : { "default": mod };
67
+ };
68
+ Object.defineProperty(exports, "__esModule", { value: true });
69
+ var bun_test_1 = require("bun:test");
70
+ var supertest_1 = __importDefault(require("supertest"));
71
+ var api_1 = require("./api");
72
+ var auth_1 = require("./auth");
73
+ var expressServer_1 = require("./expressServer");
74
+ var permissions_1 = require("./permissions");
75
+ var tests_1 = require("./tests");
76
+ function getMessageSummaryOpenApiMiddleware(options) {
77
+ return options.openApi.path({
78
+ parameters: [
79
+ {
80
+ in: "query",
81
+ name: "foodIds",
82
+ schema: {
83
+ type: "string",
84
+ },
85
+ },
86
+ ],
87
+ responses: {
88
+ 200: {
89
+ content: {
90
+ "application/json": {
91
+ schema: {
92
+ properties: {
93
+ message: {
94
+ type: "string",
95
+ },
96
+ },
97
+ type: "object",
98
+ },
99
+ },
100
+ },
101
+ description: "Success",
102
+ },
103
+ },
104
+ tags: ["Food"],
105
+ });
106
+ }
107
+ function addRoutes(router, options) {
108
+ var _this = this;
109
+ router.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, __assign(__assign({}, options), { allowAnonymous: true, openApiExtraModelProperties: {
110
+ foo: {
111
+ type: "string",
112
+ },
113
+ }, permissions: {
114
+ create: [permissions_1.Permissions.IsAny],
115
+ delete: [permissions_1.Permissions.IsAny],
116
+ list: [permissions_1.Permissions.IsAny],
117
+ read: [permissions_1.Permissions.IsAny],
118
+ update: [permissions_1.Permissions.IsAny],
119
+ }, populatePaths: [{ path: "ownerId" }, { path: "eatenBy" }], queryFields: ["calories"] })));
120
+ router.use("/food/count", getMessageSummaryOpenApiMiddleware, function (_req, res) { return __awaiter(_this, void 0, void 0, function () {
121
+ return __generator(this, function (_a) {
122
+ res.json({ message: "count" });
123
+ return [2 /*return*/];
124
+ });
125
+ }); });
126
+ }
127
+ (0, bun_test_1.describe)("openApi", function () {
128
+ var server;
129
+ var app;
130
+ (0, bun_test_1.beforeEach)(function () { return __awaiter(void 0, void 0, void 0, function () {
131
+ return __generator(this, function (_a) {
132
+ process.env.REFRESH_TOKEN_SECRET = "testsecret1234";
133
+ process.env.ENABLE_SWAGGER = "true";
134
+ app = (0, expressServer_1.setupServer)({
135
+ addRoutes: addRoutes,
136
+ skipListen: true,
137
+ userModel: tests_1.UserModel,
138
+ });
139
+ (0, auth_1.setupAuth)(app, tests_1.UserModel);
140
+ (0, auth_1.addAuthRoutes)(app, tests_1.UserModel);
141
+ return [2 /*return*/];
142
+ });
143
+ }); });
144
+ (0, bun_test_1.it)("gets the openapi.json", function () { return __awaiter(void 0, void 0, void 0, function () {
145
+ var res;
146
+ return __generator(this, function (_a) {
147
+ switch (_a.label) {
148
+ case 0:
149
+ server = (0, supertest_1.default)(app);
150
+ return [4 /*yield*/, server.get("/openapi.json").expect(200)];
151
+ case 1:
152
+ res = _a.sent();
153
+ (0, bun_test_1.expect)(res.body).toMatchSnapshot();
154
+ return [2 /*return*/];
155
+ }
156
+ });
157
+ }); });
158
+ (0, bun_test_1.it)("gets the openapi.json with ETag header", function () { return __awaiter(void 0, void 0, void 0, function () {
159
+ var res;
160
+ return __generator(this, function (_a) {
161
+ switch (_a.label) {
162
+ case 0:
163
+ server = (0, supertest_1.default)(app);
164
+ return [4 /*yield*/, server.get("/openapi.json").expect(200)];
165
+ case 1:
166
+ res = _a.sent();
167
+ (0, bun_test_1.expect)(res.headers.etag).toBeDefined();
168
+ (0, bun_test_1.expect)(res.headers.etag).toMatch(/^"[a-f0-9]{16}"$/);
169
+ return [2 /*return*/];
170
+ }
171
+ });
172
+ }); });
173
+ (0, bun_test_1.it)("returns 304 when If-None-Match matches ETag", function () { return __awaiter(void 0, void 0, void 0, function () {
174
+ var firstRes, etag, secondRes;
175
+ return __generator(this, function (_a) {
176
+ switch (_a.label) {
177
+ case 0:
178
+ server = (0, supertest_1.default)(app);
179
+ return [4 /*yield*/, server.get("/openapi.json").expect(200)];
180
+ case 1:
181
+ firstRes = _a.sent();
182
+ etag = firstRes.headers.etag;
183
+ (0, bun_test_1.expect)(etag).toBeDefined();
184
+ return [4 /*yield*/, server.get("/openapi.json").set("If-None-Match", etag).expect(304)];
185
+ case 2:
186
+ secondRes = _a.sent();
187
+ (0, bun_test_1.expect)(secondRes.body).toEqual({});
188
+ (0, bun_test_1.expect)(secondRes.headers.etag).toBe(etag);
189
+ return [2 /*return*/];
190
+ }
191
+ });
192
+ }); });
193
+ (0, bun_test_1.it)("returns 200 when If-None-Match does not match ETag", 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:
198
+ server = (0, supertest_1.default)(app);
199
+ return [4 /*yield*/, server
200
+ .get("/openapi.json")
201
+ .set("If-None-Match", '"different-etag"')
202
+ .expect(200)];
203
+ case 1:
204
+ res = _a.sent();
205
+ (0, bun_test_1.expect)(res.body).toBeDefined();
206
+ (0, bun_test_1.expect)(res.headers.etag).toBeDefined();
207
+ (0, bun_test_1.expect)(res.headers.etag).not.toBe('"different-etag"');
208
+ return [2 /*return*/];
209
+ }
210
+ });
211
+ }); });
212
+ (0, bun_test_1.it)("gets the swagger ui", function () { return __awaiter(void 0, void 0, void 0, function () {
213
+ return __generator(this, function (_a) {
214
+ switch (_a.label) {
215
+ case 0:
216
+ server = (0, supertest_1.default)(app);
217
+ return [4 /*yield*/, server.get("/swagger/").expect(200)];
218
+ case 1:
219
+ _a.sent();
220
+ return [2 /*return*/];
221
+ }
222
+ });
223
+ }); });
224
+ (0, bun_test_1.it)("gets food with populated paths", function () { return __awaiter(void 0, void 0, void 0, function () {
225
+ var _a, _admin, notAdmin, food, res;
226
+ return __generator(this, function (_b) {
227
+ switch (_b.label) {
228
+ case 0:
229
+ server = (0, supertest_1.default)(app);
230
+ return [4 /*yield*/, (0, tests_1.setupDb)()];
231
+ case 1:
232
+ _a = __read.apply(void 0, [_b.sent(), 2]), _admin = _a[0], notAdmin = _a[1];
233
+ return [4 /*yield*/, tests_1.FoodModel.create({
234
+ name: "test",
235
+ ownerId: notAdmin._id,
236
+ })];
237
+ case 2:
238
+ food = _b.sent();
239
+ return [4 /*yield*/, server.get("/food/".concat(food._id)).expect(200)];
240
+ case 3:
241
+ res = _b.sent();
242
+ (0, bun_test_1.expect)(res.body.data.ownerId._id).toEqual(notAdmin._id.toString());
243
+ return [2 /*return*/];
244
+ }
245
+ });
246
+ }); });
247
+ // create a test for a custom express endpoint that doesnt use modelRouter and manually adds it
248
+ // to openapi
249
+ (0, bun_test_1.it)("gets the openapi.json with custom endpoint", function () { return __awaiter(void 0, void 0, void 0, function () {
250
+ var res;
251
+ return __generator(this, function (_a) {
252
+ switch (_a.label) {
253
+ case 0:
254
+ server = (0, supertest_1.default)(app);
255
+ return [4 /*yield*/, server.get("/openapi.json").expect(200)];
256
+ case 1:
257
+ res = _a.sent();
258
+ (0, bun_test_1.expect)(res.body).toMatchSnapshot();
259
+ return [2 /*return*/];
260
+ }
261
+ });
262
+ }); });
263
+ (0, bun_test_1.it)("gets the openapi.json and has correct Number query fields", function () { return __awaiter(void 0, void 0, void 0, function () {
264
+ var res, foodQuery;
265
+ return __generator(this, function (_a) {
266
+ switch (_a.label) {
267
+ case 0:
268
+ server = (0, supertest_1.default)(app);
269
+ return [4 /*yield*/, server.get("/openapi.json").expect(200)];
270
+ case 1:
271
+ res = _a.sent();
272
+ foodQuery = res.body.paths["/food/"].get.parameters.find(function (p) { return p.name === "calories"; });
273
+ // Ensure that a Number query field supports gt/gte/lt/lte and just a Number
274
+ (0, bun_test_1.expect)(foodQuery.schema).toEqual({
275
+ oneOf: [
276
+ { type: "number" },
277
+ {
278
+ properties: {
279
+ $gt: { type: "number" },
280
+ $gte: { type: "number" },
281
+ $lt: { type: "number" },
282
+ $lte: { type: "number" },
283
+ },
284
+ type: "object",
285
+ },
286
+ ],
287
+ });
288
+ (0, bun_test_1.expect)(foodQuery).toMatchSnapshot();
289
+ return [2 /*return*/];
290
+ }
291
+ });
292
+ }); });
293
+ });
294
+ function addRoutesPopulate(router, options) {
295
+ options === null || options === void 0 ? void 0 : options.openApi.component("schemas", "LimitedUser", {
296
+ properties: {
297
+ email: {
298
+ description: "LimitedUser's email",
299
+ type: "string",
300
+ },
301
+ name: {
302
+ description: "LimitedUser's name",
303
+ type: "string",
304
+ },
305
+ },
306
+ type: "object",
307
+ });
308
+ router.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, __assign(__assign({}, options), { allowAnonymous: true, openApiExtraModelProperties: {
309
+ foo: {
310
+ type: "string",
311
+ },
312
+ }, permissions: {
313
+ create: [permissions_1.Permissions.IsAny],
314
+ delete: [permissions_1.Permissions.IsAny],
315
+ list: [permissions_1.Permissions.IsAny],
316
+ read: [permissions_1.Permissions.IsAny],
317
+ update: [permissions_1.Permissions.IsAny],
318
+ }, populatePaths: [
319
+ { fields: ["name", "email"], path: "ownerId" },
320
+ {
321
+ fields: ["name", "email"],
322
+ openApiComponent: "LimitedUser",
323
+ path: "eatenBy",
324
+ },
325
+ {
326
+ fields: ["name", "email"],
327
+ openApiComponent: "LimitedUser",
328
+ path: "likesIds.userId",
329
+ },
330
+ ] })));
331
+ }
332
+ (0, bun_test_1.describe)("openApi without swagger", function () {
333
+ var server;
334
+ var app;
335
+ (0, bun_test_1.beforeEach)(function () { return __awaiter(void 0, void 0, void 0, function () {
336
+ return __generator(this, function (_a) {
337
+ process.env.REFRESH_TOKEN_SECRET = "testsecret1234";
338
+ process.env.ENABLE_SWAGGER = "false";
339
+ app = (0, expressServer_1.setupServer)({
340
+ addRoutes: addRoutes,
341
+ skipListen: true,
342
+ userModel: tests_1.UserModel,
343
+ });
344
+ (0, auth_1.setupAuth)(app, tests_1.UserModel);
345
+ (0, auth_1.addAuthRoutes)(app, tests_1.UserModel);
346
+ return [2 /*return*/];
347
+ });
348
+ }); });
349
+ (0, bun_test_1.it)("does not have the swagger ui", function () { return __awaiter(void 0, void 0, void 0, function () {
350
+ return __generator(this, function (_a) {
351
+ switch (_a.label) {
352
+ case 0:
353
+ server = (0, supertest_1.default)(app);
354
+ return [4 /*yield*/, server.get("/swagger/").expect(404)];
355
+ case 1:
356
+ _a.sent();
357
+ return [2 /*return*/];
358
+ }
359
+ });
360
+ }); });
361
+ });
362
+ (0, bun_test_1.describe)("openApi populate", function () {
363
+ var server;
364
+ var app;
365
+ (0, bun_test_1.beforeEach)(function () { return __awaiter(void 0, void 0, void 0, function () {
366
+ return __generator(this, function (_a) {
367
+ process.env.REFRESH_TOKEN_SECRET = "testsecret1234";
368
+ app = (0, expressServer_1.setupServer)({
369
+ addRoutes: addRoutesPopulate,
370
+ skipListen: true,
371
+ userModel: tests_1.UserModel,
372
+ });
373
+ (0, auth_1.setupAuth)(app, tests_1.UserModel);
374
+ (0, auth_1.addAuthRoutes)(app, tests_1.UserModel);
375
+ return [2 /*return*/];
376
+ });
377
+ }); });
378
+ (0, bun_test_1.it)("gets the openapi.json with populate", function () { return __awaiter(void 0, void 0, void 0, function () {
379
+ var res, properties;
380
+ return __generator(this, function (_a) {
381
+ switch (_a.label) {
382
+ case 0:
383
+ server = (0, supertest_1.default)(app);
384
+ return [4 /*yield*/, server.get("/openapi.json").expect(200)];
385
+ case 1:
386
+ res = _a.sent();
387
+ properties = res.body.paths["/food/{id}"].get.responses["200"].content["application/json"].schema
388
+ .properties;
389
+ // There's no component here, so we automatically generate the limited properties.
390
+ (0, bun_test_1.expect)(properties.ownerId).toEqual({
391
+ properties: {
392
+ email: {
393
+ type: "string",
394
+ },
395
+ name: {
396
+ type: "string",
397
+ },
398
+ },
399
+ type: "object",
400
+ });
401
+ // We only reference the component here, rather than listing each field each time.
402
+ (0, bun_test_1.expect)(properties.eatenBy).toEqual({
403
+ items: {
404
+ $ref: "#/components/schemas/LimitedUser",
405
+ },
406
+ type: "array",
407
+ });
408
+ (0, bun_test_1.expect)(properties.likesIds).toEqual({
409
+ items: {
410
+ properties: {
411
+ _id: {
412
+ type: "string",
413
+ },
414
+ likes: {
415
+ type: "boolean",
416
+ },
417
+ userId: {
418
+ $ref: "#/components/schemas/LimitedUser",
419
+ },
420
+ },
421
+ required: [],
422
+ type: "object",
423
+ },
424
+ type: "array",
425
+ });
426
+ // Ensure the component is registered and used.
427
+ (0, bun_test_1.expect)(res.body.components.schemas.LimitedUser).toEqual({
428
+ properties: {
429
+ email: {
430
+ description: "LimitedUser's email",
431
+ type: "string",
432
+ },
433
+ name: {
434
+ description: "LimitedUser's name",
435
+ type: "string",
436
+ },
437
+ },
438
+ type: "object",
439
+ });
440
+ (0, bun_test_1.expect)(res.body).toMatchSnapshot();
441
+ return [2 /*return*/];
442
+ }
443
+ });
444
+ }); });
445
+ });