@bgord/tools 0.15.1 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (122) hide show
  1. package/dist/basename.vo.d.ts +11 -2
  2. package/dist/basename.vo.js +22 -13
  3. package/dist/date-calculator.service.d.ts +2 -2
  4. package/dist/date-calculator.service.js +10 -11
  5. package/dist/date-range.vo.d.ts +1 -0
  6. package/dist/date-range.vo.js +2 -1
  7. package/dist/day-iso-id.vo.d.ts +3 -0
  8. package/dist/day-iso-id.vo.js +4 -4
  9. package/dist/day.vo.js +12 -10
  10. package/dist/directory-path-absolute.vo.d.ts +5 -0
  11. package/dist/directory-path-absolute.vo.js +11 -5
  12. package/dist/directory-path-relative.vo.d.ts +6 -0
  13. package/dist/directory-path-relative.vo.js +12 -6
  14. package/dist/dll.service.js +37 -31
  15. package/dist/extension.vo.d.ts +6 -2
  16. package/dist/extension.vo.js +11 -9
  17. package/dist/file-path-absolute-schema.vo.d.ts +5 -0
  18. package/dist/file-path-absolute-schema.vo.js +12 -6
  19. package/dist/file-path-relative-schema.vo.d.ts +6 -1
  20. package/dist/file-path-relative-schema.vo.js +10 -6
  21. package/dist/file-path.vo.js +4 -4
  22. package/dist/filename-from-string.vo.d.ts +6 -4
  23. package/dist/filename-from-string.vo.js +15 -14
  24. package/dist/filename-suffix.vo.d.ts +4 -2
  25. package/dist/filename-suffix.vo.js +5 -3
  26. package/dist/filename.vo.d.ts +2 -2
  27. package/dist/filename.vo.js +9 -9
  28. package/dist/height.vo.d.ts +6 -4
  29. package/dist/height.vo.js +56 -51
  30. package/dist/index.d.ts +0 -1
  31. package/dist/index.js +0 -1
  32. package/dist/language.vo.d.ts +1 -1
  33. package/dist/language.vo.js +1 -2
  34. package/dist/mime.vo.d.ts +3 -1
  35. package/dist/mime.vo.js +8 -6
  36. package/dist/month-iso-id.vo.d.ts +3 -0
  37. package/dist/month-iso-id.vo.js +7 -12
  38. package/dist/month.vo.js +15 -13
  39. package/dist/object-key.vo.d.ts +5 -0
  40. package/dist/object-key.vo.js +16 -6
  41. package/dist/package-version.vo.d.ts +3 -0
  42. package/dist/package-version.vo.js +12 -34
  43. package/dist/pagination.service.d.ts +1 -1
  44. package/dist/pagination.service.js +11 -11
  45. package/dist/quarter-iso-id.vo.d.ts +3 -0
  46. package/dist/quarter-iso-id.vo.js +8 -7
  47. package/dist/rate-limiter.service.d.ts +3 -2
  48. package/dist/rate-limiter.service.js +4 -2
  49. package/dist/reordering.service.d.ts +20 -2
  50. package/dist/reordering.service.js +49 -29
  51. package/dist/revision.vo.d.ts +8 -3
  52. package/dist/revision.vo.js +13 -6
  53. package/dist/rounding.adapter.js +1 -2
  54. package/dist/size.vo.d.ts +1 -0
  55. package/dist/size.vo.js +4 -7
  56. package/dist/streak-calculator.service.d.ts +3 -4
  57. package/dist/streak-calculator.service.js +11 -17
  58. package/dist/time-zone-offset-value.vo.d.ts +1 -1
  59. package/dist/time-zone-offset-value.vo.js +1 -7
  60. package/dist/time.service.d.ts +11 -6
  61. package/dist/time.service.js +31 -18
  62. package/dist/timezone.vo.js +1 -3
  63. package/dist/tsconfig.tsbuildinfo +1 -1
  64. package/dist/week-iso-id.vo.d.ts +3 -0
  65. package/dist/week-iso-id.vo.js +4 -4
  66. package/dist/week.vo.js +1 -1
  67. package/dist/weekday.vo.d.ts +7 -6
  68. package/dist/weekday.vo.js +20 -13
  69. package/dist/weight.vo.d.ts +12 -0
  70. package/dist/weight.vo.js +37 -27
  71. package/dist/year-iso-id.vo.d.ts +3 -0
  72. package/dist/year-iso-id.vo.js +4 -6
  73. package/dist/year.vo.d.ts +2 -0
  74. package/dist/year.vo.js +4 -2
  75. package/package.json +1 -1
  76. package/readme.md +0 -1
  77. package/src/basename.vo.ts +25 -14
  78. package/src/clock.vo.ts +1 -0
  79. package/src/date-calculator.service.ts +10 -15
  80. package/src/date-range.vo.ts +3 -1
  81. package/src/day-iso-id.vo.ts +9 -10
  82. package/src/day.vo.ts +17 -10
  83. package/src/directory-path-absolute.vo.ts +12 -5
  84. package/src/directory-path-relative.vo.ts +13 -6
  85. package/src/dll.service.ts +45 -43
  86. package/src/extension.vo.ts +14 -12
  87. package/src/file-path-absolute-schema.vo.ts +15 -6
  88. package/src/file-path-relative-schema.vo.ts +13 -6
  89. package/src/file-path.vo.ts +15 -11
  90. package/src/filename-from-string.vo.ts +20 -15
  91. package/src/filename-suffix.vo.ts +8 -4
  92. package/src/filename.vo.ts +14 -15
  93. package/src/height.vo.ts +71 -53
  94. package/src/index.ts +0 -1
  95. package/src/language.vo.ts +1 -2
  96. package/src/mime.vo.ts +10 -7
  97. package/src/month-iso-id.vo.ts +10 -20
  98. package/src/month.vo.ts +19 -13
  99. package/src/object-key.vo.ts +21 -7
  100. package/src/outlier-detector.service.ts +1 -0
  101. package/src/package-version.vo.ts +18 -47
  102. package/src/pagination.service.ts +15 -13
  103. package/src/quarter-iso-id.vo.ts +11 -13
  104. package/src/quarter.vo.ts +3 -0
  105. package/src/rate-limiter.service.ts +7 -7
  106. package/src/reordering.service.ts +52 -38
  107. package/src/revision.vo.ts +17 -8
  108. package/src/rounding.adapter.ts +1 -3
  109. package/src/size.vo.ts +6 -16
  110. package/src/streak-calculator.service.ts +12 -17
  111. package/src/time-zone-offset-value.vo.ts +2 -7
  112. package/src/time.service.ts +43 -45
  113. package/src/timezone.vo.ts +1 -3
  114. package/src/week-iso-id.vo.ts +13 -14
  115. package/src/week.vo.ts +4 -2
  116. package/src/weekday.vo.ts +27 -13
  117. package/src/weight.vo.ts +49 -30
  118. package/src/year-iso-id.vo.ts +6 -9
  119. package/src/year.vo.ts +12 -2
  120. package/dist/stepper.service.d.ts +0 -23
  121. package/dist/stepper.service.js +0 -33
  122. package/src/stepper.service.ts +0 -43
