@synnaxlabs/x 0.44.1 → 0.44.3

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 (80) hide show
  1. package/.turbo/turbo-build.log +19 -19
  2. package/dist/bounds-8OC_obRs.js +186 -0
  3. package/dist/bounds-CRK04jp7.cjs +1 -0
  4. package/dist/bounds.cjs +1 -1
  5. package/dist/bounds.js +1 -1
  6. package/dist/compare.cjs +1 -1
  7. package/dist/compare.js +1 -1
  8. package/dist/deep.cjs +1 -1
  9. package/dist/deep.js +56 -46
  10. package/dist/{external-BFgJjMcS.js → external-BM_NS5yM.js} +6 -6
  11. package/dist/external-E3ErJeeM.cjs +1 -0
  12. package/dist/{index-PNh31WTW.js → index-CBMHFqs4.js} +34 -33
  13. package/dist/index-DOJlZHqJ.cjs +1 -0
  14. package/dist/index.cjs +3 -3
  15. package/dist/index.js +235 -229
  16. package/dist/path-Blh4wJuA.js +110 -0
  17. package/dist/path-CPSfCjde.cjs +1 -0
  18. package/dist/{scale-C6qKDbRb.cjs → scale-C3fEtXxW.cjs} +1 -1
  19. package/dist/{scale-EWNUk-bn.js → scale-Db1Gunj0.js} +1 -1
  20. package/dist/scale.cjs +1 -1
  21. package/dist/scale.js +1 -1
  22. package/dist/series-BcF7A8Je.cjs +6 -0
  23. package/dist/{series-Bn5y1tD1.js → series-Cl3Vh_u-.js} +588 -471
  24. package/dist/spatial.cjs +1 -1
  25. package/dist/spatial.js +2 -2
  26. package/dist/src/breaker/breaker.d.ts +2 -1
  27. package/dist/src/breaker/breaker.d.ts.map +1 -1
  28. package/dist/src/deep/copy.d.ts.map +1 -1
  29. package/dist/src/deep/path.d.ts +1 -0
  30. package/dist/src/deep/path.d.ts.map +1 -1
  31. package/dist/src/id/id.d.ts +3 -1
  32. package/dist/src/id/id.d.ts.map +1 -1
  33. package/dist/src/math/math.d.ts +2 -1
  34. package/dist/src/math/math.d.ts.map +1 -1
  35. package/dist/src/observe/observe.d.ts +1 -0
  36. package/dist/src/observe/observe.d.ts.map +1 -1
  37. package/dist/src/primitive/primitive.d.ts +13 -1
  38. package/dist/src/primitive/primitive.d.ts.map +1 -1
  39. package/dist/src/status/status.d.ts +1 -0
  40. package/dist/src/status/status.d.ts.map +1 -1
  41. package/dist/src/strings/strings.d.ts +1 -1
  42. package/dist/src/strings/strings.d.ts.map +1 -1
  43. package/dist/src/telem/series.d.ts +7 -0
  44. package/dist/src/telem/series.d.ts.map +1 -1
  45. package/dist/src/telem/telem.d.ts +78 -1
  46. package/dist/src/telem/telem.d.ts.map +1 -1
  47. package/dist/src/zod/util.d.ts.map +1 -1
  48. package/dist/telem.cjs +1 -1
  49. package/dist/telem.js +1 -1
  50. package/dist/zod.cjs +1 -1
  51. package/dist/zod.js +1 -1
  52. package/package.json +1 -1
  53. package/src/breaker/breaker.ts +4 -0
  54. package/src/deep/copy.ts +12 -1
  55. package/src/deep/path.spec.ts +41 -0
  56. package/src/deep/path.ts +19 -2
  57. package/src/id/id.ts +8 -4
  58. package/src/math/math.spec.ts +20 -0
  59. package/src/math/math.ts +32 -29
  60. package/src/observe/observe.ts +2 -0
  61. package/src/primitive/primitive.spec.ts +7 -4
  62. package/src/primitive/primitive.ts +17 -1
  63. package/src/spatial/bounds/bounds.ts +1 -1
  64. package/src/status/status.ts +11 -0
  65. package/src/strings/strings.spec.ts +3 -0
  66. package/src/strings/strings.ts +2 -1
  67. package/src/telem/series.spec.ts +543 -2
  68. package/src/telem/series.ts +28 -9
  69. package/src/telem/telem.spec.ts +556 -0
  70. package/src/telem/telem.ts +118 -5
  71. package/src/testutil/testutil.spec.ts +6 -3
  72. package/src/zod/util.ts +5 -3
  73. package/tsconfig.tsbuildinfo +1 -1
  74. package/dist/bounds-D6e9xoHt.cjs +0 -1
  75. package/dist/bounds-Dj9nG39I.js +0 -174
  76. package/dist/external-mffsMzJx.cjs +0 -1
  77. package/dist/index-B1m2qvBT.cjs +0 -1
  78. package/dist/path-BXdMenka.js +0 -101
  79. package/dist/path-st_E5-LJ.cjs +0 -1
  80. package/dist/series-CYAhDD6B.cjs +0 -6
