@synnaxlabs/x 0.26.7 → 0.28.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 (92) hide show
  1. package/.turbo/turbo-build.log +20 -18
  2. package/dist/compare.cjs +1 -1
  3. package/dist/compare.js +1 -1
  4. package/dist/deep.cjs +1 -1
  5. package/dist/deep.js +39 -39
  6. package/dist/{index-DCwgKLH_.js → index-DPWtMAJa.js} +12 -8
  7. package/dist/index-DYgqYo6X.cjs +1 -0
  8. package/dist/index.cjs +2 -2
  9. package/dist/index.js +172 -158
  10. package/dist/kv.cjs +1 -1
  11. package/dist/kv.js +2 -4
  12. package/dist/observe.cjs +1 -1
  13. package/dist/observe.js +26 -16
  14. package/dist/path-577Fmn5N.cjs +1 -0
  15. package/dist/path-B-1-i3qC.js +72 -0
  16. package/dist/{series-CaeAWaDJ.js → series-DIZdmPCa.js} +65 -50
  17. package/dist/{series-CNcMldp_.cjs → series-DMsaUp_n.cjs} +7 -7
  18. package/dist/src/binary/encoder.d.ts +0 -1
  19. package/dist/src/change/change.d.ts +0 -1
  20. package/dist/src/clamp/clamp.d.ts +0 -1
  21. package/dist/src/compare/compare.d.ts +5 -1
  22. package/dist/src/compare/compare.d.ts.map +1 -1
  23. package/dist/src/control/control.d.ts +0 -1
  24. package/dist/src/control/index.d.ts.map +1 -1
  25. package/dist/src/deep/delete.d.ts +0 -1
  26. package/dist/src/deep/equal.d.ts +0 -1
  27. package/dist/src/deep/merge.d.ts +0 -1
  28. package/dist/src/deep/path.d.ts +78 -3
  29. package/dist/src/deep/path.d.ts.map +1 -1
  30. package/dist/src/errors/errors.d.ts.map +1 -1
  31. package/dist/src/errors/index.d.ts.map +1 -1
  32. package/dist/src/id/id.d.ts +0 -1
  33. package/dist/src/identity.d.ts +0 -1
  34. package/dist/src/kv/types.d.ts +16 -0
  35. package/dist/src/kv/types.d.ts.map +1 -1
  36. package/dist/src/migrate/migrate.d.ts +34 -5
  37. package/dist/src/migrate/migrate.d.ts.map +1 -1
  38. package/dist/src/mock/MockGLBufferController.d.ts +5 -6
  39. package/dist/src/mock/MockGLBufferController.d.ts.map +1 -1
  40. package/dist/src/numeric/index.d.ts.map +1 -1
  41. package/dist/src/numeric/numeric.d.ts.map +1 -1
  42. package/dist/src/observe/observe.d.ts +24 -2
  43. package/dist/src/observe/observe.d.ts.map +1 -1
  44. package/dist/src/primitive.d.ts +1 -2
  45. package/dist/src/primitive.d.ts.map +1 -1
  46. package/dist/src/record.d.ts +0 -1
  47. package/dist/src/renderable.d.ts +0 -1
  48. package/dist/src/runtime/os.d.ts +0 -1
  49. package/dist/src/search.d.ts +0 -1
  50. package/dist/src/spatial/base.d.ts +0 -1
  51. package/dist/src/spatial/bounds/bounds.d.ts +0 -1
  52. package/dist/src/spatial/box/box.d.ts +0 -1
  53. package/dist/src/spatial/dimensions/dimensions.d.ts +0 -1
  54. package/dist/src/spatial/direction/direction.d.ts +0 -1
  55. package/dist/src/spatial/location/location.d.ts +0 -1
  56. package/dist/src/spatial/position/position.d.ts +0 -1
  57. package/dist/src/spatial/scale/scale.d.ts +0 -1
  58. package/dist/src/spatial/xy/xy.d.ts +0 -1
  59. package/dist/src/telem/generate.d.ts +0 -1
  60. package/dist/src/telem/series.d.ts +0 -1
  61. package/dist/src/telem/telem.d.ts +19 -1
  62. package/dist/src/telem/telem.d.ts.map +1 -1
  63. package/dist/src/zodutil/zodutil.d.ts +0 -1
  64. package/dist/telem.cjs +1 -1
  65. package/dist/telem.js +1 -1
  66. package/dist/types-BpAJW2TM.js +11 -0
  67. package/dist/types-zRwnQ1hc.cjs +1 -0
  68. package/dist/zodutil.cjs +1 -1
  69. package/dist/zodutil.js +1 -1
  70. package/package.json +3 -3
  71. package/src/caseconv/caseconv.spec.ts +9 -0
  72. package/src/compare/compare.ts +6 -0
  73. package/src/control/index.ts +9 -0
  74. package/src/deep/path.spec.ts +29 -0
  75. package/src/deep/path.ts +102 -10
  76. package/src/errors/errors.ts +9 -0
  77. package/src/errors/index.ts +9 -0
  78. package/src/kv/types.ts +13 -0
  79. package/src/migrate/migrate.spec.ts +78 -0
  80. package/src/migrate/migrate.ts +69 -9
  81. package/src/mock/MockGLBufferController.ts +7 -7
  82. package/src/numeric/index.ts +9 -0
  83. package/src/numeric/numeric.ts +9 -0
  84. package/src/observe/observe.ts +30 -1
  85. package/src/primitive.ts +1 -1
  86. package/src/telem/series.spec.ts +14 -0
  87. package/src/telem/series.ts +1 -1
  88. package/src/telem/telem.ts +26 -0
  89. package/tsconfig.tsbuildinfo +1 -1
  90. package/dist/index-CvHP07Bs.cjs +0 -1
  91. package/dist/path-Coj1BKMh.cjs +0 -1
  92. package/dist/path-DH9iaDDf.js +0 -58
