@synnaxlabs/x 0.54.2 → 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 (150) 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/strings/strings.d.ts +9 -0
  53. package/dist/src/strings/strings.d.ts.map +1 -1
  54. package/dist/src/telem/clockSkew.d.ts +17 -0
  55. package/dist/src/telem/clockSkew.d.ts.map +1 -0
  56. package/dist/src/telem/clockSkew.spec.d.ts +2 -0
  57. package/dist/src/telem/clockSkew.spec.d.ts.map +1 -0
  58. package/dist/src/telem/external.d.ts +2 -0
  59. package/dist/src/telem/external.d.ts.map +1 -1
  60. package/dist/src/telem/series.d.ts +6 -3
  61. package/dist/src/telem/series.d.ts.map +1 -1
  62. package/dist/src/telem/telem.d.ts +48 -39
  63. package/dist/src/telem/telem.d.ts.map +1 -1
  64. package/dist/src/telem/types.gen.d.ts +19 -0
  65. package/dist/src/telem/types.gen.d.ts.map +1 -0
  66. package/dist/src/text/external.d.ts +3 -0
  67. package/dist/src/text/external.d.ts.map +1 -0
  68. package/dist/src/text/index.d.ts +2 -0
  69. package/dist/src/text/index.d.ts.map +1 -0
  70. package/dist/src/text/types.d.ts +21 -0
  71. package/dist/src/text/types.d.ts.map +1 -0
  72. package/dist/src/text/types.gen.d.ts +13 -0
  73. package/dist/src/text/types.gen.d.ts.map +1 -0
  74. package/dist/src/throttle/index.d.ts +2 -0
  75. package/dist/src/throttle/index.d.ts.map +1 -0
  76. package/dist/src/throttle/throttle.d.ts +3 -0
  77. package/dist/src/throttle/throttle.d.ts.map +1 -0
  78. package/dist/src/throttle/throttle.spec.d.ts +2 -0
  79. package/dist/src/throttle/throttle.spec.d.ts.map +1 -0
  80. package/dist/src/zod/parse.d.ts.map +1 -1
  81. package/dist/x.cjs +10 -13
  82. package/dist/x.js +2271 -2084
  83. package/package.json +12 -12
  84. package/src/array/nullable.ts +1 -4
  85. package/src/caseconv/caseconv.spec.ts +71 -0
  86. package/src/caseconv/caseconv.ts +15 -2
  87. package/src/compare/compare.spec.ts +115 -0
  88. package/src/compare/compare.ts +29 -0
  89. package/src/debounce/debounce.spec.ts +258 -24
  90. package/src/debounce/debounce.ts +49 -30
  91. package/src/deep/copy.spec.ts +13 -0
  92. package/src/deep/difference.ts +1 -1
  93. package/src/destructor/destructor.ts +2 -0
  94. package/src/errors/errors.spec.ts +30 -0
  95. package/src/errors/errors.ts +29 -17
  96. package/src/index.ts +4 -1
  97. package/src/notation/external.ts +11 -0
  98. package/src/notation/index.ts +1 -1
  99. package/src/notation/notation.spec.ts +260 -2
  100. package/src/notation/notation.ts +25 -7
  101. package/src/notation/types.gen.ts +16 -0
  102. package/src/primitive/primitive.spec.ts +58 -5
  103. package/src/primitive/primitive.ts +22 -0
  104. package/src/record/record.spec.ts +26 -0
  105. package/src/record/record.ts +20 -5
  106. package/src/require/index.ts +10 -0
  107. package/src/require/require.ts +10 -0
  108. package/src/spatial/base.ts +1 -93
  109. package/src/spatial/bounds/bounds.ts +10 -10
  110. package/src/spatial/box/box.ts +5 -5
  111. package/src/spatial/direction/direction.ts +16 -17
  112. package/src/spatial/external.ts +1 -2
  113. package/src/spatial/location/location.ts +19 -17
  114. package/src/spatial/scale/scale.ts +2 -2
  115. package/src/spatial/sticky/sticky.spec.ts +2 -2
  116. package/src/spatial/sticky/sticky.ts +6 -13
  117. package/src/spatial/types.gen.ts +140 -0
  118. package/src/spatial/xy/xy.ts +7 -7
  119. package/src/status/status.spec.ts +65 -0
  120. package/src/status/status.ts +20 -0
  121. package/src/strings/strings.spec.ts +19 -0
  122. package/src/strings/strings.ts +16 -0
  123. package/src/telem/clockSkew.spec.ts +58 -0
  124. package/src/telem/clockSkew.ts +46 -0
  125. package/src/telem/external.ts +9 -0
  126. package/src/telem/series.spec.ts +235 -4
  127. package/src/telem/series.ts +172 -58
  128. package/src/telem/telem.spec.ts +147 -9
  129. package/src/telem/telem.ts +101 -84
  130. package/src/telem/types.gen.ts +28 -0
  131. package/src/text/external.ts +11 -0
  132. package/src/text/index.ts +10 -0
  133. package/src/text/types.gen.ts +16 -0
  134. package/src/text/types.ts +37 -0
  135. package/src/{worker → throttle}/index.ts +1 -1
  136. package/src/throttle/throttle.spec.ts +147 -0
  137. package/src/throttle/throttle.ts +44 -0
  138. package/src/zod/parse.ts +2 -3
  139. package/tsconfig.tsbuildinfo +1 -1
  140. package/dist/src/spatial/spatial.d.ts +0 -3
  141. package/dist/src/spatial/spatial.d.ts.map +0 -1
  142. package/dist/src/worker/index.d.ts +0 -2
  143. package/dist/src/worker/index.d.ts.map +0 -1
  144. package/dist/src/worker/worker.d.ts +0 -33
  145. package/dist/src/worker/worker.d.ts.map +0 -1
  146. package/dist/src/worker/worker.spec.d.ts +0 -2
  147. package/dist/src/worker/worker.spec.d.ts.map +0 -1
  148. package/src/spatial/spatial.ts +0 -44
  149. package/src/worker/worker.spec.ts +0 -41
  150. package/src/worker/worker.ts +0 -86
