@bgord/tools 0.17.2 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/age-years.vo.d.ts +11 -0
- package/dist/age-years.vo.js +9 -0
- package/dist/age.vo.d.ts +11 -16
- package/dist/age.vo.js +20 -31
- package/dist/api-key.vo.d.ts +3 -1
- package/dist/api-key.vo.js +10 -5
- package/dist/basename.vo.d.ts +9 -9
- package/dist/basename.vo.js +22 -22
- package/dist/clock.vo.d.ts +10 -4
- package/dist/clock.vo.js +12 -14
- package/dist/date-calculator.service.d.ts +2 -1
- package/dist/date-formatter.service.d.ts +3 -4
- package/dist/date-range.vo.d.ts +7 -1
- package/dist/date-range.vo.js +5 -2
- package/dist/day-iso-id.vo.d.ts +5 -2
- package/dist/day-iso-id.vo.js +11 -7
- package/dist/day.vo.d.ts +4 -3
- package/dist/day.vo.js +18 -16
- package/dist/directory-path-absolute.vo.d.ts +10 -6
- package/dist/directory-path-absolute.vo.js +19 -17
- package/dist/directory-path-relative.vo.d.ts +10 -7
- package/dist/directory-path-relative.vo.js +18 -17
- package/dist/division-factor.vo.d.ts +7 -0
- package/dist/division-factor.vo.js +9 -0
- package/dist/duration-ms.vo.d.ts +6 -0
- package/dist/duration-ms.vo.js +3 -0
- package/dist/duration.service.d.ts +2 -14
- package/dist/duration.service.js +16 -35
- package/dist/email-mask.service.d.ts +1 -6
- package/dist/email-mask.service.js +6 -8
- package/dist/etags.vo.d.ts +4 -3
- package/dist/etags.vo.js +3 -3
- package/dist/extension.vo.d.ts +6 -4
- package/dist/extension.vo.js +15 -10
- package/dist/feature-flag-value.vo.d.ts +10 -0
- package/dist/feature-flag-value.vo.js +8 -0
- package/dist/feature-flag.vo.d.ts +1 -7
- package/dist/feature-flag.vo.js +1 -7
- package/dist/file-path-absolute-schema.vo.d.ts +10 -7
- package/dist/file-path-absolute-schema.vo.js +17 -17
- package/dist/file-path-relative-schema.vo.d.ts +10 -7
- package/dist/file-path-relative-schema.vo.js +14 -12
- package/dist/file-path.vo.d.ts +4 -4
- package/dist/file-path.vo.js +8 -8
- package/dist/filename-from-string.vo.d.ts +4 -2
- package/dist/filename-from-string.vo.js +10 -8
- package/dist/filename-suffix.vo.d.ts +7 -3
- package/dist/filename-suffix.vo.js +13 -7
- package/dist/filename.vo.d.ts +2 -0
- package/dist/filename.vo.js +8 -2
- package/dist/height-milimiters.vo.d.ts +6 -0
- package/dist/height-milimiters.vo.js +10 -0
- package/dist/height.vo.d.ts +3 -20
- package/dist/height.vo.js +11 -62
- package/dist/hour-format.service.js +1 -1
- package/dist/hour-schema.vo.d.ts +7 -0
- package/dist/hour-schema.vo.js +8 -0
- package/dist/hour.vo.d.ts +4 -3
- package/dist/hour.vo.js +8 -8
- package/dist/iban-mask.service.d.ts +1 -3
- package/dist/iban-mask.service.js +2 -2
- package/dist/iban-schema.vo.d.ts +7 -0
- package/dist/iban-schema.vo.js +10 -0
- package/dist/iban.vo.d.ts +4 -10
- package/dist/iban.vo.js +6 -13
- package/dist/image.vo.d.ts +6 -4
- package/dist/image.vo.js +13 -12
- package/dist/index.d.ts +24 -2
- package/dist/index.js +24 -2
- package/dist/language.vo.d.ts +2 -1
- package/dist/language.vo.js +6 -4
- package/dist/linear-regression.service.d.ts +27 -0
- package/dist/{simple-linear-regression.service.js → linear-regression.service.js} +17 -15
- package/dist/mean.service.d.ts +3 -1
- package/dist/mean.service.js +3 -4
- package/dist/mime-types.vo.d.ts +1 -2
- package/dist/mime-value.vo.d.ts +9 -0
- package/dist/mime-value.vo.js +9 -0
- package/dist/mime.vo.d.ts +11 -17
- package/dist/mime.vo.js +10 -27
- package/dist/min-max-scaler.service.d.ts +7 -5
- package/dist/min-max-scaler.service.js +12 -10
- package/dist/minute-schema.vo.d.ts +7 -0
- package/dist/minute-schema.vo.js +8 -0
- package/dist/minute.vo.d.ts +4 -3
- package/dist/minute.vo.js +8 -8
- package/dist/money-amount.vo.d.ts +7 -0
- package/dist/money-amount.vo.js +7 -0
- package/dist/money.vo.d.ts +9 -18
- package/dist/money.vo.js +14 -27
- package/dist/month-iso-id.vo.d.ts +4 -2
- package/dist/month-iso-id.vo.js +13 -7
- package/dist/month.vo.d.ts +4 -3
- package/dist/month.vo.js +21 -21
- package/dist/multiplication-factor.vo.d.ts +7 -0
- package/dist/multiplication-factor.vo.js +9 -0
- package/dist/object-key.vo.d.ts +9 -6
- package/dist/object-key.vo.js +20 -19
- package/dist/outlier-detector.service.d.ts +3 -1
- package/dist/outlier-detector.service.js +2 -2
- package/dist/package-version-schema.vo.d.ts +11 -0
- package/dist/package-version-schema.vo.js +15 -0
- package/dist/package-version.vo.d.ts +11 -20
- package/dist/package-version.vo.js +11 -20
- package/dist/pagination-page.vo.d.ts +6 -0
- package/dist/pagination-page.vo.js +7 -0
- package/dist/pagination-skip.vo.d.ts +7 -0
- package/dist/pagination-skip.vo.js +9 -0
- package/dist/pagination-take.vo.d.ts +7 -0
- package/dist/pagination-take.vo.js +9 -0
- package/dist/pagination.service.d.ts +3 -8
- package/dist/pagination.service.js +5 -12
- package/dist/percentage.service.d.ts +3 -1
- package/dist/percentage.service.js +2 -2
- package/dist/population-standard-deviation.service.d.ts +3 -1
- package/dist/population-standard-deviation.service.js +5 -4
- package/dist/quarter-iso-id.vo.d.ts +3 -2
- package/dist/quarter-iso-id.vo.js +7 -9
- package/dist/quarter.vo.d.ts +2 -1
- package/dist/quarter.vo.js +10 -7
- package/dist/random.service.d.ts +3 -4
- package/dist/random.service.js +5 -11
- package/dist/rate-limiter.service.d.ts +2 -2
- package/dist/rate-limiter.service.js +8 -8
- package/dist/reordering-item-position-value.vo.d.ts +6 -0
- package/dist/reordering-item-position-value.vo.js +6 -0
- package/dist/reordering.service.d.ts +7 -23
- package/dist/reordering.service.js +15 -24
- package/dist/revision-value.vo.d.ts +7 -0
- package/dist/revision-value.vo.js +6 -0
- package/dist/revision.vo.d.ts +6 -13
- package/dist/revision.vo.js +10 -22
- package/dist/rounding.adapter.d.ts +7 -2
- package/dist/rounding.adapter.js +13 -5
- package/dist/size-bytes.vo.d.ts +6 -0
- package/dist/size-bytes.vo.js +7 -0
- package/dist/size.vo.d.ts +15 -15
- package/dist/size.vo.js +41 -51
- package/dist/stopwatch.service.d.ts +3 -1
- package/dist/stopwatch.service.js +2 -2
- package/dist/sum.service.js +8 -8
- package/dist/thousands-separator.service.js +4 -1
- package/dist/time.service.d.ts +8 -0
- package/dist/time.service.js +13 -0
- package/dist/timestamp.vo.d.ts +1 -1
- package/dist/timestamp.vo.js +4 -5
- package/dist/timezone.vo.d.ts +4 -1
- package/dist/timezone.vo.js +12 -6
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/week-iso-id.vo.d.ts +4 -2
- package/dist/week-iso-id.vo.js +15 -9
- package/dist/week.vo.d.ts +4 -3
- package/dist/week.vo.js +21 -22
- package/dist/weekday.vo.d.ts +1 -1
- package/dist/weekday.vo.js +6 -8
- package/dist/weight-grams.vo.d.ts +7 -0
- package/dist/weight-grams.vo.js +7 -0
- package/dist/weight.vo.d.ts +12 -35
- package/dist/weight.vo.js +23 -72
- package/dist/year-iso-id.vo.d.ts +3 -2
- package/dist/year-iso-id.vo.js +6 -4
- package/dist/year.vo.d.ts +5 -6
- package/dist/year.vo.js +21 -26
- package/dist/z-score.service.d.ts +3 -1
- package/dist/z-score.service.js +2 -2
- package/package.json +4 -4
- package/readme.md +21 -2
- package/src/age-years.vo.ts +14 -0
- package/src/age.vo.ts +22 -35
- package/src/api-key.vo.ts +11 -5
- package/src/basename.vo.ts +24 -22
- package/src/clock.vo.ts +16 -17
- package/src/date-calculator.service.ts +2 -1
- package/src/date-formatter.service.ts +4 -5
- package/src/date-range.vo.ts +6 -2
- package/src/day-iso-id.vo.ts +12 -8
- package/src/day.vo.ts +27 -24
- package/src/directory-path-absolute.vo.ts +23 -18
- package/src/directory-path-relative.vo.ts +21 -18
- package/src/division-factor.vo.ts +13 -0
- package/src/duration-ms.vo.ts +7 -0
- package/src/duration.service.ts +16 -40
- package/src/email-mask.service.ts +7 -15
- package/src/etags.vo.ts +4 -5
- package/src/extension.vo.ts +17 -10
- package/src/feature-flag-value.vo.ts +12 -0
- package/src/feature-flag.vo.ts +1 -9
- package/src/file-path-absolute-schema.vo.ts +18 -17
- package/src/file-path-relative-schema.vo.ts +15 -12
- package/src/file-path.vo.ts +8 -8
- package/src/filename-from-string.vo.ts +12 -9
- package/src/filename-suffix.vo.ts +14 -7
- package/src/filename.vo.ts +11 -2
- package/src/height-milimiters.vo.ts +12 -0
- package/src/height.vo.ts +12 -83
- package/src/hour-format.service.ts +2 -1
- package/src/hour-schema.vo.ts +12 -0
- package/src/hour.vo.ts +12 -12
- package/src/iban-mask.service.ts +3 -5
- package/src/iban-schema.vo.ts +15 -0
- package/src/iban.vo.ts +9 -22
- package/src/image.vo.ts +14 -12
- package/src/index.ts +24 -2
- package/src/language.vo.ts +7 -4
- package/src/linear-regression.service.ts +71 -0
- package/src/mean.service.ts +3 -5
- package/src/mime-types.vo.ts +1 -3
- package/src/mime-value.vo.ts +12 -0
- package/src/mime.vo.ts +12 -33
- package/src/min-max-scaler.service.ts +13 -11
- package/src/minute-schema.vo.ts +12 -0
- package/src/minute.vo.ts +12 -12
- package/src/money-amount.vo.ts +11 -0
- package/src/money.vo.ts +20 -38
- package/src/month-iso-id.vo.ts +14 -7
- package/src/month.vo.ts +25 -24
- package/src/multiplication-factor.vo.ts +13 -0
- package/src/object-key.vo.ts +25 -21
- package/src/outlier-detector.service.ts +2 -2
- package/src/package-version-schema.vo.ts +21 -0
- package/src/package-version.vo.ts +17 -33
- package/src/pagination-page.vo.ts +11 -0
- package/src/pagination-skip.vo.ts +13 -0
- package/src/pagination-take.vo.ts +13 -0
- package/src/pagination.service.ts +5 -22
- package/src/percentage.service.ts +2 -2
- package/src/population-standard-deviation.service.ts +5 -4
- package/src/quarter-iso-id.vo.ts +7 -10
- package/src/quarter.vo.ts +14 -9
- package/src/random.service.ts +6 -9
- package/src/rate-limiter.service.ts +9 -8
- package/src/reordering-item-position-value.vo.ts +10 -0
- package/src/reordering.service.ts +19 -28
- package/src/revision-value.vo.ts +10 -0
- package/src/revision.vo.ts +10 -25
- package/src/rounding.adapter.ts +16 -3
- package/src/size-bytes.vo.ts +11 -0
- package/src/size.vo.ts +43 -54
- package/src/stopwatch.service.ts +3 -3
- package/src/sum.service.ts +8 -8
- package/src/thousands-separator.service.ts +4 -1
- package/src/time.service.ts +15 -0
- package/src/timestamp.vo.ts +4 -5
- package/src/timezone.vo.ts +12 -6
- package/src/week-iso-id.vo.ts +16 -12
- package/src/week.vo.ts +26 -28
- package/src/weekday.vo.ts +6 -9
- package/src/weight-grams.vo.ts +11 -0
- package/src/weight.vo.ts +28 -85
- package/src/year-iso-id.vo.ts +7 -4
- package/src/year.vo.ts +27 -33
- package/src/z-score.service.ts +2 -2
- package/dist/simple-linear-regression.service.d.ts +0 -25
- package/dist/streak-calculator.service.d.ts +0 -13
- package/dist/streak-calculator.service.js +0 -22
- package/src/simple-linear-regression.service.ts +0 -69
- package/src/streak-calculator.service.ts +0 -32
package/src/month.vo.ts
CHANGED
|
@@ -1,46 +1,47 @@
|
|
|
1
|
+
import { endOfMonth, format, getMonth, setMonth, startOfMonth } from "date-fns";
|
|
1
2
|
import { DateRange } from "./date-range.vo";
|
|
2
3
|
import { MonthIsoId, type MonthIsoIdType } from "./month-iso-id.vo";
|
|
3
4
|
import { Timestamp, type TimestampType } from "./timestamp.vo";
|
|
4
5
|
|
|
5
6
|
export class Month extends DateRange {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
static fromTimestamp(timestamp: TimestampType): Month {
|
|
8
|
+
const start = Timestamp.parse(startOfMonth(timestamp).getTime());
|
|
9
|
+
const end = Timestamp.parse(endOfMonth(timestamp).getTime());
|
|
9
10
|
|
|
10
|
-
|
|
11
|
-
return Month.fromTimestamp(Timestamp.parse(this.getStart() - 1));
|
|
11
|
+
return new Month(start, end);
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
return Month.fromTimestamp(
|
|
14
|
+
static fromNow(now: TimestampType): Month {
|
|
15
|
+
return Month.fromTimestamp(now);
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
const
|
|
20
|
-
|
|
18
|
+
static fromIsoId(iso: MonthIsoIdType): Month {
|
|
19
|
+
const [year, month] = MonthIsoId.parse(iso).split("-").map(Number);
|
|
20
|
+
|
|
21
|
+
const reference = setMonth(Date.UTC(year), month - 1).getTime();
|
|
21
22
|
|
|
22
|
-
return Month.fromTimestamp(Timestamp.parse(
|
|
23
|
+
return Month.fromTimestamp(Timestamp.parse(reference));
|
|
23
24
|
}
|
|
24
25
|
|
|
25
|
-
|
|
26
|
-
|
|
26
|
+
toIsoId(): MonthIsoIdType {
|
|
27
|
+
return MonthIsoId.parse(format(this.getStart(), "yyyy-MM"));
|
|
28
|
+
}
|
|
27
29
|
|
|
28
|
-
|
|
30
|
+
previous(): Month {
|
|
31
|
+
return this.shift(-1);
|
|
29
32
|
}
|
|
30
33
|
|
|
31
|
-
|
|
32
|
-
return
|
|
34
|
+
next(): Month {
|
|
35
|
+
return this.shift(1);
|
|
33
36
|
}
|
|
34
37
|
|
|
35
|
-
|
|
36
|
-
const
|
|
37
|
-
const year = Number(validated.slice(0, 4));
|
|
38
|
-
const monthIndex = Number(validated.slice(5, 7)) - 1;
|
|
38
|
+
shift(count: number): Month {
|
|
39
|
+
const shifted = setMonth(this.getStart(), getMonth(this.getStart()) + count).getTime();
|
|
39
40
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
const endUtc = nextStartUtc - 1;
|
|
41
|
+
return Month.fromTimestamp(Timestamp.parse(shifted));
|
|
42
|
+
}
|
|
43
43
|
|
|
44
|
-
|
|
44
|
+
toString(): string {
|
|
45
|
+
return this.toIsoId();
|
|
45
46
|
}
|
|
46
47
|
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { z } from "zod/v4";
|
|
2
|
+
|
|
3
|
+
export const MultiplicationFactorError = {
|
|
4
|
+
Type: "multiplication.factor.type",
|
|
5
|
+
Invalid: "multiplication.factor.invalid",
|
|
6
|
+
} as const;
|
|
7
|
+
|
|
8
|
+
export const MultiplicationFactor = z
|
|
9
|
+
.number(MultiplicationFactorError.Type)
|
|
10
|
+
.min(0, MultiplicationFactorError.Invalid)
|
|
11
|
+
.brand("MultiplicationFactor");
|
|
12
|
+
|
|
13
|
+
export type MultiplicationFactorType = z.infer<typeof MultiplicationFactor>;
|
package/src/object-key.vo.ts
CHANGED
|
@@ -1,30 +1,34 @@
|
|
|
1
1
|
import { z } from "zod/v4";
|
|
2
2
|
|
|
3
|
-
export const
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
export const ObjectKeyError = {
|
|
4
|
+
Type: "object.key.type",
|
|
5
|
+
LeadingSlash: "object.key.leading.slash",
|
|
6
|
+
Empty: "object.key.empty",
|
|
7
|
+
TooLong: "object.key.too.long",
|
|
8
|
+
BadChars: "object.key.bad.chars",
|
|
9
|
+
DotSegments: "object.key.dot.segments",
|
|
10
|
+
} as const;
|
|
8
11
|
|
|
9
|
-
//
|
|
10
|
-
const
|
|
11
|
-
|
|
12
|
+
// Lowercase letters, digits, dots, underscores, and hyphens
|
|
13
|
+
const OBJECT_KEY_SEGMENT_CHARS_WHITELIST = /^[a-z0-9._-]+$/;
|
|
14
|
+
|
|
15
|
+
const DOT_SEGMENTS = [".", ".."];
|
|
12
16
|
|
|
13
17
|
export const ObjectKey = z
|
|
14
|
-
.string()
|
|
15
|
-
.
|
|
16
|
-
|
|
17
|
-
.refine((value) => value.
|
|
18
|
-
|
|
19
|
-
.refine(
|
|
20
|
-
|
|
18
|
+
.string(ObjectKeyError.Type)
|
|
19
|
+
.min(1, ObjectKeyError.Empty)
|
|
20
|
+
.max(256, ObjectKeyError.TooLong)
|
|
21
|
+
.refine((value) => !value.startsWith("/"), ObjectKeyError.LeadingSlash)
|
|
22
|
+
// Allow only known characters for users/avatars/1234567890/avatar.png segments
|
|
23
|
+
.refine(
|
|
24
|
+
(value) => value.split("/").every((segment) => OBJECT_KEY_SEGMENT_CHARS_WHITELIST.test(segment)),
|
|
25
|
+
ObjectKeyError.BadChars,
|
|
26
|
+
)
|
|
27
|
+
// Reject object keys like users/./avatar.png or users/../avatar.png
|
|
21
28
|
.refine(
|
|
22
|
-
(value) =>
|
|
23
|
-
|
|
24
|
-
.split("/")
|
|
25
|
-
.every((segment) => SEGMENT_ALLOWED_REGEX.test(segment) && segment !== "." && segment !== ".."),
|
|
26
|
-
ObjectKeyBadSegmentsError,
|
|
29
|
+
(value) => value.split("/").every((segment) => !DOT_SEGMENTS.includes(segment)),
|
|
30
|
+
ObjectKeyError.DotSegments,
|
|
27
31
|
)
|
|
28
|
-
.brand("
|
|
32
|
+
.brand("ObjectKey");
|
|
29
33
|
|
|
30
34
|
export type ObjectKeyType = z.infer<typeof ObjectKey>;
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { ZScore } from "./z-score.service";
|
|
2
2
|
|
|
3
|
-
export const
|
|
3
|
+
export const OutlierDetectorError = { NotEnoughValues: "outlier.detector.not.enough.values" } as const;
|
|
4
4
|
|
|
5
5
|
export class OutlierDetector {
|
|
6
6
|
private readonly zScore: ZScore;
|
|
7
7
|
private readonly threshold: number;
|
|
8
8
|
|
|
9
9
|
constructor(values: number[], threshold: number) {
|
|
10
|
-
if (values.length < 2) throw new Error(
|
|
10
|
+
if (values.length < 2) throw new Error(OutlierDetectorError.NotEnoughValues);
|
|
11
11
|
|
|
12
12
|
this.zScore = new ZScore(values);
|
|
13
13
|
this.threshold = Math.abs(threshold);
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { z } from "zod/v4";
|
|
2
|
+
|
|
3
|
+
export const PackageVersionSchemaError = {
|
|
4
|
+
Type: "package.version.schema.error",
|
|
5
|
+
BadChars: "package.version.schema.bad.chars",
|
|
6
|
+
} as const;
|
|
7
|
+
|
|
8
|
+
// v, 1-4 digits, dot, 1-4 digits, dot, 1-4 digits - () for capturing groups
|
|
9
|
+
const PACKAGE_VERSIONS_CHARS_WHITELIST = /^v([0-9]{1,4})\.([0-9]{1,4})\.([0-9]{1,4})$/;
|
|
10
|
+
|
|
11
|
+
export const PackageVersionSchema = z
|
|
12
|
+
.string(PackageVersionSchemaError.Type)
|
|
13
|
+
.regex(PACKAGE_VERSIONS_CHARS_WHITELIST, PackageVersionSchemaError.BadChars)
|
|
14
|
+
.transform((value) => {
|
|
15
|
+
const match = PACKAGE_VERSIONS_CHARS_WHITELIST.exec(value)!;
|
|
16
|
+
|
|
17
|
+
return { major: Number(match[1]), minor: Number(match[2]), patch: Number(match[3]) };
|
|
18
|
+
})
|
|
19
|
+
.brand("PackageVersionSchema");
|
|
20
|
+
|
|
21
|
+
export type PackageVersionSchemaType = z.infer<typeof PackageVersionSchema>;
|
|
@@ -1,33 +1,23 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { PackageVersionSchema } from "./package-version-schema.vo";
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
export const PackageVersionValue = z
|
|
10
|
-
.string(PackageVersionError)
|
|
11
|
-
.regex(/^v(\d+)\.(\d+)\.(\d+)$/, PackageVersionError)
|
|
12
|
-
.transform((value) => {
|
|
13
|
-
const match = /^v(\d+)\.(\d+)\.(\d+)$/.exec(value)!;
|
|
3
|
+
export class PackageVersion {
|
|
4
|
+
constructor(
|
|
5
|
+
private readonly major: number,
|
|
6
|
+
private readonly minor: number,
|
|
7
|
+
private readonly patch: number,
|
|
8
|
+
) {}
|
|
14
9
|
|
|
15
|
-
|
|
16
|
-
const
|
|
17
|
-
const patch = Number(match[3]);
|
|
10
|
+
static fromStringWithV(candidate: string): PackageVersion {
|
|
11
|
+
const version = PackageVersionSchema.parse(candidate);
|
|
18
12
|
|
|
19
|
-
return
|
|
20
|
-
}
|
|
21
|
-
.brand("PackageVersionValue");
|
|
13
|
+
return new PackageVersion(version.major, version.minor, version.patch);
|
|
14
|
+
}
|
|
22
15
|
|
|
23
|
-
|
|
16
|
+
static fromString(candidate: string): PackageVersion {
|
|
17
|
+
const version = PackageVersionSchema.parse(`v${candidate}`);
|
|
24
18
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
readonly major: MajorType,
|
|
28
|
-
readonly minor: MinorType,
|
|
29
|
-
readonly patch: PatchType,
|
|
30
|
-
) {}
|
|
19
|
+
return new PackageVersion(version.major, version.minor, version.patch);
|
|
20
|
+
}
|
|
31
21
|
|
|
32
22
|
isGreaterThanOrEqual(another: PackageVersion): boolean {
|
|
33
23
|
if (this.major > another.major) return true;
|
|
@@ -46,13 +36,7 @@ export class PackageVersion {
|
|
|
46
36
|
return `${this.major}.${this.minor}.${this.patch}`;
|
|
47
37
|
}
|
|
48
38
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
return new PackageVersion(parsed.major, parsed.minor, parsed.patch);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
static fromString(value: string): PackageVersion {
|
|
55
|
-
const parsed = PackageVersionValue.parse(`v${value}`);
|
|
56
|
-
return new PackageVersion(parsed.major, parsed.minor, parsed.patch);
|
|
39
|
+
toJSON(): { major: number; minor: number; patch: number } {
|
|
40
|
+
return { major: this.major, minor: this.minor, patch: this.patch };
|
|
57
41
|
}
|
|
58
42
|
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { z } from "zod/v4";
|
|
2
|
+
|
|
3
|
+
export const PaginationPageError = { Type: "pagination.page.Type" } as const;
|
|
4
|
+
|
|
5
|
+
export const Page = z.coerce
|
|
6
|
+
.number(PaginationPageError.Type)
|
|
7
|
+
.int(PaginationPageError.Type)
|
|
8
|
+
.transform((value) => (value <= 0 ? 1 : value))
|
|
9
|
+
.default(1);
|
|
10
|
+
|
|
11
|
+
export type PageType = z.infer<typeof Page>;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { z } from "zod/v4";
|
|
2
|
+
|
|
3
|
+
export const PaginationSkipError = {
|
|
4
|
+
Type: "pagination.skip.type",
|
|
5
|
+
Invalid: "pagination.skip.invalid",
|
|
6
|
+
} as const;
|
|
7
|
+
|
|
8
|
+
export const Skip = z
|
|
9
|
+
.number(PaginationSkipError.Type)
|
|
10
|
+
.int(PaginationSkipError.Type)
|
|
11
|
+
.gte(0, PaginationSkipError.Invalid);
|
|
12
|
+
|
|
13
|
+
export type SkipType = z.infer<typeof Skip>;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { z } from "zod/v4";
|
|
2
|
+
|
|
3
|
+
export const PaginationTakeError = {
|
|
4
|
+
Type: "pagination.take.type",
|
|
5
|
+
Invalid: "pagination.take.invalid",
|
|
6
|
+
} as const;
|
|
7
|
+
|
|
8
|
+
export const Take = z
|
|
9
|
+
.number(PaginationTakeError.Type)
|
|
10
|
+
.int(PaginationTakeError.Type)
|
|
11
|
+
.gte(1, PaginationTakeError.Invalid);
|
|
12
|
+
|
|
13
|
+
export type TakeType = z.infer<typeof Take>;
|
|
@@ -1,24 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
const PaginationPageError = { error: "pagination.page.invalid" } as const;
|
|
6
|
-
|
|
7
|
-
const Take = z.number(PaginationTakeError).int(PaginationTakeError).gte(1, PaginationTakeError);
|
|
8
|
-
|
|
9
|
-
type TakeType = z.infer<typeof Take>;
|
|
10
|
-
|
|
11
|
-
const Skip = z.number(PaginationSkipError).int(PaginationSkipError).gte(0, PaginationSkipError);
|
|
12
|
-
|
|
13
|
-
type SkipType = z.infer<typeof Skip>;
|
|
14
|
-
|
|
15
|
-
const Page = z.coerce
|
|
16
|
-
.number(PaginationPageError)
|
|
17
|
-
.int(PaginationPageError)
|
|
18
|
-
.transform((value) => (value <= 0 ? 1 : value))
|
|
19
|
-
.default(1);
|
|
20
|
-
|
|
21
|
-
export type PageType = z.infer<typeof Page>;
|
|
1
|
+
import { Page, type PageType } from "./pagination-page.vo";
|
|
2
|
+
import { Skip, type SkipType } from "./pagination-skip.vo";
|
|
3
|
+
import { Take, type TakeType } from "./pagination-take.vo";
|
|
4
|
+
import { RoundUp } from "./rounding.adapter";
|
|
22
5
|
|
|
23
6
|
export type PaginationType = { values: { take: TakeType; skip: SkipType }; page: PageType };
|
|
24
7
|
export type PaginationValuesType = Record<string, unknown>;
|
|
@@ -57,7 +40,7 @@ export class Pagination {
|
|
|
57
40
|
}
|
|
58
41
|
|
|
59
42
|
private static getLastPage(config: PaginationExhaustedConfig): PageType {
|
|
60
|
-
return Page.parse(
|
|
43
|
+
return Page.parse(new RoundUp().round(config.total / config.pagination.values.take));
|
|
61
44
|
}
|
|
62
45
|
|
|
63
46
|
static empty = {
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { RoundToNearest } from "./rounding.adapter";
|
|
2
2
|
import type { RoundingPort } from "./rounding.port";
|
|
3
3
|
|
|
4
|
-
export const
|
|
4
|
+
export const PercentageError = { InvalidDenominator: "percentage.invalid.denominator" } as const;
|
|
5
5
|
|
|
6
6
|
export class Percentage {
|
|
7
7
|
static of(numerator: number, denominator: number, rounding: RoundingPort = new RoundToNearest()): number {
|
|
8
|
-
if (denominator === 0) throw new Error(
|
|
8
|
+
if (denominator === 0) throw new Error(PercentageError.InvalidDenominator);
|
|
9
9
|
if (numerator === 0) return 0;
|
|
10
10
|
return rounding.round((numerator / denominator) * 100);
|
|
11
11
|
}
|
|
@@ -3,19 +3,20 @@ import { RoundToDecimal } from "./rounding.adapter";
|
|
|
3
3
|
import type { RoundingPort } from "./rounding.port";
|
|
4
4
|
import { Sum } from "./sum.service";
|
|
5
5
|
|
|
6
|
-
export const
|
|
6
|
+
export const PopulationStandardDeviationError = {
|
|
7
|
+
NotEnoughValues: "population.standard.deviation.not.enough.values",
|
|
8
|
+
} as const;
|
|
7
9
|
|
|
8
10
|
export class PopulationStandardDeviation {
|
|
9
11
|
static calculate(values: number[], rounding: RoundingPort = new RoundToDecimal(2)): number {
|
|
10
|
-
if (values.length < 2) throw new Error(
|
|
12
|
+
if (values.length < 2) throw new Error(PopulationStandardDeviationError.NotEnoughValues);
|
|
11
13
|
|
|
12
14
|
const mean = Mean.calculate(values);
|
|
13
15
|
const count = values.length;
|
|
14
16
|
|
|
15
17
|
const squaredDifferences = values.map((value) => (value - mean) ** 2);
|
|
16
|
-
const sumOfSquaredDifferences = Sum.of(squaredDifferences);
|
|
17
18
|
|
|
18
|
-
const variance =
|
|
19
|
+
const variance = Sum.of(squaredDifferences) / count;
|
|
19
20
|
|
|
20
21
|
return rounding.round(Math.sqrt(variance));
|
|
21
22
|
}
|
package/src/quarter-iso-id.vo.ts
CHANGED
|
@@ -1,16 +1,13 @@
|
|
|
1
1
|
import { z } from "zod/v4";
|
|
2
2
|
|
|
3
|
-
export const QuarterIsoIdError = {
|
|
3
|
+
export const QuarterIsoIdError = {
|
|
4
|
+
Type: "quarter.iso.id.type",
|
|
5
|
+
BadChars: "quarter.iso.id.bad.chars",
|
|
6
|
+
} as const;
|
|
4
7
|
|
|
5
8
|
export const QuarterIsoId = z
|
|
6
|
-
.string(QuarterIsoIdError)
|
|
7
|
-
.regex(/^\d{4}-Q[1-4]$/, QuarterIsoIdError)
|
|
8
|
-
.
|
|
9
|
-
const [yearPart, quarterPart] = value.split("-Q");
|
|
10
|
-
const year = Number(yearPart);
|
|
11
|
-
const quarter = Number(quarterPart);
|
|
12
|
-
|
|
13
|
-
return Number.isInteger(year) && Number.isInteger(quarter) && quarter >= 1 && quarter <= 4;
|
|
14
|
-
}, QuarterIsoIdError);
|
|
9
|
+
.string(QuarterIsoIdError.Type)
|
|
10
|
+
.regex(/^\d{4}-Q[1-4]$/, QuarterIsoIdError.BadChars)
|
|
11
|
+
.brand("QuarterIsoId");
|
|
15
12
|
|
|
16
13
|
export type QuarterIsoIdType = z.infer<typeof QuarterIsoId>;
|
package/src/quarter.vo.ts
CHANGED
|
@@ -4,13 +4,6 @@ import { QuarterIsoId, type QuarterIsoIdType } from "./quarter-iso-id.vo";
|
|
|
4
4
|
import { Timestamp, type TimestampType } from "./timestamp.vo";
|
|
5
5
|
|
|
6
6
|
export class Quarter extends DateRange {
|
|
7
|
-
toIsoId(): QuarterIsoIdType {
|
|
8
|
-
const year = getYear(this.getStart());
|
|
9
|
-
const quarter = getQuarter(this.getStart());
|
|
10
|
-
|
|
11
|
-
return `${year}-Q${quarter}` as QuarterIsoIdType;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
7
|
static fromTimestamp(timestamp: TimestampType): Quarter {
|
|
15
8
|
const start = Timestamp.parse(startOfQuarter(timestamp).getTime());
|
|
16
9
|
const end = Timestamp.parse(endOfQuarter(timestamp).getTime());
|
|
@@ -24,8 +17,20 @@ export class Quarter extends DateRange {
|
|
|
24
17
|
|
|
25
18
|
static fromIsoId(isoId: QuarterIsoIdType): Quarter {
|
|
26
19
|
const [year, quarter] = QuarterIsoId.parse(isoId).split("-Q").map(Number);
|
|
27
|
-
const reference = setQuarter(new Date(Date.UTC(year, 0, 1)), quarter);
|
|
28
20
|
|
|
29
|
-
|
|
21
|
+
const reference = setQuarter(Date.UTC(year), quarter).getTime();
|
|
22
|
+
|
|
23
|
+
return Quarter.fromTimestamp(Timestamp.parse(reference));
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
toIsoId(): QuarterIsoIdType {
|
|
27
|
+
const year = getYear(this.getStart());
|
|
28
|
+
const quarter = getQuarter(this.getStart());
|
|
29
|
+
|
|
30
|
+
return QuarterIsoId.parse(`${year}-Q${quarter}`);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
toString(): string {
|
|
34
|
+
return this.toIsoId();
|
|
30
35
|
}
|
|
31
36
|
}
|
package/src/random.service.ts
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
|
+
import { RoundDown } from "./rounding.adapter";
|
|
2
|
+
|
|
1
3
|
type RandomGenerateConfigType = { min: number; max: number };
|
|
2
4
|
|
|
3
|
-
export const
|
|
4
|
-
export const RandomMaxNotIntegerError = "random.max.not.integer" as const;
|
|
5
|
-
export const RandomMinEqualsMaxError = "random.min.equals.max" as const;
|
|
6
|
-
export const RandomMinGreaterThanMaxError = "random.min.greater.than.max" as const;
|
|
5
|
+
export const RandomError = { MinMax: "random.min.max" } as const;
|
|
7
6
|
|
|
8
7
|
export class Random {
|
|
9
8
|
private static readonly DEFAULT_MIN = 0;
|
|
@@ -13,11 +12,9 @@ export class Random {
|
|
|
13
12
|
const min = config ? config.min : Random.DEFAULT_MIN;
|
|
14
13
|
const max = config ? config.max : Random.DEFAULT_MAX;
|
|
15
14
|
|
|
16
|
-
if (
|
|
17
|
-
if (
|
|
18
|
-
if (min === max) throw new Error(RandomMinEqualsMaxError);
|
|
19
|
-
if (min > max) throw new Error(RandomMinGreaterThanMaxError);
|
|
15
|
+
if (min === max) throw new Error(RandomError.MinMax);
|
|
16
|
+
if (min > max) throw new Error(RandomError.MinMax);
|
|
20
17
|
|
|
21
|
-
return
|
|
18
|
+
return new RoundDown().round(Math.random() * (max - min + 1)) + min;
|
|
22
19
|
}
|
|
23
20
|
}
|
|
@@ -6,25 +6,26 @@ type RateLimiterResultErrorType = { allowed: false; remaining: Duration };
|
|
|
6
6
|
type RateLimiterResultType = RateLimiterResultSuccessType | RateLimiterResultErrorType;
|
|
7
7
|
|
|
8
8
|
export class RateLimiter {
|
|
9
|
-
private
|
|
9
|
+
private lastInvocation: TimestampType | null = null;
|
|
10
10
|
|
|
11
11
|
constructor(private readonly duration: Duration) {}
|
|
12
12
|
|
|
13
|
-
verify(
|
|
14
|
-
if (this.
|
|
15
|
-
this.
|
|
13
|
+
verify(now: TimestampType): RateLimiterResultType {
|
|
14
|
+
if (this.lastInvocation == null) {
|
|
15
|
+
this.lastInvocation = now;
|
|
16
16
|
|
|
17
17
|
return { allowed: true };
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
const
|
|
20
|
+
const nextAllowedTimestamp = this.lastInvocation + this.duration.ms;
|
|
21
|
+
|
|
22
|
+
if (nextAllowedTimestamp <= now) {
|
|
23
|
+
this.lastInvocation = now;
|
|
21
24
|
|
|
22
|
-
if (nextAllowedTimestampMs <= currentTimestampMs) {
|
|
23
|
-
this.lastInvocationTimestampMs = currentTimestampMs;
|
|
24
25
|
return { allowed: true };
|
|
25
26
|
}
|
|
26
27
|
|
|
27
|
-
const remainingDelta =
|
|
28
|
+
const remainingDelta = nextAllowedTimestamp - now;
|
|
28
29
|
|
|
29
30
|
return { allowed: false, remaining: Duration.Ms(remainingDelta) };
|
|
30
31
|
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { z } from "zod/v4";
|
|
2
|
+
|
|
3
|
+
export const ReorderingItemPositionValueError = { Invalid: "reordering.position.type" } as const;
|
|
4
|
+
|
|
5
|
+
export const ReorderingItemPositionValue = z
|
|
6
|
+
.number(ReorderingItemPositionValueError.Invalid)
|
|
7
|
+
.int(ReorderingItemPositionValueError.Invalid)
|
|
8
|
+
.min(0, ReorderingItemPositionValueError.Invalid);
|
|
9
|
+
|
|
10
|
+
export type ReorderingItemPositionValueType = z.infer<typeof ReorderingItemPositionValue>;
|
|
@@ -1,30 +1,24 @@
|
|
|
1
1
|
import { z } from "zod/v4";
|
|
2
2
|
import { DoublyLinkedList, Node } from "./dll.service";
|
|
3
|
+
import {
|
|
4
|
+
ReorderingItemPositionValue,
|
|
5
|
+
type ReorderingItemPositionValueType,
|
|
6
|
+
} from "./reordering-item-position-value.vo";
|
|
3
7
|
|
|
4
|
-
export const
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
export const ReorderingItemIdError = { error: "reordering.item_id.invalid" } as const;
|
|
8
|
+
export const ReorderingError = {
|
|
9
|
+
CannotFindItem: "reordering.cannot.find.item",
|
|
10
|
+
CannotFindCurrent: "reordering.cannot.find.current",
|
|
11
|
+
CannotFindTarget: "reordering.cannot.find.target",
|
|
12
|
+
};
|
|
10
13
|
|
|
11
|
-
|
|
12
|
-
.number(ReorderingPositionError)
|
|
13
|
-
.int(ReorderingPositionError)
|
|
14
|
-
.min(0, ReorderingPositionError);
|
|
15
|
-
export type ReorderingItemPositionValueType = z.infer<typeof ReorderingItemPositionValue>;
|
|
14
|
+
const ReorderingIdError = { Type: "reordering.id.type" } as const;
|
|
16
15
|
|
|
17
|
-
export const
|
|
18
|
-
|
|
19
|
-
.min(1, ReorderingCorrelationIdError);
|
|
20
|
-
export type ReorderingCorrelationIdType = z.infer<typeof ReorderingCorrelationId>;
|
|
21
|
-
|
|
22
|
-
export const ReorderingItemId = z.string(ReorderingItemIdError);
|
|
23
|
-
export type ReorderingItemIdType = z.infer<typeof ReorderingItemId>;
|
|
16
|
+
export const ReorderingId = z.string(ReorderingIdError.Type).min(1, ReorderingIdError.Type);
|
|
17
|
+
export type ReorderingItemIdType = z.infer<typeof ReorderingId>;
|
|
24
18
|
|
|
25
19
|
export const Reordering = z.object({
|
|
26
|
-
correlationId:
|
|
27
|
-
id:
|
|
20
|
+
correlationId: ReorderingId,
|
|
21
|
+
id: ReorderingId,
|
|
28
22
|
position: ReorderingItemPositionValue,
|
|
29
23
|
});
|
|
30
24
|
export type ReorderingType = z.infer<typeof Reordering>;
|
|
@@ -35,10 +29,7 @@ export class ReorderingPosition {
|
|
|
35
29
|
readonly value: ReorderingItemPositionValueType;
|
|
36
30
|
|
|
37
31
|
constructor(value: ReorderingItemPositionValueType) {
|
|
38
|
-
|
|
39
|
-
if (!parsed.success) throw new Error(ReorderingPositionError.error);
|
|
40
|
-
|
|
41
|
-
this.value = value;
|
|
32
|
+
this.value = ReorderingItemPositionValue.parse(value);
|
|
42
33
|
}
|
|
43
34
|
|
|
44
35
|
eq(another: ReorderingPosition): boolean {
|
|
@@ -108,7 +99,7 @@ export class ReorderingCalculator {
|
|
|
108
99
|
|
|
109
100
|
delete(id: ReorderingItem["id"]) {
|
|
110
101
|
const node = this.dll.find((x) => x.data.eq(id));
|
|
111
|
-
if (!node) throw new Error(
|
|
102
|
+
if (!node) throw new Error(ReorderingError.CannotFindItem);
|
|
112
103
|
|
|
113
104
|
this.dll.remove(node);
|
|
114
105
|
this.recalculate();
|
|
@@ -116,10 +107,10 @@ export class ReorderingCalculator {
|
|
|
116
107
|
|
|
117
108
|
transfer(transfer: ReorderingTransfer): ReturnType<ReorderingCalculator["read"]> {
|
|
118
109
|
const current = this.dll.find((node) => node.data.eq(transfer.id));
|
|
119
|
-
if (!current) throw new Error(
|
|
110
|
+
if (!current) throw new Error(ReorderingError.CannotFindCurrent);
|
|
120
111
|
|
|
121
112
|
const target = this.dll.find((node) => node.data.position.eq(transfer.to));
|
|
122
|
-
if (!target) throw new Error(
|
|
113
|
+
if (!target) throw new Error(ReorderingError.CannotFindTarget);
|
|
123
114
|
|
|
124
115
|
const direction = transfer.getDirection(current.data.position);
|
|
125
116
|
if (direction === ReorderingTransferDirection.noop) return this.read();
|
|
@@ -147,7 +138,7 @@ export class ReorderingCalculator {
|
|
|
147
138
|
let index = 0;
|
|
148
139
|
for (const node of this.dll) {
|
|
149
140
|
const id = node.data.id;
|
|
150
|
-
const position = new ReorderingPosition(index);
|
|
141
|
+
const position = new ReorderingPosition(ReorderingItemPositionValue.parse(index));
|
|
151
142
|
node.data = new ReorderingItem(id, position);
|
|
152
143
|
index += 1;
|
|
153
144
|
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { z } from "zod/v4";
|
|
2
|
+
|
|
3
|
+
export const RevisionValueError = { Type: "revision.value.type", Invalid: "revision.value.invalid" } as const;
|
|
4
|
+
|
|
5
|
+
export const RevisionValue = z
|
|
6
|
+
.number(RevisionValueError.Type)
|
|
7
|
+
.int(RevisionValueError.Type)
|
|
8
|
+
.min(0, RevisionValueError.Invalid);
|
|
9
|
+
|
|
10
|
+
export type RevisionValueType = z.infer<typeof RevisionValue>;
|