@r5v/mongoose-paginate 1.0.13 → 1.0.15

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 (104) hide show
  1. package/README.md +210 -17
  2. package/dist/{types/aggregationPagingQuery.d.ts → aggregationPagingQuery.d.ts} +6 -7
  3. package/dist/aggregationPagingQuery.d.ts.map +1 -0
  4. package/dist/aggregationPagingQuery.js +19 -4
  5. package/dist/index.d.ts +9 -0
  6. package/dist/index.d.ts.map +1 -0
  7. package/dist/index.js +18 -1
  8. package/dist/{types/pagingQuery.d.ts → pagingQuery.d.ts} +6 -7
  9. package/dist/pagingQuery.d.ts.map +1 -0
  10. package/dist/pagingQuery.js +8 -3
  11. package/dist/tests/aggregationPagingQuery.spec.d.ts +2 -0
  12. package/dist/tests/aggregationPagingQuery.spec.d.ts.map +1 -0
  13. package/dist/tests/aggregationPagingQuery.spec.js +419 -0
  14. package/dist/tests/buildPopulateFromString.spec.d.ts +2 -0
  15. package/dist/tests/buildPopulateFromString.spec.d.ts.map +1 -0
  16. package/dist/tests/buildPopulateFromString.spec.js +223 -0
  17. package/dist/tests/dotNotation.spec.d.ts.map +1 -0
  18. package/dist/tests/findProtectedPaths.spec.d.ts.map +1 -0
  19. package/dist/tests/findProtectedPaths.spec.js +128 -6
  20. package/dist/tests/getPathsWithRef.spec.d.ts.map +1 -0
  21. package/dist/tests/getPathsWithRef.spec.js +92 -18
  22. package/dist/tests/getPropertyFromDotNotation.spec.d.ts.map +1 -0
  23. package/dist/tests/insertPopulate.spec.d.ts.map +1 -0
  24. package/dist/tests/isJsonString.spec.d.ts +2 -0
  25. package/dist/tests/isJsonString.spec.d.ts.map +1 -0
  26. package/dist/tests/isJsonString.spec.js +116 -0
  27. package/dist/tests/isValidDateString.spec.d.ts +2 -0
  28. package/dist/tests/isValidDateString.spec.d.ts.map +1 -0
  29. package/dist/tests/isValidDateString.spec.js +116 -0
  30. package/dist/tests/pagingQuery.spec.d.ts.map +1 -0
  31. package/dist/tests/pagingQuery.spec.js +238 -13
  32. package/dist/tests/parseParams.spec.d.ts +2 -0
  33. package/dist/tests/parseParams.spec.d.ts.map +1 -0
  34. package/dist/tests/parseParams.spec.js +212 -0
  35. package/dist/tests/parseSortString.spec.d.ts.map +1 -0
  36. package/dist/tests/schemaTraversal.spec.d.ts +2 -0
  37. package/dist/tests/schemaTraversal.spec.d.ts.map +1 -0
  38. package/dist/tests/schemaTraversal.spec.js +139 -0
  39. package/dist/types/index.d.ts +72 -3
  40. package/dist/types/index.d.ts.map +1 -1
  41. package/dist/utils/buildPopulateFromString.d.ts +8 -0
  42. package/dist/utils/buildPopulateFromString.d.ts.map +1 -0
  43. package/dist/utils/buildPopulateFromString.js +54 -49
  44. package/dist/utils/dotNotation.d.ts +11 -0
  45. package/dist/utils/dotNotation.d.ts.map +1 -0
  46. package/dist/utils/dotNotation.js +2 -2
  47. package/dist/utils/findKeyWithValue.d.ts.map +1 -0
  48. package/dist/utils/findProtectedPaths.d.ts.map +1 -0
  49. package/dist/utils/findProtectedPaths.js +14 -56
  50. package/dist/utils/getPathsWithRef.d.ts +5 -0
  51. package/dist/utils/getPathsWithRef.d.ts.map +1 -0
  52. package/dist/utils/getPathsWithRef.js +59 -96
  53. package/dist/utils/index.d.ts +9 -0
  54. package/dist/utils/index.d.ts.map +1 -0
  55. package/dist/utils/index.js +33 -0
  56. package/dist/utils/isJsonString.d.ts.map +1 -0
  57. package/dist/utils/isValidDateString.d.ts.map +1 -0
  58. package/dist/utils/parseParams.d.ts.map +1 -0
  59. package/dist/utils/parseParams.js +14 -12
  60. package/dist/utils/parsePopulateQuery.d.ts.map +1 -0
  61. package/dist/utils/parsePopulateQuery.js +1 -1
  62. package/dist/{types/utils → utils}/parseSortString.d.ts +1 -1
  63. package/dist/utils/parseSortString.d.ts.map +1 -0
  64. package/dist/utils/schemaTraversal.d.ts +9 -0
  65. package/dist/utils/schemaTraversal.d.ts.map +1 -0
  66. package/dist/utils/schemaTraversal.js +37 -0
  67. package/package.json +23 -2
  68. package/dist/types/aggregationPagingQuery.d.ts.map +0 -1
  69. package/dist/types/pagingQuery.d.ts.map +0 -1
  70. package/dist/types/tests/dotNotation.spec.d.ts.map +0 -1
  71. package/dist/types/tests/findProtectedPaths.spec.d.ts.map +0 -1
  72. package/dist/types/tests/getPathsWithRef.spec.d.ts.map +0 -1
  73. package/dist/types/tests/getPropertyFromDotNotation.spec.d.ts.map +0 -1
  74. package/dist/types/tests/insertPopulate.spec.d.ts.map +0 -1
  75. package/dist/types/tests/pagingQuery.spec.d.ts.map +0 -1
  76. package/dist/types/tests/parseSortString.spec.d.ts.map +0 -1
  77. package/dist/types/types/index.d.ts +0 -61
  78. package/dist/types/types/index.d.ts.map +0 -1
  79. package/dist/types/utils/buildPopulateFromString.d.ts +0 -8
  80. package/dist/types/utils/buildPopulateFromString.d.ts.map +0 -1
  81. package/dist/types/utils/dotNotation.d.ts +0 -11
  82. package/dist/types/utils/dotNotation.d.ts.map +0 -1
  83. package/dist/types/utils/findKeyWithValue.d.ts.map +0 -1
  84. package/dist/types/utils/findProtectedPaths.d.ts.map +0 -1
  85. package/dist/types/utils/getPathsWithRef.d.ts +0 -5
  86. package/dist/types/utils/getPathsWithRef.d.ts.map +0 -1
  87. package/dist/types/utils/isJsonString.d.ts.map +0 -1
  88. package/dist/types/utils/isValidDateString.d.ts.map +0 -1
  89. package/dist/types/utils/parseParams.d.ts.map +0 -1
  90. package/dist/types/utils/parsePopulateQuery.d.ts.map +0 -1
  91. package/dist/types/utils/parseSortString.d.ts.map +0 -1
  92. /package/dist/{types/tests → tests}/dotNotation.spec.d.ts +0 -0
  93. /package/dist/{types/tests → tests}/findProtectedPaths.spec.d.ts +0 -0
  94. /package/dist/{types/tests → tests}/getPathsWithRef.spec.d.ts +0 -0
  95. /package/dist/{types/tests → tests}/getPropertyFromDotNotation.spec.d.ts +0 -0
  96. /package/dist/{types/tests → tests}/insertPopulate.spec.d.ts +0 -0
  97. /package/dist/{types/tests → tests}/pagingQuery.spec.d.ts +0 -0
  98. /package/dist/{types/tests → tests}/parseSortString.spec.d.ts +0 -0
  99. /package/dist/{types/utils → utils}/findKeyWithValue.d.ts +0 -0
  100. /package/dist/{types/utils → utils}/findProtectedPaths.d.ts +0 -0
  101. /package/dist/{types/utils → utils}/isJsonString.d.ts +0 -0
  102. /package/dist/{types/utils → utils}/isValidDateString.d.ts +0 -0
  103. /package/dist/{types/utils → utils}/parseParams.d.ts +0 -0
  104. /package/dist/{types/utils → utils}/parsePopulateQuery.d.ts +0 -0
