crud-query-parser 0.1.0 → 1.0.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 (64) hide show
  1. package/README.md +138 -35
  2. package/dist/adapters/array/index.d.mts +90 -0
  3. package/dist/adapters/array/index.d.ts +90 -0
  4. package/dist/adapters/array/index.js +2 -0
  5. package/dist/adapters/array/index.js.map +1 -0
  6. package/dist/adapters/array/index.mjs +2 -0
  7. package/dist/adapters/array/index.mjs.map +1 -0
  8. package/dist/adapters/dynamodb/index.d.mts +239 -0
  9. package/dist/adapters/dynamodb/index.d.ts +239 -0
  10. package/dist/adapters/dynamodb/index.js +2 -0
  11. package/dist/adapters/dynamodb/index.js.map +1 -0
  12. package/dist/adapters/dynamodb/index.mjs +2 -0
  13. package/dist/adapters/dynamodb/index.mjs.map +1 -0
  14. package/dist/adapters/mongodb/index.d.mts +163 -0
  15. package/dist/adapters/mongodb/index.d.ts +163 -0
  16. package/dist/adapters/mongodb/index.js +2 -0
  17. package/dist/adapters/mongodb/index.js.map +1 -0
  18. package/dist/adapters/mongodb/index.mjs +2 -0
  19. package/dist/adapters/mongodb/index.mjs.map +1 -0
  20. package/dist/adapters/typeorm/index.d.mts +4 -4
  21. package/dist/adapters/typeorm/index.d.ts +4 -4
  22. package/dist/adapters/typeorm/index.js +1 -1
  23. package/dist/adapters/typeorm/index.js.map +1 -1
  24. package/dist/adapters/typeorm/index.mjs +1 -1
  25. package/dist/adapters/typeorm/index.mjs.map +1 -1
  26. package/dist/{crud-request-x16CuDRF.d.mts → crud-request-DLo0ZuzD.d.mts} +41 -1
  27. package/dist/{crud-request-x16CuDRF.d.ts → crud-request-DLo0ZuzD.d.ts} +41 -1
  28. package/dist/{crud-request-where.builder-BwWLx0Bh.d.mts → crud-request-where.builder-CBx-JZhl.d.mts} +1 -1
  29. package/dist/{crud-request-where.builder-B5241Aht.d.ts → crud-request-where.builder-CqbP5LT9.d.ts} +1 -1
  30. package/dist/filters/index.d.mts +1 -1
  31. package/dist/filters/index.d.ts +1 -1
  32. package/dist/filters/index.js +1 -1
  33. package/dist/filters/index.js.map +1 -1
  34. package/dist/filters/index.mjs +1 -1
  35. package/dist/filters/index.mjs.map +1 -1
  36. package/dist/helpers/express/index.d.mts +30 -0
  37. package/dist/helpers/express/index.d.ts +30 -0
  38. package/dist/helpers/express/index.js +2 -0
  39. package/dist/helpers/express/index.js.map +1 -0
  40. package/dist/helpers/express/index.mjs +2 -0
  41. package/dist/helpers/express/index.mjs.map +1 -0
  42. package/dist/helpers/nestjs/index.d.mts +4 -3
  43. package/dist/helpers/nestjs/index.d.ts +4 -3
  44. package/dist/helpers/nestjs/index.js +1 -1
  45. package/dist/helpers/nestjs/index.js.map +1 -1
  46. package/dist/helpers/nestjs/index.mjs +1 -1
  47. package/dist/helpers/nestjs/index.mjs.map +1 -1
  48. package/dist/index.d.mts +12 -4
  49. package/dist/index.d.ts +12 -4
  50. package/dist/index.js +1 -1
  51. package/dist/index.js.map +1 -1
  52. package/dist/index.mjs +1 -1
  53. package/dist/index.mjs.map +1 -1
  54. package/dist/parsers/crud/index.d.mts +44 -5
  55. package/dist/parsers/crud/index.d.ts +44 -5
  56. package/dist/parsers/crud/index.js +1 -1
  57. package/dist/parsers/crud/index.js.map +1 -1
  58. package/dist/parsers/crud/index.mjs +1 -1
  59. package/dist/parsers/crud/index.mjs.map +1 -1
  60. package/dist/{query-adapter-CEcyFcWr.d.ts → query-adapter-CZhPC1aq.d.ts} +3 -3
  61. package/dist/{query-adapter-CeTK3yxp.d.mts → query-adapter-sgeUb8CV.d.mts} +3 -3
  62. package/dist/{request-parser-BxVulcsX.d.ts → request-parser-Cr3cxMRw.d.ts} +4 -4
  63. package/dist/{request-parser-BMkszvGr.d.mts → request-parser-DzuXbRsB.d.mts} +4 -4
  64. package/package.json +83 -5
package/README.md CHANGED
@@ -1,6 +1,20 @@
1
1
  # crud-query-parser
2
2
 
