@bgord/tools 0.13.0 → 0.13.2

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 (72) hide show
  1. package/dist/directory-path-absolute.vo.d.ts +1 -1
  2. package/dist/directory-path-absolute.vo.js +1 -1
  3. package/dist/file-path-absolute-schema.vo.d.ts +1 -1
  4. package/dist/index.d.ts +6 -1
  5. package/dist/index.js +6 -1
  6. package/dist/minute.vo.d.ts +2 -1
  7. package/dist/money.vo.js +2 -4
  8. package/dist/month-iso-id.vo.d.ts +3 -0
  9. package/dist/month-iso-id.vo.js +15 -0
  10. package/dist/month.vo.d.ts +9 -0
  11. package/dist/month.vo.js +22 -0
  12. package/dist/package-version.vo.js +0 -5
  13. package/dist/pagination.service.js +1 -8
  14. package/dist/quarter-iso-id.vo.d.ts +3 -0
  15. package/dist/quarter-iso-id.vo.js +10 -0
  16. package/dist/quarter.vo.d.ts +9 -0
  17. package/dist/quarter.vo.js +24 -0
  18. package/dist/stopwatch.service.js +1 -1
  19. package/dist/tsconfig.tsbuildinfo +1 -1
  20. package/dist/visually-unambiguous-characters-generator.service.js +1 -0
  21. package/dist/weekday.vo.d.ts +0 -3
  22. package/dist/weekday.vo.js +0 -9
  23. package/dist/year-iso-id.vo.d.ts +3 -0
  24. package/dist/year-iso-id.vo.js +8 -0
  25. package/dist/year.vo.d.ts +11 -0
  26. package/dist/year.vo.js +32 -0
  27. package/package.json +5 -5
  28. package/readme.md +6 -1
  29. package/src/api-key.vo.ts +0 -1
  30. package/src/basename.vo.ts +0 -1
  31. package/src/date-calculator.service.ts +1 -4
  32. package/src/date-formatter.service.ts +0 -1
  33. package/src/directory-path-absolute.vo.ts +1 -1
  34. package/src/dll.service.ts +0 -5
  35. package/src/email-mask.service.ts +0 -1
  36. package/src/etags.vo.ts +0 -1
  37. package/src/extension.vo.ts +0 -1
  38. package/src/feature-flag.vo.ts +1 -1
  39. package/src/filename-from-string.vo.ts +0 -1
  40. package/src/filename-suffix.vo.ts +0 -1
  41. package/src/image.vo.ts +0 -2
  42. package/src/index.ts +6 -1
  43. package/src/language.vo.ts +0 -1
  44. package/src/min-max-scaler.service.ts +1 -5
  45. package/src/minute.vo.ts +3 -1
  46. package/src/money.vo.ts +2 -4
  47. package/src/month-iso-id.vo.ts +25 -0
  48. package/src/month.vo.ts +26 -0
  49. package/src/object-key.vo.ts +0 -1
  50. package/src/package-version.vo.ts +0 -12
  51. package/src/pagination.service.ts +4 -29
  52. package/src/quarter-iso-id.vo.ts +18 -0
  53. package/src/quarter.vo.ts +28 -0
  54. package/src/reordering.service.ts +1 -6
  55. package/src/revision.vo.ts +0 -2
  56. package/src/simple-linear-regression.service.ts +0 -2
  57. package/src/size.vo.ts +0 -1
  58. package/src/stepper.service.ts +0 -1
  59. package/src/stopwatch.service.ts +1 -1
  60. package/src/streak-calculator.service.ts +1 -6
  61. package/src/time-zone-offset-value.vo.ts +0 -1
  62. package/src/timestamp.vo.ts +0 -1
  63. package/src/timezone.vo.ts +0 -1
  64. package/src/ts-utils.ts +0 -2
  65. package/src/visually-unambiguous-characters-generator.service.ts +1 -0
  66. package/src/week-iso-id.vo.ts +0 -1
  67. package/src/weekday.vo.ts +0 -12
  68. package/src/year-iso-id.vo.ts +13 -0
  69. package/src/year.vo.ts +36 -0
  70. package/dist/leap-year-checker.service.d.ts +0 -4
  71. package/dist/leap-year-checker.service.js +0 -9
  72. package/src/leap-year-checker.service.ts +0 -11
@@ -1,3 +1,3 @@
1
1
  import { z } from "zod/v4";
2
- export declare const DirectoryPathAbsoluteSchema: z.core.$ZodBranded<z.ZodPipe<z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>, z.ZodTransform<string, string>>, "directory_path_absolute_">;
2
+ export declare const DirectoryPathAbsoluteSchema: z.core.$ZodBranded<z.ZodPipe<z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>, z.ZodTransform<string, string>>, "directory_path_absolute">;
3
3
  export type DirectoryPathAbsoluteType = z.infer<typeof DirectoryPathAbsoluteSchema>;
@@ -14,4 +14,4 @@ export const DirectoryPathAbsoluteSchema = z
14
14
  const segments = value.slice(1).split("/");
15
15
  return segments.every((segment) => segment.length > 0 && /^[A-Za-z0-9._-]+$/.test(segment) && segment !== "." && segment !== "..");
