@terreno/api 0.13.2 → 0.14.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 (175) hide show
  1. package/dist/__tests__/versionCheckPlugin.test.js +53 -3
  2. package/dist/api.arrayOperations.test.js +1 -0
  3. package/dist/api.asyncHandler.test.d.ts +1 -0
  4. package/dist/api.asyncHandler.test.js +236 -0
  5. package/dist/api.d.ts +15 -4
  6. package/dist/api.errors.test.js +1 -0
  7. package/dist/api.hooks.test.js +1 -0
  8. package/dist/api.js +153 -104
  9. package/dist/api.query.test.js +1 -0
  10. package/dist/api.test.js +174 -0
  11. package/dist/auth.d.ts +10 -5
  12. package/dist/auth.js +163 -90
  13. package/dist/auth.test.js +159 -0
  14. package/dist/betterAuthApp.test.js +1 -0
  15. package/dist/betterAuthSetup.d.ts +5 -6
  16. package/dist/betterAuthSetup.js +17 -14
  17. package/dist/betterAuthSetup.test.js +1 -0
  18. package/dist/config.d.ts +48 -0
  19. package/dist/config.js +248 -0
  20. package/dist/config.test.d.ts +1 -0
  21. package/dist/config.test.js +328 -0
  22. package/dist/configuration.test.js +1 -0
  23. package/dist/configurationApp.d.ts +1 -1
  24. package/dist/configurationApp.js +17 -13
  25. package/dist/configurationPlugin.test.js +1 -0
  26. package/dist/consentApp.test.js +1 -0
  27. package/dist/envConfigurationPlugin.d.ts +2 -0
  28. package/dist/envConfigurationPlugin.js +173 -0
  29. package/dist/envConfigurationPlugin.test.d.ts +1 -0
  30. package/dist/envConfigurationPlugin.test.js +322 -0
  31. package/dist/errors.d.ts +18 -7
  32. package/dist/errors.js +106 -10
  33. package/dist/errors.test.js +16 -1
  34. package/dist/example.js +16 -7
  35. package/dist/expressServer.d.ts +10 -9
  36. package/dist/expressServer.js +62 -53
  37. package/dist/expressServer.test.js +53 -2
  38. package/dist/githubAuth.d.ts +2 -1
  39. package/dist/githubAuth.js +41 -26
  40. package/dist/githubAuth.test.js +1 -0
  41. package/dist/index.d.ts +4 -0
  42. package/dist/index.js +4 -0
  43. package/dist/logger.d.ts +1 -1
  44. package/dist/logger.js +42 -20
  45. package/dist/models/versionConfig.d.ts +2 -0
  46. package/dist/models/versionConfig.js +8 -0
  47. package/dist/notifiers/googleChatNotifier.js +14 -16
  48. package/dist/notifiers/googleChatNotifier.test.js +1 -0
  49. package/dist/notifiers/slackNotifier.js +16 -14
  50. package/dist/notifiers/slackNotifier.test.js +41 -3
  51. package/dist/notifiers/zoomNotifier.js +7 -10
  52. package/dist/notifiers/zoomNotifier.test.js +1 -0
  53. package/dist/openApi.d.ts +1 -1
  54. package/dist/openApi.test.js +1 -0
  55. package/dist/openApiBuilder.d.ts +39 -6
  56. package/dist/openApiBuilder.js +1 -31
  57. package/dist/openApiBuilder.test.js +1 -0
  58. package/dist/openApiValidator.js +1 -0
  59. package/dist/openApiValidator.test.js +65 -0
  60. package/dist/permissions.d.ts +4 -4
  61. package/dist/permissions.js +67 -65
  62. package/dist/permissions.middleware.test.js +1 -0
  63. package/dist/permissions.test.js +1 -0
  64. package/dist/plugins.d.ts +5 -5
  65. package/dist/plugins.js +18 -9
  66. package/dist/plugins.test.js +1 -1
  67. package/dist/populate.d.ts +15 -8
  68. package/dist/populate.js +23 -24
  69. package/dist/populate.test.js +1 -0
  70. package/dist/realtime/changeStreamWatcher.d.ts +73 -0
  71. package/dist/realtime/changeStreamWatcher.js +720 -0
  72. package/dist/realtime/index.d.ts +6 -0
  73. package/dist/realtime/index.js +27 -0
  74. package/dist/realtime/queryMatcher.d.ts +14 -0
  75. package/dist/realtime/queryMatcher.js +250 -0
  76. package/dist/realtime/queryStore.d.ts +37 -0
  77. package/dist/realtime/queryStore.js +195 -0
  78. package/dist/realtime/realtime.test.d.ts +10 -0
  79. package/dist/realtime/realtime.test.js +2158 -0
  80. package/dist/realtime/realtimeApp.d.ts +93 -0
  81. package/dist/realtime/realtimeApp.js +560 -0
  82. package/dist/realtime/registry.d.ts +40 -0
  83. package/dist/realtime/registry.js +38 -0
  84. package/dist/realtime/socketUser.d.ts +10 -0
  85. package/dist/realtime/socketUser.js +17 -0
  86. package/dist/realtime/types.d.ts +100 -0
  87. package/dist/realtime/types.js +2 -0
  88. package/dist/requestContext.d.ts +37 -0
  89. package/dist/requestContext.js +344 -0
  90. package/dist/requestContext.test.d.ts +1 -0
  91. package/dist/requestContext.test.js +241 -0
  92. package/dist/terrenoApp.d.ts +8 -0
  93. package/dist/terrenoApp.js +50 -13
  94. package/dist/terrenoApp.test.js +194 -21
  95. package/dist/terrenoPlugin.d.ts +11 -0
  96. package/dist/tests/bunSetup.js +1 -0
  97. package/dist/tests.js +1 -1
  98. package/dist/transformers.d.ts +2 -2
  99. package/dist/transformers.js +5 -3
  100. package/dist/transformers.test.js +90 -0
  101. package/dist/types/consentResponse.d.ts +6 -3
  102. package/dist/versionCheckPlugin.d.ts +2 -0
  103. package/dist/versionCheckPlugin.js +18 -12
  104. package/package.json +4 -2
  105. package/src/__tests__/versionCheckPlugin.test.ts +37 -3
  106. package/src/api.arrayOperations.test.ts +1 -0
  107. package/src/api.asyncHandler.test.ts +177 -0
  108. package/src/api.errors.test.ts +1 -0
  109. package/src/api.hooks.test.ts +1 -0
  110. package/src/api.query.test.ts +1 -0
  111. package/src/api.test.ts +132 -0
  112. package/src/api.ts +199 -84
  113. package/src/auth.test.ts +160 -0
  114. package/src/auth.ts +120 -50
  115. package/src/betterAuthApp.test.ts +1 -0
  116. package/src/betterAuthSetup.test.ts +1 -0
  117. package/src/betterAuthSetup.ts +46 -19
  118. package/src/config.test.ts +255 -0
  119. package/src/config.ts +206 -0
  120. package/src/configuration.test.ts +1 -0
  121. package/src/configurationApp.ts +59 -24
  122. package/src/configurationPlugin.test.ts +1 -0
  123. package/src/consentApp.test.ts +1 -0
  124. package/src/envConfigurationPlugin.test.ts +143 -0
  125. package/src/envConfigurationPlugin.ts +100 -0
  126. package/src/errors.test.ts +19 -1
  127. package/src/errors.ts +94 -20
  128. package/src/example.ts +46 -21
  129. package/src/express.d.ts +18 -1
  130. package/src/expressServer.test.ts +50 -2
  131. package/src/expressServer.ts +80 -50
  132. package/src/githubAuth.test.ts +1 -0
  133. package/src/githubAuth.ts +59 -38
  134. package/src/index.ts +4 -0
  135. package/src/logger.ts +47 -17
  136. package/src/models/versionConfig.ts +13 -2
  137. package/src/notifiers/googleChatNotifier.test.ts +1 -0
  138. package/src/notifiers/googleChatNotifier.ts +7 -9
  139. package/src/notifiers/slackNotifier.test.ts +29 -3
  140. package/src/notifiers/slackNotifier.ts +9 -7
  141. package/src/notifiers/zoomNotifier.test.ts +1 -0
  142. package/src/notifiers/zoomNotifier.ts +8 -11
  143. package/src/openApi.test.ts +1 -0
  144. package/src/openApi.ts +4 -4
  145. package/src/openApiBuilder.test.ts +1 -0
  146. package/src/openApiBuilder.ts +14 -11
  147. package/src/openApiValidator.test.ts +59 -0
  148. package/src/openApiValidator.ts +3 -2
  149. package/src/permissions.middleware.test.ts +1 -0
  150. package/src/permissions.test.ts +1 -0
  151. package/src/permissions.ts +30 -25
  152. package/src/plugins.test.ts +1 -1
  153. package/src/plugins.ts +21 -14
  154. package/src/populate.test.ts +1 -0
  155. package/src/populate.ts +44 -36
  156. package/src/realtime/changeStreamWatcher.ts +568 -0
  157. package/src/realtime/index.ts +34 -0
  158. package/src/realtime/queryMatcher.ts +179 -0
  159. package/src/realtime/queryStore.ts +132 -0
  160. package/src/realtime/realtime.test.ts +1755 -0
  161. package/src/realtime/realtimeApp.ts +478 -0
  162. package/src/realtime/registry.ts +64 -0
  163. package/src/realtime/socketUser.ts +25 -0
  164. package/src/realtime/types.ts +112 -0
  165. package/src/requestContext.test.ts +196 -0
  166. package/src/requestContext.ts +368 -0
  167. package/src/terrenoApp.test.ts +137 -11
  168. package/src/terrenoApp.ts +64 -17
  169. package/src/terrenoPlugin.ts +12 -0
  170. package/src/tests/bunSetup.ts +1 -0
  171. package/src/tests.ts +7 -2
  172. package/src/transformers.test.ts +70 -2
  173. package/src/transformers.ts +15 -7
  174. package/src/types/consentResponse.ts +8 -10
  175. package/src/versionCheckPlugin.ts +15 -7
