@terreno/api 0.13.0 → 0.13.2

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.
package/dist/plugins.js CHANGED
@@ -154,13 +154,13 @@ var createdUpdatedPlugin = function (schema) {
154
154
  }
155
155
  // If we aren't specifying created, use now.
156
156
  if (!this.created) {
157
- this.created = new Date();
157
+ this.created = luxon_1.DateTime.now().toJSDate();
158
158
  }
159
159
  // All writes change the updated time.
160
- this.updated = new Date();
160
+ this.updated = luxon_1.DateTime.now().toJSDate();
161
161
  });
162
162
  schema.pre(/save|updateOne|insertMany/, function () {
163
- void this.updateOne({}, { $set: { updated: new Date() } });
163
+ void this.updateOne({}, { $set: { updated: luxon_1.DateTime.now().toJSDate() } });
164
164
  });
165
165
  };
166
166
  exports.createdUpdatedPlugin = createdUpdatedPlugin;
@@ -313,6 +313,7 @@ var DateOnly = /** @class */ (function (_super) {
313
313
  // When using $gt, $gte, $lt, $lte, etc, we need to cast the value to a Date
314
314
  DateOnly.prototype.castForQuery = function ($conditional, val, context) {
315
315
  if ($conditional == null) {
316
+ // noExplicitAny: applySetters is an internal Mongoose SchemaType method not in public type definitions
316
317
  return this.applySetters(val, context);
317
318
  }
318
319
  var handler = this.$conditionalHandlers[$conditional];
@@ -345,10 +346,13 @@ var DateOnly = /** @class */ (function (_super) {
345
346
  throw new mongoose_1.Error.CastError("DateOnly", val, this.path, new Error("Value is not a valid date"));
346
347
  };
347
348
  DateOnly.prototype.get = function (val) {
348
- return (val instanceof Date ? luxon_1.DateTime.fromJSDate(val).startOf("day").toJSDate() : val);
349
+ return (val instanceof Date
350
+ ? luxon_1.DateTime.fromJSDate(val).startOf("day").toJSDate()
351
+ : val);
349
352
  };
350
353
  return DateOnly;
351
354
  }(mongoose_1.SchemaType));
352
355
  exports.DateOnly = DateOnly;
353
356
  // Register DateOnly with Mongoose's Schema.Types
357
+ // noExplicitAny: DateOnly is a custom SchemaType not declared in Mongoose's Schema.Types interface
354
358
  mongoose_1.default.Schema.Types.DateOnly = DateOnly;
@@ -92,6 +92,7 @@ var tests_1 = require("./tests");
92
92
  (0, bun_test_1.describe)("populate functions", function () {
93
93
  var admin;
94
94
  var notAdmin;
95
+ // noExplicitAny: typing as HydratedDocument<Food> causes cascading errors on populated field access patterns (e.g. populated.ownerId.name)
95
96
  var spinach;
96
97
  (0, bun_test_1.beforeEach)(function () { return __awaiter(void 0, void 0, void 0, function () {
97
98
  var _a, _b;
@@ -228,7 +229,9 @@ var tests_1 = require("./tests");
228
229
  });
229
230
  (0, bun_test_1.it)("replaces Mixed fields with only description", function () {
230
231
  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
+ var properties = {
233
+ data: { description: "any data", type: "object" },
234
+ };
232
235
  (0, populate_1.fixMixedFields)(schema, properties);
233
236
  (0, bun_test_1.expect)(properties.data).toEqual({ description: "any data" });
234
237
  });
@@ -338,6 +341,7 @@ var tests_1 = require("./tests");
338
341
  populatePaths: [{ fields: ["__proto__.polluted"], path: "ownerId" }],
339
342
  });
340
343
  (0, bun_test_1.expect)(result.properties).toBeDefined();
344
+ // noExplicitAny: testing that prototype pollution did not add a 'polluted' property to Object.prototype
341
345
  (0, bun_test_1.expect)(Object.prototype.polluted).toBeUndefined();
342
346
  var ownerProps = result.properties.ownerId.properties;
343
347
  (0, bun_test_1.expect)(ownerProps).toBeDefined();
@@ -168,7 +168,10 @@ var GcpSecretProvider = /** @class */ (function () {
168
168
  case 4:
169
169
  SecretManagerServiceClient = (_b = mod.SecretManagerServiceClient) !== null && _b !== void 0 ? _b : (_c = mod.default) === null || _c === void 0 ? void 0 : _c.SecretManagerServiceClient;
170
170
  if (!SecretManagerServiceClient) {
171
- throw new Error("SecretManagerServiceClient not found in @google-cloud/secret-manager module");
171
+ throw new errors_1.APIError({
172
+ status: 500,
173
+ title: "SecretManagerServiceClient not found in @google-cloud/secret-manager module",
174
+ });
172
175
  }
173
176
  this.client = new SecretManagerServiceClient();
174
177
  _d.label = 5;
package/dist/tests.js CHANGED
@@ -156,6 +156,7 @@ var foodSchema = new mongoose_1.Schema({
156
156
  type: mongoose_1.Schema.Types.ObjectId,
157
157
  },
158
158
  ],
159
+ // noExplicitAny: DateOnly is a custom SchemaType not recognized by Mongoose's built-in type definitions
159
160
  expiration: { description: "Expiration date of the food", type: plugins_1.DateOnly },
160
161
  hidden: {
161
162
  default: false,
@@ -6,7 +6,7 @@ export interface TerrenoTransformer<T> {
6
6
  transform?: (obj: Partial<T>, method: "create" | "update", user?: User) => Partial<T> | undefined;
7
7
  serialize?: (obj: T, user?: User) => Partial<T> | undefined;
8
8
  }
9
- export declare function AdminOwnerTransformer<T>(options: {
9
+ export declare const AdminOwnerTransformer: <T>(options: {
10
10
  anonReadFields?: string[];
11
11
  authReadFields?: string[];
12
12
  ownerReadFields?: string[];
@@ -15,11 +15,11 @@ export declare function AdminOwnerTransformer<T>(options: {
15
15
  authWriteFields?: string[];
16
16
  ownerWriteFields?: string[];
17
17
  adminWriteFields?: string[];
18
- }): TerrenoTransformer<T>;
19
- export declare function transform<T>(options: ModelRouterOptions<T>, data: Partial<T> | Partial<T>[], method: "create" | "update", user?: User): Partial<T> | (Partial<T> | undefined)[] | undefined;
20
- export declare function serialize<T>(req: express.Request, options: ModelRouterOptions<T>, data: (Document<any, any, any> & T) | (Document<any, any, any> & T)[]): Partial<T> | (Partial<T> | undefined)[] | undefined;
18
+ }) => TerrenoTransformer<T>;
19
+ export declare const transform: <T>(options: ModelRouterOptions<T>, data: Partial<T> | Partial<T>[], method: "create" | "update", user?: User) => Partial<T> | (Partial<T> | undefined)[] | undefined;
20
+ export declare const serialize: <T>(req: express.Request, options: ModelRouterOptions<T>, data: (Document & T) | (Document & T)[]) => Partial<T> | (Partial<T> | undefined)[] | undefined;
21
21
  /**
22
22
  * Default response handler for modelRouter. Calls toObject on each doc and returns the result,
23
23
  * using transformers.serializer if provided.
24
24
  */
25
- export declare function defaultResponseHandler<T>(doc: (Document<any, any, any> & T) | (Document<any, any, any> & T)[] | null, method: "list" | "create" | "read" | "update", request: express.Request, options: ModelRouterOptions<T>): Promise<Partial<T> | (Partial<T> | undefined)[] | null | undefined>;
25
+ export declare const defaultResponseHandler: <T>(doc: (Document & T) | (Document & T)[] | null, method: "list" | "create" | "read" | "update", request: express.Request, options: ModelRouterOptions<T>) => Promise<Partial<T> | (Partial<T> | undefined)[] | null | undefined>;
@@ -72,13 +72,10 @@ 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
- exports.AdminOwnerTransformer = AdminOwnerTransformer;
76
- exports.transform = transform;
77
- exports.serialize = serialize;
78
- exports.defaultResponseHandler = defaultResponseHandler;
75
+ exports.defaultResponseHandler = exports.serialize = exports.transform = exports.AdminOwnerTransformer = void 0;
79
76
  var errors_1 = require("./errors");
80
77
  var logger_1 = require("./logger");
81
- function getUserType(user, obj) {
78
+ var getUserType = function (user, obj) {
82
79
  if (user === null || user === void 0 ? void 0 : user.admin) {
83
80
  return "admin";
84
81
  }
@@ -89,9 +86,9 @@ function getUserType(user, obj) {
89
86
  return "auth";
90
87
  }
91
88
  return "anon";
92
- }
93
- function AdminOwnerTransformer(options) {
94
- function pickFields(obj, fields) {
89
+ };
90
+ var AdminOwnerTransformer = function (options) {
91
+ var pickFields = function (obj, fields) {
95
92
  var e_1, _a;
96
93
  var newData = {};
97
94
  try {
@@ -110,7 +107,7 @@ function AdminOwnerTransformer(options) {
110
107
  finally { if (e_1) throw e_1.error; }
111
108
  }
112
109
  return newData;
113
- }
110
+ };
114
111
  return {
115
112
  serialize: function (obj, user) {
116
113
  var _a, _b, _c, _d;
@@ -126,7 +123,6 @@ function AdminOwnerTransformer(options) {
126
123
  }
127
124
  return pickFields(obj, __spreadArray(__spreadArray([], __read(((_d = options.anonReadFields) !== null && _d !== void 0 ? _d : [])), false), ["id"], false));
128
125
  },
129
- // TODO: Migrate AdminOwnerTransform to use pre-hooks.
130
126
  transform: function (obj, _method, user) {
131
127
  var _a, _b, _c, _d;
132
128
  var userType = getUserType(user, obj);
@@ -145,13 +141,17 @@ function AdminOwnerTransformer(options) {
145
141
  }
146
142
  var unallowedFields = Object.keys(obj).filter(function (k) { return !allowedFields.includes(k); });
147
143
  if (unallowedFields.length) {
148
- throw new Error("User of type ".concat(userType, " cannot write fields: ").concat(unallowedFields.join(", ")));
144
+ throw new errors_1.APIError({
145
+ status: 403,
146
+ title: "User of type ".concat(userType, " cannot write fields: ").concat(unallowedFields.join(", ")),
147
+ });
149
148
  }
150
149
  return obj;
151
150
  },
152
151
  };
153
- }
154
- function transform(options, data, method, user) {
152
+ };
153
+ exports.AdminOwnerTransformer = AdminOwnerTransformer;
154
+ var transform = function (options, data, method, user) {
155
155
  var _a, _b;
156
156
  if (!((_a = options.transformer) === null || _a === void 0 ? void 0 : _a.transform)) {
157
157
  return data;
@@ -163,8 +163,9 @@ function transform(options, data, method, user) {
163
163
  return transformFn(data, method, user);
164
164
  }
165
165
  return data.map(function (d) { return transformFn(d, method, user); });
166
- }
167
- function serialize(req, options, data) {
166
+ };
167
+ exports.transform = transform;
168
+ var serialize = function (req, options, data) {
168
169
  var _a;
169
170
  var serializeFn = function (serializeData, serializeUser) {
170
171
  var _a, _b;
@@ -190,30 +191,30 @@ function serialize(req, options, data) {
190
191
  return serializeFn(data, req.user);
191
192
  }
192
193
  return data.map(function (d) { return serializeFn(d, req.user); });
193
- }
194
+ };
195
+ exports.serialize = serialize;
194
196
  /**
195
197
  * Default response handler for modelRouter. Calls toObject on each doc and returns the result,
196
198
  * using transformers.serializer if provided.
197
199
  */
198
- function defaultResponseHandler(doc, method, request, options) {
199
- return __awaiter(this, void 0, void 0, function () {
200
- var errorObj;
201
- return __generator(this, function (_a) {
202
- if (!doc) {
203
- return [2 /*return*/, null];
204
- }
205
- try {
206
- return [2 /*return*/, serialize(request, options, doc)];
207
- }
208
- catch (error) {
209
- errorObj = error;
210
- throw new errors_1.APIError({
211
- error: errorObj,
212
- status: 400,
213
- title: "Error serializing ".concat(method, " response: ").concat(errorObj.message),
214
- });
215
- }
216
- return [2 /*return*/];
217
- });
200
+ var defaultResponseHandler = function (doc, method, request, options) { return __awaiter(void 0, void 0, void 0, function () {
201
+ var errorObj;
202
+ return __generator(this, function (_a) {
203
+ if (!doc) {
204
+ return [2 /*return*/, null];
205
+ }
206
+ try {
207
+ return [2 /*return*/, (0, exports.serialize)(request, options, doc)];
208
+ }
209
+ catch (error) {
210
+ errorObj = error;
211
+ throw new errors_1.APIError({
212
+ error: errorObj,
213
+ status: 400,
214
+ title: "Error serializing ".concat(method, " response: ").concat(errorObj.message),
215
+ });
216
+ }
217
+ return [2 /*return*/];
218
218
  });
219
- }
219
+ }); };
220
+ exports.defaultResponseHandler = defaultResponseHandler;
package/dist/utils.js CHANGED
@@ -82,6 +82,7 @@ var __values = (this && this.__values) || function(o) {
82
82
  Object.defineProperty(exports, "__esModule", { value: true });
83
83
  exports.checkModelsStrict = exports.timeout = exports.isValidObjectId = void 0;
84
84
  var mongoose_1 = __importStar(require("mongoose"));
85
+ var errors_1 = require("./errors");
85
86
  var logger_1 = require("./logger");
86
87
  // A better version of mongoose's ObjectId.isValid,
87
88
  // which falsely will say any 12 character string is valid.
@@ -119,16 +120,25 @@ var checkModelsStrict = function (ignoredModels) {
119
120
  var model = models_1_1.value;
120
121
  var schema = mongoose_1.default.model(model).schema;
121
122
  if (((_b = schema.get("toObject")) === null || _b === void 0 ? void 0 : _b.virtuals) !== true) {
122
- throw new Error("Model ".concat(model, " toObject.virtuals not set to true"));
123
+ throw new errors_1.APIError({
124
+ status: 500,
125
+ title: "Model ".concat(model, " toObject.virtuals not set to true"),
126
+ });
123
127
  }
124
128
  if (((_c = schema.get("toJSON")) === null || _c === void 0 ? void 0 : _c.virtuals) !== true) {
125
- throw new Error("Model ".concat(model, " toJSON.virtuals not set to true"));
129
+ throw new errors_1.APIError({
130
+ status: 500,
131
+ title: "Model ".concat(model, " toJSON.virtuals not set to true"),
132
+ });
126
133
  }
127
134
  if (ignoredModels.includes(model)) {
128
135
  continue;
129
136
  }
130
137
  if (schema.get("strict") !== "throw") {
131
- throw new Error("Model ".concat(model, " is not set to strict mode."));
138
+ throw new errors_1.APIError({
139
+ status: 500,
140
+ title: "Model ".concat(model, " is not set to strict mode."),
141
+ });
132
142
  }
133
143
  }
134
144
  }
package/package.json CHANGED
@@ -104,5 +104,5 @@
104
104
  "updateSnapshot": "bun test --update-snapshots"
105
105
  },
106
106
  "types": "dist/index.d.ts",
107
- "version": "0.13.0"
107
+ "version": "0.13.2"
108
108
  }
package/src/api.test.ts CHANGED
@@ -576,7 +576,7 @@ describe("@terreno/api", () => {
576
576
  );
577
577
  server = supertest(app);
578
578
 
579
- const res = await server.post("/food").send({calories: 15, name: "Broccoli"}).expect(400);
579
+ const res = await server.post("/food").send({calories: 15, name: "Broccoli"}).expect(403);
580
580
  expect(res.body.title).toContain("cannot write fields");
581
581
  });
582
582
 
@@ -1324,7 +1324,7 @@ describe("@terreno/api", () => {
1324
1324
  );
1325
1325
  server = supertest(app);
1326
1326
 
1327
- const res = await server.post("/food").send({calories: 15, name: "Broccoli"}).expect(400);
1327
+ const res = await server.post("/food").send({calories: 15, name: "Broccoli"}).expect(403);
1328
1328
  expect(res.body.title).toContain("cannot write fields");
1329
1329
  });
1330
1330
 
package/src/api.ts CHANGED
@@ -528,6 +528,9 @@ function _buildModelRouter<T>(model: Model<T>, options: ModelRouterOptions<T>):
528
528
  try {
529
529
  body = transform<T>(options, req.body, "create", req.user);
530
530
  } catch (error: any) {
531
+ if (isAPIError(error)) {
532
+ throw error;
533
+ }
531
534
  throw new APIError({
532
535
  disableExternalErrorTracking: getDisableExternalErrorTracking(error),
533
536
  error,
@@ -827,6 +830,9 @@ function _buildModelRouter<T>(model: Model<T>, options: ModelRouterOptions<T>):
827
830
  try {
828
831
  body = transform<T>(options, req.body, "update", req.user);
829
832
  } catch (error: any) {
833
+ if (isAPIError(error)) {
834
+ throw error;
835
+ }
830
836
  throw new APIError({
831
837
  disableExternalErrorTracking: getDisableExternalErrorTracking(error),
832
838
  error,
@@ -1080,6 +1086,9 @@ function _buildModelRouter<T>(model: Model<T>, options: ModelRouterOptions<T>):
1080
1086
  try {
1081
1087
  body = transform<T>(options, body, "update", req.user) as Partial<T>;
1082
1088
  } catch (error: any) {
1089
+ if (isAPIError(error)) {
1090
+ throw error;
1091
+ }
1083
1092
  throw new APIError({
1084
1093
  disableExternalErrorTracking: getDisableExternalErrorTracking(error),
1085
1094
  error,
package/src/consentApp.ts CHANGED
@@ -6,7 +6,7 @@
6
6
  * endpoints for fetching pending consents and submitting responses.
7
7
  */
8
8
 
9
- import type express from "express";
9
+ import {type Application, Router} from "express";
10
10
  import {DateTime} from "luxon";
11
11
  import {asyncHandler, modelRouter} from "./api";
12
12
  import type {User} from "./auth";
@@ -47,7 +47,7 @@ export class ConsentApp implements TerrenoPlugin {
47
47
  this.options = options;
48
48
  }
49
49
 
50
- register(app: express.Application): void {
50
+ register(app: Application): void {
51
51
  const {auditTrail, resolveConsentForms, aiConfig} = this.options;
52
52
 
53
53
  // Admin CRUD for consent forms
@@ -192,7 +192,7 @@ export class ConsentApp implements TerrenoPlugin {
192
192
  );
193
193
 
194
194
  // User-facing consent endpoints
195
- const router = require("express").Router() as express.Router;
195
+ const router = Router();
196
196
 
197
197
  // GET /consents/pending - fetch pending consent forms for the current user
198
198
  router.get(