@@ -54,13 +54,13 @@ describe("primitive", () => {
54
54
 
55
55
  describe("isStringer", () => {
56
56
  it("should return true for a stringer", () => {
57
- expect(primitive.isStringer(new ExampleStringer("cat"))).toEqual(true);
57
+ expect(primitive.isStringer(new ExampleStringer("cat"))).toBe(true);
58
58
  });
59
59
  it("should return false for a non-stringer", () => {
60
- expect(primitive.isStringer(0)).toEqual(false);
60
+ expect(primitive.isStringer(0)).toBe(false);
61
61
  });
62
62
  it("should return false for null", () => {
63
- expect(primitive.isStringer(null)).toEqual(false);
63
+ expect(primitive.isStringer(null)).toBe(false);
64
64
  });
65
65
  });
66
66
 
@@ -178,12 +178,65 @@ describe("primitive", () => {
178
178
 
179
179
  describe("isCrudeValueExtension", () => {
180
180
  it("should return true for a CrudeValueExtension", () => {
181
- expect(primitive.isCrudeValueExtension({ value: 12n })).toEqual(true);
181
+ expect(primitive.isCrudeValueExtension({ value: 12n })).toBe(true);
182
182
  });
183
183
  it("should return false for a non-CrudeValueExtension", () => {
184
- expect(primitive.isCrudeValueExtension(12n)).toEqual(false);
184
+ expect(primitive.isCrudeValueExtension(12n)).toBe(false);
185
185
  });
186
186
  });
187
187
  });
188
188
  });