package/src/deep/path.ts CHANGED
@@ -126,6 +126,16 @@ const defaultGetter = (obj: record.Unknown, key: string): unknown => {
126
126
  return undefined;
127
127
  };
128
128
 
129
+ export const resolvePath = <T = record.Unknown>(path: string, obj: T): string => {
130
+ const parts = path.split(".");
131
+ parts.forEach((part, i) => {
132
+ obj = defaultGetter(obj as record.Unknown, part) as T;
133
+ if (obj != null && typeof obj === "object" && "key" in obj)
134
+ parts[i] = obj.key as string;
135
+ });
136
+ return parts.join(".");
137
+ };
138
+
129
139
  /**
130
140
  * Gets the value at the given path on the object. If the path does not exist
131
141
  * and the optional flag is set to true, null will be returned. If the path does
@@ -158,6 +168,12 @@ export const get = (<V = record.Unknown, T = record.Unknown>(
158
168
  return result as V;
159
169
  }) as Get;
160
170
 
171
+ const getIndex = (part: string): number | null => {
172
+ // in order to be considered an index, all characters must be numbers
173
+ for (const char of part) if (isNaN(parseInt(char))) return null;
174
+ return parseInt(part);
175
+ };
176
+
161
177
  /**
162
178
  * Sets the value at the given path on the object. If the parents of the deep path
163
179
  * do not exist, new objects will be created.
@@ -180,8 +196,9 @@ export const set = <V>(obj: V, path: string, value: unknown): void => {
180
196
  return;
181
197
  }
182
198
  if (result.length === 0) return;
183
- const index = parseInt(parts[parts.length - 1]);
184
- if (isNaN(index)) {
199
+ const index = getIndex(parts[parts.length - 1]);
200
+ // If we can't parse an index, try to interpret it as an object key.
201
+ if (index == null) {
185
202
  const first = result[0];
186
203
  if (typeof first === "object" && "key" in first) {
187
204
  const objIndex = result.findIndex((o) => o.key === parts[parts.length - 1]);
package/src/id/id.ts CHANGED
@@ -9,15 +9,19 @@
9
9
 
10
10
  import { customAlphabet } from "nanoid/non-secure";
11
11
 
12
- const ALPHANUMERIC = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
12
+ const ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
13
+ const ALPHANUMERIC = `0123456789${ALPHABET}`;
13
14
 
14
15
  export const LENGTH = 11;
15
16
 
16
- const createInternal = customAlphabet(ALPHANUMERIC, LENGTH);
17
+ const createPrefix = customAlphabet(ALPHABET, 1);
18
+ const createInternal = customAlphabet(ALPHANUMERIC, LENGTH - 1);
17
19
 
18
20
  /**
19
- * Creates a unique alphanumeric string of length 11.
21
+ * Creates a unique alphanumeric string of length 11. The returned id always begins
22
+ * with a letter to disambiguate from values that can be potentially interpreted as
23
+ * numbers.
20
24
  *
21
25
  * @returns {string} A unique alphanumeric string.
22
26
  */
23
- export const create = (): string => createInternal();
27
+ export const create = (): string => `${createPrefix()}${createInternal()}`;
@@ -73,6 +73,10 @@ describe("math", () => {
73
73
  it("should return false if a bigint and a number are not equal", () => {
74
74
  expect(math.equal(1n, 2)).toBe(false);
75
75
  });
76
+ it("should handle bigint a and decimal b correctly", () => {
77
+ expect(math.equal(5n, 5.5)).toBe(false);
78
+ expect(math.equal(5n, 5.0)).toBe(true);
79
+ });
76
80
  });
