@terreno/api 0.9.3 → 0.11.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 (52) hide show
  1. package/bunfig.toml +5 -2
  2. package/bunfig.unit.toml +3 -0
  3. package/dist/auth.test.js +257 -0
  4. package/dist/consentApp.test.js +245 -0
  5. package/dist/expressServer.js +3 -9
  6. package/dist/expressServer.test.js +4 -7
  7. package/dist/githubAuth.test.js +380 -0
  8. package/dist/logger.test.d.ts +1 -0
  9. package/dist/logger.test.js +143 -0
  10. package/dist/notifiers/googleChatNotifier.test.js +37 -0
  11. package/dist/openApi.js +2 -2
  12. package/dist/openApi.test.js +125 -0
  13. package/dist/openApiBuilder.d.ts +1 -0
  14. package/dist/openApiBuilder.js +13 -2
  15. package/dist/openApiBuilder.test.js +66 -0
  16. package/dist/openApiEtag.test.js +8 -0
  17. package/dist/openApiValidator.test.js +309 -0
  18. package/dist/permissions.middleware.test.d.ts +1 -0
  19. package/dist/permissions.middleware.test.js +341 -0
  20. package/dist/plugins.d.ts +8 -8
  21. package/dist/plugins.js +38 -32
  22. package/dist/populate.test.js +99 -0
  23. package/dist/syncConsents.js +2 -2
  24. package/dist/syncConsents.test.js +273 -0
  25. package/dist/tests/bunSetup.js +27 -22
  26. package/dist/tests.d.ts +3 -3
  27. package/dist/tests.js +78 -82
  28. package/dist/utils.d.ts +2 -2
  29. package/dist/utils.js +7 -7
  30. package/package.json +2 -1
  31. package/src/__snapshots__/openApi.test.ts.snap +48 -0
  32. package/src/auth.test.ts +147 -0
  33. package/src/consentApp.test.ts +162 -0
  34. package/src/expressServer.test.ts +4 -11
  35. package/src/expressServer.ts +4 -8
  36. package/src/githubAuth.test.ts +307 -1
  37. package/src/logger.test.ts +149 -0
  38. package/src/notifiers/googleChatNotifier.test.ts +24 -0
  39. package/src/openApi.test.ts +157 -1
  40. package/src/openApi.ts +6 -2
  41. package/src/openApiBuilder.test.ts +81 -0
  42. package/src/openApiBuilder.ts +17 -2
  43. package/src/openApiEtag.test.ts +11 -0
  44. package/src/openApiValidator.test.ts +410 -0
  45. package/src/permissions.middleware.test.ts +197 -0
  46. package/src/plugins.ts +32 -23
  47. package/src/populate.test.ts +78 -2
  48. package/src/syncConsents.test.ts +145 -0
  49. package/src/syncConsents.ts +1 -1
  50. package/src/tests/bunSetup.ts +14 -8
  51. package/src/tests.ts +8 -8
  52. package/src/utils.ts +4 -4