package/README.md CHANGED
@@ -82,7 +82,49 @@ PagingQuery(Express.Request, mongoose.Model, options )
82
82
  | $sort | space separated mongoose sort string \n name -value | inserts sort into the query. In aggregation queries this will insert a sort after the existing pipeline | PagingQuery, AggregationPagingQuery |
83
83
  | $preSort | comma separated dot notation string \n books,-_id \| -_id,-name,address.-address1 | in aggregate queries this will insert a sort object after the initial match in the pipeline. | AggregationPagingQuery |
84
84
  | $count | comma separated dot notation string | this comma separate string will traverse the results and insert a new field with the $size of the selected key/value . this new name field will be appended with `_count` | AggregationPagingQuery |
85
- | $postFilter | Any Json Object | creates and additional filter and in aggregation queries is appended to the end of the pipeline | AggregationPagingQuery |
85
+ | $postFilter | Any Json Object | creates and additional filter and in aggregation queries is appended to the end of the pipeline | AggregationPagingQuery |
86
+
87
+ #### Example URLs
88
+
89
+ ```bash
90
+ # Basic pagination
91
+ GET /api/books?$limit=10&$skip=20
92
+
93
+ # Filter by field (JSON format)
94
+ GET /api/books?$filter={"genre":"Horror"}
95
+
96
+ # Filter with operators
97
+ GET /api/books?$filter={"price.amount":{"$gte":20,"$lte":50}}
98
+
99
+ # Sort (space-separated: field direction)
100
+ GET /api/books?$sort=price.amount%20desc
101
+ GET /api/books?$sort=title%20asc,createdAt%20desc
102
+
103
+ # Select specific fields
104
+ GET /api/books?$select=title,isbn,price
105
+
106
+ # Populate references
107
+ GET /api/books?$populate=author,publisher
108
+
109
+ # Deep populate with field selection
110
+ GET /api/books?$populate=author[firstName,lastName],publisher[name]
111
+
112
+ # Aggregation with preSort (sorts before pipeline)
113
+ GET /api/books?$preSort=createdAt%20desc
114
+
115
+ # Aggregation with postFilter (filters on computed fields)
116
+ GET /api/books?$postFilter={"priceCategory":"expensive"}
117
+
118
+ # Count array fields
119
+ GET /api/books?$count=authors,tags
120
+ # Result includes: authors_count, tags_count
121
+
122
+ # Disable paging (returns array instead of paged object)
123
+ GET /api/books?$paging=false
124
+
125
+ # Lean query (returns plain objects)
126
+ GET /api/books?$lean=true
127
+ ```
86
128
 
