@bgord/tools 0.6.0 → 0.8.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 (91) hide show
  1. package/dist/clock.vo.d.ts +25 -0
  2. package/dist/clock.vo.js +45 -0
  3. package/dist/dll.service.d.ts +29 -0
  4. package/dist/dll.service.js +148 -0
  5. package/dist/email-mask.service.d.ts +6 -0
  6. package/dist/email-mask.service.js +14 -0
  7. package/dist/feature-flag.vo.d.ts +11 -0
  8. package/dist/feature-flag.vo.js +15 -0
  9. package/dist/filter.vo.d.ts +17 -0
  10. package/dist/filter.vo.js +21 -0
  11. package/dist/hour.vo.d.ts +24 -0
  12. package/dist/hour.vo.js +52 -0
  13. package/dist/image.vo.d.ts +5 -0
  14. package/dist/image.vo.js +3 -0
  15. package/dist/index.d.ts +27 -0
  16. package/dist/index.js +27 -0
  17. package/dist/leap-year-checker.service.d.ts +4 -0
  18. package/dist/leap-year-checker.service.js +9 -0
  19. package/dist/mean.service.d.ts +4 -0
  20. package/dist/mean.service.js +11 -0
  21. package/dist/mime-types.vo.d.ts +2 -0
  22. package/dist/mime-types.vo.js +7 -0
  23. package/dist/mime.vo.d.ts +1 -1
  24. package/dist/min-max-scaler.service.d.ts +36 -0
  25. package/dist/min-max-scaler.service.js +58 -0
  26. package/dist/minute.vo.d.ts +14 -0
  27. package/dist/minute.vo.js +34 -0
  28. package/dist/money.vo.d.ts +24 -0
  29. package/dist/money.vo.js +64 -0
  30. package/dist/outlier-detector.service.d.ts +6 -0
  31. package/dist/outlier-detector.service.js +13 -0
  32. package/dist/pagination.service.d.ts +58 -0
  33. package/dist/pagination.service.js +56 -0
  34. package/dist/percentage.service.d.ts +4 -0
  35. package/dist/percentage.service.js +11 -0
  36. package/dist/population-standard-deviation.service.d.ts +4 -0
  37. package/dist/population-standard-deviation.service.js +16 -0
  38. package/dist/random.service.d.ts +8 -0
  39. package/dist/random.service.js +19 -0
  40. package/dist/reordering.service.d.ts +61 -0
  41. package/dist/reordering.service.js +121 -0
  42. package/dist/revision.vo.d.ts +20 -0
  43. package/dist/revision.vo.js +45 -0
  44. package/dist/simple-linear-regression.service.d.ts +18 -0
  45. package/dist/simple-linear-regression.service.js +50 -0
  46. package/dist/stepper.service.d.ts +23 -0
  47. package/dist/stepper.service.js +34 -0
  48. package/dist/streak-calculator.service.d.ts +14 -0
  49. package/dist/streak-calculator.service.js +27 -0
  50. package/dist/sum.service.d.ts +3 -0
  51. package/dist/sum.service.js +5 -0
  52. package/dist/thousands-separator.service.d.ts +4 -0
  53. package/dist/thousands-separator.service.js +6 -0
  54. package/dist/visually-unambiguous-characters-generator.service.d.ts +4 -0
  55. package/dist/visually-unambiguous-characters-generator.service.js +35 -0
  56. package/dist/z-score.service.d.ts +8 -0
  57. package/dist/z-score.service.js +16 -0
  58. package/package.json +2 -2
  59. package/readme.md +1 -0
  60. package/src/api-key.vo.ts +1 -0
  61. package/src/clock.vo.ts +67 -0
  62. package/src/dll.service.ts +185 -0
  63. package/src/email-mask.service.ts +22 -0
  64. package/src/feature-flag.vo.ts +19 -0
  65. package/src/filter.vo.ts +38 -0
  66. package/src/hour.vo.ts +71 -0
  67. package/src/image.vo.ts +9 -0
  68. package/src/index.ts +27 -0
  69. package/src/leap-year-checker.service.ts +11 -0
  70. package/src/mean.service.ts +14 -0
  71. package/src/mime-types.vo.ts +9 -0
  72. package/src/mime.vo.ts +3 -1
  73. package/src/min-max-scaler.service.ts +94 -0
  74. package/src/minute.vo.ts +46 -0
  75. package/src/money.vo.ts +99 -0
  76. package/src/outlier-detector.service.ts +20 -0
  77. package/src/package-version.vo.ts +2 -0
  78. package/src/pagination.service.ts +111 -0
  79. package/src/percentage.service.ts +17 -0
  80. package/src/population-standard-deviation.service.ts +21 -0
  81. package/src/random.service.ts +29 -0
  82. package/src/rate-limiter.service.ts +1 -3
  83. package/src/reordering.service.ts +169 -0
  84. package/src/revision.vo.ts +61 -0
  85. package/src/simple-linear-regression.service.ts +74 -0
  86. package/src/stepper.service.ts +50 -0
  87. package/src/streak-calculator.service.ts +42 -0
  88. package/src/sum.service.ts +5 -0
  89. package/src/thousands-separator.service.ts +7 -0
  90. package/src/visually-unambiguous-characters-generator.service.ts +42 -0
  91. package/src/z-score.service.ts +24 -0
