@zola_do/crud 0.2.15 → 0.2.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +88 -0
- package/dist/shared/exceptions/global-exception.filter.d.ts +5 -0
- package/dist/shared/exceptions/global-exception.filter.js +16 -5
- package/dist/shared/exceptions/global-exception.filter.js.map +1 -1
- package/dist/shared/utils/get-client-ip.helper.d.ts +12 -0
- package/dist/shared/utils/get-client-ip.helper.js +26 -0
- package/dist/shared/utils/get-client-ip.helper.js.map +1 -0
- package/dist/shared/utils/index.d.ts +1 -0
- package/dist/shared/utils/index.js +1 -0
- package/dist/shared/utils/index.js.map +1 -1
- package/dist/shared/utils/redis-idempotency.store.d.ts +12 -0
- package/dist/shared/utils/redis-idempotency.store.js +27 -0
- package/dist/shared/utils/redis-idempotency.store.js.map +1 -0
- package/dist/shared/utils/validate-dto.js +2 -1
- package/dist/shared/utils/validate-dto.js.map +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -774,6 +774,94 @@ export class OrdersModule {}
|
|
|
774
774
|
|
|
775
775
|
Requires `@nestjs/microservices` peer dependency.
|
|
776
776
|
|
|
777
|
+
## Advanced features
|
|
778
|
+
|
|
779
|
+
### Bulk create and bulk delete
|
|
780
|
+
|
|
781
|
+
Enable batch endpoints on `EntityCrudController`:
|
|
782
|
+
|
|
783
|
+
```typescript
|
|
784
|
+
@EntityCrudController(
|
|
785
|
+
{
|
|
786
|
+
enableBulkCreate: true,
|
|
787
|
+
enableBulkDelete: true,
|
|
788
|
+
bulkCreatePermission: 'products:create',
|
|
789
|
+
bulkDeletePermission: 'products:delete',
|
|
790
|
+
},
|
|
791
|
+
Product,
|
|
792
|
+
)
|
|
793
|
+
export class ProductController extends EntityCrudControllerHost {}
|
|
794
|
+
```
|
|
795
|
+
|
|
796
|
+
- `POST /products/bulk` — body `{ items: [...] }`
|
|
797
|
+
- `POST /products/bulk-delete` — body `{ ids: string[] }`
|
|
798
|
+
- Disable per operation with `operations.bulkCreate` / `operations.bulkDelete` (`hide` | `block`)
|
|
799
|
+
|
|
800
|
+
### Create idempotency
|
|
801
|
+
|
|
802
|
+
Register a `CrudIdempotencyStore` and enable on entity options:
|
|
803
|
+
|
|
804
|
+
```typescript
|
|
805
|
+
import {
|
|
806
|
+
CRUD_IDEMPOTENCY_STORE,
|
|
807
|
+
InMemoryCrudIdempotencyStore,
|
|
808
|
+
} from '@zola_do/crud';
|
|
809
|
+
|
|
810
|
+
@Module({
|
|
811
|
+
providers: [
|
|
812
|
+
{
|
|
813
|
+
provide: CRUD_IDEMPOTENCY_STORE,
|
|
814
|
+
useClass: InMemoryCrudIdempotencyStore, // swap for Redis in production
|
|
815
|
+
},
|
|
816
|
+
],
|
|
817
|
+
})
|
|
818
|
+
export class AppModule {}
|
|
819
|
+
|
|
820
|
+
@EntityCrudController({ idempotency: { enabled: true, ttlMs: 86_400_000 } }, Order)
|
|
821
|
+
export class OrderController extends EntityCrudControllerHost {}
|
|
822
|
+
```
|
|
823
|
+
|
|
824
|
+
Clients send `Idempotency-Key` (configurable via `headerName`). Replays return the stored create response.
|
|
825
|
+
|
|
826
|
+
### Optimistic concurrency
|
|
827
|
+
|
|
828
|
+
```typescript
|
|
829
|
+
@EntityCrudController({ optimisticConcurrency: true }, Product)
|
|
830
|
+
export class ProductController extends EntityCrudControllerHost {}
|
|
831
|
+
```
|
|
832
|
+
|
|
833
|
+
Updates require a version field on the entity; conflicts return HTTP 409.
|
|
834
|
+
|
|
835
|
+
### Cursor pagination (plain query params)
|
|
836
|
+
|
|
837
|
+
CRUD list endpoints accept compact `?q=` (see `@zola_do/collection-query`) **or** plain params:
|
|
838
|
+
|
|
839
|
+
```
|
|
840
|
+
GET /products?cursor=<token>&limit=20&direction=next
|
|
841
|
+
GET /products?cursor=<token>&limit=20&direction=prev
|
|
842
|
+
```
|
|
843
|
+
|
|
844
|
+
Response may include cursor metadata alongside `total` / `items` when cursor mode is active.
|
|
845
|
+
|
|
846
|
+
### `collectionQueryDefaults`
|
|
847
|
+
|
|
848
|
+
Apply default column allowlists for every list on a controller:
|
|
849
|
+
|
|
850
|
+
```typescript
|
|
851
|
+
@EntityCrudController(
|
|
852
|
+
{
|
|
853
|
+
collectionQueryDefaults: {
|
|
854
|
+
allowedFilterColumns: ['name', 'status', 'createdAt'],
|
|
855
|
+
allowedSortColumns: ['name', 'createdAt'],
|
|
856
|
+
},
|
|
857
|
+
},
|
|
858
|
+
Product,
|
|
859
|
+
)
|
|
860
|
+
export class ProductController extends EntityCrudControllerHost {}
|
|
861
|
+
```
|
|
862
|
+
|
|
863
|
+
Unset allowlists skip enforcement — set defaults for defense in depth.
|
|
864
|
+
|
|
777
865
|
## Troubleshooting
|
|
778
866
|
|
|
779
867
|
### Q: How do I add custom methods to the CRUD service?
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import { ArgumentsHost, ExceptionFilter } from '@nestjs/common';
|
|
2
|
+
export type GlobalExceptionFilterOptions = {
|
|
3
|
+
includePath?: boolean;
|
|
4
|
+
};
|
|
2
5
|
export declare class GlobalExceptionFilter implements ExceptionFilter {
|
|
3
6
|
private readonly logger;
|
|
7
|
+
private readonly includePath;
|
|
8
|
+
constructor(options?: GlobalExceptionFilterOptions);
|
|
4
9
|
catch(exception: any, host: ArgumentsHost): void;
|
|
5
10
|
}
|
|
@@ -5,10 +5,14 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
5
5
|
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
6
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
7
|
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
8
11
|
var GlobalExceptionFilter_1;
|
|
9
12
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
13
|
exports.GlobalExceptionFilter = void 0;
|
|
11
14
|
const common_1 = require("@nestjs/common");
|
|
15
|
+
const get_client_ip_helper_1 = require("../utils/get-client-ip.helper");
|
|
12
16
|
function normalizeHttpExceptionMessage(exception) {
|
|
13
17
|
const res = exception.getResponse();
|
|
14
18
|
if (typeof res === 'string') {
|
|
@@ -26,11 +30,12 @@ function normalizeHttpExceptionMessage(exception) {
|
|
|
26
30
|
return exception.message;
|
|
27
31
|
}
|
|
28
32
|
let GlobalExceptionFilter = GlobalExceptionFilter_1 = class GlobalExceptionFilter {
|
|
29
|
-
constructor() {
|
|
33
|
+
constructor(options = {}) {
|
|
30
34
|
this.logger = new common_1.Logger(GlobalExceptionFilter_1.name);
|
|
35
|
+
this.includePath = options.includePath !== false;
|
|
31
36
|
}
|
|
32
37
|
catch(exception, host) {
|
|
33
|
-
var _a, _b, _c
|
|
38
|
+
var _a, _b, _c;
|
|
34
39
|
const ctx = host.switchToHttp();
|
|
35
40
|
const response = ctx.getResponse();
|
|
36
41
|
const request = ctx.getRequest();
|
|
@@ -42,6 +47,9 @@ let GlobalExceptionFilter = GlobalExceptionFilter_1 = class GlobalExceptionFilte
|
|
|
42
47
|
let message;
|
|
43
48
|
if (isHttp) {
|
|
44
49
|
message = normalizeHttpExceptionMessage(exception);
|
|
50
|
+
if (isProd && status >= common_1.HttpStatus.INTERNAL_SERVER_ERROR) {
|
|
51
|
+
message = 'Internal server error';
|
|
52
|
+
}
|
|
45
53
|
}
|
|
46
54
|
else {
|
|
47
55
|
const err = exception;
|
|
@@ -52,17 +60,20 @@ let GlobalExceptionFilter = GlobalExceptionFilter_1 = class GlobalExceptionFilte
|
|
|
52
60
|
const responseData = {
|
|
53
61
|
statusCode: status,
|
|
54
62
|
message,
|
|
55
|
-
path: request.url,
|
|
56
63
|
timestamp: new Date().toISOString(),
|
|
57
64
|
};
|
|
65
|
+
if (this.includePath) {
|
|
66
|
+
responseData.path = request.url;
|
|
67
|
+
}
|
|
58
68
|
const err = exception instanceof Error ? exception : undefined;
|
|
59
|
-
this.logger.error(Object.assign(Object.assign({}, responseData), { exceptionMessage: err === null || err === void 0 ? void 0 : err.message, stack: err === null || err === void 0 ? void 0 : err.stack, name: err === null || err === void 0 ? void 0 : err.name, remoteAddress: (_b = (_a = request.socket) === null || _a === void 0 ? void 0 : _a.remoteAddress) !== null && _b !== void 0 ? _b : (_c = request.connection) === null || _c === void 0 ? void 0 : _c.remoteAddress
|
|
69
|
+
this.logger.error(Object.assign(Object.assign({}, responseData), { exceptionMessage: err === null || err === void 0 ? void 0 : err.message, stack: err === null || err === void 0 ? void 0 : err.stack, name: err === null || err === void 0 ? void 0 : err.name, clientIp: (0, get_client_ip_helper_1.getClientIp)(request), remoteAddress: (_b = (_a = request.socket) === null || _a === void 0 ? void 0 : _a.remoteAddress) !== null && _b !== void 0 ? _b : (_c = request.connection) === null || _c === void 0 ? void 0 : _c.remoteAddress }));
|
|
60
70
|
response.status(status).json(responseData);
|
|
61
71
|
return;
|
|
62
72
|
}
|
|
63
73
|
};
|
|
64
74
|
exports.GlobalExceptionFilter = GlobalExceptionFilter;
|
|
65
75
|
exports.GlobalExceptionFilter = GlobalExceptionFilter = GlobalExceptionFilter_1 = __decorate([
|
|
66
|
-
(0, common_1.Catch)()
|
|
76
|
+
(0, common_1.Catch)(),
|
|
77
|
+
__metadata("design:paramtypes", [Object])
|
|
67
78
|
], GlobalExceptionFilter);
|
|
68
79
|
//# sourceMappingURL=global-exception.filter.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"global-exception.filter.js","sourceRoot":"","sources":["../../../src/shared/exceptions/global-exception.filter.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"global-exception.filter.js","sourceRoot":"","sources":["../../../src/shared/exceptions/global-exception.filter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export type ClientIpRequest = {
|
|
2
|
+
ip?: string;
|
|
3
|
+
ips?: string[];
|
|
4
|
+
headers?: Record<string, string | string[] | undefined>;
|
|
5
|
+
socket?: {
|
|
6
|
+
remoteAddress?: string;
|
|
7
|
+
};
|
|
8
|
+
connection?: {
|
|
9
|
+
remoteAddress?: string;
|
|
10
|
+
};
|
|
11
|
+
};
|
|
12
|
+
export declare function getClientIp(req: ClientIpRequest): string;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getClientIp = getClientIp;
|
|
4
|
+
function getClientIp(req) {
|
|
5
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
6
|
+
if ((_a = req.ips) === null || _a === void 0 ? void 0 : _a.length) {
|
|
7
|
+
return req.ips[0];
|
|
8
|
+
}
|
|
9
|
+
if (req.ip) {
|
|
10
|
+
return req.ip;
|
|
11
|
+
}
|
|
12
|
+
const xff = (_b = req.headers) === null || _b === void 0 ? void 0 : _b['x-forwarded-for'];
|
|
13
|
+
if (xff) {
|
|
14
|
+
const raw = Array.isArray(xff) ? xff[0] : xff;
|
|
15
|
+
const first = (_c = raw.split(',')[0]) === null || _c === void 0 ? void 0 : _c.trim();
|
|
16
|
+
if (first) {
|
|
17
|
+
return first;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
const realIp = (_d = req.headers) === null || _d === void 0 ? void 0 : _d['x-real-ip'];
|
|
21
|
+
if (realIp) {
|
|
22
|
+
return Array.isArray(realIp) ? realIp[0] : realIp;
|
|
23
|
+
}
|
|
24
|
+
return ((_h = (_f = (_e = req.socket) === null || _e === void 0 ? void 0 : _e.remoteAddress) !== null && _f !== void 0 ? _f : (_g = req.connection) === null || _g === void 0 ? void 0 : _g.remoteAddress) !== null && _h !== void 0 ? _h : 'unknown');
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=get-client-ip.helper.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"get-client-ip.helper.js","sourceRoot":"","sources":["../../../src/shared/utils/get-client-ip.helper.ts"],"names":[],"mappings":";;AAaA,kCA4BC;AA5BD,SAAgB,WAAW,CAAC,GAAoB;;IAC9C,IAAI,MAAA,GAAG,CAAC,GAAG,0CAAE,MAAM,EAAE,CAAC;QACpB,OAAO,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;IACpB,CAAC;IAED,IAAI,GAAG,CAAC,EAAE,EAAE,CAAC;QACX,OAAO,GAAG,CAAC,EAAE,CAAC;IAChB,CAAC;IAED,MAAM,GAAG,GAAG,MAAA,GAAG,CAAC,OAAO,0CAAG,iBAAiB,CAAC,CAAC;IAC7C,IAAI,GAAG,EAAE,CAAC;QACR,MAAM,GAAG,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;QAC9C,MAAM,KAAK,GAAG,MAAA,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,0CAAE,IAAI,EAAE,CAAC;QACxC,IAAI,KAAK,EAAE,CAAC;YACV,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;IAED,MAAM,MAAM,GAAG,MAAA,GAAG,CAAC,OAAO,0CAAG,WAAW,CAAC,CAAC;IAC1C,IAAI,MAAM,EAAE,CAAC;QACX,OAAO,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;IACpD,CAAC;IAED,OAAO,CACL,MAAA,MAAA,MAAA,GAAG,CAAC,MAAM,0CAAE,aAAa,mCACzB,MAAA,GAAG,CAAC,UAAU,0CAAE,aAAa,mCAC7B,SAAS,CACV,CAAC;AACJ,CAAC"}
|
|
@@ -4,5 +4,6 @@ export * from './crud-hooks.helper';
|
|
|
4
4
|
export * from './cursor-query.helper';
|
|
5
5
|
export * from './collection-query-merge';
|
|
6
6
|
export * from './in-memory-idempotency.store';
|
|
7
|
+
export * from './redis-idempotency.store';
|
|
7
8
|
export * from './transactional-repository.helper';
|
|
8
9
|
export * from './crud-transaction.decorators';
|
|
@@ -20,6 +20,7 @@ __exportStar(require("./crud-hooks.helper"), exports);
|
|
|
20
20
|
__exportStar(require("./cursor-query.helper"), exports);
|
|
21
21
|
__exportStar(require("./collection-query-merge"), exports);
|
|
22
22
|
__exportStar(require("./in-memory-idempotency.store"), exports);
|
|
23
|
+
__exportStar(require("./redis-idempotency.store"), exports);
|
|
23
24
|
__exportStar(require("./transactional-repository.helper"), exports);
|
|
24
25
|
__exportStar(require("./crud-transaction.decorators"), exports);
|
|
25
26
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/shared/utils/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,wDAAsC;AACtC,iDAA+B;AAC/B,sDAAoC;AACpC,wDAAsC;AACtC,2DAAyC;AACzC,gEAA8C;AAC9C,oEAAkD;AAClD,gEAA8C"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/shared/utils/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,wDAAsC;AACtC,iDAA+B;AAC/B,sDAAoC;AACpC,wDAAsC;AACtC,2DAAyC;AACzC,gEAA8C;AAC9C,4DAA0C;AAC1C,oEAAkD;AAClD,gEAA8C"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { CrudIdempotencyStore } from '../types/crud-idempotency-store.interface';
|
|
2
|
+
export type RedisIdempotencyClient = {
|
|
3
|
+
get(key: string): Promise<string | null>;
|
|
4
|
+
set(key: string, value: string, mode: 'PX', ttlMs: number): Promise<unknown>;
|
|
5
|
+
};
|
|
6
|
+
export declare class RedisCrudIdempotencyStore implements CrudIdempotencyStore {
|
|
7
|
+
private readonly redis;
|
|
8
|
+
private readonly keyPrefix;
|
|
9
|
+
constructor(redis: RedisIdempotencyClient, keyPrefix?: string);
|
|
10
|
+
get(key: string): Promise<any | undefined>;
|
|
11
|
+
set(key: string, body: any, ttlMs?: number): Promise<void>;
|
|
12
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.RedisCrudIdempotencyStore = void 0;
|
|
4
|
+
class RedisCrudIdempotencyStore {
|
|
5
|
+
constructor(redis, keyPrefix = 'crud:idempotency:') {
|
|
6
|
+
this.redis = redis;
|
|
7
|
+
this.keyPrefix = keyPrefix;
|
|
8
|
+
}
|
|
9
|
+
async get(key) {
|
|
10
|
+
const raw = await this.redis.get(`${this.keyPrefix}${key}`);
|
|
11
|
+
if (!raw) {
|
|
12
|
+
return undefined;
|
|
13
|
+
}
|
|
14
|
+
try {
|
|
15
|
+
return JSON.parse(raw);
|
|
16
|
+
}
|
|
17
|
+
catch (_a) {
|
|
18
|
+
return undefined;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
async set(key, body, ttlMs) {
|
|
22
|
+
const ttl = ttlMs && ttlMs > 0 ? ttlMs : 86400000;
|
|
23
|
+
await this.redis.set(`${this.keyPrefix}${key}`, JSON.stringify(body), 'PX', ttl);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
exports.RedisCrudIdempotencyStore = RedisCrudIdempotencyStore;
|
|
27
|
+
//# sourceMappingURL=redis-idempotency.store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redis-idempotency.store.js","sourceRoot":"","sources":["../../../src/shared/utils/redis-idempotency.store.ts"],"names":[],"mappings":";;;AAWA,MAAa,yBAAyB;IACpC,YACmB,KAA6B,EAC7B,YAAY,mBAAmB;QAD/B,UAAK,GAAL,KAAK,CAAwB;QAC7B,cAAS,GAAT,SAAS,CAAsB;IAC/C,CAAC;IAEJ,KAAK,CAAC,GAAG,CAAC,GAAW;QACnB,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,SAAS,GAAG,GAAG,EAAE,CAAC,CAAC;QAC5D,IAAI,CAAC,GAAG,EAAE,CAAC;YACT,OAAO,SAAS,CAAC;QACnB,CAAC;QACD,IAAI,CAAC;YACH,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACzB,CAAC;QAAC,WAAM,CAAC;YACP,OAAO,SAAS,CAAC;QACnB,CAAC;IACH,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,GAAW,EAAE,IAAS,EAAE,KAAc;QAC9C,MAAM,GAAG,GAAG,KAAK,IAAI,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,QAAU,CAAC;QACpD,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAClB,GAAG,IAAI,CAAC,SAAS,GAAG,GAAG,EAAE,EACzB,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EACpB,IAAI,EACJ,GAAG,CACJ,CAAC;IACJ,CAAC;CACF;AA3BD,8DA2BC"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.validateDto = validateDto;
|
|
4
|
+
const common_1 = require("@nestjs/common");
|
|
4
5
|
const class_transformer_1 = require("class-transformer");
|
|
5
6
|
const class_validator_1 = require("class-validator");
|
|
6
7
|
async function validateDto(dto, itemData) {
|
|
@@ -8,7 +9,7 @@ async function validateDto(dto, itemData) {
|
|
|
8
9
|
const errors = (0, class_validator_1.validateSync)(instance);
|
|
9
10
|
const errorMessages = errors.flatMap((e) => Object.values(e.constraints));
|
|
10
11
|
if (errorMessages.length > 0) {
|
|
11
|
-
throw errorMessages;
|
|
12
|
+
throw new common_1.BadRequestException({ errors: errorMessages });
|
|
12
13
|
}
|
|
13
14
|
}
|
|
14
15
|
//# sourceMappingURL=validate-dto.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validate-dto.js","sourceRoot":"","sources":["../../../src/shared/utils/validate-dto.ts"],"names":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"validate-dto.js","sourceRoot":"","sources":["../../../src/shared/utils/validate-dto.ts"],"names":[],"mappings":";;AAIA,kCAOC;AAXD,2CAAqD;AACrD,yDAAoD;AACpD,qDAA+C;AAExC,KAAK,UAAU,WAAW,CAAC,GAAQ,EAAE,QAAa;IACvD,MAAM,QAAQ,GAAG,IAAA,mCAAe,EAAC,GAAG,EAAE,QAAQ,CAAQ,CAAC;IACvD,MAAM,MAAM,GAAG,IAAA,8BAAY,EAAC,QAAQ,CAAC,CAAC;IACtC,MAAM,aAAa,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC;IAC1E,IAAI,aAAa,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC7B,MAAM,IAAI,4BAAmB,CAAC,EAAE,MAAM,EAAE,aAAa,EAAE,CAAC,CAAC;IAC3D,CAAC;AACH,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zola_do/crud",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.16",
|
|
4
4
|
"description": "Generic CRUD controllers and services for NestJS",
|
|
5
5
|
"author": "zolaDO",
|
|
6
6
|
"license": "ISC",
|
|
@@ -78,8 +78,8 @@
|
|
|
78
78
|
}
|
|
79
79
|
},
|
|
80
80
|
"dependencies": {
|
|
81
|
-
"@zola_do/collection-query": "^0.2.
|
|
82
|
-
"@zola_do/authorization": "^0.2.
|
|
81
|
+
"@zola_do/collection-query": "^0.2.15",
|
|
82
|
+
"@zola_do/authorization": "^0.2.15"
|
|
83
83
|
},
|
|
84
84
|
"devDependencies": {
|
|
85
85
|
"rimraf": "^6.1.3",
|