@synnaxlabs/x 0.55.0 → 0.56.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 (140) hide show
  1. package/.turbo/turbo-build.log +10 -13
  2. package/dist/src/array/nullable.d.ts +1 -1
  3. package/dist/src/array/nullable.d.ts.map +1 -1
  4. package/dist/src/caseconv/caseconv.d.ts.map +1 -1
  5. package/dist/src/compare/compare.d.ts +14 -0
  6. package/dist/src/compare/compare.d.ts.map +1 -1
  7. package/dist/src/debounce/debounce.d.ts +7 -2
  8. package/dist/src/debounce/debounce.d.ts.map +1 -1
  9. package/dist/src/destructor/destructor.d.ts +1 -0
  10. package/dist/src/destructor/destructor.d.ts.map +1 -1
  11. package/dist/src/errors/errors.d.ts +6 -10
  12. package/dist/src/errors/errors.d.ts.map +1 -1
  13. package/dist/src/index.d.ts +4 -1
  14. package/dist/src/index.d.ts.map +1 -1
  15. package/dist/src/notation/external.d.ts +3 -0
  16. package/dist/src/notation/external.d.ts.map +1 -0
  17. package/dist/src/notation/index.d.ts +1 -1
  18. package/dist/src/notation/notation.d.ts +5 -9
  19. package/dist/src/notation/notation.d.ts.map +1 -1
  20. package/dist/src/notation/types.gen.d.ts +9 -0
  21. package/dist/src/notation/types.gen.d.ts.map +1 -0
  22. package/dist/src/primitive/primitive.d.ts +16 -0
  23. package/dist/src/primitive/primitive.d.ts.map +1 -1
  24. package/dist/src/record/record.d.ts +8 -1
  25. package/dist/src/record/record.d.ts.map +1 -1
  26. package/dist/src/require/index.d.ts +2 -0
  27. package/dist/src/require/index.d.ts.map +1 -0
  28. package/dist/src/require/require.d.ts +2 -0
  29. package/dist/src/require/require.d.ts.map +1 -0
  30. package/dist/src/spatial/base.d.ts +1 -103
  31. package/dist/src/spatial/base.d.ts.map +1 -1
  32. package/dist/src/spatial/bounds/bounds.d.ts +3 -3
  33. package/dist/src/spatial/bounds/bounds.d.ts.map +1 -1
  34. package/dist/src/spatial/box/box.d.ts +7 -13
  35. package/dist/src/spatial/box/box.d.ts.map +1 -1
  36. package/dist/src/spatial/direction/direction.d.ts +17 -16
  37. package/dist/src/spatial/direction/direction.d.ts.map +1 -1
  38. package/dist/src/spatial/external.d.ts +1 -2
  39. package/dist/src/spatial/external.d.ts.map +1 -1
  40. package/dist/src/spatial/location/location.d.ts +28 -28
  41. package/dist/src/spatial/location/location.d.ts.map +1 -1
  42. package/dist/src/spatial/scale/scale.d.ts +2 -2
  43. package/dist/src/spatial/scale/scale.d.ts.map +1 -1
  44. package/dist/src/spatial/sticky/sticky.d.ts +15 -15
  45. package/dist/src/spatial/sticky/sticky.d.ts.map +1 -1
  46. package/dist/src/spatial/types.gen.d.ts +179 -2
  47. package/dist/src/spatial/types.gen.d.ts.map +1 -1
  48. package/dist/src/spatial/xy/xy.d.ts +4 -4
  49. package/dist/src/spatial/xy/xy.d.ts.map +1 -1
  50. package/dist/src/status/status.d.ts +11 -0
  51. package/dist/src/status/status.d.ts.map +1 -1
  52. package/dist/src/telem/external.d.ts +1 -0
  53. package/dist/src/telem/external.d.ts.map +1 -1
  54. package/dist/src/telem/series.d.ts +5 -2
  55. package/dist/src/telem/series.d.ts.map +1 -1
  56. package/dist/src/telem/telem.d.ts +42 -34
  57. package/dist/src/telem/telem.d.ts.map +1 -1
  58. package/dist/src/telem/types.gen.d.ts +19 -0
  59. package/dist/src/telem/types.gen.d.ts.map +1 -0
  60. package/dist/src/text/external.d.ts +3 -0
  61. package/dist/src/text/external.d.ts.map +1 -0
  62. package/dist/src/text/index.d.ts +2 -0
  63. package/dist/src/text/index.d.ts.map +1 -0
  64. package/dist/src/text/types.d.ts +21 -0
  65. package/dist/src/text/types.d.ts.map +1 -0
  66. package/dist/src/text/types.gen.d.ts +13 -0
  67. package/dist/src/text/types.gen.d.ts.map +1 -0
  68. package/dist/src/throttle/index.d.ts +2 -0
  69. package/dist/src/throttle/index.d.ts.map +1 -0
  70. package/dist/src/throttle/throttle.d.ts +3 -0
  71. package/dist/src/throttle/throttle.d.ts.map +1 -0
  72. package/dist/src/throttle/throttle.spec.d.ts +2 -0
  73. package/dist/src/throttle/throttle.spec.d.ts.map +1 -0
  74. package/dist/src/zod/parse.d.ts.map +1 -1
  75. package/dist/x.cjs +9 -9
  76. package/dist/x.js +1469 -1346
  77. package/package.json +11 -11
  78. package/src/array/nullable.ts +1 -4
  79. package/src/caseconv/caseconv.spec.ts +71 -0
  80. package/src/caseconv/caseconv.ts +15 -2
  81. package/src/compare/compare.spec.ts +115 -0
  82. package/src/compare/compare.ts +29 -0
  83. package/src/debounce/debounce.spec.ts +258 -24
  84. package/src/debounce/debounce.ts +49 -30
  85. package/src/deep/copy.spec.ts +13 -0
  86. package/src/deep/difference.ts +1 -1
  87. package/src/destructor/destructor.ts +2 -0
  88. package/src/errors/errors.spec.ts +30 -0
  89. package/src/errors/errors.ts +29 -17
  90. package/src/index.ts +4 -1
  91. package/src/notation/external.ts +11 -0
  92. package/src/notation/index.ts +1 -1
  93. package/src/notation/notation.spec.ts +260 -2
  94. package/src/notation/notation.ts +25 -7
  95. package/src/notation/types.gen.ts +16 -0
  96. package/src/primitive/primitive.spec.ts +58 -5
  97. package/src/primitive/primitive.ts +22 -0
  98. package/src/record/record.spec.ts +26 -0
  99. package/src/record/record.ts +20 -5
  100. package/src/require/index.ts +10 -0
  101. package/src/require/require.ts +10 -0
  102. package/src/spatial/base.ts +1 -93
  103. package/src/spatial/bounds/bounds.ts +10 -10
  104. package/src/spatial/box/box.ts +5 -5
  105. package/src/spatial/direction/direction.ts +16 -17
  106. package/src/spatial/external.ts +1 -2
  107. package/src/spatial/location/location.ts +19 -17
  108. package/src/spatial/scale/scale.ts +2 -2
  109. package/src/spatial/sticky/sticky.spec.ts +2 -2
  110. package/src/spatial/sticky/sticky.ts +6 -13
  111. package/src/spatial/types.gen.ts +140 -0
  112. package/src/spatial/xy/xy.ts +7 -7
  113. package/src/status/status.spec.ts +65 -0
  114. package/src/status/status.ts +20 -0
  115. package/src/telem/external.ts +8 -0
  116. package/src/telem/series.spec.ts +183 -0
  117. package/src/telem/series.ts +54 -16
  118. package/src/telem/telem.spec.ts +128 -9
  119. package/src/telem/telem.ts +91 -79
  120. package/src/telem/types.gen.ts +28 -0
  121. package/src/text/external.ts +11 -0
  122. package/src/text/index.ts +10 -0
  123. package/src/text/types.gen.ts +16 -0
  124. package/src/text/types.ts +37 -0
  125. package/src/{worker → throttle}/index.ts +1 -1
  126. package/src/throttle/throttle.spec.ts +147 -0
  127. package/src/throttle/throttle.ts +44 -0
  128. package/src/zod/parse.ts +2 -3
  129. package/tsconfig.tsbuildinfo +1 -1
  130. package/dist/src/spatial/spatial.d.ts +0 -3
  131. package/dist/src/spatial/spatial.d.ts.map +0 -1
  132. package/dist/src/worker/index.d.ts +0 -2
  133. package/dist/src/worker/index.d.ts.map +0 -1
  134. package/dist/src/worker/worker.d.ts +0 -33
  135. package/dist/src/worker/worker.d.ts.map +0 -1
  136. package/dist/src/worker/worker.spec.d.ts +0 -2
  137. package/dist/src/worker/worker.spec.d.ts.map +0 -1
  138. package/src/spatial/spatial.ts +0 -44
  139. package/src/worker/worker.spec.ts +0 -41
  140. package/src/worker/worker.ts +0 -86