package/dist/telem.js CHANGED
@@ -1,4 +1,4 @@
1
- import { d as s, D as i, M as t, R as r, S, e as m, c as p, b as T, T as d, a as y, g as l, i as n, f as D, t as o } from "./series-CaeAWaDJ.js";
1
+ import { d as s, D as i, M as t, R as r, S, e as m, c as p, b as T, T as d, a as y, g as l, i as n, f as D, t as o } from "./series-DIZdmPCa.js";
2
2
  export {
3
3
  s as DataType,
4
4
  i as Density,
@@ -0,0 +1,11 @@
1
+ import { z as t } from "zod";
2
+ const e = t.object({
3
+ key: t.string(),
4
+ value: t.string()
5
+ }), r = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
6
+ __proto__: null,
7
+ stringPairZ: e
8
+ }, Symbol.toStringTag, { value: "Module" }));
9
+ export {
10
+ r as t
11
+ };
@@ -0,0 +1 @@
1
+ "use strict";const e=require("zod"),t=e.z.object({key:e.z.string(),value:e.z.string()}),r=Object.freeze(Object.defineProperty({__proto__:null,stringPairZ:t},Symbol.toStringTag,{value:"Module"}));exports.types=r;
package/dist/zodutil.cjs CHANGED
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const o=require("./path-Coj1BKMh.cjs"),c=e=>o.transformPath(e,(t,r,n)=>{const u=r===n.length-1,i=!isNaN(parseInt(t)),l=!isNaN(parseInt(n[r+1]));return i&&(t="element"),u||l?t:[t,"shape"]}),s=(e,t)=>{if(e==null)return null;const r=e[t];return r==null&&typeof e=="object"&&"sourceType"in e?e.sourceType()[t]:r},a=(e,t,r)=>o.get(s(e,"shape"),c(t),{...r,getter:s}),p=Object.freeze(Object.defineProperty({__proto__:null,getFieldSchema:a,getFieldSchemaPath:c},Symbol.toStringTag,{value:"Module"}));exports.zodutil=p;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const o=require("./path-577Fmn5N.cjs"),c=e=>o.transformPath(e,(t,r,n)=>{const u=r===n.length-1,i=!isNaN(parseInt(t)),l=!isNaN(parseInt(n[r+1]));return i&&(t="element"),u||l?t:[t,"shape"]}),s=(e,t)=>{if(e==null)return null;const r=e[t];return r==null&&typeof e=="object"&&"sourceType"in e?e.sourceType()[t]:r},a=(e,t,r)=>o.get(s(e,"shape"),c(t),{...r,getter:s}),p=Object.freeze(Object.defineProperty({__proto__:null,getFieldSchema:a,getFieldSchemaPath:c},Symbol.toStringTag,{value:"Module"}));exports.zodutil=p;
package/dist/zodutil.js CHANGED
@@ -1,4 +1,4 @@
1
- import { t as l, g as a } from "./path-DH9iaDDf.js";
1
+ import { t as l, g as a } from "./path-B-1-i3qC.js";
2
2
  const o = (e) => l(e, (t, r, n) => {
3
3
  const c = r === n.length - 1, u = !isNaN(parseInt(t)), i = !isNaN(parseInt(n[r + 1]));
4
4
  return u && (t = "element"), c || i ? t : [t, "shape"];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@synnaxlabs/x",
3
- "version": "0.26.7",
3
+ "version": "0.28.0",
4
4
  "type": "module",
5
5
  "description": "Common Utilities for Synnax Labs",
6
6
  "repository": "https://github.com/synnaxlabs/synnax/tree/main/x/go",
@@ -24,8 +24,8 @@
24
24
  "vite": "5.3.3",
25
25
  "vitest": "^1.6.0",
26
26
  "@synnaxlabs/tsconfig": "0.0.2",
27
- "eslint-config-synnaxlabs": "0.0.1",
28
- "@synnaxlabs/vite-plugin": "0.0.1"
27
+ "@synnaxlabs/vite-plugin": "0.0.1",
28
+ "eslint-config-synnaxlabs": "0.0.1"
29
29
  },
30
30
  "main": "./dist/index.cjs",
31
31
  "module": "./dist/index.js",
@@ -1,3 +1,12 @@
1
+ // Copyright 2024 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
+
1
10
  import { describe, expect, it } from "vitest";
2
11
 
3
12
  import { caseconv } from "@/caseconv";
@@ -115,12 +115,18 @@ export const LESS_THAN = -1;
115
115
  /** The greater than return value of a compare function. */
116
116
  export const GREATER_THAN = 1;
117
117
 
118
+ /** @returns true if the result of the comparison is less than 0. */
118
119
  export const isLessThan = (n: number): boolean => n < EQUAL;
119
120
 
121
+ /** @returns true if the result of the comparison is greater than 0. */
120
122
  export const isGreaterThan = (n: number): boolean => n > EQUAL;
121
123
 
124
+ /** @returns true if the result of the comparison is equal to 0. */
122
125
  export const isGreaterThanEqual = (n: number): boolean => n >= EQUAL;
123
126
 
127
+ /** @returns true if the result of the comparison is equal to 0. */
128
+ export const isEqualTo = (n: number): boolean => n === EQUAL;
129
+
124
130
  export const stringsWithNumbers = (a: string, b: string): number => {
125
131
  const alphaNumericRegex = /([a-zA-Z]+)|(\d+)/g;
126
132
 
@@ -1 +1,10 @@
1
+ // Copyright 2024 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
+
1
10
  export * as control from "@/control/control";
@@ -144,4 +144,33 @@ describe("path", () => {
144
144
  expect(deep.pathsMatch("a.b.c", "")).toEqual(true);
145
145
  });
146
146
  });