package/src/index.ts CHANGED
@@ -1,21 +1,48 @@
1
1
  export * from "./api-key.vo";
2
2
  export * from "./build-version.vo";
3
+ export * from "./clock.vo";
3
4
  export * from "./date-calculator.service";
4
5
  export * from "./date-formatter.service";
5
6
  export * from "./dates-of-the-week.vo";
7
+ export * from "./dll.service";
8
+ export * from "./email-mask.service";
6
9
  export * from "./etags.vo";
10
+ export * from "./feature-flag.vo";
11
+ export * from "./filter.vo";
12
+ export * from "./hour.vo";
13
+ export * from "./image.vo";
7
14
  export * from "./language.vo";
15
+ export * from "./leap-year-checker.service";
16
+ export * from "./mean.service";
17
+ export * from "./mime-types.vo";
8
18
  export * from "./mime.vo";
19
+ export * from "./min-max-scaler.service";
20
+ export * from "./minute.vo";
21
+ export * from "./money.vo";
9
22
  export * from "./noop.service";
23
+ export * from "./outlier-detector.service";
10
24
  export * from "./package-version.vo";
25
+ export * from "./pagination.service";
26
+ export * from "./percentage.service";
27
+ export * from "./population-standard-deviation.service";
28
+ export * from "./random.service";
11
29
  export * from "./rate-limiter.service";
12
30
  export * from "./relative-date.vo";
31
+ export * from "./reordering.service";
32
+ export * from "./revision.vo";
13
33
  export * from "./rounding.service";
34
+ export * from "./simple-linear-regression.service";
14
35
  export * from "./size.vo";
15
36
  export * from "./sleep.service";
37
+ export * from "./stepper.service";
16
38
  export * from "./stopwatch.service";
39
+ export * from "./streak-calculator.service";
40
+ export * from "./sum.service";
41
+ export * from "./thousands-separator.service";
17
42
  export * from "./time-zone-offset-value.vo";
18
43
  export * from "./time.service";
19
44
  export * from "./timestamp.vo";
20
45
  export * from "./timezone.vo";
21
46
  export * from "./ts-utils";
47
+ export * from "./visually-unambiguous-characters-generator.service";
48
+ export * from "./z-score.service";
@@ -0,0 +1,11 @@
1
+ export class LeapYearChecker {
2
+ static isLeapYear(year: number): boolean {
3
+ return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0;
4
+ }
5
+
6
+ static isCurrentYearLeapYear(): boolean {
7
+ const year = new Date().getFullYear();
8
+
9
+ return LeapYearChecker.isLeapYear(year);
10
+ }
11
+ }
@@ -0,0 +1,14 @@
1
+ import { RoundToDecimal, RoundingStrategy } from "./rounding.service";
2
+ import { Sum } from "./sum.service";
3
+
4
+ export class Mean {
5
+ static calculate(values: number[], rounding: RoundingStrategy = new RoundToDecimal(2)): number {
6
+ if (values.length === 0) {
7
+ throw new Error("Values should not be empty");
8
+ }
9
+
10
+ const mean = Sum.of(values) / values.length;
11
+
12
+ return rounding.round(mean);
13
+ }
14
+ }
@@ -0,0 +1,9 @@
1
+ import type { MimeRawType } from "./mime.vo";
2
+
3
+ export const MIME_TYPES: Record<string, MimeRawType[]> = {
4
+ wildcard: ["*/*"],
5
+ jpeg: ["image/jpeg"],
6
+ png: ["image/png"],
7
+ wav: ["audio/x-wav", "audio/wav"],
8
+ mp4: ["video/mp4"],
9
+ };
package/src/mime.vo.ts CHANGED
@@ -1,5 +1,7 @@
1
- type MimeRawType = string;
1
+ export type MimeRawType = string;
2
+
2
3
  type MimeTypeType = string;