@@ -1,13 +1,31 @@
1
1
  import { z } from "zod/v4";
2
+ export declare const ReorderingPositionError: {
3
+ readonly error: "reordering.position.invalid";
4
+ };
5
+ export declare const ReorderingCannotFindItemError: {
6
+ readonly error: "reordering.item.not_found";
7
+ };
8
+ export declare const ReorderingCannotFindCurrentError: {
9
+ readonly error: "reordering.current_item.not_found";
10
+ };
11
+ export declare const ReorderingCannotFindTargetError: {
12
+ readonly error: "reordering.target_item.not_found";
13
+ };
14
+ export declare const ReorderingCorrelationIdError: {
15
+ readonly error: "reordering.correlation_id.invalid";
16
+ };
17
+ export declare const ReorderingItemIdError: {
18
+ readonly error: "reordering.item_id.invalid";
19
+ };
2
20
  export declare const ReorderingItemPositionValue: z.ZodNumber;
3
21
  export type ReorderingItemPositionValueType = z.infer<typeof ReorderingItemPositionValue>;
4
22
  export declare const ReorderingCorrelationId: z.ZodString;
5
23
  export type ReorderingCorrelationIdType = z.infer<typeof ReorderingCorrelationId>;
6
- export declare const ReorderingItemId: z.ZodUUID;
24
+ export declare const ReorderingItemId: z.ZodString;
7
25
  export type ReorderingItemIdType = z.infer<typeof ReorderingItemId>;
8
26
  export declare const Reordering: z.ZodObject<{
9
27
  correlationId: z.ZodString;
10
- id: z.ZodUUID;
28
+ id: z.ZodString;
11
29
  position: z.ZodNumber;
12
30
  }, z.core.$strip>;
13
31
  export type ReorderingType = z.infer<typeof Reordering>;
@@ -1,8 +1,19 @@
1
1
  import { z } from "zod/v4";
2
2
  import { DoublyLinkedList, Node } from "./dll.service";