@@ -10,7 +10,9 @@
10
10
  import { afterEach, beforeEach, describe, expect, it, test } from "vitest";
11
11
 
12
12
  import { binary } from "@/binary";
13
+ import { primitive } from "@/primitive";
13
14
  import {
15
+ convertDataType,
14
16
  type CrudeDataType,
15
17
  DataType,
16
18
  Density,
@@ -19,7 +21,7 @@ import {
19
21
  TimeRange,
20
22
  TimeSpan,
21
23
  TimeStamp,
22
- type TimeStampStringFormat,
24
+ type TimestampFormat,
23
25
  } from "@/telem";
24
26
 
25
27
  describe("TimeStamp", () => {
@@ -136,7 +138,7 @@ describe("TimeStamp", () => {
136
138
  expect(ts.millisecond).toBe(0);
137
139
  });
138
140
 
139
- test("should round-trip when using local tzInfo", () => {
141
+ test("should round-trip when using local TimeZone", () => {
140
142
  const input = "2025-11-03T17:44:45.809";
141
143
  const ts1 = new TimeStamp(input, "local");
142
144
  const output = ts1.toString("ISO", "local").slice(0, -1);
@@ -539,10 +541,9 @@ describe("TimeStamp", () => {
539
541
  .add(TimeSpan.minutes(20))
540
542
  .add(TimeSpan.milliseconds(12));
541
543
 
542
- const FORMAT_TESTS: [TimeStampStringFormat, string][] = [
544
+ const FORMAT_TESTS: [TimestampFormat, string][] = [
543
545
  ["ISO", "2022-12-15T12:20:00.012Z"],
544
546
  ["ISODate", "2022-12-15"],
545
- ["ISOTime", "12:20:00.012"],
546
547
  ["time", "12:20:00"],
547
548
  ["preciseTime", "12:20:00.012"],
548
549
  ["date", "Dec 15"],
@@ -920,10 +921,10 @@ describe("TimeStamp", () => {
920
921
  });
921
922
 
922
923
  describe("formatBySpan", () => {
923
- test("should return 'shortDate' for spans >= 30 days", () => {
924
+ test("should return 'date' for spans >= 30 days", () => {
924
925
  const ts = new TimeStamp([2022, 12, 15], "UTC");
925
926
  const span = TimeSpan.days(30);
926
- expect(ts.formatBySpan(span)).toBe("shortDate");
927
+ expect(ts.formatBySpan(span)).toBe("date");
927
928
  });
928
929
 
929
930
  test("should return 'dateTime' for spans >= 1 day", () => {
@@ -944,16 +945,34 @@ describe("TimeStamp", () => {
944
945
  expect(ts.formatBySpan(span)).toBe("preciseTime");
945
946
  });
946
947
 
947
- test("should return 'ISOTime' for spans < 1 second", () => {
948
+ test("should return 'preciseTime' for spans < 1 second", () => {
948
949
  const ts = new TimeStamp([2022, 12, 15], "UTC");
949
950
  const span = TimeSpan.milliseconds(500);
950
- expect(ts.formatBySpan(span)).toBe("ISOTime");
951
+ expect(ts.formatBySpan(span)).toBe("preciseTime");
951
952
  });
952
953
 
953
954
  test("should work with very small spans", () => {
954
955
  const ts = new TimeStamp([2022, 12, 15], "UTC");
955
956
  const span = TimeSpan.microseconds(100);
956
- expect(ts.formatBySpan(span)).toBe("ISOTime");
957
+ expect(ts.formatBySpan(span)).toBe("preciseTime");
958
+ });
959
+ });
960
+
961
+ describe("hash", () => {
962
+ it("returns the bigint nanosecond value as a string", () => {
963
+ expect(new TimeStamp(1234567890n).hash()).toBe("1234567890");
964
+ });
965
+
966
+ it("is stable across instances representing the same value", () => {
967
+ expect(new TimeStamp(42n).hash()).toBe(new TimeStamp(42n).hash());
968
+ });
969
+
970
+ it("differs across distinct values", () => {
971
+ expect(new TimeStamp(1n).hash()).not.toBe(new TimeStamp(2n).hash());
972
+ });
973
+
974
+ it("satisfies primitive.isHashable", () => {
975
+ expect(primitive.isHashable(new TimeStamp(0n))).toBe(true);
957
976
  });
958
977
  });
959
978
  });
@@ -1358,6 +1377,20 @@ describe("TimeSpan", () => {
1358
1377
  expect(ts2.valueOf()).toBe(BigInt(Number.MAX_SAFE_INTEGER));
1359
1378
  });
1360
1379
  });
1380
+
1381
+ describe("hash", () => {
1382
+ it("returns the bigint nanosecond value as a string", () => {
1383
+ expect(TimeSpan.milliseconds(500).hash()).toBe("500000000");
1384
+ });
1385
+
1386
+ it("is stable across instances representing the same value", () => {
1387
+ expect(TimeSpan.seconds(1).hash()).toBe(TimeSpan.seconds(1).hash());
1388
+ });
1389
+
1390
+ it("satisfies primitive.isHashable", () => {
1391
+ expect(primitive.isHashable(TimeSpan.ZERO)).toBe(true);
1392
+ });
1393
+ });
1361
1394
  });
1362
1395
 
1363
1396
  describe("Rate", () => {
@@ -1474,6 +1507,20 @@ describe("Rate", () => {
1474
1507
  expect(result.valueOf()).toBe(200);
1475
1508
  });
1476
1509
  });
1510
+
1511
+ describe("hash", () => {
1512
+ it("returns the Hz value as a string", () => {
1513
+ expect(new Rate(100).hash()).toBe("100");
1514
+ });
1515
+
1516
+ it("is stable across instances representing the same value", () => {
1517
+ expect(new Rate(60).hash()).toBe(new Rate(60).hash());
1518
+ });
1519
+
1520
+ it("satisfies primitive.isHashable", () => {
1521
+ expect(primitive.isHashable(new Rate(1))).toBe(true);
1522
+ });
1523
+ });
1477
1524
  });
1478
1525
 
1479
1526
  describe("TimeRange", () => {
@@ -1751,6 +1798,33 @@ describe("TimeRange", () => {
1751
1798
  });
1752
1799
  });
1753
1800
  });
1801
+
1802
+ describe("hash", () => {
1803
+ it("composes the start and end hashes with a dash", () => {
1804
+ const tr = new TimeRange(new TimeStamp(100n), new TimeStamp(200n));
1805
+ expect(tr.hash()).toBe("100-200");
1806
+ });
1807
+
1808
+ it("is stable across instances representing the same range", () => {
1809
+ const a = new TimeRange(new TimeStamp(1n), new TimeStamp(2n));
1810
+ const b = new TimeRange(new TimeStamp(1n), new TimeStamp(2n));
1811
+ expect(a.hash()).toBe(b.hash());
1812
+ });
1813
+
1814
+ it("differs when start or end differs", () => {
1815
+ const base = new TimeRange(new TimeStamp(1n), new TimeStamp(2n));
1816
+ expect(base.hash()).not.toBe(
1817
+ new TimeRange(new TimeStamp(1n), new TimeStamp(3n)).hash(),
1818
+ );
1819
+ expect(base.hash()).not.toBe(
1820
+ new TimeRange(new TimeStamp(0n), new TimeStamp(2n)).hash(),
1821
+ );
1822
+ });
1823
+
1824
+ it("satisfies primitive.isHashable", () => {
1825
+ expect(primitive.isHashable(TimeRange.ZERO)).toBe(true);
1826
+ });
1827
+ });
1754
1828
  });
1755
1829
 
1756
1830
  describe("Density", () => {
@@ -2022,6 +2096,25 @@ describe("DataType", () => {
2022
2096
  });
2023
2097
  });
2024
2098
  });
2099
+
2100
+ describe("hash", () => {
2101
+ it("returns the data type identifier as a string", () => {
2102
+ expect(DataType.FLOAT32.hash()).toBe("float32");
2103
+ expect(DataType.STRING.hash()).toBe("string");
2104
+ });
2105
+
2106
+ it("is stable across instances representing the same data type", () => {
2107
+ expect(new DataType("uint8").hash()).toBe(new DataType("uint8").hash());
2108
+ });
2109
+
2110
+ it("differs across distinct data types", () => {
2111
+ expect(DataType.FLOAT32.hash()).not.toBe(DataType.FLOAT64.hash());
2112
+ });
2113
+
2114
+ it("satisfies primitive.isHashable", () => {
2115
+ expect(primitive.isHashable(DataType.UNKNOWN)).toBe(true);
2116
+ });
2117
+ });
2025
2118
  });
2026
2119
 
2027
2120
  describe("Size", () => {
@@ -2163,3 +2256,29 @@ describe("Size", () => {
2163
2256
  });
2164
2257
  });
2165
2258
  });
