@bgord/tools 0.12.26 → 0.13.1

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 (106) hide show
  1. package/dist/clock.vo.d.ts +4 -2
  2. package/dist/clock.vo.js +7 -1
  3. package/dist/directory-path-absolute.vo.d.ts +1 -1
  4. package/dist/directory-path-absolute.vo.js +1 -1
  5. package/dist/file-path-absolute-schema.vo.d.ts +1 -1
  6. package/dist/hour.vo.d.ts +2 -0
  7. package/dist/hour.vo.js +11 -11
  8. package/dist/index.d.ts +7 -3
  9. package/dist/index.js +7 -3
  10. package/dist/mean.service.js +1 -2
  11. package/dist/mime.vo.js +2 -4
  12. package/dist/min-max-scaler.service.js +5 -10
  13. package/dist/minute.vo.d.ts +2 -0
  14. package/dist/minute.vo.js +8 -10
  15. package/dist/money.vo.js +3 -7
  16. package/dist/month-iso-id.vo.d.ts +3 -0
  17. package/dist/month-iso-id.vo.js +15 -0
  18. package/dist/month.vo.d.ts +9 -0
  19. package/dist/month.vo.js +22 -0
  20. package/dist/outlier-detector.service.js +1 -2
  21. package/dist/package-version.vo.js +0 -5
  22. package/dist/pagination.service.js +1 -8
  23. package/dist/percentage.service.js +1 -2
  24. package/dist/population-standard-deviation.service.js +1 -2
  25. package/dist/quarter-iso-id.vo.d.ts +3 -0
  26. package/dist/quarter-iso-id.vo.js +10 -0
  27. package/dist/quarter.vo.d.ts +9 -0
  28. package/dist/quarter.vo.js +24 -0
  29. package/dist/random.service.js +4 -8
  30. package/dist/reordering.service.js +3 -6
  31. package/dist/revision.vo.js +2 -4
  32. package/dist/simple-linear-regression.service.js +6 -12
  33. package/dist/stepper.service.js +2 -4
  34. package/dist/stopwatch.service.js +2 -3
  35. package/dist/timezone.vo.js +2 -6
  36. package/dist/tsconfig.tsbuildinfo +1 -1
  37. package/dist/visually-unambiguous-characters-generator.service.js +2 -4
  38. package/dist/weekday.vo.d.ts +37 -0
  39. package/dist/weekday.vo.js +86 -0
  40. package/dist/year-iso-id.vo.d.ts +3 -0
  41. package/dist/year-iso-id.vo.js +8 -0
  42. package/dist/year.vo.d.ts +11 -0
  43. package/dist/year.vo.js +32 -0
  44. package/dist/z-score.service.js +1 -2
  45. package/package.json +3 -3
  46. package/readme.md +7 -3
  47. package/src/api-key.vo.ts +0 -1
  48. package/src/basename.vo.ts +0 -1
  49. package/src/clock.vo.ts +9 -2
  50. package/src/date-calculator.service.ts +1 -4
  51. package/src/date-formatter.service.ts +0 -1
  52. package/src/directory-path-absolute.vo.ts +1 -1
  53. package/src/dll.service.ts +0 -5
  54. package/src/email-mask.service.ts +0 -1
  55. package/src/etags.vo.ts +0 -1
  56. package/src/extension.vo.ts +0 -1
  57. package/src/feature-flag.vo.ts +1 -1
  58. package/src/filename-from-string.vo.ts +0 -1
  59. package/src/filename-suffix.vo.ts +0 -1
  60. package/src/hour.vo.ts +15 -13
  61. package/src/image.vo.ts +0 -2
  62. package/src/index.ts +7 -3
  63. package/src/language.vo.ts +0 -1
  64. package/src/mean.service.ts +1 -3
  65. package/src/mime.vo.ts +2 -9
  66. package/src/min-max-scaler.service.ts +6 -21
  67. package/src/minute.vo.ts +11 -15
  68. package/src/money.vo.ts +3 -9
  69. package/src/month-iso-id.vo.ts +25 -0
  70. package/src/month.vo.ts +26 -0
  71. package/src/object-key.vo.ts +0 -1
  72. package/src/outlier-detector.service.ts +1 -3
  73. package/src/package-version.vo.ts +0 -12
  74. package/src/pagination.service.ts +4 -29
  75. package/src/percentage.service.ts +1 -5
  76. package/src/population-standard-deviation.service.ts +1 -3
  77. package/src/quarter-iso-id.vo.ts +18 -0
  78. package/src/quarter.vo.ts +28 -0
  79. package/src/random.service.ts +5 -19
  80. package/src/rate-limiter.service.ts +0 -3
  81. package/src/reordering.service.ts +6 -19
  82. package/src/revision.vo.ts +2 -8
  83. package/src/simple-linear-regression.service.ts +6 -21
  84. package/src/size.vo.ts +0 -1
  85. package/src/stepper.service.ts +2 -9
  86. package/src/stopwatch.service.ts +2 -4
  87. package/src/streak-calculator.service.ts +1 -6
  88. package/src/time-zone-offset-value.vo.ts +0 -1
  89. package/src/timestamp.vo.ts +0 -1
  90. package/src/timezone.vo.ts +2 -9
  91. package/src/ts-utils.ts +0 -2
  92. package/src/visually-unambiguous-characters-generator.service.ts +2 -4
  93. package/src/week-iso-id.vo.ts +0 -1
  94. package/src/weekday.vo.ts +101 -0
  95. package/src/year-iso-id.vo.ts +13 -0
  96. package/src/year.vo.ts +36 -0
  97. package/src/z-score.service.ts +1 -3
  98. package/dist/build-version.vo.d.ts +0 -3
  99. package/dist/build-version.vo.js +0 -2
  100. package/dist/filter.vo.d.ts +0 -17
  101. package/dist/filter.vo.js +0 -22
  102. package/dist/leap-year-checker.service.d.ts +0 -4
  103. package/dist/leap-year-checker.service.js +0 -9
  104. package/src/build-version.vo.ts +0 -5
  105. package/src/filter.vo.ts +0 -38
  106. package/src/leap-year-checker.service.ts +0 -11