16
16
  }, "abs_dir_bad_segments")
17
- .brand("directory_path_absolute_");
17
+ .brand("directory_path_absolute");
@@ -1,6 +1,6 @@
1
1
  import { z } from "zod/v4";
2
2
  import { Filename } from "./filename.vo";
3
3
  export declare const FilePathAbsoluteSchema: z.ZodPipe<z.ZodPipe<z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>, z.ZodTransform<string, string>>, z.ZodTransform<{
4
- directory: string & z.core.$brand<"directory_path_absolute_">;
4
+ directory: string & z.core.$brand<"directory_path_absolute">;
5
5
  filename: Filename;
6
6
  }, string>>;
package/dist/index.d.ts CHANGED
@@ -25,13 +25,14 @@ export * from "./iban.vo";
25
25
  export * from "./iban-mask.service";
26
26
  export * from "./image.vo";
27
27
  export * from "./language.vo";
28
- export * from "./leap-year-checker.service";
29
28
  export * from "./mean.service";
30
29
  export * from "./mime.vo";
31
30
  export * from "./mime-types.vo";
32
31
  export * from "./min-max-scaler.service";
33
32
  export * from "./minute.vo";
34
33
  export * from "./money.vo";
34
+ export * from "./month.vo";
35
+ export * from "./month-iso-id.vo";
35
36
  export * from "./noop.service";
36
37
  export * from "./notification-template.vo";
37
38
  export * from "./object-key.vo";
@@ -40,6 +41,8 @@ export * from "./package-version.vo";
40
41
  export * from "./pagination.service";
41
42
  export * from "./percentage.service";
42
43
  export * from "./population-standard-deviation.service";
44
+ export * from "./quarter.vo";
45
+ export * from "./quarter-iso-id.vo";
43
46
  export * from "./random.service";
44
47
  export * from "./rate-limiter.service";
45
48
  export * from "./relative-date.vo";
@@ -62,4 +65,6 @@ export * from "./visually-unambiguous-characters-generator.service";
62
65
  export * from "./week.vo";
63
66
  export * from "./week-iso-id.vo";
64
67
  export * from "./weekday.vo";
68
+ export * from "./year.vo";
69
+ export * from "./year-iso-id.vo";
65
70
  export * from "./z-score.service";
package/dist/index.js CHANGED
@@ -25,13 +25,14 @@ export * from "./iban.vo";
25
25
  export * from "./iban-mask.service";
26
26
  export * from "./image.vo";
27
27
  export * from "./language.vo";
28
- export * from "./leap-year-checker.service";
29
28
  export * from "./mean.service";
30
29
  export * from "./mime.vo";
31
30
  export * from "./mime-types.vo";
32
31
  export * from "./min-max-scaler.service";
33
32
  export * from "./minute.vo";
34
33
  export * from "./money.vo";
34
+ export * from "./month.vo";
35
+ export * from "./month-iso-id.vo";
35
36
  export * from "./noop.service";
36
37
  export * from "./notification-template.vo";
37
38
  export * from "./object-key.vo";
@@ -40,6 +41,8 @@ export * from "./package-version.vo";
40
41
  export * from "./pagination.service";
41
42
  export * from "./percentage.service";
42
43
  export * from "./population-standard-deviation.service";
44
+ export * from "./quarter.vo";
45
+ export * from "./quarter-iso-id.vo";
43
46
  export * from "./random.service";
44
47
  export * from "./rate-limiter.service";
45
48
  export * from "./relative-date.vo";
@@ -62,4 +65,6 @@ export * from "./visually-unambiguous-characters-generator.service";
62
65
  export * from "./week.vo";
63
66
  export * from "./week-iso-id.vo";
64
67
  export * from "./weekday.vo";
68
+ export * from "./year.vo";
69
+ export * from "./year-iso-id.vo";
65
70
  export * from "./z-score.service";