4
+
3
5
  type MimeSubtypeType = string;
4
6
 
5
7
  export class Mime {
@@ -0,0 +1,94 @@
1
+ import { RoundToDecimal, RoundingStrategy } from "./rounding.service";
2
+
3
+ type MinMaxScalerValueType = number;
4
+
5
+ type MinMaxScalerConfigType = {
6
+ min: MinMaxScalerValueType;
7
+ max: MinMaxScalerValueType;
8
+ bound?: {
9
+ lower: MinMaxScalerValueType;
10
+ upper: MinMaxScalerValueType;
11
+ };
12
+ rounding?: RoundingStrategy;
13
+ };
14
+
15
+ export class MinMaxScaler {
16
+ private readonly min: MinMaxScalerValueType;
17
+ private readonly max: MinMaxScalerValueType;
18
+ private readonly lower: MinMaxScalerValueType;
19
+ private readonly upper: MinMaxScalerValueType;
20
+
21
+ private readonly rounding: RoundingStrategy;
22
+
23
+ constructor(config: MinMaxScalerConfigType) {
24
+ const rounding = config.rounding ?? new RoundToDecimal(2);
25
+
26
+ const lower = config.bound?.lower ?? 0;
27
+ const upper = config.bound?.upper ?? 1;
28
+
29
+ if (config.max - config.min < 0) {
30
+ throw new Error("Invalid MinMaxScaler min-max config");
31
+ }
32
+
33
+ if (upper - lower <= 0) {
34
+ throw new Error("Invalid MinMaxScaler bound config");
35
+ }
36
+
37
+ this.rounding = rounding;
38
+
39
+ this.min = config.min;
40
+ this.max = config.max;
41
+ this.lower = lower;
42
+ this.upper = upper;
43
+ }
44
+
45
+ scale(value: MinMaxScalerValueType) {
46
+ const { min, max, lower, upper } = this;
47
+
48
+ if (value < min || value > max) {
49
+ throw new Error("Value out of min/max range");
50
+ }
51
+
52
+ if (min === max)
53
+ return {
54
+ original: value,
55
+ scaled: (lower + upper) / 2,
56
+ isMin: value === min,
57
+ isMax: value === max,
58
+ };
59
+
60
+ const result = ((value - min) / (max - min)) * (upper - lower) + lower;
61
+
62
+ return {
63
+ original: value,
64
+ scaled: this.rounding.round(result),
65
+ isMin: value === min,
66
+ isMax: value === max,
67
+ };
68
+ }
69
+
70
+ descale(scaled: MinMaxScalerValueType) {
71
+ const { min, max, lower, upper } = this;
72
+
73
+ if (scaled < lower || scaled > upper) {
74
+ throw new Error("Scaled value out of bounds");
75
+ }
76
+
77
+ const result = ((scaled - lower) / (upper - lower)) * (max - min) + min;
78
+
79
+ return {
80
+ original: this.rounding.round(result),
81
+ scaled,
82
+ isLowerBound: scaled === lower,
83
+ isUpperBound: scaled === upper,
84
+ };
85
+ }
86
+
87
+ static getMinMax(values: MinMaxScalerValueType[]) {
88
+ if (values.length === 0) {
89
+ throw new Error("An empty array supplied");
90
+ }
91
+
92
+ return { min: Math.min(...values), max: Math.max(...values) };
93
+ }
94
+ }
@@ -0,0 +1,46 @@
1
+ export class Minute {
2
+ private readonly value: number;
3
+
4
+ static readonly ZERO = new Minute(0);
5
+
6
+ static readonly MAX = new Minute(59);
7
+
8
+ constructor(candidate: number) {
9
+ if (!Number.isInteger(candidate)) {
10
+ throw new Error("Invalid minute");
11
+ }
12
+
13
+ if (candidate < 0) {
14
+ throw new Error("Invalid minute");
15
+ }
16
+
17
+ if (candidate >= 60) {
18
+ throw new Error("Invalid minute");
19
+ }
20
+
21
+ this.value = candidate;
22
+ }
23
+
24
+ get() {
25
+ return {
26
+ raw: this.value,
27
+ formatted: this.value.toString().padStart(2, "0"),
28
+ };
29
+ }
30
+
31
+ equals(another: Minute): boolean {
32
+ return this.value === another.get().raw;
33
+ }
34
+
35
+ isAfter(another: Minute): boolean {
36
+ return this.value > another.get().raw;
37
+ }
38
+
39
+ isBefore(another: Minute): boolean {
40
+ return this.value < another.get().raw;
41
+ }
42
+
43
+ static list() {
44
+ return Array.from({ length: 60 }).map((_, index) => new Minute(index));
45
+ }
46
+ }
@@ -0,0 +1,99 @@
1
+ import { z } from "zod/v4";
2
+
3
+ import { RoundToNearest, RoundingStrategy } from "./rounding.service";
4
+
5
+ export const MoneyAmount = z
6
+ .number()
7
+ .int({ message: "money.amount.invalid " })
8
+ .min(0, { message: "money.amount.invalid " });
9
+
10
+ export type MoneyAmountType = z.infer<typeof MoneyAmount>;
11
+
12
+ export const MoneyMultiplicationFactor = z
13
+ .number()
14
+ .min(0, { message: "money.multiplication-factor.invalid" });
15
+
16
+ export type MoneyMultiplicationFactorType = z.infer<typeof MoneyMultiplicationFactor>;
17
+
18
+ export const MoneyDivisionFactor = z
19
+ .number()
20
+ .min(0, { message: "money.division-factor.invalid" })
21
+ .refine((value) => value !== 0, { message: "money.division-factor.invalid" });
22
+
23
+ export type MoneyDivisionFactorType = z.infer<typeof MoneyDivisionFactor>;
24
+
25
+ export class Money {
26
+ private static readonly ZERO = 0;
27
+
28
+ private readonly amount: MoneyAmountType;
29
+
30
+ private readonly rounding: RoundingStrategy;
31
+
32
+ constructor(value: number = Money.ZERO, rounding?: RoundingStrategy) {
33
+ this.amount = MoneyAmount.parse(value);
34
+ this.rounding = rounding ?? new RoundToNearest();
35
+ }
36
+
37
+ getAmount(): MoneyAmountType {
38
+ return this.amount;
39
+ }
40
+
41
+ add(money: Money) {
42
+ const result = this.rounding.round(this.amount + money.getAmount());
43
+
44
+ return new Money(MoneyAmount.parse(result), this.rounding);
45
+ }
46
+
47
+ multiply(factor: MoneyMultiplicationFactorType) {
48
+ const result = this.rounding.round(this.amount * factor);
49
+
50
+ return new Money(MoneyAmount.parse(result), this.rounding);
51
+ }
52
+
53
+ subtract(money: Money) {
54
+ const result = this.rounding.round(this.amount - money.getAmount());
55
+
56
+ if (result < Money.ZERO) {
57
+ throw new Error("Less than zero");
58
+ }
59
+
60
+ return new Money(MoneyAmount.parse(result), this.rounding);
61
+ }
62
+
63
+ divide(factor: MoneyDivisionFactorType) {
64
+ if (factor === 0) {
65
+ throw new Error("Cannot divide by zero");
66
+ }
67
+
68
+ const result = this.rounding.round(this.amount / factor);
69
+
70
+ return new Money(MoneyAmount.parse(result), this.rounding);
71
+ }
72
+
73
+ equals(another: Money): boolean {
74
+ return this.amount === another.getAmount();
75
+ }
76
+
77
+ isGreaterThan(another: Money): boolean {
78
+ return this.amount > another.getAmount();
79
+ }
80
+
81
+ isLessThan(another: Money): boolean {
82
+ return this.amount < another.getAmount();
83
+ }
84
+
85
+ isZero(): boolean {
86
+ return this.amount === Money.ZERO;
87
+ }
88
+
89
+ format(): string {
90
+ const result = this.amount / 100;
91
+
92
+ const whole = Math.floor(result);
93
+
94
+ const fraction = this.amount % 100;
95
+ const fractionFormatted = fraction.toString().padStart(2, "0");
96
+
97
+ return `${whole}.${fractionFormatted}`;
98
+ }
99
+ }
@@ -0,0 +1,20 @@
1
+ import { ZScore } from "./z-score.service";
2
+
3
+ export class OutlierDetector {
4
+ private readonly zScore: ZScore;
5
+
6
+ private readonly threshold: number;
7
+
8
+ constructor(values: number[], threshold: number) {
9
+ if (values.length < 2) {
10
+ throw new Error("At least two values are needed");
11
+ }
12
+
13
+ this.zScore = new ZScore(values);
14
+ this.threshold = Math.abs(threshold);
15
+ }
16
+
17
+ check(value: number): boolean {
18
+ return this.zScore.calculate(value) <= this.threshold;
19
+ }
20
+ }
@@ -1,7 +1,9 @@
1
1
  import { z } from "zod/v4";
2
2
 
3
3
  type MajorType = number;
4
+
4
5
  type MinorType = number;
6
+
5
7
  type PatchType = number;
6
8
 
7
9
  export const PackageVersionValue = z
@@ -0,0 +1,111 @@
1
+ import { z } from "zod/v4";
2
+
3
+ const Take = z.number().int().positive();
4
+
5
+ type TakeType = z.infer<typeof Take>;
6
+
7
+ const Skip = z.number().int().positive();
8
+
9
+ type SkipType = z.infer<typeof Skip>;
10
+
11
+ const Page = z.coerce
12
+ .number()
13
+ .int()
14
+ .transform((value) => (value <= 0 ? 1 : value))
15
+ .default(1);
16
+
17
+ export type PageType = z.infer<typeof Page>;
18
+
19
+ export type PaginationType = {
20
+ values: { take: TakeType; skip: SkipType };
21
+ page: PageType;
22
+ };
23
+
24
+ export type PaginationValuesType = Record<string, unknown>;
25
+
26
+ export type TotalType = number;
27
+
28
+ export type ExhaustedType = boolean;
29
+
30
+ export type PaginationExhaustedConfig = {
31
+ total: TotalType;
32
+ pagination: PaginationType;
33
+ };
34
+
35
+ export type PaginationPrepareConfigType<T> = {
36
+ total: TotalType;
37
+ pagination: PaginationType;
38
+ result: T[];
39
+ };
40
+
41
+ export class Pagination {
42
+ static parse(values: PaginationValuesType, _take: TakeType): PaginationType {
43
+ const page = Page.parse(values.page);
44
+ const take = Take.parse(_take);
45
+
46
+ const skip = (page - 1) * take;
47
+
48
+ return { values: { take, skip }, page };
49
+ }
50
+
51
+ static prepare<T>(config: PaginationPrepareConfigType<T>): Paged<T> {
52
+ const exhausted = Pagination.isExhausted(config);
53
+
54
+ const currentPage = config.pagination.page;
55
+ const lastPage = Pagination.getLastPage(config);
56
+
57
+ const previousPage = currentPage > 1 ? currentPage - 1 : undefined;
58
+ const nextPage = currentPage < lastPage ? currentPage + 1 : undefined;
59
+
60
+ return {
61
+ result: config.result,
62
+ meta: {
63
+ exhausted,
64
+ currentPage,
65
+ previousPage,
66
+ nextPage,
67
+ lastPage,
68
+ total: config.total,
69
+ },
70
+ };
71
+ }
72
+
73
+ static isExhausted(config: PaginationExhaustedConfig): ExhaustedType {
74
+ const lastPage = Pagination.getLastPage(config);
75
+ const currentPage = config.pagination.page;
76
+
77
+ return lastPage <= currentPage;
78
+ }
79
+
80
+ private static getLastPage(config: PaginationExhaustedConfig): PageType {
81
+ return Math.ceil(config.total / config.pagination.values.take);
82
+ }
83
+
84
+ static empty = {
85
+ result: [],
86
+ meta: {
87
+ exhausted: true,
88
+ currentPage: 1,
89
+ previousPage: undefined,
90
+ nextPage: undefined,
91
+ lastPage: 1,
92
+ total: 0,
93
+ },
94
+ };
95
+
96
+ static getFirstPage({ take }: { take: TakeType }): PaginationType {
97
+ return { values: { take, skip: 0 }, page: 1 };
98
+ }
99
+ }
100
+
101
+ export type Paged<T> = {
102
+ result: T[];
103
+ meta: {
104
+ exhausted: ExhaustedType;
105
+ currentPage: PageType;
106
+ previousPage: PageType | undefined;
107
+ nextPage: PageType | undefined;
108
+ lastPage: PageType;
109
+ total: TotalType;
110
+ };
111
+ };
@@ -0,0 +1,17 @@
1
+ import { RoundToNearest, RoundingStrategy } from "./rounding.service";
2
+
3
+ export class Percentage {
4
+ static of(
5
+ numerator: number,
6
+ denominator: number,
7
+ rounding: RoundingStrategy = new RoundToNearest(),
8
+ ): number {
9
+ if (denominator === 0) {
10
+ throw new Error("Invalid denominator");
11
+ }
12
+
13
+ if (numerator === 0) return 0;
14
+
15
+ return rounding.round((numerator / denominator) * 100);
16
+ }
17
+ }
@@ -0,0 +1,21 @@
1
+ import { Mean } from "./mean.service";
2
+ import { RoundToDecimal, RoundingStrategy } from "./rounding.service";
3
+ import { Sum } from "./sum.service";
4
+
5
+ export class PopulationStandardDeviation {
6
+ static calculate(values: number[], rounding: RoundingStrategy = new RoundToDecimal(2)): number {
7
+ if (values.length < 2) {
8
+ throw new Error("At least two values are needed");
9
+ }
10
+
11
+ const mean = Mean.calculate(values);
12
+ const n = values.length;
13
+
14
+ const squaredDifferences = values.map((value) => (value - mean) ** 2);
15
+ const sumOfSquaredDifferences = Sum.of(squaredDifferences);
16
+
17
+ const variance = sumOfSquaredDifferences / n;
18
+
19
+ return rounding.round(Math.sqrt(variance));
20
+ }
21
+ }
@@ -0,0 +1,29 @@
1
+ type RandomGenerateConfigType = {
2
+ min: number;
3
+ max: number;
4
+ };
5
+
6
+ export class Random {
7
+ static generate(config?: RandomGenerateConfigType) {
8
+ const min = config?.min ?? 0;
9
+ const max = config?.max ?? 1;
10
+
11
+ if (!Number.isInteger(min)) {
12
+ throw new Error("Minimum value is not an integer");
13
+ }
14
+
15
+ if (!Number.isInteger(max)) {
16
+ throw new Error("Maximum value is not an integer");
17
+ }
18
+
19
+ if (min === max) {
20
+ throw new Error("Minimum and maximum values cannot be equal");
21
+ }
22
+
23
+ if (min > max) {
24
+ throw new Error("Minimum value cannot be greater than maximum value");
25
+ }
26
+
27
+ return Math.floor(Math.random() * (max - min + 1)) + min;
28
+ }
29
+ }
@@ -1,9 +1,7 @@
1
1
  import type { TimestampType } from "./timestamp.vo";
2
2
  import type { Falsy } from "./ts-utils";
3
3
 
4
- type RateLimiterOptionsType = {
5
- ms: TimestampType;
6
- };
4
+ type RateLimiterOptionsType = { ms: TimestampType };
7
5
 
8
6
  type RateLimiterResultSuccessType = { allowed: true };
9
7