@@ -3,12 +3,10 @@ export class Stepper {
3
3
  current = Stepper.DEFAULT_CURRENT;
4
4
  total;
5
5
  constructor(config) {
6
- if (!Number.isInteger(config.total)) {
6
+ if (!Number.isInteger(config.total))
7
7
  throw new Error("Total value is not an integer");
8
- }
9
- if (config.total <= Stepper.DEFAULT_CURRENT) {
8
+ if (config.total <= Stepper.DEFAULT_CURRENT)
10
9
  throw new Error("Total value should be greater than one");
11
- }
12
10
  this.total = config.total;
13
11
  }
14
12
  continue() {
@@ -2,7 +2,7 @@ import { Timestamp } from "./timestamp.vo";
2
2
  var StopwatchState;
3
3
  (function (StopwatchState) {
4
4
  StopwatchState["started"] = "started";
5
- StopwatchState["stopped"] = "finished";
5
+ StopwatchState["stopped"] = "stopped";
6
6
  })(StopwatchState || (StopwatchState = {}));
7
7
  export class Stopwatch {
8
8
  startMs;
@@ -12,9 +12,8 @@ export class Stopwatch {
12
12
  this.startMs = startMs;
13
13
  }
14
14
  stop() {
15
- if (this.state === StopwatchState.stopped) {
15
+ if (this.state === StopwatchState.stopped)
16
16
  throw new Error("Stopwatch is already stopped");
17
- }
18
17
  this.state = StopwatchState.stopped;
19
18
  this.stopMs = Timestamp.parse(Date.now());
20
19
  return { durationMs: Timestamp.parse(this.stopMs - this.startMs) };
@@ -4,16 +4,12 @@ export const Timezone = z
4
4
  .min(1)
5
5
  .refine((value) => {
6
6
  try {
7
- // Create a dummy date and time format using the specified timezone
8
- const dummyDate = new Date();
7
+ const date = new Date();
9
8
  const formatter = new Intl.DateTimeFormat("en-US", { timeZone: value });
10
- // Format the dummy date
11
- formatter.format(dummyDate);
12
- // If the formatting succeeds without throwing an error, the timezone is valid
9
+ formatter.format(date);
13
10
  return true;
14
11
  }
15
12
  catch (_error) {
16
- // An error occurred, indicating an invalid timezone
17
13
  return false;
18
14
  }
19
15
  }, { message: "timezone.invalid" })
@@ -1 +1 @@
1
- {"root":["../src/api-key.vo.ts","../src/basename.vo.ts","../src/build-version.vo.ts","../src/clock.vo.ts","../src/date-calculator.service.ts","../src/date-formatter.service.ts","../src/date-range.vo.ts","../src/dates-of-the-week.vo.ts","../src/day-iso-id.vo.ts","../src/day.vo.ts","../src/directory-path-absolute.vo.ts","../src/directory-path-relative.vo.ts","../src/dll.service.ts","../src/email-mask.service.ts","../src/etags.vo.ts","../src/extension.vo.ts","../src/feature-flag.vo.ts","../src/file-path-absolute-schema.vo.ts","../src/file-path-relative-schema.vo.ts","../src/file-path.vo.ts","../src/filename-from-string.vo.ts","../src/filename-suffix.vo.ts","../src/filename.vo.ts","../src/filter.vo.ts","../src/hour.vo.ts","../src/iban-mask.service.ts","../src/iban.vo.ts","../src/image.vo.ts","../src/index.ts","../src/language.vo.ts","../src/leap-year-checker.service.ts","../src/mean.service.ts","../src/mime-types.vo.ts","../src/mime.vo.ts","../src/min-max-scaler.service.ts","../src/minute.vo.ts","../src/money.vo.ts","../src/noop.service.ts","../src/notification-template.vo.ts","../src/object-key.vo.ts","../src/outlier-detector.service.ts","../src/package-version.vo.ts","../src/pagination.service.ts","../src/percentage.service.ts","../src/population-standard-deviation.service.ts","../src/random.service.ts","../src/rate-limiter.service.ts","../src/relative-date.vo.ts","../src/reordering.service.ts","../src/revision.vo.ts","../src/rounding.service.ts","../src/simple-linear-regression.service.ts","../src/size.vo.ts","../src/stepper.service.ts","../src/stopwatch.service.ts","../src/streak-calculator.service.ts","../src/sum.service.ts","../src/thousands-separator.service.ts","../src/time-zone-offset-value.vo.ts","../src/time.service.ts","../src/timestamp.vo.ts","../src/timezone.vo.ts","../src/ts-utils.ts","../src/visually-unambiguous-characters-generator.service.ts","../src/week-iso-id.vo.ts","../src/week.vo.ts","../src/z-score.service.ts"],"version":"5.9.2"}
1
+ {"root":["../src/api-key.vo.ts","../src/basename.vo.ts","../src/clock.vo.ts","../src/date-calculator.service.ts","../src/date-formatter.service.ts","../src/date-range.vo.ts","../src/dates-of-the-week.vo.ts","../src/day-iso-id.vo.ts","../src/day.vo.ts","../src/directory-path-absolute.vo.ts","../src/directory-path-relative.vo.ts","../src/dll.service.ts","../src/email-mask.service.ts","../src/etags.vo.ts","../src/extension.vo.ts","../src/feature-flag.vo.ts","../src/file-path-absolute-schema.vo.ts","../src/file-path-relative-schema.vo.ts","../src/file-path.vo.ts","../src/filename-from-string.vo.ts","../src/filename-suffix.vo.ts","../src/filename.vo.ts","../src/hour.vo.ts","../src/iban-mask.service.ts","../src/iban.vo.ts","../src/image.vo.ts","../src/index.ts","../src/language.vo.ts","../src/mean.service.ts","../src/mime-types.vo.ts","../src/mime.vo.ts","../src/min-max-scaler.service.ts","../src/minute.vo.ts","../src/money.vo.ts","../src/month-iso-id.vo.ts","../src/month.vo.ts","../src/noop.service.ts","../src/notification-template.vo.ts","../src/object-key.vo.ts","../src/outlier-detector.service.ts","../src/package-version.vo.ts","../src/pagination.service.ts","../src/percentage.service.ts","../src/population-standard-deviation.service.ts","../src/quarter-iso-id.vo.ts","../src/quarter.vo.ts","../src/random.service.ts","../src/rate-limiter.service.ts","../src/relative-date.vo.ts","../src/reordering.service.ts","../src/revision.vo.ts","../src/rounding.service.ts","../src/simple-linear-regression.service.ts","../src/size.vo.ts","../src/stepper.service.ts","../src/stopwatch.service.ts","../src/streak-calculator.service.ts","../src/sum.service.ts","../src/thousands-separator.service.ts","../src/time-zone-offset-value.vo.ts","../src/time.service.ts","../src/timestamp.vo.ts","../src/timezone.vo.ts","../src/ts-utils.ts","../src/visually-unambiguous-characters-generator.service.ts","../src/week-iso-id.vo.ts","../src/week.vo.ts","../src/weekday.vo.ts","../src/year-iso-id.vo.ts","../src/year.vo.ts","../src/z-score.service.ts"],"version":"5.9.2"}
@@ -1,5 +1,6 @@
1
1
  import { Random } from "./random.service";
2
2
  export class VisuallyUnambiguousCharactersGenerator {
3
+ // prettier-ignore
3
4
  static chars = [
4
5
  "a",
5
6
  "b",
@@ -26,10 +27,7 @@ export class VisuallyUnambiguousCharactersGenerator {
26
27
  ];
27
28
  static generate(length = 1) {
28
29
  return Array.from({ length })
29
- .map(() => VisuallyUnambiguousCharactersGenerator.chars[Random.generate({
30
- min: 0,
31
- max: VisuallyUnambiguousCharactersGenerator.chars.length - 1,
32
- })])
30
+ .map(() => VisuallyUnambiguousCharactersGenerator.chars[Random.generate({ min: 0, max: VisuallyUnambiguousCharactersGenerator.chars.length - 1 })])
33
31
  .join("");
34
32
  }
35
33
  }
@@ -0,0 +1,37 @@
1
+ import type { TimestampType } from "./timestamp.vo";
2
+ export type WeekdayFormatter = (value: Weekday["value"]) => string;
3
+ export declare enum WeekdayFormatterEnum {
4
+ FULL = "FULL",// "Sunday"
5
+ SHORT = "SHORT",// "Sun"
6
+ ISO_NUMBER = "ISO_NUMBER",// Monday=1 ... Sunday=7
7
+ ZERO_BASED_NUMBER = "ZERO_BASED_NUMBER"
8
+ }
9
+ export declare const WeekdayFormatters: Record<WeekdayFormatterEnum, WeekdayFormatter>;
10
+ export declare class Weekday {
11
+ private readonly value;
12
+ private readonly formatter;
13
+ static readonly SUNDAY: Weekday;
14
+ static readonly MONDAY: Weekday;
15
+ static readonly TUESDAY: Weekday;
16
+ static readonly WEDNESDAY: Weekday;
17
+ static readonly THURSDAY: Weekday;
18
+ static readonly FRIDAY: Weekday;
19
+ static readonly SATURDAY: Weekday;
20
+ constructor(candidate: number, formatter?: WeekdayFormatter);
21
+ static fromUtcTimestamp(timestamp: TimestampType, formatter?: WeekdayFormatter): Weekday;
22
+ get(formatter?: WeekdayFormatter): {
23
+ raw: number;
24
+ formatted: string;
25
+ };
26
+ equals(another: Weekday): boolean;
27
+ toIsoNumber(): number;
28
+ isMonday(): boolean;
29
+ isTuesday(): boolean;
30
+ isWednesday(): boolean;
31
+ isThursday(): boolean;
32
+ isFriday(): boolean;
33
+ isSaturday(): boolean;
34
+ isSunday(): boolean;
35
+ static list(formatter?: WeekdayFormatter): Weekday[];
36
+ static listMondayFirst(formatter?: WeekdayFormatter): Weekday[];
37
+ }
@@ -0,0 +1,86 @@
1
+ export var WeekdayFormatterEnum;
2
+ (function (WeekdayFormatterEnum) {
3
+ WeekdayFormatterEnum["FULL"] = "FULL";
4
+ WeekdayFormatterEnum["SHORT"] = "SHORT";
5
+ WeekdayFormatterEnum["ISO_NUMBER"] = "ISO_NUMBER";
6
+ WeekdayFormatterEnum["ZERO_BASED_NUMBER"] = "ZERO_BASED_NUMBER";
7
+ })(WeekdayFormatterEnum || (WeekdayFormatterEnum = {}));
8
+ const FULL_NAMES = [
9
+ "Sunday",
10
+ "Monday",
11
+ "Tuesday",
12
+ "Wednesday",
13
+ "Thursday",
14
+ "Friday",
15
+ "Saturday",
16
+ ];
17
+ const SHORT_NAMES = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
18
+ export const WeekdayFormatters = {
19
+ FULL: (value) => FULL_NAMES[value],
20
+ SHORT: (value) => SHORT_NAMES[value],
21
+ ISO_NUMBER: (value) => (value === 0 ? 7 : value).toString(), // ISO-8601: Mon=1..Sun=7
22
+ ZERO_BASED_NUMBER: (value) => value.toString(), // JS getUTCDay(): Sun=0..Sat=6
23
+ };
24
+ export class Weekday {
25
+ value;
26
+ formatter;
27
+ static SUNDAY = new Weekday(0);
28
+ static MONDAY = new Weekday(1);
29
+ static TUESDAY = new Weekday(2);
30
+ static WEDNESDAY = new Weekday(3);
31
+ static THURSDAY = new Weekday(4);
32
+ static FRIDAY = new Weekday(5);
33
+ static SATURDAY = new Weekday(6);
34
+ constructor(candidate, formatter) {
35
+ if (!Number.isInteger(candidate))
36
+ throw new Error("Invalid weekday");
37
+ if (candidate < 0)
38
+ throw new Error("Invalid weekday");
39
+ if (candidate > 6)
40
+ throw new Error("Invalid weekday");
41
+ this.value = candidate;
42
+ this.formatter = formatter ?? WeekdayFormatters.FULL;
43
+ }
44
+ static fromUtcTimestamp(timestamp, formatter) {
45
+ const day = new Date(timestamp).getUTCDay(); // 0..6
46
+ return new Weekday(day, formatter);
47
+ }
48
+ get(formatter) {
49
+ const format = formatter ?? this.formatter;
50
+ return { raw: this.value, formatted: format(this.value) };
51
+ }
52
+ equals(another) {
53
+ return this.value === another.get().raw;
54
+ }
55
+ toIsoNumber() {
56
+ return this.value === 0 ? 7 : this.value;
57
+ }
58
+ isMonday() {
59
+ return this.value === 1;
60
+ }
61
+ isTuesday() {
62
+ return this.value === 2;
63
+ }
64
+ isWednesday() {
65
+ return this.value === 3;
66
+ }
67
+ isThursday() {
68
+ return this.value === 4;
69
+ }
70
+ isFriday() {
71
+ return this.value === 5;
72
+ }
73
+ isSaturday() {
74
+ return this.value === 6;
75
+ }
76
+ isSunday() {
77
+ return this.value === 0;
78
+ }
79
+ static list(formatter) {
80
+ return Array.from({ length: 7 }).map((_, index) => new Weekday(index, formatter));
81
+ }
82
+ static listMondayFirst(formatter) {
83
+ const days = Weekday.list(formatter);
84
+ return [...days.slice(1), days[0]];
85
+ }
86
+ }
@@ -0,0 +1,3 @@
1
+ import { z } from "zod/v4";
2
+ export declare const YearIsoId: z.ZodString;
3
+ export type YearIsoIdType = z.infer<typeof YearIsoId>;
@@ -0,0 +1,8 @@
1
+ import { z } from "zod/v4";
2
+ export const YearIsoId = z
3
+ .string()
4
+ .regex(/^\d{4}$/)
5
+ .refine((value) => {
6
+ const year = Number(value);
7
+ return value.length === 4 && Number.isInteger(year);
8
+ }, { message: "year-iso-id.invalid" });
@@ -0,0 +1,11 @@
1
+ import { DateRange } from "./date-range.vo";
2
+ import { type TimestampType } from "./timestamp.vo";
3
+ import { type YearIsoIdType } from "./year-iso-id.vo";
4
+ export declare class Year extends DateRange {
5
+ toIsoId(): YearIsoIdType;
6
+ isLeapYear(): boolean;
7
+ static fromTimestamp(timestamp: TimestampType): Year;
8
+ static fromNow(now: TimestampType): Year;
9
+ static fromNumber(value: number): Year;
10
+ static fromIsoId(isoId: YearIsoIdType): Year;
11
+ }
@@ -0,0 +1,32 @@
1
+ import { endOfYear, getYear, startOfYear } from "date-fns";
2
+ import { DateRange } from "./date-range.vo";
3
+ import { Timestamp } from "./timestamp.vo";
4
+ import { YearIsoId } from "./year-iso-id.vo";
5
+ export class Year extends DateRange {
6
+ toIsoId() {
7
+ return String(getYear(this.getStart()));
8
+ }
9
+ isLeapYear() {
10
+ const year = getYear(this.getStart());
11
+ return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0;
12
+ }
13
+ static fromTimestamp(timestamp) {
14
+ const start = Timestamp.parse(startOfYear(timestamp).getTime());
15
+ const end = Timestamp.parse(endOfYear(timestamp).getTime());
16
+ return new Year(start, end);
17
+ }
18
+ static fromNow(now) {
19
+ return Year.fromTimestamp(now);
20
+ }
21
+ static fromNumber(value) {
22
+ if (!Number.isInteger(value))
23
+ throw new Error("year.invalid_integer");
24
+ if (value < 0 || value > 9999)
25
+ throw new Error("year.out_of_range");
26
+ const reference = Timestamp.parse(Date.UTC(value, 0, 1, 0, 0, 0, 0));
27
+ return Year.fromTimestamp(reference);
28
+ }
29
+ static fromIsoId(isoId) {
30
+ return Year.fromNumber(Number(YearIsoId.parse(isoId)));
31
+ }
32
+ }
@@ -7,9 +7,8 @@ export class ZScore {
7
7
  standardDeviation;
8
8
  constructor(values, rounding = new RoundToDecimal(2)) {
9
9
  this.rounding = rounding;
10
- if (values.length < 2) {
10
+ if (values.length < 2)
11
11
  throw new Error("At least two values are needed");
12
- }
13
12
  this.mean = Mean.calculate(values);
14
13
  this.standardDeviation = PopulationStandardDeviation.calculate(values);
15
14
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bgord/tools",
3
- "version": "0.12.26",
3
+ "version": "0.13.1",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "author": "Bartosz Gordon",
@@ -32,14 +32,14 @@
32
32
  "only-allow": "1.2.1",
33
33
  "shellcheck": "4.1.0",
34
34
  "typescript": "5.9.2",
35
- "zod": "4.1.8"
35
+ "zod": "4.1.9"
36
36
  },
37
37
  "dependencies": {
38
38
  "date-fns": "4.1.0",
39
39
  "mime-types": "3.0.1"
40
40
  },
41
41
  "peerDependencies": {
42
- "zod": "4.1.8"
42
+ "zod": "4.1.9"
43
43
  },
44
44
  "sideEffects": false
45
45
  }
package/readme.md CHANGED
@@ -26,7 +26,6 @@ Run the tests
26
26
  src/
27
27
  ├── api-key.vo.ts
28
28
  ├── basename.vo.ts
29
- ├── build-version.vo.ts
30
29
  ├── clock.vo.ts
31
30
  ├── date-calculator.service.ts
32
31
  ├── date-formatter.service.ts
@@ -47,19 +46,19 @@ src/
47
46
  ├── filename-from-string.vo.ts
48
47
  ├── filename-suffix.vo.ts
49
48
  ├── filename.vo.ts
50
- ├── filter.vo.ts
51
49
  ├── hour.vo.ts
52
50
  ├── iban-mask.service.ts
53
51
  ├── iban.vo.ts
54
52
  ├── image.vo.ts
55
53
  ├── language.vo.ts
56
- ├── leap-year-checker.service.ts
57
54
  ├── mean.service.ts
58
55
  ├── mime-types.vo.ts
59
56
  ├── mime.vo.ts
60
57
  ├── min-max-scaler.service.ts
61
58
  ├── minute.vo.ts
62
59
  ├── money.vo.ts
60
+ ├── month-iso-id.vo.ts
61
+ ├── month.vo.ts
63
62
  ├── noop.service.ts
64
63
  ├── notification-template.vo.ts
65
64
  ├── object-key.vo.ts
@@ -68,6 +67,8 @@ src/
68
67
  ├── pagination.service.ts
69
68
  ├── percentage.service.ts
70
69
  ├── population-standard-deviation.service.ts
70
+ ├── quarter-iso-id.vo.ts
71
+ ├── quarter.vo.ts
71
72
  ├── random.service.ts
72
73
  ├── rate-limiter.service.ts
73
74
  ├── relative-date.vo.ts
@@ -89,6 +90,9 @@ src/
89
90
  ├── visually-unambiguous-characters-generator.service.ts
90
91
  ├── week-iso-id.vo.ts
91
92
  ├── week.vo.ts
93
+ ├── weekday.vo.ts
94
+ ├── year-iso-id.vo.ts
95
+ ├── year.vo.ts
92
96
  └── z-score.service.ts
93
97
  ```
94
98
 
package/src/api-key.vo.ts CHANGED
@@ -1,5 +1,4 @@
1
1
  import { z } from "zod/v4";
2
2
 
3
3
  export const ApiKey = z.string().trim().length(64).brand("ApiKey");
4
-
5
4
  export type ApiKeyType = z.infer<typeof ApiKey>;
@@ -15,5 +15,4 @@ export const BasenameSchema = z
15
15
  .refine((s) => !s.endsWith("."), "basename_trailing_dot_forbidden")
16
16
  .regex(/^[A-Za-z0-9._-]+$/, "basename_bad_chars")
17
17
  .brand("basename");
18
-
19
18
  export type BasenameType = z.infer<typeof BasenameSchema>;
package/src/clock.vo.ts CHANGED
@@ -1,5 +1,6 @@
1
- import { type Hour, HourFormatters } from "./hour.vo";
2
- import type { Minute } from "./minute.vo";
1
+ import { Hour, HourFormatters } from "./hour.vo";
2
+ import { Minute } from "./minute.vo";
3
+ import type { TimestampType } from "./timestamp.vo";
3
4
 
4
5
  export type ClockFormatter = (hour: Hour, minute: Minute) => string;
5
6
 
@@ -26,6 +27,12 @@ export class Clock {
26
27
  this.formatter = (formatter as ClockFormatter) ?? ClockFormatters.TWENTY_FOUR_HOURS;
27
28
  }
28
29
 
30
+ static fromUtcTimestamp(timestamp: TimestampType, formatter?: ClockFormatter) {
31
+ const hour = Hour.fromUtcTimestamp(timestamp);
32
+ const minute = Minute.fromUtcTimestamp(timestamp);
33
+ return new Clock(hour, minute, formatter);
34
+ }
35
+
29
36
  get(formatter?: ClockFormatter) {
30
37
  const format = formatter ?? this.formatter;
31
38
 
@@ -1,10 +1,7 @@
1
1
  import { Time } from "./time.service";
2
2
  import type { TimestampType } from "./timestamp.vo";
3
3
 
4
- type GetStartOfDayTsInTzConfigType = {
5
- now: TimestampType;
6
- timeZoneOffsetMs: number;
7
- };
4
+ type GetStartOfDayTsInTzConfigType = { now: TimestampType; timeZoneOffsetMs: number };
8
5
 
9
6
  export class DateCalculator {
10
7
  static getStartOfDayTsInTz(config: GetStartOfDayTsInTzConfigType) {
@@ -1,7 +1,6 @@
1
1
  import { format, formatDistanceToNow } from "date-fns";
2
2
 
3
3
  type FormattedDateType = string;
4
-
5
4
  type DateFormattersInputType = Parameters<typeof format>[0];
6
5
 
7
6
  export class DateFormatters {
@@ -17,6 +17,6 @@ export const DirectoryPathAbsoluteSchema = z
17
17
  segment.length > 0 && /^[A-Za-z0-9._-]+$/.test(segment) && segment !== "." && segment !== "..",
18
18
  );
19
19
  }, "abs_dir_bad_segments")
20
- .brand("directory_path_absolute_");
20
+ .brand("directory_path_absolute");
21
21
 
22
22
  export type DirectoryPathAbsoluteType = z.infer<typeof DirectoryPathAbsoluteSchema>;
@@ -1,8 +1,6 @@
1
1
  export class Node<T> {
2
2
  data: T;
3
-
4
3
  prev: Node<T> | null = null;
5
-
6
4
  next: Node<T> | null = null;
7
5
 
8
6
  constructor(data: Node<T>["data"]) {
@@ -39,11 +37,8 @@ export class Node<T> {
39
37
 
40
38
  export class DoublyLinkedList<T> {
41
39
  static EMPTY_SIZE = 0;
42
-
43
40
  private size = DoublyLinkedList.EMPTY_SIZE;
44
-
45
41
  private head: Node<T> | null = null;
46
-
47
42
  private tail: Node<T> | null = null;
48
43
 
49
44
  getSize(): DoublyLinkedList<T>["size"] {
@@ -1,7 +1,6 @@
1
1
  import { z } from "zod/v4";
2
2
 
3
3
  export const Email = z.email().brand("Email");
4
-
5
4
  export type EmailType = z.infer<typeof Email>;
6
5
 
7
6
  type EmailMaskedType = string;
package/src/etags.vo.ts CHANGED
@@ -1,7 +1,6 @@
1
1
  import { z } from "zod/v4";
2
2
 
3
3
  const RevisionValue = z.number().int().min(0);
4
-
5
4
  type RevisionValueType = z.infer<typeof RevisionValue>;
6
5
 
7
6
  type ETagValueType = string;
@@ -13,5 +13,4 @@ export const ExtensionSchema = z
13
13
  .regex(/^[a-z0-9]+$/, "extension_bad_chars"),
14
14
  )
15
15
  .brand("extension");
16
-
17
16
  export type ExtensionType = z.infer<typeof ExtensionSchema>;
@@ -4,8 +4,8 @@ export enum FeatureFlagEnum {
4
4
  yes = "yes",
5
5
  no = "no",
6
6
  }
7
- export const FeatureFlagValue = z.enum(FeatureFlagEnum);
8
7
 
8
+ export const FeatureFlagValue = z.enum(FeatureFlagEnum);
9
9
  export type FeatureFlagValueType = z.infer<typeof FeatureFlagValue>;
10
10
 
11
11
  export class FeatureFlag {
@@ -16,5 +16,4 @@ export const FilenameFromStringSchema = z
16
16
  const extension = ExtensionSchema.parse(string.slice(index + 1));
17
17
  return { basename: base, extension: extension };
18
18
  });
19
-
20
19
  export type FilenameFromString = z.infer<typeof FilenameFromStringSchema>;
@@ -6,5 +6,4 @@ export const FilenameSuffixSchema = z
6
6
  .transform((value) => value.replace(/[^A-Za-z0-9_-]/g, ""))
7
7
  .pipe(z.string().max(32, "suffix_too_long"))
8
8
  .brand("basename_suffix");
9
-
10
9
  export type FilenameSuffixSchemaType = z.infer<typeof FilenameSuffixSchema>;
package/src/hour.vo.ts CHANGED
@@ -1,3 +1,5 @@
1
+ import type { TimestampType } from "./timestamp.vo";
2
+
1
3
  export type HourFormatter = (value: Hour["value"]) => string;
2
4
 
3
5
  export enum HourFormatterEnum {
@@ -14,13 +16,13 @@ export const HourFormatters: Record<HourFormatterEnum, HourFormatter> = {
14
16
  TWENTY_FOUR_HOURS_WO_PADDING: (value) => value.toString(),
15
17
 
16
18
  AM_PM: (value) => {
17
- if (value < 12) return `${value.toString()} a.m.`;
18
- return `${value.toString()} p.m.`;
19
+ const twelveHour = value % 12 || 12;
20
+ return `${twelveHour.toString()} ${value < 12 ? "a.m." : "p.m."}`;
19
21
  },
20
22
 
21
- TWELVE_HOURS: (value) => (value % 12).toString().padStart(2, "0"),
23
+ TWELVE_HOURS: (value) => (value % 12 || 12).toString().padStart(2, "0"),
22
24
 
23
- TWELVE_HOURS_WO_PADDING: (value) => (value % 12).toString(),
25
+ TWELVE_HOURS_WO_PADDING: (value) => (value % 12 || 12).toString(),
24
26
  } as const;
25
27
 
26
28
  export class Hour {
@@ -33,15 +35,9 @@ export class Hour {
33
35
  static readonly MAX = new Hour(23);
34
36
 
35
37
  constructor(candidate: number, formatter?: HourFormatter) {
36
- if (!Number.isInteger(candidate)) {
37
- throw new Error("Invalid hour");
38
- }
39
- if (candidate < 0) {
40
- throw new Error("Invalid hour");
41
- }
42
- if (candidate >= 24) {
43
- throw new Error("Invalid hour");
44
- }
38
+ if (!Number.isInteger(candidate)) throw new Error("Invalid hour");
39
+ if (candidate < 0) throw new Error("Invalid hour");
40
+ if (candidate >= 24) throw new Error("Invalid hour");
45
41
 
46
42
  this.value = candidate;
47
43
  this.formatter = (formatter as HourFormatter) ?? HourFormatters.TWENTY_FOUR_HOURS;
@@ -65,6 +61,12 @@ export class Hour {
65
61
  return this.value < another.get().raw;
66
62
  }
67
63
 
64
+ static fromUtcTimestamp(timestamp: TimestampType, formatter?: HourFormatter): Hour {
65
+ const hours = new Date(timestamp).getUTCHours();
66
+
67
+ return new Hour(hours, formatter);
68
+ }
69
+
68
70
  static list(formatter?: HourFormatter) {
69
71
  return Array.from({ length: 24 }).map((_, index) => new Hour(index, formatter));
70
72
  }
package/src/image.vo.ts CHANGED
@@ -1,9 +1,7 @@
1
1
  import { z } from "zod/v4";
2
2
 
3
3
  export const Width = z.number().int().positive().max(10000).brand("Width");
4
-
5
4
  export type WidthType = z.infer<typeof Width>;
6
5
 
7
6
  export const Height = z.number().int().positive().max(10000).brand("Height");
8
-
9
7
  export type HeightType = z.infer<typeof Height>;
package/src/index.ts CHANGED
@@ -1,6 +1,5 @@
1
1
  export * from "./api-key.vo";
2
2
  export * from "./basename.vo";
3
- export * from "./build-version.vo";
4
3
  export * from "./clock.vo";
5
4
  export * from "./date-calculator.service";
6
5
  export * from "./date-formatter.service";
@@ -21,19 +20,19 @@ export * from "./file-path-relative-schema.vo";
21
20
  export * from "./filename.vo";
22
21
  export * from "./filename-from-string.vo";
23
22
  export * from "./filename-suffix.vo";
24
- export * from "./filter.vo";
25
23
  export * from "./hour.vo";
26
24
  export * from "./iban.vo";
27
25
  export * from "./iban-mask.service";
28
26
  export * from "./image.vo";
29
27
  export * from "./language.vo";
30
- export * from "./leap-year-checker.service";
31
28
  export * from "./mean.service";
32
29
  export * from "./mime.vo";
33
30
  export * from "./mime-types.vo";
34
31
  export * from "./min-max-scaler.service";
35
32
  export * from "./minute.vo";
36
33
  export * from "./money.vo";
34
+ export * from "./month.vo";
35
+ export * from "./month-iso-id.vo";
37
36
  export * from "./noop.service";
38
37
  export * from "./notification-template.vo";
39
38
  export * from "./object-key.vo";
@@ -42,6 +41,8 @@ export * from "./package-version.vo";
42
41
  export * from "./pagination.service";
43
42
  export * from "./percentage.service";
44
43
  export * from "./population-standard-deviation.service";
44
+ export * from "./quarter.vo";
45
+ export * from "./quarter-iso-id.vo";
45
46
  export * from "./random.service";
46
47
  export * from "./rate-limiter.service";
47
48
  export * from "./relative-date.vo";
@@ -63,4 +64,7 @@ export * from "./ts-utils";
63
64
  export * from "./visually-unambiguous-characters-generator.service";
64
65
  export * from "./week.vo";
65
66
  export * from "./week-iso-id.vo";
67
+ export * from "./weekday.vo";
68
+ export * from "./year.vo";
69
+ export * from "./year-iso-id.vo";
66
70
  export * from "./z-score.service";
@@ -4,5 +4,4 @@ export const Language = z
4
4
  .string()
5
5
  .length(2)
6
6
  .regex(/^[a-z]{2}$/, { message: "invalid_language" });
7
-
8
7
  export type LanguageType = z.infer<typeof Language>;
@@ -3,9 +3,7 @@ import { Sum } from "./sum.service";
3
3
 
4
4
  export class Mean {
5
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
- }
6
+ if (values.length === 0) throw new Error("Values should not be empty");
9
7
 
10
8
  const mean = Sum.of(values) / values.length;
11
9