147
+ describe("delete", () => {
148
+ const a: TestRecord = {
149
+ a: 1,
150
+ b: {
151
+ c: 2,
152
+ },
153
+ c: [1, 2, 3],
154
+ };
155
+ it("should delete a key", () => {
156
+ const cpy = deep.copy(a);
157
+ deep.remove(a, "b.c");
158
+ expect(a).toEqual({ ...cpy, b: {} });
159
+ });
160
+ it("should delete an array index", () => {
161
+ const cpy = deep.copy(a);
162
+ deep.remove(a, "c.1");
163
+ expect(a).toEqual({ ...cpy, c: [1, 3] });
164
+ });
165
+ it("should not throw an error when the index is out of bounds", () => {
166
+ const cpy = deep.copy(a);
167
+ deep.remove(a, "c.100");
168
+ expect(a).toEqual(cpy);
169
+ });
170
+ it("should not throw an error when the key exists", () => {
171
+ const cpy = deep.copy(a);
172
+ deep.remove(a, "b.d");
173
+ expect(a).toEqual(cpy);
174
+ });
175
+ });
147
176
  });
package/src/deep/path.ts CHANGED
@@ -36,6 +36,10 @@ type Prev = [
36
36
  ...Array<0>,
37
37
  ];
38
38
 
39
+ /**
40
+ * A type that represents a deep key in an object.
41
+ * @example type Key<T, 3> = "a" | "a.b" | "a.b.c"
42
+ */
39
43
  export type Key<T, D extends number = 5> = [D] extends [never]