189
+
190
+ describe("isHashable", () => {
191
+ class HashableThing implements primitive.Hashable {
192
+ constructor(private readonly v: string) {}
193
+ hash(): string {
194
+ return this.v;
195
+ }
196
+ }
197
+
198
+ it("returns true for an object with a hash() method", () => {
199
+ expect(primitive.isHashable(new HashableThing("x"))).toBe(true);
200
+ });
201
+
202
+ it("returns true for a plain object literal with a hash function", () => {
203
+ expect(primitive.isHashable({ hash: () => "x" })).toBe(true);
204
+ });
205
+
206
+ it("narrows the type for downstream calls", () => {
207
+ const v: unknown = new HashableThing("abc");
208
+ if (primitive.isHashable(v)) expect(v.hash()).toEqual("abc");
209
+ else throw new Error("expected isHashable to narrow");
210
+ });
211
+
212
+ it("returns false for null", () => {
213
+ expect(primitive.isHashable(null)).toBe(false);
214
+ });
215
+
216
+ it("returns false for undefined", () => {
217
+ expect(primitive.isHashable(undefined)).toBe(false);
218
+ });
219
+
220
+ it("returns false for primitives", () => {
221
+ expect(primitive.isHashable("x")).toBe(false);
222
+ expect(primitive.isHashable(42)).toBe(false);
223
+ expect(primitive.isHashable(42n)).toBe(false);
224
+ expect(primitive.isHashable(true)).toBe(false);
225
+ });
226
+
227
+ it("returns false for plain objects without a hash function", () => {
228
+ expect(primitive.isHashable({})).toBe(false);
229
+ expect(primitive.isHashable({ key: "x" })).toBe(false);
230
+ });
231
+
232
+ it("returns false when hash is not a function", () => {
233
+ expect(primitive.isHashable({ hash: "not a function" })).toBe(false);
234
+ expect(primitive.isHashable({ hash: 42 })).toBe(false);
235
+ expect(primitive.isHashable({ hash: null })).toBe(false);
236
+ });
237
+
238
+ it("returns false for arrays", () => {
239
+ expect(primitive.isHashable([1, 2, 3])).toBe(false);
240
+ });
241
+ });
189
242
  });
@@ -61,6 +61,28 @@ export interface Stringer {
61
61
  export const isStringer = (value: unknown): boolean =>
62
62
  value != null && typeof value === "object" && "toString" in value;
63
63
 
64
+ /**
65
+ * Hashable is a duck-typed protocol for class instances that can produce a stable,
66
+ * canonical string representation of themselves. Implementers commit to: (1) the
67
+ * returned string uniquely identifies the value's logical content, and (2) two
68
+ * instances representing the same logical value return equal strings.
69
+ *
70
+ * Used by cache-key derivation paths (e.g. flux queryCache) so non-primitive query
71
+ * fields hash to a stable identifier rather than relying on enumerable own-property
72
+ * iteration, which is fragile for class instances.
73
+ */
74
+ export interface Hashable {
75
+ /** @returns a stable, canonical string representation of the value. */
76
+ hash: () => string;
77
+ }
78
+
79
+ /** @returns true if the value implements primitive.Hashable, otherwise returns false. */
80
+ export const isHashable = (value: unknown): value is Hashable =>
81
+ value != null &&
82
+ typeof value === "object" &&
83
+ "hash" in value &&
84
+ typeof value.hash === "function";
85
+
64
86
  /**
65
87
  * Type representing zero values for each primitive type
66
88
  */
@@ -353,6 +353,32 @@ describe("record", () => {
353
353
  const obj = { nested: { key: "value" }, arr: [1, 2, 3] };
354
354
  expect(record.nullishToEmpty().parse(obj)).toEqual(obj);
355
355
  });
356
+
357
+ describe("with typed key and value schemas", () => {
358
+ const schema = record.nullishToEmpty(z.string(), z.number());
359
+
360
+ it("should coerce null to empty object", () => {
361
+ expect(schema.parse(null)).toEqual({});
362
+ });
363
+
364
+ it("should coerce undefined to empty object", () => {
365
+ expect(schema.parse(undefined)).toEqual({});
366
+ });
367
+
368
+ it("should pass through populated record matching the value schema", () => {
369
+ expect(schema.parse({ a: 1, b: 2 })).toEqual({ a: 1, b: 2 });
370
+ });
371
+
372
+ it("should reject values that don't match the value schema", () => {
373
+ expect(() => schema.parse({ a: "not a number" })).toThrow();
374
+ });
375
+
376
+ it("should preserve the inferred typed shape", () => {
377
+ const parsed = schema.parse({ a: 1 });
378
+ const value: number = parsed.a;
379
+ expect(value).toBe(1);
380
+ });
381
+ });
356
382
  });
357
383
 
