@invariant--labs/foundation 1.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.pnp.cjs +22192 -0
- package/.pnp.loader.mjs +2126 -0
- package/.yarnrc.yml +1 -0
- package/CHANGELOG.md +527 -0
- package/README.md +3 -0
- package/eslint.config.mjs +52 -0
- package/invariant.json +22 -0
- package/jest/jest.config.base.ts +30 -0
- package/jest/jest.spec.base.ts +7 -0
- package/jest.config.js +49 -0
- package/package.json +99 -0
- package/src/core/application/index.ts +1 -0
- package/src/core/application/persistence/index.ts +3 -0
- package/src/core/application/persistence/query-builder.ts +2 -0
- package/src/core/application/persistence/read-projection.ts +2 -0
- package/src/core/application/persistence/repository.ts +23 -0
- package/src/core/domain/entities/aggregate-root.ts +34 -0
- package/src/core/domain/entities/entity.spec.ts +43 -0
- package/src/core/domain/entities/entity.ts +29 -0
- package/src/core/domain/entities/identifier.spec.ts +34 -0
- package/src/core/domain/entities/identifier.ts +16 -0
- package/src/core/domain/entities/index.ts +5 -0
- package/src/core/domain/entities/projection.ts +7 -0
- package/src/core/domain/entities/unique-entity-id.ts +9 -0
- package/src/core/domain/events/domain-event.ts +7 -0
- package/src/core/domain/events/index.ts +1 -0
- package/src/core/domain/index.ts +3 -0
- package/src/core/domain/value-objects/index.ts +2 -0
- package/src/core/domain/value-objects/range.ts +4 -0
- package/src/core/domain/value-objects/value-object.spec.ts +45 -0
- package/src/core/domain/value-objects/value-object.ts +17 -0
- package/src/core/errors/command-failure.error.spec.ts +30 -0
- package/src/core/errors/command-failure.error.ts +9 -0
- package/src/core/errors/command-filter.error.ts +3 -0
- package/src/core/errors/detailed.error.ts +25 -0
- package/src/core/errors/domain.error.spec.ts +27 -0
- package/src/core/errors/domain.error.ts +9 -0
- package/src/core/errors/entity-not-found.error.spec.ts +32 -0
- package/src/core/errors/entity-not-found.error.ts +9 -0
- package/src/core/errors/fake-implementation.error.spec.ts +27 -0
- package/src/core/errors/fake-implementation.error.ts +15 -0
- package/src/core/errors/index.ts +8 -0
- package/src/core/errors/query-failure.error.ts +8 -0
- package/src/core/errors/too-many-results.error.ts +3 -0
- package/src/core/index.ts +4 -0
- package/src/core/infrastructure/config/config.provider.ts +78 -0
- package/src/core/infrastructure/config/config.service.ts +25 -0
- package/src/core/infrastructure/config/index.ts +2 -0
- package/src/core/infrastructure/messaging/fake/fake-messaging.service.ts +17 -0
- package/src/core/infrastructure/messaging/fake/fake-queue-manager.service.ts +9 -0
- package/src/core/infrastructure/messaging/fake/fake-queue-messaging.service.ts +9 -0
- package/src/core/infrastructure/messaging/fake/index.ts +3 -0
- package/src/core/infrastructure/messaging/index.ts +6 -0
- package/src/core/infrastructure/messaging/messaging.service.ts +3 -0
- package/src/core/infrastructure/messaging/queue-manager.service.ts +6 -0
- package/src/core/infrastructure/messaging/queue-messaging.service.ts +3 -0
- package/src/core/infrastructure/messaging/rabbitmq/index.ts +2 -0
- package/src/core/infrastructure/messaging/rabbitmq/rabbit-messaging.service.ts +11 -0
- package/src/core/infrastructure/messaging/rabbitmq/rabbit-queue-messaging.service.ts +11 -0
- package/src/core/infrastructure/messaging/types.ts +28 -0
- package/src/core/infrastructure/persistence/errors/index.ts +2 -0
- package/src/core/infrastructure/persistence/errors/model-to-entity-conversion.error.ts +9 -0
- package/src/core/infrastructure/persistence/errors/persistence.error.ts +8 -0
- package/src/core/infrastructure/persistence/in-memory/fake.repository.ts +79 -0
- package/src/core/infrastructure/persistence/in-memory/in-memory.query-builder.ts +4 -0
- package/src/core/infrastructure/persistence/in-memory/in-memory.repository.spec.ts +50 -0
- package/src/core/infrastructure/persistence/in-memory/in-memory.repository.ts +81 -0
- package/src/core/infrastructure/persistence/in-memory/index.ts +3 -0
- package/src/core/infrastructure/persistence/index.ts +4 -0
- package/src/core/infrastructure/persistence/read-side/in-memory.query-builder.ts +4 -0
- package/src/core/infrastructure/persistence/read-side/index.ts +1 -0
- package/src/core/infrastructure/persistence/read-side/knex/index.ts +2 -0
- package/src/core/infrastructure/persistence/read-side/knex/knex-types.definition.ts +13 -0
- package/src/core/infrastructure/persistence/read-side/knex/knex.query-builder.ts +70 -0
- package/src/core/infrastructure/persistence/read-side/knex-query-builder.ts +70 -0
- package/src/core/infrastructure/persistence/read-side/knex-types.definition.ts +13 -0
- package/src/core/infrastructure/persistence/write-side/aggregate-typeorm-repository.ts +87 -0
- package/src/core/infrastructure/persistence/write-side/entity-typeorm-repository.ts +64 -0
- package/src/core/infrastructure/persistence/write-side/in-memory.repository.ts +82 -0
- package/src/core/infrastructure/persistence/write-side/index.ts +1 -0
- package/src/core/infrastructure/persistence/write-side/model-attributes.ts +4 -0
- package/src/core/infrastructure/persistence/write-side/orm-embedded-mapper.ts +11 -0
- package/src/core/infrastructure/persistence/write-side/orm-mapper.ts +11 -0
- package/src/core/infrastructure/persistence/write-side/typeorm/aggregate-typeorm.repository.ts +87 -0
- package/src/core/infrastructure/persistence/write-side/typeorm/entity-typeorm.repository.ts +64 -0
- package/src/core/infrastructure/persistence/write-side/typeorm/index.ts +5 -0
- package/src/core/infrastructure/persistence/write-side/typeorm/model-attributes.ts +4 -0
- package/src/core/infrastructure/persistence/write-side/typeorm/orm-embedded.mapper.ts +11 -0
- package/src/core/infrastructure/persistence/write-side/typeorm/orm.mapper.ts +11 -0
- package/src/core/types/architecture-layer.ts +52 -0
- package/src/core/types/array-element.ts +5 -0
- package/src/core/types/index.ts +2 -0
- package/src/index.ts +30 -0
- package/src/modules/config/config.module.ts +9 -0
- package/src/modules/config/index.ts +2 -0
- package/src/modules/graphql/index.ts +1 -0
- package/src/modules/graphql/paginated-response.object-type.ts +23 -0
- package/src/modules/healthcheck/healthcheck-out.dto.ts +43 -0
- package/src/modules/healthcheck/index.ts +1 -0
- package/src/modules/knex/index.ts +3 -0
- package/src/modules/knex/knex-core.module.ts +30 -0
- package/src/modules/knex/knex.decorator.ts +5 -0
- package/src/modules/knex/knex.interface.ts +12 -0
- package/src/modules/knex/knex.module.ts +14 -0
- package/src/modules/knex/knex.token.ts +2 -0
- package/src/modules/logger/app-logger.ts +28 -0
- package/src/modules/logger/index.ts +5 -0
- package/src/modules/logger/log.ts +26 -0
- package/src/modules/logger/logger.module.ts +47 -0
- package/src/modules/logger/logger.service.ts +131 -0
- package/src/modules/logger/transports/console-color.transport.ts +41 -0
- package/src/modules/logger/transports/console-json.transport.ts +23 -0
- package/src/modules/logger/transports/console.transport.ts +10 -0
- package/src/modules/logger/transports/fake.transport.ts +18 -0
- package/src/modules/logger/transports/index.ts +5 -0
- package/src/modules/logger/transports/logger-transport.ts +22 -0
- package/src/modules/messaging/index.ts +2 -0
- package/src/modules/messaging/messaging.module.ts +92 -0
- package/src/modules/queue/default-queue-name.resolver.ts +5 -0
- package/src/modules/queue/index.ts +3 -0
- package/src/modules/queue/queue.module.ts +73 -0
- package/src/modules/queue/rabbit-queue-manager.service.ts +67 -0
- package/src/nestjs/errors/handlers/error-handler.ts +38 -0
- package/src/nestjs/errors/handlers/error-handler.type.ts +23 -0
- package/src/nestjs/errors/handlers/generic-error-handler.ts +59 -0
- package/src/nestjs/errors/handlers/handle-errors.decorator.ts +43 -0
- package/src/nestjs/errors/handlers/index.ts +5 -0
- package/src/nestjs/errors/handlers/validation-error-handler.ts +46 -0
- package/src/nestjs/errors/index.ts +2 -0
- package/src/nestjs/errors/parsers/axios-metadata.parser.ts +21 -0
- package/src/nestjs/errors/parsers/error-metadata.definition.ts +1 -0
- package/src/nestjs/errors/parsers/index.ts +5 -0
- package/src/nestjs/errors/parsers/knex-metadata.parser.ts +18 -0
- package/src/nestjs/errors/parsers/typeorm-metadata.parser.ts +19 -0
- package/src/nestjs/errors/parsers/workos-metadata.parser.ts +17 -0
- package/src/nestjs/http/decorators/index.ts +1 -0
- package/src/nestjs/http/decorators/log-app-ctx.decorator.ts +32 -0
- package/src/nestjs/http/dtos/date-range.dto.ts +15 -0
- package/src/nestjs/http/dtos/index.ts +1 -0
- package/src/nestjs/http/filters/all-exceptions.filter.ts +92 -0
- package/src/nestjs/http/filters/command-failure.filter.ts +12 -0
- package/src/nestjs/http/filters/index.ts +4 -0
- package/src/nestjs/http/filters/rpc-exceptions.filter.ts +10 -0
- package/src/nestjs/http/filters/too-many-results.filter.ts +12 -0
- package/src/nestjs/http/index.ts +6 -0
- package/src/nestjs/http/interceptors/index.ts +2 -0
- package/src/nestjs/http/interceptors/log-app-ctx.interceptor.ts +109 -0
- package/src/nestjs/http/interceptors/serialize-output.interceptor.ts +19 -0
- package/src/nestjs/http/middleware/http-logger.middleware.ts +31 -0
- package/src/nestjs/http/middleware/index.ts +2 -0
- package/src/nestjs/http/middleware/logger.middleware.ts +21 -0
- package/src/nestjs/http/swagger/index.ts +1 -0
- package/src/nestjs/http/swagger/swagger.ts +44 -0
- package/src/nestjs/index.ts +2 -0
- package/src/testing/command-bus.stub.ts +33 -0
- package/src/testing/event-bus.stub.ts +56 -0
- package/src/testing/event-publisher.stub.ts +24 -0
- package/src/testing/fake-logger.ts +20 -0
- package/src/testing/index.ts +2 -0
- package/src/testing/query-bus.stub.ts +27 -0
- package/src/testing/stub.spec.ts +250 -0
- package/src/testing/stub.ts +170 -0
- package/src/testing/stubs/command-bus.stub.ts +33 -0
- package/src/testing/stubs/event-bus.stub.ts +56 -0
- package/src/testing/stubs/event-publisher.stub.ts +24 -0
- package/src/testing/stubs/index.ts +5 -0
- package/src/testing/stubs/query-bus.stub.ts +27 -0
- package/src/testing/stubs/stub.ts +170 -0
- package/src/utils/array.spec.ts +29 -0
- package/src/utils/array.ts +10 -0
- package/src/utils/base64.spec.ts +18 -0
- package/src/utils/base64.ts +6 -0
- package/src/utils/collection.ts +4 -0
- package/src/utils/common.ts +13 -0
- package/src/utils/csv.ts +49 -0
- package/src/utils/date/date-range.ts +4 -0
- package/src/utils/date/date.spec.ts +100 -0
- package/src/utils/date/date.ts +177 -0
- package/src/utils/date/diff.spec.ts +66 -0
- package/src/utils/date/diffYear.spec.ts +23 -0
- package/src/utils/date/fillMissingRangeValues.spec.ts +523 -0
- package/src/utils/date/groubBy.spec.ts +183 -0
- package/src/utils/date/index.ts +2 -0
- package/src/utils/date/isSame.spec.ts +111 -0
- package/src/utils/file.spec.ts +66 -0
- package/src/utils/file.ts +5 -0
- package/src/utils/hash-key.ts +23 -0
- package/src/utils/index.ts +14 -0
- package/src/utils/invariant.ts +3 -0
- package/src/utils/iso-date.ts +11 -0
- package/src/utils/object.spec.ts +18 -0
- package/src/utils/object.ts +6 -0
- package/src/utils/paginated.ts +36 -0
- package/src/utils/string.spec.ts +10 -0
- package/src/utils/string.ts +19 -0
- package/src/utils/type.ts +9 -0
- package/src/utils/xml.ts +6 -0
- package/src/validation/ensure-array.decorator.ts +5 -0
- package/src/validation/index.ts +7 -0
- package/src/validation/is-iso-date.decorator.spec.ts +29 -0
- package/src/validation/is-iso-date.decorator.ts +30 -0
- package/src/validation/is-less-than-or-equal.decorator.spec.ts +30 -0
- package/src/validation/is-less-than-or-equal.decorator.ts +52 -0
- package/src/validation/is-less-than.decorator.spec.ts +36 -0
- package/src/validation/is-less-than.decorator.ts +52 -0
- package/src/validation/is-more-than-or-equal.decorator.spec.ts +30 -0
- package/src/validation/is-more-than-or-equal.decorator.ts +52 -0
- package/src/validation/is-more-than.decorator.spec.ts +36 -0
- package/src/validation/is-more-than.decorator.ts +52 -0
- package/src/validation/is-time-string.decorator.spec.ts +35 -0
- package/src/validation/is-time-string.decorator.ts +29 -0
- package/tsconfig.build.json +6 -0
- package/tsconfig.json +34 -0
- package/tsconfig.spec.json +14 -0
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { isSame } from "./date";
|
|
2
|
+
|
|
3
|
+
const DATE = +new Date("2023-01-01");
|
|
4
|
+
|
|
5
|
+
const ONE_MS = 1;
|
|
6
|
+
const ONE_SECOND = ONE_MS * 1000;
|
|
7
|
+
const ONE_MINUTE = ONE_SECOND * 60;
|
|
8
|
+
const ONE_HOUR = ONE_MINUTE * 60;
|
|
9
|
+
const ONE_DAY = ONE_HOUR * 60;
|
|
10
|
+
const ONE_MONTH = ONE_DAY * 31;
|
|
11
|
+
const ONE_YEAR = ONE_MONTH * 12;
|
|
12
|
+
|
|
13
|
+
describe("isSame", () => {
|
|
14
|
+
describe("millisecond", () => {
|
|
15
|
+
it("should return true", () => {
|
|
16
|
+
const result = isSame(new Date(DATE), new Date(DATE), "millisecond");
|
|
17
|
+
|
|
18
|
+
expect(result).toEqual(true);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it("should return false", () => {
|
|
22
|
+
const result = isSame(new Date(DATE), new Date(DATE + ONE_MS), "millisecond");
|
|
23
|
+
|
|
24
|
+
expect(result).toEqual(false);
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
describe("second", () => {
|
|
29
|
+
it("should return true", () => {
|
|
30
|
+
const result = isSame(new Date(DATE), new Date(DATE + ONE_MS), "second");
|
|
31
|
+
|
|
32
|
+
expect(result).toEqual(true);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it("should return false", () => {
|
|
36
|
+
const result = isSame(new Date(DATE), new Date(DATE + ONE_SECOND), "second");
|
|
37
|
+
|
|
38
|
+
expect(result).toEqual(false);
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
describe("minute", () => {
|
|
43
|
+
it("should return true", () => {
|
|
44
|
+
const result = isSame(new Date(DATE), new Date(DATE + ONE_SECOND), "minute");
|
|
45
|
+
|
|
46
|
+
expect(result).toEqual(true);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it("should return false", () => {
|
|
50
|
+
const result = isSame(new Date(DATE), new Date(DATE + ONE_MINUTE), "minute");
|
|
51
|
+
|
|
52
|
+
expect(result).toEqual(false);
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
describe("hour", () => {
|
|
57
|
+
it("should return true", () => {
|
|
58
|
+
const result = isSame(new Date(DATE), new Date(DATE + ONE_MINUTE), "hour");
|
|
59
|
+
|
|
60
|
+
expect(result).toEqual(true);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it("should return false", () => {
|
|
64
|
+
const result = isSame(new Date(DATE), new Date(DATE + ONE_HOUR), "hour");
|
|
65
|
+
|
|
66
|
+
expect(result).toEqual(false);
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
describe("day", () => {
|
|
71
|
+
it("should return true", () => {
|
|
72
|
+
const result = isSame(new Date(DATE), new Date(DATE + ONE_HOUR), "day");
|
|
73
|
+
|
|
74
|
+
expect(result).toEqual(true);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it("should return false", () => {
|
|
78
|
+
const result = isSame(new Date(DATE), new Date(DATE + ONE_DAY), "day");
|
|
79
|
+
|
|
80
|
+
expect(result).toEqual(false);
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
describe("month", () => {
|
|
85
|
+
it("should return true", () => {
|
|
86
|
+
const result = isSame(new Date(DATE), new Date(DATE + ONE_DAY), "month");
|
|
87
|
+
|
|
88
|
+
expect(result).toEqual(true);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it("should return false", () => {
|
|
92
|
+
const result = isSame(new Date(DATE), new Date(DATE + ONE_MONTH), "month");
|
|
93
|
+
|
|
94
|
+
expect(result).toEqual(false);
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
describe("year", () => {
|
|
99
|
+
it("should return true", () => {
|
|
100
|
+
const result = isSame(new Date(DATE), new Date(DATE + ONE_MONTH), "year");
|
|
101
|
+
|
|
102
|
+
expect(result).toEqual(true);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it("should return false", () => {
|
|
106
|
+
const result = isSame(new Date(DATE), new Date(DATE + ONE_YEAR), "year");
|
|
107
|
+
|
|
108
|
+
expect(result).toEqual(false);
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
});
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { getFileExtension } from "./file";
|
|
2
|
+
|
|
3
|
+
describe("getFileExtension", () => {
|
|
4
|
+
it("should return the file extension based on the mimetype", () => {
|
|
5
|
+
const cases = [
|
|
6
|
+
{
|
|
7
|
+
mimetype: "image/jpeg",
|
|
8
|
+
expected: "jpg",
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
mimetype: "image/png",
|
|
12
|
+
expected: "png",
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
mimetype: "application/pdf",
|
|
16
|
+
expected: "pdf",
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
mimetype: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
20
|
+
expected: "docx",
|
|
21
|
+
},
|
|
22
|
+
];
|
|
23
|
+
|
|
24
|
+
cases.forEach((c) => {
|
|
25
|
+
const file = {
|
|
26
|
+
mimetype: c.mimetype,
|
|
27
|
+
originalname: `test.${c.expected}`,
|
|
28
|
+
} as Express.Multer.File;
|
|
29
|
+
|
|
30
|
+
const extension = getFileExtension(file);
|
|
31
|
+
|
|
32
|
+
expect(extension).toEqual(c.expected);
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it("should return the file extension based on the originalname if mimetype is not available", () => {
|
|
37
|
+
const cases = [
|
|
38
|
+
{
|
|
39
|
+
originalname: "test.jpg",
|
|
40
|
+
expected: "jpg",
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
originalname: "test.png",
|
|
44
|
+
expected: "png",
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
originalname: "test.pdf",
|
|
48
|
+
expected: "pdf",
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
originalname: "test.docx",
|
|
52
|
+
expected: "docx",
|
|
53
|
+
},
|
|
54
|
+
];
|
|
55
|
+
|
|
56
|
+
cases.forEach((c) => {
|
|
57
|
+
const file = {
|
|
58
|
+
originalname: c.originalname,
|
|
59
|
+
} as Express.Multer.File;
|
|
60
|
+
|
|
61
|
+
const extension = getFileExtension(file);
|
|
62
|
+
|
|
63
|
+
expect(extension).toEqual(c.expected);
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
});
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access */
|
|
2
|
+
export function hashKey(queryKey: unknown): string {
|
|
3
|
+
return JSON.stringify(queryKey, (_, val) =>
|
|
4
|
+
isPlainObject(val)
|
|
5
|
+
? Object.keys(val)
|
|
6
|
+
.sort()
|
|
7
|
+
.reduce<Record<string, unknown>>((result, key) => {
|
|
8
|
+
result[key] = val[key];
|
|
9
|
+
return result;
|
|
10
|
+
}, {})
|
|
11
|
+
: val,
|
|
12
|
+
);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function isPlainObject(o: unknown): o is Record<PropertyKey, unknown> {
|
|
16
|
+
if (Object.prototype.toString.call(o) !== "[object Object]") {return false;}
|
|
17
|
+
|
|
18
|
+
const proto = Object.getPrototypeOf(o);
|
|
19
|
+
if (proto === null) {return true;}
|
|
20
|
+
|
|
21
|
+
const ctor = proto.constructor;
|
|
22
|
+
return typeof ctor === "function" && ctor instanceof ctor && Object.getPrototypeOf(o) === Object.prototype;
|
|
23
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export * as arrayUtils from "./array";
|
|
2
|
+
export * from "./base64";
|
|
3
|
+
export * as collectionUtils from "./collection";
|
|
4
|
+
export * as csvUtils from "./csv";
|
|
5
|
+
export * as dateUtils from "./date";
|
|
6
|
+
export * as fileUtils from "./file";
|
|
7
|
+
export * from "./invariant";
|
|
8
|
+
export * from "./iso-date";
|
|
9
|
+
export * as objectUtils from "./object";
|
|
10
|
+
export * from "./paginated";
|
|
11
|
+
export * as stringUtils from "./string";
|
|
12
|
+
export * as typeUtils from "./type";
|
|
13
|
+
export * as xmlUtils from "./xml";
|
|
14
|
+
export * from "./hash-key";
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { DateTime } from "luxon";
|
|
2
|
+
|
|
3
|
+
export type ISODate = `${number}-${number}-${number}`;
|
|
4
|
+
|
|
5
|
+
export const toISODate = (date: Date): ISODate => {
|
|
6
|
+
return DateTime.fromJSDate(date).toISODate() as ISODate;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export const isValidISODate = (date: string): boolean => {
|
|
10
|
+
return DateTime.fromFormat(date, "yyyy-MM-dd").isValid;
|
|
11
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { isNotNil } from "./object";
|
|
2
|
+
|
|
3
|
+
describe(isNotNil.name, () => {
|
|
4
|
+
it("should return true if value is not null or undefined", () => {
|
|
5
|
+
expect(isNotNil(1)).toBe(true);
|
|
6
|
+
expect(isNotNil("")).toBe(true);
|
|
7
|
+
expect(isNotNil({})).toBe(true);
|
|
8
|
+
expect(isNotNil([])).toBe(true);
|
|
9
|
+
expect(isNotNil(false)).toBe(true);
|
|
10
|
+
expect(isNotNil(true)).toBe(true);
|
|
11
|
+
expect(isNotNil(0)).toBe(true);
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it("should return false if value is null or undefined", () => {
|
|
15
|
+
expect(isNotNil(null)).toBe(false);
|
|
16
|
+
expect(isNotNil(undefined)).toBe(false);
|
|
17
|
+
});
|
|
18
|
+
});
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import _ from "lodash";
|
|
2
|
+
|
|
3
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
|
4
|
+
export const get = <T>(obj: T, path: string) => _.get(obj, path);
|
|
5
|
+
export const omit = <T extends object, K extends keyof T>(obj: T, ...paths: K[]) => _.omit(obj, ...paths);
|
|
6
|
+
export const isNotNil = <T>(value: T | null | undefined): value is T => value != null;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export interface PaginatedInput {
|
|
2
|
+
take: number;
|
|
3
|
+
skip: number;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export interface PaginatedResults<T> {
|
|
7
|
+
items: T[];
|
|
8
|
+
total?: number;
|
|
9
|
+
hasMore: boolean;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface CursorPaginatedResult<T> {
|
|
13
|
+
items: T[];
|
|
14
|
+
nextCursor: string | null;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export enum OrderByEnum {
|
|
18
|
+
ASC = "asc",
|
|
19
|
+
DESC = "desc",
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface DataPaginatedInput {
|
|
23
|
+
first: number;
|
|
24
|
+
after: number;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface DataPaginatedResults<T> {
|
|
28
|
+
data: T[];
|
|
29
|
+
page_info: DataPageInfo;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface DataPageInfo {
|
|
33
|
+
has_more: boolean;
|
|
34
|
+
next_page: number;
|
|
35
|
+
total?: number;
|
|
36
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { generateTextWithLength } from "./string";
|
|
2
|
+
|
|
3
|
+
describe("generateTextWithLength", () => {
|
|
4
|
+
it("should generate a string with the given length", () => {
|
|
5
|
+
const length = 100;
|
|
6
|
+
const str = generateTextWithLength(length);
|
|
7
|
+
|
|
8
|
+
expect(str.length).toEqual(length);
|
|
9
|
+
});
|
|
10
|
+
});
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import slugify from "slugify";
|
|
2
|
+
|
|
3
|
+
export type SlugOpts = {
|
|
4
|
+
replacement?: string;
|
|
5
|
+
remove?: RegExp;
|
|
6
|
+
lower?: boolean;
|
|
7
|
+
strict?: boolean;
|
|
8
|
+
locale?: string;
|
|
9
|
+
trim?: boolean;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export const generateSlug = (str: string, ops: SlugOpts = {}) => slugify(str, { lower: true, ...ops });
|
|
13
|
+
|
|
14
|
+
export const generateTextWithLength = (length: number) => "a".repeat(length);
|
|
15
|
+
|
|
16
|
+
export const validateEmail = (email: string) => {
|
|
17
|
+
const pattern = /^[\w-+.]+@([\w-]+\.)+[\w-]{2,4}$/;
|
|
18
|
+
return pattern.test(email);
|
|
19
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export type PartialPick<T, F extends keyof T> = Omit<T, F> & Partial<Pick<T, F>>;
|
|
2
|
+
|
|
3
|
+
export type RequiredPick<T, F extends keyof T> = Omit<T, F> & {
|
|
4
|
+
[P in F]-?: T[P];
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
export type NonNullablePick<T, F extends keyof T> = Omit<T, F> & {
|
|
8
|
+
[P in F]: NonNullable<T[P]>;
|
|
9
|
+
};
|
package/src/utils/xml.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { applyDecorators } from "@nestjs/common";
|
|
2
|
+
import { Transform } from "class-transformer";
|
|
3
|
+
|
|
4
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
|
5
|
+
export const EnsureArray = () => applyDecorators(Transform(({ value }) => (typeof value === "string" ? [value] : value)));
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export * from "./ensure-array.decorator";
|
|
2
|
+
export * from "./is-iso-date.decorator";
|
|
3
|
+
export * from "./is-less-than.decorator";
|
|
4
|
+
export * from "./is-less-than-or-equal.decorator";
|
|
5
|
+
export * from "./is-more-than.decorator";
|
|
6
|
+
export * from "./is-more-than-or-equal.decorator";
|
|
7
|
+
export * from "./is-time-string.decorator";
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { describe, it, expect } from "@jest/globals";
|
|
2
|
+
import { validate } from "class-validator";
|
|
3
|
+
|
|
4
|
+
import { IsISODate } from "./is-iso-date.decorator";
|
|
5
|
+
|
|
6
|
+
class Dto {
|
|
7
|
+
@IsISODate()
|
|
8
|
+
date: unknown;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
describe("IsISODate", () => {
|
|
12
|
+
it("should accept yyyy-MM-dd format", async () => {
|
|
13
|
+
const dto = Object.assign(new Dto(), { date: "2024-01-15" });
|
|
14
|
+
const errors = await validate(dto);
|
|
15
|
+
expect(errors).toHaveLength(0);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it("should reject invalid date strings", async () => {
|
|
19
|
+
const dto = Object.assign(new Dto(), { date: "15-01-2024" });
|
|
20
|
+
const errors = await validate(dto);
|
|
21
|
+
expect(errors.length).toBeGreaterThan(0);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it("should reject non-string values", async () => {
|
|
25
|
+
const dto = Object.assign(new Dto(), { date: 12345 });
|
|
26
|
+
const errors = await validate(dto);
|
|
27
|
+
expect(errors.length).toBeGreaterThan(0);
|
|
28
|
+
});
|
|
29
|
+
});
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import {
|
|
2
|
+
registerDecorator,
|
|
3
|
+
ValidationArguments,
|
|
4
|
+
ValidationOptions,
|
|
5
|
+
ValidatorConstraint,
|
|
6
|
+
ValidatorConstraintInterface,
|
|
7
|
+
} from "class-validator";
|
|
8
|
+
import { DateTime } from "luxon";
|
|
9
|
+
|
|
10
|
+
export function IsISODate(validationOptions?: ValidationOptions) {
|
|
11
|
+
return function (object: object, propertyName: string) {
|
|
12
|
+
registerDecorator({
|
|
13
|
+
target: object.constructor,
|
|
14
|
+
propertyName,
|
|
15
|
+
options: validationOptions,
|
|
16
|
+
validator: IsISODateConstraint,
|
|
17
|
+
});
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
@ValidatorConstraint({ name: "isISODate" })
|
|
22
|
+
class IsISODateConstraint implements ValidatorConstraintInterface {
|
|
23
|
+
validate(value: unknown) {
|
|
24
|
+
return typeof value === "string" && DateTime.fromFormat(value, "yyyy-MM-dd").isValid;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
defaultMessage(args: ValidationArguments) {
|
|
28
|
+
return `${args.property} must be in yyyy-MM-dd format`;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { describe, it, expect } from "@jest/globals";
|
|
2
|
+
import { validate } from "class-validator";
|
|
3
|
+
|
|
4
|
+
import { IsLessThanOrEqual } from "./is-less-than-or-equal.decorator";
|
|
5
|
+
|
|
6
|
+
class Dto {
|
|
7
|
+
max: number;
|
|
8
|
+
@IsLessThanOrEqual("max")
|
|
9
|
+
min: number;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
describe("IsLessThanOrEqual", () => {
|
|
13
|
+
it("should pass when value is less", async () => {
|
|
14
|
+
const dto = Object.assign(new Dto(), { max: 10, min: 5 });
|
|
15
|
+
const errors = await validate(dto);
|
|
16
|
+
expect(errors).toHaveLength(0);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it("should pass when value is equal", async () => {
|
|
20
|
+
const dto = Object.assign(new Dto(), { max: 5, min: 5 });
|
|
21
|
+
const errors = await validate(dto);
|
|
22
|
+
expect(errors).toHaveLength(0);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it("should fail when value is greater", async () => {
|
|
26
|
+
const dto = Object.assign(new Dto(), { max: 5, min: 10 });
|
|
27
|
+
const errors = await validate(dto);
|
|
28
|
+
expect(errors.length).toBeGreaterThan(0);
|
|
29
|
+
});
|
|
30
|
+
});
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import {
|
|
2
|
+
registerDecorator,
|
|
3
|
+
ValidationArguments,
|
|
4
|
+
ValidationOptions,
|
|
5
|
+
ValidatorConstraint,
|
|
6
|
+
ValidatorConstraintInterface,
|
|
7
|
+
} from "class-validator";
|
|
8
|
+
|
|
9
|
+
import { isValidISODate } from "../utils";
|
|
10
|
+
|
|
11
|
+
export function IsLessThanOrEqual(property: string, validationOptions?: ValidationOptions) {
|
|
12
|
+
return function (object: object, propertyName: string) {
|
|
13
|
+
registerDecorator({
|
|
14
|
+
target: object.constructor,
|
|
15
|
+
propertyName,
|
|
16
|
+
constraints: [property],
|
|
17
|
+
options: validationOptions,
|
|
18
|
+
validator: IsLessThanOrEqualConstraint,
|
|
19
|
+
});
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
@ValidatorConstraint({ name: "isLessThanOrEqual" })
|
|
24
|
+
class IsLessThanOrEqualConstraint implements ValidatorConstraintInterface {
|
|
25
|
+
validate(value: unknown, args: ValidationArguments) {
|
|
26
|
+
const relatedPropertyName = args.constraints[0] as string;
|
|
27
|
+
const relatedValue = (args.object as Record<string, unknown>)[relatedPropertyName];
|
|
28
|
+
|
|
29
|
+
if (!relatedValue) {
|
|
30
|
+
return true;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (typeof value === "number" && typeof relatedValue === "number") {
|
|
34
|
+
return value <= relatedValue;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (value instanceof Date && relatedValue instanceof Date) {
|
|
38
|
+
return +value <= +relatedValue;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (typeof value === "string" && typeof relatedValue === "string" && isValidISODate(value) && isValidISODate(relatedValue)) {
|
|
42
|
+
return +new Date(value) <= +new Date(relatedValue);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
throw new Error("Unsupported comparison");
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
defaultMessage(args: ValidationArguments) {
|
|
49
|
+
const relatedPropertyName = args.constraints[0] as string;
|
|
50
|
+
return `${args.property} must be more than or equal ${relatedPropertyName}`;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { describe, it, expect } from "@jest/globals";
|
|
2
|
+
import { validate } from "class-validator";
|
|
3
|
+
|
|
4
|
+
import { IsLessThan } from "./is-less-than.decorator";
|
|
5
|
+
|
|
6
|
+
class Dto {
|
|
7
|
+
max: number;
|
|
8
|
+
@IsLessThan("max")
|
|
9
|
+
min: number;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
describe("IsLessThan", () => {
|
|
13
|
+
it("should pass when value is less", async () => {
|
|
14
|
+
const dto = Object.assign(new Dto(), { max: 10, min: 5 });
|
|
15
|
+
const errors = await validate(dto);
|
|
16
|
+
expect(errors).toHaveLength(0);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it("should fail when value is equal", async () => {
|
|
20
|
+
const dto = Object.assign(new Dto(), { max: 5, min: 5 });
|
|
21
|
+
const errors = await validate(dto);
|
|
22
|
+
expect(errors.length).toBeGreaterThan(0);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it("should fail when value is greater", async () => {
|
|
26
|
+
const dto = Object.assign(new Dto(), { max: 5, min: 10 });
|
|
27
|
+
const errors = await validate(dto);
|
|
28
|
+
expect(errors.length).toBeGreaterThan(0);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it("should pass when related value is falsy", async () => {
|
|
32
|
+
const dto = Object.assign(new Dto(), { max: 0, min: 5 });
|
|
33
|
+
const errors = await validate(dto);
|
|
34
|
+
expect(errors).toHaveLength(0);
|
|
35
|
+
});
|
|
36
|
+
});
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import {
|
|
2
|
+
registerDecorator,
|
|
3
|
+
ValidationArguments,
|
|
4
|
+
ValidationOptions,
|
|
5
|
+
ValidatorConstraint,
|
|
6
|
+
ValidatorConstraintInterface,
|
|
7
|
+
} from "class-validator";
|
|
8
|
+
|
|
9
|
+
import { isValidISODate } from "../utils";
|
|
10
|
+
|
|
11
|
+
export function IsLessThan(property: string, validationOptions?: ValidationOptions) {
|
|
12
|
+
return function (object: object, propertyName: string) {
|
|
13
|
+
registerDecorator({
|
|
14
|
+
target: object.constructor,
|
|
15
|
+
propertyName,
|
|
16
|
+
constraints: [property],
|
|
17
|
+
options: validationOptions,
|
|
18
|
+
validator: IsLessThanConstraint,
|
|
19
|
+
});
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
@ValidatorConstraint({ name: "isLessThan" })
|
|
24
|
+
class IsLessThanConstraint implements ValidatorConstraintInterface {
|
|
25
|
+
validate(value: unknown, args: ValidationArguments) {
|
|
26
|
+
const relatedPropertyName = args.constraints[0] as string;
|
|
27
|
+
const relatedValue = (args.object as Record<string, unknown>)[relatedPropertyName];
|
|
28
|
+
|
|
29
|
+
if (!relatedValue) {
|
|
30
|
+
return true;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (typeof value === "number" && typeof relatedValue === "number") {
|
|
34
|
+
return value < relatedValue;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (value instanceof Date && relatedValue instanceof Date) {
|
|
38
|
+
return +value < +relatedValue;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (typeof value === "string" && typeof relatedValue === "string" && isValidISODate(value) && isValidISODate(relatedValue)) {
|
|
42
|
+
return +new Date(value) < +new Date(relatedValue);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
throw new Error("Unsupported comparison");
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
defaultMessage(args: ValidationArguments) {
|
|
49
|
+
const relatedPropertyName = args.constraints[0] as string;
|
|
50
|
+
return `${args.property} must be more than ${relatedPropertyName}`;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { describe, it, expect } from "@jest/globals";
|
|
2
|
+
import { validate } from "class-validator";
|
|
3
|
+
|
|
4
|
+
import { IsMoreThanOrEqual } from "./is-more-than-or-equal.decorator";
|
|
5
|
+
|
|
6
|
+
class Dto {
|
|
7
|
+
min: number;
|
|
8
|
+
@IsMoreThanOrEqual("min")
|
|
9
|
+
max: number;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
describe("IsMoreThanOrEqual", () => {
|
|
13
|
+
it("should pass when value is greater", async () => {
|
|
14
|
+
const dto = Object.assign(new Dto(), { min: 5, max: 10 });
|
|
15
|
+
const errors = await validate(dto);
|
|
16
|
+
expect(errors).toHaveLength(0);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it("should pass when value is equal", async () => {
|
|
20
|
+
const dto = Object.assign(new Dto(), { min: 5, max: 5 });
|
|
21
|
+
const errors = await validate(dto);
|
|
22
|
+
expect(errors).toHaveLength(0);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it("should fail when value is less", async () => {
|
|
26
|
+
const dto = Object.assign(new Dto(), { min: 10, max: 5 });
|
|
27
|
+
const errors = await validate(dto);
|
|
28
|
+
expect(errors.length).toBeGreaterThan(0);
|
|
29
|
+
});
|
|
30
|
+
});
|