2259
+
2260
+ describe("convertDataType", () => {
2261
+ it("preserves precision when subtracting a bigint offset above 2^53 (INT64 -> FLOAT32)", () => {
2262
+ const offset = 1778020940471336960n;
2263
+ const value = 1778020940471336960n + 137438953472n;
2264
+ const result = convertDataType(DataType.INT64, DataType.FLOAT32, value, offset);
2265
+ expect(result).toBe(137438953472);
2266
+ });
2267
+
2268
+ it("preserves precision when subtracting a bigint offset above 2^53 (TIMESTAMP -> FLOAT32)", () => {
2269
+ const offset = 1778020940471336960n;
2270
+ const value = 1778020940471336960n + 1n;
2271
+ const result = convertDataType(DataType.TIMESTAMP, DataType.FLOAT32, value, offset);
2272
+ expect(result).toBe(1);
2273
+ });
2274
+
2275
+ it("returns a bigint when target uses bigint and source does not", () => {
2276
+ const result = convertDataType(DataType.FLOAT32, DataType.INT64, 100, 10);
2277
+ expect(result).toBe(90n);
2278
+ });
2279
+
2280
+ it("falls back to math.sub when neither source nor target use bigint", () => {
2281
+ const result = convertDataType(DataType.FLOAT32, DataType.FLOAT64, 5.5, 1.5);
2282
+ expect(result).toBe(4);
2283
+ });
2284
+ });
@@ -12,25 +12,11 @@ import { z } from "zod";
12
12
  import { math } from "@/math";