3
- export const ReorderingItemPositionValue = z.number().int().min(0);
4
- export const ReorderingCorrelationId = z.string().min(1);
5
- export const ReorderingItemId = z.uuid();
3
+ export const ReorderingPositionError = { error: "reordering.position.invalid" };
4
+ export const ReorderingCannotFindItemError = { error: "reordering.item.not_found" };
5
+ export const ReorderingCannotFindCurrentError = { error: "reordering.current_item.not_found" };
6
+ export const ReorderingCannotFindTargetError = { error: "reordering.target_item.not_found" };
7
+ export const ReorderingCorrelationIdError = { error: "reordering.correlation_id.invalid" };
8
+ export const ReorderingItemIdError = { error: "reordering.item_id.invalid" };
9
+ export const ReorderingItemPositionValue = z
10
+ .number(ReorderingPositionError)
11
+ .int(ReorderingPositionError)
12
+ .min(0, ReorderingPositionError);
13
+ export const ReorderingCorrelationId = z
14
+ .string(ReorderingCorrelationIdError)
15
+ .min(1, ReorderingCorrelationIdError);
16
+ export const ReorderingItemId = z.string(ReorderingItemIdError);
6
17
  export const Reordering = z.object({
7
18
  correlationId: ReorderingCorrelationId,
8
19
  id: ReorderingItemId,
@@ -11,9 +22,9 @@ export const Reordering = z.object({
11
22
  export class ReorderingPosition {
12
23
  value;
13
24
  constructor(value) {
14
- if (!ReorderingItemPositionValue.safeParse(value).success) {
15
- throw new Error("Position is not a positive integer");
16
- }
25
+ const parsed = ReorderingItemPositionValue.safeParse(value);
26
+ if (!parsed.success)
27
+ throw new Error(ReorderingPositionError.error);
17
28
  this.value = value;
18
29
  }
19
30
  eq(another) {
@@ -41,15 +52,16 @@ export class ReorderingTransfer {
41
52
  id;
42
53
  to;
43
54
  constructor(config) {
44
- this.id = config.id;
45
- this.to = new ReorderingPosition(config.to);
55
+ const id = config.id;
56
+ const to = new ReorderingPosition(config.to);
57
+ this.id = id;
58
+ this.to = to;
46
59
  }
47
60
  getDirection(currentPosition) {
48
61
  if (this.to.value === currentPosition.value)
49
62
  return ReorderingTransferDirection.noop;
50
- if (this.to.value > currentPosition.value) {
63
+ if (this.to.value > currentPosition.value)
51
64
  return ReorderingTransferDirection.downwards;
52
- }
53
65
  return ReorderingTransferDirection.upwards;
54
66
  }
55
67
  }
@@ -66,54 +78,62 @@ export class ReorderingCalculator {
66
78
  return reordering;
67
79
  }
68
80
  add(id) {
69
- const position = new ReorderingPosition(ReorderingItemPositionValue.parse(this.dll.getSize()));
81
+ const size = this.dll.getSize();
82
+ const position = new ReorderingPosition(ReorderingItemPositionValue.parse(size));
70
83
  const item = new ReorderingItem(id, position);
71
- this.dll.append(new Node(item));
84
+ const node = new Node(item);
85
+ this.dll.append(node);
72
86
  return item;
73
87
  }
74
88
  delete(id) {
75
- const item = this.dll.find((x) => x.data.eq(id));
76
- if (!item)
77
- throw new Error("Cannot find Item");
78
- this.dll.remove(item);
89
+ const node = this.dll.find((x) => x.data.eq(id));
90
+ if (!node)
91
+ throw new Error(ReorderingCannotFindItemError.error);
92
+ this.dll.remove(node);
79
93
  this.recalculate();
80
94
  }
81
95
  transfer(transfer) {
82
96
  const current = this.dll.find((node) => node.data.eq(transfer.id));
83
- const target = this.dll.find((node) => node.data.position.eq(transfer.to));
84
97
  if (!current)
85
- throw new Error("Cannot find current Item");
98
+ throw new Error(ReorderingCannotFindCurrentError.error);
99
+ const target = this.dll.find((node) => node.data.position.eq(transfer.to));
86
100
  if (!target)
87
- throw new Error("Cannot find target Item");
101
+ throw new Error(ReorderingCannotFindTargetError.error);
88
102
  const direction = transfer.getDirection(current.data.position);
89
103
  if (direction === ReorderingTransferDirection.noop)
90
104
  return this.read();
105
+ // remove first to avoid temporary invalid duplicates of positions
106
+ this.dll.remove(current);
91
107
  if (direction === ReorderingTransferDirection.upwards) {
92
- this.dll.remove(current);
93
108
  this.dll.insertBefore(current, target);
94
- this.recalculate();
95
109
  }
96
- if (direction === ReorderingTransferDirection.downwards) {
97
- this.dll.remove(current);
110
+ else {
98
111
  this.dll.insertAfter(current, target);
99
- this.recalculate();
100
112
  }
113
+ this.recalculate();
101
114
  return this.read();
102
115
  }
103
116
  read() {
104
- const ids = Array.from(this.dll).map((item) => item.data.id);
105
- const items = Array.from(this.dll).map((item) => item.data);
117
+ const ids = Array.from(this.dll).map((node) => node.data.id);
118
+ const items = Array.from(this.dll).map((node) => node.data);
106
119
  return { ids, items };
107
120
  }
108
121
  recalculate() {
109
- this.dll = ReorderingCalculator.fromArray(this.read().ids).dll;
122
+ let index = 0;
123
+ for (const node of this.dll) {
124
+ const id = node.data.id;
125
+ const position = new ReorderingPosition(index);
126
+ node.data = new ReorderingItem(id, position);
127
+ index += 1;
128
+ }
110
129
  }
111
130
  }
112
131
  export class ReorderingIntegrator {
113
132
  static appendPosition(reordering) {
114
133
  return function (item) {
115
- const position = ReorderingItemPositionValue.parse(reordering.find((x) => x.id === item.id)?.position ?? 0);
116
- return { ...item, position };
134
+ const found = reordering.find((x) => x.id === item.id);
135
+ const positionValue = ReorderingItemPositionValue.parse(found?.position ?? 0);
136
+ return { ...item, position: positionValue };
117
137
  };
118
138
  }
119
139
  static sortByPosition() {
@@ -1,12 +1,17 @@
1
1
  import { z } from "zod/v4";
2
2
  import type { ETag, WeakETag } from "./etags.vo";
3
- export declare const RevisionValue: z.ZodNumber;
3
+ export declare const RevisionValueError: {
4
+ readonly error: "invalid.revision.value";
5
+ };
6
+ export declare const RevisionValue: z.core.$ZodBranded<z.ZodNumber, "RevisionValue">;
4
7
  export type RevisionValueType = z.infer<typeof RevisionValue>;
8
+ export declare const RevisionInvalidErrorMessage: "revision.invalid";
9
+ export declare const RevisionMismatchErrorMessage: "revision.mismatch";
5
10
  export declare class Revision {
11
+ static readonly INITIAL: RevisionValueType;
6
12
  readonly value: RevisionValueType;
7
- static initial: RevisionValueType;
8
13
  constructor(value: unknown);
9
- matches(another: RevisionValueType): boolean;
14
+ equals(another: RevisionValueType): boolean;
10
15
  validate(another: RevisionValueType): void;
11
16
  next(): Revision;
12
17
  static fromETag(etag: ETag | null): Revision;
@@ -1,19 +1,26 @@
1
1
  import { z } from "zod/v4";
2
- export const RevisionValue = z.number().int().min(0);
2
+ export const RevisionValueError = { error: "invalid.revision.value" };
3
+ export const RevisionValue = z
4
+ .number(RevisionValueError)
5
+ .int(RevisionValueError)
6
+ .min(0, RevisionValueError)
7
+ .brand("RevisionValue");
8
+ export const RevisionInvalidErrorMessage = "revision.invalid";
9
+ export const RevisionMismatchErrorMessage = "revision.mismatch";
3
10
  export class Revision {
11
+ static INITIAL = RevisionValue.parse(0);
4
12
  value;
5
- static initial = 0;
6
13
  constructor(value) {
7
14
  const result = RevisionValue.safeParse(value);
8
15
  if (!result.success)
9
16
  throw new InvalidRevisionError();
10
17
  this.value = result.data;
11
18
  }
12
- matches(another) {
19
+ equals(another) {
13
20
  return this.value === another;
14
21
  }
15
22
  validate(another) {
16
- if (!this.matches(another))
23
+ if (!this.equals(another))
17
24
  throw new RevisionMismatchError();
18
25
  }
19
26
  next() {
@@ -32,13 +39,13 @@ export class Revision {
32
39
  }
33
40
  export class RevisionMismatchError extends Error {
34
41
  constructor() {
35
- super();
42
+ super(RevisionMismatchErrorMessage);
36
43
  Object.setPrototypeOf(this, RevisionMismatchError.prototype);
37
44
  }
38
45
  }
39
46
  export class InvalidRevisionError extends Error {
40
47
  constructor() {
41
- super();
48
+ super(RevisionInvalidErrorMessage);
42
49
  Object.setPrototypeOf(this, InvalidRevisionError.prototype);
43
50
  }
44
51
  }
@@ -18,9 +18,8 @@ export class RoundToDecimal {
18
18
  decimals;
19
19
  constructor(decimals) {
20
20
  this.decimals = decimals;
21
- if (!Number.isInteger(decimals) || decimals < 0 || decimals > 100) {
21
+ if (!Number.isInteger(decimals) || decimals < 0 || decimals > 100)
22
22
  throw new Error(RoundingDecimalsError);
23
- }
24
23
  }
25
24
  round(value) {
26
25
  return Number.parseFloat(value.toFixed(this.decimals));
package/dist/size.vo.d.ts CHANGED
@@ -18,6 +18,7 @@ export declare class Size {
18
18
  private static readonly KB_MULTIPLIER;
19
19
  private static readonly MB_MULTIPLIER;
20
20
  private static readonly GB_MULTIPLIER;
21
+ private static readonly ROUNDER;
21
22
  constructor(config: SizeConfigType);
22
23
  static fromBytes(candidate: number): Size;
23
24
  static fromKb(candidate: number): Size;
package/dist/size.vo.js CHANGED
@@ -15,6 +15,7 @@ export class Size {
15
15
  static KB_MULTIPLIER = 1024;
16
16
  static MB_MULTIPLIER = 1024 * Size.KB_MULTIPLIER;
17
17
  static GB_MULTIPLIER = 1024 * Size.MB_MULTIPLIER;
18
+ static ROUNDER = new RoundToDecimal(2);
18
19
  constructor(config) {
19
20
  this.unit = config.unit;
20
21
  this.value = SizeValue.parse(config.value);
@@ -46,19 +47,15 @@ export class Size {
46
47
  return this.bytes > another.toBytes();
47
48
  }
48
49
  format(unit) {
49
- const rounding = new RoundToDecimal(2);
50
50
  switch (unit) {
51
51
  case SizeUnit.kB: {
52
- const kbs = rounding.round(this.bytes / Size.KB_MULTIPLIER);
53
- return `${kbs} ${SizeUnit.kB}`;
52
+ return `${Size.ROUNDER.round(this.bytes / Size.KB_MULTIPLIER)} ${SizeUnit.kB}`;
54
53
  }
55
54
  case SizeUnit.MB: {
56
- const mbs = rounding.round(this.bytes / Size.MB_MULTIPLIER);
57
- return `${mbs} ${SizeUnit.MB}`;
55
+ return `${Size.ROUNDER.round(this.bytes / Size.MB_MULTIPLIER)} ${SizeUnit.MB}`;
58
56
  }
59
57
  case SizeUnit.GB: {
60
- const gbs = rounding.round(this.bytes / Size.GB_MULTIPLIER);
61
- return `${gbs} ${SizeUnit.GB}`;
58
+ return `${Size.ROUNDER.round(this.bytes / Size.GB_MULTIPLIER)} ${SizeUnit.GB}`;
62
59
  }
63
60
  default: {
64
61
  // SizeUnit.b
@@ -1,4 +1,3 @@
1
- import { format } from "date-fns";
2
1
  type DateType = string;
3
2
  export type StreakType = {
4
3
  cutoff: DateType;
@@ -7,8 +6,8 @@ export type StreakType = {
7
6
  };
8
7
  export declare class StreakCalculator {
9
8
  private readonly cutoff;
10
- constructor();
11
- calculate(_dates: DateType[]): StreakType;
12
- static format(date: Parameters<typeof format>[0]): DateType;
9
+ constructor(now?: Date);
10
+ calculate(inputDates: DateType[]): StreakType;
11
+ static format(date: Date | number): DateType;
13
12
  }
14
13
  export {};
@@ -1,24 +1,18 @@
1
- import { format, isAfter, isEqual, subDays } from "date-fns";
1
+ import { format, subDays } from "date-fns";
2
2
  export class StreakCalculator {
3
3
  cutoff;
4
- constructor() {
5
- const today = new Date();
6
- this.cutoff = StreakCalculator.format(today);
4
+ constructor(now = new Date()) {
5
+ this.cutoff = StreakCalculator.format(now);
7
6
  }
8
- calculate(_dates) {
9
- const dates = Array.from(new Set(_dates));
7
+ calculate(inputDates) {
8
+ const dates = Array.from(new Set(inputDates));
9
+ const datesSet = new Set(dates);
10
10
  let streak = 0;
11
- let streakTail = this.cutoff;
12
- for (let i = 0; i < dates.length; i++) {
13
- const date = dates[i];
14
- if (isAfter(date, streakTail))
15
- continue;
16
- if (isEqual(streakTail, date) || isAfter(date, streakTail)) {
17
- streakTail = StreakCalculator.format(subDays(date, 1));
18
- streak++;
19
- }
20
- else
21
- break;
11
+ let cursor = this.cutoff;
12
+ while (datesSet.has(cursor)) {
13
+ streak++;
14
+ const cursorDate = new Date(`${cursor}T00:00:00Z`);
15
+ cursor = StreakCalculator.format(subDays(cursorDate, 1));
22
16
  }
23
17
  return { cutoff: this.cutoff, dates, streak };
24
18
  }
@@ -1,3 +1,3 @@
1
1
  import { z } from "zod/v4";
2
- export declare const TimeZoneOffsetValue: z.core.$ZodBranded<z.ZodPipe<z.ZodPipe<z.ZodUnion<[z.ZodString, z.ZodUndefined]>, z.ZodTransform<number, string | undefined>>, z.ZodTransform<number, number>>, "TimeZoneOffsetValue">;
2
+ export declare const TimeZoneOffsetValue: z.core.$ZodBranded<z.ZodCatch<z.ZodCoercedNumber<unknown>>, "TimeZoneOffsetValue">;
3
3
  export type TimeZoneOffsetValueType = z.infer<typeof TimeZoneOffsetValue>;
@@ -1,8 +1,2 @@
1
1
  import { z } from "zod/v4";
2
- export const TimeZoneOffsetValue = z
3
- .string()
4
- .trim()
5
- .or(z.undefined())
6
- .transform((value) => Number(value))
7
- .transform((value) => (Number.isNaN(value) ? 0 : value))
8
- .brand("TimeZoneOffsetValue");
2
+ export const TimeZoneOffsetValue = z.coerce.number().catch(0).brand("TimeZoneOffsetValue");
@@ -7,16 +7,21 @@ interface TimeResultInterface {
7
7
  readonly ms: TimestampType;
8
8
  isAfter(another: TimeResultInterface): boolean;
9
9
  isBefore(another: TimeResultInterface): boolean;
10
+ add(another: TimeResultInterface): TimeResultInterface;
11
+ subtract(another: TimeResultInterface): TimeResultInterface;
10
12
  }
11
13
  export declare class TimeResult implements TimeResultInterface {
12
- readonly days: number;
13
- readonly hours: number;
14
- readonly minutes: number;
15
- readonly seconds: number;
16
- readonly ms: TimestampType;
17
- constructor(days: number, hours: number, minutes: number, seconds: number, ms: TimestampType);
14
+ private readonly valueMs;
15
+ constructor(ms: TimestampType);
16
+ get days(): number;
17
+ get hours(): number;
18
+ get minutes(): number;
19
+ get seconds(): number;
20
+ get ms(): TimestampType;
18
21
  isAfter(another: TimeResultInterface): boolean;
19
22
  isBefore(another: TimeResultInterface): boolean;
23
+ add(another: TimeResultInterface): TimeResultInterface;
24
+ subtract(another: TimeResultInterface): TimeResultInterface;
20
25
  }
21
26
  export declare class Time {
22
27
  static Days(value: number): TimeResultInterface;
@@ -1,40 +1,53 @@
1
1
  import { RoundToDecimal } from "./rounding.adapter";
2
2
  const rounding = new RoundToDecimal(2);
3
3
  export class TimeResult {
4
- days;
5
- hours;
6
- minutes;
7
- seconds;
8
- ms;
9
- constructor(days, hours, minutes, seconds, ms) {
10
- this.days = days;
11
- this.hours = hours;
12
- this.minutes = minutes;
13
- this.seconds = seconds;
14
- this.ms = ms;
4
+ valueMs;
5
+ constructor(ms) {
6
+ this.valueMs = ms;
7
+ }
8
+ get days() {
9
+ return rounding.round(this.valueMs / 86_400_000);
10
+ }
11
+ get hours() {
12
+ return rounding.round(this.valueMs / 3_600_000);
13
+ }
14
+ get minutes() {
15
+ return rounding.round(this.valueMs / 60_000);
16
+ }
17
+ get seconds() {
18
+ return rounding.round(this.valueMs / 1_000);
19
+ }
20
+ get ms() {
21
+ return this.valueMs;
15
22
  }
16
23
  isAfter(another) {
17
- return this.ms > another.ms;
24
+ return this.valueMs > another.ms;
18
25
  }
19
26
  isBefore(another) {
20
- return this.ms < another.ms;
27
+ return this.valueMs < another.ms;
28
+ }
29
+ add(another) {
30
+ return new TimeResult((this.valueMs + another.ms));
31
+ }
32
+ subtract(another) {
33
+ return new TimeResult((this.valueMs - another.ms));
21
34
  }
22
35
  }
23
36
  export class Time {
24
37
  static Days(value) {
25
- return new TimeResult(value, value * 24, value * 24 * 60, value * 24 * 60 * 60, (value * 24 * 60 * 60 * 1000));
38
+ return new TimeResult((value * 86_400_000));
26
39
  }
27
40
  static Hours(value) {
28
- return new TimeResult(rounding.round(value / 24), value, value * 60, value * 60 * 60, (value * 60 * 60 * 1000));
41
+ return new TimeResult((value * 3_600_000));
29
42
  }
30
43
  static Minutes(value) {
31
- return new TimeResult(rounding.round(value / 60 / 24), rounding.round(value / 60), value, value * 60, (value * 60 * 1000));
44
+ return new TimeResult((value * 60_000));
32
45
  }
33
46
  static Seconds(value) {
34
- return new TimeResult(rounding.round(value / 60 / 60 / 24), rounding.round(value / 60 / 60), rounding.round(value / 60), value, (value * 1000));
47
+ return new TimeResult((value * 1_000));
35
48
  }
36
49
  static Ms(value) {
37
- return new TimeResult(rounding.round(value / 1000 / 60 / 60 / 24), rounding.round(value / 1000 / 60 / 60), rounding.round(value / 1000 / 60), rounding.round(value / 1000), value);
50
+ return new TimeResult(value);
38
51
  }
39
52
  static Now(now) {
40
53
  return {
@@ -5,9 +5,7 @@ export const Timezone = z
5
5
  .min(1, TimezoneError)
6
6
  .refine((value) => {
7
7
  try {
8
- const date = new Date();
9
- const formatter = new Intl.DateTimeFormat("en-US", { timeZone: value });
10
- formatter.format(date);
8
+ new Intl.DateTimeFormat("en-US", { timeZone: value }).format(new Date());
11
9
  return true;
12
10
  }
13
11
  catch (_error) {
@@ -1 +1 @@
1
- {"root":["../src/age.vo.ts","../src/api-key.vo.ts","../src/basename.vo.ts","../src/clock-format.service.ts","../src/clock.vo.ts","../src/date-calculator.service.ts","../src/date-formatter.service.ts","../src/date-range.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/height.vo.ts","../src/hour-format.service.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.adapter.ts","../src/rounding.port.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/weight.vo.ts","../src/year-iso-id.vo.ts","../src/year.vo.ts","../src/z-score.service.ts"],"version":"5.9.3"}
1
+ {"root":["../src/age.vo.ts","../src/api-key.vo.ts","../src/basename.vo.ts","../src/clock-format.service.ts","../src/clock.vo.ts","../src/date-calculator.service.ts","../src/date-formatter.service.ts","../src/date-range.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/height.vo.ts","../src/hour-format.service.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.adapter.ts","../src/rounding.port.ts","../src/simple-linear-regression.service.ts","../src/size.vo.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/weight.vo.ts","../src/year-iso-id.vo.ts","../src/year.vo.ts","../src/z-score.service.ts"],"version":"5.9.3"}
@@ -1,3 +1,6 @@
1
1
  import { z } from "zod/v4";
2
+ export declare const WeekIsoIdError: {
3
+ readonly error: "week-iso-id.invalid";
4
+ };
2
5
  export declare const WeekIsoId: z.ZodString;
3
6
  export type WeekIsoIdType = z.infer<typeof WeekIsoId>;
@@ -1,15 +1,15 @@
1
1
  import { getISOWeeksInYear } from "date-fns";
2
2
  import { z } from "zod/v4";
3
+ export const WeekIsoIdError = { error: "week-iso-id.invalid" };
3
4
  export const WeekIsoId = z
4
- .string()
5
- .regex(/^\d{4}-W\d{2}$/)
5
+ .string(WeekIsoIdError)
6
+ .regex(/^\d{4}-W\d{2}$/, WeekIsoIdError)
6
7
  .refine((value) => {
7
8
  const [yearPart, weekPart] = value.split("-W");
8
9
  const year = Number(yearPart);
9
10
  const week = Number(weekPart);
10
11
  if (!(Number.isInteger(year) && Number.isInteger(week)) || week < 1)
11
12
  return false;
12
- // Does this ISO week-year actually have that many weeks?
13
13
  const weeksInYear = getISOWeeksInYear(new Date(Date.UTC(year, 0, 4)));
14
14
  return week <= weeksInYear;
15
- }, { message: "week-iso-id.invalid" });
15
+ }, WeekIsoIdError);
package/dist/week.vo.js CHANGED
@@ -6,7 +6,7 @@ export class Week extends DateRange {
6
6
  toIsoId() {
7
7
  const year = getISOWeekYear(this.getStart());
8
8
  const week = getISOWeek(this.getStart()).toString().padStart(2, "0");
9
- return `${year}-W${week}`;
9
+ return WeekIsoId.parse(`${year}-W${week}`);
10
10
  }
11
11
  previous() {
12
12
  const shifted = addWeeks(new Date(this.getStart()), -1).getTime();
@@ -6,6 +6,7 @@ export declare enum WeekdayFormatterEnum {
6
6
  ISO_NUMBER = "ISO_NUMBER",// Monday=1 ... Sunday=7
7
7
  ZERO_BASED_NUMBER = "ZERO_BASED_NUMBER"
8
8
  }
9
+ export declare const WeekdayValueError: "invalid.weekday";
9
10
  export declare const WeekdayFormatters: Record<WeekdayFormatterEnum, WeekdayFormatter>;
10
11
  export declare class Weekday {
11
12
  private readonly value;
@@ -19,11 +20,11 @@ export declare class Weekday {
19
20
  static readonly SATURDAY: Weekday;
20
21
  constructor(candidate: number, formatter?: WeekdayFormatter);
21
22
  static fromUtcTimestamp(timestamp: TimestampType, formatter?: WeekdayFormatter): Weekday;
22
- get(formatter?: WeekdayFormatter): {
23
- raw: number;
24
- formatted: string;
25
- };
23
+ get(): number;
24
+ format(formatter?: WeekdayFormatter): string;
25
+ toString(): string;
26
26
  equals(another: Weekday): boolean;
27
+ /** ISO-8601 weekday number: Monday=1 ... Sunday=7 */
27
28
  toIsoNumber(): number;
28
29
  isMonday(): boolean;
29
30
  isTuesday(): boolean;
@@ -32,6 +33,6 @@ export declare class Weekday {
32
33
  isFriday(): boolean;
33
34
  isSaturday(): boolean;
34
35
  isSunday(): boolean;
35
- static list(formatter?: WeekdayFormatter): Weekday[];
36
- static listMondayFirst(formatter?: WeekdayFormatter): Weekday[];
36
+ static list(formatter?: WeekdayFormatter): readonly Weekday[];
37
+ static listMondayFirst(formatter?: WeekdayFormatter): readonly Weekday[];
37
38
  }
@@ -5,6 +5,7 @@ export var WeekdayFormatterEnum;
5
5
  WeekdayFormatterEnum["ISO_NUMBER"] = "ISO_NUMBER";
6
6
  WeekdayFormatterEnum["ZERO_BASED_NUMBER"] = "ZERO_BASED_NUMBER";
7
7
  })(WeekdayFormatterEnum || (WeekdayFormatterEnum = {}));
8
+ export const WeekdayValueError = "invalid.weekday";
8
9
  const FULL_NAMES = [
9
10
  "Sunday",
10
11
  "Monday",
@@ -22,7 +23,9 @@ export const WeekdayFormatters = {
22
23
  ZERO_BASED_NUMBER: (value) => value.toString(), // JS getUTCDay(): Sun=0..Sat=6
23
24
  };
24
25
  export class Weekday {
26
+ // 0..6 (Sun..Sat)
25
27
  value;
28
+ // default formatter used by toString()/format() when no runtime formatter given
26
29
  formatter;
27
30
  static SUNDAY = new Weekday(0);
28
31
  static MONDAY = new Weekday(1);
@@ -32,26 +35,29 @@ export class Weekday {
32
35
  static FRIDAY = new Weekday(5);
33
36
  static SATURDAY = new Weekday(6);
34
37
  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");
38
+ if (!Number.isInteger(candidate) || candidate < 0 || candidate > 6)
39
+ throw new Error(WeekdayValueError);
41
40
  this.value = candidate;
42
41
  this.formatter = formatter ?? WeekdayFormatters.FULL;
43
42
  }
44
43
  static fromUtcTimestamp(timestamp, formatter) {
45
- const day = new Date(timestamp).getUTCDay(); // 0..6
46
- return new Weekday(day, formatter);
44
+ const dayZeroBased = new Date(timestamp).getUTCDay(); // 0..6
45
+ return new Weekday(dayZeroBased, formatter);
47
46
  }
48
- get(formatter) {
49
- const format = formatter ?? this.formatter;
50
- return { raw: this.value, formatted: format(this.value) };
47
+ get() {
48
+ return this.value;
49
+ }
50
+ format(formatter) {
51
+ const chosen = formatter ?? this.formatter;
52
+ return chosen(this.value);
53
+ }
54
+ toString() {
55
+ return this.format(WeekdayFormatters.FULL);
51
56
  }
52
57
  equals(another) {
53
- return this.value === another.get().raw;
58
+ return this.value === another.value;
54
59
  }
60
+ /** ISO-8601 weekday number: Monday=1 ... Sunday=7 */
55
61
  toIsoNumber() {
56
62
  return this.value === 0 ? 7 : this.value;
57
63
  }
@@ -77,7 +83,8 @@ export class Weekday {
77
83
  return this.value === 0;
78
84
  }
79
85
  static list(formatter) {
80
- return Array.from({ length: 7 }).map((_, index) => new Weekday(index, formatter));
86
+ const chosen = formatter ?? undefined;
87
+ return Array.from({ length: 7 }, (_, index) => new Weekday(index, chosen));
81
88
  }
82
89
  static listMondayFirst(formatter) {
83
90
  const days = Weekday.list(formatter);