3
- This library parses HTTP requests and converts them to TypeORM query builders, allowing advanced filtering, column selection, pagination and relations.
3
+ [![NPM](https://img.shields.io/npm/v/crud-query-parser)](https://www.npmjs.com/package/crud-query-parser)
4
+
5
+ This library parses query parameters from HTTP requests and converts them to database queries, allowing advanced filtering, column selection, pagination and relation joining.
6
+
7
+ ## Features
8
+
9
+ - Modular architecture, can be extended as needed
10
+ - Flexible request manipulation and filtering
11
+ - Supports the `@nestjsx/crud` query parameter syntax
12
+ - [TypeORM support](./docs/adapters/typeorm.md)
13
+ - [MongoDB and Mongoose support](./docs/adapters/mongodb.md)
14
+ - [DynamoDB support](./docs/adapters/dynamodb.md)
15
+ - [JS arrays support](./docs/adapters/array.md)
16
+ - Framework-agnostic
17
+ - Supports [NestJS](./docs/frameworks/nestjs.md), [NextJS](./docs/frameworks/nextjs.md), [Express](./docs/frameworks/express.md), [Fastify](./docs/frameworks/fastify.md), [h3](./docs/frameworks/h3.md), [tinyhttp](./docs/frameworks/tinyhttp.md), [http package](./docs/frameworks/nodejs-http.md) and many more
4
18
 
5
19
  ## Install
6
20
 
@@ -10,22 +24,33 @@ npm install crud-query-parser
10
24
 
11
25
  ## Usage
12
26
 
27
+ ```mermaid
28
+ flowchart LR
29
+ A(RequestParser) --> B(Filters) --> C(QueryAdapter)
30
+ ```
31
+
13
32
  You have to pick a request parser and a query adapter.
14
33
 
15
34
  ```ts
35
+ const parser = new CrudRequestParser();
36
+ const adapter = new TypeOrmQueryAdapter();
16
37
  const userRepository = AppDataSource.getRepository(UserEntity); // TypeORM repository
17
38
 
18
39
  // ...
19
40
 
20
41
  // The request query object
21
42
  // This object will likely come from the HTTP request
22
- const requestQuery = { ... };
43
+ const requestQuery = { ... };
23
44
 
24
45
  // Parses the query into a CrudRequest object
25
- const crudRequest = parser.parse(requestQuery);
46
+ let crudRequest = parser.parse(requestQuery);
47
+
48
+ // Apply filters
49
+ // crudRequest = filterRelations(crudRequest, ['posts']);
50
+ // crudRequest = ensureLimit(crudRequest, 25, 100);
26
51
 
27
- // Using the query adapter, you can query in your ORM from the CrudRequest
28
- const result = adapter.getMany(userRepository.createQueryBuilder(), crudRequest); // GetManyResult<UserEntity>
52
+ // Using the query adapter, you can run the query through your ORM by using the CrudRequest
53
+ const result = await adapter.getMany(userRepository.createQueryBuilder(), crudRequest); // GetManyResult<UserEntity>
29
54
 
30
55
  // The result object has properties like data, page, total
31
56
  console.log(result);
@@ -33,9 +58,9 @@ console.log(result);
33
58
 
34
59
  ## Request parsers
35
60
 
36
- ### CRUD
61
+ ### CRUD Request
37
62
 
38
- The CRUD parser is an implementation of the `@nestjsx/crud` [query params format](https://github.com/nestjsx/crud/wiki/Requests#query-params).
63
+ The CRUD Request parser is an implementation of the `@nestjsx/crud` [query params format](https://github.com/nestjsx/crud/wiki/Requests#query-params).
39
64
 
40
65
  ```ts
41
66
  import { CrudRequestParser } from 'crud-query-parser/parsers/crud';
@@ -46,6 +71,8 @@ const parser = new CrudRequestParser();
46
71
  // const crudRequest = parser.parse(request.query);
47
72
  ```
48
73
 
74
+ Read more about the [CRUD Request parser](./docs/parsers/crud.md).
75
+
49
76
  ## Database adapters
50
77
 
51
78
  ### TypeORM
@@ -53,60 +80,136 @@ const parser = new CrudRequestParser();
53
80
  This adapter works with TypeORM 0.3.x and 0.2.x
54
81
 
55
82
  ```ts
56
- import { TypeormQueryAdapter } from 'crud-query-parser/adapters/typeorm';
83
+ import { TypeOrmQueryAdapter } from 'crud-query-parser/adapters/typeorm';
57
84
 
58
- const adapter = new TypeormQueryAdapter();
85
+ const adapter = new TypeOrmQueryAdapter();
59
86
 
60
87
  // Then, you can pass a query builder to it:
61
- // const result = adapter.getMany(repository.createQueryBuilder(), crudRequest);
88
+ // const result = await adapter.getMany(repository.createQueryBuilder(), crudRequest);
89
+ ```
90
+
91
+ Read more about the [TypeORM adapter](./docs/adapters/typeorm.md).
92
+
93
+ ### DynamoDB
94
+
95
+ This adapter requires [@aws-sdk/client-dynamodb](https://www.npmjs.com/package/@aws-sdk/client-dynamodb) and [@aws-sdk/util-dynamodb](https://www.npmjs.com/package/@aws-sdk/util-dynamodb) 3.x.x
96
+
97
+ ```ts
98
+ import { DynamoDBQueryAdapter } from 'crud-query-parser/adapters/dynamodb';
99
+ import { DynamoDBClient } from '@aws-sdk/client-dynamodb';
100
+
101
+ const adapter = new DynamoDBQueryAdapter({
102
+ client: new DynamoDBClient(),
103
+ tableName: 'posts',
104
+ partitionKey: 'id',
105
+ });
106
+
107
+ // Then, you can pass a partial Query/Scan input to it:
108
+ // const result = await adapter.getMany({}, crudRequest);
62
109
  ```
63
110
 
64
- ## Helpers
111
+ Read more about the [DynamoDB adapter](./docs/adapters/dynamodb.md).
112
+
113
+ ### MongoDB
114
+
115
+ ```ts
116
+ import { MongoDBQueryAdapter } from 'crud-query-parser/adapters/mongodb';
117
+
118
+ const adapter = new MongoDBQueryAdapter();
65
119
 
66
- ### NestJS
120
+ // Then, you can pass a collection to it:
121
+ // const result = await adapter.getMany(collection, crudRequest);
122
+ ```
67
123
 
68
- The NestJS integration has OpenAPI support and decorators that automatically parses the request.
124
+ Read more about the [MongoDB adapter](./docs/adapters/mongodb.md).
69
125
 
70
- Sample code:
126
+ ### Mongoose
71
127
 
72
128
  ```ts
129
+ import { MongooseQueryAdapter } from 'crud-query-parser/adapters/mongodb';
130
+
131
+ const adapter = new MongooseQueryAdapter();
132
+
133
+ // Then, you can pass a Query to it:
134
+ // const result = await adapter.getMany(Model.find(), crudRequest);
135
+ ```
136
+
137
+ Read more about the [Mongoose adapter](./docs/adapters/mongodb.md).
138
+
139
+ ### Array
140
+
141
+ This adapter can filter, sort and map plain JS arrays.
142
+
143
+ ```ts
144
+ import { ArrayQueryAdapter } from 'crud-query-parser/adapters/array';
145
+
146
+ const adapter = new ArrayQueryAdapter();
147
+
148
+ // Then, you can pass an array of entities to it:
149
+ // const result = await adapter.getMany([], crudRequest);
150
+ ```
151
+
152
+ Read more about the [array adapter](./docs/adapters/array.md).
153
+
154
+ ## Frameworks
155
+
156
+ crud-query-parser is framework-agnostic. You can pass any query parameters object to the parser and it should work out-of-the-box.
157
+ We have a few examples for the frameworks listed below:
158
+
159
+ - [NestJS](./docs/frameworks/nestjs.md)
160
+ - [NextJS](./docs/frameworks/nextjs.md)
161
+ - [Express](./docs/frameworks/express.md)
162
+ - [Fastify](./docs/frameworks/fastify.md)
163
+ - [h3](./docs/frameworks/h3.md)
164
+ - [tinyhttp](./docs/frameworks/tinyhttp.md)
165
+ - [Node.js http](./docs/frameworks/nodejs-http.md)
166
+
167
+ We also have special integrations to improve DX:
168
+
169
+ ### NestJS Integration
170
+
171
+ The NestJS Integration has decorators that automatically parses the request. It also has built-in OpenAPI support.
172
+
173
+ ```ts
174
+ import { Crud, ParseCrudRequest } from 'crud-query-parser/helpers/nestjs';
175
+ import { CrudRequestParser } from 'crud-query-parser/parsers/crud';
176
+
73
177
  @Controller('users')
74
178
  export class UserController {
75
179
 
76
- constructor(
77
- private service: UserService,
78
- ) {}
79
-
80
180
  @Get()
81
181
  @Crud(CrudRequestParser) // <- You specify which parser to use
82
182
  public async getMany(@ParseCrudRequest() crudRequest: CrudRequest) { // <- The request query will be automatically parsed
83
- return this.service.getMany(crudRequest);
183
+ // ...
84
184
  }
85
185
 
86
186
  }
87
187
  ```
88
188
 
189
+ Read more about the [NestJS Integration](docs/frameworks/nestjs.md).
190
+
191
+ ### Express Integration
192
+
193
+ The Express Integration has a middleware that automatically parses and memoizes the request.
194
+
89
195
  ```ts
90
- @Injectable()
91
- export class UserService {
92
-
93
- protected crudAdapter = new TypeormQueryAdapter();
94
-
95
- constructor(
96
- @InjectRepository(UserEntity)
97
- private repository: Repository<UserEntity>,
98
- ) {}
99
-
100
- public async getMany(crudRequest: CrudRequest) {
101
- return await this.crudAdapter.getMany(this.repository.createQueryBuilder(), crudRequest);
102
- }
196
+ import { crud } from 'crud-query-parser/helpers/express';
197
+ import { CrudRequestParser } from 'crud-query-parser/parsers/crud';
103
198
 
104
- }
199
+ app.use(crud(CrudRequestParser)); // <- You specify which parser to use
200
+
201
+ app.get('/users', (req, res) => {
202
+ const crudRequest = req.getCrudRequest(); // <- The request will be automatically parsed and memoized
203
+
204
+ // ...
205
+ });
105
206
  ```
106
207
 
208
+ Read more about the [Express Integration](docs/frameworks/express.md).
209
+
107
210
  ## Filters
108
211
 
109
- You may need to filter what the user can or cannot query. You can modify the `CrudRequest` object as needed.
212
+ You may need to filter what the user can or cannot query. You are free to modify the `CrudRequest` object however you like.
110
213
 
111
214
  There are a few filters provided by the library, which are listed below.
112
215
 
@@ -134,7 +237,7 @@ crudRequest = ensureEqCondition(crudRequest, {
134
237
  ### Ensure page limit
135
238
 
136
239
  This filter will ensure that the requested limit does not go above the maximum.
137
- It also sets the default limit whenever the limit is omitted.
240
+ It also sets a default value whenever the limit is omitted.
138
241
 
139
242
  ```ts
140
243
  import { ensureLimit } from 'crud-query-parser/filters';
@@ -0,0 +1,90 @@
1
+ import { Q as QueryAdapter, G as GetManyResult } from '../../query-adapter-sgeUb8CV.mjs';
2
+ import { C as CrudRequest, P as ParsedRequestSelect, d as CrudRequestWhere, c as CrudRequestOrder, i as CrudRequestWhereOperator, h as CrudRequestWhereValueType } from '../../crud-request-DLo0ZuzD.mjs';
3
+
4
+ interface ArrayQueryAdapterOptions<T> {
5
+ /**
6
+ * Creates an empty entity.
7
+ * This will be called when the request `select` list is not empty.
8
+ * This can be used to instantiate an entity class.
9
+ * If not defined, it will create a plain object instead.
10
+ *
11
+ * @param existing The existing entity, which will be replaced by the created one
12
+ */
13
+ createEmptyEntity?: (existing: T) => T;
14
+ }
15
+ /**
16
+ * Adapts queries to plain JS arrays
17
+ */
18
+ declare class ArrayQueryAdapter<T extends object> implements QueryAdapter<T[], T> {
19
+ protected readonly options: ArrayQueryAdapterOptions<T>;
20
+ constructor(options?: ArrayQueryAdapterOptions<T>);
21
+ /**
22
+ * @inheritDoc
23
+ */
24
+ build(data: T[], request: CrudRequest): T[];
25
+ /**
26
+ * @inheritDoc
27
+ */
28
+ getMany<E extends T = T>(data: T[], request: CrudRequest): Promise<GetManyResult<E>>;
29
+ /**
30
+ * @inheritDoc
31
+ */
32
+ getOne<E extends T = T>(data: T[], request: CrudRequest): Promise<E | null>;
33
+ /**
34
+ * Creates new objects containing only the select fields
35
+ *
36
+ * @param data The original data
37
+ * @param select The fields to keep
38
+ */
39
+ protected applySelect(data: T[], select: ParsedRequestSelect): T[];
40
+ /**
41
+ * Filters the data based on where conditions
42
+ *
43
+ * @param data The original data
44
+ * @param where The where conditions
45
+ */
46
+ protected applyWhere(data: T[], where: CrudRequestWhere): T[];
47
+ /**
48
+ * Sorts the data based on the order list
49
+ *
50
+ * @param data The original data
51
+ * @param order The ordering rules
52
+ */
53
+ protected applyOrder(data: T[], order: CrudRequestOrder[]): T[];
54
+ /**
55
+ * Slices the data based on the offset and limit
56
+ *
57
+ * @param data The original data
58
+ * @param offset The offset
59
+ * @param limit The limit
60
+ */
61
+ protected applyLimits(data: T[], offset: number, limit: number | undefined): T[];
62
+ /**
63
+ * Compare two values.
64
+ *
65
+ * Returns positive when A is greater than B;
66
+ * Returns negative when B is greater than A;
67
+ * Returns 0 when both values are equivalent.
68
+ *
69
+ * @param a The first value
70
+ * @param b The second value
71
+ */
72
+ protected compareOrder(a: any, b: any): number;
73
+ /**
74
+ * Evaluates the where condition
75
+ *
76
+ * @param data The entity to check
77
+ * @param where The where condition
78
+ */
79
+ protected checkWhere(data: T, where: CrudRequestWhere): boolean;
80
+ /**
81
+ * Evaluates a where operator
82
+ *
83
+ * @param item The left value to check
84
+ * @param operator The operator
85
+ * @param value The right value to check
86
+ */
87
+ protected checkWhereOperator(item: any, operator: CrudRequestWhereOperator, value: CrudRequestWhereValueType | CrudRequestWhereValueType[]): boolean;
88
+ }
89
+
90
+ export { ArrayQueryAdapter, type ArrayQueryAdapterOptions };
@@ -0,0 +1,90 @@
1
+ import { Q as QueryAdapter, G as GetManyResult } from '../../query-adapter-CZhPC1aq.js';
2
+ import { C as CrudRequest, P as ParsedRequestSelect, d as CrudRequestWhere, c as CrudRequestOrder, i as CrudRequestWhereOperator, h as CrudRequestWhereValueType } from '../../crud-request-DLo0ZuzD.js';
3
+
4
+ interface ArrayQueryAdapterOptions<T> {
5
+ /**
6
+ * Creates an empty entity.
7
+ * This will be called when the request `select` list is not empty.
8
+ * This can be used to instantiate an entity class.
9
+ * If not defined, it will create a plain object instead.
10
+ *
11
+ * @param existing The existing entity, which will be replaced by the created one
12
+ */
13
+ createEmptyEntity?: (existing: T) => T;
14
+ }
15
+ /**
16
+ * Adapts queries to plain JS arrays
17
+ */
18
+ declare class ArrayQueryAdapter<T extends object> implements QueryAdapter<T[], T> {
19
+ protected readonly options: ArrayQueryAdapterOptions<T>;
20
+ constructor(options?: ArrayQueryAdapterOptions<T>);
21
+ /**
22
+ * @inheritDoc
23
+ */
24
+ build(data: T[], request: CrudRequest): T[];
25
+ /**
26
+ * @inheritDoc
27
+ */
28
+ getMany<E extends T = T>(data: T[], request: CrudRequest): Promise<GetManyResult<E>>;
29
+ /**
30
+ * @inheritDoc
31
+ */
32
+ getOne<E extends T = T>(data: T[], request: CrudRequest): Promise<E | null>;
33
+ /**
34
+ * Creates new objects containing only the select fields
35
+ *
36
+ * @param data The original data
37
+ * @param select The fields to keep
38
+ */
39
+ protected applySelect(data: T[], select: ParsedRequestSelect): T[];
40
+ /**
41
+ * Filters the data based on where conditions
42
+ *
43
+ * @param data The original data
44
+ * @param where The where conditions
45
+ */
46
+ protected applyWhere(data: T[], where: CrudRequestWhere): T[];
47
+ /**
48
+ * Sorts the data based on the order list
49
+ *
50
+ * @param data The original data
51
+ * @param order The ordering rules
52
+ */
53
+ protected applyOrder(data: T[], order: CrudRequestOrder[]): T[];
54
+ /**
55
+ * Slices the data based on the offset and limit
56
+ *
57
+ * @param data The original data
58
+ * @param offset The offset
59
+ * @param limit The limit
60
+ */
61
+ protected applyLimits(data: T[], offset: number, limit: number | undefined): T[];
62
+ /**
63
+ * Compare two values.
64
+ *
65
+ * Returns positive when A is greater than B;
66
+ * Returns negative when B is greater than A;
67
+ * Returns 0 when both values are equivalent.
68
+ *
69
+ * @param a The first value
70
+ * @param b The second value
71
+ */
72
+ protected compareOrder(a: any, b: any): number;
73
+ /**
74
+ * Evaluates the where condition
75
+ *
76
+ * @param data The entity to check
77
+ * @param where The where condition
78
+ */
79
+ protected checkWhere(data: T, where: CrudRequestWhere): boolean;
80
+ /**
81
+ * Evaluates a where operator
82
+ *
83
+ * @param item The left value to check
84
+ * @param operator The operator
85
+ * @param value The right value to check
86
+ */
87
+ protected checkWhereOperator(item: any, operator: CrudRequestWhereOperator, value: CrudRequestWhereValueType | CrudRequestWhereValueType[]): boolean;
88
+ }
89
+
90
+ export { ArrayQueryAdapter, type ArrayQueryAdapterOptions };
@@ -0,0 +1,2 @@
1
+ "use strict";var a=Object.defineProperty;var d=Object.getOwnPropertyDescriptor;var b=Object.getOwnPropertyNames;var S=Object.prototype.hasOwnProperty;var i=(t,e)=>a(t,"name",{value:e,configurable:!0});var R=(t,e)=>{for(var r in e)a(t,r,{get:e[r],enumerable:!0})},m=(t,e,r,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let o of b(e))!S.call(t,o)&&o!==r&&a(t,o,{get:()=>e[o],enumerable:!(n=d(e,o))||n.enumerable});return t};var O=t=>m(a({},"__esModule",{value:!0}),t);var w={};R(w,{ArrayQueryAdapter:()=>N});module.exports=O(w);function c(t,e){if(typeof e=="number"||typeof e=="string"||typeof e=="boolean"||e instanceof Date)return e;throw new Error(`${t} must be a string, number or boolean`)}i(c,"ensurePrimitive");function u(t,e){if(typeof e=="string")return e;throw new Error(`${t} must be a string`)}i(u,"ensureString");function p(t,e,r=0){if(!Array.isArray(e)||e.length<r)throw new Error(`${t} must be an array with at least ${r} items`);return e}i(p,"ensureArray");function f(t){return t!=null}i(f,"isValid");function E(t,e,r){return t??(e&&r?e*(r-1):0)}i(E,"getOffset");function T(t,e){let r=t;for(let n=0;n<e.length;n++){let o=e[n];if(!f(r))return;if(typeof r!="object")throw new Error(`Cannot get ${o} as it is not an object (got ${typeof r})`);r=r[o]}return r}i(T,"pathGetValue");function h(t,e,r){let n=t;for(let o=0;o<e.length;o++){let s=e[o];if(typeof n!="object")throw new Error(`Cannot set ${s} as it is not an object (got ${typeof n})`);o===e.length-1?n[s]=r:f(n[s])?n=n[s]:n=n[s]={}}}i(h,"pathSetValue");function L(t,e,r,n){let o=t.length,s=n??e,l=s>0?Math.floor(r/s)+1:1,g=s>0?Math.ceil(e/s):0;return{data:t,count:o,total:e,page:l,pageCount:g}}i(L,"createGetManyResult");var N=class{constructor(e={}){this.options=e}static{i(this,"ArrayQueryAdapter")}build(e,r){return e=this.applyWhere(e,r.where),e=this.applyOrder(e,r.order),e=this.applyLimits(e,E(r.offset,r.limit,r.page),r.limit),e=this.applySelect(e,r.select),e}async getMany(e,r){e=this.applyWhere(e,r.where),e=this.applyOrder(e,r.order);let n=e.length,o=E(r.offset,r.limit,r.page),s=r.limit||n;return e=this.applyLimits(e,o,s),e=this.applySelect(e,r.select),L(e,n,o,s)}async getOne(e,r){return e=this.applyWhere(e,r.where),e=this.applyOrder(e,r.order),e=e.slice(0,1),e=this.applySelect(e,r.select),e.length===0?null:e[0]}applySelect(e,r){return r.length===0?e:e.map(n=>{let o=this.options.createEmptyEntity?.(n)??{};for(let s of r)h(o,s.field,T(n,s.field));return o})}applyWhere(e,r){return e.filter(n=>this.checkWhere(n,r))}applyOrder(e,r){return e.sort((n,o)=>{for(let s of r){let l=T(n,s.field),g=T(o,s.field),y=this.compareOrder(l,g);if(s.order==="DESC"&&(y=-y),y!==0)return y}return 0})}applyLimits(e,r,n){return!r&&!n?e:e.slice(r,r&&n?r+n:n)}compareOrder(e,r){return typeof e=="number"&&typeof r=="number"?e-r:typeof e=="string"&&typeof r=="string"?e.localeCompare(r):typeof e=="boolean"&&typeof r=="boolean"?+e-+r:0}checkWhere(e,r){return r.and?r.and.every(n=>this.checkWhere(e,n)):r.or?r.or.some(n=>this.checkWhere(e,n)):r.field?this.checkWhereOperator(T(e,r.field),r.operator,r.value):!0}checkWhereOperator(e,r,n){switch(r){case"eq":return e==n;case"neq":return e!=n;case"gt":return e>c("> operator",n);case"gte":return e>=c(">= operator",n);case"lt":return e<c("< operator",n);case"lte":return e<=c("<= operator",n);case"between":let o=p("BETWEEN operator",n,2);return e>=c("left BETWEEN value",o[0])&&e<=c("right BETWEEN value",o[1]);case"is_null":return!f(e);case"not_null":return f(e);case"contains":return typeof e!="string"?!1:e.includes(u("CONTAINS",n));case"not_contains":return typeof e!="string"?!0:!e.includes(u("NOT CONTAINS",n));case"starts":return typeof e!="string"?!1:e.startsWith(u("STARTS",n));case"ends":return typeof e!="string"?!1:e.endsWith(u("ENDS",n));case"in":return p("IN",n).includes(e);case"not_in":return!p("NOT IN",n).includes(e);case"eq_lower":return e?.toString().toLowerCase()==u("== LOWER",n).toLowerCase();case"neq_lower":return e?.toString().toLowerCase()!=u("!= LOWER",n).toLowerCase();case"contains_lower":return typeof e!="string"?!1:e.toLowerCase().includes(u("CONTAINS LOWER",n).toLowerCase());case"not_contains_lower":return typeof e!="string"?!0:!e.toLowerCase().includes(u("NOT CONTAINS LOWER",n).toLowerCase());case"starts_lower":return typeof e!="string"?!1:e.toLowerCase().startsWith(u("STARTS LOWER",n).toLowerCase());case"ends_lower":return typeof e!="string"?!1:e.toLowerCase().endsWith(u("ENDS LOWER",n).toLowerCase());case"in_lower":{let s=e?.toString().toLowerCase();return p("IN LOWER",n).some(l=>s===l?.toString().toLowerCase())}case"not_in_lower":{let s=e?.toString().toLowerCase();return!p("NOT IN LOWER",n).some(l=>s===l?.toString().toLowerCase())}}throw new Error(`Unsupported operator "${r}"`)}};0&&(module.exports={ArrayQueryAdapter});
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/adapters/array/index.ts","../../../src/utils/functions.ts","../../../src/utils/field-path.ts","../../../src/utils/objects.ts","../../../src/adapters/array/array.query-adapter.ts"],"sourcesContent":["\r\nexport * from './array.query-adapter';\r\n","export function ensurePrimitive(fieldName: string, data: any): number | string | boolean | Date {\r\n if (typeof data === 'number' || typeof data === 'string' || typeof data === 'boolean' || data instanceof Date)\r\n return data;\r\n\r\n throw new Error(`${fieldName} must be a string, number or boolean`);\r\n}\r\n\r\nexport function ensurePrimitiveOrNull(fieldName: string, data: any): number | string | boolean | Date | undefined | null {\r\n if (data === null || data === undefined)\r\n return data;\r\n\r\n if (typeof data === 'number' || typeof data === 'string' || typeof data === 'boolean' || data instanceof Date)\r\n return data;\r\n\r\n throw new Error(`${fieldName} must be a string, number, boolean or null`);\r\n}\r\n\r\nexport function ensureString(fieldName: string, data: any): string {\r\n if (typeof data === 'string')\r\n return data;\r\n\r\n throw new Error(`${fieldName} must be a string`);\r\n}\r\n\r\nexport function ensureArray<T>(fieldName: string, data: T[] | any, minLength: number = 0): T[] {\r\n if (!Array.isArray(data) || data.length < minLength)\r\n throw new Error(`${fieldName} must be an array with at least ${minLength} items`);\r\n\r\n return data;\r\n}\r\n\r\nexport function ensureEmpty(fieldName: string, data: any) {\r\n if (isValid(data) && data !== true)\r\n throw new Error(`${fieldName} must be true, null or undefined`);\r\n}\r\n\r\nexport function isValid<T>(value: T | undefined | null): value is T {\r\n return value !== null && value !== undefined;\r\n}\r\n\r\nexport function getOffset(offset: number | undefined, limit?: number, page?: number): number {\r\n return offset ?? (limit && page ? limit * (page - 1) : 0);\r\n}\r\n\r\nexport interface Type<T> extends Function { new (... args: any[]): T; }\r\n\r\nexport function createInstance<T extends object>(clazzOrInstance: T | Type<T> | undefined): T | undefined {\r\n if (typeof clazzOrInstance === 'function')\r\n return new clazzOrInstance();\r\n\r\n if (typeof clazzOrInstance === 'object')\r\n return clazzOrInstance as T;\r\n\r\n return undefined;\r\n}\r\n\r\nexport function escapeRegex(str: string): string {\r\n return str.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\r\n}\r\n","import { isValid } from './functions';\r\n\r\n/**\r\n * Parses a path by splitting it by dots\r\n *\r\n * @param value The full path as a string or the parts already split\r\n */\r\nexport function pathParse(value: string | string[]): string[] {\r\n if (typeof value === 'string')\r\n return value.split('.');\r\n\r\n return value;\r\n}\r\n\r\n/**\r\n * Checks whether two field paths are equal\r\n *\r\n * E.g. [\"path\", \"to\", \"field\"] is equal to [\"path\", \"to\", \"field\"] but not [\"something\", \"else\"]\r\n */\r\nexport function pathEquals(path1: string[], path2: string[]): boolean {\r\n if (path1.length !== path2.length)\r\n return false;\r\n\r\n return path1.every((p1, i) => path2[i] === p1);\r\n}\r\n\r\n/**\r\n * Checks whether a path starts with another path.\r\n *\r\n * E.g. [\"path\", \"to\", \"field\"] starts with [\"path\"] or [\"path\", \"to\"] but not [\"something\", \"else\"]\r\n */\r\nexport function pathStartsWith(path: string[], start: string[]): boolean {\r\n if (path.length < start.length)\r\n return false;\r\n\r\n return start.every((start, i) => path[i] === start);\r\n}\r\n\r\n/**\r\n * Checks whether the base of a path matches.\r\n *\r\n * E.g. [\"path\", \"to\", \"field\"] has a base of [\"path\", \"to\"] but not [\"path\"]\r\n */\r\nexport function pathHasBase(path: string[], base: string[]): boolean {\r\n if (path.length - 1 !== base.length)\r\n return false;\r\n\r\n return base.every((start, i) => path[i] === start);\r\n}\r\n\r\n/**\r\n * Breaks a path into the base part and the field name part\r\n *\r\n * @param path The full path\r\n */\r\nexport function pathGetBaseAndName(path: string[]): [string[], string] {\r\n if (path.length === 0)\r\n throw new Error('Cannot break an empty path');\r\n\r\n const base = [...path];\r\n const name = base.pop()!;\r\n\r\n return [base, name];\r\n}\r\n\r\n\r\n/**\r\n * Gets the last part of the path: the field name\r\n *\r\n * @param path The full path\r\n */\r\nexport function pathGetFieldName(path: string[]): string {\r\n return path.length > 0 ? path[path.length - 1] : '';\r\n}\r\n\r\n/**\r\n * Sets a value for the given path\r\n *\r\n * @param obj The root object\r\n * @param field The full field path\r\n */\r\nexport function pathGetValue(obj: object, field: string[]): any {\r\n let value: any = obj;\r\n\r\n for (let i = 0; i < field.length; i++) {\r\n const name = field[i];\r\n\r\n if (!isValid(value))\r\n return undefined;\r\n\r\n if (typeof value !== 'object')\r\n throw new Error(`Cannot get ${name} as it is not an object (got ${typeof value})`);\r\n\r\n value = value[name];\r\n }\r\n\r\n return value;\r\n}\r\n\r\n/**\r\n * Sets a value for the given path\r\n *\r\n * @param obj The root object\r\n * @param field The full field path\r\n * @param value The value to be set\r\n */\r\nexport function pathSetValue(obj: object, field: string[], value: any): void {\r\n let self: any = obj;\r\n\r\n for (let i = 0; i < field.length; i++) {\r\n const name = field[i];\r\n\r\n if (typeof self !== 'object')\r\n throw new Error(`Cannot set ${name} as it is not an object (got ${typeof self})`);\r\n\r\n const isLast = i === field.length - 1;\r\n\r\n if (isLast)\r\n self[name] = value;\r\n else if (!isValid(self[name]))\r\n self = self[name] = {};\r\n else\r\n self = self[name];\r\n }\r\n}\r\n","import { GetManyResult } from '../models/get-many-result';\r\nimport { CrudRequest } from '../models/crud-request';\r\n\r\n/**\r\n * Creates a CrudRequest object, filling required missing properties with empty values\r\n */\r\nexport function createCrudRequest(crudRequest?: Partial<CrudRequest>): CrudRequest {\r\n return {\r\n select: [],\r\n relations: [],\r\n order: [],\r\n where: { and: [] },\r\n ...crudRequest,\r\n };\r\n}\r\n\r\n/**\r\n * Creates a GetManyResult object\r\n *\r\n * @param data The entity list to be returned\r\n * @param total The total amount of entities in the database\r\n * @param offset The offset used for querying\r\n * @param limit The limit used for querying\r\n */\r\nexport function createGetManyResult<T>(data: T[], total: number, offset: number, limit?: number): GetManyResult<T> {\r\n const count = data.length;\r\n const actualLimit = limit ?? total;\r\n const page = actualLimit > 0 ? Math.floor(offset / actualLimit) + 1 : 1;\r\n const pageCount = actualLimit > 0 ? Math.ceil(total / actualLimit) : 0;\r\n\r\n return {\r\n data,\r\n count,\r\n total,\r\n page,\r\n pageCount,\r\n };\r\n}\r\n","import { QueryAdapter } from '../../models/query-adapter';\r\nimport { CrudRequest, CrudRequestOrder, ParsedRequestSelect } from '../../models/crud-request';\r\nimport { GetManyResult } from '../../models/get-many-result';\r\nimport { CrudRequestWhere, CrudRequestWhereOperator, CrudRequestWhereValueType } from '../../models/crud-request-where';\r\nimport { ensureArray, ensurePrimitive, ensureString, getOffset, isValid } from '../../utils/functions';\r\nimport { pathGetValue, pathSetValue } from '../../utils/field-path';\r\nimport { createGetManyResult } from '../../utils/objects';\r\n\r\nexport interface ArrayQueryAdapterOptions<T> {\r\n /**\r\n * Creates an empty entity.\r\n * This will be called when the request `select` list is not empty.\r\n * This can be used to instantiate an entity class.\r\n * If not defined, it will create a plain object instead.\r\n *\r\n * @param existing The existing entity, which will be replaced by the created one\r\n */\r\n createEmptyEntity?: (existing: T) => T;\r\n}\r\n\r\n/**\r\n * Adapts queries to plain JS arrays\r\n */\r\nexport class ArrayQueryAdapter<T extends object> implements QueryAdapter<T[], T> {\r\n\r\n constructor(\r\n protected readonly options: ArrayQueryAdapterOptions<T> = {},\r\n ) {}\r\n\r\n /**\r\n * @inheritDoc\r\n */\r\n public build(data: T[], request: CrudRequest): T[] {\r\n data = this.applyWhere(data, request.where);\r\n data = this.applyOrder(data, request.order);\r\n data = this.applyLimits(data, getOffset(request.offset, request.limit, request.page), request.limit);\r\n data = this.applySelect(data, request.select);\r\n\r\n return data;\r\n }\r\n\r\n /**\r\n * @inheritDoc\r\n */\r\n public async getMany<E extends T = T>(data: T[], request: CrudRequest): Promise<GetManyResult<E>> {\r\n data = this.applyWhere(data, request.where);\r\n data = this.applyOrder(data, request.order);\r\n\r\n const total = data.length;\r\n const offset = getOffset(request.offset, request.limit, request.page);\r\n const limit = request.limit || total;\r\n\r\n data = this.applyLimits(data, offset, limit);\r\n data = this.applySelect(data, request.select);\r\n\r\n return createGetManyResult(data as E[], total, offset, limit);\r\n }\r\n\r\n /**\r\n * @inheritDoc\r\n */\r\n public async getOne<E extends T = T>(data: T[], request: CrudRequest): Promise<E | null> {\r\n data = this.applyWhere(data, request.where);\r\n data = this.applyOrder(data, request.order);\r\n data = data.slice(0, 1);\r\n data = this.applySelect(data, request.select);\r\n\r\n if (data.length === 0)\r\n return null;\r\n\r\n return data[0] as E;\r\n }\r\n\r\n /**\r\n * Creates new objects containing only the select fields\r\n *\r\n * @param data The original data\r\n * @param select The fields to keep\r\n */\r\n protected applySelect(data: T[], select: ParsedRequestSelect): T[] {\r\n if (select.length === 0)\r\n return data;\r\n\r\n return data.map(item => {\r\n const newObject = this.options.createEmptyEntity?.(item) ?? <T>{};\r\n\r\n for (const field of select) {\r\n pathSetValue(newObject, field.field, pathGetValue(item, field.field));\r\n }\r\n\r\n return newObject;\r\n })\r\n }\r\n\r\n /**\r\n * Filters the data based on where conditions\r\n *\r\n * @param data The original data\r\n * @param where The where conditions\r\n */\r\n protected applyWhere(data: T[], where: CrudRequestWhere): T[] {\r\n return data.filter(item => this.checkWhere(item, where));\r\n }\r\n\r\n /**\r\n * Sorts the data based on the order list\r\n *\r\n * @param data The original data\r\n * @param order The ordering rules\r\n */\r\n protected applyOrder(data: T[], order: CrudRequestOrder[]): T[] {\r\n return data.sort((a, b) => {\r\n for (const o of order) {\r\n const valueA = pathGetValue(a, o.field);\r\n const valueB = pathGetValue(b, o.field);\r\n\r\n let comparison = this.compareOrder(valueA, valueB);\r\n\r\n if (o.order === 'DESC')\r\n comparison = -comparison;\r\n\r\n if (comparison !== 0)\r\n return comparison;\r\n }\r\n\r\n return 0;\r\n });\r\n }\r\n\r\n /**\r\n * Slices the data based on the offset and limit\r\n *\r\n * @param data The original data\r\n * @param offset The offset\r\n * @param limit The limit\r\n */\r\n protected applyLimits(data: T[], offset: number, limit: number | undefined): T[] {\r\n if (!offset && !limit)\r\n return data;\r\n\r\n return data.slice(offset, offset && limit ? offset + limit : limit);\r\n }\r\n\r\n /**\r\n * Compare two values.\r\n *\r\n * Returns positive when A is greater than B;\r\n * Returns negative when B is greater than A;\r\n * Returns 0 when both values are equivalent.\r\n *\r\n * @param a The first value\r\n * @param b The second value\r\n */\r\n protected compareOrder(a: any, b: any): number {\r\n if (typeof a === 'number' && typeof b === 'number')\r\n return a - b;\r\n\r\n if (typeof a === 'string' && typeof b === 'string')\r\n return a.localeCompare(b);\r\n\r\n if (typeof a === 'boolean' && typeof b === 'boolean')\r\n return +a - +b;\r\n\r\n return 0;\r\n }\r\n\r\n /**\r\n * Evaluates the where condition\r\n *\r\n * @param data The entity to check\r\n * @param where The where condition\r\n */\r\n protected checkWhere(data: T, where: CrudRequestWhere): boolean {\r\n if (where.and)\r\n return where.and.every(item => this.checkWhere(data, item));\r\n\r\n if (where.or)\r\n return where.or.some(item => this.checkWhere(data, item));\r\n\r\n if (where.field)\r\n return this.checkWhereOperator(pathGetValue(data, where.field), where.operator, where.value);\r\n\r\n return true;\r\n }\r\n\r\n /**\r\n * Evaluates a where operator\r\n *\r\n * @param item The left value to check\r\n * @param operator The operator\r\n * @param value The right value to check\r\n */\r\n protected checkWhereOperator(item: any, operator: CrudRequestWhereOperator, value: CrudRequestWhereValueType | CrudRequestWhereValueType[]): boolean {\r\n switch (operator) {\r\n case CrudRequestWhereOperator.EQ:\r\n return item == value;\r\n\r\n case CrudRequestWhereOperator.NEQ:\r\n return item != value;\r\n\r\n case CrudRequestWhereOperator.GT:\r\n return item > ensurePrimitive('> operator', value);\r\n\r\n case CrudRequestWhereOperator.GTE:\r\n return item >= ensurePrimitive('>= operator', value);\r\n\r\n case CrudRequestWhereOperator.LT:\r\n return item < ensurePrimitive('< operator', value);\r\n\r\n case CrudRequestWhereOperator.LTE:\r\n return item <= ensurePrimitive('<= operator', value);\r\n\r\n case CrudRequestWhereOperator.BETWEEN:\r\n const arr = ensureArray('BETWEEN operator', value, 2);\r\n\r\n return item >= ensurePrimitive('left BETWEEN value', arr[0]) &&\r\n item <= ensurePrimitive('right BETWEEN value', arr[1]);\r\n\r\n case CrudRequestWhereOperator.IS_NULL:\r\n return !isValid(item);\r\n\r\n case CrudRequestWhereOperator.NOT_NULL:\r\n return isValid(item);\r\n\r\n case CrudRequestWhereOperator.CONTAINS:\r\n return typeof item !== 'string' ? false :\r\n item.includes(ensureString('CONTAINS', value));\r\n\r\n case CrudRequestWhereOperator.NOT_CONTAINS:\r\n return typeof item !== 'string' ? true :\r\n !item.includes(ensureString('NOT CONTAINS', value));\r\n\r\n case CrudRequestWhereOperator.STARTS:\r\n return typeof item !== 'string' ? false :\r\n item.startsWith(ensureString('STARTS', value));\r\n\r\n case CrudRequestWhereOperator.ENDS:\r\n return typeof item !== 'string' ? false :\r\n item.endsWith(ensureString('ENDS', value));\r\n\r\n case CrudRequestWhereOperator.IN:\r\n return ensureArray('IN', value).includes(item);\r\n\r\n case CrudRequestWhereOperator.NOT_IN:\r\n return !ensureArray('NOT IN', value).includes(item);\r\n\r\n case CrudRequestWhereOperator.EQ_LOWER:\r\n return item?.toString().toLowerCase() == ensureString('== LOWER', value).toLowerCase();\r\n\r\n case CrudRequestWhereOperator.NEQ_LOWER:\r\n return item?.toString().toLowerCase() != ensureString('!= LOWER', value).toLowerCase();\r\n\r\n case CrudRequestWhereOperator.CONTAINS_LOWER:\r\n return typeof item !== 'string' ? false :\r\n item.toLowerCase().includes(ensureString('CONTAINS LOWER', value).toLowerCase());\r\n\r\n case CrudRequestWhereOperator.NOT_CONTAINS_LOWER:\r\n return typeof item !== 'string' ? true :\r\n !item.toLowerCase().includes(ensureString('NOT CONTAINS LOWER', value).toLowerCase());\r\n\r\n case CrudRequestWhereOperator.STARTS_LOWER:\r\n return typeof item !== 'string' ? false :\r\n item.toLowerCase().startsWith(ensureString('STARTS LOWER', value).toLowerCase());\r\n\r\n case CrudRequestWhereOperator.ENDS_LOWER:\r\n return typeof item !== 'string' ? false :\r\n item.toLowerCase().endsWith(ensureString('ENDS LOWER', value).toLowerCase());\r\n\r\n case CrudRequestWhereOperator.IN_LOWER: {\r\n const val = item?.toString().toLowerCase();\r\n\r\n return ensureArray('IN LOWER', value).some(elem => val === elem?.toString().toLowerCase());\r\n }\r\n\r\n case CrudRequestWhereOperator.NOT_IN_LOWER: {\r\n const val = item?.toString().toLowerCase();\r\n\r\n return !ensureArray('NOT IN LOWER', value).some(elem => val === elem?.toString().toLowerCase());\r\n }\r\n }\r\n\r\n throw new Error(`Unsupported operator \"${operator}\"`);\r\n }\r\n}\r\n"],"mappings":"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","names":["array_exports","__export","ArrayQueryAdapter","__toCommonJS","ensurePrimitive","fieldName","data","__name","ensureString","fieldName","data","__name","ensureArray","minLength","isValid","value","__name","getOffset","offset","limit","page","pathGetValue","obj","field","value","i","name","isValid","__name","pathSetValue","self","createGetManyResult","data","total","offset","limit","count","actualLimit","page","pageCount","__name","ArrayQueryAdapter","options","__name","data","request","getOffset","total","offset","limit","createGetManyResult","select","item","newObject","field","pathSetValue","pathGetValue","where","order","a","b","o","valueA","valueB","comparison","operator","value","ensurePrimitive","arr","ensureArray","isValid","ensureString","val","elem"]}
@@ -0,0 +1,2 @@
1
+ var L=Object.defineProperty;var i=(o,e)=>L(o,"name",{value:e,configurable:!0});function c(o,e){if(typeof e=="number"||typeof e=="string"||typeof e=="boolean"||e instanceof Date)return e;throw new Error(`${o} must be a string, number or boolean`)}i(c,"ensurePrimitive");function u(o,e){if(typeof e=="string")return e;throw new Error(`${o} must be a string`)}i(u,"ensureString");function p(o,e,r=0){if(!Array.isArray(e)||e.length<r)throw new Error(`${o} must be an array with at least ${r} items`);return e}i(p,"ensureArray");function f(o){return o!=null}i(f,"isValid");function g(o,e,r){return o??(e&&r?e*(r-1):0)}i(g,"getOffset");function T(o,e){let r=o;for(let n=0;n<e.length;n++){let s=e[n];if(!f(r))return;if(typeof r!="object")throw new Error(`Cannot get ${s} as it is not an object (got ${typeof r})`);r=r[s]}return r}i(T,"pathGetValue");function E(o,e,r){let n=o;for(let s=0;s<e.length;s++){let t=e[s];if(typeof n!="object")throw new Error(`Cannot set ${t} as it is not an object (got ${typeof n})`);s===e.length-1?n[t]=r:f(n[t])?n=n[t]:n=n[t]={}}}i(E,"pathSetValue");function N(o,e,r,n){let s=o.length,t=n??e,l=t>0?Math.floor(r/t)+1:1,a=t>0?Math.ceil(e/t):0;return{data:o,count:s,total:e,page:l,pageCount:a}}i(N,"createGetManyResult");var h=class{constructor(e={}){this.options=e}static{i(this,"ArrayQueryAdapter")}build(e,r){return e=this.applyWhere(e,r.where),e=this.applyOrder(e,r.order),e=this.applyLimits(e,g(r.offset,r.limit,r.page),r.limit),e=this.applySelect(e,r.select),e}async getMany(e,r){e=this.applyWhere(e,r.where),e=this.applyOrder(e,r.order);let n=e.length,s=g(r.offset,r.limit,r.page),t=r.limit||n;return e=this.applyLimits(e,s,t),e=this.applySelect(e,r.select),N(e,n,s,t)}async getOne(e,r){return e=this.applyWhere(e,r.where),e=this.applyOrder(e,r.order),e=e.slice(0,1),e=this.applySelect(e,r.select),e.length===0?null:e[0]}applySelect(e,r){return r.length===0?e:e.map(n=>{let s=this.options.createEmptyEntity?.(n)??{};for(let t of r)E(s,t.field,T(n,t.field));return s})}applyWhere(e,r){return e.filter(n=>this.checkWhere(n,r))}applyOrder(e,r){return e.sort((n,s)=>{for(let t of r){let l=T(n,t.field),a=T(s,t.field),y=this.compareOrder(l,a);if(t.order==="DESC"&&(y=-y),y!==0)return y}return 0})}applyLimits(e,r,n){return!r&&!n?e:e.slice(r,r&&n?r+n:n)}compareOrder(e,r){return typeof e=="number"&&typeof r=="number"?e-r:typeof e=="string"&&typeof r=="string"?e.localeCompare(r):typeof e=="boolean"&&typeof r=="boolean"?+e-+r:0}checkWhere(e,r){return r.and?r.and.every(n=>this.checkWhere(e,n)):r.or?r.or.some(n=>this.checkWhere(e,n)):r.field?this.checkWhereOperator(T(e,r.field),r.operator,r.value):!0}checkWhereOperator(e,r,n){switch(r){case"eq":return e==n;case"neq":return e!=n;case"gt":return e>c("> operator",n);case"gte":return e>=c(">= operator",n);case"lt":return e<c("< operator",n);case"lte":return e<=c("<= operator",n);case"between":let s=p("BETWEEN operator",n,2);return e>=c("left BETWEEN value",s[0])&&e<=c("right BETWEEN value",s[1]);case"is_null":return!f(e);case"not_null":return f(e);case"contains":return typeof e!="string"?!1:e.includes(u("CONTAINS",n));case"not_contains":return typeof e!="string"?!0:!e.includes(u("NOT CONTAINS",n));case"starts":return typeof e!="string"?!1:e.startsWith(u("STARTS",n));case"ends":return typeof e!="string"?!1:e.endsWith(u("ENDS",n));case"in":return p("IN",n).includes(e);case"not_in":return!p("NOT IN",n).includes(e);case"eq_lower":return e?.toString().toLowerCase()==u("== LOWER",n).toLowerCase();case"neq_lower":return e?.toString().toLowerCase()!=u("!= LOWER",n).toLowerCase();case"contains_lower":return typeof e!="string"?!1:e.toLowerCase().includes(u("CONTAINS LOWER",n).toLowerCase());case"not_contains_lower":return typeof e!="string"?!0:!e.toLowerCase().includes(u("NOT CONTAINS LOWER",n).toLowerCase());case"starts_lower":return typeof e!="string"?!1:e.toLowerCase().startsWith(u("STARTS LOWER",n).toLowerCase());case"ends_lower":return typeof e!="string"?!1:e.toLowerCase().endsWith(u("ENDS LOWER",n).toLowerCase());case"in_lower":{let t=e?.toString().toLowerCase();return p("IN LOWER",n).some(l=>t===l?.toString().toLowerCase())}case"not_in_lower":{let t=e?.toString().toLowerCase();return!p("NOT IN LOWER",n).some(l=>t===l?.toString().toLowerCase())}}throw new Error(`Unsupported operator "${r}"`)}};export{h as ArrayQueryAdapter};
2
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/utils/functions.ts","../../../src/utils/field-path.ts","../../../src/utils/objects.ts","../../../src/adapters/array/array.query-adapter.ts"],"sourcesContent":["export function ensurePrimitive(fieldName: string, data: any): number | string | boolean | Date {\r\n if (typeof data === 'number' || typeof data === 'string' || typeof data === 'boolean' || data instanceof Date)\r\n return data;\r\n\r\n throw new Error(`${fieldName} must be a string, number or boolean`);\r\n}\r\n\r\nexport function ensurePrimitiveOrNull(fieldName: string, data: any): number | string | boolean | Date | undefined | null {\r\n if (data === null || data === undefined)\r\n return data;\r\n\r\n if (typeof data === 'number' || typeof data === 'string' || typeof data === 'boolean' || data instanceof Date)\r\n return data;\r\n\r\n throw new Error(`${fieldName} must be a string, number, boolean or null`);\r\n}\r\n\r\nexport function ensureString(fieldName: string, data: any): string {\r\n if (typeof data === 'string')\r\n return data;\r\n\r\n throw new Error(`${fieldName} must be a string`);\r\n}\r\n\r\nexport function ensureArray<T>(fieldName: string, data: T[] | any, minLength: number = 0): T[] {\r\n if (!Array.isArray(data) || data.length < minLength)\r\n throw new Error(`${fieldName} must be an array with at least ${minLength} items`);\r\n\r\n return data;\r\n}\r\n\r\nexport function ensureEmpty(fieldName: string, data: any) {\r\n if (isValid(data) && data !== true)\r\n throw new Error(`${fieldName} must be true, null or undefined`);\r\n}\r\n\r\nexport function isValid<T>(value: T | undefined | null): value is T {\r\n return value !== null && value !== undefined;\r\n}\r\n\r\nexport function getOffset(offset: number | undefined, limit?: number, page?: number): number {\r\n return offset ?? (limit && page ? limit * (page - 1) : 0);\r\n}\r\n\r\nexport interface Type<T> extends Function { new (... args: any[]): T; }\r\n\r\nexport function createInstance<T extends object>(clazzOrInstance: T | Type<T> | undefined): T | undefined {\r\n if (typeof clazzOrInstance === 'function')\r\n return new clazzOrInstance();\r\n\r\n if (typeof clazzOrInstance === 'object')\r\n return clazzOrInstance as T;\r\n\r\n return undefined;\r\n}\r\n\r\nexport function escapeRegex(str: string): string {\r\n return str.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\r\n}\r\n","import { isValid } from './functions';\r\n\r\n/**\r\n * Parses a path by splitting it by dots\r\n *\r\n * @param value The full path as a string or the parts already split\r\n */\r\nexport function pathParse(value: string | string[]): string[] {\r\n if (typeof value === 'string')\r\n return value.split('.');\r\n\r\n return value;\r\n}\r\n\r\n/**\r\n * Checks whether two field paths are equal\r\n *\r\n * E.g. [\"path\", \"to\", \"field\"] is equal to [\"path\", \"to\", \"field\"] but not [\"something\", \"else\"]\r\n */\r\nexport function pathEquals(path1: string[], path2: string[]): boolean {\r\n if (path1.length !== path2.length)\r\n return false;\r\n\r\n return path1.every((p1, i) => path2[i] === p1);\r\n}\r\n\r\n/**\r\n * Checks whether a path starts with another path.\r\n *\r\n * E.g. [\"path\", \"to\", \"field\"] starts with [\"path\"] or [\"path\", \"to\"] but not [\"something\", \"else\"]\r\n */\r\nexport function pathStartsWith(path: string[], start: string[]): boolean {\r\n if (path.length < start.length)\r\n return false;\r\n\r\n return start.every((start, i) => path[i] === start);\r\n}\r\n\r\n/**\r\n * Checks whether the base of a path matches.\r\n *\r\n * E.g. [\"path\", \"to\", \"field\"] has a base of [\"path\", \"to\"] but not [\"path\"]\r\n */\r\nexport function pathHasBase(path: string[], base: string[]): boolean {\r\n if (path.length - 1 !== base.length)\r\n return false;\r\n\r\n return base.every((start, i) => path[i] === start);\r\n}\r\n\r\n/**\r\n * Breaks a path into the base part and the field name part\r\n *\r\n * @param path The full path\r\n */\r\nexport function pathGetBaseAndName(path: string[]): [string[], string] {\r\n if (path.length === 0)\r\n throw new Error('Cannot break an empty path');\r\n\r\n const base = [...path];\r\n const name = base.pop()!;\r\n\r\n return [base, name];\r\n}\r\n\r\n\r\n/**\r\n * Gets the last part of the path: the field name\r\n *\r\n * @param path The full path\r\n */\r\nexport function pathGetFieldName(path: string[]): string {\r\n return path.length > 0 ? path[path.length - 1] : '';\r\n}\r\n\r\n/**\r\n * Sets a value for the given path\r\n *\r\n * @param obj The root object\r\n * @param field The full field path\r\n */\r\nexport function pathGetValue(obj: object, field: string[]): any {\r\n let value: any = obj;\r\n\r\n for (let i = 0; i < field.length; i++) {\r\n const name = field[i];\r\n\r\n if (!isValid(value))\r\n return undefined;\r\n\r\n if (typeof value !== 'object')\r\n throw new Error(`Cannot get ${name} as it is not an object (got ${typeof value})`);\r\n\r\n value = value[name];\r\n }\r\n\r\n return value;\r\n}\r\n\r\n/**\r\n * Sets a value for the given path\r\n *\r\n * @param obj The root object\r\n * @param field The full field path\r\n * @param value The value to be set\r\n */\r\nexport function pathSetValue(obj: object, field: string[], value: any): void {\r\n let self: any = obj;\r\n\r\n for (let i = 0; i < field.length; i++) {\r\n const name = field[i];\r\n\r\n if (typeof self !== 'object')\r\n throw new Error(`Cannot set ${name} as it is not an object (got ${typeof self})`);\r\n\r\n const isLast = i === field.length - 1;\r\n\r\n if (isLast)\r\n self[name] = value;\r\n else if (!isValid(self[name]))\r\n self = self[name] = {};\r\n else\r\n self = self[name];\r\n }\r\n}\r\n","import { GetManyResult } from '../models/get-many-result';\r\nimport { CrudRequest } from '../models/crud-request';\r\n\r\n/**\r\n * Creates a CrudRequest object, filling required missing properties with empty values\r\n */\r\nexport function createCrudRequest(crudRequest?: Partial<CrudRequest>): CrudRequest {\r\n return {\r\n select: [],\r\n relations: [],\r\n order: [],\r\n where: { and: [] },\r\n ...crudRequest,\r\n };\r\n}\r\n\r\n/**\r\n * Creates a GetManyResult object\r\n *\r\n * @param data The entity list to be returned\r\n * @param total The total amount of entities in the database\r\n * @param offset The offset used for querying\r\n * @param limit The limit used for querying\r\n */\r\nexport function createGetManyResult<T>(data: T[], total: number, offset: number, limit?: number): GetManyResult<T> {\r\n const count = data.length;\r\n const actualLimit = limit ?? total;\r\n const page = actualLimit > 0 ? Math.floor(offset / actualLimit) + 1 : 1;\r\n const pageCount = actualLimit > 0 ? Math.ceil(total / actualLimit) : 0;\r\n\r\n return {\r\n data,\r\n count,\r\n total,\r\n page,\r\n pageCount,\r\n };\r\n}\r\n","import { QueryAdapter } from '../../models/query-adapter';\r\nimport { CrudRequest, CrudRequestOrder, ParsedRequestSelect } from '../../models/crud-request';\r\nimport { GetManyResult } from '../../models/get-many-result';\r\nimport { CrudRequestWhere, CrudRequestWhereOperator, CrudRequestWhereValueType } from '../../models/crud-request-where';\r\nimport { ensureArray, ensurePrimitive, ensureString, getOffset, isValid } from '../../utils/functions';\r\nimport { pathGetValue, pathSetValue } from '../../utils/field-path';\r\nimport { createGetManyResult } from '../../utils/objects';\r\n\r\nexport interface ArrayQueryAdapterOptions<T> {\r\n /**\r\n * Creates an empty entity.\r\n * This will be called when the request `select` list is not empty.\r\n * This can be used to instantiate an entity class.\r\n * If not defined, it will create a plain object instead.\r\n *\r\n * @param existing The existing entity, which will be replaced by the created one\r\n */\r\n createEmptyEntity?: (existing: T) => T;\r\n}\r\n\r\n/**\r\n * Adapts queries to plain JS arrays\r\n */\r\nexport class ArrayQueryAdapter<T extends object> implements QueryAdapter<T[], T> {\r\n\r\n constructor(\r\n protected readonly options: ArrayQueryAdapterOptions<T> = {},\r\n ) {}\r\n\r\n /**\r\n * @inheritDoc\r\n */\r\n public build(data: T[], request: CrudRequest): T[] {\r\n data = this.applyWhere(data, request.where);\r\n data = this.applyOrder(data, request.order);\r\n data = this.applyLimits(data, getOffset(request.offset, request.limit, request.page), request.limit);\r\n data = this.applySelect(data, request.select);\r\n\r\n return data;\r\n }\r\n\r\n /**\r\n * @inheritDoc\r\n */\r\n public async getMany<E extends T = T>(data: T[], request: CrudRequest): Promise<GetManyResult<E>> {\r\n data = this.applyWhere(data, request.where);\r\n data = this.applyOrder(data, request.order);\r\n\r\n const total = data.length;\r\n const offset = getOffset(request.offset, request.limit, request.page);\r\n const limit = request.limit || total;\r\n\r\n data = this.applyLimits(data, offset, limit);\r\n data = this.applySelect(data, request.select);\r\n\r\n return createGetManyResult(data as E[], total, offset, limit);\r\n }\r\n\r\n /**\r\n * @inheritDoc\r\n */\r\n public async getOne<E extends T = T>(data: T[], request: CrudRequest): Promise<E | null> {\r\n data = this.applyWhere(data, request.where);\r\n data = this.applyOrder(data, request.order);\r\n data = data.slice(0, 1);\r\n data = this.applySelect(data, request.select);\r\n\r\n if (data.length === 0)\r\n return null;\r\n\r\n return data[0] as E;\r\n }\r\n\r\n /**\r\n * Creates new objects containing only the select fields\r\n *\r\n * @param data The original data\r\n * @param select The fields to keep\r\n */\r\n protected applySelect(data: T[], select: ParsedRequestSelect): T[] {\r\n if (select.length === 0)\r\n return data;\r\n\r\n return data.map(item => {\r\n const newObject = this.options.createEmptyEntity?.(item) ?? <T>{};\r\n\r\n for (const field of select) {\r\n pathSetValue(newObject, field.field, pathGetValue(item, field.field));\r\n }\r\n\r\n return newObject;\r\n })\r\n }\r\n\r\n /**\r\n * Filters the data based on where conditions\r\n *\r\n * @param data The original data\r\n * @param where The where conditions\r\n */\r\n protected applyWhere(data: T[], where: CrudRequestWhere): T[] {\r\n return data.filter(item => this.checkWhere(item, where));\r\n }\r\n\r\n /**\r\n * Sorts the data based on the order list\r\n *\r\n * @param data The original data\r\n * @param order The ordering rules\r\n */\r\n protected applyOrder(data: T[], order: CrudRequestOrder[]): T[] {\r\n return data.sort((a, b) => {\r\n for (const o of order) {\r\n const valueA = pathGetValue(a, o.field);\r\n const valueB = pathGetValue(b, o.field);\r\n\r\n let comparison = this.compareOrder(valueA, valueB);\r\n\r\n if (o.order === 'DESC')\r\n comparison = -comparison;\r\n\r\n if (comparison !== 0)\r\n return comparison;\r\n }\r\n\r\n return 0;\r\n });\r\n }\r\n\r\n /**\r\n * Slices the data based on the offset and limit\r\n *\r\n * @param data The original data\r\n * @param offset The offset\r\n * @param limit The limit\r\n */\r\n protected applyLimits(data: T[], offset: number, limit: number | undefined): T[] {\r\n if (!offset && !limit)\r\n return data;\r\n\r\n return data.slice(offset, offset && limit ? offset + limit : limit);\r\n }\r\n\r\n /**\r\n * Compare two values.\r\n *\r\n * Returns positive when A is greater than B;\r\n * Returns negative when B is greater than A;\r\n * Returns 0 when both values are equivalent.\r\n *\r\n * @param a The first value\r\n * @param b The second value\r\n */\r\n protected compareOrder(a: any, b: any): number {\r\n if (typeof a === 'number' && typeof b === 'number')\r\n return a - b;\r\n\r\n if (typeof a === 'string' && typeof b === 'string')\r\n return a.localeCompare(b);\r\n\r\n if (typeof a === 'boolean' && typeof b === 'boolean')\r\n return +a - +b;\r\n\r\n return 0;\r\n }\r\n\r\n /**\r\n * Evaluates the where condition\r\n *\r\n * @param data The entity to check\r\n * @param where The where condition\r\n */\r\n protected checkWhere(data: T, where: CrudRequestWhere): boolean {\r\n if (where.and)\r\n return where.and.every(item => this.checkWhere(data, item));\r\n\r\n if (where.or)\r\n return where.or.some(item => this.checkWhere(data, item));\r\n\r\n if (where.field)\r\n return this.checkWhereOperator(pathGetValue(data, where.field), where.operator, where.value);\r\n\r\n return true;\r\n }\r\n\r\n /**\r\n * Evaluates a where operator\r\n *\r\n * @param item The left value to check\r\n * @param operator The operator\r\n * @param value The right value to check\r\n */\r\n protected checkWhereOperator(item: any, operator: CrudRequestWhereOperator, value: CrudRequestWhereValueType | CrudRequestWhereValueType[]): boolean {\r\n switch (operator) {\r\n case CrudRequestWhereOperator.EQ:\r\n return item == value;\r\n\r\n case CrudRequestWhereOperator.NEQ:\r\n return item != value;\r\n\r\n case CrudRequestWhereOperator.GT:\r\n return item > ensurePrimitive('> operator', value);\r\n\r\n case CrudRequestWhereOperator.GTE:\r\n return item >= ensurePrimitive('>= operator', value);\r\n\r\n case CrudRequestWhereOperator.LT:\r\n return item < ensurePrimitive('< operator', value);\r\n\r\n case CrudRequestWhereOperator.LTE:\r\n return item <= ensurePrimitive('<= operator', value);\r\n\r\n case CrudRequestWhereOperator.BETWEEN:\r\n const arr = ensureArray('BETWEEN operator', value, 2);\r\n\r\n return item >= ensurePrimitive('left BETWEEN value', arr[0]) &&\r\n item <= ensurePrimitive('right BETWEEN value', arr[1]);\r\n\r\n case CrudRequestWhereOperator.IS_NULL:\r\n return !isValid(item);\r\n\r\n case CrudRequestWhereOperator.NOT_NULL:\r\n return isValid(item);\r\n\r\n case CrudRequestWhereOperator.CONTAINS:\r\n return typeof item !== 'string' ? false :\r\n item.includes(ensureString('CONTAINS', value));\r\n\r\n case CrudRequestWhereOperator.NOT_CONTAINS:\r\n return typeof item !== 'string' ? true :\r\n !item.includes(ensureString('NOT CONTAINS', value));\r\n\r\n case CrudRequestWhereOperator.STARTS:\r\n return typeof item !== 'string' ? false :\r\n item.startsWith(ensureString('STARTS', value));\r\n\r\n case CrudRequestWhereOperator.ENDS:\r\n return typeof item !== 'string' ? false :\r\n item.endsWith(ensureString('ENDS', value));\r\n\r\n case CrudRequestWhereOperator.IN:\r\n return ensureArray('IN', value).includes(item);\r\n\r\n case CrudRequestWhereOperator.NOT_IN:\r\n return !ensureArray('NOT IN', value).includes(item);\r\n\r\n case CrudRequestWhereOperator.EQ_LOWER:\r\n return item?.toString().toLowerCase() == ensureString('== LOWER', value).toLowerCase();\r\n\r\n case CrudRequestWhereOperator.NEQ_LOWER:\r\n return item?.toString().toLowerCase() != ensureString('!= LOWER', value).toLowerCase();\r\n\r\n case CrudRequestWhereOperator.CONTAINS_LOWER:\r\n return typeof item !== 'string' ? false :\r\n item.toLowerCase().includes(ensureString('CONTAINS LOWER', value).toLowerCase());\r\n\r\n case CrudRequestWhereOperator.NOT_CONTAINS_LOWER:\r\n return typeof item !== 'string' ? true :\r\n !item.toLowerCase().includes(ensureString('NOT CONTAINS LOWER', value).toLowerCase());\r\n\r\n case CrudRequestWhereOperator.STARTS_LOWER:\r\n return typeof item !== 'string' ? false :\r\n item.toLowerCase().startsWith(ensureString('STARTS LOWER', value).toLowerCase());\r\n\r\n case CrudRequestWhereOperator.ENDS_LOWER:\r\n return typeof item !== 'string' ? false :\r\n item.toLowerCase().endsWith(ensureString('ENDS LOWER', value).toLowerCase());\r\n\r\n case CrudRequestWhereOperator.IN_LOWER: {\r\n const val = item?.toString().toLowerCase();\r\n\r\n return ensureArray('IN LOWER', value).some(elem => val === elem?.toString().toLowerCase());\r\n }\r\n\r\n case CrudRequestWhereOperator.NOT_IN_LOWER: {\r\n const val = item?.toString().toLowerCase();\r\n\r\n return !ensureArray('NOT IN LOWER', value).some(elem => val === elem?.toString().toLowerCase());\r\n }\r\n }\r\n\r\n throw new Error(`Unsupported operator \"${operator}\"`);\r\n }\r\n}\r\n"],"mappings":"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","names":["ensurePrimitive","fieldName","data","__name","ensureString","fieldName","data","__name","ensureArray","minLength","isValid","value","__name","getOffset","offset","limit","page","pathGetValue","obj","field","value","i","name","isValid","__name","pathSetValue","self","createGetManyResult","data","total","offset","limit","count","actualLimit","page","pageCount","__name","ArrayQueryAdapter","options","__name","data","request","getOffset","total","offset","limit","createGetManyResult","select","item","newObject","field","pathSetValue","pathGetValue","where","order","a","b","o","valueA","valueB","comparison","operator","value","ensurePrimitive","arr","ensureArray","isValid","ensureString","val","elem"]}