358
384
  describe("omit", () => {
@@ -141,6 +141,14 @@ export const omit = <T, K extends keyof T>(obj: T, ...keys: K[]): Omit<T, K> =>
141
141
  return result;
142
142
  };
143
143
 
144
+ export interface NullishToEmpty {
145
+ (): z.ZodType<Unknown>;
146
+ <K extends z.ZodType<Key>, V extends z.ZodType>(
147
+ key: K,
148
+ value: V,
149
+ ): z.ZodType<Record<z.infer<K>, z.infer<V>>>;
150
+ }
151
+
144
152
  /**
145
153
  * For required JSON/record fields: coerces null/undefined to empty object {}.
146
154
  * Use when the record must always be present and iterable.
@@ -149,9 +157,16 @@ export const omit = <T, K extends keyof T>(obj: T, ...keys: K[]): Omit<T, K> =>
149
157
  * - undefined → {}
150
158
  * - {} → {}
151
159
  * - {data} → {data}
160
+ *
161
+ * Without arguments the value type is unknown. Pass key and value zod schemas
162
+ * to type the resulting record (e.g. for `map<string, Color>` fields).
152
163
  */
153
- export const nullishToEmpty = (): z.ZodType<Unknown> =>
154
- z.union([
155
- z.union([z.null(), z.undefined()]).transform<Unknown>(() => ({})),
156
- unknownZ(),
157
- ]);
164
+ export const nullishToEmpty = ((key?: z.ZodType, value?: z.ZodType) => {
165
+ if (key === undefined || value === undefined)
166
+ return z
167
+ .union([z.null().transform<Unknown>(() => ({})), unknownZ()])
168
+ .default(() => ({}));
169
+ return z
170
+ .union([z.null().transform(() => ({})), z.record(key as z.ZodType<Key>, value)])
171
+ .default(() => ({}));
172
+ }) as NullishToEmpty;
@@ -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 require from "@/require/require";
@@ -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 type Require<T, K extends keyof T> = Pick<Required<T>, K> & Omit<T, K>;
@@ -9,99 +9,7 @@
9
9
 
10
10
  import { z } from "zod";
11
11
 
12
- // Tuples
12
+ export * from "@/spatial/types.gen";
13
13
 
14
14
  export const numberCouple = z.tuple([z.number(), z.number()]);
15
15
  export type NumberCouple<T extends number | bigint = number> = [T, T];
16
-
17
- // Direction
18
-
19
- export const DIRECTIONS = ["x", "y"] as const;
20
- export const directionZ = z.enum(DIRECTIONS);
21
- export type Direction = z.infer<typeof directionZ>;
22
-
23
- // Location
24
-
25
- export const OUTER_LOCATIONS = ["top", "right", "bottom", "left"] as const;
26
- export const outerLocationZ = z.enum(OUTER_LOCATIONS);
27
- export type OuterLocation = z.infer<typeof outerLocationZ>;
28
-
29
- export const X_LOCATIONS = ["left", "right"] as const;
30
- export const xLocationZ = z.enum(X_LOCATIONS);
31
- export type XLocation = z.infer<typeof xLocationZ>;
32
-
33
- export const Y_LOCATIONS = ["top", "bottom"] as const;
34
- export const yLocationZ = z.enum(Y_LOCATIONS);
35
- export type YLocation = z.infer<typeof yLocationZ>;
36
-
37
- export const CENTER_LOCATIONS = ["center"] as const;
38
- export const centerLocationZ = z.enum(CENTER_LOCATIONS);
39
- export type CenterLocation = z.infer<typeof centerLocationZ>;
40
-
41
- export const LOCATIONS = ["top", "right", "bottom", "left", "center"] as const;
42
- export const locationZ = z.enum(LOCATIONS);
43
- export type Location = z.infer<typeof locationZ>;
44
-
45
- // Alignment
46
-
47
- export const ALIGNMENTS = ["start", "center", "end"] as const;
48
- export const alignmentZ = z.enum(ALIGNMENTS);
49
- export type Alignment = z.infer<typeof alignmentZ>;
50
-
51
- // Order
52
-
53
- export const ORDERS = ["first", "last"] as const;
54
- export const orderZ = z.enum(ORDERS);
55
- export type Order = z.infer<typeof orderZ>;
56
-
57
- // XY
58
-
59
- export const clientXyZ = z.object({ clientX: z.number(), clientY: z.number() });
60
- export type ClientXY = z.infer<typeof clientXyZ>;
61
-
62
- // Dimensions
63
-
64
- export const dimensionsZ = z.object({ width: z.number(), height: z.number() });
65
- export type Dimensions = z.infer<typeof dimensionsZ>;
66
-
67
- export const signedDimensionsZ = z.object({
68
- signedWidth: z.number(),
69
- signedHeight: z.number(),
70
- });
71
- export type SignedDimensions = z.infer<typeof signedDimensionsZ>;
72
-
73
- export type Dimension = "width" | "height";
74
- export type SignedDimension = "signedWidth" | "signedHeight";
75
-
76
- // Bounds
77
-
78
- export const boundsZ = z.object({ lower: z.number(), upper: z.number() });
79
-
80
- // Generic bounds interface (supports bigint)
81
- export interface Bounds<T extends number | bigint = number> {
82
- lower: T;
83
- upper: T;
84
- }
85
-
86
- export type CrudeBounds<T extends number | bigint = number> =
87
- | Bounds<T>
88
- | NumberCouple<T>;
89
-
90
- // Derived/complex types
91
-
92
- export const crudeDirection = z.enum([
93
- "x",
94
- "y",
95
- ...OUTER_LOCATIONS,
96
- ...CENTER_LOCATIONS,
97
- ]);
98
- export type CrudeDirection = z.infer<typeof crudeDirection>;
99
- export type CrudeXDirection = "x" | "left" | "right";
100
- export type CrudeYDirection = "y" | "top" | "bottom";
101
- export type AngularDirection = "clockwise" | "counterclockwise";
102
- export const crudeLocation = z.union([
103
- directionZ,
104
- z.enum([...OUTER_LOCATIONS, ...CENTER_LOCATIONS]),
105
- z.instanceof(String),
106
- ]);
107
- export type CrudeLocation = z.infer<typeof crudeLocation>;
@@ -9,11 +9,11 @@
9
9
 
10
10
  import { abs, add, equal as mathEqual, min as mathMin, sub } from "@/math/math";
11
11
  import { type numeric } from "@/numeric";
12
- import { type Bounds, boundsZ, type CrudeBounds } from "@/spatial/base";
12
+ import { type Bounds, boundsZ, type NumberCouple } from "@/spatial/base";
13
13
 
14
14
  export { type Bounds, boundsZ };
15
15
 
16
- export type Crude<T extends numeric.Value = number> = CrudeBounds<T>;
16
+ export type Crude<T extends numeric.Value = number> = Bounds<T> | NumberCouple<T>;
17
17
 
18
18
  /** Options for the `construct` function. */
19
19
  interface ConstructOptions {
@@ -209,7 +209,7 @@ export const clamp = <T extends numeric.Value>(bounds: Crude<T>, target: T): T =
209
209
  */
210
210
  export const contains = <T extends numeric.Value>(
211
211
  bounds: Crude<T>,
212
- target: T | CrudeBounds<T>,
212
+ target: T | Crude<T>,
213
213
  ): boolean => {
214
214
  const _bounds = construct(bounds);
215
215
  if (typeof target === "number" || typeof target === "bigint")
@@ -594,7 +594,7 @@ export const traverse = <T extends numeric.Value = number>(
594
594
  if (dir === 0) return start;
595
595
 
596
596
  let remainingDist = dist;
597
- let currentPosition = start as number | bigint;
597
+ let currentPosition = start;
598
598
 
599
599
  while (mathEqual(remainingDist, 0) === false) {
600
600
  // Find the bound we're currently in or adjacent to
@@ -607,15 +607,15 @@ export const traverse = <T extends numeric.Value = number>(
607
607
  const b = _bounds[index];
608
608
  let distanceInBound: T;
609
609
  if (dir > 0) distanceInBound = sub(b.upper, currentPosition);
610
- else distanceInBound = sub(currentPosition, b.lower) as T;
610
+ else distanceInBound = sub(currentPosition, b.lower);
611
611
 
612
612
  if (distanceInBound > (0 as T)) {
613
613
  const moveDist = mathMin(abs(remainingDist), distanceInBound);
614
- currentPosition = add(currentPosition, dir > 0 ? moveDist : -moveDist) as T;
614
+ currentPosition = add(currentPosition, dir > 0 ? moveDist : -moveDist);
615
615
  remainingDist = sub<T>(remainingDist, dir > 0 ? moveDist : -moveDist);
616
616
 
617
617
  // If we've exhausted the distance, return the current position
618
- if (mathEqual(remainingDist, 0)) return currentPosition as T;
618
+ if (mathEqual(remainingDist, 0)) return currentPosition;
619
619
  continue;
620
620
  }
621
621
  }
@@ -626,17 +626,17 @@ export const traverse = <T extends numeric.Value = number>(
626
626
  const nextBounds = _bounds.filter((b) => b.lower > currentPosition);
627
627
  if (nextBounds.length > 0) currentPosition = nextBounds[0].lower;
628
628
  // No more bounds in this direction
629
- else return currentPosition as T;
629
+ else return currentPosition;
630
630
  } else {
631
631
  // Move to the previous bound's upper value
632
632
  const prevBounds = _bounds.filter((b) => b.upper < currentPosition);
633
633
  if (prevBounds.length > 0)
634
634
  currentPosition = prevBounds[prevBounds.length - 1].upper;
635
635
  // No more bounds in this direction
636
- else return currentPosition as T;
636
+ else return currentPosition;
637
637
  }
638
638
  }
639
- return currentPosition as T;
639
+ return currentPosition;
640
640
  };
641
641
 
642
642
  /**
@@ -32,7 +32,7 @@ export const domRect = z.object({
32
32
  export const box = z.object({
33
33
  one: xy.xyZ,
34
34
  two: xy.xyZ,
35
- root: location.corner,
35
+ root: location.cornerZ,
36
36
  });
37
37
 
38
38
  export type Box = z.infer<typeof box>;
@@ -50,7 +50,7 @@ export const ZERO = { one: xy.ZERO, two: xy.ZERO, root: location.TOP_LEFT };
50
50
  */
51
51
  export const DECIMAL = { one: xy.ZERO, two: xy.ONE, root: location.BOTTOM_LEFT };
52
52
 
53
- export const copy = (b: Box, root?: location.CornerXY): Box => ({
53
+ export const copy = (b: Box, root?: location.Corner): Box => ({
54
54
  one: b.one,
55
55
  two: b.two,
56
56
  root: root ?? b.root,
@@ -71,7 +71,7 @@ export const construct = (
71
71
  second?: number | xy.XY | dimensions.Dimensions | dimensions.Signed,
72
72
  width: number = 0,
73
73
  height: number = 0,
74
- coordinateRoot?: location.CornerXY,
74
+ coordinateRoot?: location.Corner,
75
75
  ): Box => {
76
76
  const b: Box = {
77
77
  one: { ...xy.ZERO },
@@ -339,7 +339,7 @@ export const yBounds = (b: Crude): bounds.Bounds => {
339
339
  return { lower: b_.one.y, upper: b_.two.y };
340
340
  };
341
341
 
342
- export const reRoot = (b: Box, corner: location.CornerXY): Box => copy(b, corner);
342
+ export const reRoot = (b: Box, corner: location.Corner): Box => copy(b, corner);
343
343
 
344
344
  export const edgePoints = (b: Crude, loc: location.Location): [xy.XY, xy.XY] => {
345
345
  const b_ = construct(b);
@@ -461,7 +461,7 @@ export const constructWithAlternateRoot = (
461
461
  width: number,
462
462
  height: number,
463
463
  currRoot: location.XY,
464
- newRoot: location.CornerXY,
464
+ newRoot: location.Corner,
465
465
  ): Box => {
466
466
  const first = { x, y };
467
467
  const second = { x: x + width, y: y + height };
@@ -7,30 +7,29 @@
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
  import {
11
13
  type AngularDirection,
12
- type CrudeDirection,
13
- crudeDirection,
14
- type CrudeXDirection,
15
- type CrudeYDirection,
14
+ CENTER_LOCATIONS,
16
15
  type Dimension,
17
16
  type Direction,
18
17
  DIRECTIONS,
19
18
  directionZ,
20
19
  type Location,
20
+ OUTER_LOCATIONS,
21
21
  type SignedDimension,
22
22
  Y_LOCATIONS,
23
23
  type YLocation,
24
- } from "@/spatial/base";
24
+ } from "@/spatial/types.gen";
25
25
 
26
26
  export { type Direction, DIRECTIONS, directionZ };
27
27
 
28
- export const crude = crudeDirection;
29
-
30
- export type Crude = CrudeDirection;
31
- export type CrudeX = CrudeXDirection;
32
- export type CrudeY = CrudeYDirection;
33
28
  export type Angular = AngularDirection;
29
+ export const crudeZ = z.enum(["x", "y", ...OUTER_LOCATIONS, ...CENTER_LOCATIONS]);
30
+ export type Crude = z.infer<typeof crudeZ>;
31
+ export type CrudeX = "x" | "left" | "right";
32
+ export type CrudeY = "y" | "top" | "bottom";
34
33
 
35
34
  export const construct = (c: Crude): Direction => {
36
35
  if (DIRECTIONS.includes(c as Direction)) return c as Direction;
@@ -38,24 +37,24 @@ export const construct = (c: Crude): Direction => {
38
37
  return "x";
39
38
  };
40
39
 
41
- export const swap = (direction: CrudeDirection): Direction =>
40
+ export const swap = (direction: Crude): Direction =>
42
41
  construct(direction) === "x" ? "y" : "x";
43
42
 
44
- export const dimension = (direction: CrudeDirection): Dimension =>
43
+ export const dimension = (direction: Crude): Dimension =>
45
44
  construct(direction) === "x" ? "width" : "height";
46
45
 
47
- export const location = (direction: CrudeDirection): Location =>
46
+ export const location = (direction: Crude): Location =>
48
47
  construct(direction) === "x" ? "left" : "top";
49
48
 
50
- export const isDirection = (c: unknown): c is Direction => crude.safeParse(c).success;
49
+ export const isDirection = (c: unknown): c is Direction => crudeZ.safeParse(c).success;
51
50
 
52
- export const signedDimension = (direction: CrudeDirection): SignedDimension =>
51
+ export const signedDimension = (direction: Crude): SignedDimension =>
53
52
  construct(direction) === "x" ? "signedWidth" : "signedHeight";
54
53
 
55
- export const isX = (direction: CrudeDirection): direction is CrudeXDirection => {
54
+ export const isX = (direction: Crude): direction is CrudeX => {
56
55
  if (direction === "center") return false;
57
56
  return construct(direction) === "x";
58
57
  };
59
58
 
60
- export const isY = (direction: CrudeDirection): direction is CrudeYDirection =>
59
+ export const isY = (direction: Crude): direction is CrudeY =>
61
60
  construct(direction) === "y";
@@ -13,7 +13,6 @@ export * from "@/spatial/dimensions";
13
13
  export * from "@/spatial/direction";
14
14
  export * from "@/spatial/location";
15
15
  export * from "@/spatial/scale";
16
- export * as spatial from "@/spatial/spatial";
17
16
  export * from "@/spatial/sticky";
18
- export * from "@/spatial/types.gen";
17
+ export * as spatial from "@/spatial/types.gen";
19
18
  export * from "@/spatial/xy";
@@ -15,10 +15,11 @@ import {
15
15
  CENTER_LOCATIONS,
16
16
  type CenterLocation,
17
17
  centerLocationZ,
18
- type CrudeLocation,
19
- crudeLocation,
18
+ type CornerLocation,
19
+ cornerLocationZ,
20
20
  type Direction,
21
21
  DIRECTIONS,
22
+ directionZ,
22
23
  type Location,
23
24
  LOCATIONS,
24
25
  locationZ,
@@ -43,15 +44,18 @@ export {
43
44
  Y_LOCATIONS,
44
45
  };
45
46
 
46
- export const x = xLocationZ;
47
- export const y = yLocationZ;
48
- export const center = centerLocationZ;
47
+ export const xZ = xLocationZ;
48
+ export const yZ = yLocationZ;
49
+ export const centerZ = centerLocationZ;
49
50
  export const outerZ = outerLocationZ;
51
+ export const cornerZ = cornerLocationZ;
50
52
 
51
53
  export type X = XLocation;
52
54
  export type Y = YLocation;
53
55
  export type Outer = OuterLocation;
54
56
  export type Center = CenterLocation;
57
+ export type Corner = CornerLocation;
58
+ export type CornerString = "topLeft" | "topRight" | "bottomLeft" | "bottomRight";
55
59
 
56
60
  const SWAPPED: Record<Location, Location> = {
57
61
  top: "bottom",
@@ -67,10 +71,12 @@ const ROTATIONS: Record<Outer, Record<AngularDirection, Outer>> = {
67
71
  bottom: { clockwise: "right", counterclockwise: "left" },
68
72
  left: { clockwise: "bottom", counterclockwise: "top" },
69
73
  };
70
-
71
- export const crude = crudeLocation;
72
-
73
- export type Crude = CrudeLocation;
74
+ export const crudeZ = z.union([
75
+ directionZ,
76
+ z.enum([...OUTER_LOCATIONS, ...CENTER_LOCATIONS]),
77
+ z.instanceof(String),
78
+ ]);
79
+ export type Crude = z.infer<typeof crudeZ>;
74
80
 
75
81
  export const construct = (cl: Crude): Location => {
76
82
  if (cl instanceof String) return cl as Location;
@@ -93,16 +99,12 @@ export const xy = z.object({
93
99
  x: xLocationZ.or(centerLocationZ),
94
100
  y: yLocationZ.or(centerLocationZ),
95
101
  });
96
- export const corner = z.object({ x: xLocationZ, y: yLocationZ });
97
-
98
102
  export type XY = z.infer<typeof xy>;
99
- export type CornerXY = z.infer<typeof corner>;
100
- export type CornerXYString = "topLeft" | "topRight" | "bottomLeft" | "bottomRight";
101
103
 
102
- export const TOP_LEFT: CornerXY = Object.freeze({ x: "left", y: "top" });
103
- export const TOP_RIGHT: CornerXY = Object.freeze({ x: "right", y: "top" });
104
- export const BOTTOM_LEFT: CornerXY = Object.freeze({ x: "left", y: "bottom" });
105
- export const BOTTOM_RIGHT: CornerXY = Object.freeze({ x: "right", y: "bottom" });
104
+ export const TOP_LEFT: Corner = Object.freeze({ x: "left", y: "top" });
105
+ export const TOP_RIGHT: Corner = Object.freeze({ x: "right", y: "top" });
106
+ export const BOTTOM_LEFT: Corner = Object.freeze({ x: "left", y: "bottom" });
107
+ export const BOTTOM_RIGHT: Corner = Object.freeze({ x: "right", y: "bottom" });
106
108
  export const CENTER: XY = Object.freeze({ x: "center", y: "center" });
107
109
  export const TOP_CENTER: XY = Object.freeze({ x: "center", y: "top" });
108
110
  export const BOTTOM_CENTER: XY = Object.freeze({ x: "center", y: "bottom" });
@@ -417,12 +417,12 @@ export class Scale<T extends numeric.Value = number> {
417
417
  export class XY {
418
418
  x: Scale<number>;
419
419
  y: Scale<number>;
420
- currRoot: location.CornerXY | null;
420
+ currRoot: location.Corner | null;
421
421
 
422
422
  constructor(
423
423
  x: Scale<number> = new Scale<number>(),
424
424
  y: Scale<number> = new Scale<number>(),
425
- root: location.CornerXY | null = null,
425
+ root: location.Corner | null = null,
426
426
  ) {
427
427
  this.x = x;
428
428
  this.y = y;
@@ -134,7 +134,7 @@ describe("sticky", () => {
134
134
  ];
135
135
  SPECS.forEach(({ value, valid }, i) => {
136
136
  test(`xy schema ${i}`, () => {
137
- const result = sticky.xy.safeParse(value);
137
+ const result = sticky.xyZ.safeParse(value);
138
138
  expect(result.success).toBe(valid);
139
139
  });
140
140
  });
@@ -196,7 +196,7 @@ describe("sticky", () => {
196
196
  ];
197
197
  SPECS.forEach(({ value, valid }, i) => {
198
198
  test(`completeXY schema ${i}`, () => {
199
- const result = sticky.completeXY.safeParse(value);
199
+ const result = sticky.completeXYZ.safeParse(value);
200
200
  expect(result.success).toBe(valid);
201
201
  });
202
202
  });
@@ -7,25 +7,18 @@
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";
10
+ import type z from "zod";
11
11
 
12
12
  import { box } from "@/spatial/box";
13
13
  import { location } from "@/spatial/location";
14
+ import { type StickyXY, stickyXYZ } from "@/spatial/types.gen";
14
15
  import { xy as base } from "@/spatial/xy";
15
16
 
16
- export const completeXY = base.xyZ.extend({
17
- root: location.corner,
18
- units: z.object({
19
- x: z.enum(["px", "decimal"]),
20
- y: z.enum(["px", "decimal"]),
21
- }),
22
- });
17
+ export const xyZ = stickyXYZ;
18
+ export type XY = StickyXY;
23
19
 
24
- export type CompleteXY = z.infer<typeof completeXY>;
25
-
26
- export const xy = completeXY.partial({ root: true, units: true });
27
-
28
- export interface XY extends z.infer<typeof xy> {}
20
+ export const completeXYZ = stickyXYZ.required({ root: true, units: true });
21
+ export type CompleteXY = z.infer<typeof completeXYZ>;
29
22
 
30
23
  interface ToCSSReturn extends Partial<Record<location.Outer, string>> {}
31
24