13
13
  import { primitive } from "@/primitive";
14
14
  import { type bounds } from "@/spatial";
15
-
16
- /** Time zone specification when working with time stamps. */
17
- export type TZInfo = "UTC" | "local";
15
+ import { type TimestampFormat, type TimeZone } from "@/telem/types.gen";
18
16
 
19
17
  const SIMPLE_DAYS_IN_YEAR = 365;
20
18
  const SIMPLE_DAYS_IN_MONTH = 30;
21
19
 
22
- /** Different string formats for time stamps. */
23
- export type TimeStampStringFormat =
24
- | "ISO"
25
- | "ISODate"
26
- | "ISOTime"
27
- | "time"
28
- | "preciseTime"
29
- | "date"
30
- | "preciseDate"
31
- | "shortDate"
32
- | "dateTime";
33
-
34
20
  /** Different string formats for time spans. */
35
21
  export type TimeSpanStringFormat = "full" | "semantic";
36
22
 
@@ -79,35 +65,42 @@ UTC timestamp. Synnax uses a nanosecond precision int64 timestamp.
79
65
  *
80
66
  * 1. A number representing the number of nanoseconds since the Unix epoch.
81
67
  * 2. A JavaScript Date object.
82
- * 3. An array of numbers satisfying the DateComponents type, where the first element is the
83
- * year, the second is the month, and the third is the day. To increase resolution
84
- * when using this method, use the add method. It's important to note that this initializes
85
- * a timestamp at midnight UTC, regardless of the timezone specified.
86
- * 4. An ISO compliant date or date time string. The time zone component is ignored.
68
+ * 3. An array of numbers satisfying the DateComponents type, where the first element is
69
+ * the year, the second is the month, and the third is the day. To increase
70
+ * resolution when using this method, use the add method. It's important to note that
71
+ * this initializes a timestamp at midnight UTC, regardless of the TimeZone
72
+ * specified.
73
+ * 4. An ISO compliant date or date time string. The TimeZone component is ignored.
87
74
  *