@@ -1,9 +1,10 @@
1
+ import type { TimestampType } from "./timestamp.vo";
1
2
  export declare class Minute {
2
3
  private readonly value;
3
4
  static readonly ZERO: Minute;
4
5
  static readonly MAX: Minute;
5
6
  constructor(candidate: number);
6
- static fromUtcTimestamp(timestamp: number): Minute;
7
+ static fromUtcTimestamp(timestamp: TimestampType): Minute;
7
8
  get(): {
8
9
  raw: number;
9
10
  formatted: string;
package/dist/money.vo.js CHANGED
@@ -2,8 +2,8 @@ import { z } from "zod/v4";
2
2
  import { RoundToNearest } from "./rounding.service";
3
3
  export const MoneyAmount = z
4
4
  .number()
5
- .int({ message: "money.amount.invalid " })
6
- .min(0, { message: "money.amount.invalid " })
5
+ .int({ message: "money.amount.invalid" })
6
+ .min(0, { message: "money.amount.invalid" })
7
7
  .brand("MoneyAmount");
8
8
  export const MoneyMultiplicationFactor = z
9
9
  .number()
@@ -40,8 +40,6 @@ export class Money {
40
40
  return new Money(MoneyAmount.parse(result), this.rounding);
41
41
  }
42
42
  divide(factor) {
43
- if (factor === 0)
44
- throw new Error("Cannot divide by zero");
45
43
  const result = this.rounding.round(this.amount / factor);
46
44
  return new Money(MoneyAmount.parse(result), this.rounding);
47
45
  }
@@ -0,0 +1,3 @@
1
+ import { z } from "zod/v4";
2
+ export declare const MonthIsoId: z.ZodString;
3
+ export type MonthIsoIdType = z.infer<typeof MonthIsoId>;
@@ -0,0 +1,15 @@
1
+ import { z } from "zod/v4";
2
+ export const MonthIsoId = z
3
+ .string()
4
+ .regex(/^\d{4}-\d{2}$/)
5
+ .refine((value) => {
6
+ const [y, m] = value.split("-");
7
+ const year = Number(y);
8
+ const month = Number(m);
9
+ return (y.length === 4 &&
10
+ m.length === 2 &&
11
+ Number.isInteger(year) &&
12
+ Number.isInteger(month) &&
13
+ month >= 1 &&
14
+ month <= 12);
15
+ }, { message: "month-iso-id.invalid" });
@@ -0,0 +1,9 @@
1
+ import { DateRange } from "./date-range.vo";
2
+ import { type MonthIsoIdType } from "./month-iso-id.vo";
3
+ import { type TimestampType } from "./timestamp.vo";
4
+ export declare class Month extends DateRange {
5
+ toIsoId(): MonthIsoIdType;
6
+ static fromTimestamp(timestamp: TimestampType): Month;
7
+ static fromNow(now: TimestampType): Month;
8
+ static fromIsoId(iso: MonthIsoIdType): Month;
9
+ }
@@ -0,0 +1,22 @@
1
+ import { endOfMonth, startOfMonth } from "date-fns";
2
+ import { DateRange } from "./date-range.vo";
3
+ import { MonthIsoId } from "./month-iso-id.vo";
4
+ import { Timestamp } from "./timestamp.vo";
5
+ export class Month extends DateRange {
6
+ toIsoId() {
7
+ return new Date(this.getStart()).toISOString().slice(0, 7);
8
+ }
9
+ static fromTimestamp(timestamp) {
10
+ const start = Timestamp.parse(startOfMonth(timestamp).getTime());
11
+ const end = Timestamp.parse(endOfMonth(timestamp).getTime());
12
+ return new Month(start, end);
13
+ }
14
+ static fromNow(now) {
15
+ return Month.fromTimestamp(now);
16
+ }
17
+ static fromIsoId(iso) {
18
+ const [year, month] = MonthIsoId.parse(iso).split("-").map(Number);
19
+ const reference = new Date(Date.UTC(year, month - 1, 1));
20
+ return Month.fromTimestamp(Timestamp.parse(reference.getTime()));
21
+ }
22
+ }
@@ -17,11 +17,6 @@ export const PackageVersionValue = z
17
17
  Number.isInteger(Number(patch)))) {
18
18
  return false;
19
19
  }
20
- if (!(Number.isInteger(Number(major)) &&
21
- Number.isInteger(Number(minor)) &&
22
- Number.isInteger(Number(patch)))) {
23
- return false;
24
- }
25
20
  return true;
26
21
  }