@@ -0,0 +1,341 @@
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 __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
14
+ if (k2 === undefined) k2 = k;
15
+ var desc = Object.getOwnPropertyDescriptor(m, k);
16
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
17
+ desc = { enumerable: true, get: function() { return m[k]; } };
18
+ }
19
+ Object.defineProperty(o, k2, desc);
20
+ }) : (function(o, m, k, k2) {
21
+ if (k2 === undefined) k2 = k;
22
+ o[k2] = m[k];
23
+ }));
24
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
25
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
26
+ }) : function(o, v) {
27
+ o["default"] = v;
28
+ });
29
+ var __importStar = (this && this.__importStar) || (function () {
30
+ var ownKeys = function(o) {
31
+ ownKeys = Object.getOwnPropertyNames || function (o) {
32
+ var ar = [];
33
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
34
+ return ar;
35
+ };
36
+ return ownKeys(o);
37
+ };
38
+ return function (mod) {
39
+ if (mod && mod.__esModule) return mod;
40
+ var result = {};
41
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
42
+ __setModuleDefault(result, mod);
43
+ return result;
44
+ };
45
+ })();
46
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
47
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
48
+ return new (P || (P = Promise))(function (resolve, reject) {
49
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
50
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
51
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
52
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
53
+ });
54
+ };
55
+ var __generator = (this && this.__generator) || function (thisArg, body) {
56
+ 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);
57
+ return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
58
+ function verb(n) { return function (v) { return step([n, v]); }; }
59
+ function step(op) {
60
+ if (f) throw new TypeError("Generator is already executing.");
61
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
62
+ 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;
63
+ if (y = 0, t) op = [op[0] & 2, t.value];
64
+ switch (op[0]) {
65
+ case 0: case 1: t = op; break;
66
+ case 4: _.label++; return { value: op[1], done: false };
67
+ case 5: _.label++; y = op[1]; op = [0]; continue;
68
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
69
+ default:
70
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
71
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
72
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
73
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
74
+ if (t[2]) _.ops.pop();
75
+ _.trys.pop(); continue;
76
+ }
77
+ op = body.call(thisArg, _);
78
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
79
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
80
+ }
81
+ };
82
+ var __read = (this && this.__read) || function (o, n) {
83
+ var m = typeof Symbol === "function" && o[Symbol.iterator];
84
+ if (!m) return o;
85
+ var i = m.call(o), r, ar = [], e;
86
+ try {
87
+ while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
88
+ }
89
+ catch (error) { e = { error: error }; }
90
+ finally {
91
+ try {
92
+ if (r && !r.done && (m = i["return"])) m.call(i);
93
+ }
94
+ finally { if (e) throw e.error; }
95
+ }
96
+ return ar;
97
+ };
98
+ Object.defineProperty(exports, "__esModule", { value: true });
99
+ var bun_test_1 = require("bun:test");
100
+ var Sentry = __importStar(require("@sentry/bun"));
101
+ var errors_1 = require("./errors");
102
+ var permissions_1 = require("./permissions");
103
+ (0, bun_test_1.describe)("permissionMiddleware", function () {
104
+ var allPermissions = {
105
+ create: [permissions_1.Permissions.IsAny],
106
+ delete: [permissions_1.Permissions.IsAny],
107
+ list: [permissions_1.Permissions.IsAny],
108
+ read: [permissions_1.Permissions.IsAny],
109
+ update: [permissions_1.Permissions.IsAny],
110
+ };
111
+ var buildReq = function (overrides) {
112
+ if (overrides === void 0) { overrides = {}; }
113
+ return __assign({ method: "GET", params: {}, user: { id: "user-1" } }, overrides);
114
+ };
115
+ (0, bun_test_1.it)("calls next immediately for OPTIONS requests", function () { return __awaiter(void 0, void 0, void 0, function () {
116
+ var model, middleware, next;
117
+ return __generator(this, function (_a) {
118
+ switch (_a.label) {
119
+ case 0:
120
+ model = {
121
+ collection: { findOne: (0, bun_test_1.mock)(function () { return __awaiter(void 0, void 0, void 0, function () { return __generator(this, function (_a) {
122
+ return [2 /*return*/, null];
123
+ }); }); }) },
124
+ findById: (0, bun_test_1.mock)(function () { return ({ exec: (0, bun_test_1.mock)(function () { return __awaiter(void 0, void 0, void 0, function () { return __generator(this, function (_a) {
125
+ return [2 /*return*/, null];
126
+ }); }); }) }); }),
127
+ modelName: "MockModel",
128
+ };
129
+ middleware = (0, permissions_1.permissionMiddleware)(model, { permissions: allPermissions });
130
+ next = (0, bun_test_1.mock)(function () { });
131
+ return [4 /*yield*/, middleware(buildReq({ method: "OPTIONS" }), {}, next)];
132
+ case 1:
133
+ _a.sent();
134
+ (0, bun_test_1.expect)(next).toHaveBeenCalledTimes(1);
135
+ (0, bun_test_1.expect)(next.mock.calls[0]).toEqual([]);
136
+ (0, bun_test_1.expect)(model.findById).toHaveBeenCalledTimes(0);
137
+ return [2 /*return*/];
138
+ }
139
+ });
140
+ }); });
141
+ (0, bun_test_1.it)("returns APIError for unsupported HTTP methods", function () { return __awaiter(void 0, void 0, void 0, function () {
142
+ var model, middleware, next, _a, error;
143
+ return __generator(this, function (_b) {
144
+ switch (_b.label) {
145
+ case 0:
146
+ model = {
147
+ collection: { findOne: (0, bun_test_1.mock)(function () { return __awaiter(void 0, void 0, void 0, function () { return __generator(this, function (_a) {
148
+ return [2 /*return*/, null];
149
+ }); }); }) },
150
+ findById: (0, bun_test_1.mock)(function () { return ({ exec: (0, bun_test_1.mock)(function () { return __awaiter(void 0, void 0, void 0, function () { return __generator(this, function (_a) {
151
+ return [2 /*return*/, null];
152
+ }); }); }) }); }),
153
+ modelName: "MockModel",
154
+ };
155
+ middleware = (0, permissions_1.permissionMiddleware)(model, { permissions: allPermissions });
156
+ next = (0, bun_test_1.mock)(function () { });
157
+ return [4 /*yield*/, middleware(buildReq({ method: "TRACE" }), {}, next)];
158
+ case 1:
159
+ _b.sent();
160
+ (0, bun_test_1.expect)(next).toHaveBeenCalledTimes(1);
161
+ _a = __read(next.mock.calls[0], 1), error = _a[0];
162
+ (0, bun_test_1.expect)(error).toBeInstanceOf(errors_1.APIError);
163
+ (0, bun_test_1.expect)(error.status).toBe(405);
164
+ (0, bun_test_1.expect)(error.title).toContain("Method TRACE not allowed");
165
+ return [2 /*return*/];
166
+ }
167
+ });
168
+ }); });
169
+ (0, bun_test_1.it)("wraps query execution failures in a 500 APIError", function () { return __awaiter(void 0, void 0, void 0, function () {
170
+ var exec, model, middleware, next, _a, error;
171
+ return __generator(this, function (_b) {
172
+ switch (_b.label) {
173
+ case 0:
174
+ exec = (0, bun_test_1.mock)(function () { return __awaiter(void 0, void 0, void 0, function () {
175
+ return __generator(this, function (_a) {
176
+ throw new Error("query failed");
177
+ });
178
+ }); });
179
+ model = {
180
+ collection: { findOne: (0, bun_test_1.mock)(function () { return __awaiter(void 0, void 0, void 0, function () { return __generator(this, function (_a) {
181
+ return [2 /*return*/, null];
182
+ }); }); }) },
183
+ findById: (0, bun_test_1.mock)(function () { return ({ exec: exec }); }),
184
+ modelName: "MockModel",
185
+ };
186
+ middleware = (0, permissions_1.permissionMiddleware)(model, { permissions: allPermissions });
187
+ next = (0, bun_test_1.mock)(function () { });
188
+ return [4 /*yield*/, middleware(buildReq({ method: "GET", params: { id: "507f1f77bcf86cd799439011" } }), {}, next)];
189
+ case 1:
190
+ _b.sent();
191
+ (0, bun_test_1.expect)(exec).toHaveBeenCalledTimes(1);
192
+ _a = __read(next.mock.calls[0], 1), error = _a[0];
193
+ (0, bun_test_1.expect)(error).toBeInstanceOf(errors_1.APIError);
194
+ (0, bun_test_1.expect)(error.status).toBe(500);
195
+ (0, bun_test_1.expect)(error.title).toContain("GET failed on 507f1f77bcf86cd799439011");
196
+ return [2 /*return*/];
197
+ }
198
+ });
199
+ }); });
200
+ (0, bun_test_1.it)("captures sentry message when document does not exist", function () { return __awaiter(void 0, void 0, void 0, function () {
201
+ var captureMessageSpy, model, middleware, next, _a, error;
202
+ return __generator(this, function (_b) {
203
+ switch (_b.label) {
204
+ case 0:
205
+ captureMessageSpy = (0, bun_test_1.spyOn)(Sentry, "captureMessage");
206
+ model = {
207
+ collection: { findOne: (0, bun_test_1.mock)(function () { return __awaiter(void 0, void 0, void 0, function () { return __generator(this, function (_a) {
208
+ return [2 /*return*/, null];
209
+ }); }); }) },
210
+ findById: (0, bun_test_1.mock)(function () { return ({ exec: (0, bun_test_1.mock)(function () { return __awaiter(void 0, void 0, void 0, function () { return __generator(this, function (_a) {
211
+ return [2 /*return*/, null];
212
+ }); }); }) }); }),
213
+ modelName: "MockModel",
214
+ };
215
+ middleware = (0, permissions_1.permissionMiddleware)(model, { permissions: allPermissions });
216
+ next = (0, bun_test_1.mock)(function () { });
217
+ return [4 /*yield*/, middleware(buildReq({ method: "GET", params: { id: "507f1f77bcf86cd799439011" } }), {}, next)];
218
+ case 1:
219
+ _b.sent();
220
+ (0, bun_test_1.expect)(captureMessageSpy).toHaveBeenCalledWith("Document 507f1f77bcf86cd799439011 not found for model MockModel");
221
+ _a = __read(next.mock.calls[0], 1), error = _a[0];
222
+ (0, bun_test_1.expect)(error).toBeInstanceOf(errors_1.APIError);
223
+ (0, bun_test_1.expect)(error.status).toBe(404);
224
+ (0, bun_test_1.expect)(error.meta).toBeUndefined();
225
+ captureMessageSpy.mockRestore();
226
+ return [2 /*return*/];
227
+ }
228
+ });
229
+ }); });
230
+ (0, bun_test_1.it)("returns hidden reason metadata when document is deleted", function () { return __awaiter(void 0, void 0, void 0, function () {
231
+ var model, middleware, next, _a, error;
232
+ return __generator(this, function (_b) {
233
+ switch (_b.label) {
234
+ case 0:
235
+ model = {
236
+ collection: { findOne: (0, bun_test_1.mock)(function () { return __awaiter(void 0, void 0, void 0, function () { return __generator(this, function (_a) {
237
+ return [2 /*return*/, ({ deleted: true })];
238
+ }); }); }) },
239
+ findById: (0, bun_test_1.mock)(function () { return ({ exec: (0, bun_test_1.mock)(function () { return __awaiter(void 0, void 0, void 0, function () { return __generator(this, function (_a) {
240
+ return [2 /*return*/, null];
241
+ }); }); }) }); }),
242
+ modelName: "MockModel",
243
+ };
244
+ middleware = (0, permissions_1.permissionMiddleware)(model, { permissions: allPermissions });
245
+ next = (0, bun_test_1.mock)(function () { });
246
+ return [4 /*yield*/, middleware(buildReq({ method: "GET", params: { id: "507f1f77bcf86cd799439011" } }), {}, next)];
247
+ case 1:
248
+ _b.sent();
249
+ _a = __read(next.mock.calls[0], 1), error = _a[0];
250
+ (0, bun_test_1.expect)(error).toBeInstanceOf(errors_1.APIError);
251
+ (0, bun_test_1.expect)(error.status).toBe(404);
252
+ (0, bun_test_1.expect)(error.meta).toEqual({ deleted: "true" });
253
+ (0, bun_test_1.expect)(error.disableExternalErrorTracking).toBe(true);
254
+ return [2 /*return*/];
255
+ }
256
+ });
257
+ }); });
258
+ (0, bun_test_1.it)("returns hidden reason metadata when document is disabled", function () { return __awaiter(void 0, void 0, void 0, function () {
259
+ var model, middleware, next, _a, error;
260
+ return __generator(this, function (_b) {
261
+ switch (_b.label) {
262
+ case 0:
263
+ model = {
264
+ collection: { findOne: (0, bun_test_1.mock)(function () { return __awaiter(void 0, void 0, void 0, function () { return __generator(this, function (_a) {
265
+ return [2 /*return*/, ({ disabled: true })];
266
+ }); }); }) },
267
+ findById: (0, bun_test_1.mock)(function () { return ({ exec: (0, bun_test_1.mock)(function () { return __awaiter(void 0, void 0, void 0, function () { return __generator(this, function (_a) {
268
+ return [2 /*return*/, null];
269
+ }); }); }) }); }),
270
+ modelName: "MockModel",
271
+ };
272
+ middleware = (0, permissions_1.permissionMiddleware)(model, { permissions: allPermissions });
273
+ next = (0, bun_test_1.mock)(function () { });
274
+ return [4 /*yield*/, middleware(buildReq({ method: "GET", params: { id: "507f1f77bcf86cd799439011" } }), {}, next)];
275
+ case 1:
276
+ _b.sent();
277
+ _a = __read(next.mock.calls[0], 1), error = _a[0];
278
+ (0, bun_test_1.expect)(error).toBeInstanceOf(errors_1.APIError);
279
+ (0, bun_test_1.expect)(error.status).toBe(404);
280
+ (0, bun_test_1.expect)(error.meta).toEqual({ disabled: "true" });
281
+ (0, bun_test_1.expect)(error.disableExternalErrorTracking).toBe(true);
282
+ return [2 /*return*/];
283
+ }
284
+ });
285
+ }); });
286
+ (0, bun_test_1.it)("returns hidden reason metadata when document is archived", function () { return __awaiter(void 0, void 0, void 0, function () {
287
+ var model, middleware, next, _a, error;
288
+ return __generator(this, function (_b) {
289
+ switch (_b.label) {
290
+ case 0:
291
+ model = {
292
+ collection: { findOne: (0, bun_test_1.mock)(function () { return __awaiter(void 0, void 0, void 0, function () { return __generator(this, function (_a) {
293
+ return [2 /*return*/, ({ archived: true })];
294
+ }); }); }) },
295
+ findById: (0, bun_test_1.mock)(function () { return ({ exec: (0, bun_test_1.mock)(function () { return __awaiter(void 0, void 0, void 0, function () { return __generator(this, function (_a) {
296
+ return [2 /*return*/, null];
297
+ }); }); }) }); }),
298
+ modelName: "MockModel",
299
+ };
300
+ middleware = (0, permissions_1.permissionMiddleware)(model, { permissions: allPermissions });
301
+ next = (0, bun_test_1.mock)(function () { });
302
+ return [4 /*yield*/, middleware(buildReq({ method: "GET", params: { id: "507f1f77bcf86cd799439011" } }), {}, next)];
303
+ case 1:
304
+ _b.sent();
305
+ _a = __read(next.mock.calls[0], 1), error = _a[0];
306
+ (0, bun_test_1.expect)(error).toBeInstanceOf(errors_1.APIError);
307
+ (0, bun_test_1.expect)(error.status).toBe(404);
308
+ (0, bun_test_1.expect)(error.meta).toEqual({ archived: "true" });
309
+ (0, bun_test_1.expect)(error.disableExternalErrorTracking).toBe(true);
310
+ return [2 /*return*/];
311
+ }
312
+ });
313
+ }); });
314
+ (0, bun_test_1.it)("returns plain not found when hidden document has no reason", function () { return __awaiter(void 0, void 0, void 0, function () {
315
+ var model, middleware, next, _a, error;
316
+ return __generator(this, function (_b) {
317
+ switch (_b.label) {
318
+ case 0:
319
+ model = {
320
+ collection: { findOne: (0, bun_test_1.mock)(function () { return __awaiter(void 0, void 0, void 0, function () { return __generator(this, function (_a) {
321
+ return [2 /*return*/, ({ foo: "bar" })];
322
+ }); }); }) },
323
+ findById: (0, bun_test_1.mock)(function () { return ({ exec: (0, bun_test_1.mock)(function () { return __awaiter(void 0, void 0, void 0, function () { return __generator(this, function (_a) {
324
+ return [2 /*return*/, null];
325
+ }); }); }) }); }),
326
+ modelName: "MockModel",
327
+ };
328
+ middleware = (0, permissions_1.permissionMiddleware)(model, { permissions: allPermissions });
329
+ next = (0, bun_test_1.mock)(function () { });
330
+ return [4 /*yield*/, middleware(buildReq({ method: "GET", params: { id: "507f1f77bcf86cd799439011" } }), {}, next)];
331
+ case 1:
332
+ _b.sent();
333
+ _a = __read(next.mock.calls[0], 1), error = _a[0];
334
+ (0, bun_test_1.expect)(error).toBeInstanceOf(errors_1.APIError);
335
+ (0, bun_test_1.expect)(error.status).toBe(404);
336
+ (0, bun_test_1.expect)(error.meta).toBeUndefined();
337
+ return [2 /*return*/];
338
+ }
339
+ });
340
+ }); });
341
+ });
package/dist/plugins.d.ts CHANGED
@@ -4,13 +4,13 @@ export interface BaseUser {
4
4
  admin: boolean;
5
5
  email: string;
6
6
  }