87
129
  #### Options
88
130
 
@@ -97,12 +139,143 @@ PagingQuery(Express.Request, mongoose.Model, options )
97
139
  | staticPostFilter | Mongo Filter Object | create a filter on the pipeline that is added after all the pipeline stages. this cannot be overwritten by params | AggregationPagingQuery | | {} |
98
140
  | staticFilter | Mongo Filter Object | create a filter on the pipeline that is added before all the pipeline stages. on find requests, this is added to the filter object. this cannot be overwritten by params | AggregationPagingQuery | | {} |
99
141
  | pipeline | MongoPipelineStage[] | pipeline request object. if the first item in pipeline stage is a $match or another required first stage operator. it will be placed before all other modifiers | AggregationPagingQuery | true | [] |
100
- | removeProtected \(REMOVED\) | boolean | auto remove protected (select: false) for root Model | AggregationPagingQuery | | false |
142
+ | removeProtected | boolean | **EXPERIMENTAL** - auto remove protected fields (those with `select: false` in schema) from aggregation results. Use with caution and test thoroughly in your environment | AggregationPagingQuery | | false |
101
143
 
102
144
  ## Utilities
103
- |Name| Description |
104
- |:---|:-------------------------------------------------------------------------------------------------|
105
- |buildPopulate| creates a populate object from dot notation string "author\[name\].books,user.publisher\[name\]" |
145
+
146
+ All utilities can be imported directly from the main package or from the `/utils` subpath:
147
+
148
+ ```typescript
149
+ // Direct import
150
+ import { buildPopulate, parseSortString, getPropertyFromDotNotation } from '@r5v/mongoose-paginate'
151
+
152
+ // Subpath import (all utilities)
153
+ import * as utils from '@r5v/mongoose-paginate/utils'
154
+ ```
155
+
156
+ ### Populate Utilities
157
+
158
+ | Name | Signature | Description |
159
+ |:-----|:----------|:------------|
160
+ | `buildPopulate` | `(pathString: string) => PopulateItem[]` | Creates a Mongoose populate object from dot notation string. Supports deep population with field selection using bracket notation: `"author[name].books[title],publisher[name]"` |
161
+
162
+ ```typescript
163
+ import { buildPopulate } from '@r5v/mongoose-paginate'
164
+
165
+ const populate = buildPopulate('author[name,email].books[title],publisher')
166
+ // Result: [
167
+ // { path: 'author', select: 'name email', populate: [{ path: 'books', select: 'title' }] },
168
+ // { path: 'publisher' }
169
+ // ]
170
+ ```
171
+
172
+ ### Sort Utilities
173
+
174
+ | Name | Signature | Description |
175
+ |:-----|:----------|:------------|
176
+ | `parseSortString` | `(sortString: string) => [string, SortOrder][]` | Parses a sort string into Mongoose sort array format. Example: `"name -createdAt"` → `[["name", 1], ["createdAt", -1]]` |
177
+ | `parseAggregateSortString` | `(sortString: string) => {[key: string]: SortOrder}` | Parses a sort string into aggregation pipeline sort object. Example: `"name -createdAt"` → `{ name: 1, createdAt: -1 }` |
178
+
179
+ ```typescript
180
+ import { parseSortString, parseAggregateSortString } from '@r5v/mongoose-paginate'
181
+
182
+ const sortArray = parseSortString('name 1, createdAt -1')
183
+ // Result: [["name", 1], ["createdAt", -1]]
184
+
185
+ const sortObj = parseAggregateSortString('name 1, createdAt -1')
186
+ // Result: { name: 1, createdAt: -1 }
187
+ ```
188
+
189
+ ### Dot Notation Utilities
190
+
191
+ | Name | Signature | Description |
192
+ |:-----|:----------|:------------|
193
+ | `getPropertyFromDotNotation` | `(obj: any, path: string) => any` | Gets a nested property value using dot notation path |
194
+ | `dotNotationToObject` | `(dotString: string, value?: unknown) => object` | Converts a dot notation string to a nested object |
195
+ | `createObjectFromDotNotation` | `(dotNotationMap: {[key: string]: any}) => object` | Creates a nested object from multiple dot notation key-value pairs |
196
+ | `setPropertyFromDotNotation` | `(obj: Record<string, unknown>, path: string, value: unknown) => object` | Sets a nested property value (mutates original object) |
197
+ | `setPropertiesFromDotNotation` | `(obj: Record<string, unknown>, dotNotationMap: Record<string, unknown>) => object` | Sets multiple nested properties (mutates original object) |
198
+ | `setPropertyFromDotNotationImmutable` | `(obj: Record<string, unknown>, path: string, value: unknown) => object` | Sets a nested property value (returns new object) |
199
+ | `setPropertiesFromDotNotationImmutable` | `(obj: Record<string, unknown>, dotNotationMap: Record<string, unknown>) => object` | Sets multiple nested properties (returns new object) |
200
+
201
+ ```typescript
202
+ import {
203
+ getPropertyFromDotNotation,
204
+ dotNotationToObject,
205
+ setPropertyFromDotNotation
206
+ } from '@r5v/mongoose-paginate'
207
+
208
+ // Get nested value
209
+ const user = { profile: { name: 'John', address: { city: 'NYC' } } }
210
+ getPropertyFromDotNotation(user, 'profile.address.city') // 'NYC'
211
+
212
+ // Create nested object from dot notation
213
+ dotNotationToObject('user.profile.name', 'John')
214
+ // Result: { user: { profile: { name: 'John' } } }
215
+
216
+ // Set nested property
217
+ const obj = { user: { name: 'John' } }
218
+ setPropertyFromDotNotation(obj, 'user.email', 'john@example.com')
219
+ // Result: { user: { name: 'John', email: 'john@example.com' } }
220
+ ```
221
+
222
+ ### Validation Utilities
223
+
224
+ | Name | Signature | Description |
225
+ |:-----|:----------|:------------|
226
+ | `isJsonString` | `(str: string) => boolean` | Checks if a string is valid JSON |
227
+ | `isValidDateString` | `(value: string) => boolean` | Checks if a string is a valid ISO 8601 date format |
228
+
229
+ ```typescript
230
+ import { isJsonString, isValidDateString } from '@r5v/mongoose-paginate'
231
+
232
+ isJsonString('{"name": "John"}') // true
233
+ isJsonString('invalid') // false
234
+
235
+ isValidDateString('2024-01-15') // true
236
+ isValidDateString('2024-01-15T10:30:00.000Z') // true
237
+ isValidDateString('invalid-date') // false
238
+ ```
239
+
240
+ ### Schema Traversal Utilities
241
+
242
+ | Name | Signature | Description |
243
+ |:-----|:----------|:------------|
244
+ | `buildFullPath` | `(parentPath: string, childPath: string) => string` | Builds a dot-notation path from parent and child segments |
245
+ | `traverseSchemaObject` | `(obj: any, callback: (obj, path) => void, currentPath?: string) => void` | Recursively traverses a Mongoose schema object, calling callback for each node |
246
+
247
+ ```typescript
248
+ import { buildFullPath, traverseSchemaObject } from '@r5v/mongoose-paginate'
249
+
250
+ // Build dot-notation paths
251
+ buildFullPath('user', 'profile') // 'user.profile'
252
+ buildFullPath('', 'name') // 'name'
253
+ buildFullPath('parent', '') // 'parent'
254
+
255
+ // Traverse schema objects
256
+ traverseSchemaObject(schemaType, (obj, currentPath) => {
257
+ if (obj.options?.ref) {
258
+ console.log(`Found ref at ${currentPath}: ${obj.options.ref}`)
259
+ }
260
+ })
261
+ ```
262
+
263
+ ## Types
264
+
265
+ All TypeScript types are exported and can be imported:
266
+
267
+ ```typescript
268
+ import type {
269
+ PagingQueryOptions,
270
+ AggregateQueryOptions,
271
+ QueryParameters,
272
+ PagingQueryParsedRequestParams,
273
+ AggregateQueryParsedRequestParams
274
+ } from '@r5v/mongoose-paginate'
275
+
276
+ // Or from the types subpath
277
+ import type * from '@r5v/mongoose-paginate/types'
278
+ ```
106
279
 
