@pcg/core 1.0.0-alpha.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 (98) hide show
  1. package/.turbo/turbo-build.log +15 -0
  2. package/CHANGELOG.md +7 -0
  3. package/dist/index.d.ts +1400 -0
  4. package/dist/index.js +2171 -0
  5. package/dist/index.js.map +1 -0
  6. package/package.json +55 -0
  7. package/src/abstracts/index.ts +3 -0
  8. package/src/abstracts/nestjs-resource-service.ts +154 -0
  9. package/src/abstracts/nestjs-service.ts +25 -0
  10. package/src/configs/app.config.ts +185 -0
  11. package/src/configs/db.config.ts +122 -0
  12. package/src/configs/index.ts +4 -0
  13. package/src/configs/logger.config.ts +62 -0
  14. package/src/context/action-context.ts +34 -0
  15. package/src/context/current-user.ts +49 -0
  16. package/src/context/index.ts +5 -0
  17. package/src/context/platform-method-context.ts +5 -0
  18. package/src/context/service-method-context.ts +47 -0
  19. package/src/db/snake-naming.strategy.ts +277 -0
  20. package/src/enums/app-env.enum.ts +36 -0
  21. package/src/enums/app-mode.enum.ts +5 -0
  22. package/src/enums/app-server.enum.ts +39 -0
  23. package/src/enums/index.ts +4 -0
  24. package/src/enums/worker-mode.enum.ts +11 -0
  25. package/src/errors/access-denied.error.ts +18 -0
  26. package/src/errors/bad-request.error.ts +9 -0
  27. package/src/errors/forbidden.error.ts +9 -0
  28. package/src/errors/index.ts +8 -0
  29. package/src/errors/input-validation.error.ts +16 -0
  30. package/src/errors/nest-error.filter.ts +70 -0
  31. package/src/errors/nest-error.ts +63 -0
  32. package/src/errors/not-found.error.ts +9 -0
  33. package/src/errors/unauthorized.error.ts +9 -0
  34. package/src/exceptions/http-exception-response.ts +34 -0
  35. package/src/exceptions/http-exceptions.filter.ts +95 -0
  36. package/src/index.ts +32 -0
  37. package/src/jwt/extractors.ts +80 -0
  38. package/src/jwt/types.ts +209 -0
  39. package/src/logger/classes/logger-factory.ts +54 -0
  40. package/src/logger/classes/logger.ts +340 -0
  41. package/src/logger/classes/nest-system-logger.ts +63 -0
  42. package/src/logger/classes/typeorm-logger.ts +83 -0
  43. package/src/logger/index.ts +20 -0
  44. package/src/logger/logger.constants.ts +24 -0
  45. package/src/logger/logger.interfaces.ts +98 -0
  46. package/src/logger/logger.module.ts +45 -0
  47. package/src/logger/logger.providers.ts +140 -0
  48. package/src/logger/winston.tools.ts +241 -0
  49. package/src/middlewares/app.middleware.ts +26 -0
  50. package/src/middlewares/index.ts +1 -0
  51. package/src/modules/hooks/base-hook.ts +64 -0
  52. package/src/modules/hooks/decorators/on-hook.decorator.ts +19 -0
  53. package/src/modules/hooks/hooks.module.ts +10 -0
  54. package/src/modules/hooks/hooks.service.ts +28 -0
  55. package/src/modules/hooks/index.ts +11 -0
  56. package/src/modules/id/id.module.ts +26 -0
  57. package/src/modules/id/id.service.ts +57 -0
  58. package/src/modules/id/index.ts +2 -0
  59. package/src/modules/postgres-pubsub/index.ts +3 -0
  60. package/src/modules/postgres-pubsub/postgres-pubsub.module.ts +14 -0
  61. package/src/modules/postgres-pubsub/postgres-pubsub.ts +461 -0
  62. package/src/pagination/constants.ts +9 -0
  63. package/src/pagination/cursor/cursor-pagination.exception.ts +16 -0
  64. package/src/pagination/cursor/cursor-pagination.helpers.ts +145 -0
  65. package/src/pagination/cursor/cursor-pagination.input.ts +96 -0
  66. package/src/pagination/cursor/cursor-pagination.types.ts +127 -0
  67. package/src/pagination/index.ts +9 -0
  68. package/src/pagination/offset/offset-pagination.exception.ts +15 -0
  69. package/src/pagination/offset/offset-pagination.helpers.ts +122 -0
  70. package/src/pagination/offset/offset-pagination.input.ts +30 -0
  71. package/src/pagination/offset/offset-pagination.types.ts +82 -0
  72. package/src/pagination/tools.ts +53 -0
  73. package/src/tools/compose.ts +92 -0
  74. package/src/tools/convert-to-bigint.ts +27 -0
  75. package/src/tools/create-list-meta.ts +64 -0
  76. package/src/tools/define-statuses.ts +15 -0
  77. package/src/tools/env.ts +139 -0
  78. package/src/tools/fetch-total-with-query.ts +48 -0
  79. package/src/tools/generate-entity-id.ts +23 -0
  80. package/src/tools/get-request-language.ts +13 -0
  81. package/src/tools/is-object.ts +10 -0
  82. package/src/tools/postgres/locale-to-pg-collate.ts +21 -0
  83. package/src/tools/remove-undefined-properties.ts +20 -0
  84. package/src/tools/request-id.ts +25 -0
  85. package/src/tools/stringify-opts.ts +20 -0
  86. package/src/tools/typeorm/add-filter.ts +164 -0
  87. package/src/tools/typeorm/ensure-inner-join.ts +36 -0
  88. package/src/tools/typeorm/ensure-left-join.ts +36 -0
  89. package/src/tools/typeorm/is-alias-already-busy.ts +25 -0
  90. package/src/tools/wait.ts +26 -0
  91. package/src/types/express-request.ts +8 -0
  92. package/src/types/list-mehod-options.ts +32 -0
  93. package/src/types/list-meta.ts +16 -0
  94. package/src/types/maybe.ts +2 -0
  95. package/src/validation/index.ts +1 -0
  96. package/src/validation/validation-pipe.ts +14 -0
  97. package/tsconfig.lib.json +9 -0
  98. package/tsdown.config.ts +15 -0