7
- export declare function baseUserPlugin(schema: Schema<any, any, any, any>): void;
7
+ export declare const baseUserPlugin: (schema: Schema<any, any, any, any>) => void;
8
8
  /** For models with the isDeletedPlugin, extend this interface to add the appropriate fields. */
9
9
  export interface IsDeleted {
10
10
  deleted: boolean;
11
11
  }
12
- export declare function isDeletedPlugin(schema: Schema<any, any, any, any>, defaultValue?: boolean): void;
13
- export declare function isDisabledPlugin(schema: Schema<any, any, any, any>, defaultValue?: boolean): void;
12
+ export declare const isDeletedPlugin: (schema: Schema<any, any, any, any>, defaultValue?: boolean) => void;
13
+ export declare const isDisabledPlugin: (schema: Schema<any, any, any, any>, defaultValue?: boolean) => void;
14
14
  export interface CreatedDeleted {
15
15
  updated: {
16
16
  type: Date;
@@ -21,8 +21,8 @@ export interface CreatedDeleted {
21
21
  required: true;
22
22
  };
23
23
  }
24
- export declare function createdUpdatedPlugin(schema: Schema<any, any, any, any>): void;
25
- export declare function firebaseJWTPlugin(schema: Schema): void;
24
+ export declare const createdUpdatedPlugin: (schema: Schema<any, any, any, any>) => void;
25
+ export declare const firebaseJWTPlugin: (schema: Schema) => void;
26
26
  /**
27
27
  * This adds a static method `Model.findOneOrNone` to the schema. This should replace `Model.findOne` in most instances.
28
28
  * `Model.findOne` should only be used with a unique index, but that's not apparent from the docs. Otherwise you can wind
@@ -30,7 +30,7 @@ export declare function firebaseJWTPlugin(schema: Schema): void;
30
30
  * document, or throws an exception if multiple are found.
31
31
  * @param schema Mongoose Schema
32
32
  */
33
- export declare function findOneOrNone<T>(schema: Schema<T>): void;
33
+ export declare const findOneOrNone: <T>(schema: Schema<T>) => void;
34
34
  /**
35
35
  * This adds a static method `Model.findExactlyOne` to the schema. This or findOneOrNone should replace `Model.findOne`
36
36
  * in most instances.
@@ -39,14 +39,14 @@ export declare function findOneOrNone<T>(schema: Schema<T>): void;
39
39
  * multiple or none are found.
40
40
  * @param schema Mongoose Schema
41
41
  */
42
- export declare function findExactlyOne<T>(schema: Schema<T>): void;
42
+ export declare const findExactlyOne: <T>(schema: Schema<T>) => void;
43
43
  /**
44
44
  * This adds a static method `Model.upsert` to the schema. This method will either update an existing document
45
45
  * that matches the conditions or create a new document if none exists. It throws an error if multiple documents
46
46
  * match the conditions to prevent ambiguous updates.
47
47
  * @param schema Mongoose Schema
48
48
  */
49
- export declare function upsertPlugin<T>(schema: Schema<any, any, any, any>): void;
49
+ export declare const upsertPlugin: <T>(schema: Schema<any, any, any, any>) => void;
50
50
  /** For models with the upsertPlugin, extend this interface to add the upsert static method. */
51
51
  export interface HasUpsert<T> {
52
52
  upsert(conditions: Record<string, any>, update: Record<string, any>): Promise<T>;
package/dist/plugins.js CHANGED
@@ -95,23 +95,18 @@ var __generator = (this && this.__generator) || function (thisArg, body) {
95
95
  }
96
96
  };
97
97
  Object.defineProperty(exports, "__esModule", { value: true });
98
- exports.DateOnly = void 0;
99
- exports.baseUserPlugin = baseUserPlugin;
100
- exports.isDeletedPlugin = isDeletedPlugin;
101
- exports.isDisabledPlugin = isDisabledPlugin;
102
- exports.createdUpdatedPlugin = createdUpdatedPlugin;
103
- exports.firebaseJWTPlugin = firebaseJWTPlugin;
104
- exports.findOneOrNone = findOneOrNone;
105
- exports.findExactlyOne = findExactlyOne;
106
- exports.upsertPlugin = upsertPlugin;
98
+ exports.DateOnly = exports.upsertPlugin = exports.findExactlyOne = exports.findOneOrNone = exports.firebaseJWTPlugin = exports.createdUpdatedPlugin = exports.isDisabledPlugin = exports.isDeletedPlugin = exports.baseUserPlugin = void 0;
107
99
  var luxon_1 = require("luxon");
108
100
  var mongoose_1 = __importStar(require("mongoose"));
109
101
  var errors_1 = require("./errors");
110
- function baseUserPlugin(schema) {
111
- schema.add({ admin: { default: false, type: Boolean } });
112
- schema.add({ email: { index: true, type: String } });
113
- }
114
- function isDeletedPlugin(schema, defaultValue) {
102
+ var baseUserPlugin = function (schema) {
103
+ schema.add({
104
+ admin: { default: false, description: "Whether the user has admin privileges", type: Boolean },
105
+ });
106
+ schema.add({ email: { description: "The user's email address", index: true, type: String } });
107
+ };
108
+ exports.baseUserPlugin = baseUserPlugin;
109
+ var isDeletedPlugin = function (schema, defaultValue) {
115
110
  if (defaultValue === void 0) { defaultValue = false; }
116
111
  schema.add({
117
112
  deleted: {
@@ -122,20 +117,21 @@ function isDeletedPlugin(schema, defaultValue) {
122
117
  type: Boolean,
123
118
  },
124
119
  });
125
- function applyDeleteFilter(q) {
120
+ var applyDeleteFilter = function (q) {
126
121
  var query = q.getQuery();
127
122
  if (query && query.deleted === undefined) {
128
123
  void q.where({ deleted: { $ne: true } });
129
124
  }
130
- }
125
+ };
131
126
  schema.pre("find", function () {
132
127
  applyDeleteFilter(this);
133
128
  });
134
129
  schema.pre("findOne", function () {
135
130
  applyDeleteFilter(this);
136
131
  });
137
- }
138
- function isDisabledPlugin(schema, defaultValue) {
132
+ };
133
+ exports.isDeletedPlugin = isDeletedPlugin;
134
+ var isDisabledPlugin = function (schema, defaultValue) {
139
135
  if (defaultValue === void 0) { defaultValue = false; }
140
136
  schema.add({
141
137
  disabled: {
@@ -145,10 +141,13 @@ function isDisabledPlugin(schema, defaultValue) {
145
141
  type: Boolean,
146
142
  },
147
143
  });
148
- }
149
- function createdUpdatedPlugin(schema) {
150
- schema.add({ updated: { index: true, type: Date } });
151
- schema.add({ created: { index: true, type: Date } });
144
+ };
145
+ exports.isDisabledPlugin = isDisabledPlugin;
146
+ var createdUpdatedPlugin = function (schema) {
147
+ schema.add({
148
+ updated: { description: "When this document was last updated", index: true, type: Date },
149
+ });
150
+ schema.add({ created: { description: "When this document was created", index: true, type: Date } });
152
151
  schema.pre("save", function () {
153
152
  if (this.disableCreatedUpdatedPlugin === true) {
154
153
  return;
@@ -163,10 +162,14 @@ function createdUpdatedPlugin(schema) {
163
162
  schema.pre(/save|updateOne|insertMany/, function () {
164
163
  void this.updateOne({}, { $set: { updated: new Date() } });
165
164
  });
166
- }
167
- function firebaseJWTPlugin(schema) {
168
- schema.add({ firebaseId: { index: true, type: String } });
169
- }
165
+ };
166
+ exports.createdUpdatedPlugin = createdUpdatedPlugin;
167
+ var firebaseJWTPlugin = function (schema) {
168
+ schema.add({
169
+ firebaseId: { description: "The user's Firebase authentication ID", index: true, type: String },
170
+ });
171
+ };
172
+ exports.firebaseJWTPlugin = firebaseJWTPlugin;
170
173
  /**
171
174
  * This adds a static method `Model.findOneOrNone` to the schema. This should replace `Model.findOne` in most instances.
172
175
  * `Model.findOne` should only be used with a unique index, but that's not apparent from the docs. Otherwise you can wind
@@ -174,7 +177,7 @@ function firebaseJWTPlugin(schema) {
174
177
  * document, or throws an exception if multiple are found.
175
178
  * @param schema Mongoose Schema
176
179
  */
177
- function findOneOrNone(schema) {
180
+ var findOneOrNone = function (schema) {
178
181
  schema.statics.findOneOrNone = function (query, errorArgs) {
179
182
  return __awaiter(this, void 0, void 0, function () {
180
183
  var results;
@@ -194,7 +197,8 @@ function findOneOrNone(schema) {
194
197
  });
195
198
  });
196
199
  };
197
- }
200
+ };
201
+ exports.findOneOrNone = findOneOrNone;
198
202
  /**
199
203
  * This adds a static method `Model.findExactlyOne` to the schema. This or findOneOrNone should replace `Model.findOne`
200
204
  * in most instances.
@@ -203,7 +207,7 @@ function findOneOrNone(schema) {
203
207
  * multiple or none are found.
204
208
  * @param schema Mongoose Schema
205
209
  */
206
- function findExactlyOne(schema) {
210
+ var findExactlyOne = function (schema) {
207
211
  schema.statics.findExactlyOne = function (query, errorArgs) {
208
212
  return __awaiter(this, void 0, void 0, function () {
209
213
  var results;
@@ -223,14 +227,15 @@ function findExactlyOne(schema) {
223
227
  });
224
228
  });
225
229
  };
226
- }
230
+ };
231
+ exports.findExactlyOne = findExactlyOne;
227
232
  /**
228
233
  * This adds a static method `Model.upsert` to the schema. This method will either update an existing document
229
234
  * that matches the conditions or create a new document if none exists. It throws an error if multiple documents
230
235
  * match the conditions to prevent ambiguous updates.
231
236
  * @param schema Mongoose Schema
232
237
  */
233
- function upsertPlugin(schema) {
238
+ var upsertPlugin = function (schema) {
234
239
  schema.statics.upsert = function (conditions, update) {
235
240
  return __awaiter(this, void 0, void 0, function () {
236
241
  var docs, doc, combinedData, newDoc;
@@ -259,7 +264,8 @@ function upsertPlugin(schema) {
259
264
  });
260
265
  });
261
266
  };
262
- }
267
+ };
268
+ exports.upsertPlugin = upsertPlugin;
263
269
  var DateOnly = /** @class */ (function (_super) {
264
270
  __extends(DateOnly, _super);
265
271
  function DateOnly(key, options) {
@@ -1,4 +1,37 @@
1
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
+ })();
2
35
  var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
36
  function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
37
  return new (P || (P = Promise))(function (resolve, reject) {
@@ -53,6 +86,7 @@ var __read = (this && this.__read) || function (o, n) {
53
86
  };
54
87
  Object.defineProperty(exports, "__esModule", { value: true });
55
88
  var bun_test_1 = require("bun:test");
89
+ var mongoose_1 = __importStar(require("mongoose"));
56
90
  var populate_1 = require("./populate");
57
91
  var tests_1 = require("./tests");
58
92
  (0, bun_test_1.describe)("populate functions", function () {
@@ -183,3 +217,68 @@ var tests_1 = require("./tests");
183
217
  (0, bun_test_1.expect)(result.containers[1].items).toEqual(["item-3", "item-4"]);
184
218
  });
185
219
  });
220
+ (0, bun_test_1.describe)("fixMixedFields", function () {
221
+ (0, bun_test_1.it)("returns early when schema is missing", function () {
222
+ var properties = { foo: { type: "object" } };
223
+ (0, bun_test_1.expect)(function () { return (0, populate_1.fixMixedFields)(null, properties); }).not.toThrow();
224
+ });
225
+ (0, bun_test_1.it)("returns early when properties is missing", function () {
226
+ var schema = new mongoose_1.Schema({});
227
+ (0, bun_test_1.expect)(function () { return (0, populate_1.fixMixedFields)(schema, null); }).not.toThrow();
228
+ });
229
+ (0, bun_test_1.it)("replaces Mixed fields with only description", function () {
230
+ var schema = new mongoose_1.Schema({ data: { description: "any data", type: mongoose_1.Schema.Types.Mixed } });
231
+ var properties = { data: { description: "any data", type: "object" } };
232
+ (0, populate_1.fixMixedFields)(schema, properties);
233
+ (0, bun_test_1.expect)(properties.data).toEqual({ description: "any data" });
234
+ });
235
+ (0, bun_test_1.it)("recurses into arrays of sub-documents", function () {
236
+ var subSchema = new mongoose_1.Schema({ meta: { type: mongoose_1.Schema.Types.Mixed } });
237
+ var schema = new mongoose_1.Schema({ items: [subSchema] });
238
+ var properties = {
239
+ items: {
240
+ items: {
241
+ properties: {
242
+ meta: { type: "object" },
243
+ },
244
+ },
245
+ type: "array",
246
+ },
247
+ };
248
+ (0, populate_1.fixMixedFields)(schema, properties);
249
+ (0, bun_test_1.expect)(properties.items.items.properties.meta).toEqual({ description: undefined });
250
+ });
251
+ (0, bun_test_1.it)("skips unknown paths", function () {
252
+ var schema = new mongoose_1.Schema({ foo: String });
253
+ var properties = { unknownKey: { type: "string" } };
254
+ (0, bun_test_1.expect)(function () { return (0, populate_1.fixMixedFields)(schema, properties); }).not.toThrow();
255
+ });
256
+ });
257
+ (0, bun_test_1.describe)("getOpenApiSpecForModel edge cases", function () {
258
+ (0, bun_test_1.it)("returns model properties without populatePaths", function () {
259
+ var result = (0, populate_1.getOpenApiSpecForModel)(tests_1.UserModel);
260
+ (0, bun_test_1.expect)(result.properties).toBeDefined();
261
+ });
262
+ (0, bun_test_1.it)("returns with extraModelProperties merged", function () {
263
+ var result = (0, populate_1.getOpenApiSpecForModel)(tests_1.UserModel, {
264
+ extraModelProperties: { customField: { type: "string" } },
265
+ });
266
+ (0, bun_test_1.expect)(result.properties.customField).toEqual({ type: "string" });
267
+ });
268
+ (0, bun_test_1.it)("skips populate paths without ref", function () {
269
+ // Create a schema with a non-referenced ObjectId field
270
+ var testSchema = new mongoose_1.Schema({ name: String, simpleId: mongoose_1.Schema.Types.ObjectId });
271
+ var TestModelNoRef = mongoose_1.default.models.TestModelNoRef || mongoose_1.default.model("TestModelNoRef", testSchema);
272
+ var result = (0, populate_1.getOpenApiSpecForModel)(TestModelNoRef, {
273
+ populatePaths: [{ path: "simpleId" }],
274
+ });
275
+ // Should not throw, simpleId stays as-is
276
+ (0, bun_test_1.expect)(result.properties).toBeDefined();
277
+ });
278
+ (0, bun_test_1.it)("populates with fields allowlist", function () {
279
+ var result = (0, populate_1.getOpenApiSpecForModel)(tests_1.FoodModel, {
280
+ populatePaths: [{ fields: ["name"], path: "ownerId" }],
281
+ });
282
+ (0, bun_test_1.expect)(result.properties).toBeDefined();
283
+ });
284
+ });