@synnaxlabs/x 0.45.0 → 0.46.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 (160) hide show
  1. package/.turbo/turbo-build.log +35 -33
  2. package/dist/array.cjs +1 -1
  3. package/dist/array.js +2 -5
  4. package/dist/binary.cjs +1 -1
  5. package/dist/binary.js +1 -1
  6. package/dist/box-BXWXSkKu.js +203 -0
  7. package/dist/box-rH3ggwXk.cjs +1 -0
  8. package/dist/box.cjs +1 -1
  9. package/dist/box.js +1 -1
  10. package/dist/caseconv.cjs +1 -1
  11. package/dist/caseconv.js +1 -1
  12. package/dist/deep.cjs +1 -1
  13. package/dist/deep.js +211 -99
  14. package/dist/{external-BM_NS5yM.js → external-2YWy569j.js} +3 -3
  15. package/dist/{external-uXk0Avrg.cjs → external-B6edOwoQ.cjs} +1 -1
  16. package/dist/{external-BPgtxa8d.js → external-B80i4ymZ.js} +2 -2
  17. package/dist/external-BxmTQZ6m.cjs +1 -0
  18. package/dist/external-DLiGrXn7.cjs +1 -0
  19. package/dist/external-Du5qzfYv.js +35 -0
  20. package/dist/get-CtJEJIC_.js +82 -0
  21. package/dist/get-D2VRwUw4.cjs +1 -0
  22. package/dist/{index-CwGPVvbl.cjs → index-Bfvg0v-N.cjs} +1 -1
  23. package/dist/{index-uDxeM-cl.cjs → index-CyNZHQFw.cjs} +1 -1
  24. package/dist/{index-Bxlv0uf_.js → index-qmkoZBNO.js} +1 -1
  25. package/dist/{index-BHXRDFYj.js → index-yz34Wc2p.js} +1 -1
  26. package/dist/index.cjs +3 -3
  27. package/dist/index.js +411 -378
  28. package/dist/{location-BPoXwOni.cjs → location-0qDBiCqP.cjs} +1 -1
  29. package/dist/{location-CVxysrHI.js → location-BIzpxczO.js} +1 -1
  30. package/dist/location.cjs +1 -1
  31. package/dist/location.js +1 -1
  32. package/dist/runtime.cjs +1 -1
  33. package/dist/runtime.js +1 -1
  34. package/dist/{scale-DjxC6ep2.js → scale-BBWhTUqJ.js} +3 -3
  35. package/dist/{scale-BHs716im.cjs → scale-BXy1w8R_.cjs} +1 -1
  36. package/dist/scale.cjs +1 -1
  37. package/dist/scale.js +1 -1
  38. package/dist/series-B2zqvP8A.cjs +6 -0
  39. package/dist/{series-W5Aafjeu.js → series-Clbw-fZI.js} +543 -451
  40. package/dist/spatial.cjs +1 -1
  41. package/dist/spatial.js +4 -4
  42. package/dist/src/array/external.d.ts +4 -0
  43. package/dist/src/array/external.d.ts.map +1 -0
  44. package/dist/src/array/index.d.ts +1 -1
  45. package/dist/src/array/index.d.ts.map +1 -1
  46. package/dist/src/array/nullable.d.ts +3 -0
  47. package/dist/src/array/nullable.d.ts.map +1 -0
  48. package/dist/src/array/update.d.ts +14 -0
  49. package/dist/src/array/update.d.ts.map +1 -0
  50. package/dist/src/array/update.spec.d.ts +2 -0
  51. package/dist/src/array/update.spec.d.ts.map +1 -0
  52. package/dist/src/deep/copy.spec.d.ts +2 -0
  53. package/dist/src/deep/copy.spec.d.ts.map +1 -0
  54. package/dist/src/deep/external.d.ts +3 -1
  55. package/dist/src/deep/external.d.ts.map +1 -1
  56. package/dist/src/deep/get.bench.d.ts +2 -0
  57. package/dist/src/deep/get.bench.d.ts.map +1 -0
  58. package/dist/src/deep/get.d.ts +16 -0
  59. package/dist/src/deep/get.d.ts.map +1 -0
  60. package/dist/src/deep/get.spec.d.ts +2 -0
  61. package/dist/src/deep/get.spec.d.ts.map +1 -0
  62. package/dist/src/deep/partial.spec.d.ts +2 -0
  63. package/dist/src/deep/partial.spec.d.ts.map +1 -0
  64. package/dist/src/deep/path.d.ts +4 -92
  65. package/dist/src/deep/path.d.ts.map +1 -1
  66. package/dist/src/deep/remove.bench.d.ts +2 -0
  67. package/dist/src/deep/remove.bench.d.ts.map +1 -0
  68. package/dist/src/deep/remove.d.ts +2 -0
  69. package/dist/src/deep/remove.d.ts.map +1 -0
  70. package/dist/src/deep/remove.spec.d.ts +2 -0
  71. package/dist/src/deep/remove.spec.d.ts.map +1 -0
  72. package/dist/src/deep/set.bench.d.ts +2 -0
  73. package/dist/src/deep/set.bench.d.ts.map +1 -0
  74. package/dist/src/deep/set.d.ts +2 -0
  75. package/dist/src/deep/set.d.ts.map +1 -0
  76. package/dist/src/deep/set.spec.d.ts +2 -0
  77. package/dist/src/deep/set.spec.d.ts.map +1 -0
  78. package/dist/src/index.d.ts +1 -0
  79. package/dist/src/index.d.ts.map +1 -1
  80. package/dist/src/label/index.d.ts +2 -0
  81. package/dist/src/label/index.d.ts.map +1 -0
  82. package/dist/src/label/label.d.ts +11 -0
  83. package/dist/src/label/label.d.ts.map +1 -0
  84. package/dist/src/math/constants.d.ts +16 -0
  85. package/dist/src/math/constants.d.ts.map +1 -0
  86. package/dist/src/math/external.d.ts +1 -0
  87. package/dist/src/math/external.d.ts.map +1 -1
  88. package/dist/src/migrate/migrate.d.ts +1 -1
  89. package/dist/src/spatial/box/box.d.ts.map +1 -1
  90. package/dist/src/spatial/xy/xy.d.ts +10 -0
  91. package/dist/src/spatial/xy/xy.d.ts.map +1 -1
  92. package/dist/src/status/status.d.ts +25 -15
  93. package/dist/src/status/status.d.ts.map +1 -1
  94. package/dist/src/strings/strings.d.ts +1 -0
  95. package/dist/src/strings/strings.d.ts.map +1 -1
  96. package/dist/src/telem/series.d.ts +4 -4
  97. package/dist/src/telem/series.d.ts.map +1 -1
  98. package/dist/src/telem/telem.d.ts +35 -29
  99. package/dist/src/telem/telem.d.ts.map +1 -1
  100. package/dist/telem.cjs +1 -1
  101. package/dist/telem.js +14 -13
  102. package/dist/{xy-DWwtHmgn.cjs → xy-C-MUIjVs.cjs} +1 -1
  103. package/dist/{xy-DYPw8-8C.js → xy-DnrCAZaw.js} +16 -9
  104. package/dist/xy.cjs +1 -1
  105. package/dist/xy.js +1 -1
  106. package/dist/zod.cjs +1 -1
  107. package/dist/zod.js +1 -1
  108. package/package.json +7 -7
  109. package/src/array/external.ts +12 -0
  110. package/src/array/index.ts +1 -1
  111. package/src/array/nullable.ts +7 -0
  112. package/src/array/update.spec.ts +133 -0
  113. package/src/array/update.ts +66 -0
  114. package/src/caseconv/caseconv.spec.ts +2 -0
  115. package/src/caseconv/caseconv.ts +9 -9
  116. package/src/deep/copy.spec.ts +148 -0
  117. package/src/deep/external.ts +3 -1
  118. package/src/deep/get.bench.ts +170 -0
  119. package/src/deep/get.spec.ts +196 -0
  120. package/src/deep/get.ts +79 -0
  121. package/src/deep/partial.spec.ts +194 -0
  122. package/src/deep/path.spec.ts +94 -182
  123. package/src/deep/path.ts +27 -198
  124. package/src/deep/remove.bench.ts +238 -0
  125. package/src/deep/remove.spec.ts +219 -0
  126. package/src/deep/remove.ts +102 -0
  127. package/src/deep/set.bench.ts +208 -0
  128. package/src/deep/set.spec.ts +369 -0
  129. package/src/deep/set.ts +91 -0
  130. package/src/index.ts +1 -0
  131. package/src/label/index.ts +10 -0
  132. package/src/label/label.ts +20 -0
  133. package/src/math/constants.ts +29 -0
  134. package/src/math/external.ts +1 -0
  135. package/src/spatial/box/box.ts +1 -2
  136. package/src/spatial/xy/xy.ts +18 -1
  137. package/src/status/status.ts +47 -27
  138. package/src/strings/strings.spec.ts +42 -0
  139. package/src/strings/strings.ts +14 -0
  140. package/src/telem/series.ts +2 -3
  141. package/src/telem/telem.spec.ts +155 -18
  142. package/src/telem/telem.ts +185 -56
  143. package/src/testutil/testutil.spec.ts +1 -1
  144. package/src/zod/nullToUndefined.spec.ts +2 -2
  145. package/src/zod/toArray.spec.ts +9 -3
  146. package/tsconfig.tsbuildinfo +1 -1
  147. package/.vscode/settings.json +0 -5
  148. package/README.md +0 -38
  149. package/dist/box-BYuq-Gjx.js +0 -203
  150. package/dist/box-Blu-4d1n.cjs +0 -1
  151. package/dist/external-E3ErJeeM.cjs +0 -1
  152. package/dist/path-Blh4wJuA.js +0 -110
  153. package/dist/path-CPSfCjde.cjs +0 -1
  154. package/dist/series-C6ZwNf8i.cjs +0 -6
  155. package/dist/src/deep/delete.d.ts +0 -3
  156. package/dist/src/deep/delete.d.ts.map +0 -1
  157. package/dist/src/deep/delete.spec.d.ts +0 -2
  158. package/dist/src/deep/delete.spec.d.ts.map +0 -1
  159. package/src/deep/delete.spec.ts +0 -73
  160. package/src/deep/delete.ts +0 -27