@@ -72,6 +72,7 @@ var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
72
72
  return to.concat(ar || Array.prototype.slice.call(from));
73
73
  };
74
74
  Object.defineProperty(exports, "__esModule", { value: true });
75
+ // biome-ignore-all lint/suspicious/noExplicitAny: test mock typing
75
76
  var bun_test_1 = require("bun:test");
76
77
  var api_1 = require("./api");
77
78
  var auth_1 = require("./auth");
@@ -262,6 +263,70 @@ var setupFreshApp = function () { return __awaiter(void 0, void 0, void 0, funct
262
263
  });
263
264
  }); });
264
265
  });
266
+ (0, bun_test_1.describe)("per-route validation: object-form options", function () {
267
+ (0, bun_test_1.it)("applies object validation options with per-operation control", function () { return __awaiter(void 0, void 0, void 0, function () {
268
+ var removedProps, errorsCaught, freshApp, admin, res;
269
+ return __generator(this, function (_a) {
270
+ switch (_a.label) {
271
+ case 0:
272
+ (0, openApiValidator_1.configureOpenApiValidator)({ removeAdditional: true });
273
+ removedProps = [];
274
+ errorsCaught = [];
275
+ return [4 /*yield*/, setupFreshApp()];
276
+ case 1:
277
+ freshApp = _a.sent();
278
+ freshApp.use("/required", (0, api_1.modelRouter)(tests_1.RequiredModel, __assign(__assign({}, requiredRouterOptions), { validation: {
279
+ excludeFromCreate: ["about"],
280
+ onAdditionalPropertiesRemoved: function (props) {
281
+ removedProps.push.apply(removedProps, __spreadArray([], __read(props), false));
282
+ },
283
+ onError: function (errors) {
284
+ errorsCaught.push(errors);
285
+ },
286
+ validateCreate: true,
287
+ validateQuery: true,
288
+ validateUpdate: true,
289
+ } })));
290
+ return [4 /*yield*/, (0, tests_1.authAsUser)(freshApp, "admin")];
291
+ case 2:
292
+ admin = _a.sent();
293
+ return [4 /*yield*/, admin.post("/required").send({ name: "Validated" }).expect(201)];
294
+ case 3:
295
+ res = _a.sent();
296
+ (0, bun_test_1.expect)(res.body.data.name).toBe("Validated");
297
+ return [2 /*return*/];
298
+ }
299
+ });
300
+ }); });
301
+ (0, bun_test_1.it)("disables create validation when validateCreate is false", function () { return __awaiter(void 0, void 0, void 0, function () {
302
+ var freshApp, admin, res;
303
+ return __generator(this, function (_a) {
304
+ switch (_a.label) {
305
+ case 0:
306
+ (0, openApiValidator_1.configureOpenApiValidator)({ removeAdditional: true });
307
+ return [4 /*yield*/, setupFreshApp()];
308
+ case 1:
309
+ freshApp = _a.sent();
310
+ freshApp.use("/required", (0, api_1.modelRouter)(tests_1.RequiredModel, __assign(__assign({}, requiredRouterOptions), { validation: {
311
+ excludeFromUpdate: ["about"],
312
+ validateCreate: false,
313
+ validateUpdate: true,
314
+ } })));
315
+ return [4 /*yield*/, (0, tests_1.authAsUser)(freshApp, "admin")];
316
+ case 2:
317
+ admin = _a.sent();
318
+ return [4 /*yield*/, admin
319
+ .post("/required")
320
+ .send({ extraField: "ignored", name: "NoCreateValidation" })
321
+ .expect(201)];
322
+ case 3:
323
+ res = _a.sent();
324
+ (0, bun_test_1.expect)(res.body.data.name).toBe("NoCreateValidation");
325
+ return [2 /*return*/];
326
+ }
327
+ });
328
+ }); });
329
+ });
265
330
  (0, bun_test_1.describe)("sanitization of non-standard mongoose-to-swagger types", function () {
266
331
  (0, bun_test_1.it)("validates models with ObjectId and DateOnly fields after sanitization", function () { return __awaiter(void 0, void 0, void 0, function () {
267
332
  var freshApp, admin, res;
@@ -19,8 +19,8 @@ export declare const Permissions: {
19
19
  IsAny: () => boolean;
20
20
  IsAuthenticated: (_method: RESTMethod, user?: User) => boolean;
21
21
  IsAuthenticatedOrReadOnly: (method: RESTMethod, user?: User) => boolean;
22
- IsOwner: (_method: RESTMethod, user?: User, obj?: any) => any;
23
- IsOwnerOrReadOnly: (method: RESTMethod, user?: User, obj?: any) => boolean;
22
+ IsOwner: (_method: RESTMethod, user?: User, obj?: unknown) => boolean;
23
+ IsOwnerOrReadOnly: (method: RESTMethod, user?: User, obj?: unknown) => boolean;
24
24
  };
25
- export declare function checkPermissions<T>(method: RESTMethod, permissions: PermissionMethod<T>[], user?: User, obj?: T): Promise<boolean>;
26
- export declare function permissionMiddleware<T>(model: Model<T>, options: Pick<ModelRouterOptions<T>, "permissions" | "populatePaths">): (req: express.Request, _res: express.Response, next: NextFunction) => Promise<void>;
25
+ export declare const checkPermissions: <T>(method: RESTMethod, permissions: PermissionMethod<T>[], user?: User, obj?: T) => Promise<boolean>;
26
+ export declare const permissionMiddleware: <T>(model: Model<T>, options: Pick<ModelRouterOptions<T>, "permissions" | "populatePaths">) => (req: express.Request, _res: express.Response, next: NextFunction) => Promise<void>;
@@ -83,10 +83,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
83
83
  return (mod && mod.__esModule) ? mod : { "default": mod };
84
84
  };
85
85
  Object.defineProperty(exports, "__esModule", { value: true });
86
- exports.Permissions = exports.OwnerQueryFilter = void 0;
87
- exports.checkPermissions = checkPermissions;
88
- exports.permissionMiddleware = permissionMiddleware;
89
- // Defaults closed
86
+ exports.permissionMiddleware = exports.checkPermissions = exports.Permissions = exports.OwnerQueryFilter = void 0;
90
87
  var Sentry = __importStar(require("@sentry/bun"));
91
88
  var mongoose_1 = __importDefault(require("mongoose"));
92
89
  var api_1 = require("./api");
@@ -96,7 +93,6 @@ var OwnerQueryFilter = function (user) {
96
93
  if (user) {
97
94
  return { ownerId: user === null || user === void 0 ? void 0 : user.id };
98
95
  }
99
- // Return a null, so we know to return no results.
100
96
  return null;
101
97
  };
102
98
  exports.OwnerQueryFilter = OwnerQueryFilter;
@@ -131,8 +127,10 @@ exports.Permissions = {
131
127
  if (user === null || user === void 0 ? void 0 : user.admin) {
132
128
  return true;
133
129
  }
134
- var ownerId = ((_a = obj === null || obj === void 0 ? void 0 : obj.ownerId) === null || _a === void 0 ? void 0 : _a._id) || (obj === null || obj === void 0 ? void 0 : obj.ownerId);
135
- return (user === null || user === void 0 ? void 0 : user.id) && ownerId && String(ownerId) === String(user === null || user === void 0 ? void 0 : user.id);
130
+ var withOwner = obj;
131
+ var ownerObj = withOwner.ownerId;
132
+ var ownerId = (_a = ownerObj === null || ownerObj === void 0 ? void 0 : ownerObj._id) !== null && _a !== void 0 ? _a : withOwner.ownerId;
133
+ return Boolean((user === null || user === void 0 ? void 0 : user.id) && ownerId && String(ownerId) === String(user === null || user === void 0 ? void 0 : user.id));
136
134
  },
137
135
  IsOwnerOrReadOnly: function (method, user, obj) {
138
136
  // When checking if we can possibly perform the action, return true.
@@ -142,61 +140,59 @@ exports.Permissions = {
142
140
  if (user === null || user === void 0 ? void 0 : user.admin) {
143
141
  return true;
144
142
  }
145
- if ((user === null || user === void 0 ? void 0 : user.id) && (obj === null || obj === void 0 ? void 0 : obj.ownerId) && String(obj === null || obj === void 0 ? void 0 : obj.ownerId) === String(user === null || user === void 0 ? void 0 : user.id)) {
143
+ var withOwner = obj;
144
+ if ((user === null || user === void 0 ? void 0 : user.id) && withOwner.ownerId && String(withOwner.ownerId) === String(user === null || user === void 0 ? void 0 : user.id)) {
146
145
  return true;
147
146
  }
148
147
  return method === "list" || method === "read";
149
148
  },
150
149
  };
151
- function checkPermissions(method, permissions, user, obj) {
152
- return __awaiter(this, void 0, void 0, function () {
153
- var anyTrue, permissions_1, permissions_1_1, perm, e_1_1;
154
- var e_1, _a;
155
- return __generator(this, function (_b) {
156
- switch (_b.label) {
157
- case 0:
158
- anyTrue = false;
159
- _b.label = 1;
160
- case 1:
161
- _b.trys.push([1, 6, 7, 8]);
162
- permissions_1 = __values(permissions), permissions_1_1 = permissions_1.next();
163
- _b.label = 2;
164
- case 2:
165
- if (!!permissions_1_1.done) return [3 /*break*/, 5];
166
- perm = permissions_1_1.value;
167
- return [4 /*yield*/, perm(method, user, obj)];
168
- case 3:
169
- // May or may not be a promise.
170
- if (!(_b.sent())) {
171
- return [2 /*return*/, false];
172
- }
173
- anyTrue = true;
174
- _b.label = 4;
175
- case 4:
176
- permissions_1_1 = permissions_1.next();
177
- return [3 /*break*/, 2];
178
- case 5: return [3 /*break*/, 8];
179
- case 6:
180
- e_1_1 = _b.sent();
181
- e_1 = { error: e_1_1 };
182
- return [3 /*break*/, 8];
183
- case 7:
184
- try {
185
- if (permissions_1_1 && !permissions_1_1.done && (_a = permissions_1.return)) _a.call(permissions_1);
186
- }
187
- finally { if (e_1) throw e_1.error; }
188
- return [7 /*endfinally*/];
189
- case 8: return [2 /*return*/, anyTrue];
190
- }
191
- });
150
+ var checkPermissions = function (method, permissions, user, obj) { return __awaiter(void 0, void 0, void 0, function () {
151
+ var anyTrue, permissions_1, permissions_1_1, perm, e_1_1;
152
+ var e_1, _a;
153
+ return __generator(this, function (_b) {
154
+ switch (_b.label) {
155
+ case 0:
156
+ anyTrue = false;
157
+ _b.label = 1;
158
+ case 1:
159
+ _b.trys.push([1, 6, 7, 8]);
160
+ permissions_1 = __values(permissions), permissions_1_1 = permissions_1.next();
161
+ _b.label = 2;
162
+ case 2:
163
+ if (!!permissions_1_1.done) return [3 /*break*/, 5];
164
+ perm = permissions_1_1.value;
165
+ return [4 /*yield*/, perm(method, user, obj)];
166
+ case 3:
167
+ if (!(_b.sent())) {
168
+ return [2 /*return*/, false];
169
+ }
170
+ anyTrue = true;
171
+ _b.label = 4;
172
+ case 4:
173
+ permissions_1_1 = permissions_1.next();
174
+ return [3 /*break*/, 2];
175
+ case 5: return [3 /*break*/, 8];
176
+ case 6:
177
+ e_1_1 = _b.sent();
178
+ e_1 = { error: e_1_1 };
179
+ return [3 /*break*/, 8];
180
+ case 7:
181
+ try {
182
+ if (permissions_1_1 && !permissions_1_1.done && (_a = permissions_1.return)) _a.call(permissions_1);
183
+ }
184
+ finally { if (e_1) throw e_1.error; }
185
+ return [7 /*endfinally*/];
186
+ case 8: return [2 /*return*/, anyTrue];
187
+ }
192
188
  });
193
- }
189
+ }); };
190
+ exports.checkPermissions = checkPermissions;
194
191
  // Check the permissions for a given model and method. If the method is a read, update, or delete,
195
192
  // finds the relevant object, checks the permissions, and attaches the object to the request as
196
193
  // req.obj.
197
- function permissionMiddleware(model, options) {
198
- var _this = this;
199
- return function (req, _res, next) { return __awaiter(_this, void 0, void 0, function () {
194
+ var permissionMiddleware = function (model, options) {
195
+ return function (req, _res, next) { return __awaiter(void 0, void 0, void 0, function () {
200
196
  var method, reqMethod, builtQuery, populatedQuery, data, error_1, hiddenDoc, error, reason, error, error_2;
201
197
  var _a, _b;
202
198
  return __generator(this, function (_c) {
@@ -233,7 +229,7 @@ function permissionMiddleware(model, options) {
233
229
  title: "Method ".concat(req.method, " not allowed"),
234
230
  });
235
231
  }
236
- return [4 /*yield*/, checkPermissions(method, options.permissions[method], req.user)];
232
+ return [4 /*yield*/, (0, exports.checkPermissions)(method, options.permissions[method], req.user)];
237
233
  case 2:
238
234
  // All methods check for permissions.
239
235
  if (!(_c.sent())) {
@@ -247,14 +243,16 @@ function permissionMiddleware(model, options) {
247
243
  return [2 /*return*/, next()];
248
244
  }
249
245
  builtQuery = model.findById(req.params.id);
250
- populatedQuery = (0, api_1.addPopulateToQuery)(builtQuery, options.populatePaths);
246
+ populatedQuery = (0, api_1.addPopulateToQuery)(
247
+ // biome-ignore lint/suspicious/noExplicitAny: Query types vary based on populate paths
248
+ builtQuery, options.populatePaths);
251
249
  data = void 0;
252
250
  _c.label = 3;
253
251
  case 3:
254
252
  _c.trys.push([3, 5, , 6]);
255
253
  return [4 /*yield*/, populatedQuery.exec()];
256
254
  case 4:
257
- data = _c.sent();
255
+ data = (_c.sent());
258
256
  return [3 /*break*/, 6];
259
257
  case 5:
260
258
  error_1 = _c.sent();
@@ -279,13 +277,16 @@ function permissionMiddleware(model, options) {
279
277
  error.meta = undefined;
280
278
  throw error;
281
279
  }
282
- reason = hiddenDoc.deleted
283
- ? { deleted: "true" }
284
- : hiddenDoc.disabled
285
- ? { disabled: "true" }
286
- : hiddenDoc.archived
287
- ? { archived: "true" }
288
- : null;
280
+ reason = null;
281
+ if (hiddenDoc.deleted) {
282
+ reason = { deleted: "true" };
283
+ }
284
+ else if (hiddenDoc.disabled) {
285
+ reason = { disabled: "true" };
286
+ }
287
+ else if (hiddenDoc.archived) {
288
+ reason = { archived: "true" };
289
+ }
289
290
  // If no reason found, treat as not found
290
291
  if (!reason) {
291
292
  error = new errors_1.APIError({
@@ -302,7 +303,7 @@ function permissionMiddleware(model, options) {
302
303
  status: 404,
303
304
  title: "Document ".concat(req.params.id, " not found for model ").concat(model.modelName),
304
305
  });
305
- case 8: return [4 /*yield*/, checkPermissions(method, options.permissions[method], req.user, data)];
306
+ case 8: return [4 /*yield*/, (0, exports.checkPermissions)(method, options.permissions[method], req.user, data)];
306
307
  case 9:
307
308
  if (!(_c.sent())) {
308
309
  throw new errors_1.APIError({
@@ -320,4 +321,5 @@ function permissionMiddleware(model, options) {
320
321
  }
321
322
  });
322
323
  }); };
323
- }
324
+ };
325
+ exports.permissionMiddleware = permissionMiddleware;
@@ -96,6 +96,7 @@ var __read = (this && this.__read) || function (o, n) {
96
96
  return ar;
97
97
  };
98
98
  Object.defineProperty(exports, "__esModule", { value: true });
99
+ // biome-ignore-all lint/suspicious/noExplicitAny: test mock typing
99
100
  var bun_test_1 = require("bun:test");
100
101
  var Sentry = __importStar(require("@sentry/bun"));
101
102
  var errors_1 = require("./errors");
@@ -55,6 +55,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
55
55
  return (mod && mod.__esModule) ? mod : { "default": mod };
56
56
  };
57
57
  Object.defineProperty(exports, "__esModule", { value: true });
58
+ // biome-ignore-all lint/suspicious/noExplicitAny: test mock typing
58
59
  var bun_test_1 = require("bun:test");
59
60
  var supertest_1 = __importDefault(require("supertest"));
60
61
  var api_1 = require("./api");
package/dist/plugins.d.ts CHANGED
@@ -60,19 +60,19 @@ export declare const findExactlyOne: <T>(schema: Schema<T>) => void;
60
60
  export declare const upsertPlugin: <T>(schema: Schema<any, any, any, any>) => void;
61
61
  /** For models with the upsertPlugin, extend this interface to add the upsert static method. */
62
62
  export interface HasUpsert<T> {
63
- upsert(conditions: Record<string, any>, update: Record<string, any>): Promise<T>;
63
+ upsert(conditions: Record<string, unknown>, update: Record<string, unknown>): Promise<T>;
64
64
  }
65
65
  export interface FindOneOrNonePlugin<T> {
66
- findOneOrNone(query: Record<string, any>, errorArgs?: Partial<APIErrorConstructor>): Promise<(Document & T) | null>;
66
+ findOneOrNone(query: Record<string, unknown>, errorArgs?: Partial<APIErrorConstructor>): Promise<(Document & T) | null>;
67
67
  }
68
68
  export interface FindExactlyOnePlugin<T> {
69
- findExactlyOne(query: Record<string, any>, errorArgs?: Partial<APIErrorConstructor>): Promise<Document & T>;
69
+ findExactlyOne(query: Record<string, unknown>, errorArgs?: Partial<APIErrorConstructor>): Promise<Document & T>;
70
70
  }
71
71
  export declare class DateOnly extends SchemaType {
72
72
  constructor(key: string, options: SchemaTypeOptions<Date>);
73
- handleSingle(val: any): Date | undefined;
73
+ handleSingle(val: unknown): Date | undefined;
74
74
  $conditionalHandlers: any;
75
- castForQuery($conditional: any, val: any, context: any): Date | undefined;
75
+ castForQuery($conditional: string | undefined, val: unknown, context: unknown): Date | undefined;
76
76
  cast(val: unknown): Date | undefined;
77
77
  get(val: unknown): this;
78
78
  }
package/dist/plugins.js CHANGED
@@ -99,6 +99,7 @@ exports.DateOnly = exports.upsertPlugin = exports.findExactlyOne = exports.findO
99
99
  var luxon_1 = require("luxon");
100
100
  var mongoose_1 = __importStar(require("mongoose"));
101
101
  var errors_1 = require("./errors");
102
+ // biome-ignore lint/suspicious/noExplicitAny: Schema generics must be loose to accept arbitrary consumer schemas
102
103
  var baseUserPlugin = function (schema) {
103
104
  schema.add({
104
105
  admin: { default: false, description: "Whether the user has admin privileges", type: Boolean },
@@ -106,6 +107,7 @@ var baseUserPlugin = function (schema) {
106
107
  schema.add({ email: { description: "The user's email address", index: true, type: String } });
107
108
  };
108
109
  exports.baseUserPlugin = baseUserPlugin;
110
+ // biome-ignore lint/suspicious/noExplicitAny: Schema generics must be loose to accept arbitrary consumer schemas
109
111
  var isDeletedPlugin = function (schema, defaultValue) {
110
112
  if (defaultValue === void 0) { defaultValue = false; }
111
113
  schema.add({
@@ -117,6 +119,7 @@ var isDeletedPlugin = function (schema, defaultValue) {
117
119
  type: Boolean,
118
120
  },
119
121
  });
122
+ // biome-ignore lint/suspicious/noExplicitAny: Query<any, any> must be loose to accept arbitrary consumer queries
120
123
  var applyDeleteFilter = function (q) {
121
124
  var query = q.getQuery();
122
125
  if (query && query.deleted === undefined) {
@@ -131,7 +134,9 @@ var isDeletedPlugin = function (schema, defaultValue) {
131
134
  });
132
135
  };
133
136
  exports.isDeletedPlugin = isDeletedPlugin;
134
- var isDisabledPlugin = function (schema, defaultValue) {
137
+ var isDisabledPlugin = function (
138
+ // biome-ignore lint/suspicious/noExplicitAny: Schema generics must be loose to accept arbitrary consumer schemas
139
+ schema, defaultValue) {
135
140
  if (defaultValue === void 0) { defaultValue = false; }
136
141
  schema.add({
137
142
  disabled: {
@@ -143,6 +148,7 @@ var isDisabledPlugin = function (schema, defaultValue) {
143
148
  });
144
149
  };
145
150
  exports.isDisabledPlugin = isDisabledPlugin;
151
+ // biome-ignore lint/suspicious/noExplicitAny: Schema generics must be loose to accept arbitrary consumer schemas
146
152
  var createdUpdatedPlugin = function (schema) {
147
153
  schema.add({
148
154
  updated: { description: "When this document was last updated", index: true, type: Date },
@@ -268,6 +274,7 @@ exports.findExactlyOne = findExactlyOne;
268
274
  * match the conditions to prevent ambiguous updates.
269
275
  * @param schema Mongoose Schema
270
276
  */
277
+ // biome-ignore lint/suspicious/noExplicitAny: Schema generics with unknown collide with mongoose's loose this-binding on schema.statics
271
278
  var upsertPlugin = function (schema) {
272
279
  schema.statics.upsert = function (conditions, update) {
273
280
  return __awaiter(this, void 0, void 0, function () {
@@ -285,14 +292,16 @@ var upsertPlugin = function (schema) {
285
292
  });
286
293
  }
287
294
  doc = docs[0];
288
- if (doc) {
289
- // If the document exists, update it with the provided update values.
290
- Object.assign(doc, update);
291
- return [2 /*return*/, doc.save()];
292
- }
295
+ if (!doc) return [3 /*break*/, 3];
296
+ // If the document exists, update it with the provided update values.
297
+ Object.assign(doc, update);
298
+ return [4 /*yield*/, doc.save()];
299
+ case 2: return [2 /*return*/, (_a.sent())];
300
+ case 3:
293
301
  combinedData = __assign(__assign({}, conditions), update);
294
302
  newDoc = new this(combinedData);
295
- return [2 /*return*/, newDoc.save()];
303
+ return [4 /*yield*/, newDoc.save()];
304
+ case 4: return [2 /*return*/, (_a.sent())];
296
305
  }
297
306
  });
298
307
  });
@@ -313,7 +322,7 @@ var DateOnly = /** @class */ (function (_super) {
313
322
  // When using $gt, $gte, $lt, $lte, etc, we need to cast the value to a Date
314
323
  DateOnly.prototype.castForQuery = function ($conditional, val, context) {
315
324
  if ($conditional == null) {
316
- // noExplicitAny: applySetters is an internal Mongoose SchemaType method not in public type definitions
325
+ // biome-ignore lint/suspicious/noExplicitAny: applySetters is an internal Mongoose SchemaType method not in public type definitions
317
326
  return this.applySetters(val, context);
318
327
  }
319
328
  var handler = this.$conditionalHandlers[$conditional];
@@ -354,5 +363,5 @@ var DateOnly = /** @class */ (function (_super) {
354
363
  }(mongoose_1.SchemaType));
355
364
  exports.DateOnly = DateOnly;
356
365
  // Register DateOnly with Mongoose's Schema.Types
357
- // noExplicitAny: DateOnly is a custom SchemaType not declared in Mongoose's Schema.Types interface
366
+ // biome-ignore lint/suspicious/noExplicitAny: DateOnly is a custom SchemaType not declared in Mongoose's Schema.Types interface
358
367
  mongoose_1.default.Schema.Types.DateOnly = DateOnly;
@@ -55,6 +55,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
55
55
  return (mod && mod.__esModule) ? mod : { "default": mod };
56
56
  };
57
57
  Object.defineProperty(exports, "__esModule", { value: true });
58
+ // biome-ignore-all lint/suspicious/noExplicitAny: test mock typing
58
59
  var bun_test_1 = require("bun:test");
59
60
  var mongoose_1 = require("mongoose");
60
61
  var supertest_1 = __importDefault(require("supertest"));
@@ -77,7 +78,6 @@ var StuffModel = (0, mongoose_1.model)("Stuff", stuffSchema);
77
78
  (0, bun_test_1.describe)("baseUserPlugin", function () {
78
79
  (0, bun_test_1.it)("adds admin and email fields to the schema", function () {
79
80
  var testSchema = new mongoose_1.Schema({});
80
- // biome-ignore lint/suspicious/noExplicitAny: test schema
81
81
  (0, plugins_1.baseUserPlugin)(testSchema);
82
82
  var adminPath = testSchema.path("admin");
83
83
  (0, bun_test_1.expect)(adminPath).toBeDefined();
@@ -1,15 +1,22 @@
1
- import type { Document } from "mongoose";
2
- export type PopulatePath = {
1
+ import type { Document, Schema } from "mongoose";
2
+ interface OpenApiSchemaNode {
3
+ description?: string;
4
+ items?: OpenApiSchemaNode;
5
+ properties?: Record<string, OpenApiSchemaNode>;
6
+ type?: string;
7
+ }
8
+ export interface PopulatePath {
3
9
  path: string;
4
- openApiComponent?: any;
10
+ openApiComponent?: string;
5
11
  fields?: string[];
6
- };
7
- export declare const fixMixedFields: (schema: any, properties: Record<string, any>) => void;
8
- export declare function getOpenApiSpecForModel(model: any, { populatePaths, extraModelProperties, }?: {
12
+ }
13
+ export declare const fixMixedFields: (schema: Schema | null, properties: Record<string, OpenApiSchemaNode> | Record<string, unknown> | null) => void;
14
+ export declare const getOpenApiSpecForModel: (model: any, { populatePaths, extraModelProperties, }?: {
9
15
  populatePaths?: PopulatePath[];
10
16
  extraModelProperties?: Record<string, unknown>;
11
- }): {
17
+ }) => {
12
18
  properties: Record<string, unknown>;
13
19
  required: string[];
14
20
  };
15
- export declare function unpopulate<T>(doc: Document<T>, path: string): Document<T>;
21
+ export declare const unpopulate: <T>(doc: Document<T>, path: string) => Document<T>;
22
+ export {};
package/dist/populate.js CHANGED
@@ -50,19 +50,14 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
50
50
  return (mod && mod.__esModule) ? mod : { "default": mod };
51
51
  };
52
52
  Object.defineProperty(exports, "__esModule", { value: true });
53
- exports.fixMixedFields = void 0;
54
- exports.getOpenApiSpecForModel = getOpenApiSpecForModel;
55
- exports.unpopulate = unpopulate;
53
+ exports.unpopulate = exports.getOpenApiSpecForModel = exports.fixMixedFields = void 0;
56
54
  var isArray_1 = __importDefault(require("lodash/isArray"));
57
55
  var mongoose_to_swagger_1 = __importDefault(require("mongoose-to-swagger"));
58
56
  var errors_1 = require("./errors");
59
57
  var m2sOptions = {
60
58
  props: ["readOnly", "required", "enum", "default"],
61
59
  };
62
- // This function filters an object to only include specified keys.
63
- // It supports nested keys using dot notation (e.g., 'user.name').
64
- // If no keys are provided, it returns the original object.
65
- // The function recursively traverses the object structure to handle nested properties.
60
+ // Keeps only the specified dot-notation keys from an object.
66
61
  var filterKeys = function (obj, keysToKeep) {
67
62
  if (!keysToKeep) {
68
63
  return obj;
@@ -98,7 +93,7 @@ var filterKeys = function (obj, keysToKeep) {
98
93
  };
99
94
  // Helper function to get the path in the OpenAPI schema, so we can swap out the type for the
100
95
  // populated model component or generated type.
101
- function getPathInSchema(schema, path) {
96
+ var getPathInSchema = function (schema, path) {
102
97
  var _a;
103
98
  var keys = path.split(".");
104
99
  var currentSchema = schema;
@@ -119,23 +114,21 @@ function getPathInSchema(schema, path) {
119
114
  break;
120
115
  }
121
116
  else {
122
- throw new Error("Path ".concat(path, " not found in schema at key ").concat(key));
117
+ throw new errors_1.APIError({ status: 500, title: "Path ".concat(path, " not found in schema at key ").concat(key) });
123
118
  }
124
119
  }
125
120
  return fullPath;
126
- }
127
- // Replaces populated properties with the populated schema.
128
- // Recursively walks a Mongoose schema and fixes any Mixed fields in the
129
- // OpenAPI properties so they use an empty schema (accepts any type) instead
130
- // of the `{type: "object", properties: {}}` that mongoose-to-swagger emits.
121
+ };
122
+ // Corrects Mixed-type fields in OpenAPI properties so they accept any value.
131
123
  var fixMixedFields = function (schema, properties) {
132
124
  var e_1, _a;
133
125
  var _b, _c, _d;
134
126
  if (!properties || !schema) {
135
127
  return;
136
128
  }
129
+ var props = properties;
137
130
  try {
138
- for (var _e = __values(Object.keys(properties)), _f = _e.next(); !_f.done; _f = _e.next()) {
131
+ for (var _e = __values(Object.keys(props)), _f = _e.next(); !_f.done; _f = _e.next()) {
139
132
  var key = _f.value;
140
133
  var schemaPath = schema.path(key);
141
134
  if (!schemaPath) {
@@ -143,14 +136,15 @@ var fixMixedFields = function (schema, properties) {
143
136
  }
144
137
  // Direct Mixed field
145
138
  if (schemaPath.instance === "Mixed") {
146
- properties[key] = { description: (_b = properties[key]) === null || _b === void 0 ? void 0 : _b.description };
139
+ props[key] = { description: (_b = props[key]) === null || _b === void 0 ? void 0 : _b.description };
147
140
  continue;
148
141
  }
149
142
  // Array of sub-documents — check each sub-field for Mixed
150
- if (schemaPath.instance === "Array" &&
151
- schemaPath.schema &&
152
- ((_d = (_c = properties[key]) === null || _c === void 0 ? void 0 : _c.items) === null || _d === void 0 ? void 0 : _d.properties)) {
153
- (0, exports.fixMixedFields)(schemaPath.schema, properties[key].items.properties);
143
+ if (schemaPath.instance === "Array" && schemaPath.schema) {
144
+ var itemProperties = (_d = (_c = props[key]) === null || _c === void 0 ? void 0 : _c.items) === null || _d === void 0 ? void 0 : _d.properties;
145
+ if (itemProperties) {
146
+ (0, exports.fixMixedFields)(schemaPath.schema, itemProperties);
147
+ }
154
148
  }
155
149
  }
156
150
  }
@@ -163,7 +157,9 @@ var fixMixedFields = function (schema, properties) {
163
157
  }
164
158
  };
165
159
  exports.fixMixedFields = fixMixedFields;
166
- function getOpenApiSpecForModel(model, _a) {
160
+ var getOpenApiSpecForModel = function (
161
+ // biome-ignore lint/suspicious/noExplicitAny: noExplicitAny: Mongoose Model param uses deep internal APIs (schema.path().options.ref, schema.virtuals, schema.childSchemas, db.model) that are not exposed in public type definitions
162
+ model, _a) {
167
163
  var e_2, _b, _c, e_3, _d, e_4, _e, e_5, _f;
168
164
  var _g, _h, _j, _k;
169
165
  var _l = _a === void 0 ? {} : _a, populatePaths = _l.populatePaths, extraModelProperties = _l.extraModelProperties;
@@ -296,17 +292,19 @@ function getOpenApiSpecForModel(model, _a) {
296
292
  properties: __assign(__assign({}, modelSwagger.properties), extraModelProperties),
297
293
  required: (_k = modelSwagger.required) !== null && _k !== void 0 ? _k : [],
298
294
  };
299
- }
295
+ };
296
+ exports.getOpenApiSpecForModel = getOpenApiSpecForModel;
300
297
  // Helper function to unpopulate a document that has been populated.
301
298
  // This is helpful for supporting backwards compatibility. E.g. you use populatePaths
302
299
  // to populate a document but if the version header for the request is below the version
303
300
  // that the populatePath was added, we remove the population and just return the _id.
304
- function unpopulate(doc, path) {
301
+ var unpopulate = function (doc, path) {
305
302
  if (!path) {
306
303
  throw new errors_1.APIError({ status: 500, title: "path is required for unpopulate" });
307
304
  }
308
305
  var pathParts = path.split(".");
309
306
  // Recursive because we need to support nested paths.
307
+ // biome-ignore lint/suspicious/noExplicitAny: noExplicitAny: recursive document traversal uses bracket-notation indexing on arbitrary nested document shapes that Mongoose Document types do not expose
310
308
  var recursiveUnpopulate = function (current, parts) {
311
309
  var e_6, _a;
312
310
  var _b;
@@ -352,4 +350,5 @@ function unpopulate(doc, path) {
352
350
  return current;
353
351
  };
354
352
  return recursiveUnpopulate(doc, pathParts);
355
- }
353
+ };
354
+ exports.unpopulate = unpopulate;
@@ -85,6 +85,7 @@ var __read = (this && this.__read) || function (o, n) {
85
85
  return ar;
86
86
  };
87
87
  Object.defineProperty(exports, "__esModule", { value: true });
88
+ // biome-ignore-all lint/suspicious/noExplicitAny: test mock typing
88
89
  var bun_test_1 = require("bun:test");
89
90
  var mongoose_1 = __importStar(require("mongoose"));
90
91
  var populate_1 = require("./populate");