@bgord/tools 0.17.2 → 1.0.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.
- package/dist/age-years.vo.d.ts +11 -0
- package/dist/age-years.vo.js +9 -0
- package/dist/age.vo.d.ts +11 -16
- package/dist/age.vo.js +20 -31
- package/dist/api-key.vo.d.ts +3 -1
- package/dist/api-key.vo.js +10 -5
- package/dist/basename.vo.d.ts +9 -9
- package/dist/basename.vo.js +22 -22
- package/dist/clock.vo.d.ts +10 -4
- package/dist/clock.vo.js +12 -14
- package/dist/date-calculator.service.d.ts +2 -1
- package/dist/date-formatter.service.d.ts +3 -4
- package/dist/date-range.vo.d.ts +7 -1
- package/dist/date-range.vo.js +5 -2
- package/dist/day-iso-id.vo.d.ts +5 -2
- package/dist/day-iso-id.vo.js +11 -7
- package/dist/day.vo.d.ts +4 -3
- package/dist/day.vo.js +18 -16
- package/dist/directory-path-absolute.vo.d.ts +10 -6
- package/dist/directory-path-absolute.vo.js +19 -17
- package/dist/directory-path-relative.vo.d.ts +10 -7
- package/dist/directory-path-relative.vo.js +18 -17
- package/dist/division-factor.vo.d.ts +7 -0
- package/dist/division-factor.vo.js +9 -0
- package/dist/duration-ms.vo.d.ts +6 -0
- package/dist/duration-ms.vo.js +3 -0
- package/dist/duration.service.d.ts +2 -14
- package/dist/duration.service.js +16 -35
- package/dist/email-mask.service.d.ts +1 -6
- package/dist/email-mask.service.js +6 -8
- package/dist/etags.vo.d.ts +4 -3
- package/dist/etags.vo.js +3 -3
- package/dist/extension.vo.d.ts +6 -4
- package/dist/extension.vo.js +15 -10
- package/dist/feature-flag-value.vo.d.ts +10 -0
- package/dist/feature-flag-value.vo.js +8 -0
- package/dist/feature-flag.vo.d.ts +1 -7
- package/dist/feature-flag.vo.js +1 -7
- package/dist/file-path-absolute-schema.vo.d.ts +10 -7
- package/dist/file-path-absolute-schema.vo.js +17 -17
- package/dist/file-path-relative-schema.vo.d.ts +10 -7
- package/dist/file-path-relative-schema.vo.js +14 -12
- package/dist/file-path.vo.d.ts +4 -4
- package/dist/file-path.vo.js +8 -8
- package/dist/filename-from-string.vo.d.ts +4 -2
- package/dist/filename-from-string.vo.js +10 -8
- package/dist/filename-suffix.vo.d.ts +7 -3
- package/dist/filename-suffix.vo.js +13 -7
- package/dist/filename.vo.d.ts +2 -0
- package/dist/filename.vo.js +8 -2
- package/dist/height-milimiters.vo.d.ts +6 -0
- package/dist/height-milimiters.vo.js +10 -0
- package/dist/height.vo.d.ts +3 -20
- package/dist/height.vo.js +11 -62
- package/dist/hour-format.service.js +1 -1
- package/dist/hour-schema.vo.d.ts +7 -0
- package/dist/hour-schema.vo.js +8 -0
- package/dist/hour.vo.d.ts +4 -3
- package/dist/hour.vo.js +8 -8
- package/dist/iban-mask.service.d.ts +1 -3
- package/dist/iban-mask.service.js +2 -2
- package/dist/iban-schema.vo.d.ts +7 -0
- package/dist/iban-schema.vo.js +10 -0
- package/dist/iban.vo.d.ts +4 -10
- package/dist/iban.vo.js +6 -13
- package/dist/image.vo.d.ts +6 -4
- package/dist/image.vo.js +13 -12
- package/dist/index.d.ts +24 -2
- package/dist/index.js +24 -2
- package/dist/language.vo.d.ts +2 -1
- package/dist/language.vo.js +6 -4
- package/dist/linear-regression.service.d.ts +27 -0
- package/dist/{simple-linear-regression.service.js → linear-regression.service.js} +17 -15
- package/dist/mean.service.d.ts +3 -1
- package/dist/mean.service.js +3 -4
- package/dist/mime-types.vo.d.ts +1 -2
- package/dist/mime-value.vo.d.ts +9 -0
- package/dist/mime-value.vo.js +9 -0
- package/dist/mime.vo.d.ts +11 -17
- package/dist/mime.vo.js +10 -27
- package/dist/min-max-scaler.service.d.ts +7 -5
- package/dist/min-max-scaler.service.js +12 -10
- package/dist/minute-schema.vo.d.ts +7 -0
- package/dist/minute-schema.vo.js +8 -0
- package/dist/minute.vo.d.ts +4 -3
- package/dist/minute.vo.js +8 -8
- package/dist/money-amount.vo.d.ts +7 -0
- package/dist/money-amount.vo.js +7 -0
- package/dist/money.vo.d.ts +9 -18
- package/dist/money.vo.js +14 -27
- package/dist/month-iso-id.vo.d.ts +4 -2
- package/dist/month-iso-id.vo.js +13 -7
- package/dist/month.vo.d.ts +4 -3
- package/dist/month.vo.js +21 -21
- package/dist/multiplication-factor.vo.d.ts +7 -0
- package/dist/multiplication-factor.vo.js +9 -0
- package/dist/object-key.vo.d.ts +9 -6
- package/dist/object-key.vo.js +20 -19
- package/dist/outlier-detector.service.d.ts +3 -1
- package/dist/outlier-detector.service.js +2 -2
- package/dist/package-version-schema.vo.d.ts +11 -0
- package/dist/package-version-schema.vo.js +15 -0
- package/dist/package-version.vo.d.ts +11 -20
- package/dist/package-version.vo.js +11 -20
- package/dist/pagination-page.vo.d.ts +6 -0
- package/dist/pagination-page.vo.js +7 -0
- package/dist/pagination-skip.vo.d.ts +7 -0
- package/dist/pagination-skip.vo.js +9 -0
- package/dist/pagination-take.vo.d.ts +7 -0
- package/dist/pagination-take.vo.js +9 -0
- package/dist/pagination.service.d.ts +3 -8
- package/dist/pagination.service.js +5 -12
- package/dist/percentage.service.d.ts +3 -1
- package/dist/percentage.service.js +2 -2
- package/dist/population-standard-deviation.service.d.ts +3 -1
- package/dist/population-standard-deviation.service.js +5 -4
- package/dist/quarter-iso-id.vo.d.ts +3 -2
- package/dist/quarter-iso-id.vo.js +7 -9
- package/dist/quarter.vo.d.ts +2 -1
- package/dist/quarter.vo.js +10 -7
- package/dist/random.service.d.ts +3 -4
- package/dist/random.service.js +5 -11
- package/dist/rate-limiter.service.d.ts +2 -2
- package/dist/rate-limiter.service.js +8 -8
- package/dist/reordering-item-position-value.vo.d.ts +6 -0
- package/dist/reordering-item-position-value.vo.js +6 -0
- package/dist/reordering.service.d.ts +7 -23
- package/dist/reordering.service.js +15 -24
- package/dist/revision-value.vo.d.ts +7 -0
- package/dist/revision-value.vo.js +6 -0
- package/dist/revision.vo.d.ts +6 -13
- package/dist/revision.vo.js +10 -22
- package/dist/rounding.adapter.d.ts +7 -2
- package/dist/rounding.adapter.js +13 -5
- package/dist/size-bytes.vo.d.ts +6 -0
- package/dist/size-bytes.vo.js +7 -0
- package/dist/size.vo.d.ts +15 -15
- package/dist/size.vo.js +41 -51
- package/dist/stopwatch.service.d.ts +3 -1
- package/dist/stopwatch.service.js +2 -2
- package/dist/sum.service.js +8 -8
- package/dist/thousands-separator.service.js +4 -1
- package/dist/time.service.d.ts +8 -0
- package/dist/time.service.js +13 -0
- package/dist/timestamp.vo.d.ts +1 -1
- package/dist/timestamp.vo.js +4 -5
- package/dist/timezone.vo.d.ts +4 -1
- package/dist/timezone.vo.js +12 -6
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/week-iso-id.vo.d.ts +4 -2
- package/dist/week-iso-id.vo.js +15 -9
- package/dist/week.vo.d.ts +4 -3
- package/dist/week.vo.js +21 -22
- package/dist/weekday.vo.d.ts +1 -1
- package/dist/weekday.vo.js +6 -8
- package/dist/weight-grams.vo.d.ts +7 -0
- package/dist/weight-grams.vo.js +7 -0
- package/dist/weight.vo.d.ts +12 -35
- package/dist/weight.vo.js +23 -72
- package/dist/year-iso-id.vo.d.ts +3 -2
- package/dist/year-iso-id.vo.js +6 -4
- package/dist/year.vo.d.ts +5 -6
- package/dist/year.vo.js +21 -26
- package/dist/z-score.service.d.ts +3 -1
- package/dist/z-score.service.js +2 -2
- package/package.json +4 -4
- package/readme.md +21 -2
- package/src/age-years.vo.ts +14 -0
- package/src/age.vo.ts +22 -35
- package/src/api-key.vo.ts +11 -5
- package/src/basename.vo.ts +24 -22
- package/src/clock.vo.ts +16 -17
- package/src/date-calculator.service.ts +2 -1
- package/src/date-formatter.service.ts +4 -5
- package/src/date-range.vo.ts +6 -2
- package/src/day-iso-id.vo.ts +12 -8
- package/src/day.vo.ts +27 -24
- package/src/directory-path-absolute.vo.ts +23 -18
- package/src/directory-path-relative.vo.ts +21 -18
- package/src/division-factor.vo.ts +13 -0
- package/src/duration-ms.vo.ts +7 -0
- package/src/duration.service.ts +16 -40
- package/src/email-mask.service.ts +7 -15
- package/src/etags.vo.ts +4 -5
- package/src/extension.vo.ts +17 -10
- package/src/feature-flag-value.vo.ts +12 -0
- package/src/feature-flag.vo.ts +1 -9
- package/src/file-path-absolute-schema.vo.ts +18 -17
- package/src/file-path-relative-schema.vo.ts +15 -12
- package/src/file-path.vo.ts +8 -8
- package/src/filename-from-string.vo.ts +12 -9
- package/src/filename-suffix.vo.ts +14 -7
- package/src/filename.vo.ts +11 -2
- package/src/height-milimiters.vo.ts +12 -0
- package/src/height.vo.ts +12 -83
- package/src/hour-format.service.ts +2 -1
- package/src/hour-schema.vo.ts +12 -0
- package/src/hour.vo.ts +12 -12
- package/src/iban-mask.service.ts +3 -5
- package/src/iban-schema.vo.ts +15 -0
- package/src/iban.vo.ts +9 -22
- package/src/image.vo.ts +14 -12
- package/src/index.ts +24 -2
- package/src/language.vo.ts +7 -4
- package/src/linear-regression.service.ts +71 -0
- package/src/mean.service.ts +3 -5
- package/src/mime-types.vo.ts +1 -3
- package/src/mime-value.vo.ts +12 -0
- package/src/mime.vo.ts +12 -33
- package/src/min-max-scaler.service.ts +13 -11
- package/src/minute-schema.vo.ts +12 -0
- package/src/minute.vo.ts +12 -12
- package/src/money-amount.vo.ts +11 -0
- package/src/money.vo.ts +20 -38
- package/src/month-iso-id.vo.ts +14 -7
- package/src/month.vo.ts +25 -24
- package/src/multiplication-factor.vo.ts +13 -0
- package/src/object-key.vo.ts +25 -21
- package/src/outlier-detector.service.ts +2 -2
- package/src/package-version-schema.vo.ts +21 -0
- package/src/package-version.vo.ts +17 -33
- package/src/pagination-page.vo.ts +11 -0
- package/src/pagination-skip.vo.ts +13 -0
- package/src/pagination-take.vo.ts +13 -0
- package/src/pagination.service.ts +5 -22
- package/src/percentage.service.ts +2 -2
- package/src/population-standard-deviation.service.ts +5 -4
- package/src/quarter-iso-id.vo.ts +7 -10
- package/src/quarter.vo.ts +14 -9
- package/src/random.service.ts +6 -9
- package/src/rate-limiter.service.ts +9 -8
- package/src/reordering-item-position-value.vo.ts +10 -0
- package/src/reordering.service.ts +19 -28
- package/src/revision-value.vo.ts +10 -0
- package/src/revision.vo.ts +10 -25
- package/src/rounding.adapter.ts +16 -3
- package/src/size-bytes.vo.ts +11 -0
- package/src/size.vo.ts +43 -54
- package/src/stopwatch.service.ts +3 -3
- package/src/sum.service.ts +8 -8
- package/src/thousands-separator.service.ts +4 -1
- package/src/time.service.ts +15 -0
- package/src/timestamp.vo.ts +4 -5
- package/src/timezone.vo.ts +12 -6
- package/src/week-iso-id.vo.ts +16 -12
- package/src/week.vo.ts +26 -28
- package/src/weekday.vo.ts +6 -9
- package/src/weight-grams.vo.ts +11 -0
- package/src/weight.vo.ts +28 -85
- package/src/year-iso-id.vo.ts +7 -4
- package/src/year.vo.ts +27 -33
- package/src/z-score.service.ts +2 -2
- package/dist/simple-linear-regression.service.d.ts +0 -25
- package/dist/streak-calculator.service.d.ts +0 -13
- package/dist/streak-calculator.service.js +0 -22
- package/src/simple-linear-regression.service.ts +0 -69
- package/src/streak-calculator.service.ts +0 -32
package/src/revision.vo.ts
CHANGED
|
@@ -1,14 +1,7 @@
|
|
|
1
|
-
import { z } from "zod/v4";
|
|
2
1
|
import type { ETag, WeakETag } from "./etags.vo";
|
|
2
|
+
import { RevisionValue, type RevisionValueType } from "./revision-value.vo";
|
|
3
3
|
|
|
4
|
-
export const
|
|
5
|
-
|
|
6
|
-
export const RevisionValue = z.number(RevisionValueError).int(RevisionValueError).min(0, RevisionValueError);
|
|
7
|
-
|
|
8
|
-
export type RevisionValueType = z.infer<typeof RevisionValue>;
|
|
9
|
-
|
|
10
|
-
export const RevisionInvalidErrorMessage = "revision.invalid" as const;
|
|
11
|
-
export const RevisionMismatchErrorMessage = "revision.mismatch" as const;
|
|
4
|
+
export const RevisionError = { Missing: "revision.missing", Mismatch: "revision.mismatch" } as const;
|
|
12
5
|
|
|
13
6
|
export class Revision {
|
|
14
7
|
static readonly INITIAL: RevisionValueType = RevisionValue.parse(0);
|
|
@@ -16,9 +9,7 @@ export class Revision {
|
|
|
16
9
|
readonly value: RevisionValueType;
|
|
17
10
|
|
|
18
11
|
constructor(value: unknown) {
|
|
19
|
-
|
|
20
|
-
if (!result.success) throw new InvalidRevisionError();
|
|
21
|
-
this.value = result.data;
|
|
12
|
+
this.value = RevisionValue.parse(value);
|
|
22
13
|
}
|
|
23
14
|
|
|
24
15
|
equals(another: RevisionValueType): boolean {
|
|
@@ -26,7 +17,7 @@ export class Revision {
|
|
|
26
17
|
}
|
|
27
18
|
|
|
28
19
|
validate(another: RevisionValueType): void {
|
|
29
|
-
if (!this.equals(another)) throw new
|
|
20
|
+
if (!this.equals(another)) throw new Error(RevisionError.Mismatch);
|
|
30
21
|
}
|
|
31
22
|
|
|
32
23
|
next(): Revision {
|
|
@@ -34,26 +25,20 @@ export class Revision {
|
|
|
34
25
|
}
|
|
35
26
|
|
|
36
27
|
static fromETag(etag: ETag | null): Revision {
|
|
37
|
-
if (!etag) throw new
|
|
28
|
+
if (!etag) throw new Error(RevisionError.Missing);
|
|
38
29
|
return new Revision(etag.revision);
|
|
39
30
|
}
|
|
40
31
|
|
|
41
32
|
static fromWeakETag(weakEtag: WeakETag | null): Revision {
|
|
42
|
-
if (!weakEtag) throw new
|
|
33
|
+
if (!weakEtag) throw new Error(RevisionError.Missing);
|
|
43
34
|
return new Revision(weakEtag.revision);
|
|
44
35
|
}
|
|
45
|
-
}
|
|
46
36
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
super(RevisionMismatchErrorMessage);
|
|
50
|
-
Object.setPrototypeOf(this, RevisionMismatchError.prototype);
|
|
37
|
+
toString(): string {
|
|
38
|
+
return this.value.toString();
|
|
51
39
|
}
|
|
52
|
-
}
|
|
53
40
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
super(RevisionInvalidErrorMessage);
|
|
57
|
-
Object.setPrototypeOf(this, InvalidRevisionError.prototype);
|
|
41
|
+
toJSON(): number {
|
|
42
|
+
return this.value;
|
|
58
43
|
}
|
|
59
44
|
}
|
package/src/rounding.adapter.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { z } from "zod/v4";
|
|
1
2
|
import type { RoundingPort } from "./rounding.port";
|
|
2
3
|
|
|
3
4
|
export class RoundToNearest implements RoundingPort {
|
|
@@ -18,11 +19,23 @@ export class RoundDown implements RoundingPort {
|
|
|
18
19
|
}
|
|
19
20
|
}
|
|
20
21
|
|
|
21
|
-
export const
|
|
22
|
+
export const RoundingDecimalError = {
|
|
23
|
+
Type: "rounding.decimal.type",
|
|
24
|
+
Invalid: "rounding.decimal.invalid",
|
|
25
|
+
} as const;
|
|
26
|
+
|
|
27
|
+
export const RoundingDecimal = z
|
|
28
|
+
.number(RoundingDecimalError.Type)
|
|
29
|
+
.int(RoundingDecimalError.Invalid)
|
|
30
|
+
.min(1, RoundingDecimalError.Invalid)
|
|
31
|
+
.max(100, RoundingDecimalError.Invalid)
|
|
32
|
+
.brand("RoundingDecimal");
|
|
22
33
|
|
|
23
34
|
export class RoundToDecimal implements RoundingPort {
|
|
24
|
-
|
|
25
|
-
|
|
35
|
+
private readonly decimals: number;
|
|
36
|
+
|
|
37
|
+
constructor(candidate: number) {
|
|
38
|
+
this.decimals = RoundingDecimal.parse(candidate);
|
|
26
39
|
}
|
|
27
40
|
|
|
28
41
|
round(value: number): number {
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { z } from "zod/v4";
|
|
2
|
+
|
|
3
|
+
export const SizeBytesError = { Invalid: "size.bytes.invalid" } as const;
|
|
4
|
+
|
|
5
|
+
export const SizeBytes = z
|
|
6
|
+
.number(SizeBytesError.Invalid)
|
|
7
|
+
.int(SizeBytesError.Invalid)
|
|
8
|
+
.gte(0, SizeBytesError.Invalid)
|
|
9
|
+
.brand("SizeBytes");
|
|
10
|
+
|
|
11
|
+
export type SizeBytesType = z.infer<typeof SizeBytes>;
|
package/src/size.vo.ts
CHANGED
|
@@ -1,22 +1,18 @@
|
|
|
1
|
-
import { z } from "zod/v4";
|
|
2
1
|
import { RoundToDecimal } from "./rounding.adapter";
|
|
2
|
+
import { SizeBytes, type SizeBytesType } from "./size-bytes.vo";
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
enum SizeUnitEnum {
|
|
5
5
|
b = "b",
|
|
6
6
|
kB = "kB",
|
|
7
7
|
MB = "MB",
|
|
8
8
|
GB = "GB",
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
type SizeValueType = z.infer<typeof SizeValue>;
|
|
13
|
-
|
|
14
|
-
type SizeConfigType = { unit: SizeUnit; value: number };
|
|
11
|
+
type SizeConfigType = { unit: SizeUnitEnum; value: number };
|
|
15
12
|
|
|
16
13
|
export class Size {
|
|
17
|
-
private readonly unit:
|
|
18
|
-
private readonly
|
|
19
|
-
private readonly bytes: SizeValueType;
|
|
14
|
+
private readonly unit: SizeUnitEnum;
|
|
15
|
+
private readonly bytes: SizeBytesType;
|
|
20
16
|
|
|
21
17
|
private static readonly KB_MULTIPLIER = 1024;
|
|
22
18
|
private static readonly MB_MULTIPLIER = 1024 * Size.KB_MULTIPLIER;
|
|
@@ -26,35 +22,26 @@ export class Size {
|
|
|
26
22
|
|
|
27
23
|
constructor(config: SizeConfigType) {
|
|
28
24
|
this.unit = config.unit;
|
|
29
|
-
this.
|
|
30
|
-
this.bytes = this.calculateBytes();
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
static fromBytes(candidate: number): Size {
|
|
34
|
-
const value = SizeValue.parse(candidate);
|
|
35
|
-
return new Size({ value, unit: SizeUnit.b });
|
|
25
|
+
this.bytes = this.calculateBytes(config.value, config.unit);
|
|
36
26
|
}
|
|
37
27
|
|
|
38
|
-
static
|
|
39
|
-
|
|
40
|
-
return new Size({ value, unit: SizeUnit.kB });
|
|
28
|
+
static fromBytes(value: SizeConfigType["value"]): Size {
|
|
29
|
+
return new Size({ value, unit: SizeUnitEnum.b });
|
|
41
30
|
}
|
|
42
31
|
|
|
43
|
-
static
|
|
44
|
-
|
|
45
|
-
return new Size({ value, unit: SizeUnit.MB });
|
|
32
|
+
static fromKb(value: SizeConfigType["value"]): Size {
|
|
33
|
+
return new Size({ value, unit: SizeUnitEnum.kB });
|
|
46
34
|
}
|
|
47
35
|
|
|
48
|
-
static
|
|
49
|
-
|
|
50
|
-
return new Size({ value, unit: SizeUnit.GB });
|
|
36
|
+
static fromMB(value: SizeConfigType["value"]): Size {
|
|
37
|
+
return new Size({ value, unit: SizeUnitEnum.MB });
|
|
51
38
|
}
|
|
52
39
|
|
|
53
|
-
|
|
54
|
-
return
|
|
40
|
+
static fromGB(value: SizeConfigType["value"]): Size {
|
|
41
|
+
return new Size({ value, unit: SizeUnitEnum.GB });
|
|
55
42
|
}
|
|
56
43
|
|
|
57
|
-
toBytes():
|
|
44
|
+
toBytes(): SizeBytesType {
|
|
58
45
|
return this.bytes;
|
|
59
46
|
}
|
|
60
47
|
|
|
@@ -62,41 +49,43 @@ export class Size {
|
|
|
62
49
|
return this.bytes > another.toBytes();
|
|
63
50
|
}
|
|
64
51
|
|
|
65
|
-
format(unit:
|
|
52
|
+
format(unit: SizeUnitEnum): string {
|
|
66
53
|
switch (unit) {
|
|
67
|
-
case
|
|
68
|
-
return `${Size.ROUNDER.round(this.bytes / Size.KB_MULTIPLIER)} ${
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
return `${
|
|
75
|
-
}
|
|
76
|
-
default: {
|
|
77
|
-
// SizeUnit.b
|
|
78
|
-
return `${this.bytes} ${SizeUnit.b}`;
|
|
79
|
-
}
|
|
54
|
+
case SizeUnitEnum.kB:
|
|
55
|
+
return `${Size.ROUNDER.round(this.bytes / Size.KB_MULTIPLIER)} ${SizeUnitEnum.kB}`;
|
|
56
|
+
case SizeUnitEnum.MB:
|
|
57
|
+
return `${Size.ROUNDER.round(this.bytes / Size.MB_MULTIPLIER)} ${SizeUnitEnum.MB}`;
|
|
58
|
+
case SizeUnitEnum.GB:
|
|
59
|
+
return `${Size.ROUNDER.round(this.bytes / Size.GB_MULTIPLIER)} ${SizeUnitEnum.GB}`;
|
|
60
|
+
default:
|
|
61
|
+
return `${this.bytes} ${SizeUnitEnum.b}`;
|
|
80
62
|
}
|
|
81
63
|
}
|
|
82
64
|
|
|
83
|
-
static toBytes(config: SizeConfigType):
|
|
65
|
+
static toBytes(config: SizeConfigType): SizeBytesType {
|
|
84
66
|
return new Size(config).toBytes();
|
|
85
67
|
}
|
|
86
68
|
|
|
87
|
-
static unit =
|
|
69
|
+
static unit = SizeUnitEnum;
|
|
88
70
|
|
|
89
|
-
private calculateBytes():
|
|
90
|
-
switch (
|
|
91
|
-
case
|
|
92
|
-
return
|
|
93
|
-
case
|
|
94
|
-
return
|
|
95
|
-
case
|
|
96
|
-
return
|
|
71
|
+
private calculateBytes(value: SizeConfigType["value"], unit: SizeUnitEnum): SizeBytesType {
|
|
72
|
+
switch (unit) {
|
|
73
|
+
case SizeUnitEnum.kB:
|
|
74
|
+
return SizeBytes.parse(value * Size.KB_MULTIPLIER);
|
|
75
|
+
case SizeUnitEnum.MB:
|
|
76
|
+
return SizeBytes.parse(value * Size.MB_MULTIPLIER);
|
|
77
|
+
case SizeUnitEnum.GB:
|
|
78
|
+
return SizeBytes.parse(value * Size.GB_MULTIPLIER);
|
|
97
79
|
default:
|
|
98
|
-
|
|
99
|
-
return this.value;
|
|
80
|
+
return SizeBytes.parse(value);
|
|
100
81
|
}
|
|
101
82
|
}
|
|
83
|
+
|
|
84
|
+
toString(): string {
|
|
85
|
+
return this.format(this.unit);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
toJSON(): { bytes: number } {
|
|
89
|
+
return { bytes: this.bytes };
|
|
90
|
+
}
|
|
102
91
|
}
|
package/src/stopwatch.service.ts
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { Duration } from "./duration.service";
|
|
2
2
|
import { Timestamp, type TimestampType } from "./timestamp.vo";
|
|
3
3
|
|
|
4
|
+
export const StopwatchError = { AlreadyStopped: "stopwatch.already.stopped" } as const;
|
|
5
|
+
|
|
4
6
|
enum StopwatchState {
|
|
5
7
|
started = "started",
|
|
6
8
|
stopped = "stopped",
|
|
7
9
|
}
|
|
8
10
|
|
|
9
|
-
export const StopwatchStateError = "stopwatch.already.stopped" as const;
|
|
10
|
-
|
|
11
11
|
export type StopwatchResultType = Duration;
|
|
12
12
|
|
|
13
13
|
export class Stopwatch {
|
|
@@ -16,7 +16,7 @@ export class Stopwatch {
|
|
|
16
16
|
constructor(private readonly startMs: TimestampType) {}
|
|
17
17
|
|
|
18
18
|
stop(): StopwatchResultType {
|
|
19
|
-
if (this.state === StopwatchState.stopped) throw new Error(
|
|
19
|
+
if (this.state === StopwatchState.stopped) throw new Error(StopwatchError.AlreadyStopped);
|
|
20
20
|
|
|
21
21
|
this.state = StopwatchState.stopped;
|
|
22
22
|
|
package/src/sum.service.ts
CHANGED
|
@@ -4,16 +4,16 @@ export class Sum {
|
|
|
4
4
|
}
|
|
5
5
|
|
|
6
6
|
static precise(values: readonly number[]): number {
|
|
7
|
-
let
|
|
8
|
-
let
|
|
7
|
+
let sum = 0;
|
|
8
|
+
let compensation = 0;
|
|
9
9
|
|
|
10
|
-
for (const
|
|
11
|
-
const
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
|
|
10
|
+
for (const value of values) {
|
|
11
|
+
const adjusted = value - compensation;
|
|
12
|
+
const temporary = sum + adjusted;
|
|
13
|
+
compensation = temporary - sum - adjusted;
|
|
14
|
+
sum = temporary;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
return
|
|
17
|
+
return sum;
|
|
18
18
|
}
|
|
19
19
|
}
|
|
@@ -2,6 +2,9 @@ export class ThousandsSeparator {
|
|
|
2
2
|
private static DEFAULT_SEPARATOR = " ";
|
|
3
3
|
|
|
4
4
|
static format(value: number, separator = ThousandsSeparator.DEFAULT_SEPARATOR): string {
|
|
5
|
-
|
|
5
|
+
// B - not a word boundary, prevents inserting at the very start
|
|
6
|
+
// (?=([0-9]{3})+(?![0-9])) - positive lookahead, find three digits
|
|
7
|
+
// (?![0-9]) - negative lookahead, the next character is not a digit
|
|
8
|
+
return value.toString().replace(/\B(?=([0-9]{3})+(?![0-9]))/g, separator);
|
|
6
9
|
}
|
|
7
10
|
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { Duration } from "./duration.service";
|
|
2
|
+
import { Timestamp, type TimestampType } from "./timestamp.vo";
|
|
3
|
+
|
|
4
|
+
export const Time = {
|
|
5
|
+
Now(now: TimestampType) {
|
|
6
|
+
return {
|
|
7
|
+
Add(duration: Duration): TimestampType {
|
|
8
|
+
return Timestamp.parse(now + duration.ms);
|
|
9
|
+
},
|
|
10
|
+
Minus(duration: Duration): TimestampType {
|
|
11
|
+
return Timestamp.parse(now - duration.ms);
|
|
12
|
+
},
|
|
13
|
+
};
|
|
14
|
+
},
|
|
15
|
+
};
|
package/src/timestamp.vo.ts
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import { z } from "zod/v4";
|
|
2
2
|
|
|
3
|
-
export const TimestampError = {
|
|
3
|
+
export const TimestampError = { Invalid: "timestamp.invalid" } as const;
|
|
4
4
|
|
|
5
5
|
export const Timestamp = z
|
|
6
|
-
.number(TimestampError)
|
|
7
|
-
.int(TimestampError)
|
|
8
|
-
.gte(0, TimestampError)
|
|
9
|
-
.lte(Number.MAX_SAFE_INTEGER, TimestampError)
|
|
6
|
+
.number(TimestampError.Invalid)
|
|
7
|
+
.int(TimestampError.Invalid)
|
|
8
|
+
.gte(0, TimestampError.Invalid)
|
|
10
9
|
.brand("Timestamp");
|
|
11
10
|
|
|
12
11
|
export type TimestampType = z.infer<typeof Timestamp>;
|
package/src/timezone.vo.ts
CHANGED
|
@@ -1,18 +1,24 @@
|
|
|
1
1
|
import { z } from "zod/v4";
|
|
2
2
|
|
|
3
|
-
export const TimezoneError = {
|
|
3
|
+
export const TimezoneError = {
|
|
4
|
+
Type: "timezone.type",
|
|
5
|
+
Empty: "timezone.empty",
|
|
6
|
+
TooLong: "timezone.too.long",
|
|
7
|
+
Invalid: "timezone.invalid",
|
|
8
|
+
} as const;
|
|
4
9
|
|
|
5
10
|
export const Timezone = z
|
|
6
|
-
.string(TimezoneError)
|
|
7
|
-
.min(1, TimezoneError)
|
|
11
|
+
.string(TimezoneError.Type)
|
|
12
|
+
.min(1, TimezoneError.Empty)
|
|
13
|
+
.max(128, TimezoneError.TooLong)
|
|
8
14
|
.refine((value) => {
|
|
9
15
|
try {
|
|
10
|
-
new Intl.DateTimeFormat("en-US", { timeZone: value }).format(
|
|
16
|
+
new Intl.DateTimeFormat("en-US", { timeZone: value }).format(Date.now());
|
|
11
17
|
return true;
|
|
12
|
-
} catch
|
|
18
|
+
} catch {
|
|
13
19
|
return false;
|
|
14
20
|
}
|
|
15
|
-
}, TimezoneError)
|
|
21
|
+
}, TimezoneError.Invalid)
|
|
16
22
|
.brand("Timezone");
|
|
17
23
|
|
|
18
24
|
export type TimezoneType = z.infer<typeof Timezone>;
|
package/src/week-iso-id.vo.ts
CHANGED
|
@@ -1,22 +1,26 @@
|
|
|
1
1
|
import { getISOWeeksInYear } from "date-fns";
|
|
2
2
|
import { z } from "zod/v4";
|
|
3
3
|
|
|
4
|
-
export const WeekIsoIdError = {
|
|
4
|
+
export const WeekIsoIdError = {
|
|
5
|
+
Type: "week.iso.id.type",
|
|
6
|
+
BadChars: "week.iso.id.bad.chars",
|
|
7
|
+
Invalid: "week.iso.id.invalid",
|
|
8
|
+
} as const;
|
|
9
|
+
|
|
10
|
+
// Four digits, hypen, W, followed by two digits
|
|
11
|
+
const WEEK_ISO_ID_CHARS_WHITELIST = /^[0-9]{4}-W[0-9]{2}$/;
|
|
5
12
|
|
|
6
13
|
export const WeekIsoId = z
|
|
7
|
-
.string(WeekIsoIdError)
|
|
8
|
-
.regex(
|
|
14
|
+
.string(WeekIsoIdError.Type)
|
|
15
|
+
.regex(WEEK_ISO_ID_CHARS_WHITELIST, WeekIsoIdError.BadChars)
|
|
9
16
|
.refine((value) => {
|
|
10
|
-
const [
|
|
11
|
-
|
|
12
|
-
const
|
|
13
|
-
const week = Number(weekPart);
|
|
14
|
-
|
|
15
|
-
if (!(Number.isInteger(year) && Number.isInteger(week)) || week < 1) return false;
|
|
16
|
-
|
|
17
|
-
const weeksInYear = getISOWeeksInYear(new Date(Date.UTC(year, 0, 4)));
|
|
17
|
+
const [year, week] = value.split("-W").map(Number);
|
|
18
|
+
// ISO-8601 rule: Jan 4 is always in week 01 of the ISO week-year.
|
|
19
|
+
const weeksInYear = getISOWeeksInYear(Date.UTC(year, 0, 4));
|
|
18
20
|
|
|
21
|
+
if (week < 1) return false;
|
|
19
22
|
return week <= weeksInYear;
|
|
20
|
-
}, WeekIsoIdError)
|
|
23
|
+
}, WeekIsoIdError.Invalid)
|
|
24
|
+
.brand("WeekIsoId");
|
|
21
25
|
|
|
22
26
|
export type WeekIsoIdType = z.infer<typeof WeekIsoId>;
|
package/src/week.vo.ts
CHANGED
|
@@ -4,50 +4,48 @@ import { Timestamp, type TimestampType } from "./timestamp.vo";
|
|
|
4
4
|
import { WeekIsoId, type WeekIsoIdType } from "./week-iso-id.vo";
|
|
5
5
|
|
|
6
6
|
export class Week extends DateRange {
|
|
7
|
-
|
|
8
|
-
const
|
|
9
|
-
const
|
|
7
|
+
static fromTimestamp(timestamp: TimestampType): Week {
|
|
8
|
+
const start = Timestamp.parse(startOfISOWeek(timestamp).getTime());
|
|
9
|
+
const end = Timestamp.parse(endOfISOWeek(timestamp).getTime());
|
|
10
10
|
|
|
11
|
-
return
|
|
11
|
+
return new Week(start, end);
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
return Week.fromTimestamp(Timestamp.parse(shifted));
|
|
14
|
+
static fromNow(now: TimestampType): Week {
|
|
15
|
+
return Week.fromTimestamp(now);
|
|
18
16
|
}
|
|
19
17
|
|
|
20
|
-
|
|
21
|
-
const
|
|
18
|
+
static fromIsoId(isoId: WeekIsoIdType): Week {
|
|
19
|
+
const [year, week] = WeekIsoId.parse(isoId).split("-W").map(Number);
|
|
22
20
|
|
|
23
|
-
|
|
21
|
+
// ISO-8601 rule: Jan 4 is always in week 01 of the ISO week-year.
|
|
22
|
+
const reference = setISOWeek(Date.UTC(year, 0, 4), week).getTime();
|
|
23
|
+
|
|
24
|
+
return Week.fromTimestamp(Timestamp.parse(reference));
|
|
24
25
|
}
|
|
25
26
|
|
|
26
|
-
|
|
27
|
-
const
|
|
27
|
+
toIsoId(): WeekIsoIdType {
|
|
28
|
+
const year = getISOWeekYear(this.getStart());
|
|
29
|
+
const week = getISOWeek(this.getStart());
|
|
28
30
|
|
|
29
|
-
return
|
|
31
|
+
return WeekIsoId.parse(`${year}-W${String(week).padStart(2, "0")}`);
|
|
30
32
|
}
|
|
31
33
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
const end = Timestamp.parse(endOfISOWeek(timestamp).getTime());
|
|
35
|
-
|
|
36
|
-
return new Week(start, end);
|
|
34
|
+
previous(): Week {
|
|
35
|
+
return this.shift(-1);
|
|
37
36
|
}
|
|
38
37
|
|
|
39
|
-
|
|
40
|
-
return
|
|
38
|
+
next(): Week {
|
|
39
|
+
return this.shift(1);
|
|
41
40
|
}
|
|
42
41
|
|
|
43
|
-
|
|
44
|
-
const
|
|
45
|
-
const year = Number(yearPart);
|
|
46
|
-
const week = Number(weekPart);
|
|
42
|
+
shift(count: number): Week {
|
|
43
|
+
const shifted = addWeeks(this.getStart(), count).getTime();
|
|
47
44
|
|
|
48
|
-
|
|
49
|
-
|
|
45
|
+
return Week.fromTimestamp(Timestamp.parse(shifted));
|
|
46
|
+
}
|
|
50
47
|
|
|
51
|
-
|
|
48
|
+
toString(): string {
|
|
49
|
+
return this.toIsoId();
|
|
52
50
|
}
|
|
53
51
|
}
|
package/src/weekday.vo.ts
CHANGED
|
@@ -61,13 +61,12 @@ export class Weekday {
|
|
|
61
61
|
return this.value;
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
-
format(
|
|
65
|
-
|
|
66
|
-
return chosen(this.value);
|
|
64
|
+
format(): string {
|
|
65
|
+
return this.formatter(this.value);
|
|
67
66
|
}
|
|
68
67
|
|
|
69
68
|
toString(): string {
|
|
70
|
-
return this.format(
|
|
69
|
+
return this.format();
|
|
71
70
|
}
|
|
72
71
|
|
|
73
72
|
equals(another: Weekday): boolean {
|
|
@@ -102,14 +101,12 @@ export class Weekday {
|
|
|
102
101
|
}
|
|
103
102
|
|
|
104
103
|
static list(formatter?: WeekdayFormatter): readonly Weekday[] {
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
return Array.from({ length: 7 }, (_, index) => new Weekday(index, chosen));
|
|
104
|
+
return Array.from({ length: 7 }, (_, index) => new Weekday(index, formatter));
|
|
108
105
|
}
|
|
109
106
|
|
|
110
107
|
static listMondayFirst(formatter?: WeekdayFormatter): readonly Weekday[] {
|
|
111
|
-
const
|
|
108
|
+
const [Sunday, ...rest] = Weekday.list(formatter);
|
|
112
109
|
|
|
113
|
-
return [...
|
|
110
|
+
return [...rest, Sunday];
|
|
114
111
|
}
|
|
115
112
|
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { z } from "zod/v4";
|
|
2
|
+
|
|
3
|
+
export const WeightGramsError = { Type: "weight.grams.type", Invalid: "weight.grams.invalid" } as const;
|
|
4
|
+
|
|
5
|
+
export const WeightGrams = z
|
|
6
|
+
.number(WeightGramsError.Type)
|
|
7
|
+
.int(WeightGramsError.Type)
|
|
8
|
+
.gte(0, WeightGramsError.Invalid)
|
|
9
|
+
.brand("WeightGrams");
|
|
10
|
+
|
|
11
|
+
export type WeightGramsType = z.infer<typeof WeightGrams>;
|