107
280
  ## Build
108
281
 
@@ -123,18 +296,38 @@ $ yarn run start
123
296
 
124
297
  #### Aggregations Order of operations
125
298
 
126
- 1. staticFilter \| \$filter \| $match (if first item in pipeline)
127
- 2. \$preSort
128
- 3. apply pipeline
129
- 4. \$select \| project
130
- 5. remove protected fields
131
- 6. \$count
132
- 7. \$sort
133
- 8. apply options
134
-
135
-
136
- ## NOTES
137
- 1. removeProtected removed from aggregation query due to inconsistent results after publication
299
+ 1. First pipeline stage (if `$search`, `$searchMeta`, or `$geoNear`)
300
+ 2. `$match` combining: `staticFilter` + `$filter` (if `enableFilter`) + first pipeline `$match` stage
301
+ 3. `$preSort` (if `enablePreSort` is true)
302
+ 4. Remaining pipeline stages
303
+ 5. `$select` / `$project`
304
+ 6. `$count` (adds `_count` fields for array sizes)
305
+ 7. `$postFilter` combining: `staticPostFilter` + `$postFilter` (if `enablePostFilter`)
306
+ 8. `$sort` (final sorting, can sort on computed fields)
307
+ 9. Apply pagination (`$skip`, `$limit`) and options
308
+
309
+
310
+ ### 1.0.15
311
+ - **Re-enabled `removeProtected` option** for `AggregationPagingQuery` (EXPERIMENTAL) - Automatically excludes fields marked with `select: false` in your schema (e.g., passwords, secret keys) from aggregation results. Use with caution and test thoroughly in your environment.
312
+ - Uses refactored `findProtectedPaths` utility which safely traverses schemas without JSON serialization
313
+
314
+ ### 1.0.14
315
+ - Added proper TypeScript package exports with subpaths (`/utils`, `/types`)
316
+ - Exported all utility functions (dot notation, sort parsing, validation)
317
+ - Exported all TypeScript types
318
+ - **Fixed Express Request type compatibility** - Added `RequestLike` interface to allow custom request types without index signature issues
319
+ - Fixed JSON.parse crash on malformed `$filter`/`$postFilter` parameters
320
+ - Fixed regex global flag bug in populate parsing
321
+ - Added input validation to constructors
322
+ - Improved type safety (reduced `any` usage)
323
+ - Removed deprecated `$includes` parameter
324
+ - Replaced `JSON.parse(JSON.stringify())` with `structuredClone()`
325
+ - Fixed inconsistent equality operator (`==` to `===`)
326
+ - **Fixed empty pipeline crash** - `AggregationPagingQuery.initQuery` no longer crashes when pipeline is empty
327
+ - **Refactored `buildPopulate`** - Extracted inner functions to module level for better testability
328
+ - **Refactored `findProtectedPaths`** - Removed JSON.stringify that could fail on circular references
329
+ - **Refactored `getPathsWithRef`** - Simplified input handling, removed brittle string parsing
330
+ - **Added `schemaTraversal` utility** - Shared `buildFullPath` and `traverseSchemaObject` functions for schema traversal
138
331
 
