vanta-api 1.1.7 → 1.1.8

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 (2) hide show
  1. package/package.json +1 -1
  2. package/src/api-features.js +74 -69
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vanta-api",
3
- "version": "1.1.7",
3
+ "version": "1.1.8",
4
4
  "description": "Advanced API features and security configuration for Node.js/MongoDB.",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -33,8 +33,10 @@ export class ApiFeatures {
33
33
  filter() {
34
34
  // Parse and sanitize both query and manual filters
35
35
  const queryFilters = this._parseQueryFilters();
36
- const manual = this._sanitizeFilters(this.manualFilters);
37
- const merged = { ...queryFilters, ...manual };
36
+ const merged = this._sanitizeFilters({
37
+ ...queryFilters,
38
+ ...this.manualFilters,
39
+ });
38
40
  const safe = this._applySecurityFilters(merged);
39
41
 
40
42
  if (Object.keys(safe).length) {
@@ -117,7 +119,7 @@ export class ApiFeatures {
117
119
 
118
120
  // Apply lookups
119
121
  for (const opt of final) {
120
- const field = typeof opt === 'string' ? opt : opt.path;
122
+ const field = typeof opt === "string" ? opt : opt.path;
121
123
  const proj =
122
124
  typeof opt === "object" && opt.select
123
125
  ? opt.select.split(" ").reduce((a, f) => {
@@ -215,87 +217,90 @@ export class ApiFeatures {
215
217
  });
216
218
  }
217
219
 
218
- _parseQueryFilters() {
219
- const obj = { ...this.query };
220
- // پاک کردن پارامترهای سیستماتیک
221
- ["page", "limit", "sort", "fields", "populate"].forEach(k => delete obj[k]);
222
-
223
- const out = {};
224
-
225
- for (const [rawKey, rawVal] of Object.entries(obj)) {
226
- if (typeof rawVal === 'object' && !Array.isArray(rawVal)) {
227
- out[rawKey] = {};
228
- for (let [op, val] of Object.entries(rawVal)) {
229
- const cleanOp = op.replace(/^\$/, '');
230
- if (securityConfig.allowedOperators.includes(cleanOp)) {
231
- const v = /^[0-9]+$/.test(val) ? parseInt(val, 10) : val;
232
- out[rawKey][`$${cleanOp}`] = v;
220
+ _parseQueryFilters() {
221
+ const obj = { ...this.query };
222
+ // پاک کردن پارامترهای سیستماتیک
223
+ ["page", "limit", "sort", "fields", "populate"].forEach(
224
+ (k) => delete obj[k]
225
+ );
226
+
227
+ const out = {};
228
+
229
+ for (const [rawKey, rawVal] of Object.entries(obj)) {
230
+ if (typeof rawVal === "object" && !Array.isArray(rawVal)) {
231
+ out[rawKey] = {};
232
+ for (let [op, val] of Object.entries(rawVal)) {
233
+ const cleanOp = op.replace(/^\$/, "");
234
+ if (securityConfig.allowedOperators.includes(cleanOp)) {
235
+ const v = /^[0-9]+$/.test(val) ? parseInt(val, 10) : val;
236
+ out[rawKey][`$${cleanOp}`] = v;
237
+ }
238
+ }
239
+ } else if (/^\w+\[\$?\w+\]$/.test(rawKey)) {
240
+ const [, field, op] = rawKey.match(/^(\w+)\[\$?(\w+)\]$/);
241
+ if (securityConfig.allowedOperators.includes(op)) {
242
+ const v = /^[0-9]+$/.test(rawVal) ? parseInt(rawVal, 10) : rawVal;
243
+ out[field] = { [`$${op}`]: v };
233
244
  }
234
- }
235
- }
236
- else if (/^\w+\[\$?\w+\]$/.test(rawKey)) {
237
- const [, field, op] = rawKey.match(/^(\w+)\[\$?(\w+)\]$/);
238
- if (securityConfig.allowedOperators.includes(op)) {
239
- const v = /^[0-9]+$/.test(rawVal) ? parseInt(rawVal, 10) : rawVal;
240
- out[field] = { [`$${op}`]: v };
241
- }
242
- }
243
- else {
244
- if (typeof rawVal === "string" && rawVal.includes(",")) {
245
- out[rawKey] = rawVal.split(",");
246
245
  } else {
247
- out[rawKey] = rawVal;
246
+ if (typeof rawVal === "string" && rawVal.includes(",")) {
247
+ out[rawKey] = rawVal.split(",");
248
+ } else {
249
+ out[rawKey] = rawVal;
250
+ }
248
251
  }
249
252
  }
250
- }
251
-
252
- return out;
253
- }
254
253
 
254
+ return out;
255
+ }
255
256
 
256
- _sanitizeFilters(filters) {
257
- return JSON.parse(JSON.stringify(filters), (key, val) => {
258
- // اگر val شیئی حاوی $eq یا eq باشد و آن فیلد ObjectId معتبر باشد
257
+ _sanitizeFilters(filters) {
258
+ const resultObj = {};
259
+ const resualt = Object.entries(filters).map((el) => {
260
+ const [keyObj, val] = el;
259
261
  if (
260
- typeof val === 'object' &&
261
- val !== null &&
262
- (this.#isStrictObjectId(val['$eq']) || this.#isStrictObjectId(val['eq']))
262
+ typeof val === "object" &&
263
+ (this.#isStrictObjectId(val["$eq"]) ||
264
+ this.#isStrictObjectId(val["eq"]))
263
265
  ) {
264
266
  const newVal = { ...val };
265
- if (this.#isStrictObjectId(val['$eq'])) {
266
- newVal['$eq'] = new mongoose.Types.ObjectId(val['$eq']);
267
+ if (this.#isStrictObjectId(val["$eq"])) {
268
+ newVal["$eq"] = new ObjectId(val["$eq"]);
267
269
  }
268
- if (this.#isStrictObjectId(val['eq'])) {
269
- newVal['eq'] = new mongoose.Types.ObjectId(val['eq']);
270
+ if (this.#isStrictObjectId(val["eq"])) {
271
+ newVal["eq"] = new ObjectId(val["eq"]);
270
272
  }
271
- return newVal;
273
+ resultObj[keyObj] = newVal;
274
+ return;
272
275
  }
273
-
274
- // تبدیل true/false
275
- if (val === "true") return true;
276
- if (val === "false") return false;
277
-
278
- // تبدیل عدد صحیح
279
- if (typeof val === 'string' && /^[0-9]+$/.test(val)) return parseInt(val, 10);
280
-
281
- // اگر val یک رشته است و ObjectId معتبر باشد، به ObjectId تبدیل شود
282
- if (
283
- typeof val === 'string' &&
284
- this.#isStrictObjectId(val)
285
- ) {
286
- return new mongoose.Types.ObjectId(val);
276
+ if (val === "true") {
277
+ resultObj[keyObj] = true;
278
+ return;
287
279
  }
288
-
289
- return val;
280
+ if (val === "false") {
281
+ resultObj[keyObj] = false;
282
+ return;
283
+ }
284
+ if (typeof val === "string" && /^[0-9]+$/.test(val)) {
285
+ resultObj[keyObj] = parseInt(val, 10);
286
+ return;
287
+ }
288
+ if (typeof val === "string" && this.#isStrictObjectId(val)) {
289
+ resultObj[keyObj] = new ObjectId(val);
290
+ return;
291
+ }
292
+ resultObj[keyObj] = val;
290
293
  });
294
+ return resultObj;
295
+ }
296
+
297
+ #isStrictObjectId(id) {
298
+ return (
299
+ typeof id === "string" &&
300
+ mongoose.Types.ObjectId.isValid(id) &&
301
+ new mongoose.Types.ObjectId(id).toString() === id
302
+ );
291
303
  }
292
- #isStrictObjectId(id) {
293
- return (
294
- typeof id === 'string' &&
295
- mongoose.Types.ObjectId.isValid(id) &&
296
- (new mongoose.Types.ObjectId(id)).toString() === id
297
- );
298
- }
299
304
 
300
305
  _applySecurityFilters(filters) {
301
306
  let res = { ...filters };