@bgord/tools 0.7.0 → 0.9.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 (75) hide show
  1. package/dist/api-key.vo.d.ts +1 -1
  2. package/dist/api-key.vo.js +1 -1
  3. package/dist/build-version.vo.d.ts +1 -1
  4. package/dist/build-version.vo.js +1 -1
  5. package/dist/clock.vo.js +3 -0
  6. package/dist/dll.service.js +7 -8
  7. package/dist/email-mask.service.d.ts +4 -2
  8. package/dist/email-mask.service.js +1 -1
  9. package/dist/etags.vo.d.ts +1 -1
  10. package/dist/etags.vo.js +11 -7
  11. package/dist/filter.vo.js +1 -0
  12. package/dist/hour.vo.js +4 -2
  13. package/dist/iban.vo.d.ts +13 -0
  14. package/dist/iban.vo.js +27 -0
  15. package/dist/image.vo.d.ts +2 -2
  16. package/dist/image.vo.js +2 -2
  17. package/dist/index.d.ts +1 -0
  18. package/dist/index.js +1 -0
  19. package/dist/language.vo.d.ts +1 -1
  20. package/dist/language.vo.js +2 -1
  21. package/dist/mime.vo.js +4 -0
  22. package/dist/min-max-scaler.service.js +5 -0
  23. package/dist/minute.vo.js +3 -2
  24. package/dist/money.vo.d.ts +3 -3
  25. package/dist/money.vo.js +9 -4
  26. package/dist/outlier-detector.service.js +2 -0
  27. package/dist/package-version.vo.d.ts +2 -2
  28. package/dist/package-version.vo.js +5 -1
  29. package/dist/pagination.service.d.ts +3 -3
  30. package/dist/pagination.service.js +21 -19
  31. package/dist/rate-limiter.service.d.ts +1 -1
  32. package/dist/rate-limiter.service.js +4 -2
  33. package/dist/reordering.service.d.ts +7 -7
  34. package/dist/reordering.service.js +12 -8
  35. package/dist/revision.vo.d.ts +1 -1
  36. package/dist/revision.vo.js +3 -2
  37. package/dist/rounding.service.js +1 -0
  38. package/dist/simple-linear-regression.service.js +2 -1
  39. package/dist/size.vo.d.ts +1 -1
  40. package/dist/size.vo.js +11 -8
  41. package/dist/stepper.service.js +3 -2
  42. package/dist/stopwatch.service.d.ts +1 -1
  43. package/dist/stopwatch.service.js +6 -6
  44. package/dist/streak-calculator.service.js +1 -0
  45. package/dist/thousands-separator.service.js +1 -1
  46. package/dist/time-zone-offset-value.vo.d.ts +1 -1
  47. package/dist/time-zone-offset-value.vo.js +2 -1
  48. package/dist/time.service.js +5 -0
  49. package/dist/timestamp.vo.d.ts +1 -1
  50. package/dist/timestamp.vo.js +2 -1
  51. package/dist/timezone.vo.d.ts +1 -1
  52. package/dist/timezone.vo.js +2 -1
  53. package/dist/visually-unambiguous-characters-generator.service.js +24 -24
  54. package/dist/z-score.service.js +3 -0
  55. package/package.json +4 -4
  56. package/readme.md +1 -0
  57. package/src/api-key.vo.ts +1 -1
  58. package/src/build-version.vo.ts +1 -1
  59. package/src/email-mask.service.ts +4 -2
  60. package/src/etags.vo.ts +3 -3
  61. package/src/iban.vo.ts +39 -0
  62. package/src/image.vo.ts +2 -2
  63. package/src/index.ts +1 -0
  64. package/src/language.vo.ts +2 -1
  65. package/src/money.vo.ts +6 -3
  66. package/src/package-version.vo.ts +2 -1
  67. package/src/pagination.service.ts +11 -8
  68. package/src/rate-limiter.service.ts +2 -2
  69. package/src/reordering.service.ts +9 -8
  70. package/src/revision.vo.ts +2 -2
  71. package/src/size.vo.ts +4 -4
  72. package/src/stopwatch.service.ts +4 -4
  73. package/src/time-zone-offset-value.vo.ts +2 -1
  74. package/src/timestamp.vo.ts +2 -1
  75. package/src/timezone.vo.ts +2 -1