40
44
  ? never
41
45
  : T extends object
@@ -46,11 +50,24 @@ export type Key<T, D extends number = 5> = [D] extends [never]
46
50
  }[keyof T]
47
51
  : "";
48
52
 
53
+ /** Options for the get function. */
49
54
  export interface GetOptions<O extends boolean | undefined = boolean | undefined> {
50
55
  optional: O;
51
56
  getter?: (obj: UnknownRecord, key: string) => unknown;
57
+ separator?: string;
52
58
  }
53
59
 
60
+ /**
61
+ * A function that gets the value at the given path on the object. If the path does not exist
62
+ * and the optional flag is set to true, null will be returned. If the path does not exist and
63
+ * the optional flag is set to false, an error will be thrown.
64
+ * @param obj the object to get the value from.
65
+ * @param path the path to get the value at.
66
+ * @param options the options for getting the value.
67
+ * @param options.optional whether the path is optional.
68
+ * @param options.getter a custom getter function to use on each part of the path.
69
+ * @returns the value at the given path on the object.
70
+ */
54
71
  export interface Get {
55
72
  <V = unknown, T = UnknownRecord>(
56
73
  obj: T,
@@ -64,11 +81,20 @@ export interface Get {
64
81
  ): V | null;
65
82
  }
66
83
 
84
+ /** A strongly typed version of the @see Get function. */
67
85
  export interface TypedGet<V = unknown, T = UnknownRecord> {
68
86
  (obj: T, path: string, options?: GetOptions<false>): V;
69
87
  (obj: T, path: string, options?: GetOptions<boolean | undefined>): V | null;
70
88
  }
71
89
 
90
+ /**
91
+ * Executes the given replacer function on each part of the path.
92
+ * @param path the path to transform
93
+ * @param replacer the function to execute on each part of the path. If multiple
94
+ * parts are returned, they will be joined with a period. If null/undefined is returned,
95
+ * the part will be removed from the path.
96
+ * @returns the transformed path.
97
+ */
72
98
  export const transformPath = (
73
99
  path: string,
74
100
  replacer: (
@@ -76,26 +102,39 @@ export const transformPath = (
76
102
  index: number,
77
103
  parts: string[],
78
104
  ) => string | string[] | undefined,
105
+ separator = ".",
79
106
  ): string => {
80
- const parts = path.split(".");
107
+ const parts = path.split(separator);
81
108
  const result = parts
82
109
  .map((part, index) => {
83
110
  const r = replacer(part, index, parts);
84
111
  if (r == null) return null;
85
112
  if (typeof r === "string") return r;
86
- return r.join(".");
113
+ return r.join(separator);
87
114
  })
88
115
  .filter((part) => part != null) as string[];
89
- return result.join(".");
116
+ return result.join(separator);
90
117
  };
91
118
 
119
+ /**
120
+ * Gets the value at the given path on the object. If the path does not exist
121
+ * and the optional flag is set to true, null will be returned. If the path does
122
+ * not exist and the optional flag is set to false, an error will be thrown.
123
+ * @param obj the object to get the value from.
124
+ * @param path the path to get the value at.
125
+ * @param opts the options for getting the value.
126
+ * @param opts.optional whether the path is optional.
127
+ * @param opts.getter a custom getter function to use on each part of the path.
128
+ * @returns the value at the given path on the object.
129
+ */
92
130
  export const get = (<V = unknown, T = UnknownRecord>(
93
131
  obj: T,
94
132
  path: string,
95
- opts: GetOptions = { optional: false },
133
+ opts: GetOptions = { optional: false, separator: "." },
96
134
  ): V | null => {
135
+ opts.separator ??= ".";
97
136
  const { optional, getter = (obj, key) => (obj as UnknownRecord)[key] } = opts;
98
- const parts = path.split(".");
137
+ const parts = path.split(opts.separator);
99
138
  if (parts.length === 1 && parts[0] === "") return obj as unknown as V;
100
139
  let result: UnknownRecord = obj as UnknownRecord;
101
140
  for (const part of parts) {
@@ -109,27 +148,66 @@ export const get = (<V = unknown, T = UnknownRecord>(
109
148
  return result as V;
110
149
  }) as unknown as Get;
111
150
 
151
+ /**
152
+ * Sets the value at the given path on the object. If the parents of the deep path
153
+ * do not exist, new objects will be created.
154
+ * @param obj the object to set the value on.
155
+ * @param path the path to set the value at.
156
+ * @param value the value to set.
157
+ */
112
158
  export const set = <V>(obj: V, path: string, value: unknown): void => {
113
159
  const parts = path.split(".");
114
160
  let result: UnknownRecord = obj as UnknownRecord;
115
161
  for (let i = 0; i < parts.length - 1; i++) {
116
162
  const part = parts[i];
117
- if (result[part] == null) {
118
- throw new Error(`Path ${path} does not exist`);
119
- }
163
+ if (result[part] == null) result[part] = {};
120
164
  result = result[part] as UnknownRecord;
121
165
  }
122
166
  result[parts[parts.length - 1]] = value;
123
167
  };
124
168
 
169
+ /**
170
+ * Removes the value at the given path, modifying the object in place.
171
+ * @param obj the object to remove the value from.
172
+ * @param path the path to remove the value from.
173
+ * @returns the object with the value removed.
174
+ */
175
+ export const remove = <V>(obj: V, path: string): void => {
176
+ const parts = path.split(".");
177
+ let result: UnknownRecord = obj as UnknownRecord;
178
+ for (let i = 0; i < parts.length - 1; i++) {
179
+ const part = parts[i];
180
+ if (result[part] == null) return;
181
+ result = result[part] as UnknownRecord;
182
+ }
183
+ // if its an array, we need to splice it
184
+ if (Array.isArray(result)) {
185
+ const index = parseInt(parts[parts.length - 1], 10);
186
+ if (isNaN(index)) return;
187
+ result.splice(index, 1);
188
+ return;
189
+ }
190
+ delete result[parts[parts.length - 1]];
191
+ };
192
+
193
+ /**
194
+ * Returns the element at the given index in the path.
195
+ * @param path the path to get the element from
196
+ * @param index the index of the element to get
197
+ * @returns the element at the given index in the path
198
+ */
125
199
  export const element = (path: string, index: number): string => {
126
200
  const parts = path.split(".");
127
201
  if (index < 0) return parts[parts.length + index];
128
202
  return parts[index];
129
203
  };
130
204
 
131
- export const join = (path: string[]): string => path.join(".");
132
-
205
+ /**
206
+ * Checks if the path exists in the object.
207
+ * @param obj the object to check
208
+ * @param path the path to check
209
+ * @returns whether the path exists in the object
210
+ */
133
211
  export const has = <V = unknown, T = UnknownRecord>(obj: T, path: string): boolean => {
134
212
  try {
135
213
  get<V, T>(obj, path);
@@ -139,6 +217,20 @@ export const has = <V = unknown, T = UnknownRecord>(obj: T, path: string): boole
139
217
  }
140
218
  };
141
219
 
220
+ /**
221
+ * Checks if the path matches the given pattern.
222
+ *
223
+ * @param path The path to check.
224
+ * @param pattern The pattern to match against. Only "*" is supported as a wildcard.
225
+ * @returns Whether the path matches the pattern.
226
+ *
227
+ * * @example
228
+ * pathsMatch("a.b.c", "a.b.c") // true
229
+ * pathsMatch("a.b.c", "a.b") // true
230
+ * pathsMatch("a.b", "a.b.c") // false
231
+ * pathsMatch("a.b.c", "a.*") // true
232
+ * pathsMatch("a.b.c", "a.*.c") // true
233
+ */
142
234
  export const pathsMatch = (path: string, pattern: string): boolean => {
143
235
  if (pattern.length === 0) return true;
144
236
  const parts = path.split(".");
@@ -1,3 +1,12 @@
1
+ // Copyright 2024 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
+
1
10
  class Canceled extends Error {
2
11
  static readonly MESSAGE = "canceled";
3
12
  constructor() {
@@ -1 +1,10 @@
1
+ // Copyright 2024 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
+
1
10
  export * as errors from "@/errors/errors";
package/src/kv/types.ts CHANGED
@@ -7,6 +7,8 @@
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 { z } from "zod";
11
+
10
12
  export interface KV<K = string, V = string, WK = K, WV = V, D = K>
11
13
  extends Reader<K, V>,
12
14
  Writer<WK, WV>,
@@ -50,3 +52,14 @@ export interface AsyncDeleter<K = string> {
50
52
  /** Deletes a key-value pair from the store. */
51
53
  delete: (key: K) => Promise<void>;
52
54
  }
55
+
56
+ export const stringPairZ = z.object({
57
+ key: z.string(),
58
+ value: z.string(),
59
+ });
60
+
61
+ /** A general purpose key-value pair. */
62
+ export interface Pair<K = string, V = string> {
63
+ key: K;
64
+ value: V;
65
+ }
@@ -61,6 +61,84 @@ describe("compareSemVer", () => {
61
61
  expect(migrate.compareSemVer("1.0.0", "0.0.0")).toBeGreaterThan(0);
62
62
  expect(migrate.semVerNewer("3.0.0", "0.3.0")).toBeTruthy();
63
63
  });
64
+ describe("only check major", () => {
65
+ it("should return equal when the major versions are equal but the minor and patch are different", () => {
66
+ expect(
67
+ migrate.compareSemVer("1.0.0", "1.1.0", {
68
+ checkMinor: false,
69
+ checkPatch: false,
70
+ }),
71
+ ).toBe(0);
72
+ });
73
+ it("should return greater than when the major version is higher", () => {
74
+ expect(
75
+ migrate.compareSemVer("2.0.0", "1.1.0", {
76
+ checkMinor: false,
77
+ checkPatch: false,
78
+ }),
79
+ ).toBeGreaterThan(0);
80
+ });
81
+ it("should return less than when the major version is lower", () => {
82
+ expect(
83
+ migrate.compareSemVer("1.0.0", "2.1.0", {
84
+ checkMinor: false,
85
+ checkPatch: false,
86
+ }),
87
+ ).toBeLessThan(0);
88
+ });
89
+ });
90
+ describe("only check minor", () => {
91
+ it("should return equal when the minor versions are equal but the major and patch are different", () => {
92
+ expect(
93
+ migrate.compareSemVer("1.0.0", "2.0.0", {
94
+ checkMajor: false,
95
+ checkPatch: false,
96
+ }),
97
+ ).toBe(0);
98
+ });
99
+ it("should return greater than when the minor version is higher", () => {
100
+ expect(
101
+ migrate.compareSemVer("1.2.0", "1.1.0", {
102
+ checkMajor: false,
103
+ checkPatch: false,
104
+ }),
105
+ ).toBeGreaterThan(0);
106
+ });
107
+ it("should return less than when the minor version is lower", () => {
108
+ expect(
109
+ migrate.compareSemVer("1.0.0", "1.1.0", {
110
+ checkMajor: false,
111
+ checkPatch: false,
112
+ }),
113
+ ).toBeLessThan(0);
114
+ });
115
+ });
116
+ describe("only check patch", () => {
117
+ it("should return equal when the patch versions are equal but the major and minor are different", () => {
118
+ expect(
119
+ migrate.compareSemVer("2.1.1", "2.2.1", {
120
+ checkMajor: false,
121
+ checkMinor: false,
122
+ }),
123
+ ).toBe(0);
124
+ });
125
+ it("should return greater than when the patch version is higher", () => {
126
+ expect(
127
+ migrate.compareSemVer("1.4.2", "1.9.1", {
128
+ checkMajor: false,
129
+ checkMinor: false,
130
+ }),
131
+ ).toBeGreaterThan(0);
132
+ });
133
+ it("should return less than when the patch version is lower", () => {
134
+ expect(
135
+ migrate.compareSemVer("10000.2.0", "95.6.1", {
136
+ checkMajor: false,
137
+ checkMinor: false,
138
+ }),
139
+ ).toBeLessThan(0);
140
+ });
141
+ });
64
142
  });
65
143
 
66
144
  describe("migrator", () => {
@@ -15,40 +15,100 @@ export const semVerZ = z.string().regex(/^\d+\.\d+\.\d+$/);
15
15
 
16
16
  export type SemVer = z.infer<typeof semVerZ>;
17
17
 
18
+ export interface CompareSemVerOptions {
19
+ /**
20
+ * Whether to validate that major versions are equal.
21
+ * @default true
22
+ */
23
+ checkMajor?: boolean;
24
+ /**
25
+ * Whether to validate that minor versions are equal.
26
+ * @default true
27
+ */
28
+ checkMinor?: boolean;
29
+ /**
30
+ * Whether to validate that patch versions are equal.
31
+ * @default true
32
+ */
33
+ checkPatch?: boolean;
34
+ }
35
+
18
36
  /**
19
37
  * Compares the two semantic versions.
20
38
  *
21
39
  * @param a The first semantic version.
22
40
  * @param b The second semantic version.
41
+ * @param opts - Optional object to disable checking specific version parts
42
+ * (major, minor, patch).
23
43
  * @returns a number, where the the number is compare.LESS_THAN (negative) if a is OLDER
24
44
  * than B, compare.EQUAL (0) if a is the same as b, and compare.GREATER_THAN (positive)
25
45
  * if a is NEWER than b.
26
46
  */
27
- export const compareSemVer: compare.CompareF<string> = (a, b) => {
47
+ export const compareSemVer = ((
48
+ a: SemVer,
49
+ b: SemVer,
50
+ opts: CompareSemVerOptions = {},
51
+ ) => {
52
+ opts.checkMajor ??= true;
53
+ opts.checkMinor ??= true;
54
+ opts.checkPatch ??= true;
28
55
  const semA = semVerZ.parse(a);
29
56
  const semB = semVerZ.parse(b);
30
57
  const [aMajor, aMinor, aPatch] = semA.split(".").map(Number);
31
58
  const [bMajor, bMinor, bPatch] = semB.split(".").map(Number);
32
- if (aMajor !== bMajor) return aMajor - bMajor;
33
- if (aMinor !== bMinor) return aMinor - bMinor;
34
- return aPatch - bPatch;
35
- };
59
+ if (opts.checkMajor) {
60
+ if (aMajor < bMajor) return compare.LESS_THAN;
61
+ if (aMajor > bMajor) return compare.GREATER_THAN;
62
+ }
63
+ if (opts.checkMinor) {
64
+ if (aMinor < bMinor) return compare.LESS_THAN;
65
+ if (aMinor > bMinor) return compare.GREATER_THAN;
66
+ }
67
+ if (opts.checkPatch) {
68
+ if (aPatch < bPatch) return compare.LESS_THAN;
69
+ if (aPatch > bPatch) return compare.GREATER_THAN;
70
+ }
71
+ return compare.EQUAL;
72
+ }) satisfies compare.CompareF<SemVer>;
73
+
74
+ /**
75
+ * @returns true if the two semantic versions are equal.
76
+ * @param a - The first semantic version.
77
+ * @param b - The second semantic version.
78
+ * @param opts - Optional object to disable checking specific version parts
79
+ * (major, minor, patch).
80
+ */
81
+ export const versionsEqual = (
82
+ a: SemVer,
83
+ b: SemVer,
84
+ opts: CompareSemVerOptions = {},
85
+ ): boolean => compare.isEqualTo(compareSemVer(a, b, opts));
36
86
 
37
87
  /**
38
88
  * @returns true if the first semantic version is newer than the second.
39
89
  * @param a The first semantic version.
40
90
  * @param b The second semantic version.
91
+ * @param opts - Optional object to disable checking specific version parts
92
+ * (major, minor, patch).
41
93
  */
42
- export const semVerNewer = (a: SemVer, b: SemVer): boolean =>
43
- compare.isGreaterThan(compareSemVer(a, b));
94
+ export const semVerNewer = (
95
+ a: SemVer,
96
+ b: SemVer,
97
+ opts: CompareSemVerOptions = {},
98
+ ): boolean => compare.isGreaterThan(compareSemVer(a, b, opts));
44
99
 
45
100
  /**
46
101
  * @returns true if the first semantic version is older than the second.
47
102
  * @param a The first semantic version.
48
103
  * @param b The second semantic version.
104
+ * @param opts - Optional object to disable checking specific version parts
105
+ * (major, minor, patch).
49
106
  */
50
- export const semVerOlder = (a: SemVer, b: SemVer): boolean =>
51
- compare.isLessThan(compareSemVer(a, b));
107
+ export const semVerOlder = (
108
+ a: SemVer,
109
+ b: SemVer,
110
+ opts: CompareSemVerOptions = {},
111
+ ): boolean => compare.isLessThan(compareSemVer(a, b, opts));
52
112
 
53
113
  export type Migratable<V extends string = string> = { version: V };
54
114
 
@@ -7,7 +7,7 @@
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 { type Mock,vi } from "vitest";
10
+ import { type Mock, vi } from "vitest";
11
11
 
12
12
  import { type GLBufferController } from "@/telem/gl";
13
13
 
@@ -20,14 +20,14 @@ export class MockGLBufferController implements GLBufferController {
20
20
  counter: number = 0;
21
21
  buffers: Record<number, ArrayBuffer> = {};
22
22
 
23
- createBufferMock: Mock<[], WebGLBuffer | null> = vi.fn();
24
- bufferDataMock: Mock<[number, ArrayBufferLike | number, number]> = vi.fn();
25
- bufferSubDataMock: Mock<[number, number, ArrayBufferLike]> = vi.fn();
26
- bindBufferMock: Mock<[number, WebGLBuffer | null]> = vi.fn();
27
- deleteBufferMock: Mock<[WebGLBuffer | null]> = vi.fn();
23
+ createBufferMock: Mock<() => WebGLBuffer | null> = vi.fn();
24
+ bufferDataMock: Mock<(a: number, b: ArrayBufferLike | number, c: number) => void> =
25
+ vi.fn();
26
+ bufferSubDataMock: Mock<(a: number, b: number, c: ArrayBufferLike) => void> = vi.fn();
27
+ bindBufferMock: Mock<(a: number, b: WebGLBuffer | null) => void> = vi.fn();
28
+ deleteBufferMock: Mock<(a: WebGLBuffer | null) => void> = vi.fn();
28
29
 
29
30
  deleteBuffer(buffer: WebGLBuffer | null): void {
30
-
31
31
  if (buffer != null) delete this.buffers[buffer as number];
32
32
  this.deleteBufferMock(buffer);
33
33
  }
@@ -1 +1,10 @@
1
+ // Copyright 2024 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
+
1
10
  export * as numeric from "@/numeric/numeric";
@@ -1,3 +1,12 @@
1
+ // Copyright 2024 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
+
1
10
  export type Value = number | bigint;
2
11
  export type ValueGuard<T> = [T] extends [number]
3
12
  ? number