@@ -0,0 +1,96 @@
1
+ import {
2
+ ArgsType,
3
+ Field, Int,
4
+ } from '@nestjs/graphql';
5
+ import { IsOptional, Min } from 'class-validator';
6
+
7
+ @ArgsType()
8
+ export class CursorPaginationInput {
9
+ /**
10
+ * The amount of items to be requested per page from the start
11
+ * @example
12
+ * ```graphql
13
+ * query {
14
+ * messages(first: 3) {
15
+ * edges {
16
+ * cursor
17
+ * node {
18
+ * id
19
+ * }
20
+ * }
21
+ * }
22
+ * }
23
+ * ```
24
+ */
25
+ @Field(() => Int, {
26
+ nullable: true,
27
+ })
28
+ @Min(1)
29
+ @IsOptional()
30
+ first?: number;
31
+
32
+ /**
33
+ * The cursor to start the pagination
34
+ * @example
35
+ * ```graphql
36
+ * query {
37
+ * messages(first: 3, after: "xxx") {
38
+ * edges {
39
+ * cursor
40
+ * node {
41
+ * id
42
+ * }
43
+ * }
44
+ * }
45
+ * }
46
+ * ```
47
+ */
48
+ @Field(() => String, {
49
+ nullable: true,
50
+ })
51
+ after?: string;
52
+
53
+ /**
54
+ * The amount of items to be requested per page from the end
55
+ * @example
56
+ * ```graphql
57
+ * query {
58
+ * messages(last: 2) {
59
+ * edges {
60
+ * cursor
61
+ * node {
62
+ * id
63
+ * }
64
+ * }
65
+ * }
66
+ * }
67
+ * ```
68
+ */
69
+ @Field(() => Int, {
70
+ nullable: true,
71
+ })
72
+ @Min(1)
73
+ @IsOptional()
74
+ last?: number;
75
+
76
+ /**
77
+ * The cursor to end the pagination
78
+ * @example
79
+ * ```graphql
80
+ * query {
81
+ * messages(last: 2, before: "xxx") {
82
+ * edges {
83
+ * cursor
84
+ * node {
85
+ * id
86
+ * }
87
+ * }
88
+ * }
89
+ * }
90
+ * ```
91
+ */
92
+ @Field(() => String, {
93
+ nullable: true,
94
+ })
95
+ before?: string;
96
+ }
@@ -0,0 +1,127 @@
1
+ import { Type } from '@nestjs/common';
2
+ import { Field, ObjectType } from '@nestjs/graphql';
3
+
4
+ import type { MaybeNull } from '#/types/maybe';
5
+
6
+ @ObjectType()
7
+ export class CursorPaginationPageInfo {
8
+ /**
9
+ * The cursor to the first item in the list
10
+ */
11
+ @Field(() => String, {
12
+ nullable: true,
13
+ })
14
+ startCursor!: MaybeNull<string>;
15
+
16
+ /**
17
+ * The cursor to the last item in the list
18
+ */
19
+ @Field(() => String, {
20
+ nullable: true,
21
+ })
22
+ endCursor!: MaybeNull<string>;
23
+
24
+ /**
25
+ * Whether there are more items in the list before the start cursor
26
+ */
27
+ @Field(() => Boolean, {
28
+ nullable: true,
29
+ })
30
+ hasPreviousPage?: boolean;
31
+
32
+ /**
33
+ * Whether there are more items in the list after the end cursor
34
+ */
35
+ @Field(() => Boolean, {
36
+ nullable: true,
37
+ })
38
+ hasNextPage?: boolean;
39
+ }
40
+
41
+ /**
42
+ * The edge type for cursor pagination
43
+ */
44
+ export interface IEdge<T> {
45
+ /**
46
+ * The cursor for the item
47
+ */
48
+ cursor: string;
49
+
50
+ /**
51
+ * The item
52
+ */
53
+ node: T;
54
+ }
55
+
56
+ export interface ICursorPaginated<T> {
57
+ /**
58
+ * The list of edges
59
+ */
60
+ edges: IEdge<T>[];
61
+
62
+ /**
63
+ * The pagination info
64
+ */
65
+ pageInfo: CursorPaginationPageInfo;
66
+ }
67
+
68
+ /**
69
+ * Creates a new GraphQL object type with the name `Paginated${classRef.name}`
70
+ * that implements the `ICursorPaginated` interface
71
+ * @param classRef The class reference of the items
72
+ *
73
+ * @example Create PaginatedMessages type
74
+ * ```ts
75
+ * @ObjectType()
76
+ * class PaginatedMessages extends CursorPaginated(Message) {}
77
+ * ```
78
+ */
79
+ // eslint-disable-next-line func-style
80
+ export function CursorPaginated<T>(classRef: Type<T>): Type<ICursorPaginated<T>> {
81
+ @ObjectType(`${classRef.name}Edge`)
82
+ abstract class EdgeType implements IEdge<T> {
83
+ /**
84
+ * The cursor for the item
85
+ */
86
+ @Field(() => String, {
87
+ description: 'The cursor for the item',
88
+ })
89
+ cursor!: string;
90
+
91
+ /**
92
+ * The item
93
+ */
94
+ @Field(() => classRef, {
95
+ description: 'The item',
96
+ })
97
+ node!: T;
98
+ }
99
+
100
+ @ObjectType({
101
+ isAbstract: true,
102
+ })
103
+ abstract class CursorPaginatedType implements ICursorPaginated<T> {
104
+ /**
105
+ * The list of edges
106
+ */
107
+ @Field(() => [EdgeType], {
108
+ description: 'The list of edges',
109
+ })
110
+ edges!: EdgeType[];
111
+
112
+ /**
113
+ * The pagination info
114
+ */
115
+ @Field(() => CursorPaginationPageInfo, {
116
+ description: 'The pagination info',
117
+ })
118
+ pageInfo!: CursorPaginationPageInfo;
119
+ }
120
+
121
+ return CursorPaginatedType as Type<ICursorPaginated<T>>;
122
+ }
123
+
124
+ export enum CursorOrderBy {
125
+ createdAt_ASC = 'createdAt_ASC',
126
+ createdAt_DESC = 'createdAt_DESC',
127
+ }
@@ -0,0 +1,9 @@
1
+ export * from './cursor/cursor-pagination.helpers.js';
2
+ export * from './cursor/cursor-pagination.input.js';
3
+ export * from './cursor/cursor-pagination.types.js';
4
+
5
+ export * from './offset/offset-pagination.helpers.js';
6
+ export * from './offset/offset-pagination.input.js';
7
+ export * from './offset/offset-pagination.types.js';
8
+
9
+ export * from './tools.js';
@@ -0,0 +1,15 @@
1
+ import { NestError } from '#/errors';
2
+
3
+ export enum OffsetErrorMessage {
4
+ OUTPUT_LIMIT = `Can't build page meta. Invalid limit`,
5
+ }
6
+
7
+ export class OffsetPaginationError extends NestError {
8
+ constructor(public readonly message: OffsetErrorMessage) {
9
+ super({
10
+ message,
11
+ key: 'NST_INVALID_OFFSET_PAGINATION',
12
+ });
13
+ this.name = 'OffsetPaginationError';
14
+ }
15
+ }
@@ -0,0 +1,122 @@
1
+ import { snakeCase } from '@pcg/text-kit';
2
+ import { GraphQLResolveInfo } from 'graphql';
3
+
4
+ import { ListMeta } from '#/types/list-meta';
5
+
6
+ import { PAGINATION_DEFAULT_LIMIT } from '../constants.js';
7
+ import { requiresTotalCount, truncPaginationLimit } from '../tools.js';
8
+ import { OffsetErrorMessage, OffsetPaginationError } from './offset-pagination.exception.js';
9
+ import { OffsetPaginationInput } from './offset-pagination.input.js';
10
+ import { IOffsetPaginated } from './offset-pagination.types.js';
11
+
12
+ export type SortOrder = 'ASC' | 'DESC';
13
+
14
+ export interface SortParams<T> {
15
+ /**
16
+ * Original sort field name.
17
+ * @example
18
+ * const orderBy = 'createdAt_ASC';
19
+ * const sort = extractSortParams(orderBy); // sort.fieldName = 'uploaderName';
20
+ * if (sort.fieldName === uploaderName) {
21
+ * ...
22
+ * }
23
+ */
24
+ fieldName: T;
25
+
26
+ /**
27
+ * Database column name. Camel cased fieldName.
28
+ * @example
29
+ * const orderBy = 'createdAt_ASC';
30
+ * const sort = extractSortParams(orderBy); // sort.columnName = 'created_at';
31
+ * query.orderBy(`v.${sort.columnName}`, sort.direction);
32
+ */
33
+ columnName: string;
34
+
35
+ /**
36
+ * Sort direction (or order) (ASC | DESC)
37
+ * @example
38
+ * const orderBy = 'createdAt_ASC';
39
+ * const sort = extractSortParams(orderBy); // sort.direction = 'ASC';
40
+ * query.orderBy(`v.${sort.columnName}`, sort.direction);
41
+ */
42
+ direction: SortOrder;
43
+ }
44
+
45
+ /**
46
+ * Extract sort field name and sort direction from orderBy string.
47
+ * @example
48
+ * const orderBy = 'createdAt_ASC';
49
+ *
50
+ * // sort.fieldName = 'createdAt', sort.direction = 'ASC'; sort.columnName = 'created_at';
51
+ * const sort = extractSortParams(orderBy);
52
+ */
53
+ export const extractSortParams = <T>(orderBy: string): SortParams<T> => {
54
+ const slices = orderBy.split('_');
55
+
56
+ if (slices.length !== 2) {
57
+ throw new Error('Invalid orderBy argument');
58
+ }
59
+
60
+ return {
61
+ fieldName: slices[0] as unknown as T,
62
+ direction: slices[1] as unknown as SortOrder,
63
+ columnName: snakeCase(slices[0]),
64
+ };
65
+ };
66
+
67
+ export interface OffsetPaginationOptions {
68
+ limit?: number;
69
+ offset?: number;
70
+
71
+ /**
72
+ * If true, total count will be calculated by additional SELECT COUNT.
73
+ * @default true
74
+ */
75
+ needCountTotal: boolean;
76
+ }
77
+
78
+ export interface CreateOffsetPaginationOptionsParams {
79
+ maxLimit?: number;
80
+ defaultLimit?: number;
81
+ }
82
+
83
+ export const createOffsetPaginationOptions = (
84
+ input: OffsetPaginationInput,
85
+ info: GraphQLResolveInfo,
86
+ params?: CreateOffsetPaginationOptionsParams,
87
+ ): OffsetPaginationOptions => {
88
+ const options: OffsetPaginationOptions = {
89
+ needCountTotal: requiresTotalCount(info),
90
+ limit: params?.defaultLimit ?? PAGINATION_DEFAULT_LIMIT,
91
+ };
92
+
93
+ if (input.limit) {
94
+ options.limit = truncPaginationLimit(Number(input.limit), params?.maxLimit);
95
+
96
+ if (input.page) {
97
+ options.offset = (Number(input.page) - 1) * options.limit;
98
+ }
99
+ }
100
+
101
+ return options;
102
+ };
103
+
104
+ export const offsetPaginatedOutput = <T>(items: T[], {
105
+ limit,
106
+ offset = 0,
107
+ total = 0,
108
+ }: ListMeta): IOffsetPaginated<T> => {
109
+ if (!limit) {
110
+ throw new OffsetPaginationError(OffsetErrorMessage.OUTPUT_LIMIT);
111
+ }
112
+
113
+ return {
114
+ items,
115
+ pageInfo: {
116
+ totalPages: Math.ceil(total / limit),
117
+ totalItems: total,
118
+ page: (offset / limit) + 1,
119
+ limit,
120
+ },
121
+ };
122
+ };
@@ -0,0 +1,30 @@
1
+ import {
2
+ ArgsType,
3
+ Field, Int,
4
+ } from '@nestjs/graphql';
5
+
6
+ import { PAGINATION_DEFAULT_LIMIT } from '../constants.js';
7
+
8
+ @ArgsType()
9
+ export class OffsetPaginationInput {
10
+ /**
11
+ * the amount of items to be requested per page
12
+ */
13
+ @Field(() => Int, {
14
+ description: 'the amount of items to be requested per page',
15
+ defaultValue: PAGINATION_DEFAULT_LIMIT,
16
+ nullable: true,
17
+ })
18
+ limit?: number;
19
+
20
+ /**
21
+ * @default 1
22
+ * the page that is requested
23
+ */
24
+ @Field(() => Int, {
25
+ defaultValue: 1,
26
+ description: 'the page that is requested',
27
+ })
28
+ page?: number = 1;
29
+ }
30
+
@@ -0,0 +1,82 @@
1
+ import { Type } from '@nestjs/common';
2
+ import {
3
+ Field, Int,
4
+ ObjectType,
5
+ } from '@nestjs/graphql';
6
+
7
+ @ObjectType()
8
+ export class OffsetPaginationPageInfo {
9
+ /**
10
+ * The total amount of pages
11
+ * (total items / limit)
12
+ */
13
+ @Field(() => Int, {
14
+ description: 'The total amount of pages (total items / limit)',
15
+ })
16
+ totalPages!: number;
17
+
18
+ /**
19
+ * The total amount of items
20
+ */
21
+ @Field(() => Int, {
22
+ description: 'The total amount of items',
23
+ })
24
+ totalItems!: number;
25
+
26
+ /**
27
+ * The current page
28
+ */
29
+ @Field(() => Int, {
30
+ description: 'The current page',
31
+ })
32
+ page!: number;
33
+
34
+ /**
35
+ * The amount of items to be requested per page
36
+ */
37
+ @Field(() => Int, {
38
+ description: 'The amount of items to be requested per page',
39
+ })
40
+ limit!: number;
41
+ }
42
+
43
+ export interface IOffsetPaginated<T> {
44
+ items: T[];
45
+ pageInfo: OffsetPaginationPageInfo;
46
+ }
47
+
48
+ /**
49
+ * Creates a new GraphQL object type with the name `Paginated${classRef.name}`
50
+ * that implements the `IOffsetPaginated` interface
51
+ * @param classRef The class reference of the items
52
+ *
53
+ * @example Create PaginatedUsers type
54
+ * ```ts
55
+ * @ObjectType()
56
+ * class PaginatedUsers extends OffsetPaginated(User) {}
57
+ * ```
58
+ */
59
+ // eslint-disable-next-line func-style
60
+ export function OffsetPaginated<T>(classRef: Type<T>): Type<IOffsetPaginated<T>> {
61
+ @ObjectType({
62
+ isAbstract: true,
63
+ })
64
+ abstract class OffsetPaginatedType implements IOffsetPaginated<T> {
65
+ /**
66
+ * The items of the current page
67
+ */
68
+ @Field(() => [classRef], {
69
+ description: 'The items of the current page',
70
+ })
71
+ items!: T[];
72
+
73
+ /**
74
+ * The pagination information
75
+ * (total pages, total items, current page, limit)
76
+ */
77
+ @Field(() => OffsetPaginationPageInfo)
78
+ pageInfo!: OffsetPaginationPageInfo;
79
+ }
80
+
81
+ return OffsetPaginatedType as Type<IOffsetPaginated<T>>;
82
+ }
@@ -0,0 +1,53 @@
1
+ import { GraphQLResolveInfo } from 'graphql';
2
+
3
+ import { PAGINATION_MAX_LIMIT } from './constants.js';
4
+
5
+ export const truncPaginationLimit = (limit: number, maxLimit = PAGINATION_MAX_LIMIT) =>
6
+ Math.min(limit, maxLimit);
7
+
8
+ /**
9
+ * Determines whether the GraphQL query requires a total count of items.
10
+ * @param info - The GraphQLResolveInfo object containing information about the query.
11
+ * @returns A boolean indicating whether the query requires a total count of items.
12
+ */
13
+ export const requiresTotalCount = (info: GraphQLResolveInfo): boolean => {
14
+ const fieldNode = info.fieldNodes[0];
15
+
16
+ if (!fieldNode.selectionSet) {
17
+ return false;
18
+ }
19
+
20
+ for (const selection of fieldNode.selectionSet.selections) {
21
+ if (selection.kind === 'Field' && selection.name.value === 'pageInfo') {
22
+ if (selection.selectionSet) {
23
+ for (const pageInfoSelection of selection.selectionSet.selections) {
24
+ if (
25
+ pageInfoSelection.kind === 'Field' &&
26
+ (pageInfoSelection.name.value === 'totalPages' || pageInfoSelection.name.value === 'totalItems')
27
+ ) {
28
+ return true;
29
+ }
30
+ }
31
+ }
32
+ }
33
+ }
34
+
35
+ if (info.fragments) {
36
+ const fragments = Object.values(info.fragments);
37
+ for (const fragment of fragments) {
38
+ // const regex = new RegExp('/OffsetPagination|PageInfo/i');
39
+ if (fragment.selectionSet) {
40
+ for (const pageInfoSelection of fragment.selectionSet.selections) {
41
+ if (
42
+ pageInfoSelection.kind === 'Field' &&
43
+ (pageInfoSelection.name.value === 'totalPages' || pageInfoSelection.name.value === 'totalItems')
44
+ ) {
45
+ return true;
46
+ }
47
+ }
48
+ }
49
+ }
50
+ }
51
+
52
+ return false;
53
+ };
@@ -0,0 +1,92 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+ export type Constructor = new (...args: any[]) => any
3
+
4
+ type MixinFunction<TInput, TOutput> = (source: TInput) => TOutput
5
+
6
+ /**
7
+ * Compose a class by applying mixins to it.
8
+ * The code is inspired by https://justinfagnani.com/2015/12/21/real-mixins-with-javascript-classes/, its
9
+ * just that I have added the support for static types too.
10
+ *
11
+ * @param baseClass - The base class to extend
12
+ * @param mixins - One or more mixin functions to apply sequentially
13
+ * @returns The composed class with all mixins applied
14
+ */
15
+ export function compose<TBase extends Constructor, TResult1>(
16
+ baseClass: TBase,
17
+ mixin1: MixinFunction<TBase, TResult1>
18
+ ): TResult1
19
+ export function compose<TBase extends Constructor, TResult1, TResult2>(
20
+ baseClass: TBase,
21
+ mixin1: MixinFunction<TBase, TResult1>,
22
+ mixin2: MixinFunction<TResult1, TResult2>
23
+ ): TResult2
24
+ export function compose<TBase extends Constructor, TResult1, TResult2, TResult3>(
25
+ baseClass: TBase,
26
+ mixin1: MixinFunction<TBase, TResult1>,
27
+ mixin2: MixinFunction<TResult1, TResult2>,
28
+ mixin3: MixinFunction<TResult2, TResult3>
29
+ ): TResult3
30
+ export function compose<TBase extends Constructor, TResult1, TResult2, TResult3, TResult4>(
31
+ baseClass: TBase,
32
+ mixin1: MixinFunction<TBase, TResult1>,
33
+ mixin2: MixinFunction<TResult1, TResult2>,
34
+ mixin3: MixinFunction<TResult2, TResult3>,
35
+ mixin4: MixinFunction<TResult3, TResult4>
36
+ ): TResult4
37
+ export function compose<TBase extends Constructor, TResult1, TResult2, TResult3, TResult4, TResult5>(
38
+ baseClass: TBase,
39
+ mixin1: MixinFunction<TBase, TResult1>,
40
+ mixin2: MixinFunction<TResult1, TResult2>,
41
+ mixin3: MixinFunction<TResult2, TResult3>,
42
+ mixin4: MixinFunction<TResult3, TResult4>,
43
+ mixin5: MixinFunction<TResult4, TResult5>
44
+ ): TResult5
45
+ export function compose<TBase extends Constructor, TResult1, TResult2, TResult3, TResult4, TResult5, TResult6>(
46
+ baseClass: TBase,
47
+ mixin1: MixinFunction<TBase, TResult1>,
48
+ mixin2: MixinFunction<TResult1, TResult2>,
49
+ mixin3: MixinFunction<TResult2, TResult3>,
50
+ mixin4: MixinFunction<TResult3, TResult4>,
51
+ mixin5: MixinFunction<TResult4, TResult5>,
52
+ mixin6: MixinFunction<TResult5, TResult6>
53
+ ): TResult6
54
+ export function compose<TBase extends Constructor, TResult1, TResult2, TResult3, TResult4, TResult5, TResult6, TResult7>(
55
+ baseClass: TBase,
56
+ mixin1: MixinFunction<TBase, TResult1>,
57
+ mixin2: MixinFunction<TResult1, TResult2>,
58
+ mixin3: MixinFunction<TResult2, TResult3>,
59
+ mixin4: MixinFunction<TResult3, TResult4>,
60
+ mixin5: MixinFunction<TResult4, TResult5>,
61
+ mixin6: MixinFunction<TResult5, TResult6>,
62
+ mixin7: MixinFunction<TResult6, TResult7>
63
+ ): TResult7
64
+ export function compose<TBase extends Constructor, TResult1, TResult2, TResult3, TResult4, TResult5, TResult6, TResult7, TResult8>(
65
+ baseClass: TBase,
66
+ mixin1: MixinFunction<TBase, TResult1>,
67
+ mixin2: MixinFunction<TResult1, TResult2>,
68
+ mixin3: MixinFunction<TResult2, TResult3>,
69
+ mixin4: MixinFunction<TResult3, TResult4>,
70
+ mixin5: MixinFunction<TResult4, TResult5>,
71
+ mixin6: MixinFunction<TResult5, TResult6>,
72
+ mixin7: MixinFunction<TResult6, TResult7>,
73
+ mixin8: MixinFunction<TResult7, TResult8>
74
+ ): TResult8
75
+ export function compose<TBase extends Constructor, TResult1, TResult2, TResult3, TResult4, TResult5, TResult6, TResult7, TResult8, TResult9>(
76
+ baseClass: TBase,
77
+ mixin1: MixinFunction<TBase, TResult1>,
78
+ mixin2: MixinFunction<TResult1, TResult2>,
79
+ mixin3: MixinFunction<TResult2, TResult3>,
80
+ mixin4: MixinFunction<TResult3, TResult4>,
81
+ mixin5: MixinFunction<TResult4, TResult5>,
82
+ mixin6: MixinFunction<TResult5, TResult6>,
83
+ mixin7: MixinFunction<TResult6, TResult7>,
84
+ mixin8: MixinFunction<TResult7, TResult8>,
85
+ mixin9: MixinFunction<TResult8, TResult9>
86
+ ): TResult9
87
+ export function compose<TBase extends Constructor, TMixin extends MixinFunction<TBase, TBase>>(
88
+ baseClass: TBase,
89
+ ...mixins: TMixin[]
90
+ ) {
91
+ return mixins.reduce((currentClass, mixin) => mixin(currentClass), baseClass);
92
+ }
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Converts a number or string value to a BigInt, preserving null and undefined values.
3
+ *
4
+ * @param value - The value to convert. Can be a number, string, undefined, or null.
5
+ * @returns The converted BigInt value, or the original value if it was null or undefined.
6
+ *
7
+ * @example
8
+ * ```typescript
9
+ * convertToBigInt(123) // returns 123n
10
+ * convertToBigInt("456") // returns 456n
11
+ * convertToBigInt(null) // returns null
12
+ * convertToBigInt(undefined) // returns undefined
13
+ * ```
14
+ *
15
+ * @throws {RangeError} Throws when the input cannot be converted to a valid BigInt.
16
+ */
17
+ export const convertToBigInt = (value: number | string | undefined | null): bigint | undefined | null => {
18
+ if (value === null) {
19
+ return value;
20
+ }
21
+
22
+ if (typeof value === 'undefined') {
23
+ return value;
24
+ }
25
+
26
+ return BigInt(value);
27
+ };