package/dist/size.vo.js CHANGED
@@ -7,8 +7,14 @@ export var SizeUnit;
7
7
  SizeUnit["MB"] = "MB";
8
8
  SizeUnit["GB"] = "GB";
9
9
  })(SizeUnit || (SizeUnit = {}));
10
- const SizeValue = z.number().positive();
10
+ const SizeValue = z.number().positive().brand("SizeValue");
11
11
  export class Size {
12
+ unit;
13
+ value;
14
+ bytes;
15
+ static KB_MULTIPLIER = 1024;
16
+ static MB_MULTIPLIER = 1024 * Size.KB_MULTIPLIER;
17
+ static GB_MULTIPLIER = 1024 * Size.MB_MULTIPLIER;
12
18
  constructor(config) {
13
19
  this.unit = config.unit;
14
20
  this.value = SizeValue.parse(config.value);
@@ -47,21 +53,18 @@ export class Size {
47
53
  static toBytes(config) {
48
54
  return new Size(config).toBytes();
49
55
  }
56
+ static unit = SizeUnit;
50
57
  calculateBytes(config) {
51
58
  switch (config.unit) {
52
59
  case SizeUnit.kB:
53
- return config.value * Size.KB_MULTIPLIER;
60
+ return SizeValue.parse(config.value * Size.KB_MULTIPLIER);
54
61
  case SizeUnit.MB:
55
- return config.value * Size.MB_MULTIPLIER;
62
+ return SizeValue.parse(config.value * Size.MB_MULTIPLIER);
56
63
  case SizeUnit.GB:
57
- return config.value * Size.GB_MULTIPLIER;
64
+ return SizeValue.parse(config.value * Size.GB_MULTIPLIER);
58
65
  default:
59
66
  // SizeUnit.b
60
67
  return config.value;
61
68
  }
62
69
  }
63
70
  }
64
- Size.KB_MULTIPLIER = 1024;
65
- Size.MB_MULTIPLIER = 1024 * Size.KB_MULTIPLIER;
66
- Size.GB_MULTIPLIER = 1024 * Size.MB_MULTIPLIER;
67
- Size.unit = SizeUnit;
@@ -1,6 +1,8 @@
1
1
  export class Stepper {
2
+ static DEFAULT_CURRENT = 1;
3
+ current = Stepper.DEFAULT_CURRENT;
4
+ total;
2
5
  constructor(config) {
3
- this.current = Stepper.DEFAULT_CURRENT;
4
6
  if (!Number.isInteger(config.total)) {
5
7
  throw new Error("Total value is not an integer");
6
8
  }
@@ -31,4 +33,3 @@ export class Stepper {
31
33
  return this.current === this.total;
32
34
  }
33
35
  }
34
- Stepper.DEFAULT_CURRENT = 1;
@@ -1,4 +1,4 @@
1
- import type { TimestampType } from "./timestamp.vo";
1
+ import { TimestampType } from "./timestamp.vo";
2
2
  export type StopwatchResultType = {
3
3
  durationMs: TimestampType;
4
4
  };
@@ -1,19 +1,19 @@
1
+ import { Timestamp } from "./timestamp.vo";
1
2
  var StopwatchState;
2
3
  (function (StopwatchState) {
3
4
  StopwatchState["started"] = "started";
4
5
  StopwatchState["stopped"] = "finished";
5
6
  })(StopwatchState || (StopwatchState = {}));
6
7
  export class Stopwatch {
7
- constructor() {
8
- this.state = StopwatchState.started;
9
- this.startMs = Date.now();
10
- }
8
+ state = StopwatchState.started;
9
+ startMs = Timestamp.parse(Date.now());
10
+ stopMs;
11
11
  stop() {
12
12
  if (this.state === StopwatchState.stopped) {
13
13
  throw new Error("Stopwatch is already stopped");
14
14
  }
15
15
  this.state = StopwatchState.stopped;
16
- this.stopMs = Date.now();
17
- return { durationMs: this.stopMs - this.startMs };
16
+ this.stopMs = Timestamp.parse(Date.now());
17
+ return { durationMs: Timestamp.parse(this.stopMs - this.startMs) };
18
18
  }
19
19
  }
@@ -1,5 +1,6 @@
1
1
  import { format, isAfter, isEqual, subDays } from "date-fns";
2
2
  export class StreakCalculator {
3
+ cutoff;
3
4
  constructor() {
4
5
  const today = new Date();
5
6
  this.cutoff = StreakCalculator.format(today);
@@ -1,6 +1,6 @@
1
1
  export class ThousandsSeparator {
2
+ static DEFAULT_SEPARATOR = " ";
2
3
  static format(value, separator = ThousandsSeparator.DEFAULT_SEPARATOR) {
3
4
  return value.toString().replace(/\B(?=(\d{3})+(?!\d))/g, separator);
4
5
  }
5
6
  }
6
- ThousandsSeparator.DEFAULT_SEPARATOR = " ";
@@ -1,3 +1,3 @@
1
1
  import { z } from "zod/v4";
2
- export declare const TimeZoneOffsetValue: z.ZodPipe<z.ZodPipe<z.ZodUnion<[z.ZodString, z.ZodUndefined]>, z.ZodTransform<number, string | undefined>>, z.ZodTransform<number, number>>;
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">;
3
3
  export type TimeZoneOffsetValueType = z.infer<typeof TimeZoneOffsetValue>;
@@ -4,4 +4,5 @@ export const TimeZoneOffsetValue = z
4
4
  .trim()
5
5
  .or(z.undefined())
6
6
  .transform((value) => Number(value))
7
- .transform((value) => (Number.isNaN(value) ? 0 : value));
7
+ .transform((value) => (Number.isNaN(value) ? 0 : value))
8
+ .brand("TimeZoneOffsetValue");
@@ -1,6 +1,11 @@
1
1
  import { RoundToDecimal } from "./rounding.service";
2
2
  const rounding = new RoundToDecimal(2);
3
3
  export class TimeResult {
4
+ days;
5
+ hours;
6
+ minutes;
7
+ seconds;
8
+ ms;
4
9
  constructor(days, hours, minutes, seconds, ms) {
5
10
  this.days = days;
6
11
  this.hours = hours;
@@ -1,3 +1,3 @@
1
1
  import { z } from "zod/v4";
2
- export declare const Timestamp: z.ZodDefault<z.ZodNumber>;
2
+ export declare const Timestamp: z.core.$ZodBranded<z.ZodDefault<z.ZodNumber>, "Timestamp">;
3
3
  export type TimestampType = z.infer<typeof Timestamp>;
@@ -3,4 +3,5 @@ export const Timestamp = z
3
3
  .number()
4
4
  .int()
5
5
  .positive()
6
- .default(() => Date.now());
6
+ .default(() => Date.now())
7
+ .brand("Timestamp");
@@ -1,3 +1,3 @@
1
1
  import { z } from "zod/v4";
2
- export declare const Timezone: z.ZodString;
2
+ export declare const Timezone: z.core.$ZodBranded<z.ZodString, "Timezone">;
3
3
  export type TimezoneType = z.infer<typeof Timezone>;
@@ -16,4 +16,5 @@ export const Timezone = z
16
16
  // An error occurred, indicating an invalid timezone
17
17
  return false;
18
18
  }
19
- }, { message: "timezone.invalid" });
19
+ }, { message: "timezone.invalid" })
20
+ .brand("Timezone");
@@ -1,5 +1,29 @@
1
1
  import { Random } from "./random.service";
2
2
  export class VisuallyUnambiguousCharactersGenerator {
3
+ static chars = [
4
+ "a",
5
+ "b",
6
+ "c",
7
+ "d",
8
+ "e",
9
+ "f",
10
+ "h",
11
+ "i",
12
+ "j",
13
+ "k",
14
+ "m",
15
+ "n",
16
+ "o",
17
+ "p",
18
+ "r",
19
+ "s",
20
+ "t",
21
+ "w",
22
+ "x",
23
+ "y",
24
+ "3",
25
+ "4",
26
+ ];
3
27
  static generate(length = 1) {
4
28
  return Array.from({ length })
5
29
  .map(() => VisuallyUnambiguousCharactersGenerator.chars[Random.generate({
@@ -9,27 +33,3 @@ export class VisuallyUnambiguousCharactersGenerator {
9
33
  .join("");
10
34
  }
11
35
  }
12
- VisuallyUnambiguousCharactersGenerator.chars = [
13
- "a",
14
- "b",
15
- "c",
16
- "d",
17
- "e",
18
- "f",
19
- "h",
20
- "i",
21
- "j",
22
- "k",
23
- "m",
24
- "n",
25
- "o",
26
- "p",
27
- "r",
28
- "s",
29
- "t",
30
- "w",
31
- "x",
32
- "y",
33
- "3",
34
- "4",
35
- ];
@@ -2,6 +2,9 @@ import { Mean } from "./mean.service";
2
2
  import { PopulationStandardDeviation } from "./population-standard-deviation.service";
3
3
  import { RoundToDecimal } from "./rounding.service";
4
4
  export class ZScore {
5
+ rounding;
6
+ mean;
7
+ standardDeviation;
5
8
  constructor(values, rounding = new RoundToDecimal(2)) {
6
9
  this.rounding = rounding;
7
10
  if (values.length < 2) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bgord/tools",
3
- "version": "0.7.0",
3
+ "version": "0.9.0",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "author": "Bartosz Gordon",
@@ -14,14 +14,14 @@
14
14
  },
15
15
  "files": ["dist", "src"],
16
16
  "scripts": {
17
- "build": "tsc --build",
17
+ "build": "rm -rf dist/ && tsc --build",
18
18
  "preinstall": "bunx only-allow bun"
19
19
  },
20
20
  "devDependencies": {
21
21
  "@biomejs/biome": "1.9.4",
22
22
  "@commitlint/cli": "19.8.1",
23
23
  "@commitlint/config-conventional": "19.8.1",
24
- "@types/bun": "1.2.15",
24
+ "@types/bun": "1.2.16",
25
25
  "cspell": "9.0.2",
26
26
  "lefthook": "1.11.13",
27
27
  "only-allow": "1.2.1",
@@ -30,7 +30,7 @@
30
30
  },
31
31
  "dependencies": {
32
32
  "date-fns": "4.1.0",
33
- "zod": "3.25.55"
33
+ "zod": "3.25.64"
34
34
  },
35
35
  "sideEffects": false
36
36
  }
package/readme.md ADDED
@@ -0,0 +1 @@
1
+ # @bgord/tools
package/src/api-key.vo.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { z } from "zod/v4";
2
2
 
3
- export const ApiKey = z.string().trim().length(64);
3
+ export const ApiKey = z.string().trim().length(64).brand("ApiKey");
4
4
 
5
5
  export type ApiKeyType = z.infer<typeof ApiKey>;
@@ -1,5 +1,5 @@
1
1
  import { z } from "zod/v4";
2
2
 
3
- export const BuildVersion = z.string().min(1).max(8);
3
+ export const BuildVersion = z.string().min(1).max(8).brand("BuildVersion");
4
4
 
5
5
  export type BuildVersionType = z.infer<typeof BuildVersion>;
@@ -1,11 +1,13 @@
1
1
  import { z } from "zod/v4";
2
2
 
3
- export const Email = z.email();
3
+ export const Email = z.email().brand("Email");
4
4
 
5
5
  export type EmailType = z.infer<typeof Email>;
6
6
 
7
+ type EmailMaskedType = string;
8
+
7
9
  export class EmailMask {
8
- static censor(email: EmailType): EmailType {
10
+ static censor(email: EmailType): EmailMaskedType {
9
11
  const [beforeAt, afterAt] = email.split("@");
10
12
 
11
13
  const local = beforeAt as string;
package/src/etags.vo.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { z } from "zod/v4";
2
2
 
3
- const RevisionValue = z.number().int().min(0);
3
+ const RevisionValue = z.number().int().min(0).brand("RevisionValue");
4
4
 
5
5
  type RevisionValueType = z.infer<typeof RevisionValue>;
6
6
 
@@ -23,7 +23,7 @@ export class ETag {
23
23
  const candidate = Number(value);
24
24
 
25
25
  if (Number.isNaN(candidate)) return null;
26
- return new ETag(candidate);
26
+ return new ETag(RevisionValue.parse(candidate));
27
27
  }
28
28
  }
29
29
 
@@ -46,6 +46,6 @@ export class WeakETag {
46
46
  const candidate = Number(value.split("W/")[1]);
47
47
 
48
48
  if (Number.isNaN(candidate)) return null;
49
- return new WeakETag(candidate);
49
+ return new WeakETag(RevisionValue.parse(candidate));
50
50
  }
51
51
  }
package/src/iban.vo.ts ADDED
@@ -0,0 +1,39 @@
1
+ import { z } from "zod";
2
+
3
+ // Basic IBAN format regex (2-letter country code + 2 digits + 11–30 alphanumerics)
4
+ const IBAN_REGEX = /^[A-Z]{2}[0-9]{2}[A-Z0-9]{11,30}$/;
5
+
6
+ const IBANValueSchema = z
7
+ .string()
8
+ .transform((val) => val.replace(/\s+/g, "").toUpperCase())
9
+ .refine((iban) => IBAN_REGEX.test(iban), {
10
+ message: "invalid.iban.format",
11
+ });
12
+
13
+ type IBANValueType = z.infer<typeof IBANValueSchema>;
14
+
15
+ type IBANCountryCode = string;
16
+
17
+ export class IBAN {
18
+ private readonly value: IBANValueType;
19
+
20
+ constructor(value: string) {
21
+ this.value = IBANValueSchema.parse(value);
22
+ }
23
+
24
+ toString(): IBANValueType {
25
+ return this.value;
26
+ }
27
+
28
+ format(): string {
29
+ return this.value.replace(/(.{4})(?=.)/g, "$1 ");
30
+ }
31
+
32
+ get countryCode(): IBANCountryCode {
33
+ return this.value.slice(0, 2);
34
+ }
35
+
36
+ equals(other: IBAN): boolean {
37
+ return this.value === other.value;
38
+ }
39
+ }
package/src/image.vo.ts CHANGED
@@ -1,9 +1,9 @@
1
1
  import { z } from "zod/v4";
2
2
 
3
- export const Width = z.number().int().positive().max(10000);
3
+ export const Width = z.number().int().positive().max(10000).brand("Width");
4
4
 
5
5
  export type WidthType = z.infer<typeof Width>;
6
6
 
7
- export const Height = z.number().int().positive().max(10000);
7
+ export const Height = z.number().int().positive().max(10000).brand("Height");
8
8
 
9
9
  export type HeightType = z.infer<typeof Height>;
package/src/index.ts CHANGED
@@ -10,6 +10,7 @@ export * from "./etags.vo";
10
10
  export * from "./feature-flag.vo";
11
11
  export * from "./filter.vo";
12
12
  export * from "./hour.vo";
13
+ export * from "./iban.vo";
13
14
  export * from "./image.vo";
14
15
  export * from "./language.vo";
15
16
  export * from "./leap-year-checker.service";
@@ -3,6 +3,7 @@ import { z } from "zod/v4";
3
3
  export const Language = z
4
4
  .string()
5
5
  .length(2)
6
- .regex(/^[a-z]{2}$/, { message: "invalid_language" });
6
+ .regex(/^[a-z]{2}$/, { message: "invalid_language" })
7
+ .brand("Language");
7
8
 
8
9
  export type LanguageType = z.infer<typeof Language>;
package/src/money.vo.ts CHANGED
@@ -5,20 +5,23 @@ import { RoundToNearest, RoundingStrategy } from "./rounding.service";
5
5
  export const MoneyAmount = z
6
6
  .number()
7
7
  .int({ message: "money.amount.invalid " })
8
- .min(0, { message: "money.amount.invalid " });
8
+ .min(0, { message: "money.amount.invalid " })
9
+ .brand("MoneyAmount");
9
10
 
10
11
  export type MoneyAmountType = z.infer<typeof MoneyAmount>;
11
12
 
12
13
  export const MoneyMultiplicationFactor = z
13
14
  .number()
14
- .min(0, { message: "money.multiplication-factor.invalid" });
15
+ .min(0, { message: "money.multiplication-factor.invalid" })
16
+ .brand("MoneyMultiplicationFactor");
15
17
 
16
18
  export type MoneyMultiplicationFactorType = z.infer<typeof MoneyMultiplicationFactor>;
17
19
 
18
20
  export const MoneyDivisionFactor = z
19
21
  .number()
20
22
  .min(0, { message: "money.division-factor.invalid" })
21
- .refine((value) => value !== 0, { message: "money.division-factor.invalid" });
23
+ .refine((value) => value !== 0, { message: "money.division-factor.invalid" })
24
+ .brand("MoneyDivisionFactor");
22
25
 
23
26
  export type MoneyDivisionFactorType = z.infer<typeof MoneyDivisionFactor>;
24
27
 
@@ -57,7 +57,8 @@ export const PackageVersionValue = z
57
57
  minor: Number(minor),
58
58
  patch: Number(patch),
59
59
  };
60
- });
60
+ })
61
+ .brand("PackageVersionValue");
61
62
  export type PackageVersionValueType = z.infer<typeof PackageVersionValue>;
62
63
 
63
64
  export class PackageVersion {
@@ -1,10 +1,10 @@
1
1
  import { z } from "zod/v4";
2
2
 
3
- const Take = z.number().int().positive();
3
+ const Take = z.number().int().gte(0).brand("Take");
4
4
 
5
5
  type TakeType = z.infer<typeof Take>;
6
6
 
7
- const Skip = z.number().int().positive();
7
+ const Skip = z.number().int().gte(0).brand("Skip");
8
8
 
9
9
  type SkipType = z.infer<typeof Skip>;
10
10
 
@@ -12,7 +12,8 @@ const Page = z.coerce
12
12
  .number()
13
13
  .int()
14
14
  .transform((value) => (value <= 0 ? 1 : value))
15
- .default(1);
15
+ .default(1)
16
+ .brand("Page");
16
17
 
17
18
  export type PageType = z.infer<typeof Page>;
18
19
 
@@ -43,7 +44,7 @@ export class Pagination {
43
44
  const page = Page.parse(values.page);
44
45
  const take = Take.parse(_take);
45
46
 
46
- const skip = (page - 1) * take;
47
+ const skip = Skip.parse((page - 1) * take);
47
48
 
48
49
  return { values: { take, skip }, page };
49
50
  }
@@ -54,8 +55,8 @@ export class Pagination {
54
55
  const currentPage = config.pagination.page;
55
56
  const lastPage = Pagination.getLastPage(config);
56
57
 
57
- const previousPage = currentPage > 1 ? currentPage - 1 : undefined;
58
- const nextPage = currentPage < lastPage ? currentPage + 1 : undefined;
58
+ const previousPage = currentPage > 1 ? Page.parse(currentPage - 1) : undefined;
59
+ const nextPage = currentPage < lastPage ? Page.parse(currentPage + 1) : undefined;
59
60
 
60
61
  return {
61
62
  result: config.result,
@@ -78,7 +79,9 @@ export class Pagination {
78
79
  }
79
80
 
80
81
  private static getLastPage(config: PaginationExhaustedConfig): PageType {
81
- return Math.ceil(config.total / config.pagination.values.take);
82
+ const lastPage = Math.ceil(config.total / config.pagination.values.take);
83
+
84
+ return Page.parse(lastPage);
82
85
  }
83
86
 
84
87
  static empty = {
@@ -94,7 +97,7 @@ export class Pagination {
94
97
  };
95
98
 
96
99
  static getFirstPage({ take }: { take: TakeType }): PaginationType {
97
- return { values: { take, skip: 0 }, page: 1 };
100
+ return { values: { take, skip: Skip.parse(0) }, page: Page.parse(1) };
98
101
  }
99
102
  }
100
103
 
@@ -1,4 +1,4 @@
1
- import type { TimestampType } from "./timestamp.vo";
1
+ import { Timestamp, TimestampType } from "./timestamp.vo";
2
2
  import type { Falsy } from "./ts-utils";
3
3
 
4
4
  type RateLimiterOptionsType = { ms: TimestampType };
@@ -34,7 +34,7 @@ export class RateLimiter {
34
34
 
35
35
  return {
36
36
  allowed: false,
37
- remainingMs: nextAllowedTimestampMs - currentTimestampMs,
37
+ remainingMs: Timestamp.parse(nextAllowedTimestampMs - currentTimestampMs),
38
38
  };
39
39
  }
40
40
  }
@@ -2,15 +2,15 @@ import { z } from "zod/v4";
2
2
 
3
3
  import { DoublyLinkedList, Node } from "./dll.service";
4
4
 
5
- export const ReorderingItemPositionValue = z.number().int().min(0);
5
+ export const ReorderingItemPositionValue = z.number().int().min(0).brand("ReorderingItemPositionValue");
6
6
 
7
7
  export type ReorderingItemPositionValueType = z.infer<typeof ReorderingItemPositionValue>;
8
8
 
9
- export const ReorderingCorrelationId = z.string().min(1);
9
+ export const ReorderingCorrelationId = z.string().min(1).brand("ReorderingCorrelationId");
10
10
 
11
11
  export type ReorderingCorrelationIdType = z.infer<typeof ReorderingCorrelationId>;
12
12
 
13
- export const ReorderingItemId = z.uuid();
13
+ export const ReorderingItemId = z.uuid().brand("ReorderingItemId");
14
14
 
15
15
  export type ReorderingItemIdType = z.infer<typeof ReorderingItemId>;
16
16
 
@@ -99,7 +99,7 @@ export class ReorderingCalculator {
99
99
  }
100
100
 
101
101
  add(id: ReorderingItem["id"]): ReorderingItem {
102
- const position = new ReorderingPosition(this.dll.getSize());
102
+ const position = new ReorderingPosition(ReorderingItemPositionValue.parse(this.dll.getSize()));
103
103
  const item = new ReorderingItem(id, position);
104
104
  this.dll.append(new Node(item));
105
105
  return item;
@@ -155,10 +155,11 @@ export class ReorderingCalculator {
155
155
  export class ReorderingIntegrator {
156
156
  static appendPosition(reordering: ReorderingType[]) {
157
157
  return function <T extends { id: ReorderingItemIdType }>(item: T): WithReorderingPositionValue<T> {
158
- return {
159
- ...item,
160
- position: reordering.find((x) => x.id === item.id)?.position ?? 0,
161
- };
158
+ const position = ReorderingItemPositionValue.parse(
159
+ reordering.find((x) => x.id === item.id)?.position ?? 0,
160
+ );
161
+
162
+ return { ...item, position };
162
163
  };
163
164
  }
164
165
 
@@ -2,14 +2,14 @@ import { z } from "zod/v4";
2
2
 
3
3
  import { ETag, WeakETag } from "./etags.vo";
4
4
 
5
- export const RevisionValue = z.number().int().min(0);
5
+ export const RevisionValue = z.number().int().min(0).brand("RevisionValue");
6
6
 
7
7
  export type RevisionValueType = z.infer<typeof RevisionValue>;
8
8
 
9
9
  export class Revision {
10
10
  readonly value: RevisionValueType;
11
11
 
12
- static initial: RevisionValueType = 0;
12
+ static initial: RevisionValueType = RevisionValue.parse(0);
13
13
 
14
14
  constructor(value: unknown) {
15
15
  const result = RevisionValue.safeParse(value);
package/src/size.vo.ts CHANGED
@@ -9,7 +9,7 @@ export enum SizeUnit {
9
9
  GB = "GB",
10
10
  }
11
11
 
12
- const SizeValue = z.number().positive();
12
+ const SizeValue = z.number().positive().brand("SizeValue");
13
13
 
14
14
  export type SizeValueType = z.infer<typeof SizeValue>;
15
15
 
@@ -81,11 +81,11 @@ export class Size {
81
81
  private calculateBytes(config: SizeConfigType): SizeValueType {
82
82
  switch (config.unit) {
83
83
  case SizeUnit.kB:
84
- return config.value * Size.KB_MULTIPLIER;
84
+ return SizeValue.parse(config.value * Size.KB_MULTIPLIER);
85
85
  case SizeUnit.MB:
86
- return config.value * Size.MB_MULTIPLIER;
86
+ return SizeValue.parse(config.value * Size.MB_MULTIPLIER);
87
87
  case SizeUnit.GB:
88
- return config.value * Size.GB_MULTIPLIER;
88
+ return SizeValue.parse(config.value * Size.GB_MULTIPLIER);
89
89
  default:
90
90
  // SizeUnit.b
91
91
  return config.value;
@@ -1,4 +1,4 @@
1
- import type { TimestampType } from "./timestamp.vo";
1
+ import { Timestamp, TimestampType } from "./timestamp.vo";
2
2
  import type { Falsy } from "./ts-utils";
3
3
 
4
4
  enum StopwatchState {
@@ -11,7 +11,7 @@ export type StopwatchResultType = { durationMs: TimestampType };
11
11
  export class Stopwatch {
12
12
  private state: StopwatchState = StopwatchState.started;
13
13
 
14
- private readonly startMs: TimestampType = Date.now();
14
+ private readonly startMs: TimestampType = Timestamp.parse(Date.now());
15
15
 
16
16
  private stopMs: Falsy<TimestampType>;
17
17
 
@@ -21,8 +21,8 @@ export class Stopwatch {
21
21
  }
22
22
 
23
23
  this.state = StopwatchState.stopped;
24
- this.stopMs = Date.now();
24
+ this.stopMs = Timestamp.parse(Date.now());
25
25
 
26
- return { durationMs: this.stopMs - this.startMs };
26
+ return { durationMs: Timestamp.parse(this.stopMs - this.startMs) };
27
27
  }
28
28
  }
@@ -5,6 +5,7 @@ export const TimeZoneOffsetValue = z
5
5
  .trim()
6
6
  .or(z.undefined())
7
7
  .transform((value) => Number(value))
8
- .transform((value) => (Number.isNaN(value) ? 0 : value));
8
+ .transform((value) => (Number.isNaN(value) ? 0 : value))
9
+ .brand("TimeZoneOffsetValue");
9
10
 
10
11
  export type TimeZoneOffsetValueType = z.infer<typeof TimeZoneOffsetValue>;
@@ -4,6 +4,7 @@ export const Timestamp = z
4
4
  .number()
5
5
  .int()
6
6
  .positive()
7
- .default(() => Date.now());
7
+ .default(() => Date.now())
8
+ .brand("Timestamp");
8
9
 
9
10
  export type TimestampType = z.infer<typeof Timestamp>;
@@ -21,6 +21,7 @@ export const Timezone = z
21
21
  }
22
22
  },
23
23
  { message: "timezone.invalid" },
24
- );
24
+ )
25
+ .brand("Timezone");
25
26
 
26
27
  export type TimezoneType = z.infer<typeof Timezone>;