27
22
  catch (_error) {
@@ -21,14 +21,7 @@ export class Pagination {
21
21
  const nextPage = currentPage < lastPage ? Page.parse(currentPage + 1) : undefined;
22
22
  return {
23
23
  result: config.result,
24
- meta: {
25
- exhausted,
26
- currentPage,
27
- previousPage,
28
- nextPage,
29
- lastPage,
30
- total: config.total,
31
- },
24
+ meta: { exhausted, currentPage, previousPage, nextPage, lastPage, total: config.total },
32
25
  };
33
26
  }
34
27
  static isExhausted(config) {
@@ -0,0 +1,3 @@
1
+ import { z } from "zod/v4";
2
+ export declare const QuarterIsoId: z.ZodString;
3
+ export type QuarterIsoIdType = z.infer<typeof QuarterIsoId>;
@@ -0,0 +1,10 @@
1
+ import { z } from "zod/v4";
2
+ export const QuarterIsoId = z
3
+ .string()
4
+ .regex(/^\d{4}-Q[1-4]$/)
5
+ .refine((value) => {
6
+ const [y, q] = value.split("-Q");
7
+ const year = Number(y);
8
+ const quarter = Number(q);
9
+ return (y.length === 4 && Number.isInteger(year) && Number.isInteger(quarter) && quarter >= 1 && quarter <= 4);
10
+ }, { message: "quarter-iso-id.invalid" });
@@ -0,0 +1,9 @@
1
+ import { DateRange } from "./date-range.vo";
2
+ import { type QuarterIsoIdType } from "./quarter-iso-id.vo";
3
+ import { type TimestampType } from "./timestamp.vo";
4
+ export declare class Quarter extends DateRange {
5
+ toIsoId(): QuarterIsoIdType;
6
+ static fromTimestamp(timestamp: TimestampType): Quarter;
7
+ static fromNow(now: TimestampType): Quarter;
8
+ static fromIsoId(isoId: QuarterIsoIdType): Quarter;
9
+ }
@@ -0,0 +1,24 @@
1
+ import { endOfQuarter, getQuarter, getYear, setQuarter, startOfQuarter } from "date-fns";
2
+ import { DateRange } from "./date-range.vo";
3
+ import { QuarterIsoId } from "./quarter-iso-id.vo";
4
+ import { Timestamp } from "./timestamp.vo";
5
+ export class Quarter extends DateRange {
6
+ toIsoId() {
7
+ const year = getYear(this.getStart());
8
+ const quarter = getQuarter(this.getStart());
9
+ return `${year}-Q${quarter}`;
10
+ }
11
+ static fromTimestamp(timestamp) {
12
+ const start = Timestamp.parse(startOfQuarter(timestamp).getTime());
13
+ const end = Timestamp.parse(endOfQuarter(timestamp).getTime());
14
+ return new Quarter(start, end);
15
+ }
16
+ static fromNow(now) {
17
+ return Quarter.fromTimestamp(now);
18
+ }
19
+ static fromIsoId(isoId) {
20
+ const [year, quarter] = QuarterIsoId.parse(isoId).split("-Q").map(Number);
21
+ const reference = setQuarter(new Date(Date.UTC(year, 0, 1)), quarter);
22
+ return Quarter.fromTimestamp(Timestamp.parse(reference.getTime()));
23
+ }
24
+ }
@@ -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;
@@ -1 +1 @@
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/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/weekday.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",
@@ -24,10 +24,7 @@ export declare class Weekday {
24
24
  formatted: string;
25
25
  };
26
26
  equals(another: Weekday): boolean;
27
- isAfter(another: Weekday): boolean;
28
- isBefore(another: Weekday): boolean;
29
27
  toIsoNumber(): number;
30
- isWeekend(): boolean;
31
28
  isMonday(): boolean;
32
29
  isTuesday(): boolean;
33
30
  isWednesday(): boolean;
@@ -52,18 +52,9 @@ export class Weekday {
52
52
  equals(another) {
53
53
  return this.value === another.get().raw;
54
54
  }
55
- isAfter(another) {
56
- return this.value > another.get().raw;
57
- }
58
- isBefore(another) {
59
- return this.value < another.get().raw;
60
- }
61
55
  toIsoNumber() {
62
56
  return this.value === 0 ? 7 : this.value;
63
57
  }
64
- isWeekend() {
65
- return this.value === 0 || this.value === 6;
66
- }
67
58
  isMonday() {
68
59
  return this.value === 1;
69
60
  }
@@ -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
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bgord/tools",
3
- "version": "0.13.0",
3
+ "version": "0.13.2",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "author": "Bartosz Gordon",
@@ -27,19 +27,19 @@
27
27
  "@types/bun": "1.2.22",
28
28
  "@types/mime-types": "3.0.1",
29
29
  "cspell": "9.2.1",
30
- "knip": "5.63.1",
31
- "lefthook": "1.13.0",
30
+ "knip": "5.64.0",
31
+ "lefthook": "1.13.2",
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.11"
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.11"
43
43
  },
44
44
  "sideEffects": false
45
45
  }
package/readme.md CHANGED
@@ -51,13 +51,14 @@ src/
51
51
  ├── iban.vo.ts
52
52
  ├── image.vo.ts
53
53
  ├── language.vo.ts
54
- ├── leap-year-checker.service.ts
55
54
  ├── mean.service.ts
56
55
  ├── mime-types.vo.ts
57
56
  ├── mime.vo.ts
58
57
  ├── min-max-scaler.service.ts
59
58
  ├── minute.vo.ts
60
59
  ├── money.vo.ts
60
+ ├── month-iso-id.vo.ts
61
+ ├── month.vo.ts
61
62
  ├── noop.service.ts
62
63
  ├── notification-template.vo.ts
63
64
  ├── object-key.vo.ts
@@ -66,6 +67,8 @@ src/
66
67
  ├── pagination.service.ts
67
68
  ├── percentage.service.ts
68
69
  ├── population-standard-deviation.service.ts
70
+ ├── quarter-iso-id.vo.ts
71
+ ├── quarter.vo.ts
69
72
  ├── random.service.ts
70
73
  ├── rate-limiter.service.ts
71
74
  ├── relative-date.vo.ts
@@ -88,6 +91,8 @@ src/
88
91
  ├── week-iso-id.vo.ts
89
92
  ├── week.vo.ts
90
93
  ├── weekday.vo.ts
94
+ ├── year-iso-id.vo.ts
95
+ ├── year.vo.ts
91
96
  └── z-score.service.ts
92
97
  ```
93
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>;
@@ -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/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
@@ -25,13 +25,14 @@ export * from "./iban.vo";
25
25
  export * from "./iban-mask.service";
26
26
  export * from "./image.vo";
27
27
  export * from "./language.vo";
28
- export * from "./leap-year-checker.service";
29
28
  export * from "./mean.service";
30
29
  export * from "./mime.vo";
31
30
  export * from "./mime-types.vo";
32
31
  export * from "./min-max-scaler.service";
33
32
  export * from "./minute.vo";
34
33
  export * from "./money.vo";
34
+ export * from "./month.vo";
35
+ export * from "./month-iso-id.vo";
35
36
  export * from "./noop.service";
36
37
  export * from "./notification-template.vo";
37
38
  export * from "./object-key.vo";
@@ -40,6 +41,8 @@ export * from "./package-version.vo";
40
41
  export * from "./pagination.service";
41
42
  export * from "./percentage.service";
42
43
  export * from "./population-standard-deviation.service";
44
+ export * from "./quarter.vo";
45
+ export * from "./quarter-iso-id.vo";
43
46
  export * from "./random.service";
44
47
  export * from "./rate-limiter.service";
45
48
  export * from "./relative-date.vo";
@@ -62,4 +65,6 @@ export * from "./visually-unambiguous-characters-generator.service";
62
65
  export * from "./week.vo";
63
66
  export * from "./week-iso-id.vo";
64
67
  export * from "./weekday.vo";
68
+ export * from "./year.vo";
69
+ export * from "./year-iso-id.vo";
65
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>;
@@ -1,14 +1,10 @@
1
1
  import { type RoundingStrategy, RoundToDecimal } from "./rounding.service";
2
2
 
3
3
  type MinMaxScalerValueType = number;
4
-
5
4
  type MinMaxScalerConfigType = {
6
5
  min: MinMaxScalerValueType;
7
6
  max: MinMaxScalerValueType;
8
- bound?: {
9
- lower: MinMaxScalerValueType;
10
- upper: MinMaxScalerValueType;
11
- };
7
+ bound?: { lower: MinMaxScalerValueType; upper: MinMaxScalerValueType };
12
8
  rounding?: RoundingStrategy;
13
9
  };
14
10
 
package/src/minute.vo.ts CHANGED
@@ -1,3 +1,5 @@
1
+ import type { TimestampType } from "./timestamp.vo";
2
+
1
3
  export class Minute {
2
4
  private readonly value: number;
3
5
 
@@ -13,7 +15,7 @@ export class Minute {
13
15
  this.value = candidate;
14
16
  }
15
17
 
16
- static fromUtcTimestamp(timestamp: number): Minute {
18
+ static fromUtcTimestamp(timestamp: TimestampType): Minute {
17
19
  const minutes = new Date(timestamp).getUTCMinutes();
18
20
  return new Minute(minutes);
19
21
  }
package/src/money.vo.ts CHANGED
@@ -3,8 +3,8 @@ import { type RoundingStrategy, RoundToNearest } from "./rounding.service";
3
3
 
4
4
  export const MoneyAmount = z
5
5
  .number()
6
- .int({ message: "money.amount.invalid " })
7
- .min(0, { message: "money.amount.invalid " })
6
+ .int({ message: "money.amount.invalid" })
7
+ .min(0, { message: "money.amount.invalid" })
8
8
  .brand("MoneyAmount");
9
9
 
10
10
  export type MoneyAmountType = z.infer<typeof MoneyAmount>;
@@ -61,8 +61,6 @@ export class Money {
61
61
  }
62
62
 
63
63
  divide(factor: MoneyDivisionFactorType) {
64
- if (factor === 0) throw new Error("Cannot divide by zero");
65
-
66
64
  const result = this.rounding.round(this.amount / factor);
67
65
 
68
66
  return new Money(MoneyAmount.parse(result), this.rounding);
@@ -0,0 +1,25 @@
1
+ import { z } from "zod/v4";
2
+
3
+ export const MonthIsoId = z
4
+ .string()
5
+ .regex(/^\d{4}-\d{2}$/)
6
+ .refine(
7
+ (value) => {
8
+ const [y, m] = value.split("-");
9
+
10
+ const year = Number(y);
11
+ const month = Number(m);
12
+
13
+ return (
14
+ y.length === 4 &&
15
+ m.length === 2 &&
16
+ Number.isInteger(year) &&
17
+ Number.isInteger(month) &&
18
+ month >= 1 &&
19
+ month <= 12
20
+ );
21
+ },
22
+ { message: "month-iso-id.invalid" },
23
+ );
24
+
25
+ export type MonthIsoIdType = z.infer<typeof MonthIsoId>;
@@ -0,0 +1,26 @@
1
+ import { endOfMonth, startOfMonth } from "date-fns";
2
+ import { DateRange } from "./date-range.vo";
3
+ import { MonthIsoId, type MonthIsoIdType } from "./month-iso-id.vo";
4
+ import { Timestamp, type TimestampType } from "./timestamp.vo";
5
+
6
+ export class Month extends DateRange {
7
+ toIsoId(): MonthIsoIdType {
8
+ return new Date(this.getStart()).toISOString().slice(0, 7) as MonthIsoIdType;
9
+ }
10
+
11
+ static fromTimestamp(timestamp: TimestampType): Month {
12
+ const start = Timestamp.parse(startOfMonth(timestamp).getTime());
13
+ const end = Timestamp.parse(endOfMonth(timestamp).getTime());
14
+ return new Month(start, end);
15
+ }
16
+
17
+ static fromNow(now: TimestampType): Month {
18
+ return Month.fromTimestamp(now);
19
+ }
20
+
21
+ static fromIsoId(iso: MonthIsoIdType): Month {
22
+ const [year, month] = MonthIsoId.parse(iso).split("-").map(Number);
23
+ const reference = new Date(Date.UTC(year, month - 1, 1));
24
+ return Month.fromTimestamp(Timestamp.parse(reference.getTime()));
25
+ }
26
+ }
@@ -13,5 +13,4 @@ export const ObjectKey = z
13
13
  "obj_key_bad_segments",
14
14
  )
15
15
  .brand("object_key");
16
-
17
16
  export type ObjectKeyType = z.infer<typeof ObjectKey>;
@@ -1,9 +1,7 @@
1
1
  import { z } from "zod/v4";
2
2
 
3
3
  type MajorType = number;
4
-
5
4
  type MinorType = number;
6
-
7
5
  type PatchType = number;
8
6
 
9
7
  export const PackageVersionValue = z
@@ -30,16 +28,6 @@ export const PackageVersionValue = z
30
28
  return false;
31
29
  }
32
30
 
33
- if (
34
- !(
35
- Number.isInteger(Number(major)) &&
36
- Number.isInteger(Number(minor)) &&
37
- Number.isInteger(Number(patch))
38
- )
39
- ) {
40
- return false;
41
- }
42
-
43
31
  return true;
44
32
  } catch (_error) {
45
33
  return false;
@@ -1,11 +1,9 @@
1
1
  import { z } from "zod/v4";
2
2
 
3
3
  const Take = z.number().int().gte(0);
4
-
5
4
  type TakeType = z.infer<typeof Take>;
6
5
 
7
6
  const Skip = z.number().int().gte(0);
8
-
9
7
  type SkipType = z.infer<typeof Skip>;
10
8
 
11
9
  const Page = z.coerce
@@ -13,30 +11,14 @@ const Page = z.coerce
13
11
  .int()
14
12
  .transform((value) => (value <= 0 ? 1 : value))
15
13
  .default(1);
16
-
17
14
  export type PageType = z.infer<typeof Page>;
18
15
 
19
- export type PaginationType = {
20
- values: { take: TakeType; skip: SkipType };
21
- page: PageType;
22
- };
23
-
16
+ export type PaginationType = { values: { take: TakeType; skip: SkipType }; page: PageType };
24
17
  export type PaginationValuesType = Record<string, unknown>;
25
-
26
18
  export type TotalType = number;
27
-
28
19
  export type ExhaustedType = boolean;
29
-
30
- export type PaginationExhaustedConfig = {
31
- total: TotalType;
32
- pagination: PaginationType;
33
- };
34
-
35
- export type PaginationPrepareConfigType<T> = {
36
- total: TotalType;
37
- pagination: PaginationType;
38
- result: T[];
39
- };
20
+ export type PaginationExhaustedConfig = { total: TotalType; pagination: PaginationType };
21
+ export type PaginationPrepareConfigType<T> = { total: TotalType; pagination: PaginationType; result: T[] };
40
22
 
41
23
  export class Pagination {
42
24
  static parse(values: PaginationValuesType, _take: TakeType): PaginationType {
@@ -59,14 +41,7 @@ export class Pagination {
59
41
 
60
42
  return {
61
43
  result: config.result,
62
- meta: {
63
- exhausted,
64
- currentPage,
65
- previousPage,
66
- nextPage,
67
- lastPage,
68
- total: config.total,
69
- },
44
+ meta: { exhausted, currentPage, previousPage, nextPage, lastPage, total: config.total },
70
45
  };
71
46
  }
72
47
 
@@ -0,0 +1,18 @@
1
+ import { z } from "zod/v4";
2
+
3
+ export const QuarterIsoId = z
4
+ .string()
5
+ .regex(/^\d{4}-Q[1-4]$/)
6
+ .refine(
7
+ (value) => {
8
+ const [y, q] = value.split("-Q");
9
+ const year = Number(y);
10
+ const quarter = Number(q);
11
+
12
+ return (
13
+ y.length === 4 && Number.isInteger(year) && Number.isInteger(quarter) && quarter >= 1 && quarter <= 4
14
+ );
15
+ },
16
+ { message: "quarter-iso-id.invalid" },
17
+ );
18
+ export type QuarterIsoIdType = z.infer<typeof QuarterIsoId>;
@@ -0,0 +1,28 @@
1
+ import { endOfQuarter, getQuarter, getYear, setQuarter, startOfQuarter } from "date-fns";
2
+ import { DateRange } from "./date-range.vo";
3
+ import { QuarterIsoId, type QuarterIsoIdType } from "./quarter-iso-id.vo";
4
+ import { Timestamp, type TimestampType } from "./timestamp.vo";
5
+
6
+ export class Quarter extends DateRange {
7
+ toIsoId(): QuarterIsoIdType {
8
+ const year = getYear(this.getStart());
9
+ const quarter = getQuarter(this.getStart());
10
+ return `${year}-Q${quarter}` as QuarterIsoIdType;
11
+ }
12
+
13
+ static fromTimestamp(timestamp: TimestampType): Quarter {
14
+ const start = Timestamp.parse(startOfQuarter(timestamp).getTime());
15
+ const end = Timestamp.parse(endOfQuarter(timestamp).getTime());
16
+ return new Quarter(start, end);
17
+ }
18
+
19
+ static fromNow(now: TimestampType): Quarter {
20
+ return Quarter.fromTimestamp(now);
21
+ }
22
+
23
+ static fromIsoId(isoId: QuarterIsoIdType): Quarter {
24
+ const [year, quarter] = QuarterIsoId.parse(isoId).split("-Q").map(Number);
25
+ const reference = setQuarter(new Date(Date.UTC(year, 0, 1)), quarter);
26
+ return Quarter.fromTimestamp(Timestamp.parse(reference.getTime()));
27
+ }
28
+ }
@@ -2,15 +2,12 @@ import { z } from "zod/v4";
2
2
  import { DoublyLinkedList, Node } from "./dll.service";
3
3
 
4
4
  export const ReorderingItemPositionValue = z.number().int().min(0);
5
-
6
5
  export type ReorderingItemPositionValueType = z.infer<typeof ReorderingItemPositionValue>;
7
6
 
8
7
  export const ReorderingCorrelationId = z.string().min(1);
9
-
10
8
  export type ReorderingCorrelationIdType = z.infer<typeof ReorderingCorrelationId>;
11
9
 
12
10
  export const ReorderingItemId = z.uuid();
13
-
14
11
  export type ReorderingItemIdType = z.infer<typeof ReorderingItemId>;
15
12
 
16
13
  export const Reordering = z.object({
@@ -21,9 +18,7 @@ export const Reordering = z.object({
21
18
 
22
19
  export type ReorderingType = z.infer<typeof Reordering>;
23
20
 
24
- export type WithReorderingPositionValue<T> = T & {
25
- position: ReorderingItemPositionValueType;
26
- };
21
+ export type WithReorderingPositionValue<T> = T & { position: ReorderingItemPositionValueType };
27
22
 
28
23
  export class ReorderingPosition {
29
24
  readonly value: ReorderingItemPositionValueType;
@@ -2,12 +2,10 @@ import { z } from "zod/v4";
2
2
  import type { ETag, WeakETag } from "./etags.vo";
3
3
 
4
4
  export const RevisionValue = z.number().int().min(0);
5
-
6
5
  export type RevisionValueType = z.infer<typeof RevisionValue>;
7
6
 
8
7
  export class Revision {
9
8
  readonly value: RevisionValueType;
10
-
11
9
  static initial: RevisionValueType = 0;
12
10
 
13
11
  constructor(value: unknown) {
@@ -2,9 +2,7 @@ import { type RoundingStrategy, RoundToNearest } from "./rounding.service";
2
2
  import { Sum } from "./sum.service";
3
3
 
4
4
  export type SLRPairType = { x: number; y: number };
5
-
6
5
  export type SLRParamsType = { a: number; b: number };
7
-
8
6
  export type SLRPredictionType = number;
9
7
 
10
8
  export class SimpleLinearRegression {
package/src/size.vo.ts CHANGED
@@ -9,7 +9,6 @@ export enum SizeUnit {
9
9
  }
10
10
 
11
11
  export const SizeValue = z.number().positive().brand("SizeValue");
12
-
13
12
  type SizeValueType = z.infer<typeof SizeValue>;
14
13
 
15
14
  type SizeConfigType = { unit: SizeUnit; value: number };
@@ -1,5 +1,4 @@
1
1
  type StepType = number;
2
-
3
2
  type StepperConfigType = { total: StepType };
4
3
 
5
4
  export class Stepper {
@@ -3,7 +3,7 @@ import type { Falsy } from "./ts-utils";
3
3
 
4
4
  enum StopwatchState {
5
5
  started = "started",
6
- stopped = "finished",
6
+ stopped = "stopped",
7
7
  }
8
8
 
9
9
  export type StopwatchResultType = { durationMs: TimestampType };
@@ -1,12 +1,7 @@
1
1
  import { format, isAfter, isEqual, subDays } from "date-fns";
2
2
 
3
3
  type DateType = string;
4
-
5
- export type StreakType = {
6
- cutoff: DateType;
7
- dates: DateType[];
8
- streak: number;
9
- };
4
+ export type StreakType = { cutoff: DateType; dates: DateType[]; streak: number };
10
5
 
11
6
  export class StreakCalculator {
12
7
  private readonly cutoff: DateType;
@@ -7,5 +7,4 @@ export const TimeZoneOffsetValue = z
7
7
  .transform((value) => Number(value))
8
8
  .transform((value) => (Number.isNaN(value) ? 0 : value))
9
9
  .brand("TimeZoneOffsetValue");
10
-
11
10
  export type TimeZoneOffsetValueType = z.infer<typeof TimeZoneOffsetValue>;
@@ -1,5 +1,4 @@
1
1
  import { z } from "zod/v4";
2
2
 
3
3
  export const Timestamp = z.number().int().gte(0).brand("Timestamp");
4
-
5
4
  export type TimestampType = z.infer<typeof Timestamp>;
@@ -17,5 +17,4 @@ export const Timezone = z
17
17
  { message: "timezone.invalid" },
18
18
  )
19
19
  .brand("Timezone");
20
-
21
20
  export type TimezoneType = z.infer<typeof Timezone>;
package/src/ts-utils.ts CHANGED
@@ -1,5 +1,3 @@
1
1
  export type Constructor<T> = new (...args: any[]) => T;
2
-
3
2
  export type Falsy<T> = T | null | undefined;
4
-
5
3
  export type Nullable<T> = T | null;
@@ -1,6 +1,7 @@
1
1
  import { Random } from "./random.service";
2
2
 
3
3
  export class VisuallyUnambiguousCharactersGenerator {
4
+ // prettier-ignore
4
5
  static chars = [
5
6
  "a",
6
7
  "b",
@@ -20,5 +20,4 @@ export const WeekIsoId = z
20
20
  },
21
21
  { message: "week-iso-id.invalid" },
22
22
  );
23
-
24
23
  export type WeekIsoIdType = z.infer<typeof WeekIsoId>;
package/src/weekday.vo.ts CHANGED
@@ -64,22 +64,10 @@ export class Weekday {
64
64
  return this.value === another.get().raw;
65
65
  }
66
66
 
67
- isAfter(another: Weekday): boolean {
68
- return this.value > another.get().raw;
69
- }
70
-
71
- isBefore(another: Weekday): boolean {
72
- return this.value < another.get().raw;
73
- }
74
-
75
67
  toIsoNumber(): number {
76
68
  return this.value === 0 ? 7 : this.value;
77
69
  }
78
70
 
79
- isWeekend(): boolean {
80
- return this.value === 0 || this.value === 6;
81
- }
82
-
83
71
  isMonday(): boolean {
84
72
  return this.value === 1;
85
73
  }
@@ -0,0 +1,13 @@
1
+ import { z } from "zod/v4";
2
+
3
+ export const YearIsoId = z
4
+ .string()
5
+ .regex(/^\d{4}$/)
6
+ .refine(
7
+ (value) => {
8
+ const year = Number(value);
9
+ return value.length === 4 && Number.isInteger(year);
10
+ },
11
+ { message: "year-iso-id.invalid" },
12
+ );
13
+ export type YearIsoIdType = z.infer<typeof YearIsoId>;
package/src/year.vo.ts ADDED
@@ -0,0 +1,36 @@
1
+ import { endOfYear, getYear, startOfYear } from "date-fns";
2
+ import { DateRange } from "./date-range.vo";
3
+ import { Timestamp, type TimestampType } from "./timestamp.vo";
4
+ import { YearIsoId, type YearIsoIdType } from "./year-iso-id.vo";
5
+
6
+ export class Year extends DateRange {
7
+ toIsoId(): YearIsoIdType {
8
+ return String(getYear(this.getStart())) as YearIsoIdType;
9
+ }
10
+
11
+ isLeapYear(): boolean {
12
+ const year = getYear(this.getStart());
13
+ return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0;
14
+ }
15
+
16
+ static fromTimestamp(timestamp: TimestampType): Year {
17
+ const start = Timestamp.parse(startOfYear(timestamp).getTime());
18
+ const end = Timestamp.parse(endOfYear(timestamp).getTime());
19
+ return new Year(start, end);
20
+ }
21
+
22
+ static fromNow(now: TimestampType): Year {
23
+ return Year.fromTimestamp(now);
24
+ }
25
+
26
+ static fromNumber(value: number): Year {
27
+ if (!Number.isInteger(value)) throw new Error("year.invalid_integer");
28
+ if (value < 0 || value > 9999) throw new Error("year.out_of_range");
29
+ const reference = Timestamp.parse(Date.UTC(value, 0, 1, 0, 0, 0, 0));
30
+ return Year.fromTimestamp(reference);
31
+ }
32
+
33
+ static fromIsoId(isoId: YearIsoIdType): Year {
34
+ return Year.fromNumber(Number(YearIsoId.parse(isoId)));
35
+ }
36
+ }
@@ -1,4 +0,0 @@
1
- export declare class LeapYearChecker {
2
- static isLeapYear(year: number): boolean;
3
- static isCurrentYearLeapYear(): boolean;
4
- }
@@ -1,9 +0,0 @@
1
- export class LeapYearChecker {
2
- static isLeapYear(year) {
3
- return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0;
4
- }
5
- static isCurrentYearLeapYear() {
6
- const year = new Date().getFullYear();
7
- return LeapYearChecker.isLeapYear(year);
8
- }
9
- }
@@ -1,11 +0,0 @@
1
- export class LeapYearChecker {
2
- static isLeapYear(year: number): boolean {
3
- return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0;
4
- }
5
-
6
- static isCurrentYearLeapYear(): boolean {
7
- const year = new Date().getFullYear();
8
-
9
- return LeapYearChecker.isLeapYear(year);
10
- }
11
- }