88
- * @param tzInfo - The timezone to use when parsing the timestamp. This can be either "UTC" or
89
- * "local". This parameter is ignored if the value is a Date object or a DateComponents array.
75
+ * @param timeZone - The TimeZone to use when parsing the TimeStamp. This can be either
76
+ * "UTC" or "local". This parameter is ignored if the value is a Date object or a
77
+ * DateComponents array.
90
78
  *
91
79
  * @example ts = new TimeStamp(1 * TimeSpan.HOUR) // 1 hour after the Unix epoch
92
- * @example ts = new TimeStamp([2021, 1, 1]) // 1/1/2021 at midnight UTC
93
- * @example ts = new TimeStamp([2021, 1, 1]).add(1 * TimeSpan.HOUR) // 1/1/2021 at 1am UTC
94
- * @example ts = new TimeStamp("2021-01-01T12:30:00Z") // 1/1/2021 at 12:30pm UTC
80
+ * @example ts = new TimeStamp([2021, 1, 1]) // 1/1/2021 midnight UTC
81
+ * @example ts = new TimeStamp([2021, 1, 1]).add(1 * TimeSpan.HOUR) // 1/1/2021 1am UTC
82
+ * @example ts = new TimeStamp("2021-01-01T12:30:00Z") // 1/1/2021 12:30pm UTC
95
83
  */
96
84
  export class TimeStamp
97
85
  extends primitive.ValueExtension<bigint>
