@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.
Files changed (108) hide show
  1. package/dist/duration.service.d.ts +3 -0
  2. package/dist/duration.service.js +7 -0
  3. package/package.json +2 -3
  4. package/src/age-years.vo.ts +0 -16
  5. package/src/age.vo.ts +0 -60
  6. package/src/api-key.vo.ts +0 -16
  7. package/src/basename.vo.ts +0 -33
  8. package/src/clock-format.service.ts +0 -15
  9. package/src/clock.vo.ts +0 -55
  10. package/src/date-calculator.service.ts +0 -18
  11. package/src/date-formatter.service.ts +0 -21
  12. package/src/date-range.vo.ts +0 -36
  13. package/src/day-iso-id.vo.ts +0 -21
  14. package/src/day.vo.ts +0 -59
  15. package/src/directory-path-absolute.vo.ts +0 -36
  16. package/src/directory-path-relative.vo.ts +0 -34
  17. package/src/distance-value.vo.ts +0 -13
  18. package/src/distance.vo.ts +0 -76
  19. package/src/division-factor.vo.ts +0 -12
  20. package/src/dll.service.ts +0 -177
  21. package/src/duration-ms.vo.ts +0 -9
  22. package/src/duration.service.ts +0 -89
  23. package/src/email-mask.service.ts +0 -17
  24. package/src/email.vo.ts +0 -9
  25. package/src/etags.vo.ts +0 -47
  26. package/src/extension.vo.ts +0 -27
  27. package/src/feature-flag-value.vo.ts +0 -12
  28. package/src/feature-flag.vo.ts +0 -11
  29. package/src/file-path-absolute-schema.vo.ts +0 -34
  30. package/src/file-path-relative-schema.vo.ts +0 -34
  31. package/src/file-path.vo.ts +0 -100
  32. package/src/filename-affix.vo.ts +0 -27
  33. package/src/filename-from-string.vo.ts +0 -27
  34. package/src/filename.vo.ts +0 -92
  35. package/src/height-milimiters.vo.ts +0 -16
  36. package/src/height.vo.ts +0 -79
  37. package/src/hour-format.service.ts +0 -22
  38. package/src/hour-schema.vo.ts +0 -14
  39. package/src/hour.vo.ts +0 -64
  40. package/src/iban-mask.service.ts +0 -18
  41. package/src/iban-schema.vo.ts +0 -17
  42. package/src/iban.vo.ts +0 -28
  43. package/src/image.vo.ts +0 -28
  44. package/src/index.ts +0 -105
  45. package/src/integer-non-negative.vo.ts +0 -16
  46. package/src/integer-positive.vo.ts +0 -13
  47. package/src/integer.vo.ts +0 -9
  48. package/src/language.vo.ts +0 -13
  49. package/src/linear-regression.service.ts +0 -73
  50. package/src/mean.service.ts +0 -18
  51. package/src/mime-types.vo.ts +0 -7
  52. package/src/mime-value.vo.ts +0 -14
  53. package/src/mime.vo.ts +0 -50
  54. package/src/min-max-scaler.service.ts +0 -96
  55. package/src/minute-schema.vo.ts +0 -14
  56. package/src/minute.vo.ts +0 -59
  57. package/src/money-amount.vo.ts +0 -13
  58. package/src/money.vo.ts +0 -83
  59. package/src/month-iso-id.vo.ts +0 -24
  60. package/src/month.vo.ts +0 -53
  61. package/src/multiplication-factor.vo.ts +0 -15
  62. package/src/noop.service.ts +0 -3
  63. package/src/notification-template.vo.ts +0 -10
  64. package/src/object-key.vo.ts +0 -36
  65. package/src/outlier-detector.service.ts +0 -21
  66. package/src/package-version-schema.vo.ts +0 -23
  67. package/src/package-version.vo.ts +0 -66
  68. package/src/pagination-page.vo.ts +0 -11
  69. package/src/pagination-skip.vo.ts +0 -10
  70. package/src/pagination-take.vo.ts +0 -10
  71. package/src/pagination.service.ts +0 -73
  72. package/src/percentage.service.ts +0 -15
  73. package/src/population-standard-deviation.service.ts +0 -23
  74. package/src/quarter-iso-id.vo.ts +0 -15
  75. package/src/quarter.vo.ts +0 -41
  76. package/src/random.service.ts +0 -19
  77. package/src/rate-limiter.service.ts +0 -30
  78. package/src/relative-date.vo.ts +0 -21
  79. package/src/reordering-item-position-value.vo.ts +0 -10
  80. package/src/reordering.service.ts +0 -163
  81. package/src/revision-value.vo.ts +0 -10
  82. package/src/revision.vo.ts +0 -44
  83. package/src/rounding-decimal.strategy.ts +0 -25
  84. package/src/rounding-down.strategy.ts +0 -7
  85. package/src/rounding-to-nearest.strategy.ts +0 -7
  86. package/src/rounding-up.strategy.ts +0 -7
  87. package/src/rounding.strategy.ts +0 -3
  88. package/src/size-bytes.vo.ts +0 -13
  89. package/src/size.vo.ts +0 -113
  90. package/src/slug.service.ts +0 -18
  91. package/src/sum.service.ts +0 -19
  92. package/src/thousands-separator.service.ts +0 -10
  93. package/src/time-zone-offset-value.vo.ts +0 -21
  94. package/src/timestamp-value.vo.ts +0 -13
  95. package/src/timestamp.vo.ts +0 -66
  96. package/src/timezone.vo.ts +0 -26
  97. package/src/ts-utils.ts +0 -3
  98. package/src/url-with-slash.vo.ts +0 -12
  99. package/src/url-without-slash.vo.ts +0 -12
  100. package/src/visually-unambiguous-characters-generator.service.ts +0 -39
  101. package/src/week-iso-id.vo.ts +0 -28
  102. package/src/week.vo.ts +0 -57
  103. package/src/weekday.vo.ts +0 -119
  104. package/src/weight-grams.vo.ts +0 -13
  105. package/src/weight.vo.ts +0 -91
  106. package/src/year-iso-id.vo.ts +0 -15
  107. package/src/year.vo.ts +0 -63
  108. 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;
@@ -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.19",
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",
@@ -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>;
@@ -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
- }
@@ -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
- }
@@ -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>;
@@ -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>;
@@ -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>;