@terreno/api 0.0.11 → 0.0.13

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.
@@ -78,26 +78,33 @@ var axios_1 = __importDefault(require("axios"));
78
78
  var errors_1 = require("../errors");
79
79
  var logger_1 = require("../logger");
80
80
  // Convenience method to send data to a Slack webhook.
81
+ // If `url` is provided, it will be used directly instead of looking up from environment.
82
+ // DEPRECATED: Looking up webhook URLs from the SLACK_WEBHOOKS environment variable by channel name
83
+ // is deprecated and will be removed in a future version. Please pass the `url` parameter directly.
81
84
  function sendToSlack(text_1) {
82
85
  return __awaiter(this, arguments, void 0, function (text, _a) {
83
- var slackWebhooksString, slackWebhooks, channel, slackWebhookUrl, formattedText, error_1;
86
+ var slackWebhookUrl, slackWebhooksString, slackWebhooks, channel, formattedText, error_1;
84
87
  var _b, _c, _d;
85
- var _e = _a === void 0 ? {} : _a, slackChannel = _e.slackChannel, _f = _e.shouldThrow, shouldThrow = _f === void 0 ? false : _f, env = _e.env;
88
+ var _e = _a === void 0 ? {} : _a, slackChannel = _e.slackChannel, _f = _e.shouldThrow, shouldThrow = _f === void 0 ? false : _f, env = _e.env, url = _e.url;
86
89
  return __generator(this, function (_g) {
87
90
  switch (_g.label) {
88
91
  case 0:
89
- slackWebhooksString = process.env.SLACK_WEBHOOKS;
90
- if (!slackWebhooksString) {
91
- logger_1.logger.debug("You must set SLACK_WEBHOOKS in the environment to use sendToSlack.");
92
- return [2 /*return*/];
93
- }
94
- slackWebhooks = JSON.parse(slackWebhooksString !== null && slackWebhooksString !== void 0 ? slackWebhooksString : "{}");
95
- channel = slackChannel !== null && slackChannel !== void 0 ? slackChannel : "default";
96
- slackWebhookUrl = (_b = slackWebhooks[channel]) !== null && _b !== void 0 ? _b : slackWebhooks.default;
92
+ slackWebhookUrl = url;
97
93
  if (!slackWebhookUrl) {
98
- Sentry.captureException(new Error("No webhook url set in env for ".concat(channel, ". Slack message not sent")));
99
- logger_1.logger.debug("No webhook url set in env for ".concat(channel, "."));
100
- return [2 /*return*/];
94
+ logger_1.logger.debug("DEPRECATED: Looking up webhook URLs from SLACK_WEBHOOKS environment variable is deprecated and will be removed in a future version. Please pass the url parameter directly.");
95
+ slackWebhooksString = process.env.SLACK_WEBHOOKS;
96
+ if (!slackWebhooksString) {
97
+ logger_1.logger.debug("You must set SLACK_WEBHOOKS in the environment to use sendToSlack.");
98
+ return [2 /*return*/];
99
+ }
100
+ slackWebhooks = JSON.parse(slackWebhooksString !== null && slackWebhooksString !== void 0 ? slackWebhooksString : "{}");
101
+ channel = slackChannel !== null && slackChannel !== void 0 ? slackChannel : "default";
102
+ slackWebhookUrl = (_b = slackWebhooks[channel]) !== null && _b !== void 0 ? _b : slackWebhooks.default;
103
+ if (!slackWebhookUrl) {
104
+ Sentry.captureException(new Error("No webhook url set in env for ".concat(channel, ". Slack message not sent")));
105
+ logger_1.logger.debug("No webhook url set in env for ".concat(channel, "."));
106
+ return [2 /*return*/];
107
+ }
101
108
  }
102
109
  formattedText = text;
103
110
  if (env) {
@@ -23,4 +23,4 @@ export declare const Permissions: {
23
23
  IsOwnerOrReadOnly: (method: RESTMethod, user?: User, obj?: any) => boolean;
24
24
  };
25
25
  export declare function checkPermissions<T>(method: RESTMethod, permissions: PermissionMethod<T>[], user?: User, obj?: T): Promise<boolean>;
26
- export declare function permissionMiddleware<T>(baseModel: Model<T>, options: Pick<ModelRouterOptions<T>, "permissions" | "populatePaths" | "discriminatorKey">): (req: express.Request, _res: express.Response, next: NextFunction) => Promise<void>;
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>;
@@ -194,20 +194,20 @@ function checkPermissions(method, permissions, user, obj) {
194
194
  // Check the permissions for a given model and method. If the method is a read, update, or delete,
195
195
  // finds the relevant object, checks the permissions, and attaches the object to the request as
196
196
  // req.obj.
197
- function permissionMiddleware(baseModel, options) {
197
+ function permissionMiddleware(model, options) {
198
198
  var _this = this;
199
199
  return function (req, _res, next) { return __awaiter(_this, void 0, void 0, function () {
200
- var method, reqMethod, model, builtQuery, populatedQuery, data, error_1, hiddenDoc, error, reason, error, error_2;
201
- var _a, _b, _c, _d;
202
- return __generator(this, function (_e) {
203
- switch (_e.label) {
200
+ var method, reqMethod, builtQuery, populatedQuery, data, error_1, hiddenDoc, error, reason, error, error_2;
201
+ var _a, _b;
202
+ return __generator(this, function (_c) {
203
+ switch (_c.label) {
204
204
  case 0:
205
205
  if (req.method === "OPTIONS") {
206
206
  return [2 /*return*/, next()];
207
207
  }
208
- _e.label = 1;
208
+ _c.label = 1;
209
209
  case 1:
210
- _e.trys.push([1, 10, , 11]);
210
+ _c.trys.push([1, 10, , 11]);
211
211
  method = void 0;
212
212
  reqMethod = req.method.toLowerCase();
213
213
  if (reqMethod === "post") {
@@ -233,11 +233,10 @@ function permissionMiddleware(baseModel, options) {
233
233
  title: "Method ".concat(req.method, " not allowed"),
234
234
  });
235
235
  }
236
- model = (0, api_1.getModel)(baseModel, req.body, options);
237
236
  return [4 /*yield*/, checkPermissions(method, options.permissions[method], req.user)];
238
237
  case 2:
239
238
  // All methods check for permissions.
240
- if (!(_e.sent())) {
239
+ if (!(_c.sent())) {
241
240
  throw new errors_1.APIError({
242
241
  status: 405,
243
242
  title: "Access to ".concat(method.toUpperCase(), " on ").concat(model.modelName, " ") +
@@ -250,34 +249,27 @@ function permissionMiddleware(baseModel, options) {
250
249
  builtQuery = model.findById(req.params.id);
251
250
  populatedQuery = (0, api_1.addPopulateToQuery)(builtQuery, options.populatePaths);
252
251
  data = void 0;
253
- _e.label = 3;
252
+ _c.label = 3;
254
253
  case 3:
255
- _e.trys.push([3, 5, , 6]);
254
+ _c.trys.push([3, 5, , 6]);
256
255
  return [4 /*yield*/, populatedQuery.exec()];
257
256
  case 4:
258
- data = _e.sent();
257
+ data = _c.sent();
259
258
  return [3 /*break*/, 6];
260
259
  case 5:
261
- error_1 = _e.sent();
260
+ error_1 = _c.sent();
262
261
  throw new errors_1.APIError({
263
262
  error: error_1,
264
263
  status: 500,
265
264
  title: "GET failed on ".concat(req.params.id),
266
265
  });
267
266
  case 6:
268
- if (!(!data || (["update", "delete"].includes(method) && (data === null || data === void 0 ? void 0 : data.__t) && !((_b = req.body) === null || _b === void 0 ? void 0 : _b.__t)))) return [3 /*break*/, 8];
269
- // For discriminated models, return 404 without checking hidden state
270
- if (["update", "delete"].includes(method) && (data === null || data === void 0 ? void 0 : data.__t) && !((_c = req.body) === null || _c === void 0 ? void 0 : _c.__t)) {
271
- throw new errors_1.APIError({
272
- status: 404,
273
- title: "Document ".concat(req.params.id, " not found for model ").concat(model.modelName),
274
- });
275
- }
267
+ if (!!data) return [3 /*break*/, 8];
276
268
  return [4 /*yield*/, model.collection.findOne({
277
269
  _id: new mongoose_1.default.Types.ObjectId(req.params.id),
278
270
  })];
279
271
  case 7:
280
- hiddenDoc = _e.sent();
272
+ hiddenDoc = _c.sent();
281
273
  if (!hiddenDoc) {
282
274
  Sentry.captureMessage("Document ".concat(req.params.id, " not found for model ").concat(model.modelName));
283
275
  error = new errors_1.APIError({
@@ -312,16 +304,16 @@ function permissionMiddleware(baseModel, options) {
312
304
  });
313
305
  case 8: return [4 /*yield*/, checkPermissions(method, options.permissions[method], req.user, data)];
314
306
  case 9:
315
- if (!(_e.sent())) {
307
+ if (!(_c.sent())) {
316
308
  throw new errors_1.APIError({
317
309
  status: 403,
318
- title: "Access to GET on ".concat(model.modelName, ":").concat(req.params.id, " denied for ").concat((_d = req.user) === null || _d === void 0 ? void 0 : _d.id),
310
+ title: "Access to GET on ".concat(model.modelName, ":").concat(req.params.id, " denied for ").concat((_b = req.user) === null || _b === void 0 ? void 0 : _b.id),
319
311
  });
320
312
  }
321
313
  req.obj = data;
322
314
  return [2 /*return*/, next()];
323
315
  case 10:
324
- error_2 = _e.sent();
316
+ error_2 = _c.sent();
325
317
  logger_1.logger.error("Permissions error: ".concat(error_2 instanceof Error ? error_2.message : error_2));
326
318
  return [2 /*return*/, next(error_2)];
327
319
  case 11: return [2 /*return*/];
@@ -411,3 +411,60 @@ var tests_1 = require("./tests");
411
411
  }); });
412
412
  });
413
413
  });
414
+ (0, bun_test_1.describe)("permissions module", function () {
415
+ (0, bun_test_1.describe)("OwnerQueryFilter", function () {
416
+ (0, bun_test_1.it)("returns ownerId filter when user is provided", function () {
417
+ var user = { id: "user-123" };
418
+ var filter = (0, permissions_1.OwnerQueryFilter)(user);
419
+ (0, bun_test_1.expect)(filter).toEqual({ ownerId: "user-123" });
420
+ });
421
+ (0, bun_test_1.it)("returns null when user is undefined", function () {
422
+ var filter = (0, permissions_1.OwnerQueryFilter)(undefined);
423
+ (0, bun_test_1.expect)(filter).toBeNull();
424
+ });
425
+ });
426
+ (0, bun_test_1.describe)("Permissions.IsAuthenticatedOrReadOnly", function () {
427
+ (0, bun_test_1.it)("returns true for authenticated non-anonymous users", function () {
428
+ var user = { id: "user-123", isAnonymous: false };
429
+ (0, bun_test_1.expect)(permissions_1.Permissions.IsAuthenticatedOrReadOnly("create", user)).toBe(true);
430
+ });
431
+ (0, bun_test_1.it)("returns true for read methods when user is anonymous", function () {
432
+ var user = { id: "user-123", isAnonymous: true };
433
+ (0, bun_test_1.expect)(permissions_1.Permissions.IsAuthenticatedOrReadOnly("list", user)).toBe(true);
434
+ (0, bun_test_1.expect)(permissions_1.Permissions.IsAuthenticatedOrReadOnly("read", user)).toBe(true);
435
+ });
436
+ (0, bun_test_1.it)("returns false for write methods when user is anonymous", function () {
437
+ var user = { id: "user-123", isAnonymous: true };
438
+ (0, bun_test_1.expect)(permissions_1.Permissions.IsAuthenticatedOrReadOnly("create", user)).toBe(false);
439
+ (0, bun_test_1.expect)(permissions_1.Permissions.IsAuthenticatedOrReadOnly("update", user)).toBe(false);
440
+ (0, bun_test_1.expect)(permissions_1.Permissions.IsAuthenticatedOrReadOnly("delete", user)).toBe(false);
441
+ });
442
+ });
443
+ (0, bun_test_1.describe)("Permissions.IsOwnerOrReadOnly", function () {
444
+ (0, bun_test_1.it)("returns true when no object is provided", function () {
445
+ (0, bun_test_1.expect)(permissions_1.Permissions.IsOwnerOrReadOnly("update", { id: "user-123" }, undefined)).toBe(true);
446
+ });
447
+ (0, bun_test_1.it)("returns true for admin users", function () {
448
+ var user = { admin: true, id: "admin-123" };
449
+ var obj = { ownerId: "other-user" };
450
+ (0, bun_test_1.expect)(permissions_1.Permissions.IsOwnerOrReadOnly("update", user, obj)).toBe(true);
451
+ });
452
+ (0, bun_test_1.it)("returns true when user is owner", function () {
453
+ var user = { id: "user-123" };
454
+ var obj = { ownerId: "user-123" };
455
+ (0, bun_test_1.expect)(permissions_1.Permissions.IsOwnerOrReadOnly("update", user, obj)).toBe(true);
456
+ });
457
+ (0, bun_test_1.it)("returns true for read methods when not owner", function () {
458
+ var user = { id: "user-123" };
459
+ var obj = { ownerId: "other-user" };
460
+ (0, bun_test_1.expect)(permissions_1.Permissions.IsOwnerOrReadOnly("list", user, obj)).toBe(true);
461
+ (0, bun_test_1.expect)(permissions_1.Permissions.IsOwnerOrReadOnly("read", user, obj)).toBe(true);
462
+ });
463
+ (0, bun_test_1.it)("returns false for write methods when not owner", function () {
464
+ var user = { id: "user-123" };
465
+ var obj = { ownerId: "other-user" };
466
+ (0, bun_test_1.expect)(permissions_1.Permissions.IsOwnerOrReadOnly("update", user, obj)).toBe(false);
467
+ (0, bun_test_1.expect)(permissions_1.Permissions.IsOwnerOrReadOnly("delete", user, obj)).toBe(false);
468
+ });
469
+ });
470
+ });
@@ -131,3 +131,55 @@ var tests_1 = require("./tests");
131
131
  });
132
132
  }); });
133
133
  });
134
+ (0, bun_test_1.describe)("unpopulate edge cases", function () {
135
+ (0, bun_test_1.it)("throws error when path is empty", function () {
136
+ var doc = { name: "test" };
137
+ (0, bun_test_1.expect)(function () { return (0, populate_1.unpopulate)(doc, ""); }).toThrow("path is required");
138
+ });
139
+ (0, bun_test_1.it)("unpopulates single populated field", function () {
140
+ var doc = {
141
+ name: "test",
142
+ ownerId: { _id: "owner-123", email: "owner@test.com" },
143
+ };
144
+ var result = (0, populate_1.unpopulate)(doc, "ownerId");
145
+ (0, bun_test_1.expect)(result.ownerId).toBe("owner-123");
146
+ });
147
+ (0, bun_test_1.it)("unpopulates array of populated fields", function () {
148
+ var doc = {
149
+ items: [{ _id: "item-1", name: "Item 1" }, { _id: "item-2", name: "Item 2" }, "item-3"],
150
+ name: "test",
151
+ };
152
+ var result = (0, populate_1.unpopulate)(doc, "items");
153
+ (0, bun_test_1.expect)(result.items).toEqual(["item-1", "item-2", "item-3"]);
154
+ });
155
+ (0, bun_test_1.it)("handles nested paths", function () {
156
+ var doc = {
157
+ name: "test",
158
+ nested: {
159
+ items: [
160
+ { _id: "item-1", name: "Item 1" },
161
+ { _id: "item-2", name: "Item 2" },
162
+ ],
163
+ },
164
+ };
165
+ var result = (0, populate_1.unpopulate)(doc, "nested.items");
166
+ (0, bun_test_1.expect)(result.nested.items).toEqual(["item-1", "item-2"]);
167
+ });
168
+ (0, bun_test_1.it)("returns original doc when path does not exist", function () {
169
+ var doc = { name: "test" };
170
+ var result = (0, populate_1.unpopulate)(doc, "nonexistent");
171
+ (0, bun_test_1.expect)(result).toEqual(doc);
172
+ });
173
+ (0, bun_test_1.it)("handles nested array paths", function () {
174
+ var doc = {
175
+ containers: [
176
+ { items: [{ _id: "item-1" }, { _id: "item-2" }] },
177
+ { items: [{ _id: "item-3" }, { _id: "item-4" }] },
178
+ ],
179
+ name: "test",
180
+ };
181
+ var result = (0, populate_1.unpopulate)(doc, "containers.items");
182
+ (0, bun_test_1.expect)(result.containers[0].items).toEqual(["item-1", "item-2"]);
183
+ (0, bun_test_1.expect)(result.containers[1].items).toEqual(["item-3", "item-4"]);
184
+ });
185
+ });
package/dist/tests.d.ts CHANGED
@@ -46,54 +46,36 @@ export interface Food {
46
46
  likes: boolean;
47
47
  }[];
48
48
  }
49
- export declare const UserModel: mongoose.Model<User, {}, {}, {}, mongoose.Document<unknown, {}, User, {}, mongoose.DefaultSchemaOptions> & User & {
49
+ export declare const UserModel: mongoose.Model<User, {}, {}, {}, mongoose.Document<unknown, {}, User, {}, {}> & User & {
50
50
  _id: mongoose.Types.ObjectId;
51
51
  } & {
52
52
  __v: number;
53
- } & {
54
- id: string;
55
- }, any, User>;
56
- export declare const SuperUserModel: mongoose.Model<User & SuperUser, {}, {}, {
57
- id: string;
58
- }, mongoose.Document<unknown, {}, User & SuperUser, {
59
- id: string;
60
- }, mongoose.DefaultSchemaOptions> & Omit<User & SuperUser & {
53
+ }, any>;
54
+ export declare const SuperUserModel: mongoose.Model<User & SuperUser, {}, {}, {}, mongoose.Document<unknown, {}, User & SuperUser, {}, {}> & User & SuperUser & {
61
55
  _id: mongoose.Types.ObjectId;
62
56
  } & {
63
57
  __v: number;
64
- }, "id"> & {
65
- id: string;
66
- }, any, User & SuperUser>;
67
- export declare const StaffUserModel: mongoose.Model<User & StaffUser, {}, {}, {
68
- id: string;
69
- }, mongoose.Document<unknown, {}, User & StaffUser, {
70
- id: string;
71
- }, mongoose.DefaultSchemaOptions> & Omit<User & StaffUser & {
58
+ }, any>;
59
+ export declare const StaffUserModel: mongoose.Model<User & StaffUser, {}, {}, {}, mongoose.Document<unknown, {}, User & StaffUser, {}, {}> & User & StaffUser & {
72
60
  _id: mongoose.Types.ObjectId;
73
61
  } & {
74
62
  __v: number;
75
- }, "id"> & {
76
- id: string;
77
- }, any, User & StaffUser>;
63
+ }, any>;
78
64
  export declare const FoodModel: Model<Food>;
79
65
  interface RequiredField {
80
66
  name: string;
81
67
  about?: string;
82
68
  }
83
- export declare const RequiredModel: mongoose.Model<RequiredField, {}, {}, {}, mongoose.Document<unknown, {}, RequiredField, {}, mongoose.DefaultSchemaOptions> & RequiredField & {
69
+ export declare const RequiredModel: mongoose.Model<RequiredField, {}, {}, {}, mongoose.Document<unknown, {}, RequiredField, {}, {}> & RequiredField & {
84
70
  _id: mongoose.Types.ObjectId;
85
71
  } & {
86
72
  __v: number;
87
- } & {
88
- id: string;
89
- }, any, RequiredField>;
73
+ }, any>;
90
74
  export declare function getBaseServer(): Express;
91
75
  export declare function authAsUser(app: express.Application, type: "admin" | "notAdmin"): Promise<TestAgent>;
92
- export declare function setupDb(): Promise<(mongoose.Document<unknown, {}, User, {}, mongoose.DefaultSchemaOptions> & User & {
76
+ export declare function setupDb(): Promise<(mongoose.Document<unknown, {}, User, {}, {}> & User & {
93
77
  _id: mongoose.Types.ObjectId;
94
78
  } & {
95
79
  __v: number;
96
- } & {
97
- id: string;
98
80
  })[]>;
99
81
  export {};
@@ -1,14 +1,242 @@
1
1
  "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __generator = (this && this.__generator) || function (thisArg, body) {
12
+ 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);
13
+ return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
14
+ function verb(n) { return function (v) { return step([n, v]); }; }
15
+ function step(op) {
16
+ if (f) throw new TypeError("Generator is already executing.");
17
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
18
+ 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;
19
+ if (y = 0, t) op = [op[0] & 2, t.value];
20
+ switch (op[0]) {
21
+ case 0: case 1: t = op; break;
22
+ case 4: _.label++; return { value: op[1], done: false };
23
+ case 5: _.label++; y = op[1]; op = [0]; continue;
24
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
25
+ default:
26
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
27
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
28
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
29
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
30
+ if (t[2]) _.ops.pop();
31
+ _.trys.pop(); continue;
32
+ }
33
+ op = body.call(thisArg, _);
34
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
35
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
36
+ }
37
+ };
38
+ var __importDefault = (this && this.__importDefault) || function (mod) {
39
+ return (mod && mod.__esModule) ? mod : { "default": mod };
40
+ };
2
41
  Object.defineProperty(exports, "__esModule", { value: true });
3
42
  var bun_test_1 = require("bun:test");
43
+ var mongoose_1 = __importDefault(require("mongoose"));
4
44
  var utils_1 = require("./utils");
5
45
  (0, bun_test_1.describe)("utils", function () {
6
- (0, bun_test_1.it)("checks valid ObjectIds", function () {
7
- (0, bun_test_1.expect)((0, utils_1.isValidObjectId)("62c44da0003d9f8ee8cc925c")).toBe(true);
8
- (0, bun_test_1.expect)((0, utils_1.isValidObjectId)("620000000000000000000000")).toBe(true);
9
- // Mongoose's builtin "ObjectId.isValid" will falsely say this is an ObjectId.
10
- (0, bun_test_1.expect)((0, utils_1.isValidObjectId)("1234567890ab")).toBe(false);
11
- (0, bun_test_1.expect)((0, utils_1.isValidObjectId)("microsoft123")).toBe(false);
12
- (0, bun_test_1.expect)((0, utils_1.isValidObjectId)("62c44da0003d9f8ee8cc925x")).toBe(false);
46
+ (0, bun_test_1.describe)("isValidObjectId", function () {
47
+ (0, bun_test_1.it)("checks valid ObjectIds", function () {
48
+ (0, bun_test_1.expect)((0, utils_1.isValidObjectId)("62c44da0003d9f8ee8cc925c")).toBe(true);
49
+ (0, bun_test_1.expect)((0, utils_1.isValidObjectId)("620000000000000000000000")).toBe(true);
50
+ // Mongoose's builtin "ObjectId.isValid" will falsely say this is an ObjectId.
51
+ (0, bun_test_1.expect)((0, utils_1.isValidObjectId)("1234567890ab")).toBe(false);
52
+ (0, bun_test_1.expect)((0, utils_1.isValidObjectId)("microsoft123")).toBe(false);
53
+ (0, bun_test_1.expect)((0, utils_1.isValidObjectId)("62c44da0003d9f8ee8cc925x")).toBe(false);
54
+ });
55
+ });
56
+ (0, bun_test_1.describe)("checkModelsStrict", function () {
57
+ (0, bun_test_1.it)("throws error when toObject.virtuals is not true", function () {
58
+ // Create a schema without toObject.virtuals
59
+ var testSchema = new mongoose_1.default.Schema({ name: String });
60
+ testSchema.set("strict", "throw");
61
+ // Not setting toObject.virtuals
62
+ if (mongoose_1.default.models.ToObjectTestModel) {
63
+ delete mongoose_1.default.models.ToObjectTestModel;
64
+ }
65
+ mongoose_1.default.model("ToObjectTestModel", testSchema);
66
+ try {
67
+ // This should throw because ToObjectTestModel doesn't have toObject.virtuals
68
+ (0, bun_test_1.expect)(function () { return (0, utils_1.checkModelsStrict)(); }).toThrow("toObject.virtuals not set to true");
69
+ }
70
+ finally {
71
+ delete mongoose_1.default.models.ToObjectTestModel;
72
+ }
73
+ });
74
+ (0, bun_test_1.it)("throws error when toJSON.virtuals is not true", function () {
75
+ // Create a schema with toObject.virtuals but without toJSON.virtuals
76
+ var testSchema = new mongoose_1.default.Schema({ name: String });
77
+ testSchema.set("toObject", { virtuals: true });
78
+ testSchema.set("strict", "throw");
79
+ // Not setting toJSON.virtuals
80
+ if (mongoose_1.default.models.ToJsonTestModel) {
81
+ delete mongoose_1.default.models.ToJsonTestModel;
82
+ }
83
+ mongoose_1.default.model("ToJsonTestModel", testSchema);
84
+ // Use spyOn to intercept modelNames and return only our test model
85
+ var spy = (0, bun_test_1.spyOn)(mongoose_1.default, "modelNames").mockReturnValue(["ToJsonTestModel"]);
86
+ try {
87
+ (0, bun_test_1.expect)(function () { return (0, utils_1.checkModelsStrict)(); }).toThrow("toJSON.virtuals not set to true");
88
+ }
89
+ finally {
90
+ spy.mockRestore();
91
+ delete mongoose_1.default.models.ToJsonTestModel;
92
+ }
93
+ });
94
+ (0, bun_test_1.it)("throws error when strict mode is not set to throw", function () {
95
+ // Create a schema with virtuals but without strict mode
96
+ var testSchema = new mongoose_1.default.Schema({ name: String });
97
+ testSchema.set("toObject", { virtuals: true });
98
+ testSchema.set("toJSON", { virtuals: true });
99
+ // Not setting strict to "throw"
100
+ if (mongoose_1.default.models.StrictTestModel) {
101
+ delete mongoose_1.default.models.StrictTestModel;
102
+ }
103
+ mongoose_1.default.model("StrictTestModel", testSchema);
104
+ var spy = (0, bun_test_1.spyOn)(mongoose_1.default, "modelNames").mockReturnValue(["StrictTestModel"]);
105
+ try {
106
+ (0, bun_test_1.expect)(function () { return (0, utils_1.checkModelsStrict)(); }).toThrow("is not set to strict mode");
107
+ }
108
+ finally {
109
+ spy.mockRestore();
110
+ delete mongoose_1.default.models.StrictTestModel;
111
+ }
112
+ });
113
+ (0, bun_test_1.it)("passes when all checks pass", function () {
114
+ // Create a properly configured schema
115
+ var testSchema = new mongoose_1.default.Schema({ name: String });
116
+ testSchema.set("toObject", { virtuals: true });
117
+ testSchema.set("toJSON", { virtuals: true });
118
+ testSchema.set("strict", "throw");
119
+ if (mongoose_1.default.models.GoodTestModel) {
120
+ delete mongoose_1.default.models.GoodTestModel;
121
+ }
122
+ mongoose_1.default.model("GoodTestModel", testSchema);
123
+ var spy = (0, bun_test_1.spyOn)(mongoose_1.default, "modelNames").mockReturnValue(["GoodTestModel"]);
124
+ try {
125
+ (0, bun_test_1.expect)(function () { return (0, utils_1.checkModelsStrict)(); }).not.toThrow();
126
+ }
127
+ finally {
128
+ spy.mockRestore();
129
+ delete mongoose_1.default.models.GoodTestModel;
130
+ }
131
+ });
132
+ (0, bun_test_1.it)("skips strict mode check for ignored models", function () {
133
+ // Create a properly configured model
134
+ var goodSchema = new mongoose_1.default.Schema({ name: String });
135
+ goodSchema.set("toObject", { virtuals: true });
136
+ goodSchema.set("toJSON", { virtuals: true });
137
+ goodSchema.set("strict", "throw");
138
+ if (mongoose_1.default.models.GoodModel) {
139
+ delete mongoose_1.default.models.GoodModel;
140
+ }
141
+ mongoose_1.default.model("GoodModel", goodSchema);
142
+ // Create a model without strict mode that we'll ignore
143
+ var badSchema = new mongoose_1.default.Schema({ name: String });
144
+ badSchema.set("toObject", { virtuals: true });
145
+ badSchema.set("toJSON", { virtuals: true });
146
+ // Not setting strict - should fail unless ignored
147
+ if (mongoose_1.default.models.IgnoredModel) {
148
+ delete mongoose_1.default.models.IgnoredModel;
149
+ }
150
+ mongoose_1.default.model("IgnoredModel", badSchema);
151
+ var spy = (0, bun_test_1.spyOn)(mongoose_1.default, "modelNames").mockReturnValue(["GoodModel", "IgnoredModel"]);
152
+ try {
153
+ // Without ignoring, should throw for IgnoredModel
154
+ (0, bun_test_1.expect)(function () { return (0, utils_1.checkModelsStrict)(); }).toThrow("is not set to strict mode");
155
+ // With ignoring IgnoredModel, should pass
156
+ (0, bun_test_1.expect)(function () { return (0, utils_1.checkModelsStrict)(["IgnoredModel"]); }).not.toThrow();
157
+ }
158
+ finally {
159
+ spy.mockRestore();
160
+ delete mongoose_1.default.models.GoodModel;
161
+ delete mongoose_1.default.models.IgnoredModel;
162
+ }
163
+ });
164
+ (0, bun_test_1.it)("handles multiple models and validates all", function () {
165
+ // Create three properly configured models
166
+ var schema1 = new mongoose_1.default.Schema({ name: String });
167
+ schema1.set("toObject", { virtuals: true });
168
+ schema1.set("toJSON", { virtuals: true });
169
+ schema1.set("strict", "throw");
170
+ var schema2 = new mongoose_1.default.Schema({ value: Number });
171
+ schema2.set("toObject", { virtuals: true });
172
+ schema2.set("toJSON", { virtuals: true });
173
+ schema2.set("strict", "throw");
174
+ var schema3 = new mongoose_1.default.Schema({ active: Boolean });
175
+ schema3.set("toObject", { virtuals: true });
176
+ schema3.set("toJSON", { virtuals: true });
177
+ schema3.set("strict", "throw");
178
+ if (mongoose_1.default.models.MultiModel1)
179
+ delete mongoose_1.default.models.MultiModel1;
180
+ if (mongoose_1.default.models.MultiModel2)
181
+ delete mongoose_1.default.models.MultiModel2;
182
+ if (mongoose_1.default.models.MultiModel3)
183
+ delete mongoose_1.default.models.MultiModel3;
184
+ mongoose_1.default.model("MultiModel1", schema1);
185
+ mongoose_1.default.model("MultiModel2", schema2);
186
+ mongoose_1.default.model("MultiModel3", schema3);
187
+ var spy = (0, bun_test_1.spyOn)(mongoose_1.default, "modelNames").mockReturnValue([
188
+ "MultiModel1",
189
+ "MultiModel2",
190
+ "MultiModel3",
191
+ ]);
192
+ try {
193
+ (0, bun_test_1.expect)(function () { return (0, utils_1.checkModelsStrict)(); }).not.toThrow();
194
+ }
195
+ finally {
196
+ spy.mockRestore();
197
+ delete mongoose_1.default.models.MultiModel1;
198
+ delete mongoose_1.default.models.MultiModel2;
199
+ delete mongoose_1.default.models.MultiModel3;
200
+ }
201
+ });
202
+ (0, bun_test_1.it)("handles empty model list", function () {
203
+ var spy = (0, bun_test_1.spyOn)(mongoose_1.default, "modelNames").mockReturnValue([]);
204
+ try {
205
+ (0, bun_test_1.expect)(function () { return (0, utils_1.checkModelsStrict)(); }).not.toThrow();
206
+ }
207
+ finally {
208
+ spy.mockRestore();
209
+ }
210
+ });
211
+ });
212
+ (0, bun_test_1.describe)("timeout", function () {
213
+ (0, bun_test_1.it)("resolves after specified time", function () { return __awaiter(void 0, void 0, void 0, function () {
214
+ var start, elapsed;
215
+ return __generator(this, function (_a) {
216
+ switch (_a.label) {
217
+ case 0:
218
+ start = Date.now();
219
+ return [4 /*yield*/, (0, utils_1.timeout)(50)];
220
+ case 1:
221
+ _a.sent();
222
+ elapsed = Date.now() - start;
223
+ (0, bun_test_1.expect)(elapsed).toBeGreaterThanOrEqual(40);
224
+ return [2 /*return*/];
225
+ }
226
+ });
227
+ }); });
228
+ });
229
+ (0, bun_test_1.describe)("isValidObjectId additional cases", function () {
230
+ (0, bun_test_1.it)("returns true for valid ObjectId strings", function () {
231
+ (0, bun_test_1.expect)((0, utils_1.isValidObjectId)("507f1f77bcf86cd799439011")).toBe(true);
232
+ });
233
+ (0, bun_test_1.it)("returns false for invalid ObjectId strings", function () {
234
+ (0, bun_test_1.expect)((0, utils_1.isValidObjectId)("invalid-id")).toBe(false);
235
+ (0, bun_test_1.expect)((0, utils_1.isValidObjectId)("12345")).toBe(false);
236
+ (0, bun_test_1.expect)((0, utils_1.isValidObjectId)("")).toBe(false);
237
+ });
238
+ (0, bun_test_1.it)("returns false for 12-character strings that are not valid ObjectIds", function () {
239
+ (0, bun_test_1.expect)((0, utils_1.isValidObjectId)("123456789012")).toBe(false);
240
+ });
13
241
  });
14
242
  });
package/package.json CHANGED
@@ -64,7 +64,7 @@
64
64
  "main": "dist/index.js",
65
65
  "name": "@terreno/api",
66
66
  "peerDependencies": {
67
- "mongoose": "^9.0.0"
67
+ "mongoose": "^8.0.0"
68
68
  },
69
69
  "repository": {
70
70
  "type": "git",
@@ -81,8 +81,9 @@
81
81
  "lint:fix": "biome check --write ./src",
82
82
  "lint:unsafefix": "biome check --fix --unsafe ./src",
83
83
  "test": "bun test --preload ./src/tests/bunSetup.ts",
84
+ "test:ci": "bun test --preload ./src/tests/bunSetup.ts",
84
85
  "updateSnapshot": "bun test --update-snapshots"
85
86
  },
86
87
  "types": "dist/index.d.ts",
87
- "version": "0.0.11"
88
+ "version": "0.0.13"
88
89
  }