@@ -0,0 +1,29 @@
1
+ // Copyright 2025 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 const MAX_UINT8 = 2 ** 8 - 1;
11
+ export const MAX_UINT16 = 2 ** 16 - 1;
12
+ export const MAX_UINT32 = 2 ** 32 - 1;
13
+ export const MAX_UINT64 = 2n ** 64n - 1n;
14
+ export const MAX_UINT64_NUMBER = 2 ** 64 - 1;
15
+
16
+ export const MIN_INT8 = -(2 ** 7);
17
+ export const MAX_INT8 = 2 ** 7 - 1;
18
+
19
+ export const MIN_INT16 = -(2 ** 15);
20
+ export const MAX_INT16 = 2 ** 15 - 1;
21
+
22
+ export const MIN_INT32 = -(2 ** 31);
23
+ export const MAX_INT32 = 2 ** 31 - 1;
24
+
25
+ export const MIN_INT64 = -(2n ** 63n);
26
+ export const MAX_INT64 = 2n ** 63n - 1n;
27
+
28
+ export const MIN_INT64_NUMBER = -(2 ** 63);
29
+ export const MAX_INT64_NUMBER = 2 ** 63 - 1;
@@ -7,5 +7,6 @@
7
7
  // License, use of this software will be governed by the Apache License, Version 2.0,