77
81
  describe("roundToNearestMagnitude", () => {
78
82
  it("should round to the nearest magnitude of 10", () => {
@@ -94,6 +98,10 @@ describe("math", () => {
94
98
  it("should return the minimum of a bigint and a number", () => {
95
99
  expect(math.min(1n, 2)).toBe(1n);
96
100
  });
101
+ it("should handle bigint a and decimal b correctly", () => {
102
+ expect(math.min(10n, 5.5)).toBe(6n);
103
+ expect(math.min(3n, 7.8)).toBe(3n);
104
+ });
97
105
  });
98
106
  describe("max", () => {
99
107
  it("should return the maximum of two numbers", () => {
@@ -108,6 +116,10 @@ describe("math", () => {
108
116
  it("should return the maximum of a bigint and a number", () => {
109
117
  expect(math.max(1n, 2)).toBe(2n);
110
118
  });
119
+ it("should handle bigint a and decimal b correctly", () => {
120
+ expect(math.max(3n, 5.5)).toBe(6n);
121
+ expect(math.max(10n, 7.8)).toBe(10n);
122
+ });
111
123
  });
112
124
  describe("abs", () => {
113
125
  it("should return the absolute value of a number", () => {
@@ -130,6 +142,10 @@ describe("math", () => {
130
142
  it("should multiply a bigint and a number", () => {
131
143
  expect(math.mult(1n, 2)).toBe(2n);
132
144
  });
145
+ it("should handle bigint a and decimal b correctly", () => {
146
+ expect(math.mult(3n, 2.5)).toBe(8n);
147
+ expect(math.mult(4n, 0.5)).toBe(2n);
148
+ });
133
149
  });
134
150
 
135
151
  describe("div", () => {
@@ -145,5 +161,9 @@ describe("math", () => {
145
161
  it("should divide a bigint and a number", () => {
146
162
  expect(math.div(2n, 1)).toBe(2n);
147
163
  });
164
+ it("should handle bigint a and decimal b correctly", () => {
165
+ expect(math.div(10n, 2.5)).toBe(4n);
166
+ expect(math.div(8n, 0.5)).toBe(16n);
167
+ });
148
168
  });
149
169
  });
package/src/math/math.ts CHANGED
@@ -10,27 +10,38 @@
10
10
  /** A numeric value is either a a number, or a bigint. */
11
11
  export type Numeric = number | bigint;
12
12
 
13
+ const multiCoercedOp =
14
+ (func: (a: number, b: number) => Numeric) =>
15
+ <V extends Numeric>(a: V, b: Numeric): V => {
16
+ if (typeof a === "bigint") {
17
+ if (isInteger(b))
18
+ return func(a as unknown as number, BigInt(b) as unknown as number) as V;
19
+ const res = func(Number(a), Number(b)) as number;
20
+ if (typeof res === "number") return BigInt(Math.round(res)) as V;
21
+ return res;
22
+ }
23
+ return func(Number(a), Number(b)) as V;
24
+ };
25
+
13
26
  /**
14
27
  * @returns the product of a and b, coercing b to the type of a if necessary. */
15
- export const sub = <V extends Numeric>(a: V, b: Numeric): V => {
16
- if (typeof a === "bigint") return (a - BigInt(b)) as V;
17
- return (a - Number(b)) as V;
18
- };
28
+ export const sub = multiCoercedOp((a, b) => a - b);
19
29
 
20
30
  /** @returns the sum of a and b, coercing b to the type of a if necessary. */
21
- export const add = <V extends Numeric>(a: V, b: Numeric): V => {
22
- if (typeof a === "bigint") return (a + BigInt(b)) as V;
23
- return ((a as unknown as number) + Number(b)) as V;
24
- };
31
+ export const add = multiCoercedOp((a, b) => a + b);
25
32
 
26
33
  /** @returns true if a is close to b within epsilon. */
27
34
  export const closeTo = (a: number, b: number, epsilon = 0.0001): boolean =>
28
35
  Math.abs(a - b) < epsilon;
29
36
 
30
37
  /** @returns true if a is equal to b, coercing b to the type of a if necessary. */
31
- export const equal = <V extends Numeric>(a: V, b: Numeric): boolean => {
32
- if (typeof a === "bigint") return a === BigInt(b);
33
- return a === Number(b);
38
+ export const equal = (a: Numeric, b: Numeric): boolean => {
39
+ const aIsBigInt = typeof a === "bigint";
40
+ const bIsBigInt = typeof b === "bigint";
41
+ if (aIsBigInt && bIsBigInt) return a === b;
42
+ if (aIsBigInt && isInteger(b)) return a === BigInt(b);
43
+ if (bIsBigInt && isInteger(a)) return b === BigInt(a);
44
+ return a === b;
34
45
  };
35
46
 
36
47
  /**
@@ -45,32 +56,24 @@ export const roundToNearestMagnitude = (num: number): number => {
45
56
  };
46
57
 
47
58
  /** @returns the minimum of a and b, coercing b to the type of a if necessary. */
48
- export const min = <V extends Numeric>(a: V, b: Numeric): V => {
49
- if (a <= b) return a;
50
- if (typeof a === "bigint") return (a <= b ? a : BigInt(b)) as V;
51
- return (a <= b ? a : Number(b)) as V;
59
+ export const min = multiCoercedOp((a, b) => (a <= b ? a : b));
60
+
61
+ export const isInteger = (a: Numeric): boolean => {
62
+ if (typeof a === "bigint") return true;
63
+ return Number.isInteger(a);
52
64
  };
53
65
 
54
66
  /** @returns the maximum of a and b, coercing b to the type of a if necessary. */
55
- export const max = <V extends Numeric>(a: V, b: Numeric): V => {
56
- if (typeof a === "bigint") return (a >= b ? a : BigInt(b)) as V;
57
- return (a >= b ? a : Number(b)) as V;
58
- };
67
+ export const max = multiCoercedOp((a, b) => (a >= b ? a : b));
59
68
 
60
69
  /** @returns the absolute value of a. */
61
70
  export const abs = <V extends Numeric>(a: V): V => {
62
- if (a < 0) return -a as V;
63
- return a;
71
+ if (typeof a === "bigint") return (a < 0n ? -a : a) as V;
72
+ return (a < 0 ? -a : a) as V;
64
73
  };
65
74
 
66
75
  /** @returns the multiplication of a and b, coercing b to the type of a if necessary. */
67
- export const mult = <V extends Numeric>(a: V, b: Numeric): V => {
68
- if (typeof a === "bigint") return (a * BigInt(b)) as V;
69
- return (a * Number(b)) as V;
70
- };
76
+ export const mult = multiCoercedOp((a, b) => a * b);
71
77
 
72
78
  /** @returns the division of a and b, coercing b to the type of a if necessary. */
73
- export const div = <V extends Numeric>(a: V, b: Numeric): V => {
74
- if (typeof a === "bigint") return (a / BigInt(b)) as V;
75
- return (a / Number(b)) as V;
76
- };
79
+ export const div = multiCoercedOp((a, b) => a / b);
@@ -12,6 +12,8 @@ import { type Destructor } from "@/destructor";
12
12
  /** Handler is called when the value of an Observable changes. */
13
13
  export type Handler<T> = (value: T) => void;
14
14
 
15
+ export type AsyncHandler<T> = (value: T) => Promise<void>;
16
+
15
17
  /** A generic interface for an entity whose value can be observed when it changes. */
16
18
  export interface Observable<T> {
17
19
  /**
@@ -7,11 +7,12 @@
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
- import { describe, expect, it } from "vitest";
10
+ import { describe, expect, it, test } from "vitest";
11
11
  import { z } from "zod";
12
12
 
13
13
  import { binary } from "@/binary";
14
14
  import { primitive } from "@/primitive";
15
+ import { testutil } from "@/testutil";
15
16
 
16
17
  class ExampleStringer implements primitive.Stringer {
17
18
  readonly value: string;
@@ -25,7 +26,7 @@ class ExampleStringer implements primitive.Stringer {
25
26
  }
26
27
 
27
28
  describe("primitive", () => {
28
- describe("isZero", () => {
29
+ describe("isZero and isNonZero", () => {
29
30
  interface Spec {
30
31
  value: primitive.Value;
31
32
  expected: boolean;
@@ -42,10 +43,12 @@ describe("primitive", () => {
42
43
  { value: new ExampleStringer("cat"), expected: false },
43
44
  ];
44
45
  SPECS.forEach(({ value, expected }) => {
45
- // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
46
- it(`should return ${expected} for ${value} ${typeof value}`, () => {
46
+ test(`isZero should return ${expected} for ${testutil.toString(value)}`, () => {
47
47
  expect(primitive.isZero(value)).toEqual(expected);
48
48
  });
49
+ test(`isNonZero should return ${!expected} for ${testutil.toString(value)}`, () => {
50
+ expect(primitive.isNonZero(value)).toEqual(!expected);
51
+ });
49
52
  });
50
53
  });
51
54
 
@@ -52,6 +52,16 @@ export interface Stringer {
52
52
  export const isStringer = (value: unknown): boolean =>
53
53
  value != null && typeof value === "object" && "toString" in value;
54
54
 
55
+ /**
56
+ * Type representing zero values for each primitive type
57
+ */
58
+ export type ZeroValue = "" | 0 | 0n | false | null | undefined;
59
+
60
+ /**
61
+ * Type representing non-zero values for each primitive type
62
+ */
63
+ export type NonZeroValue = Exclude<Value, ZeroValue>;
64
+
55
65
  /**
56
66
  * @returns true if the given primitive is the zero value for its type.
57
67
  * For strings value == ""
@@ -61,7 +71,7 @@ export const isStringer = (value: unknown): boolean =>
61
71
  * For objects value == null
62
72
  * For undefined returns true
63
73
  */
64
- export const isZero = <V extends Value>(value: V): boolean => {
74
+ export const isZero = <V extends Value>(value: V): value is V & ZeroValue => {
65
75
  if (isStringer(value)) return value?.toString().length === 0;
66
76
  switch (typeof value) {
67
77
  case "string":
@@ -80,3 +90,9 @@ export const isZero = <V extends Value>(value: V): boolean => {
80
90
  return false;
81
91
  }
82
92
  };
93
+
94
+ /**
95
+ * Type predicate function that narrows to non-zero values
96
+ */
97
+ export const isNonZero = <V extends Value>(value: V): value is V & NonZeroValue =>
98
+ !isZero(value);
@@ -615,7 +615,7 @@ export const traverse = <T extends numeric.Value = number>(
615
615
  currentPosition,
616
616
  dir > 0 ? moveDist : -moveDist,
617
617
  ) as T;
618
- remainingDist = math.sub(remainingDist, dir > 0 ? moveDist : -moveDist);
618
+ remainingDist = math.sub<T>(remainingDist, dir > 0 ? moveDist : -moveDist);
619
619
 
620
620
  // If we've exhausted the distance, return the current position
621
621
  if (math.equal(remainingDist, 0)) return currentPosition as T;
@@ -89,3 +89,14 @@ export const create = <D = undefined, V extends Variant = Variant>(
89
89
  time: TimeStamp.now(),
90
90
  ...spec,
91
91
  }) as unknown as Status<D, V>;
92
+
93
+ export const filterVariant = (
94
+ variant: Variant,
95
+ only: Variant | Variant[] = [],
96
+ ): Variant | undefined => {
97
+ if (Array.isArray(only)) {
98
+ if (only.includes(variant)) return variant;
99
+ return undefined;
100
+ }
101
+ return only === variant ? variant : undefined;
102
+ };
@@ -15,6 +15,9 @@ describe("naturalLanguageJoin", () => {
15
15
  it("should return an empty string for an empty array", () =>
16
16
  expect(strings.naturalLanguageJoin([])).toBe(""));
17
17
 
18
+ it("should return the string for a single string", () =>
19
+ expect(strings.naturalLanguageJoin("apple")).toBe("apple"));
20
+
18
21
  it("should return the zeroLength string for an empty array if provided", () =>
19
22
  expect(strings.naturalLanguageJoin([], "No items")).toBe("No items"));
20
23
 
@@ -24,9 +24,10 @@
24
24
  * ```
25
25
  */
26
26
  export const naturalLanguageJoin = (
27
- strings: string[],
27
+ strings: string | string[],
28
28
  zeroLength: string = "",
29
29
  ): string => {
30
+ if (typeof strings === "string") return strings;
30
31
  const length = strings.length;
31
32
  if (length === 0) return zeroLength;
32
33
  if (length === 1) return strings[0];