98
- implements primitive.Stringer
86
+ implements primitive.Stringer, primitive.Hashable
99
87
  {
100
- constructor(value?: CrudeTimeStamp, tzInfo: TZInfo = "UTC") {
88
+ /** @returns the bigint nanosecond value as a string for stable hashing. */
89
+ hash(): string {
90
+ return this.value.toString();
91
+ }
92
+
93
+ constructor(value?: CrudeTimeStamp, timeZone: TimeZone = "UTC") {
101
94
  if (value == null) super(TimeStamp.now().valueOf());
102
95
  else if (value instanceof Date)
103
96
  super(BigInt(value.getTime()) * TimeStamp.MILLISECOND.valueOf());
104
97
  else if (typeof value === "string")
105
- super(TimeStamp.parseDateTimeString(value, tzInfo).valueOf());
98
+ super(TimeStamp.parseDateTimeString(value, timeZone).valueOf());
106
99
  else if (Array.isArray(value)) super(TimeStamp.parseDate(value));
107
100
  else {
108
101
  let offset = 0n;
109
102
  if (value instanceof Number) value = value.valueOf();
110
- if (tzInfo === "local") offset = TimeStamp.utcOffset.valueOf();
103
+ if (timeZone === "local") offset = TimeStamp.utcOffset.valueOf();
111
104
  if (typeof value === "number")
112
105
  if (isFinite(value))
113
106
  if (value === math.MAX_INT64_NUMBER) value = math.MAX_INT64;
@@ -138,7 +131,7 @@ export class TimeStamp
138
131
  return this.value;
139
132
  }
140
133
 
141
- private static parseTimeString(time: string, tzInfo: TZInfo = "UTC"): bigint {
134
+ private static parseTimeString(time: string, timeZone: TimeZone = "UTC"): bigint {
142
135
  const [hours, minutes, mbeSeconds] = time.split(":");
143
136
  let seconds = "00";
144
137
  let milliseconds: string | undefined = "00";
@@ -147,13 +140,13 @@ export class TimeStamp
147
140
  .add(TimeStamp.minutes(parseInt(minutes ?? "00", 10)))
148
141
  .add(TimeStamp.seconds(parseInt(seconds ?? "00", 10)))
149
142
  .add(TimeStamp.milliseconds(parseInt(milliseconds ?? "00", 10)));
150
- if (tzInfo === "local") base = base.add(TimeStamp.utcOffset);
143
+ if (timeZone === "local") base = base.add(TimeStamp.utcOffset);
151
144
  return base.valueOf();
152
145
  }
153
146
 
154
- private static parseDateTimeString(str: string, tzInfo: TZInfo = "UTC"): bigint {
147
+ private static parseDateTimeString(str: string, timeZone: TimeZone = "UTC"): bigint {
155
148
  if (!str.includes("/") && !str.includes("-"))
156
- return TimeStamp.parseTimeString(str, tzInfo);
149
+ return TimeStamp.parseTimeString(str, timeZone);
157
150
 
158
151
  const isDateTimeLocal =
159
152
  str.match(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d{1,3})?$/) != null;
@@ -170,7 +163,7 @@ export class TimeStamp
170
163
  }
171
164
 
172
165
  const d =
173
- tzInfo === "local"
166
+ timeZone === "local"
174
167
  ? new Date(datePart.replace("T", " "))
175
168
  : new Date(`${datePart}Z`);
176
169
 
@@ -181,17 +174,17 @@ export class TimeStamp
181
174
  }
182
175
 
183
176
  const d = new Date(str);
184
- // Essential to note that this makes the date midnight in UTC! Not local!
185
- // As a result, we need to add the tzInfo offset back in.
177
+ // Essential to note that this makes the date midnight in UTC! Not local! As a
178
+ // result, we need to add the timeZone offset back in.
186
179
  if (!str.includes(":")) d.setUTCHours(0, 0, 0, 0);
187
180
  return new TimeStamp(
188
181
  BigInt(d.getTime()) * TimeStamp.MILLISECOND.valueOf(),
189
- tzInfo,
182
+ timeZone,
190
183
  ).valueOf();
191
184
  }
192
185
 
193
- private toISOString(tzInfo: TZInfo = "UTC"): string {
194
- if (tzInfo === "UTC") return this.date().toISOString();
186
+ private toISOString(timeZone: TimeZone = "UTC"): string {
187
+ if (timeZone === "UTC") return this.date().toISOString();
195
188
  const d = this.date();
196
189
  const offset = new TimeSpan(
197
190
  BigInt(d.getTimezoneOffset()) * TimeStamp.MINUTE.valueOf(),
@@ -199,8 +192,11 @@ export class TimeStamp
199
192
  return this.sub(offset).date().toISOString();
200
193
  }
201
194
 
202
- private timeString(milliseconds: boolean = false, tzInfo: TZInfo = "UTC"): string {
203
- const iso = this.toISOString(tzInfo);
195
+ private timeString(
196
+ milliseconds: boolean = false,
197
+ timeZone: TimeZone = "UTC",
198
+ ): string {
199
+ const iso = this.toISOString(timeZone);
204
200
  return milliseconds ? iso.slice(11, 23) : iso.slice(11, 19);
205
201
  }
206
202
 
@@ -639,27 +635,25 @@ export class TimeStamp
639
635
  * Returns a string representation of the TimeStamp.
640
636
  *
641
637
  * @param format - Optional format for the string representation. Defaults to "ISO".
642
- * @param tzInfo - Optional timezone info. Defaults to "UTC".
638
+ * @param timeZone - Optional TimeZone. Defaults to "UTC".
643
639
  * @returns A string representation of the TimeStamp.
644
640
  */
645
- toString(format: TimeStampStringFormat = "ISO", tzInfo: TZInfo = "UTC"): string {
641
+ toString(format: TimestampFormat = "ISO", timeZone: TimeZone = "UTC"): string {
646
642
  switch (format) {
647
643
  case "ISODate":
648
- return this.toISOString(tzInfo).slice(0, 10);
649
- case "ISOTime":
650
- return this.toISOString(tzInfo).slice(11, 23);
644
+ return this.toISOString(timeZone).slice(0, 10);
651
645
  case "time":
652
- return this.timeString(false, tzInfo);
646
+ return this.timeString(false, timeZone);
653
647
  case "preciseTime":
654
- return this.timeString(true, tzInfo);
648
+ return this.timeString(true, timeZone);
655
649
  case "date":
656
650
  return this.dateString();
657
651
  case "preciseDate":
658
- return `${this.dateString()} ${this.timeString(true, tzInfo)}`;
652
+ return `${this.dateString()} ${this.timeString(true, timeZone)}`;
659
653
  case "dateTime":
660
- return `${this.dateString()} ${this.timeString(false, tzInfo)}`;
654
+ return `${this.dateString()} ${this.timeString(false, timeZone)}`;
661
655
  default:
662
- return this.toISOString(tzInfo);
656
+ return this.toISOString(timeZone);
663
657
  }
664
658
  }
665
659
 
@@ -697,22 +691,19 @@ export class TimeStamp
697
691
  * Determines the appropriate string format based on the span magnitude.
698
692
  *
699
693
  * @param span - The span that provides context for format selection
700
- * @returns The appropriate TimeStampStringFormat
694
+ * @returns The appropriate TimestampFormat
701
695
  *
702
696
  * Rules:
703
- * - For spans >= 30 days: "shortDate" (e.g., "Nov 5")
697
+ * - For spans >= 30 days: "date" (e.g., "Nov 5")
704
698
  * - For spans >= 1 day: "dateTime" (e.g., "Nov 5 14:23:45")
705
699
  * - For spans >= 1 hour: "time" (e.g., "14:23:45")
706
- * - For spans >= 1 second: "preciseTime" (e.g., "14:23:45.123")
707
- * - For spans < 1 second: "ISOTime" (full precision time)
700
+ * - For spans < 1 hour: "preciseTime" (e.g., "14:23:45.123")
708
701
  */
709
- formatBySpan(span: TimeSpan): TimeStampStringFormat {
710
- if (span.greaterThanOrEqual(TimeSpan.days(30))) return "shortDate";
702
+ formatBySpan(span: TimeSpan): TimestampFormat {
703
+ if (span.greaterThanOrEqual(TimeSpan.days(30))) return "date";
711
704
  if (span.greaterThanOrEqual(TimeSpan.DAY)) return "dateTime";
712
705
  if (span.greaterThanOrEqual(TimeSpan.HOUR)) return "time";
713
- if (span.greaterThanOrEqual(TimeSpan.SECOND)) return "preciseTime";
714
-
715
- return "ISOTime";
706
+ return "preciseTime";
716
707
  }
717
708
 
718
709
  /**
@@ -760,56 +751,56 @@ export class TimeStamp
760
751
  * @param value - The number of nanoseconds.
761
752
  * @returns A TimeStamp representing the given number of nanoseconds.
762
753
  */
763
- static nanoseconds(value: number, tzInfo: TZInfo = "UTC"): TimeStamp {
764
- return new TimeStamp(value, tzInfo);
754
+ static nanoseconds(value: number, timeZone: TimeZone = "UTC"): TimeStamp {
755
+ return new TimeStamp(value, timeZone);
765
756
  }
766
757
 
767
758
  /** One nanosecond after the unix epoch */
768
759
  static readonly NANOSECOND = TimeStamp.nanoseconds(1);
769
760
 
770
761
  /** @returns a new TimeStamp n microseconds after the unix epoch */
771
- static microseconds(value: number, tzInfo: TZInfo = "UTC"): TimeStamp {
772
- return TimeStamp.nanoseconds(value * 1000, tzInfo);
762
+ static microseconds(value: number, timeZone: TimeZone = "UTC"): TimeStamp {
763
+ return TimeStamp.nanoseconds(value * 1000, timeZone);
773
764
  }
774
765
 
775
766
  /** One microsecond after the unix epoch */
776
767
  static readonly MICROSECOND = TimeStamp.microseconds(1);
777
768
 
778
769
  /** @returns a new TimeStamp n milliseconds after the unix epoch */
779
- static milliseconds(value: number, tzInfo: TZInfo = "UTC"): TimeStamp {
780
- return TimeStamp.microseconds(value * 1000, tzInfo);
770
+ static milliseconds(value: number, timeZone: TimeZone = "UTC"): TimeStamp {
771
+ return TimeStamp.microseconds(value * 1000, timeZone);
781
772
  }
782
773
 
783
774
  /** One millisecond after the unix epoch */
784
775
  static readonly MILLISECOND = TimeStamp.milliseconds(1);
785
776
 
786
777
  /** @returns a new TimeStamp n seconds after the unix epoch */
787
- static seconds(value: number, tzInfo: TZInfo = "UTC"): TimeStamp {
788
- return TimeStamp.milliseconds(value * 1000, tzInfo);
778
+ static seconds(value: number, timeZone: TimeZone = "UTC"): TimeStamp {
779
+ return TimeStamp.milliseconds(value * 1000, timeZone);
789
780
  }
790
781
 
791
782
  /** One second after the unix epoch */
792
783
  static readonly SECOND = TimeStamp.seconds(1);
793
784
 
794
785
  /** @returns a new TimeStamp n minutes after the unix epoch */
795
- static minutes(value: number, tzInfo: TZInfo = "UTC"): TimeStamp {
796
- return TimeStamp.seconds(value * 60, tzInfo);
786
+ static minutes(value: number, timeZone: TimeZone = "UTC"): TimeStamp {
787
+ return TimeStamp.seconds(value * 60, timeZone);
797
788
  }
798
789
 
799
790
  /** One minute after the unix epoch */
800
791
  static readonly MINUTE = TimeStamp.minutes(1);
801
792
 
802
793
  /** @returns a new TimeStamp n hours after the unix epoch */
803
- static hours(value: number, tzInfo: TZInfo = "UTC"): TimeStamp {
804
- return TimeStamp.minutes(value * 60, tzInfo);
794
+ static hours(value: number, timeZone: TimeZone = "UTC"): TimeStamp {
795
+ return TimeStamp.minutes(value * 60, timeZone);
805
796
  }
806
797
 
807
798
  /** One hour after the unix epoch */
808
799
  static readonly HOUR = TimeStamp.hours(1);
809
800
 
810
801
  /** @returns a new TimeStamp n days after the unix epoch */
811
- static days(value: number, tzInfo: TZInfo = "UTC"): TimeStamp {
812
- return TimeStamp.hours(value * 24, tzInfo);
802
+ static days(value: number, timeZone: TimeZone = "UTC"): TimeStamp {
803
+ return TimeStamp.hours(value * 24, timeZone);
813
804
  }
814
805
 
815
806
  /** One day after the unix epoch */
@@ -852,7 +843,7 @@ export class TimeStamp
852
843
  /** TimeSpan represents a nanosecond precision duration. */
853
844
  export class TimeSpan
854
845
  extends primitive.ValueExtension<bigint>
855
- implements primitive.Stringer
846
+ implements primitive.Stringer, primitive.Hashable
856
847
  {
857
848
  constructor(value: CrudeTimeSpan) {
858
849
  if (typeof value === "number") value = Math.trunc(value.valueOf());
@@ -898,6 +889,11 @@ export class TimeSpan
898
889
  return this.value;
899
890
  }
900
891
 
892
+ /** @returns the bigint nanosecond value as a string for stable hashing. */
893
+ hash(): string {
894
+ return this.value.toString();
895
+ }
896
+
901
897
  /**
902
898
  * Checks if the TimeSpan is less than another TimeSpan.
903
899
  *
@@ -1310,13 +1306,18 @@ export class TimeSpan
1310
1306
  /** Rate represents a data rate in Hz. */
1311
1307
  export class Rate
1312
1308
  extends primitive.ValueExtension<number>
1313
- implements primitive.Stringer
1309
+ implements primitive.Stringer, primitive.Hashable
1314
1310
  {
1315
1311
  constructor(value: CrudeRate) {
1316
1312
  if (primitive.isCrudeValueExtension<number>(value)) value = value.value;
1317
1313
  super(value.valueOf());
1318
1314
  }
1319
1315
 
1316
+ /** @returns the Hz value as a string for stable hashing. */
1317
+ hash(): string {
1318
+ return this.value.toString();
1319
+ }
1320
+
1320
1321
  /** @returns a pretty string representation of the rate in the format "X Hz". */
1321
1322
  toString(): string {
1322
1323
  return `${this.valueOf()} Hz`;
@@ -1549,7 +1550,12 @@ export class Density
1549
1550
  * @property start - A TimeStamp representing the start of the range.
1550
1551
  * @property end - A Timestamp representing the end of the range.
1551
1552
  */
1552
- export class TimeRange implements primitive.Stringer {
1553
+ export class TimeRange implements primitive.Stringer, primitive.Hashable {
1554
+ /** @returns a stable hash composed of the start and end timestamps. */
1555
+ hash(): string {
1556
+ return `${this.start.hash()}-${this.end.hash()}`;
1557
+ }
1558
+
1553
1559
  /**
1554
1560
  * The starting TimeStamp of the TimeRange.
1555
1561
  *
@@ -1823,8 +1829,13 @@ export class TimeRange implements primitive.Stringer {
1823
1829
  /** DataType is a string that represents a data type. */
1824
1830
  export class DataType
1825
1831
  extends primitive.ValueExtension<string>
1826
- implements primitive.Stringer
1832
+ implements primitive.Stringer, primitive.Hashable
1827
1833
  {
1834
+ /** @returns the data type identifier as a string for stable hashing. */
1835
+ hash(): string {
1836
+ return this.value;
1837
+ }
1838
+
1828
1839
  constructor(value: CrudeDataType) {
1829
1840
  if (primitive.isCrudeValueExtension<string>(value)) value = value.value;
1830
1841
  if (
@@ -2418,7 +2429,8 @@ export const convertDataType = (
2418
2429
  value: math.Numeric,
2419
2430
  offset: math.Numeric = 0,
2420
2431
  ): math.Numeric => {
2421
- if (source.usesBigInt && !target.usesBigInt) return Number(value) - Number(offset);
2432
+ if (source.usesBigInt && !target.usesBigInt)
2433
+ return Number(BigInt(value.valueOf()) - BigInt(offset.valueOf()));
2422
2434
  if (!source.usesBigInt && target.usesBigInt)
2423
2435
  return BigInt(value.valueOf()) - BigInt(offset.valueOf());
2424
2436
  return math.sub(value, offset);
@@ -0,0 +1,28 @@
1
+ // Copyright 2026 Synnax Labs, Inc.
2
+ //
3
+ // Use of this software is governed by the Business Source License included in the file
4
+ // licenses/BSL.txt.
5
+ //
6
+ // As of the Change Date specified in that file, in accordance with the Business Source
7
+ // License, use of this software will be governed by the Apache License, Version 2.0,
8
+ // included in the file licenses/APL.txt.
9
+
10
+ // Code generated by Oracle. DO NOT EDIT.
11
+
12
+ import { z } from "zod";
13
+
14
+ export const TIMESTAMP_FORMATS = [
15
+ "ISO",
16
+ "ISODate",
17
+ "time",
18
+ "preciseTime",
19
+ "date",
20
+ "preciseDate",
21
+ "dateTime",
22
+ ] as const;
23
+ export const timestampFormatZ = z.enum(TIMESTAMP_FORMATS);
24
+ export type TimestampFormat = z.infer<typeof timestampFormatZ>;
25
+
26
+ export const TIME_ZONES = ["local", "UTC"] as const;
27
+ export const timeZoneZ = z.enum(TIME_ZONES);
28
+ export type TimeZone = z.infer<typeof timeZoneZ>;
@@ -0,0 +1,11 @@
1
+ // Copyright 2026 Synnax Labs, Inc.
2
+ //
3
+ // Use of this software is governed by the Business Source License included in the file
4
+ // licenses/BSL.txt.
5
+ //
6
+ // As of the Change Date specified in that file, in accordance with the Business Source
7
+ // License, use of this software will be governed by the Apache License, Version 2.0,
8
+ // included in the file licenses/APL.txt.
9
+
10
+ export * from "@/text/types";
11
+ export * from "@/text/types.gen";
@@ -0,0 +1,10 @@
1
+ // Copyright 2026 Synnax Labs, Inc.
2
+ //
3
+ // Use of this software is governed by the Business Source License included in the file
4
+ // licenses/BSL.txt.
5
+ //
6
+ // As of the Change Date specified in that file, in accordance with the Business Source
7
+ // License, use of this software will be governed by the Apache License, Version 2.0,
8
+ // included in the file licenses/APL.txt.
9
+
10
+ export * as text from "@/text/external";
@@ -0,0 +1,16 @@
1
+ // Copyright 2026 Synnax Labs, Inc.
2
+ //
3
+ // Use of this software is governed by the Business Source License included in the file
4
+ // licenses/BSL.txt.
5
+ //
6
+ // As of the Change Date specified in that file, in accordance with the Business Source
7
+ // License, use of this software will be governed by the Apache License, Version 2.0,
8
+ // included in the file licenses/APL.txt.
9
+
10
+ // Code generated by Oracle. DO NOT EDIT.
11
+
12
+ import { z } from "zod";
13
+
14
+ export const LEVELS = ["h1", "h2", "h3", "h4", "h5", "p", "small"] as const;
15
+ export const levelZ = z.enum(LEVELS);
16
+ export type Level = z.infer<typeof levelZ>;