8
8
  // included in the file licenses/APL.txt.
9
9
 
10
+ export * from "@/math/constants";
10
11
  export * from "@/math/math";
11
12
  export * from "@/math/round";
@@ -249,8 +249,7 @@ export const loc = (b: Crude, loc: location.Location): number => {
249
249
  };
250
250
 
251
251
  /** @returns true if the area of the box is 0 and false otherwise. */
252
- export const areaIsZero = (b: Box): boolean =>
253
- b.one.x === b.two.x && b.one.y === b.two.y;
252
+ export const areaIsZero = (b: Box): boolean => area(b) === 0;
254
253
 
255
254
  /** @returns the width of the box. */
256
255
  export const width = (b: Crude): number => dim(b, "x");
@@ -194,7 +194,10 @@ export const css = (a: Crude): { left: number; top: number } => {
194
194
  */
195
195
  export const truncate = (a: Crude, precision: number = 0): XY => {
196
196
  const xy = construct(a);
197
- return { x: Number(xy.x.toFixed(precision)), y: Number(xy.y.toFixed(precision)) };
197
+ return {
198
+ x: Number(xy.x.toFixed(precision)),
199
+ y: Number(xy.y.toFixed(precision)),
200
+ };
198
201
  };
199
202
 
200
203
  /**
@@ -311,3 +314,17 @@ export const round = (a: Crude): XY => {
311
314
  const xy = construct(a);
312
315
  return { x: Math.round(xy.x), y: Math.round(xy.y) };
313
316
  };
317
+
318
+ /**
319
+ * Reciprocal of a point.
320
+ * @param a - The coordinate to invert. Can be provided in any supported format (couple, object, dimensions, etc.)
321
+ * @returns A new XY coordinate with the x and y values inverted.
322
+ * @example
323
+ * reciprocal([1, 2]) // returns { x: 1, y: 0.5 }
324
+ * reciprocal({ x: 3, y: 4 }) // returns { x: 0.3333333333333333, y: 0.25 }
325
+ * reciprocal({ width: 5, height: 6 }) // returns { x: 0.2, y: 0.16666666666666666 }
326
+ */
327
+ export const reciprocal = (a: Crude): XY => {
328
+ const xy = construct(a);
329
+ return { x: 1 / xy.x, y: 1 / xy.y };
330
+ };
@@ -9,7 +9,9 @@
9
9
 
10
10
  import { z } from "zod";
11
11
 
12
+ import { array } from "@/array";
12
13
  import { id } from "@/id";
14
+ import { label } from "@/label";
13
15
  import { type Optional } from "@/optional";
14
16
  import { TimeStamp } from "@/telem";
15
17
 
@@ -25,53 +27,70 @@ export const variantZ = z.enum([
25
27
  // Represents one of the possible variants of a status message.
26
28
  export type Variant = z.infer<typeof variantZ>;
27
29
 
28
- type StatusZodObject<D extends z.ZodType> = z.ZodObject<{
29
- key: z.ZodString;
30
- variant: typeof variantZ;
31
- message: z.ZodString;
32
- description: z.ZodOptional<z.ZodString>;
33
- time: typeof TimeStamp.z;
34
- details: D;
35
- }>;
30
+ export type StatusZodObject<DetailsSchema extends z.ZodType = z.ZodNever> = z.ZodObject<
31
+ {
32
+ key: z.ZodString;
33
+ name: z.ZodDefault<z.ZodString>;
34
+ variant: typeof variantZ;
35
+ message: z.ZodString;
36
+ description: z.ZodOptional<z.ZodString>;
37
+ labels: z.ZodOptional<ReturnType<typeof array.nullableZ<typeof label.labelZ>>>;
38
+ time: typeof TimeStamp.z;
39
+ } & ([DetailsSchema] extends [z.ZodNever] ? {} : { details: DetailsSchema })
40
+ >;
36
41
 
37
- interface StatusZFunction {
38
- (): StatusZodObject<z.ZodOptional<z.ZodUnknown>>;
39
- <D extends z.ZodType>(details: D): StatusZodObject<D>;
42
+ export interface StatusZFunction {
43
+ <DetailsSchema extends z.ZodType>(
44
+ details: DetailsSchema,
45
+ ): StatusZodObject<DetailsSchema>;
46
+ <DetailsSchema extends z.ZodType = z.ZodNever>(
47
+ details?: DetailsSchema,
48
+ ): StatusZodObject<DetailsSchema>;
40
49
  }
41
50
 
42
- export const statusZ: StatusZFunction = <D extends z.ZodType>(details?: D) =>
51
+ export const statusZ: StatusZFunction = <DetailsSchema extends z.ZodType>(
52
+ details?: DetailsSchema,
53
+ ) =>
43
54
  z.object({
44
55
  key: z.string(),
56
+ name: z.string().default(""),
45
57
  variant: variantZ,
46
58
  message: z.string(),
47
59
  description: z.string().optional(),
48
60
  time: TimeStamp.z,
61
+ labels: array.nullableZ(label.labelZ).optional(),
49
62
  details: details ?? z.unknown().optional(),
50
63
  });
51
64
 
52
- export type Status<D = undefined, V extends Variant = Variant> = {
65
+ type Base<V extends Variant> = {
53
66
  key: string;
67
+ name: string;
54
68
  variant: V;
55
69
  message: string;
56
70
  description?: string;
57
71
  time: TimeStamp;
58
- } & (D extends undefined ? {} : { details: D });
72
+ labels?: label.Label[];
73
+ };
59
74
 
60
- export type Crude<D = undefined, V extends Variant = Variant> = Optional<
61
- Status<D, V>,
62
- "time" | "key"
63
- >;
75
+ export type Status<DetailsSchema = z.ZodNever, V extends Variant = Variant> = Base<V> &
76
+ ([DetailsSchema] extends [z.ZodNever] ? {} : { details: z.output<DetailsSchema> });
64
77
 
65
- export interface ExceptionDetails {
66
- stack: string;
67
- }
78
+ export type Crude<DetailsSchema = z.ZodNever, V extends Variant = Variant> = Optional<
79
+ Base<V>,
80
+ "key" | "time" | "name"
81
+ > &
82
+ ([DetailsSchema] extends [z.ZodNever] ? {} : { details: z.output<DetailsSchema> });
83
+
84
+ export const exceptionDetailsSchema = z.object({
85
+ stack: z.string(),
86
+ });
68
87
 
69
88
  export const fromException = (
70
89
  exc: unknown,
71
90
  message?: string,
72
- ): Status<ExceptionDetails, "error"> => {
91
+ ): Status<typeof exceptionDetailsSchema, "error"> => {
73
92
  if (!(exc instanceof Error)) throw exc;
74
- return create<ExceptionDetails, "error">({
93
+ return create<typeof exceptionDetailsSchema, "error">({
75
94
  variant: "error",
76
95
  message: message ?? exc.message,
77
96
  description: message != null ? exc.message : undefined,
@@ -81,14 +100,15 @@ export const fromException = (
81
100
  });
82
101
  };
83
102
 
84
- export const create = <D = undefined, V extends Variant = Variant>(
85
- spec: Crude<D, V>,
86
- ): Status<D, V> =>
103
+ export const create = <DetailsSchema = z.ZodNever, V extends Variant = Variant>(
104
+ spec: Crude<DetailsSchema, V>,
105
+ ): Status<DetailsSchema, V> =>
87
106
  ({
88
107
  key: id.create(),
89
108
  time: TimeStamp.now(),
109
+ name: "",
90
110
  ...spec,
91
- }) as unknown as Status<D, V>;
111
+ }) as Status<DetailsSchema, V>;
92
112
 
93
113
  export const keepVariants = (
94
114
  variant?: Variant,
@@ -103,3 +103,45 @@ describe("trimPrefix", () => {
103
103
  it("should handle numbers in prefix", () =>
104
104
  expect(strings.trimPrefix("123abc", "123")).toBe("abc"));
105
105
  });
106
+
107
+ describe("pluralName", () => {
108
+ it("should handle empty string", () => expect(strings.pluralName("")).toBe(""));
109
+
110
+ it("should add 's' to regular words", () => {
111
+ expect(strings.pluralName("cat")).toBe("cats");
112
+ expect(strings.pluralName("dog")).toBe("dogs");
113
+ expect(strings.pluralName("regularType")).toBe("regularTypes");
114
+ });
115
+
116
+ it("should convert 'y' endings to 'ies'", () => {
117
+ expect(strings.pluralName("company")).toBe("companies");
118
+ expect(strings.pluralName("yEndingType")).toBe("yEndingTypes");
119
+ expect(strings.pluralName("baby")).toBe("babies");
120
+ });
121
+
122
+ it("should add 'es' to words ending in 's'", () => {
123
+ expect(strings.pluralName("class")).toBe("classes");
124
+ expect(strings.pluralName("bus")).toBe("buses");
125
+ });
126
+
127
+ it("should add 'es' to words ending in 'x'", () => {
128
+ expect(strings.pluralName("box")).toBe("boxes");
129
+ expect(strings.pluralName("fox")).toBe("foxes");
130
+ });
131
+
132
+ it("should add 'es' to words ending in 'ch'", () => {
133
+ expect(strings.pluralName("catch")).toBe("catches");
134
+ expect(strings.pluralName("church")).toBe("churches");
135
+ });
136
+
137
+ it("should add 'es' to words ending in 'sh'", () => {
138
+ expect(strings.pluralName("bush")).toBe("bushes");
139
+ expect(strings.pluralName("brush")).toBe("brushes");
140
+ });
141
+
142
+ it("should work with built-in type names", () => {
143
+ expect(strings.pluralName("string")).toBe("strings");
144
+ expect(strings.pluralName("number")).toBe("numbers");
145
+ expect(strings.pluralName("object")).toBe("objects");
146
+ });
147
+ });
@@ -108,3 +108,17 @@ export const trimPrefix = (str: string, prefix: string): string => {
108
108
  if (str.startsWith(prefix)) return str.slice(prefix.length);
109
109
  return str;
110
110
  };
111
+
112
+ export const pluralName = (name: string): string => {
113
+ if (name.length === 0) return name;
114
+ if (name[name.length - 1] === "y") return `${name.slice(0, -1)}ies`;
115
+ if (
116
+ name[name.length - 1] === "s" ||
117
+ name[name.length - 1] === "x" ||
118
+ name[name.length - 1] === "z" ||
119
+ name.endsWith("ch") ||
120
+ name.endsWith("sh")
121
+ )
122
+ return `${name}es`;
123
+ return `${name}s`;
124
+ };
@@ -359,13 +359,12 @@ export class Series<T extends TelemValue = TelemValue>
359
359
  data_ = data_.map((v) => new TimeStamp(v as CrudeTimeStamp).valueOf());
360
360
  if (this.dataType.equals(DataType.STRING)) {
361
361
  this.cachedLength = data_.length;
362
- this._data = new TextEncoder().encode(`${data_.join("\n")}\n`)
363
- .buffer as ArrayBuffer;
362
+ this._data = new TextEncoder().encode(`${data_.join("\n")}\n`).buffer;
364
363
  } else if (this.dataType.equals(DataType.JSON)) {
365
364
  this.cachedLength = data_.length;
366
365
  this._data = new TextEncoder().encode(
367
366
  `${data_.map((d) => binary.JSON_CODEC.encodeString(d)).join("\n")}\n`,
368
- ).buffer as ArrayBuffer;
367
+ ).buffer;
369
368
  } else if (this.dataType.usesBigInt && typeof first === "number")
370
369
  this._data = new this.dataType.Array(
371
370
  data_.map((v) => BigInt(Math.round(v as number))),
@@ -73,6 +73,11 @@ describe("TimeStamp", () => {
73
73
  expect(ts.equals(TimeSpan.microseconds(10).add(TimeStamp.utcOffset))).toBe(true);
74
74
  });
75
75
 
76
+ test("constructing from MIN and MAX as numbers", () => {
77
+ expect(new TimeStamp(TimeStamp.MIN.nanoseconds).equals(TimeStamp.MIN)).toBe(true);
78
+ expect(new TimeStamp(TimeStamp.MAX.nanoseconds).equals(TimeStamp.MAX)).toBe(true);
79
+ });
80
+
76
81
  test("construct from time string", () => {
77
82
  const ts = new TimeStamp("12:30", "UTC");
78
83
  expect(ts.date().getUTCHours()).toEqual(12);
@@ -460,7 +465,7 @@ describe("TimeStamp", () => {
460
465
  });
461
466
  });
462
467
 
463
- describe("fString", () => {
468
+ describe("toString with formats", () => {
464
469
  const ts = new TimeStamp([2022, 12, 15], "UTC")
465
470
  .add(TimeSpan.hours(12))
466
471
  .add(TimeSpan.minutes(20))
@@ -479,7 +484,7 @@ describe("TimeStamp", () => {
479
484
 
480
485
  FORMAT_TESTS.forEach(([format, expected]) => {
481
486
  test(`should format timestamp as ${format}`, () => {
482
- expect(ts.fString(format, "UTC")).toEqual(expected);
487
+ expect(ts.toString(format, "UTC")).toEqual(expected);
483
488
  });
484
489
  });
485
490
  });
@@ -925,6 +930,137 @@ describe("TimeSpan", () => {
925
930
  });
926
931
  });
927
932
 
933
+ describe("toString with semantic format", () => {
934
+ const TESTS: [TimeSpan, string][] = [
935
+ // Sub-second durations
936
+ [TimeSpan.ZERO, "0s"],
937
+ [TimeSpan.nanoseconds(50), "< 1s"],
938
+ [TimeSpan.microseconds(50), "< 1s"],
939
+ [TimeSpan.milliseconds(50), "< 1s"],
940
+ [TimeSpan.milliseconds(999), "< 1s"],
941
+
942
+ // Seconds
943
+ [TimeSpan.seconds(1), "1s"],
944
+ [TimeSpan.seconds(30), "30s"],
945
+ [TimeSpan.seconds(59), "59s"],
946
+
947
+ // Minutes with seconds (< 5 minutes)
948
+ [TimeSpan.seconds(60), "1m"],
949
+ [TimeSpan.seconds(90), "1m 30s"],
950
+ [TimeSpan.seconds(119), "1m 59s"],
951
+ [TimeSpan.seconds(120), "2m"],
952
+ [TimeSpan.seconds(150), "2m 30s"],
953
+ [TimeSpan.seconds(240), "4m"],
954
+ [TimeSpan.seconds(270), "4m 30s"],
955
+
956
+ // Minutes without seconds (>= 5 minutes)
957
+ [TimeSpan.seconds(300), "5m"],
958
+ [TimeSpan.seconds(330), "5m"], // seconds dropped
959
+ [TimeSpan.minutes(30), "30m"],
960
+ [TimeSpan.minutes(59), "59m"],
961
+
962
+ // Hours with minutes (< 3 hours)
963
+ [TimeSpan.minutes(60), "1h"],
964
+ [TimeSpan.minutes(90), "1h 30m"],
965
+ [TimeSpan.minutes(119), "1h 59m"],
966
+ [TimeSpan.minutes(120), "2h"],
967
+ [TimeSpan.minutes(150), "2h 30m"],
968
+ [TimeSpan.minutes(179), "2h 59m"],
969
+
970
+ // Hours without minutes (>= 3 hours)
971
+ [TimeSpan.minutes(180), "3h"],
972
+ [TimeSpan.minutes(195), "3h"], // minutes dropped
973
+ [TimeSpan.hours(12), "12h"],
974
+ [TimeSpan.hours(23), "23h"],
975
+
976
+ // Days with hours (< 2 days)
977
+ [TimeSpan.hours(24), "1d"],
978
+ [TimeSpan.hours(25), "1d 1h"],
979
+ [TimeSpan.hours(36), "1d 12h"],
980
+ [TimeSpan.hours(47), "1d 23h"],
981
+
982
+ // Days without hours (>= 2 days)
983
+ [TimeSpan.hours(48), "2d"],
984
+ [TimeSpan.hours(50), "2d"], // hours dropped
985
+ [TimeSpan.days(3), "3d"],
986
+ [TimeSpan.days(6), "6d"],
987
+
988
+ // Weeks with days (< 2 weeks)
989
+ [TimeSpan.days(7), "1w"],
990
+ [TimeSpan.days(8), "1w 1d"],
991
+ [TimeSpan.days(10), "1w 3d"],
992
+ [TimeSpan.days(13), "1w 6d"],
993
+
994
+ // Weeks without days (>= 2 weeks)
995
+ [TimeSpan.days(14), "2w"],
996
+ [TimeSpan.days(15), "2w"], // days dropped
997
+ [TimeSpan.days(21), "3w"],
998
+ [TimeSpan.days(28), "4w"],
999
+
1000
+ // Months with days (< 3 months)
1001
+ [TimeSpan.days(30), "1mo"],
1002
+ [TimeSpan.days(35), "1mo 5d"],
1003
+ [TimeSpan.days(45), "1mo 15d"],
1004
+ [TimeSpan.days(60), "2mo"],
1005
+ [TimeSpan.days(75), "2mo 15d"],
1006
+
1007
+ // Months without days (>= 3 months)
1008
+ [TimeSpan.days(90), "3mo"],
1009
+ [TimeSpan.days(95), "3mo"], // days dropped
1010
+ [TimeSpan.days(180), "6mo"],
1011
+ [TimeSpan.days(330), "11mo"],
1012
+
1013
+ // Years with months (< 2 years)
1014
+ [TimeSpan.days(364), "12mo"],
1015
+ [TimeSpan.days(365), "1y"],
1016
+ [TimeSpan.days(395), "1y 1mo"],
1017
+ [TimeSpan.days(500), "1y 4mo"],
1018
+ [TimeSpan.days(700), "1y 11mo"],
1019
+
1020
+ // Years without months (>= 2 years)
1021
+ [TimeSpan.days(730), "2y"],
1022
+ [TimeSpan.days(750), "2y"], // months dropped
1023
+ [TimeSpan.days(1095), "3y"],
1024
+ [TimeSpan.days(3650), "10y"],
1025
+
1026
+ // Complex durations
1027
+ [TimeSpan.seconds(3661), "1h 1m"],
1028
+ [TimeSpan.minutes(1441), "1d"], // 24h 1m, but minutes are dropped at day level
1029
+ [TimeSpan.minutes(1500), "1d 1h"], // 25h exactly
1030
+ [TimeSpan.hours(169), "1w 1h"],
1031
+
1032
+ // Negative durations
1033
+ [TimeSpan.seconds(-30), "-30s"],
1034
+ [TimeSpan.minutes(-90), "-1h 30m"],
1035
+ [TimeSpan.hours(-25), "-1d 1h"],
1036
+ [TimeSpan.days(-8), "-1w 1d"],
1037
+ [TimeSpan.days(-400), "-1y 1mo"],
1038
+ ];
1039
+
1040
+ TESTS.forEach(([ts, expected]) => {
1041
+ test(`${ts.valueOf()} => ${expected}`, () => {
1042
+ expect(ts.toString("semantic")).toEqual(expected);
1043
+ });
1044
+ });
1045
+ });
1046
+
1047
+ describe("toString with format", () => {
1048
+ test("toString with semantic format", () => {
1049
+ const ts = TimeSpan.hours(25);
1050
+ expect(ts.toString("semantic")).toEqual("1d 1h");
1051
+ });
1052
+
1053
+ test("toString with default format", () => {
1054
+ const ts = TimeSpan.hours(25);
1055
+ expect(ts.toString()).toEqual("1d 1h");
1056
+ });
1057
+
1058
+ test("toString with full format", () => {
1059
+ const ts = TimeSpan.hours(25).add(TimeSpan.minutes(30)).add(TimeSpan.seconds(15));
1060
+ expect(ts.toString("full")).toEqual("1d 1h 30m 15s");
1061
+ });
1062
+ });
1063
+
928
1064
  describe("schema", () => {
929
1065
  it("should parse bigint", () => {
930
1066
  const ts = TimeSpan.z.parse(1000000000n);
@@ -1632,28 +1768,29 @@ describe("DataType", () => {
1632
1768
  });
1633
1769
 
1634
1770
  const testCases = [
1635
- { input: "int8", expected: "int8" },
1636
- { input: "int16", expected: "int16" },
1637
- { input: "int32", expected: "int32" },
1638
- { input: "int64", expected: "int64" },
1639
- { input: "uint8", expected: "uint8" },
1640
- { input: "uint16", expected: "uint16" },
1641
- { input: "uint32", expected: "uint32" },
1642
- { input: "uint64", expected: "uint64" },
1643
- { input: "float32", expected: "float32" },
1644
- { input: "float64", expected: "float64" },
1645
- { input: "string", expected: "string" },
1646
- { input: "boolean", expected: "boolean" },
1647
- { input: "timestamp", expected: "timestamp" },
1648
- { input: "uuid", expected: "uuid" },
1649
- { input: "json", expected: "json" },
1771
+ { input: "int8", expected: "int8", short: "i8" },
1772
+ { input: "int16", expected: "int16", short: "i16" },
1773
+ { input: "int32", expected: "int32", short: "i32" },
1774
+ { input: "int64", expected: "int64", short: "i64" },
1775
+ { input: "uint8", expected: "uint8", short: "u8" },
1776
+ { input: "uint16", expected: "uint16", short: "u16" },
1777
+ { input: "uint32", expected: "uint32", short: "u32" },
1778
+ { input: "uint64", expected: "uint64", short: "u64" },
1779
+ { input: "float32", expected: "float32", short: "f32" },
1780
+ { input: "float64", expected: "float64", short: "f64" },
1781
+ { input: "string", expected: "string", short: "str" },
1782
+ { input: "boolean", expected: "boolean", short: "bool" },
1783
+ { input: "timestamp", expected: "timestamp", short: "ts" },
1784
+ { input: "uuid", expected: "uuid", short: "uuid" },
1785
+ { input: "json", expected: "json", short: "json" },
1650
1786
  ];
1651
1787
 
1652
- testCases.forEach(({ input, expected }) => {
1788
+ testCases.forEach(({ input, expected, short }) => {
1653
1789
  it(`should parse "${input}" to DataType with value "${expected}"`, () => {
1654
1790
  const dt = DataType.z.parse(input);
1655
1791
  expect(dt).toBeInstanceOf(DataType);
1656
1792
  expect(dt.toString()).toBe(expected);
1793
+ expect(dt.toString(true)).toBe(short);
1657
1794
  });
1658
1795
  });
1659
1796
  });