@bgord/tools 1.2.19 → 1.2.21
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/duration.service.d.ts +3 -0
- package/dist/duration.service.js +7 -0
- package/package.json +2 -3
- package/src/age-years.vo.ts +0 -16
- package/src/age.vo.ts +0 -60
- package/src/api-key.vo.ts +0 -16
- package/src/basename.vo.ts +0 -33
- package/src/clock-format.service.ts +0 -15
- package/src/clock.vo.ts +0 -55
- package/src/date-calculator.service.ts +0 -18
- package/src/date-formatter.service.ts +0 -21
- package/src/date-range.vo.ts +0 -36
- package/src/day-iso-id.vo.ts +0 -21
- package/src/day.vo.ts +0 -59
- package/src/directory-path-absolute.vo.ts +0 -36
- package/src/directory-path-relative.vo.ts +0 -34
- package/src/distance-value.vo.ts +0 -13
- package/src/distance.vo.ts +0 -76
- package/src/division-factor.vo.ts +0 -12
- package/src/dll.service.ts +0 -177
- package/src/duration-ms.vo.ts +0 -9
- package/src/duration.service.ts +0 -89
- package/src/email-mask.service.ts +0 -17
- package/src/email.vo.ts +0 -9
- package/src/etags.vo.ts +0 -47
- package/src/extension.vo.ts +0 -27
- package/src/feature-flag-value.vo.ts +0 -12
- package/src/feature-flag.vo.ts +0 -11
- package/src/file-path-absolute-schema.vo.ts +0 -34
- package/src/file-path-relative-schema.vo.ts +0 -34
- package/src/file-path.vo.ts +0 -100
- package/src/filename-affix.vo.ts +0 -27
- package/src/filename-from-string.vo.ts +0 -27
- package/src/filename.vo.ts +0 -92
- package/src/height-milimiters.vo.ts +0 -16
- package/src/height.vo.ts +0 -79
- package/src/hour-format.service.ts +0 -22
- package/src/hour-schema.vo.ts +0 -14
- package/src/hour.vo.ts +0 -64
- package/src/iban-mask.service.ts +0 -18
- package/src/iban-schema.vo.ts +0 -17
- package/src/iban.vo.ts +0 -28
- package/src/image.vo.ts +0 -28
- package/src/index.ts +0 -105
- package/src/integer-non-negative.vo.ts +0 -16
- package/src/integer-positive.vo.ts +0 -13
- package/src/integer.vo.ts +0 -9
- package/src/language.vo.ts +0 -13
- package/src/linear-regression.service.ts +0 -73
- package/src/mean.service.ts +0 -18
- package/src/mime-types.vo.ts +0 -7
- package/src/mime-value.vo.ts +0 -14
- package/src/mime.vo.ts +0 -50
- package/src/min-max-scaler.service.ts +0 -96
- package/src/minute-schema.vo.ts +0 -14
- package/src/minute.vo.ts +0 -59
- package/src/money-amount.vo.ts +0 -13
- package/src/money.vo.ts +0 -83
- package/src/month-iso-id.vo.ts +0 -24
- package/src/month.vo.ts +0 -53
- package/src/multiplication-factor.vo.ts +0 -15
- package/src/noop.service.ts +0 -3
- package/src/notification-template.vo.ts +0 -10
- package/src/object-key.vo.ts +0 -36
- package/src/outlier-detector.service.ts +0 -21
- package/src/package-version-schema.vo.ts +0 -23
- package/src/package-version.vo.ts +0 -66
- package/src/pagination-page.vo.ts +0 -11
- package/src/pagination-skip.vo.ts +0 -10
- package/src/pagination-take.vo.ts +0 -10
- package/src/pagination.service.ts +0 -73
- package/src/percentage.service.ts +0 -15
- package/src/population-standard-deviation.service.ts +0 -23
- package/src/quarter-iso-id.vo.ts +0 -15
- package/src/quarter.vo.ts +0 -41
- package/src/random.service.ts +0 -19
- package/src/rate-limiter.service.ts +0 -30
- package/src/relative-date.vo.ts +0 -21
- package/src/reordering-item-position-value.vo.ts +0 -10
- package/src/reordering.service.ts +0 -163
- package/src/revision-value.vo.ts +0 -10
- package/src/revision.vo.ts +0 -44
- package/src/rounding-decimal.strategy.ts +0 -25
- package/src/rounding-down.strategy.ts +0 -7
- package/src/rounding-to-nearest.strategy.ts +0 -7
- package/src/rounding-up.strategy.ts +0 -7
- package/src/rounding.strategy.ts +0 -3
- package/src/size-bytes.vo.ts +0 -13
- package/src/size.vo.ts +0 -113
- package/src/slug.service.ts +0 -18
- package/src/sum.service.ts +0 -19
- package/src/thousands-separator.service.ts +0 -10
- package/src/time-zone-offset-value.vo.ts +0 -21
- package/src/timestamp-value.vo.ts +0 -13
- package/src/timestamp.vo.ts +0 -66
- package/src/timezone.vo.ts +0 -26
- package/src/ts-utils.ts +0 -3
- package/src/url-with-slash.vo.ts +0 -12
- package/src/url-without-slash.vo.ts +0 -12
- package/src/visually-unambiguous-characters-generator.service.ts +0 -39
- package/src/week-iso-id.vo.ts +0 -28
- package/src/week.vo.ts +0 -57
- package/src/weekday.vo.ts +0 -119
- package/src/weight-grams.vo.ts +0 -13
- package/src/weight.vo.ts +0 -91
- package/src/year-iso-id.vo.ts +0 -15
- package/src/year.vo.ts +0 -63
- package/src/z-score.service.ts +0 -25
|
@@ -4,6 +4,7 @@ export declare class Duration {
|
|
|
4
4
|
private static readonly rounding;
|
|
5
5
|
private readonly internal;
|
|
6
6
|
static readonly MIN: Duration;
|
|
7
|
+
private static readonly NS_IN_MS;
|
|
7
8
|
private static readonly MS_IN_SECOND;
|
|
8
9
|
private static readonly MS_IN_MINUTE;
|
|
9
10
|
private static readonly MS_IN_HOUR;
|
|
@@ -16,12 +17,14 @@ export declare class Duration {
|
|
|
16
17
|
static Minutes(value: number): Duration;
|
|
17
18
|
static Seconds(value: number): Duration;
|
|
18
19
|
static Ms(value: number): Duration;
|
|
20
|
+
static Ns(value: number): Duration;
|
|
19
21
|
get weeks(): number;
|
|
20
22
|
get days(): number;
|
|
21
23
|
get hours(): number;
|
|
22
24
|
get minutes(): number;
|
|
23
25
|
get seconds(): number;
|
|
24
26
|
get ms(): DurationMsType;
|
|
27
|
+
get ns(): number;
|
|
25
28
|
isLongerThan(another: Duration): boolean;
|
|
26
29
|
isShorterThan(another: Duration): boolean;
|
|
27
30
|
equals(other: Duration): boolean;
|
package/dist/duration.service.js
CHANGED
|
@@ -5,6 +5,7 @@ export class Duration {
|
|
|
5
5
|
static rounding = new RoundingDecimalStrategy(2);
|
|
6
6
|
internal;
|
|
7
7
|
static MIN = Duration.Ms(1);
|
|
8
|
+
static NS_IN_MS = 1000000;
|
|
8
9
|
static MS_IN_SECOND = 1_000;
|
|
9
10
|
static MS_IN_MINUTE = 60 * Duration.MS_IN_SECOND;
|
|
10
11
|
static MS_IN_HOUR = 60 * Duration.MS_IN_MINUTE;
|
|
@@ -31,6 +32,9 @@ export class Duration {
|
|
|
31
32
|
static Ms(value) {
|
|
32
33
|
return new Duration(value);
|
|
33
34
|
}
|
|
35
|
+
static Ns(value) {
|
|
36
|
+
return new Duration(new RoundingToNearestStrategy().round(value / Duration.NS_IN_MS));
|
|
37
|
+
}
|
|
34
38
|
get weeks() {
|
|
35
39
|
return Duration.rounding.round(this.internal / Duration.MS_IN_WEEK);
|
|
36
40
|
}
|
|
@@ -49,6 +53,9 @@ export class Duration {
|
|
|
49
53
|
get ms() {
|
|
50
54
|
return this.internal;
|
|
51
55
|
}
|
|
56
|
+
get ns() {
|
|
57
|
+
return this.internal * Duration.NS_IN_MS;
|
|
58
|
+
}
|
|
52
59
|
isLongerThan(another) {
|
|
53
60
|
return this.internal > another.internal;
|
|
54
61
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bgord/tools",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.21",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Bartosz Gordon",
|
|
@@ -13,8 +13,7 @@
|
|
|
13
13
|
}
|
|
14
14
|
},
|
|
15
15
|
"files": [
|
|
16
|
-
"dist"
|
|
17
|
-
"src"
|
|
16
|
+
"dist"
|
|
18
17
|
],
|
|
19
18
|
"scripts": {
|
|
20
19
|
"build": "bash scripts/package-build.sh",
|
package/src/age-years.vo.ts
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import { z } from "zod/v4";
|
|
2
|
-
|
|
3
|
-
export const AgeYearsError = { Type: "age.years.type", Invalid: "age.years.invalid" };
|
|
4
|
-
|
|
5
|
-
export const AgeYearsConstraints = { min: 1, max: 130 };
|
|
6
|
-
|
|
7
|
-
// Stryker disable all
|
|
8
|
-
export const AgeYears = z
|
|
9
|
-
// Stryker restore all
|
|
10
|
-
.number(AgeYearsError.Type)
|
|
11
|
-
.int(AgeYearsError.Type)
|
|
12
|
-
.min(1, AgeYearsError.Invalid)
|
|
13
|
-
.max(130, AgeYearsError.Invalid)
|
|
14
|
-
.brand("AgeYears");
|
|
15
|
-
|
|
16
|
-
export type AgeYearsType = z.infer<typeof AgeYears>;
|
package/src/age.vo.ts
DELETED
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
import { differenceInYears } from "date-fns";
|
|
2
|
-
import { AgeYears, AgeYearsConstraints, type AgeYearsType } from "./age-years.vo";
|
|
3
|
-
import { Timestamp } from "./timestamp.vo";
|
|
4
|
-
|
|
5
|
-
export const AgeError = { FutureBirthdate: "age.future.birthdate" };
|
|
6
|
-
|
|
7
|
-
export class Age {
|
|
8
|
-
static readonly MIN = AgeYearsConstraints.min;
|
|
9
|
-
static readonly MAX = AgeYearsConstraints.max;
|
|
10
|
-
|
|
11
|
-
private constructor(private readonly value: AgeYearsType) {}
|
|
12
|
-
|
|
13
|
-
static fromValue(candidate: number): Age {
|
|
14
|
-
return new Age(AgeYears.parse(candidate));
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
static fromValueSafe(candidate: AgeYearsType): Age {
|
|
18
|
-
return new Age(candidate);
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
static fromBirthdateTimestamp(params: { birthdate: Timestamp; now: Timestamp }): Age {
|
|
22
|
-
if (params.birthdate.isAfter(params.now)) throw new Error(AgeError.FutureBirthdate);
|
|
23
|
-
return Age.fromValue(differenceInYears(params.now.ms, params.birthdate.ms));
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
static fromBirthdate(candidate: { birthdate: string; now: Timestamp }): Age {
|
|
27
|
-
const birthdate = Timestamp.fromNumber(new Date(candidate.birthdate).getTime());
|
|
28
|
-
|
|
29
|
-
if (birthdate.isAfter(candidate.now)) throw new Error(AgeError.FutureBirthdate);
|
|
30
|
-
return Age.fromValue(differenceInYears(candidate.now.ms, birthdate.ms));
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
get(): number {
|
|
34
|
-
return this.value;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
equals(other: Age): boolean {
|
|
38
|
-
return this.value === other.value;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
isOlderThan(other: Age): boolean {
|
|
42
|
-
return this.value > other.value;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
isYoungerThan(other: Age): boolean {
|
|
46
|
-
return this.value < other.value;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
isAdult(minimumAge: Age): boolean {
|
|
50
|
-
return this.value >= minimumAge.value;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
toString(): string {
|
|
54
|
-
return this.value.toString();
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
toJSON(): number {
|
|
58
|
-
return this.get();
|
|
59
|
-
}
|
|
60
|
-
}
|
package/src/api-key.vo.ts
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import { z } from "zod/v4";
|
|
2
|
-
|
|
3
|
-
export const ApiKeyError = { Type: "api.key.type", Length: "api.key.length", BadChars: "api.key.bad.chars" };
|
|
4
|
-
|
|
5
|
-
// 64 letters and digits allowed
|
|
6
|
-
const API_KEY_CHARS = /^[a-zA-Z0-9]{64}$/;
|
|
7
|
-
|
|
8
|
-
// Stryker disable all
|
|
9
|
-
export const ApiKey = z
|
|
10
|
-
// Stryker disable all
|
|
11
|
-
.string(ApiKeyError.Type)
|
|
12
|
-
.length(64, ApiKeyError.Length)
|
|
13
|
-
.regex(API_KEY_CHARS, ApiKeyError.BadChars)
|
|
14
|
-
.brand("ApiKey");
|
|
15
|
-
|
|
16
|
-
export type ApiKeyType = z.infer<typeof ApiKey>;
|
package/src/basename.vo.ts
DELETED
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import { z } from "zod/v4";
|
|
2
|
-
|
|
3
|
-
export const BasenameError = {
|
|
4
|
-
Type: "basename.type",
|
|
5
|
-
Empty: "basename.empty",
|
|
6
|
-
TooLong: "basename.too.long",
|
|
7
|
-
DotSegments: "basename.dot.segments",
|
|
8
|
-
Dotfiles: "basename.dotfiles",
|
|
9
|
-
TrailingDot: "basename.trailing.dot",
|
|
10
|
-
BadChars: "basename.bad.chars",
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
// Letters, digits, dots, underscores, and hyphens allowed
|
|
14
|
-
const BASENAME_CHARS = /^[a-zA-Z0-9._-]+$/;
|
|
15
|
-
|
|
16
|
-
const DOT_SEGMENTS = [".", ".."];
|
|
17
|
-
|
|
18
|
-
// Stryker disable all
|
|
19
|
-
export const Basename = z
|
|
20
|
-
// Stryker restore all
|
|
21
|
-
.string(BasenameError.Type)
|
|
22
|
-
.min(1, BasenameError.Empty)
|
|
23
|
-
.max(128, BasenameError.TooLong)
|
|
24
|
-
// Reject "." and ".." as a filename to avoid directory traversal
|
|
25
|
-
.refine((value) => !DOT_SEGMENTS.includes(value), BasenameError.DotSegments)
|
|
26
|
-
// Reject dotfiles like ".env"
|
|
27
|
-
.refine((value) => !value.startsWith("."), BasenameError.Dotfiles)
|
|
28
|
-
// Reject trailing dot like "picture." to avoid extension collision
|
|
29
|
-
.refine((value) => !value.endsWith("."), BasenameError.TrailingDot)
|
|
30
|
-
.regex(BASENAME_CHARS, BasenameError.BadChars)
|
|
31
|
-
.brand("Basename");
|
|
32
|
-
|
|
33
|
-
export type BasenameType = z.infer<typeof Basename>;
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import type { Hour } from "./hour.vo";
|
|
2
|
-
import { HourFormatters } from "./hour-format.service";
|
|
3
|
-
import type { Minute } from "./minute.vo";
|
|
4
|
-
|
|
5
|
-
export type ClockFormatter = (hour: Hour, minute: Minute) => string;
|
|
6
|
-
|
|
7
|
-
enum ClockFormatterEnum {
|
|
8
|
-
TWENTY_FOUR_HOURS = "TWENTY_FOUR_HOURS",
|
|
9
|
-
TWELVE_HOURS = "TWELVE_HOURS",
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export const ClockFormatters: Record<ClockFormatterEnum, ClockFormatter> = {
|
|
13
|
-
TWENTY_FOUR_HOURS: (hour, minute) => `${hour.toString()}:${minute.toString()}`,
|
|
14
|
-
TWELVE_HOURS: (hour, minute) => `${hour.format(HourFormatters.TWELVE_HOURS)}:${minute.toString()}`,
|
|
15
|
-
};
|
package/src/clock.vo.ts
DELETED
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
import { type ClockFormatter, ClockFormatters } from "./clock-format.service";
|
|
2
|
-
import { Hour } from "./hour.vo";
|
|
3
|
-
import type { HourSchemaType } from "./hour-schema.vo";
|
|
4
|
-
import { Minute } from "./minute.vo";
|
|
5
|
-
import type { MinuteSchemaType } from "./minute-schema.vo";
|
|
6
|
-
import type { Timestamp } from "./timestamp.vo";
|
|
7
|
-
|
|
8
|
-
export class Clock {
|
|
9
|
-
private readonly formatter: ClockFormatter;
|
|
10
|
-
|
|
11
|
-
constructor(
|
|
12
|
-
private readonly hour: Hour,
|
|
13
|
-
private readonly minute: Minute,
|
|
14
|
-
formatter?: ClockFormatter,
|
|
15
|
-
) {
|
|
16
|
-
this.formatter = formatter ?? ClockFormatters.TWENTY_FOUR_HOURS;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
static fromTimestamp(timestamp: Timestamp, formatter?: ClockFormatter): Clock {
|
|
20
|
-
const hour = Hour.fromTimestamp(timestamp);
|
|
21
|
-
const minute = Minute.fromTimestamp(timestamp);
|
|
22
|
-
|
|
23
|
-
return new Clock(hour, minute, formatter);
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
get(): { hour: HourSchemaType; minute: MinuteSchemaType } {
|
|
27
|
-
return { hour: this.hour.get(), minute: this.minute.get() };
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
format(): string {
|
|
31
|
-
return this.formatter(this.hour, this.minute);
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
equals(another: Clock): boolean {
|
|
35
|
-
return this.hour.equals(another.hour) && this.minute.equals(another.minute);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
isAfter(another: Clock): boolean {
|
|
39
|
-
if (!this.hour.equals(another.hour)) return this.hour.isAfter(another.hour);
|
|
40
|
-
return this.minute.isAfter(another.minute);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
isBefore(another: Clock): boolean {
|
|
44
|
-
if (!this.hour.equals(another.hour)) return this.hour.isBefore(another.hour);
|
|
45
|
-
return this.minute.isBefore(another.minute);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
toString(): string {
|
|
49
|
-
return this.format();
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
toJSON(): { hour: HourSchemaType; minute: MinuteSchemaType } {
|
|
53
|
-
return this.get();
|
|
54
|
-
}
|
|
55
|
-
}
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { Duration } from "./duration.service";
|
|
2
|
-
import { Timestamp } from "./timestamp.vo";
|
|
3
|
-
|
|
4
|
-
type GetStartOfDayTsInTzConfigType = { now: Timestamp; timeZoneOffset: Duration };
|
|
5
|
-
|
|
6
|
-
export class DateCalculator {
|
|
7
|
-
static getStartOfDayTsInTz(config: GetStartOfDayTsInTzConfigType): Timestamp {
|
|
8
|
-
const dayMs = Duration.Days(1).ms;
|
|
9
|
-
|
|
10
|
-
const utcMidnightOfNow = Timestamp.fromNumber(Math.floor(config.now.ms / dayMs) * dayMs);
|
|
11
|
-
|
|
12
|
-
let startOfDayInTz = utcMidnightOfNow.add(config.timeZoneOffset);
|
|
13
|
-
|
|
14
|
-
if (startOfDayInTz.isAfter(config.now)) startOfDayInTz = startOfDayInTz.subtract(Duration.Days(1));
|
|
15
|
-
|
|
16
|
-
return startOfDayInTz;
|
|
17
|
-
}
|
|
18
|
-
}
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import { format, formatDistanceToNow } from "date-fns";
|
|
2
|
-
|
|
3
|
-
type DateFormattersInputType = Parameters<typeof format>[0];
|
|
4
|
-
|
|
5
|
-
export class DateFormatters {
|
|
6
|
-
static datetime(date: DateFormattersInputType): string {
|
|
7
|
-
return format(date, "yyyy/MM/dd HH:mm");
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
static date(date: DateFormattersInputType): string {
|
|
11
|
-
return format(date, "yyyy/MM/dd");
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
static monthDay(date: DateFormattersInputType): string {
|
|
15
|
-
return format(date, "MM/dd");
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
static relative(date: DateFormattersInputType): string {
|
|
19
|
-
return formatDistanceToNow(date, { addSuffix: true });
|
|
20
|
-
}
|
|
21
|
-
}
|
package/src/date-range.vo.ts
DELETED
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
import type { Timestamp } from "./timestamp.vo";
|
|
2
|
-
|
|
3
|
-
export const DateRangeError = { Invalid: "date.range.invalid" };
|
|
4
|
-
|
|
5
|
-
export class DateRange {
|
|
6
|
-
constructor(
|
|
7
|
-
private readonly start: Timestamp,
|
|
8
|
-
private readonly end: Timestamp,
|
|
9
|
-
) {
|
|
10
|
-
if (start.isAfter(end)) throw new Error(DateRangeError.Invalid);
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
getStart(): Timestamp {
|
|
14
|
-
return this.start;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
getEnd(): Timestamp {
|
|
18
|
-
return this.end;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
toRange(): [Timestamp, Timestamp] {
|
|
22
|
-
return [this.start, this.end];
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
contains(timestamp: Timestamp): boolean {
|
|
26
|
-
return timestamp.isAfterOrEqual(this.start) && timestamp.isBeforeOrEqual(this.end);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
equals(other: DateRange): boolean {
|
|
30
|
-
return this.start.equals(other.start) && this.end.equals(other.end);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
toJSON(): { start: number; end: number } {
|
|
34
|
-
return { start: this.getStart().ms, end: this.getEnd().ms };
|
|
35
|
-
}
|
|
36
|
-
}
|
package/src/day-iso-id.vo.ts
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import { isValid, parseISO } from "date-fns";
|
|
2
|
-
import { z } from "zod/v4";
|
|
3
|
-
|
|
4
|
-
export const DayIsoIdError = {
|
|
5
|
-
Type: "day.iso.id.type",
|
|
6
|
-
BadChars: "day.iso.id.bad.chars",
|
|
7
|
-
InvalidDate: "day.iso.id.invalid.date",
|
|
8
|
-
};
|
|
9
|
-
|
|
10
|
-
// Four digits, hyphen, two digits, hyphen, two digits
|
|
11
|
-
export const DAY_ISO_ID_CHARS = /^[0-9]{4}-[0-9]{2}-[0-9]{2}$/;
|
|
12
|
-
|
|
13
|
-
// Stryker disable all
|
|
14
|
-
export const DayIsoId = z
|
|
15
|
-
// Stryker restore all
|
|
16
|
-
.string(DayIsoIdError.Type)
|
|
17
|
-
.regex(DAY_ISO_ID_CHARS, DayIsoIdError.BadChars)
|
|
18
|
-
.refine((value) => isValid(parseISO(value)), DayIsoIdError.InvalidDate)
|
|
19
|
-
.brand("DayIsoId");
|
|
20
|
-
|
|
21
|
-
export type DayIsoIdType = z.infer<typeof DayIsoId>;
|
package/src/day.vo.ts
DELETED
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
import { formatISO } from "date-fns";
|
|
2
|
-
import { DateRange } from "./date-range.vo";
|
|
3
|
-
import { DayIsoId, type DayIsoIdType } from "./day-iso-id.vo";
|
|
4
|
-
import { Duration } from "./duration.service";
|
|
5
|
-
import { Integer, type IntegerType } from "./integer.vo";
|
|
6
|
-
import { Timestamp } from "./timestamp.vo";
|
|
7
|
-
import type { TimestampValueType } from "./timestamp-value.vo";
|
|
8
|
-
|
|
9
|
-
export class Day extends DateRange {
|
|
10
|
-
static fromTimestamp(timestamp: Timestamp): Day {
|
|
11
|
-
const date = new Date(timestamp.ms);
|
|
12
|
-
|
|
13
|
-
const startUtc = Timestamp.fromNumber(
|
|
14
|
-
Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate()),
|
|
15
|
-
);
|
|
16
|
-
const endUtc = startUtc.add(Duration.Days(1)).subtract(Duration.Ms(1));
|
|
17
|
-
|
|
18
|
-
return new Day(startUtc, endUtc);
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
static fromTimestampValue(timestamp: TimestampValueType): Day {
|
|
22
|
-
return Day.fromTimestamp(Timestamp.fromValue(timestamp));
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
static fromNow(now: Timestamp): Day {
|
|
26
|
-
return Day.fromTimestamp(now);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
static fromIsoId(isoId: DayIsoIdType): Day {
|
|
30
|
-
const [year, month, day] = DayIsoId.parse(isoId).split("-").map(Number);
|
|
31
|
-
|
|
32
|
-
const startUtc = Timestamp.fromNumber(Date.UTC(year, month - 1, day));
|
|
33
|
-
const endUtc = startUtc.add(Duration.Days(1)).subtract(Duration.Ms(1));
|
|
34
|
-
|
|
35
|
-
return new Day(startUtc, endUtc);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
toIsoId(): DayIsoIdType {
|
|
39
|
-
const midday = this.getStart().add(Duration.Hours(12));
|
|
40
|
-
|
|
41
|
-
return DayIsoId.parse(formatISO(midday.ms, { representation: "date" }));
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
previous(): Day {
|
|
45
|
-
return this.shift(Integer.parse(-1));
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
next(): Day {
|
|
49
|
-
return this.shift(Integer.parse(1));
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
shift(count: IntegerType): Day {
|
|
53
|
-
return Day.fromTimestamp(this.getStart().add(Duration.Days(count)));
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
toString(): string {
|
|
57
|
-
return this.toIsoId();
|
|
58
|
-
}
|
|
59
|
-
}
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
import { z } from "zod/v4";
|
|
2
|
-
|
|
3
|
-
export const DirectoryPathAbsoluteError = {
|
|
4
|
-
BadSegments: "directory.path.absolue.bad.segments",
|
|
5
|
-
Empty: "directory.path.absolue.empty",
|
|
6
|
-
LeadingSlash: "directory.path.absolue.leading.slash",
|
|
7
|
-
TooLong: "directory.path.absolue.too.long",
|
|
8
|
-
TrailingSlash: "directory.path.absolue.trailing.slash",
|
|
9
|
-
Type: "directory.path.absolue.type",
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
// Letters, digits, dots, underscores, and hyphens
|
|
13
|
-
export const DIRECTORY_PATH_ABSOLUTE_CHARS = /^[a-zA-Z0-9._-]+$/;
|
|
14
|
-
|
|
15
|
-
const DOT_SEGMENTS = [".", ".."];
|
|
16
|
-
|
|
17
|
-
// Stryker disable all
|
|
18
|
-
export const DirectoryPathAbsoluteSchema = z
|
|
19
|
-
// Stryker restore all
|
|
20
|
-
.string(DirectoryPathAbsoluteError.Type)
|
|
21
|
-
.min(1, DirectoryPathAbsoluteError.Empty)
|
|
22
|
-
.max(512, DirectoryPathAbsoluteError.TooLong)
|
|
23
|
-
.refine((value) => value.startsWith("/"), DirectoryPathAbsoluteError.LeadingSlash)
|
|
24
|
-
.refine((value) => (value === "/" ? true : !value.endsWith("/")), DirectoryPathAbsoluteError.TrailingSlash)
|
|
25
|
-
.refine((value) => {
|
|
26
|
-
if (value === "/") return true;
|
|
27
|
-
|
|
28
|
-
const segments = value.slice(1).split("/");
|
|
29
|
-
|
|
30
|
-
return segments.every(
|
|
31
|
-
(segment) => DIRECTORY_PATH_ABSOLUTE_CHARS.test(segment) && !DOT_SEGMENTS.includes(segment),
|
|
32
|
-
);
|
|
33
|
-
}, DirectoryPathAbsoluteError.BadSegments)
|
|
34
|
-
.brand("DirectoryPathAbsoluteSchema");
|
|
35
|
-
|
|
36
|
-
export type DirectoryPathAbsoluteType = z.infer<typeof DirectoryPathAbsoluteSchema>;
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import { z } from "zod/v4";
|
|
2
|
-
|
|
3
|
-
export const DirectoryPathRelativeError = {
|
|
4
|
-
BadSegments: "directory.path.relative.bad.segments",
|
|
5
|
-
Empty: "directory.path.relative.empty",
|
|
6
|
-
LeadingSlash: "directory.path.relative.leading.slash",
|
|
7
|
-
TooLong: "directory.path.absolue.too.long",
|
|
8
|
-
TrailingSlash: "directory.path.absolue.trailing.slash",
|
|
9
|
-
Type: "directory.path.relative.type",
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
// Letters, digits, dots, underscores, and hyphens
|
|
13
|
-
export const DIRECTORY_PATH_RELATIVE_CHARS = /^[A-Za-z0-9._-]+$/;
|
|
14
|
-
|
|
15
|
-
const DOT_SEGMENTS = [".", ".."];
|
|
16
|
-
|
|
17
|
-
// Stryker disable all
|
|
18
|
-
export const DirectoryPathRelativeSchema = z
|
|
19
|
-
// Stryker restore all
|
|
20
|
-
.string(DirectoryPathRelativeError.Type)
|
|
21
|
-
.min(1, DirectoryPathRelativeError.Empty)
|
|
22
|
-
.max(512, DirectoryPathRelativeError.TooLong)
|
|
23
|
-
.refine((value) => !value.startsWith("/"), DirectoryPathRelativeError.LeadingSlash)
|
|
24
|
-
.refine((value) => !value.endsWith("/"), DirectoryPathRelativeError.TrailingSlash)
|
|
25
|
-
.refine(
|
|
26
|
-
(value) =>
|
|
27
|
-
value
|
|
28
|
-
.split("/")
|
|
29
|
-
.every((segment) => DIRECTORY_PATH_RELATIVE_CHARS.test(segment) && !DOT_SEGMENTS.includes(segment)),
|
|
30
|
-
DirectoryPathRelativeError.BadSegments,
|
|
31
|
-
)
|
|
32
|
-
.brand("DirectoryPathRelativeSchema");
|
|
33
|
-
|
|
34
|
-
export type DirectoryPathRelativeType = z.infer<typeof DirectoryPathRelativeSchema>;
|
package/src/distance-value.vo.ts
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import { z } from "zod/v4";
|
|
2
|
-
|
|
3
|
-
export const DistanceValueError = { Type: "distance.value.type", Invalid: "distance.value.invalid" };
|
|
4
|
-
|
|
5
|
-
// Stryker disable all
|
|
6
|
-
export const DistanceValue = z
|
|
7
|
-
// Stryker restore all
|
|
8
|
-
.number(DistanceValueError.Type)
|
|
9
|
-
.int(DistanceValueError.Type)
|
|
10
|
-
.min(0, DistanceValueError.Invalid)
|
|
11
|
-
.brand("DistanceValue");
|
|
12
|
-
|
|
13
|
-
export type DistanceValueType = z.infer<typeof DistanceValue>;
|
package/src/distance.vo.ts
DELETED
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
import { DistanceValue, type DistanceValueType } from "./distance-value.vo";
|
|
2
|
-
import type { RoundingStrategy } from "./rounding.strategy";
|
|
3
|
-
import { RoundingToNearestStrategy } from "./rounding-to-nearest.strategy";
|
|
4
|
-
|
|
5
|
-
export const DistanceError = { SubtractResultLessThanZero: "distance.subtract.result.less.than.zero" };
|
|
6
|
-
|
|
7
|
-
export class Distance {
|
|
8
|
-
private static readonly ZERO = DistanceValue.parse(0);
|
|
9
|
-
|
|
10
|
-
private constructor(private readonly value: DistanceValueType) {}
|
|
11
|
-
|
|
12
|
-
static fromMeters(candidate: number): Distance {
|
|
13
|
-
return new Distance(DistanceValue.parse(candidate));
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
static fromMetersSafe(candidate: DistanceValueType): Distance {
|
|
17
|
-
return new Distance(candidate);
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
static fromKilometers(
|
|
21
|
-
candidate: number,
|
|
22
|
-
rounding: RoundingStrategy = new RoundingToNearestStrategy(),
|
|
23
|
-
): Distance {
|
|
24
|
-
return new Distance(DistanceValue.parse(rounding.round(candidate * 1000)));
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
static fromMiles(
|
|
28
|
-
candidate: number,
|
|
29
|
-
rounding: RoundingStrategy = new RoundingToNearestStrategy(),
|
|
30
|
-
): Distance {
|
|
31
|
-
return new Distance(DistanceValue.parse(rounding.round(candidate * 1_609.344)));
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
get(): DistanceValueType {
|
|
35
|
-
return this.value;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
add(distance: Distance): Distance {
|
|
39
|
-
return new Distance(DistanceValue.parse(this.value + distance.get()));
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
subtract(money: Distance): Distance {
|
|
43
|
-
const result = this.value - money.get();
|
|
44
|
-
|
|
45
|
-
if (result < Distance.ZERO) throw new Error(DistanceError.SubtractResultLessThanZero);
|
|
46
|
-
return new Distance(DistanceValue.parse(result));
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
equals(another: Distance): boolean {
|
|
50
|
-
return this.value === another.get();
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
isLongerThan(another: Distance): boolean {
|
|
54
|
-
return this.value > another.get();
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
isShorterThan(another: Distance): boolean {
|
|
58
|
-
return this.value < another.get();
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
isZero(): boolean {
|
|
62
|
-
return this.value === Distance.ZERO;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
format(): string {
|
|
66
|
-
return this.value.toString();
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
toString(): string {
|
|
70
|
-
return this.format();
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
toJSON(): number {
|
|
74
|
-
return this.value;
|
|
75
|
-
}
|
|
76
|
-
}
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import { z } from "zod/v4";
|
|
2
|
-
|
|
3
|
-
export const DivisionFactorError = { Type: "division.factor.type", Invalid: "division.factor.invalid" };
|
|
4
|
-
|
|
5
|
-
// Stryker disable all
|
|
6
|
-
export const DivisionFactor = z
|
|
7
|
-
// Stryker restore all
|
|
8
|
-
.number(DivisionFactorError.Type)
|
|
9
|
-
.gt(0, DivisionFactorError.Invalid)
|
|
10
|
-
.brand("DivisionFactor");
|
|
11
|
-
|
|
12
|
-
export type DivisionFactorType = z.infer<typeof DivisionFactor>;
|