139
332
  ### 1.0.13
140
333
  - Fix issue where disablePaging was not working on aggregation query
@@ -1,25 +1,24 @@
1
1
  import { Model, Aggregate } from "mongoose";
2
- import type { Request } from "express";
3
- import type { AggregateQueryOptions, QueryParameters, AggregateQueryParsedRequestParams } from './types';
2
+ import type { AggregateQueryOptions, AggregateQueryParsedRequestParams, RequestLike } from './types';
4
3
  export declare class AggregationPagingQuery {
5
4
  params: AggregateQueryParsedRequestParams;
6
5
  options: AggregateQueryOptions;
7
- query: Aggregate<Array<any>> | undefined;
6
+ query: Aggregate<unknown[]> | undefined;
8
7
  protectedPaths: string[];
9
8
  model: Model<any>;
10
- constructor(req: Request<{}, any, any, Partial<QueryParameters>>, model: Model<any>, options: AggregateQueryOptions);
9
+ constructor(req: RequestLike, model: Model<any>, options: AggregateQueryOptions);
11
10
  findProtectedPaths: (model: Model<any>) => string[];
12
11
  parseParams: (defaultParams: import("./types").PagingQueryParsedRequestParams | AggregateQueryParsedRequestParams, params: import("qs").ParsedQs, isAggregate?: boolean) => import("./types").PagingQueryParsedRequestParams | AggregateQueryParsedRequestParams;
13
12
  isValidDateString: (value: string) => boolean;
14
13
  isJsonString: (str: string) => boolean;
15
14
  parseSortString: (sortString: string) => [string, import("mongoose").SortOrder][];
16
- parseAggregateSortString: (sortString: any) => {
15
+ parseAggregateSortString: (sortString: string) => {
17
16
  [key: string]: import("mongoose").SortOrder;
18
17
  };
19
18
  createCounts: () => void;
20
19
  initQuery: () => Promise<void>;
21
- typeCastObject: (strOrObj: any) => any;
20
+ typeCastObject: <T>(strOrObj: T) => T;
22
21
  private removeProtectedFields;
23
- exec: () => Promise<any>;
22
+ exec: () => Promise<unknown>;
24
23
  }
25
24
  //# sourceMappingURL=aggregationPagingQuery.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"aggregationPagingQuery.d.ts","sourceRoot":"","sources":["../src/aggregationPagingQuery.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,KAAK,EACL,SAAS,EAKZ,MAAM,UAAU,CAAC;AAClB,OAAO,KAAK,EAAC,qBAAqB,EAAE,iCAAiC,EAAE,WAAW,EAAC,MAAM,SAAS,CAAA;AAQlG,qBAAa,sBAAsB;IAC/B,MAAM,EAAG,iCAAiC,CAYzC;IACD,OAAO,EAAE,qBAAqB,CAM7B;IACD,KAAK,EAAE,SAAS,CAAC,OAAO,EAAE,CAAC,GAAG,SAAS,CAAA;IACvC,cAAc,EAAE,MAAM,EAAE,CAAK;IAC7B,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,CAAA;gBACL,GAAG,EAAE,WAAW,EAAE,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,EAAE,OAAO,EAAE,qBAAqB;IAoB/E,kBAAkB,kCAAqB;IACvC,WAAW,sPAAc;IACzB,iBAAiB,6BAAoB;IACrC,YAAY,2BAAe;IAC3B,eAAe,mEAAkB;IACjC,wBAAwB;;MAA2B;IACnD,YAAY,aAOX;IACD,SAAS,sBAsDR;IACD,cAAc,GAAI,CAAC,EAAE,UAAU,CAAC,KAAG,CAAC,CAmCnC;IAED,OAAO,CAAC,qBAAqB,CAS5B;IACD,IAAI,yBAgCH;CAEJ"}
@@ -36,7 +36,6 @@ class AggregationPagingQuery {
36
36
  $sort: {},
37
37
  $paging: true,
38
38
  $populate: [],
39
- $includes: [], // to be removed
40
39
  $select: "",
41
40
  $count: [],
42
41
  $postFilter: {},
@@ -46,6 +45,7 @@ class AggregationPagingQuery {
46
45
  enableFilter: false,
47
46
  enablePostFilter: false,
48
47
  enablePreSort: true,
48
+ removeProtected: false,
49
49
  pipeline: []
50
50
  };
51
51
  this.protectedPaths = [];
@@ -66,7 +66,7 @@ class AggregationPagingQuery {
66
66
  };
67
67
  this.initQuery = () => __awaiter(this, void 0, void 0, function* () {
68
68
  const { $filter, $sort, $preSort, $select, $count, $postFilter } = this.params;
69
- const _a = this.options, { enableFilter, enablePreSort, enablePostFilter, staticFilter, staticPostFilter, pipeline } = _a, options = __rest(_a, ["enableFilter", "enablePreSort", "enablePostFilter", "staticFilter", "staticPostFilter", "pipeline"]);
69
+ const _a = this.options, { enableFilter, enablePreSort, enablePostFilter, staticFilter, staticPostFilter, removeProtected, pipeline } = _a, options = __rest(_a, ["enableFilter", "enablePreSort", "enablePostFilter", "staticFilter", "staticPostFilter", "removeProtected", "pipeline"]);
70
70
  this.query = this.model.aggregate();
71
71
  const [p1, ...pipes] = pipeline;
72
72
  const filterObj = { $and: [Object.assign({}, staticFilter)] };
@@ -84,7 +84,7 @@ class AggregationPagingQuery {
84
84
  }
85
85
  const typeCastFilter = this.typeCastObject(filterObj);
86
86
  const typeCastPostFilter = this.typeCastObject(postFilterObj);
87
- if (Object.keys(p1).some(k => ["$search", "$searchMeta", "$geoNear"].includes(k))) {
87
+ if (p1 && Object.keys(p1).some(k => ["$search", "$searchMeta", "$geoNear"].includes(k))) {
88
88
  this.query.append(p1);
89
89
  firstObj = true;
90
90
  }
@@ -94,13 +94,16 @@ class AggregationPagingQuery {
94
94
  if (this.options.enablePreSort && Object.keys($preSort).length) {
95
95
  this.query.sort($preSort);
96
96
  }
97
- if (!firstObj) {
97
+ if (!firstObj && p1) {
98
98
  this.query.append(p1);
99
99
  }
100
100
  pipes.forEach(item => { var _a; return (_a = this.query) === null || _a === void 0 ? void 0 : _a.append(item); });
101
101
  if ($select) {
102
102
  this.query.project($select);
103
103
  }
104
+ if (removeProtected) {
105
+ this.removeProtectedFields();
106
+ }
104
107
  if ($count.length) {
105
108
  this.createCounts();
106
109
  }
@@ -180,9 +183,21 @@ class AggregationPagingQuery {
180
183
  const req = yield this.query.exec();
181
184
  return req[0];
182
185
  });
186
+ if (!req || typeof req.query !== 'object') {
187
+ throw new Error('Invalid request object: must have a query property');
188
+ }
189
+ if (!model || typeof model.aggregate !== 'function') {
190
+ throw new Error('Invalid model: must be a Mongoose model');
191
+ }
192
+ if (!options || !Array.isArray(options.pipeline)) {
193
+ throw new Error('Invalid options: pipeline must be an array');
194
+ }
183
195
  this.options = Object.assign(Object.assign({}, this.options), options);
184
196
  this.model = model;
185
197
  this.params = this.parseParams(this.params, req.query, true);
198
+ if (this.options.removeProtected) {
199
+ this.protectedPaths = this.findProtectedPaths(model);
200
+ }
186
201
  this.initQuery();
187
202
  }
188
203
  }
@@ -0,0 +1,9 @@
1
+ export { PagingQuery } from './pagingQuery';
2
+ export { AggregationPagingQuery } from './aggregationPagingQuery';
3
+ export type { RequestLike, QueryParameters, StandardParsedRequestParams, PagingQueryParsedRequestParams, AggregateQueryParsedRequestParams, StandardQueryOptions, PagingQueryOptions, AggregateQueryOptions } from './types';
4
+ export { buildPopulate } from './utils/buildPopulateFromString';
5
+ export { parseSortString, parseAggregateSortString } from './utils/parseSortString';
6
+ export { getPropertyFromDotNotation, dotNotationToObject, createObjectFromDotNotation, setPropertyFromDotNotation, setPropertiesFromDotNotation, setPropertyFromDotNotationImmutable, setPropertiesFromDotNotationImmutable } from './utils/dotNotation';
7
+ export { isJsonString } from './utils/isJsonString';
8
+ export { isValidDateString } from './utils/isValidDateString';
9
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAC3C,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAA;AAGjE,YAAY,EACR,WAAW,EACX,eAAe,EACf,2BAA2B,EAC3B,8BAA8B,EAC9B,iCAAiC,EACjC,oBAAoB,EACpB,kBAAkB,EAClB,qBAAqB,EACxB,MAAM,SAAS,CAAA;AAGhB,OAAO,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAA;AAC/D,OAAO,EAAE,eAAe,EAAE,wBAAwB,EAAE,MAAM,yBAAyB,CAAA;AACnF,OAAO,EACH,0BAA0B,EAC1B,mBAAmB,EACnB,2BAA2B,EAC3B,0BAA0B,EAC1B,4BAA4B,EAC5B,mCAAmC,EACnC,qCAAqC,EACxC,MAAM,qBAAqB,CAAA;AAC5B,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAA;AACnD,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAA"}
package/dist/index.js CHANGED
@@ -1,9 +1,26 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.buildPopulate = exports.AggregationPagingQuery = exports.PagingQuery = void 0;
3
+ exports.isValidDateString = exports.isJsonString = exports.setPropertiesFromDotNotationImmutable = exports.setPropertyFromDotNotationImmutable = exports.setPropertiesFromDotNotation = exports.setPropertyFromDotNotation = exports.createObjectFromDotNotation = exports.dotNotationToObject = exports.getPropertyFromDotNotation = exports.parseAggregateSortString = exports.parseSortString = exports.buildPopulate = exports.AggregationPagingQuery = exports.PagingQuery = void 0;
4
+ // Main classes
4
5
  var pagingQuery_1 = require("./pagingQuery");
5
6
  Object.defineProperty(exports, "PagingQuery", { enumerable: true, get: function () { return pagingQuery_1.PagingQuery; } });
6
7
  var aggregationPagingQuery_1 = require("./aggregationPagingQuery");
7
8
  Object.defineProperty(exports, "AggregationPagingQuery", { enumerable: true, get: function () { return aggregationPagingQuery_1.AggregationPagingQuery; } });
9
+ // Utilities - re-export for convenience
8
10
  var buildPopulateFromString_1 = require("./utils/buildPopulateFromString");
9
11
  Object.defineProperty(exports, "buildPopulate", { enumerable: true, get: function () { return buildPopulateFromString_1.buildPopulate; } });
12
+ var parseSortString_1 = require("./utils/parseSortString");
13
+ Object.defineProperty(exports, "parseSortString", { enumerable: true, get: function () { return parseSortString_1.parseSortString; } });
14
+ Object.defineProperty(exports, "parseAggregateSortString", { enumerable: true, get: function () { return parseSortString_1.parseAggregateSortString; } });
15
+ var dotNotation_1 = require("./utils/dotNotation");
16
+ Object.defineProperty(exports, "getPropertyFromDotNotation", { enumerable: true, get: function () { return dotNotation_1.getPropertyFromDotNotation; } });
17
+ Object.defineProperty(exports, "dotNotationToObject", { enumerable: true, get: function () { return dotNotation_1.dotNotationToObject; } });
18
+ Object.defineProperty(exports, "createObjectFromDotNotation", { enumerable: true, get: function () { return dotNotation_1.createObjectFromDotNotation; } });
19
+ Object.defineProperty(exports, "setPropertyFromDotNotation", { enumerable: true, get: function () { return dotNotation_1.setPropertyFromDotNotation; } });
20
+ Object.defineProperty(exports, "setPropertiesFromDotNotation", { enumerable: true, get: function () { return dotNotation_1.setPropertiesFromDotNotation; } });
21
+ Object.defineProperty(exports, "setPropertyFromDotNotationImmutable", { enumerable: true, get: function () { return dotNotation_1.setPropertyFromDotNotationImmutable; } });
22
+ Object.defineProperty(exports, "setPropertiesFromDotNotationImmutable", { enumerable: true, get: function () { return dotNotation_1.setPropertiesFromDotNotationImmutable; } });
23
+ var isJsonString_1 = require("./utils/isJsonString");
24
+ Object.defineProperty(exports, "isJsonString", { enumerable: true, get: function () { return isJsonString_1.isJsonString; } });
25
+ var isValidDateString_1 = require("./utils/isValidDateString");
26
+ Object.defineProperty(exports, "isValidDateString", { enumerable: true, get: function () { return isValidDateString_1.isValidDateString; } });
@@ -1,16 +1,15 @@
1
- import type { PagingQueryParsedRequestParams, PagingQueryOptions } from './types';
2
- import { Model, QueryWithHelpers } from "mongoose";
3
- import type { Request } from 'express';
1
+ import type { PagingQueryParsedRequestParams, PagingQueryOptions, RequestLike } from './types';
2
+ import { Model, QueryWithHelpers, SortOrder } from "mongoose";
4
3
  export declare class PagingQuery {
5
4
  params: PagingQueryParsedRequestParams;
6
5
  options: PagingQueryOptions;
7
- query: QueryWithHelpers<any, any> | null;
6
+ query: QueryWithHelpers<unknown, unknown> | null;
8
7
  model: Model<any>;
9
- constructor(req: Request, model: Model<any>, options?: Partial<PagingQueryOptions>);
8
+ constructor(req: RequestLike, model: Model<any>, options?: Partial<PagingQueryOptions>);
10
9
  private isJsonString;
11
10
  private initQuery;
12
- parseSortString: (sortString: string) => [string, import("mongoose").SortOrder][];
11
+ parseSortString: (sortString: string) => [string, SortOrder][];
13
12
  parseParams: (defaultParams: PagingQueryParsedRequestParams | import("./types").AggregateQueryParsedRequestParams, params: import("qs").ParsedQs, isAggregate?: boolean) => PagingQueryParsedRequestParams | import("./types").AggregateQueryParsedRequestParams;
14
- exec: () => Promise<any>;
13
+ exec: () => Promise<unknown>;
15
14
  }
16
15
  //# sourceMappingURL=pagingQuery.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pagingQuery.d.ts","sourceRoot":"","sources":["../src/pagingQuery.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,8BAA8B,EAAE,kBAAkB,EAAE,WAAW,EAAC,MAAM,SAAS,CAAA;AAC5F,OAAO,EAAE,KAAK,EAAE,gBAAgB,EAAE,SAAS,EAAC,MAAM,UAAU,CAAC;AAS7D,qBAAa,WAAW;IACpB,MAAM,EAAE,8BAA8B,CASrC;IACD,OAAO,EAAE,kBAAkB,CAAK;IAChC,KAAK,EAAE,gBAAgB,CAAC,OAAO,EAAE,OAAO,CAAC,GAAG,IAAI,CAAO;IACvD,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,CAAA;gBAEL,GAAG,EAAE,WAAW,EAAE,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,EAAE,OAAO,GAAE,OAAO,CAAC,kBAAkB,CAAM;IAY1F,OAAO,CAAC,YAAY,CAAe;IACnC,OAAO,CAAC,SAAS,CAqChB;IACD,eAAe,gDAAkB;IACjC,WAAW,sPAAc;IACzB,IAAI,yBAuBH;CACJ"}
@@ -34,7 +34,6 @@ class PagingQuery {
34
34
  $sort: [],
35
35
  $paging: true,
36
36
  $populate: [],
37
- $includes: [], // to be removed
38
37
  $select: "",
39
38
  $lean: false
40
39
  };
@@ -45,7 +44,7 @@ class PagingQuery {
45
44
  const { $filter, $sort, $select, $skip, $limit, $populate, $lean } = this.params;
46
45
  const _a = this.options, { single, staticFilter, disablePaging, disableFilter } = _a, options = __rest(_a, ["single", "staticFilter", "disablePaging", "disableFilter"]);
47
46
  const filter = disableFilter ? Object.assign({}, staticFilter) : Object.assign(Object.assign({}, $filter), staticFilter);
48
- this.query = single ? this.model.findOne(filter) : this.model.find(filter);
47
+ this.query = (single ? this.model.findOne(filter) : this.model.find(filter));
49
48
  this.query.setOptions(options);
50
49
  if (disablePaging || single) {
51
50
  this.params.$paging = false;
@@ -89,11 +88,17 @@ class PagingQuery {
89
88
  skip: $skip,
90
89
  items: []
91
90
  };
92
- resObj.items = yield this.query.exec();
91
+ resObj.items = (yield this.query.exec());
93
92
  resObj.totalRows = yield this.model.countDocuments(Object.assign(Object.assign({}, $filter), staticFilter));
94
93
  resObj.rows = resObj.items.length;
95
94
  return resObj;
96
95
  });
96
+ if (!req || typeof req.query !== 'object') {
97
+ throw new Error('Invalid request object: must have a query property');
98
+ }
99
+ if (!model || typeof model.find !== 'function') {
100
+ throw new Error('Invalid model: must be a Mongoose model');
101
+ }
97
102
  this.options = options;
98
103
  this.model = model;
99
104
  this.params = this.parseParams(this.params, req.query);
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=aggregationPagingQuery.spec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"aggregationPagingQuery.spec.d.ts","sourceRoot":"","sources":["../../src/tests/aggregationPagingQuery.spec.ts"],"names":[],"mappings":""}