@synnaxlabs/x 0.42.1 → 0.43.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 (296) hide show
  1. package/.turbo/turbo-build.log +93 -105
  2. package/.vscode/settings.json +4 -6
  3. package/README.md +1 -7
  4. package/dist/array.cjs +1 -0
  5. package/dist/array.js +7 -0
  6. package/dist/base-B5lQIJKc.js +38 -0
  7. package/dist/base-CTq-lhpU.cjs +1 -0
  8. package/dist/binary.cjs +1 -1
  9. package/dist/binary.js +1 -1
  10. package/dist/bounds.cjs +1 -1
  11. package/dist/bounds.js +2 -2
  12. package/dist/box.cjs +1 -1
  13. package/dist/box.js +1 -1
  14. package/dist/caseconv.cjs +1 -1
  15. package/dist/caseconv.js +2 -2
  16. package/dist/change.cjs +1 -1
  17. package/dist/change.js +10 -2
  18. package/dist/compare.cjs +1 -1
  19. package/dist/compare.js +1 -1
  20. package/dist/deep.cjs +1 -1
  21. package/dist/deep.js +92 -79
  22. package/dist/dimensions.cjs +1 -1
  23. package/dist/dimensions.js +41 -2
  24. package/dist/direction.cjs +1 -1
  25. package/dist/direction.js +1 -1
  26. package/dist/index-4GlOgZuh.js +99 -0
  27. package/dist/{direction-C-b6XTeU.cjs → index-B58dnYRu.cjs} +1 -1
  28. package/dist/index-BMGaoK93.cjs +1 -0
  29. package/dist/index-BTet04Hd.cjs +6 -0
  30. package/dist/{scale-DfJe9755.js → index-BUa-NXAX.js} +46 -46
  31. package/dist/{xy-D_LqxaGt.js → index-C-qYOegc.js} +62 -62
  32. package/dist/index-C07SBJhr.js +128 -0
  33. package/dist/{bounds-Bn5_l4Z3.js → index-C9EdKeu1.js} +77 -76
  34. package/dist/index-CM8ZDZ6s.cjs +1 -0
  35. package/dist/index-CV2JaHfw.cjs +1 -0
  36. package/dist/index-CeBvOwG8.cjs +3 -0
  37. package/dist/index-ClrGyGDp.js +205 -0
  38. package/dist/index-CqQXXeCI.cjs +1 -0
  39. package/dist/{location-C3aeu046.js → index-D6V-8SKw.js} +26 -26
  40. package/dist/index-DBGAIs_7.js +2492 -0
  41. package/dist/index-DEdq2tza.cjs +1 -0
  42. package/dist/index-DKMnHBGR.cjs +1 -0
  43. package/dist/index-Dql5FMcH.js +47 -0
  44. package/dist/{direction-BL0PhD1k.js → index-DxU1zwsd.js} +1 -1
  45. package/dist/index-WwMnwoLy.cjs +1 -0
  46. package/dist/index.cjs +5 -3
  47. package/dist/index.js +5372 -649
  48. package/dist/kv.cjs +1 -1
  49. package/dist/kv.js +45 -2
  50. package/dist/location.cjs +1 -1
  51. package/dist/location.js +1 -1
  52. package/dist/position.cjs +1 -1
  53. package/dist/position.js +83 -2
  54. package/dist/record.cjs +1 -1
  55. package/dist/record.js +8 -10
  56. package/dist/runtime.cjs +1 -1
  57. package/dist/runtime.js +27 -2
  58. package/dist/scale.cjs +1 -1
  59. package/dist/scale.js +1 -1
  60. package/dist/schemas-55Usj0Fg.js +3998 -0
  61. package/dist/schemas-DbXuI2Qr.cjs +27 -0
  62. package/dist/spatial.cjs +1 -1
  63. package/dist/spatial.js +25 -18
  64. package/dist/src/array/index.d.ts +2 -0
  65. package/dist/src/array/index.d.ts.map +1 -0
  66. package/dist/src/array/toArray.d.ts +19 -0
  67. package/dist/src/array/toArray.d.ts.map +1 -0
  68. package/dist/src/array/toArray.spec.d.ts +2 -0
  69. package/dist/src/array/toArray.spec.d.ts.map +1 -0
  70. package/dist/src/binary/codec.d.ts +8 -16
  71. package/dist/src/binary/codec.d.ts.map +1 -1
  72. package/dist/src/breaker/breaker.d.ts +6 -6
  73. package/dist/src/breaker/breaker.d.ts.map +1 -1
  74. package/dist/src/caseconv/caseconv.d.ts +1 -1
  75. package/dist/src/caseconv/caseconv.d.ts.map +1 -1
  76. package/dist/src/change/change.d.ts +3 -3
  77. package/dist/src/change/change.d.ts.map +1 -1
  78. package/dist/src/color/color.d.ts +63 -28
  79. package/dist/src/color/color.d.ts.map +1 -1
  80. package/dist/src/color/external.d.ts +0 -1
  81. package/dist/src/color/external.d.ts.map +1 -1
  82. package/dist/src/color/gradient.d.ts +9 -5
  83. package/dist/src/color/gradient.d.ts.map +1 -1
  84. package/dist/src/color/palette.d.ts +14 -6
  85. package/dist/src/color/palette.d.ts.map +1 -1
  86. package/dist/src/compare/compare.d.ts +4 -4
  87. package/dist/src/compare/compare.d.ts.map +1 -1
  88. package/dist/src/control/control.d.ts +32 -32
  89. package/dist/src/control/control.d.ts.map +1 -1
  90. package/dist/src/debounce/debounce.d.ts +1 -1
  91. package/dist/src/debounce/debounce.d.ts.map +1 -1
  92. package/dist/src/deep/delete.d.ts +1 -1
  93. package/dist/src/deep/delete.d.ts.map +1 -1
  94. package/dist/src/deep/difference.d.ts.map +1 -1
  95. package/dist/src/deep/equal.d.ts +4 -4
  96. package/dist/src/deep/equal.d.ts.map +1 -1
  97. package/dist/src/deep/merge.d.ts +1 -1
  98. package/dist/src/deep/merge.d.ts.map +1 -1
  99. package/dist/src/errors/errors.d.ts +62 -36
  100. package/dist/src/errors/errors.d.ts.map +1 -1
  101. package/dist/src/index.d.ts +4 -1
  102. package/dist/src/index.d.ts.map +1 -1
  103. package/dist/src/instance/index.d.ts +2 -0
  104. package/dist/src/instance/index.d.ts.map +1 -0
  105. package/dist/src/instance/matcher.d.ts +79 -0
  106. package/dist/src/instance/matcher.d.ts.map +1 -0
  107. package/dist/src/instance/matcher.spec.d.ts +2 -0
  108. package/dist/src/instance/matcher.spec.d.ts.map +1 -0
  109. package/dist/src/jsonrpc/jsonrpc.d.ts +40 -9
  110. package/dist/src/jsonrpc/jsonrpc.d.ts.map +1 -1
  111. package/dist/src/kv/types.d.ts +2 -2
  112. package/dist/src/kv/types.d.ts.map +1 -1
  113. package/dist/src/migrate/migrate.d.ts +5 -5
  114. package/dist/src/migrate/migrate.d.ts.map +1 -1
  115. package/dist/src/notation/notation.d.ts +1 -1
  116. package/dist/src/notation/notation.d.ts.map +1 -1
  117. package/dist/src/primitive/index.d.ts +2 -0
  118. package/dist/src/primitive/index.d.ts.map +1 -0
  119. package/dist/src/primitive/primitive.d.ts +39 -0
  120. package/dist/src/primitive/primitive.d.ts.map +1 -0
  121. package/dist/src/primitive/primitive.spec.d.ts +2 -0
  122. package/dist/src/primitive/primitive.spec.d.ts.map +1 -0
  123. package/dist/src/record.d.ts +2 -2
  124. package/dist/src/record.d.ts.map +1 -1
  125. package/dist/src/renderable.d.ts +1 -1
  126. package/dist/src/renderable.d.ts.map +1 -1
  127. package/dist/src/replace.d.ts.map +1 -1
  128. package/dist/src/runtime/detect.d.ts.map +1 -1
  129. package/dist/src/runtime/os.d.ts +1 -1
  130. package/dist/src/runtime/os.d.ts.map +1 -1
  131. package/dist/src/spatial/base.d.ts +10 -10
  132. package/dist/src/spatial/base.d.ts.map +1 -1
  133. package/dist/src/spatial/bounds/bounds.d.ts +82 -16
  134. package/dist/src/spatial/bounds/bounds.d.ts.map +1 -1
  135. package/dist/src/spatial/box/box.d.ts +7 -7
  136. package/dist/src/spatial/box/box.d.ts.map +1 -1
  137. package/dist/src/spatial/dimensions/dimensions.d.ts +5 -5
  138. package/dist/src/spatial/dimensions/dimensions.d.ts.map +1 -1
  139. package/dist/src/spatial/direction/direction.d.ts +1 -1
  140. package/dist/src/spatial/location/location.d.ts +4 -4
  141. package/dist/src/spatial/location/location.d.ts.map +1 -1
  142. package/dist/src/spatial/scale/scale.d.ts +11 -11
  143. package/dist/src/spatial/scale/scale.d.ts.map +1 -1
  144. package/dist/src/spatial/xy/xy.d.ts +5 -5
  145. package/dist/src/spatial/xy/xy.d.ts.map +1 -1
  146. package/dist/src/status/index.d.ts +2 -0
  147. package/dist/src/status/index.d.ts.map +1 -0
  148. package/dist/src/status/types.d.ts +11 -0
  149. package/dist/src/status/types.d.ts.map +1 -0
  150. package/dist/src/sync/index.d.ts.map +1 -1
  151. package/dist/src/sync/mutex.d.ts +0 -1
  152. package/dist/src/sync/mutex.d.ts.map +1 -1
  153. package/dist/src/telem/gl.d.ts +1 -1
  154. package/dist/src/telem/gl.d.ts.map +1 -1
  155. package/dist/src/telem/series.d.ts +468 -62
  156. package/dist/src/telem/series.d.ts.map +1 -1
  157. package/dist/src/telem/telem.d.ts +243 -53
  158. package/dist/src/telem/telem.d.ts.map +1 -1
  159. package/dist/src/uuid/index.d.ts +2 -0
  160. package/dist/src/uuid/index.d.ts.map +1 -0
  161. package/dist/src/uuid/uuid.d.ts +35 -0
  162. package/dist/src/uuid/uuid.d.ts.map +1 -0
  163. package/dist/src/uuid/uuid.spec.d.ts +2 -0
  164. package/dist/src/uuid/uuid.spec.d.ts.map +1 -0
  165. package/dist/src/zod/external.d.ts +1 -1
  166. package/dist/src/zod/external.d.ts.map +1 -1
  167. package/dist/src/zod/nullToUndefined.d.ts +3 -0
  168. package/dist/src/zod/nullToUndefined.d.ts.map +1 -0
  169. package/dist/src/zod/nullToUndefined.spec.d.ts +2 -0
  170. package/dist/src/zod/nullToUndefined.spec.d.ts.map +1 -0
  171. package/dist/src/zod/util.d.ts +2 -2
  172. package/dist/src/zod/util.d.ts.map +1 -1
  173. package/dist/telem.cjs +1 -1
  174. package/dist/telem.js +1 -1
  175. package/dist/xy.cjs +1 -1
  176. package/dist/xy.js +1 -1
  177. package/dist/zod.cjs +1 -1
  178. package/dist/zod.js +19 -2
  179. package/eslint.config.js +1 -1
  180. package/package.json +16 -14
  181. package/src/{toArray.ts → array/index.ts} +1 -5
  182. package/src/array/toArray.spec.ts +56 -0
  183. package/src/array/toArray.ts +28 -0
  184. package/src/binary/codec.spec.ts +201 -278
  185. package/src/binary/codec.ts +20 -76
  186. package/src/breaker/breaker.ts +3 -3
  187. package/src/caseconv/caseconv.ts +6 -4
  188. package/src/change/change.ts +2 -2
  189. package/src/color/color.spec.ts +121 -2
  190. package/src/color/color.ts +90 -54
  191. package/src/color/external.ts +0 -1
  192. package/src/color/gradient.ts +20 -28
  193. package/src/color/palette.ts +1 -1
  194. package/src/compare/compare.ts +5 -5
  195. package/src/control/control.ts +22 -19
  196. package/src/debounce/debounce.ts +1 -1
  197. package/src/deep/delete.ts +5 -4
  198. package/src/deep/difference.ts +6 -2
  199. package/src/deep/equal.spec.ts +1 -1
  200. package/src/deep/equal.ts +5 -5
  201. package/src/deep/merge.spec.ts +13 -1
  202. package/src/deep/merge.ts +16 -7
  203. package/src/deep/path.ts +1 -1
  204. package/src/errors/errors.ts +65 -44
  205. package/src/index.ts +4 -1
  206. package/src/instance/index.ts +10 -0
  207. package/src/instance/matcher.spec.ts +78 -0
  208. package/src/instance/matcher.ts +98 -0
  209. package/src/jsonrpc/jsonrpc.spec.ts +2 -1
  210. package/src/jsonrpc/jsonrpc.ts +36 -7
  211. package/src/kv/types.ts +1 -1
  212. package/src/migrate/migrate.spec.ts +1 -1
  213. package/src/migrate/migrate.ts +7 -7
  214. package/src/notation/notation.ts +1 -1
  215. package/src/primitive/index.ts +10 -0
  216. package/src/primitive/primitive.spec.ts +126 -0
  217. package/src/primitive/primitive.ts +82 -0
  218. package/src/record.ts +3 -5
  219. package/src/renderable.ts +1 -1
  220. package/src/replace.ts +9 -0
  221. package/src/runtime/detect.ts +0 -2
  222. package/src/runtime/os.ts +1 -1
  223. package/src/spatial/base.ts +1 -1
  224. package/src/spatial/bounds/bounds.spec.ts +20 -0
  225. package/src/spatial/bounds/bounds.ts +101 -18
  226. package/src/spatial/box/box.ts +1 -1
  227. package/src/spatial/dimensions/dimensions.ts +3 -3
  228. package/src/spatial/location/location.ts +1 -1
  229. package/src/spatial/scale/scale.ts +1 -1
  230. package/src/spatial/xy/xy.ts +1 -1
  231. package/src/status/index.ts +10 -0
  232. package/src/status/types.ts +22 -0
  233. package/src/sync/index.ts +9 -0
  234. package/src/sync/mutex.ts +9 -4
  235. package/src/telem/gl.ts +1 -1
  236. package/src/telem/series.spec.ts +735 -68
  237. package/src/telem/series.ts +536 -195
  238. package/src/telem/telem.spec.ts +60 -7
  239. package/src/telem/telem.ts +287 -95
  240. package/src/uuid/index.ts +10 -0
  241. package/src/uuid/uuid.spec.ts +82 -0
  242. package/src/uuid/uuid.ts +48 -0
  243. package/src/zod/external.ts +1 -1
  244. package/src/zod/nullToUndefined.spec.ts +31 -0
  245. package/src/zod/nullToUndefined.ts +23 -0
  246. package/src/zod/util.spec.ts +1 -1
  247. package/src/zod/util.ts +6 -6
  248. package/tsconfig.json +1 -1
  249. package/tsconfig.tsbuildinfo +1 -1
  250. package/vite.config.ts +1 -1
  251. package/dist/base-BAM2mqCy.cjs +0 -1
  252. package/dist/base-DFq0vvGn.js +0 -38
  253. package/dist/bounds-BQo7rvs9.cjs +0 -1
  254. package/dist/box-0YrQibkB.cjs +0 -1
  255. package/dist/box-Cc8IzcNo.js +0 -205
  256. package/dist/change-C-YELKx6.cjs +0 -1
  257. package/dist/change-DLl6DccR.js +0 -12
  258. package/dist/dimensions-CRgergMS.js +0 -43
  259. package/dist/dimensions-D2QGoNXO.cjs +0 -1
  260. package/dist/external-BPgtxa8d.js +0 -29
  261. package/dist/external-C-dNgNQw.cjs +0 -1
  262. package/dist/external-C8TFju8Q.js +0 -29
  263. package/dist/external-DWQITF5_.cjs +0 -1
  264. package/dist/external-DqPrWKvU.js +0 -47
  265. package/dist/external-uXk0Avrg.cjs +0 -1
  266. package/dist/index-BywOGO8U.js +0 -1074
  267. package/dist/index-CYYjI7Uf.cjs +0 -1
  268. package/dist/index-C_6NXBlg.cjs +0 -3
  269. package/dist/index-Dd8DLyMx.cjs +0 -1
  270. package/dist/index-DizUWH6z.js +0 -47
  271. package/dist/index-QGplUHuy.js +0 -98
  272. package/dist/location-BGl5Ddds.cjs +0 -1
  273. package/dist/position-Cai5-wi1.cjs +0 -1
  274. package/dist/position-DIglP1l2.js +0 -85
  275. package/dist/scale-BtZINJ-A.cjs +0 -1
  276. package/dist/series-B9JERcqi.js +0 -1977
  277. package/dist/series-DqJ6f97G.cjs +0 -11
  278. package/dist/spatial-BSWPzMkK.js +0 -11
  279. package/dist/spatial-DGpZ2sO3.cjs +0 -1
  280. package/dist/src/color/transformColorsToHex.d.ts +0 -6
  281. package/dist/src/color/transformColorsToHex.d.ts.map +0 -1
  282. package/dist/src/primitive.d.ts +0 -9
  283. package/dist/src/primitive.d.ts.map +0 -1
  284. package/dist/src/toArray.d.ts +0 -3
  285. package/dist/src/toArray.d.ts.map +0 -1
  286. package/dist/src/zod/integer.d.ts +0 -10
  287. package/dist/src/zod/integer.d.ts.map +0 -1
  288. package/dist/src/zod/integer.spec.d.ts +0 -2
  289. package/dist/src/zod/integer.spec.d.ts.map +0 -1
  290. package/dist/toArray.cjs +0 -1
  291. package/dist/toArray.js +0 -5
  292. package/dist/xy-B7065J2S.cjs +0 -1
  293. package/src/color/transformColorsToHex.ts +0 -30
  294. package/src/primitive.ts +0 -46
  295. package/src/zod/integer.spec.ts +0 -148
  296. package/src/zod/integer.ts +0 -38
@@ -7,12 +7,13 @@
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 { z } from "zod/v4";
11
11
 
12
12
  import { binary } from "@/binary";
13
13
  import { caseconv } from "@/caseconv";
14
14
  import { compare } from "@/compare";
15
15
  import { id } from "@/id";
16
+ import { instance } from "@/instance";
16
17
  import { type math } from "@/math";
17
18
  import { bounds } from "@/spatial";
18
19
  import {
@@ -27,7 +28,6 @@ import {
27
28
  type CrudeTimeStamp,
28
29
  DataType,
29
30
  isTelemValue,
30
- type Rate,
31
31
  Size,
32
32
  type TelemValue,
33
33
  TimeRange,
@@ -35,6 +35,7 @@ import {
35
35
  TimeStamp,
36
36
  type TypedArray,
37
37
  } from "@/telem/telem";
38
+ import { uuid } from "@/uuid";
38
39
 
39
40
  interface GL {
40
41
  control: GLBufferController | null;
@@ -43,8 +44,9 @@ interface GL {
43
44
  bufferUsage: GLBufferUsage;
44
45
  }
45
46
 
46
- export interface IterableIterator<T> extends Iterator<T>, Iterable<T> {}
47
+ interface IterableIterator<T> extends Iterator<T>, Iterable<T> {}
47
48
 
49
+ /** A condensed set of information describing the layout of a series. */
48
50
  export interface SeriesDigest {
49
51
  key: string;
50
52
  dataType: string;
@@ -58,7 +60,7 @@ export interface SeriesDigest {
58
60
  capacity: number;
59
61
  }
60
62
 
61
- interface BaseSeriesProps {
63
+ interface BaseSeriesArgs {
62
64
  dataType?: CrudeDataType;
63
65
  timeRange?: TimeRange;
64
66
  sampleOffset?: math.Numeric;
@@ -67,6 +69,7 @@ interface BaseSeriesProps {
67
69
  key?: string;
68
70
  }
69
71
 
72
+ /** A value or set of values that a series can be constructed from. */
70
73
  export type CrudeSeries =
71
74
  | Series
72
75
  | ArrayBuffer
@@ -79,33 +82,19 @@ export type CrudeSeries =
79
82
  | Date[]
80
83
  | TelemValue;
81
84
 
82
- export const isCrudeSeries = (value: unknown): value is CrudeSeries => {
83
- if (value == null) return false;
84
- if (Array.isArray(value)) return true;
85
- if (value instanceof ArrayBuffer) return true;
86
- if (ArrayBuffer.isView(value) && !(value instanceof DataView)) return true;
87
- if (value instanceof Series) return true;
88
- return isTelemValue(value);
89
- };
90
-
91
- export interface SeriesProps extends BaseSeriesProps {
85
+ /** Arguments for constructing a {@link Series}. */
86
+ export interface SeriesArgs extends BaseSeriesArgs {
92
87
  data?: CrudeSeries | null;
93
88
  }
94
89
 
95
- export interface SeriesAllocProps extends BaseSeriesProps {
90
+ /** Arguments for allocating a {@link Series} with a given capacity and data type. */
91
+ export interface SeriesAllocArgs extends BaseSeriesArgs {
96
92
  capacity: number;
97
93
  dataType: CrudeDataType;
98
94
  }
99
95
 
100
96
  const FULL_BUFFER = -1;
101
97
 
102
- export interface SeriesMemInfo {
103
- key: string;
104
- length: number;
105
- byteLength: Size;
106
- glBuffer: boolean;
107
- }
108
-
109
98
  const noopIterableIterator: IterableIterator<never> = {
110
99
  [Symbol.iterator]: () => noopIterableIterator,
111
100
  next: () => ({ done: true, value: undefined }),
@@ -137,18 +126,31 @@ const checkAsType = (jsType: JSType, dataType: DataType) => {
137
126
  throw new Error(`cannot convert series of type ${dataType.toString()} to bigint`);
138
127
  };
139
128
 
129
+ const SERIES_DISCRIMINATOR = "sy_x_telem_series";
130
+
140
131
  /**
141
132
  * Series is a strongly typed array of telemetry samples backed by an underlying binary
142
133
  * buffer.
143
134
  */
144
- export class Series<T extends TelemValue = TelemValue> {
145
- key: string = "";
146
- isSynnaxSeries = true;
147
- /** The data type of the array */
135
+ export class Series<T extends TelemValue = TelemValue>
136
+ implements instance.Discriminated
137
+ {
138
+ /**
139
+ * A unique identifier for the series. If specified by the user, it is their
140
+ * responsibility to ensure that it is unique. If not specified, a new ID will be
141
+ * generated.
142
+ */
143
+ key: string;
144
+ /**
145
+ * A discriminator used for identifying instances of the series class even
146
+ * when bundlers mangle the class name.
147
+ */
148
+ discriminator: string = SERIES_DISCRIMINATOR;
149
+ /** The data type of the series. */
148
150
  readonly dataType: DataType;
149
151
  /**
150
152
  * A sample offset that can be used to shift the values of all samples upwards or
151
- * downwards. Typically used to convert arrays to lower precision while preserving
153
+ * downwards. Useful to convert series to lower precision data types while preserving
152
154
  * the relative range of actual values.
153
155
  */
154
156
  sampleOffset: math.Numeric;
@@ -158,21 +160,30 @@ export class Series<T extends TelemValue = TelemValue> {
158
160
  private readonly gl: GL;
159
161
  /** The underlying data. */
160
162
  private readonly _data: ArrayBuffer;
163
+ /** The time range occupied by the series' data. */
161
164
  readonly timeRange: TimeRange = TimeRange.ZERO;
165
+ /**
166
+ * Alignment defines the location of the series relative to other series in a logical
167
+ * group. Useful for defining the position of the series within a channel's data.
168
+ */
162
169
  readonly alignment: bigint = 0n;
163
170
  /** A cached minimum value. */
164
- private _cachedMin?: math.Numeric;
171
+ private cachedMin?: math.Numeric;
165
172
  /** A cached maximum value. */
166
- private _cachedMax?: math.Numeric;
173
+ private cachedMax?: math.Numeric;
167
174
  /** The write position of the buffer. */
168
175
  private writePos: number = FULL_BUFFER;
169
176
  /** Tracks the number of entities currently using this array. */
170
177
  private _refCount: number = 0;
171
178
  /** Caches the length of the array for variable length data types. */
172
- private _cachedLength?: number;
179
+ private cachedLength?: number;
173
180
  /** Caches the indexes of the array for variable length data types. */
174
181
  private _cachedIndexes?: number[];
175
182
 
183
+ /**
184
+ * A zod schema that can be used to validate that a particular value
185
+ * can be constructed into a series.
186
+ */
176
187
  static readonly crudeZ = z.object({
177
188
  timeRange: TimeRange.z.optional(),
178
189
  dataType: DataType.z,
@@ -186,10 +197,87 @@ export class Series<T extends TelemValue = TelemValue> {
186
197
  glBufferUsage: glBufferUsageZ.optional().default("static").optional(),
187
198
  });
188
199
 
200
+ /**
201
+ * A zod schema that validates and constructs a series from it's crude
202
+ * representation.
203
+ */
189
204
  static readonly z = Series.crudeZ.transform((props) => new Series(props));
190
-
191
- constructor(props: SeriesProps | CrudeSeries) {
205
+ /**
206
+ * The Series constructor accepts either a SeriesArgs object or a CrudeSeries value.
207
+ *
208
+ * SeriesArgs interface properties:
209
+ * @property {CrudeSeries | null} [data] - The data to construct the series from. Can be:
210
+ * - A typed array (e.g. Float32Array, Int32Array)
211
+ * - A JS array of numbers, strings, or objects
212
+ * - A single value (number, string, bigint, etc.)
213
+ * - An ArrayBuffer
214
+ * - Another Series instance
215
+ * @property {CrudeDataType} [dataType] - The data type of the series. If not provided,
216
+ * will be inferred from the data. Required when constructing from an ArrayBuffer, or
217
+ * an empty JS array.
218
+ * @property {TimeRange} [timeRange] - The time range occupied by the series' data.
219
+ * Defaults to TimeRange.ZERO.
220
+ * @property {math.Numeric} [sampleOffset] - An offset to apply to each sample value.
221
+ * Useful for converting arrays to lower precision while preserving relative range.
222
+ * Defaults to 0.
223
+ * @property {GLBufferUsage} [glBufferUsage] - The WebGL buffer usage hint. Can be
224
+ * "static" or "dynamic". Defaults to "static".
225
+ * @property {bigint} [alignment] - The logical position of the series relative to other
226
+ * series in a group. Defaults to 0n.
227
+ * @property {string} [key] - A unique identifier for the series. If not provided,
228
+ * a new ID will be generated.
229
+ *
230
+ * @example
231
+ * // Create a series from a typed array
232
+ * const s1 = new Series(new Float32Array([1, 2, 3]));
233
+ *
234
+ * @example
235
+ * // Create a series from a JS array with explicit data type
236
+ * const s2 = new Series({ data: [1, 2, 3], dataType: DataType.FLOAT32 });
237
+ *
238
+ * @example
239
+ * // Create a series from a single value (data type inferred)
240
+ * const s3 = new Series(1); // Creates a FLOAT64 series
241
+ * const s4 = new Series("abc"); // Creates a STRING series
242
+ * const s5 = new Series(1n); // Creates an INT64 series
243
+ *
244
+ * @example
245
+ * // Create a series from objects (automatically uses JSON data type)
246
+ * const s6 = new Series([{ a: 1, b: "apple" }]);
247
+ *
248
+ * @example
249
+ * // Create a series with time range and alignment
250
+ * const s7 = new Series({
251
+ * data: new Float32Array([1, 2, 3]),
252
+ * timeRange: new TimeRange(1, 2),
253
+ * alignment: 1n
254
+ * });
255
+ *
256
+ * @example
257
+ * // Create a series from another series (copies properties)
258
+ * const s8 = new Series(s1);
259
+ *
260
+ * @example
261
+ * // Create a series with sample offset
262
+ * const s9 = new Series({
263
+ * data: new Float32Array([1, 2, 3]),
264
+ * sampleOffset: 2
265
+ * }); // Values will be 3, 4, 5
266
+ *
267
+ * @example
268
+ * // Create a series with WebGL buffer usage
269
+ * const s10 = new Series({
270
+ * data: new Float32Array([1, 2, 3]),
271
+ * glBufferUsage: "dynamic"
272
+ * });
273
+ *
274
+ * @throws Error if constructing from an empty JS array without specifying data type
275
+ * @throws Error if constructing from an ArrayBuffer without specifying data type
276
+ * @throws Error if data type cannot be inferred from input
277
+ */
278
+ constructor(props: SeriesArgs | CrudeSeries) {
192
279
  if (isCrudeSeries(props)) props = { data: props };
280
+ props.data ??= [];
193
281
  const {
194
282
  dataType,
195
283
  timeRange,
@@ -197,14 +285,9 @@ export class Series<T extends TelemValue = TelemValue> {
197
285
  glBufferUsage = "static",
198
286
  alignment = 0n,
199
287
  key = id.create(),
288
+ data,
200
289
  } = props;
201
- const data = props.data ?? [];
202
- if (
203
- data instanceof Series ||
204
- (typeof data === "object" &&
205
- "isSynnaxSeries" in data &&
206
- data.isSynnaxSeries === true)
207
- ) {
290
+ if (isSeries(data)) {
208
291
  const data_ = data as Series;
209
292
  this.key = data_.key;
210
293
  this.dataType = data_.dataType;
@@ -213,11 +296,11 @@ export class Series<T extends TelemValue = TelemValue> {
213
296
  this._data = data_._data;
214
297
  this.timeRange = data_.timeRange;
215
298
  this.alignment = data_.alignment;
216
- this._cachedMin = data_._cachedMin;
217
- this._cachedMax = data_._cachedMax;
299
+ this.cachedMin = data_.cachedMin;
300
+ this.cachedMax = data_.cachedMax;
218
301
  this.writePos = data_.writePos;
219
302
  this._refCount = data_._refCount;
220
- this._cachedLength = data_._cachedLength;
303
+ this.cachedLength = data_.cachedLength;
221
304
  return;
222
305
  }
223
306
  const isSingle = isTelemValue(data);
@@ -265,11 +348,11 @@ export class Series<T extends TelemValue = TelemValue> {
265
348
  )
266
349
  data_ = data_.map((v) => new TimeStamp(v as CrudeTimeStamp).valueOf());
267
350
  if (this.dataType.equals(DataType.STRING)) {
268
- this._cachedLength = data_.length;
351
+ this.cachedLength = data_.length;
269
352
  this._data = new TextEncoder().encode(`${data_.join("\n")}\n`)
270
353
  .buffer as ArrayBuffer;
271
354
  } else if (this.dataType.equals(DataType.JSON)) {
272
- this._cachedLength = data_.length;
355
+ this.cachedLength = data_.length;
273
356
  this._data = new TextEncoder().encode(
274
357
  `${data_.map((d) => binary.JSON_CODEC.encodeString(d)).join("\n")}\n`,
275
358
  ).buffer as ArrayBuffer;
@@ -296,58 +379,54 @@ export class Series<T extends TelemValue = TelemValue> {
296
379
  };
297
380
  }
298
381
 
299
- static alloc({ capacity: length, dataType, ...rest }: SeriesAllocProps): Series {
300
- if (length === 0)
382
+ /**
383
+ * Allocates a new series with a given capacity and data type.
384
+ * @param args.capacity the capacity of the series in samples. If the data type is of
385
+ * variable density (i.e. JSON, STRING, BYTES), this is the capacity in bytes.
386
+ * @param args.dataType the data type of the series.
387
+ * @param args.rest the rest of the arguments to pass to the series constructor.
388
+ */
389
+ static alloc({ capacity, dataType, ...rest }: SeriesAllocArgs): Series {
390
+ if (capacity === 0)
301
391
  throw new Error("[Series] - cannot allocate an array of length 0");
302
- const data = new new DataType(dataType).Array(length);
303
- const arr = new Series({
304
- data: data.buffer,
305
- dataType,
306
- ...rest,
307
- });
392
+ const data = new new DataType(dataType).Array(capacity);
393
+ const arr = new Series({ data: data.buffer, dataType, ...rest });
308
394
  arr.writePos = 0;
309
395
  return arr;
310
396
  }
311
397
 
312
- static createTimestamps(length: number, rate: Rate, start: TimeStamp): Series {
313
- const timeRange = start.spanRange(rate.span(length));
314
- const data = new BigInt64Array(length);
315
- for (let i = 0; i < length; i++)
316
- data[i] = BigInt(start.add(rate.span(i)).valueOf());
317
- return new Series({ data, dataType: DataType.TIMESTAMP, timeRange });
318
- }
319
-
398
+ /**
399
+ * @returns the number of references to this series i.e. the number of times this
400
+ * series has been acquired (by calling acquire) and not released (by calling
401
+ * release).
402
+ */
320
403
  get refCount(): number {
321
404
  return this._refCount;
322
405
  }
323
406
 
324
- static fromStrings(data: string[], timeRange?: TimeRange): Series {
325
- const buffer = new TextEncoder().encode(
326
- `${data.join("\n")}\n`,
327
- ) as Uint8Array<ArrayBuffer>;
328
- return new Series({ data: buffer, dataType: DataType.STRING, timeRange });
329
- }
330
-
331
- static fromJSON<T>(data: T[], timeRange?: TimeRange): Series {
332
- const buffer = new TextEncoder().encode(
333
- `${data.map((d) => binary.JSON_CODEC.encodeString(d)).join("\n")}\n`,
334
- ) as Uint8Array<ArrayBuffer>;
335
- return new Series({ data: buffer, dataType: DataType.JSON, timeRange });
336
- }
337
-
407
+ /**
408
+ * Acquires a reference to this series, optionally buffering its data into the
409
+ * specified buffer controller. This method is useful for managing the life span
410
+ * of series buffered to the GPU.
411
+ * @param gl the buffer controller to buffer the series to. If not provided, the series
412
+ * will not be buffered to the GPU.
413
+ */
338
414
  acquire(gl?: GLBufferController): void {
339
415
  this._refCount++;
340
416
  if (gl != null) this.updateGLBuffer(gl);
341
417
  }
342
418
 
419
+ /**
420
+ * Releases a reference to this series. If the reference count to the series reaches
421
+ * 0 and the series has been buffered to the GPU, the series will be deleted from
422
+ * the GPU.
423
+ */
343
424
  release(): void {
344
- if (this._refCount <= 0) {
345
- console.warn("attempted to release a series with a negative reference count");
346
- return;
347
- }
348
425
  this._refCount--;
349
- if (this._refCount === 0 && this.gl.control != null)
426
+ if (this.refCount === 0 && this.gl.control != null)
350
427
  this.maybeGarbageCollectGLBuffer(this.gl.control);
428
+ else if (this.refCount < 0)
429
+ console.warn("cannot release a series with a negative reference count");
351
430
  }
352
431
 
353
432
  /**
@@ -371,8 +450,8 @@ export class Series<T extends TelemValue = TelemValue> {
371
450
  const toWrite = other.subBytes(0, available);
372
451
  this.writeToUnderlyingData(toWrite);
373
452
  this.writePos += toWrite.byteLength.valueOf();
374
- if (this._cachedLength != null) {
375
- this._cachedLength += toWrite.length;
453
+ if (this.cachedLength != null) {
454
+ this.cachedLength += toWrite.length;
376
455
  this.calculateCachedLength();
377
456
  }
378
457
  return toWrite.length;
@@ -383,7 +462,7 @@ export class Series<T extends TelemValue = TelemValue> {
383
462
  const available = this.capacity - this.writePos;
384
463
  const toWrite = other.sub(0, available);
385
464
  this.writeToUnderlyingData(toWrite);
386
- this._cachedLength = undefined;
465
+ this.cachedLength = undefined;
387
466
  this.maybeRecomputeMinMax(toWrite);
388
467
  this.writePos += toWrite.length;
389
468
  return toWrite.length;
@@ -407,69 +486,92 @@ export class Series<T extends TelemValue = TelemValue> {
407
486
  return new this.dataType.Array(this._data);
408
487
  }
409
488
 
410
- /** @returns a native typed array with the proper data type. */
489
+ /**
490
+ * Returns a native JS typed array with the proper data type.
491
+ * If the series is not full, returns a view of the data up to the write position.
492
+ * @returns A typed array containing the series data.
493
+ */
411
494
  get data(): TypedArray {
412
495
  if (this.writePos === FULL_BUFFER) return this.underlyingData;
413
496
  return new this.dataType.Array(this._data, 0, this.writePos);
414
497
  }
415
498
 
499
+ /**
500
+ * Returns an array of the values in the series as strings.
501
+ * For variable length data types (like STRING or JSON), this decodes the underlying buffer.
502
+ * @returns An array of string representations of the series values.
503
+ */
416
504
  toStrings(): string[] {
417
- if (!this.dataType.matches(DataType.STRING, DataType.UUID))
418
- throw new Error("cannot convert non-string series to strings");
419
- return new TextDecoder().decode(this.underlyingData).split("\n").slice(0, -1);
505
+ if (this.dataType.isVariable)
506
+ return new TextDecoder().decode(this.underlyingData).split("\n").slice(0, -1);
507
+ return Array.from(this).map((d) => d.toString());
420
508
  }
421
509
 
510
+ /**
511
+ * Returns a parsed array of UUIDs from the series.
512
+ * @throws Error if the series does not have a data type of UUID.
513
+ * @returns An array of UUID strings.
514
+ */
422
515
  toUUIDs(): string[] {
423
516
  if (!this.dataType.equals(DataType.UUID))
424
517
  throw new Error("cannot convert non-uuid series to uuids");
425
518
  const den = DataType.UUID.density.valueOf();
426
- const r = Array(this.length);
427
-
428
- for (let i = 0; i < this.length; i++) {
429
- const v = this.underlyingData.slice(i * den, (i + 1) * den);
430
- const id = Array.from(new Uint8Array(v.buffer), (b) =>
431
- b.toString(16).padStart(2, "0"),
432
- )
433
- .join("")
434
- .replace(/(.{8})(.{4})(.{4})(.{4})(.{12})/, "$1-$2-$3-$4-$5");
435
- r[i] = id;
436
- }
437
- return r;
519
+ const d = new Uint8Array(this.underlyingData.buffer);
520
+ return Array.from({ length: this.length }, (_, i) => uuid.parse(d, i * den));
438
521
  }
439
522
 
440
- parseJSON<Z extends z.ZodTypeAny>(schema: Z): Array<z.output<Z>> {
523
+ /**
524
+ * Parses a JSON series into an array of values using the provided zod schema.
525
+ * @template Z The zod schema type.
526
+ * @param schema The zod schema to use to parse the JSON series.
527
+ * @throws Error if the series does not have a data type of JSON.
528
+ * @returns An array of values parsed from the JSON series.
529
+ */
530
+ parseJSON<Z extends z.ZodType>(schema: Z): Array<z.infer<Z>> {
441
531
  if (!this.dataType.equals(DataType.JSON))
442
532
  throw new Error("cannot parse non-JSON series as JSON");
443
- return new TextDecoder()
444
- .decode(this.underlyingData)
445
- .split("\n")
446
- .slice(0, -1)
447
- .map((s) => schema.parse(binary.JSON_CODEC.decodeString(s)));
533
+ return this.toStrings().map((s) => schema.parse(binary.JSON_CODEC.decodeString(s)));
448
534
  }
449
535
 
450
- /** @returns the capacity of the series in bytes. */
536
+ /**
537
+ * Returns the capacity of the series in bytes.
538
+ * @returns The size of the underlying buffer in bytes.
539
+ */
451
540
  get byteCapacity(): Size {
452
541
  return new Size(this.underlyingData.byteLength);
453
542
  }
454
543
 
455
- /** @returns the capacity of the series in samples. */
544
+ /**
545
+ * Returns the capacity of the series in samples.
546
+ * For variable length data types, this is the capacity in bytes.
547
+ * @returns The number of samples that can be stored in the series.
548
+ */
456
549
  get capacity(): number {
457
550
  if (this.dataType.isVariable) return this.byteCapacity.valueOf();
458
551
  return this.dataType.density.length(this.byteCapacity);
459
552
  }
460
553
 
461
- /** @returns the length of the series in bytes. */
554
+ /**
555
+ * Returns the length of the series in bytes.
556
+ * For variable length data types, this is the actual number of bytes used.
557
+ * @returns The size of the data in bytes.
558
+ */
462
559
  get byteLength(): Size {
463
560
  if (this.writePos === FULL_BUFFER) return this.byteCapacity;
464
561
  if (this.dataType.isVariable) return new Size(this.writePos);
465
562
  return this.dataType.density.size(this.writePos);
466
563
  }
467
564
 
468
- /** @returns the number of samples in this array. */
565
+ /**
566
+ * Returns the number of samples in this array.
567
+ * For variable length data types, this is calculated by counting newlines.
568
+ * @returns The number of samples in the series.
569
+ */
469
570
  get length(): number {
470
- if (this._cachedLength != null) return this._cachedLength;
571
+ if (this.cachedLength != null) return this.cachedLength;
471
572
  if (this.dataType.isVariable) return this.calculateCachedLength();
472
- if (this.writePos === FULL_BUFFER) return this.data.length;
573
+ if (this.writePos === FULL_BUFFER)
574
+ return this.byteCapacity.valueOf() / this.dataType.density.valueOf();
473
575
  return this.writePos;
474
576
  }
475
577
 
@@ -484,7 +586,7 @@ export class Series<T extends TelemValue = TelemValue> {
484
586
  ci.push(i + 1);
485
587
  });
486
588
  this._cachedIndexes = ci;
487
- this._cachedLength = cl;
589
+ this.cachedLength = cl;
488
590
  return cl;
489
591
  }
490
592
 
@@ -515,76 +617,84 @@ export class Series<T extends TelemValue = TelemValue> {
515
617
  private calcRawMax(): math.Numeric {
516
618
  if (this.length === 0) return -Infinity;
517
619
  if (this.dataType.equals(DataType.TIMESTAMP))
518
- this._cachedMax = this.data[this.data.length - 1];
620
+ this.cachedMax = this.data[this.data.length - 1];
519
621
  else if (this.dataType.usesBigInt) {
520
622
  const d = this.data as BigInt64Array;
521
- this._cachedMax = d.reduce((a, b) => (a > b ? a : b));
623
+ this.cachedMax = d.reduce((a, b) => (a > b ? a : b));
522
624
  } else {
523
625
  const d = this.data as Float64Array;
524
- this._cachedMax = d.reduce((a, b) => (a > b ? a : b));
626
+ this.cachedMax = d.reduce((a, b) => (a > b ? a : b));
525
627
  }
526
- return this._cachedMax;
628
+ return this.cachedMax;
527
629
  }
528
630
 
529
631
  /** @returns the maximum value in the array */
530
632
  get max(): math.Numeric {
633
+ return this.calcMax();
634
+ }
635
+
636
+ private calcMax(): math.Numeric {
531
637
  if (this.dataType.isVariable)
532
638
  throw new Error("cannot calculate maximum on a variable length data type");
533
639
  if (this.writePos === 0) return -Infinity;
534
- this._cachedMax ??= this.calcRawMax();
535
- return addSamples(this._cachedMax, this.sampleOffset);
640
+ this.cachedMax ??= this.calcRawMax();
641
+ return addSamples(this.cachedMax, this.sampleOffset);
536
642
  }
537
643
 
538
644
  private calcRawMin(): math.Numeric {
539
645
  if (this.length === 0) return Infinity;
540
- if (this.dataType.equals(DataType.TIMESTAMP)) this._cachedMin = this.data[0];
646
+ if (this.dataType.equals(DataType.TIMESTAMP)) this.cachedMin = this.data[0];
541
647
  else if (this.dataType.usesBigInt) {
542
648
  const d = this.data as BigInt64Array;
543
- this._cachedMin = d.reduce((a, b) => (a < b ? a : b));
649
+ this.cachedMin = d.reduce((a, b) => (a < b ? a : b));
544
650
  } else {
545
651
  const d = this.data as Float64Array;
546
- this._cachedMin = d.reduce((a, b) => (a < b ? a : b));
652
+ this.cachedMin = d.reduce((a, b) => (a < b ? a : b));
547
653
  }
548
- return this._cachedMin;
654
+ return this.cachedMin;
549
655
  }
550
656
 
551
657
  /** @returns the minimum value in the array */
552
658
  get min(): math.Numeric {
659
+ return this.calcMin();
660
+ }
661
+
662
+ private calcMin(): math.Numeric {
553
663
  if (this.dataType.isVariable)
554
664
  throw new Error("cannot calculate minimum on a variable length data type");
555
665
  if (this.writePos === 0) return Infinity;
556
- this._cachedMin ??= this.calcRawMin();
557
- return addSamples(this._cachedMin, this.sampleOffset);
666
+ this.cachedMin ??= this.calcRawMin();
667
+ return addSamples(this.cachedMin, this.sampleOffset);
558
668
  }
559
669
 
560
- /** @returns the bounds of this array. */
670
+ /** @returns the bounds of the series. */
561
671
  get bounds(): bounds.Bounds {
562
- return bounds.construct(Number(this.min), Number(this.max));
672
+ return bounds.construct(Number(this.min), Number(this.max), { makeValid: false });
563
673
  }
564
674
 
565
675
  private maybeRecomputeMinMax(update: Series): void {
566
- if (this._cachedMin != null) {
567
- const min = update._cachedMin ?? update.calcRawMin();
568
- if (min < this._cachedMin) this._cachedMin = min;
676
+ if (this.cachedMin != null) {
677
+ const min = update.cachedMin ?? update.calcRawMin();
678
+ if (min < this.cachedMin) this.cachedMin = min;
569
679
  }
570
- if (this._cachedMax != null) {
571
- const max = update._cachedMax ?? update.calcRawMax();
572
- if (max > this._cachedMax) this._cachedMax = max;
680
+ if (this.cachedMax != null) {
681
+ const max = update.cachedMax ?? update.calcRawMax();
682
+ if (max > this.cachedMax) this.cachedMax = max;
573
683
  }
574
684
  }
575
685
 
576
- enrich(): void {
577
- let _ = this.max;
578
-
579
- _ = this.min;
580
- }
581
-
582
- get range(): math.Numeric {
583
- return addSamples(this.max, -this.min);
584
- }
585
-
686
+ /**
687
+ * @returns the value at the given alignment.
688
+ * @param alignment the alignment to get the value at.
689
+ * @param required throws an error if the value is not found.
690
+ */
586
691
  atAlignment(alignment: bigint, required: true): T;
587
692
 
693
+ /**
694
+ * @returns the value at the given alignment.
695
+ * @param alignment the alignment to get the value at.
696
+ * @param required throws an error if the value is not found.
697
+ */
588
698
  atAlignment(alignment: bigint, required?: false): T | undefined;
589
699
 
590
700
  atAlignment(alignment: bigint, required?: boolean): T | undefined {
@@ -596,8 +706,18 @@ export class Series<T extends TelemValue = TelemValue> {
596
706
  return this.at(index, required as true);
597
707
  }
598
708
 
709
+ /**
710
+ * @returns the value at the given index.
711
+ * @param index the index to get the value at.
712
+ * @param required throws an error if the value is not found.
713
+ */
599
714
  at(index: number, required: true): T;
600
715
 
716
+ /**
717
+ * @returns the value at the given index.
718
+ * @param index the index to get the value at.
719
+ * @param required throws an error if the value is not found.
720
+ */
601
721
  at(index: number, required?: false): T | undefined;
602
722
 
603
723
  at(index: number, required?: boolean): T | undefined {
@@ -659,6 +779,13 @@ export class Series<T extends TelemValue = TelemValue> {
659
779
  return left;
660
780
  }
661
781
 
782
+ /**
783
+ * Updates the WebGL buffer for the series if it is not up to date. This method
784
+ * should be called whenever a series has been previously buffered to the GPU and
785
+ * then modified via calls to write().
786
+ * @param gl the buffer controller to update the buffer for. This controller should
787
+ * be the same buffer previously passed to {@method acquire} or {@method updateGLBuffer}.
788
+ */
662
789
  updateGLBuffer(gl: GLBufferController): void {
663
790
  this.gl.control = gl;
664
791
  if (
@@ -696,10 +823,23 @@ export class Series<T extends TelemValue = TelemValue> {
696
823
  }
697
824
  }
698
825
 
826
+ /**
827
+ * Reinterprets the series as containing strings as its JS primitive type.
828
+ * @throws if the series does not have a data type of STRING or JSON.
829
+ */
699
830
  as(jsType: "string"): Series<string>;
700
831
 
832
+ /**
833
+ * Reinterprets the series as containing numbers as its JS primitive type.
834
+ * @throws if the series does not have a numeric data type.
835
+ */
701
836
  as(jsType: "number"): Series<number>;
702
837
 
838
+ /**
839
+ * Reinterprets the series as containing bigints as its JS primitive type.
840
+ * @throws if the series does not have a data type that requires bigints i.e.
841
+ * INT64 and UINT64.
842
+ */
703
843
  as(jsType: "bigint"): Series<bigint>;
704
844
 
705
845
  as<T extends TelemValue>(jsType: "string" | "number" | "bigint"): Series<T> {
@@ -707,6 +847,7 @@ export class Series<T extends TelemValue = TelemValue> {
707
847
  return this as unknown as Series<T>;
708
848
  }
709
849
 
850
+ /** @returns a digest containing information about the series. */
710
851
  get digest(): SeriesDigest {
711
852
  return {
712
853
  key: this.key,
@@ -716,21 +857,21 @@ export class Series<T extends TelemValue = TelemValue> {
716
857
  lower: alignmentDigest(this.alignmentBounds.lower),
717
858
  upper: alignmentDigest(this.alignmentBounds.upper),
718
859
  },
719
- timeRange: this.timeRange?.toString(),
860
+ timeRange: this.timeRange.toString(),
720
861
  length: this.length,
721
862
  capacity: this.capacity,
722
863
  };
723
864
  }
724
865
 
725
- get memInfo(): SeriesMemInfo {
726
- return {
727
- key: this.key,
728
- length: this.length,
729
- byteLength: this.byteLength,
730
- glBuffer: this.gl.buffer != null,
731
- };
732
- }
733
-
866
+ /**
867
+ * @returns the alignment bounds of the series, representing the logical space
868
+ * occupied by the series in a group of series. This is typically used to order the
869
+ * series within a channel's data.
870
+ *
871
+ * The lower bound is the alignment of the first sample, and the upper bound is the
872
+ * alignment of the last sample + 1. The lower bound is inclusive, while the upper bound
873
+ * is exclusive.
874
+ */
734
875
  get alignmentBounds(): bounds.Bounds<bigint> {
735
876
  return bounds.construct(this.alignment, this.alignment + BigInt(this.length));
736
877
  }
@@ -743,6 +884,12 @@ export class Series<T extends TelemValue = TelemValue> {
743
884
  this.gl.control = null;
744
885
  }
745
886
 
887
+ /**
888
+ * @returns the WebGL buffer for the series. This method should only be called after
889
+ * the series has been buffered to the GPU via a call to {@method acquire} or
890
+ * {@method updateGLBuffer}.
891
+ * @throws if the series has not been buffered to the GPU.
892
+ */
746
893
  get glBuffer(): WebGLBuffer {
747
894
  if (this.gl.buffer == null) throw new Error("gl buffer not initialized");
748
895
  if (!(this.gl.prevBuffer === this.writePos)) console.warn("buffer not updated");
@@ -759,19 +906,43 @@ export class Series<T extends TelemValue = TelemValue> {
759
906
  return new FixedSeriesIterator(this) as Iterator<T>;
760
907
  }
761
908
 
909
+ /**
910
+ * Returns a slice of the series from start to end.
911
+ * @param start The start index (inclusive).
912
+ * @param end The end index (exclusive).
913
+ * @returns A new series containing the sliced data.
914
+ */
762
915
  slice(start: number, end?: number): Series {
763
916
  return this.sliceSub(false, start, end);
764
917
  }
765
918
 
919
+ /**
920
+ * Returns a subarray view of the series from start to end.
921
+ * @param start The start index (inclusive).
922
+ * @param end The end index (exclusive).
923
+ * @returns A new series containing the subarray data.
924
+ */
766
925
  sub(start: number, end?: number): Series {
767
926
  return this.sliceSub(true, start, end);
768
927
  }
769
928
 
770
- subIterator(start: number, end?: number): IterableIterator<T> {
929
+ /**
930
+ * Returns an iterator over a portion of the series.
931
+ * @param start The start index (inclusive).
932
+ * @param end The end index (exclusive).
933
+ * @returns An iterator over the specified range.
934
+ */
935
+ subIterator(start: number, end?: number): Iterator<T> {
771
936
  return new SubIterator(this, start, end ?? this.length);
772
937
  }
773
938
 
774
- subAlignmentIterator(start: bigint, end: bigint): IterableIterator<T> {
939
+ /**
940
+ * Returns an iterator over a portion of the series based on alignment.
941
+ * @param start The start alignment (inclusive).
942
+ * @param end The end alignment (exclusive).
943
+ * @returns An iterator over the specified alignment range.
944
+ */
945
+ subAlignmentIterator(start: bigint, end: bigint): Iterator<T> {
775
946
  return new SubIterator(
776
947
  this,
777
948
  Number(start - this.alignment),
@@ -807,6 +978,11 @@ export class Series<T extends TelemValue = TelemValue> {
807
978
  });
808
979
  }
809
980
 
981
+ /**
982
+ * Creates a new series with a different alignment.
983
+ * @param alignment The new alignment value.
984
+ * @returns A new series with the specified alignment.
985
+ */
810
986
  reAlign(alignment: bigint): Series {
811
987
  return new Series({
812
988
  data: this.buffer,
@@ -818,26 +994,44 @@ export class Series<T extends TelemValue = TelemValue> {
818
994
  });
819
995
  }
820
996
 
997
+ /**
998
+ * Returns a string representation of the series.
999
+ * For series with more than 10 elements, shows the first 5 and last 5 elements.
1000
+ * @returns A string representation of the series.
1001
+ */
821
1002
  toString(): string {
822
- let data = `${this.dataType.toString()} ${this.length} [`;
1003
+ let data = `Series(${this.dataType.toString()} ${this.length} [`;
823
1004
  if (this.length <= 10) data += Array.from(this).map((v) => v.toString());
824
1005
  else {
825
1006
  for (let i = 0; i < 5; i++) {
826
1007
  data += `${this.at(i)?.toString()}`;
827
- if (i < 4) data += ",";
1008
+ data += ",";
828
1009
  }
829
- data += "...";
1010
+ data += "...,";
830
1011
  for (let i = -5; i < 0; i++) {
831
1012
  data += this.at(i)?.toString();
832
1013
  if (i < -1) data += ",";
833
1014
  }
834
1015
  }
835
- data += "]";
1016
+ data += "])";
836
1017
  return data;
837
1018
  }
838
1019
  }
839
1020
 
840
- class SubIterator<T> implements Iterator<T>, Iterable<T> {
1021
+ /** @returns true if a Series can be constructed from the given value, and false otherwise. */
1022
+ export const isCrudeSeries = (value: unknown): value is CrudeSeries => {
1023
+ if (value == null) return false;
1024
+ if (Array.isArray(value)) return true;
1025
+ if (value instanceof ArrayBuffer) return true;
1026
+ if (ArrayBuffer.isView(value) && !(value instanceof DataView)) return true;
1027
+ if (value instanceof Series) return true;
1028
+ return isTelemValue(value);
1029
+ };
1030
+
1031
+ /** @returns true if the given value is an instance of the series class. */
1032
+ const isSeries = instance.createMatcher<Series>(SERIES_DISCRIMINATOR, Series);
1033
+
1034
+ class SubIterator<T> implements Iterator<T> {
841
1035
  private readonly series: Series;
842
1036
  private readonly end: number;
843
1037
  private index: number;
@@ -853,10 +1047,6 @@ class SubIterator<T> implements Iterator<T>, Iterable<T> {
853
1047
  if (this.index >= this.end) return { done: true, value: undefined };
854
1048
  return { done: false, value: this.series.at(this.index++, true) as T };
855
1049
  }
856
-
857
- [Symbol.iterator](): Iterator<T> {
858
- return this;
859
- }
860
1050
  }
861
1051
 
862
1052
  class StringSeriesIterator implements Iterator<string> {
@@ -884,15 +1074,11 @@ class StringSeriesIterator implements Iterator<string> {
884
1074
  const s = this.decoder.decode(this.series.buffer.slice(start, end));
885
1075
  return { done: false, value: s };
886
1076
  }
887
-
888
- [Symbol.iterator](): Iterator<TelemValue> {
889
- return this;
890
- }
891
1077
  }
892
1078
 
893
1079
  class JSONSeriesIterator implements Iterator<unknown> {
894
1080
  private readonly wrapped: Iterator<string>;
895
- private static SCHEMA = z.record(z.string(), z.unknown());
1081
+ private static schema = z.record(z.string(), z.unknown());
896
1082
 
897
1083
  constructor(wrapped: Iterator<string>) {
898
1084
  this.wrapped = wrapped;
@@ -903,15 +1089,9 @@ class JSONSeriesIterator implements Iterator<unknown> {
903
1089
  if (next.done === true) return { done: true, value: undefined };
904
1090
  return {
905
1091
  done: false,
906
- value: binary.JSON_CODEC.decodeString(next.value, JSONSeriesIterator.SCHEMA),
1092
+ value: binary.JSON_CODEC.decodeString(next.value, JSONSeriesIterator.schema),
907
1093
  };
908
1094
  }
909
-
910
- [Symbol.iterator](): Iterator<object> {
911
- return this;
912
- }
913
-
914
- [Symbol.toStringTag] = "JSONSeriesIterator";
915
1095
  }
916
1096
 
917
1097
  class FixedSeriesIterator implements Iterator<math.Numeric> {
@@ -929,17 +1109,59 @@ class FixedSeriesIterator implements Iterator<math.Numeric> {
929
1109
  value: this.series.at(this.index++, true) as math.Numeric,
930
1110
  };
931
1111
  }
932
-
933
- [Symbol.iterator](): Iterator<math.Numeric> {
934
- return this;
935
- }
936
-
937
- [Symbol.toStringTag] = "SeriesIterator";
938
1112
  }
939
1113
 
1114
+ /**
1115
+ * MultiSeries represents a collection of Series instances that share the same data type.
1116
+ * It provides a unified interface for working with multiple series as if they were a single
1117
+ * continuous series.
1118
+ *
1119
+
1120
+ */
940
1121
  export class MultiSeries<T extends TelemValue = TelemValue> implements Iterable<T> {
1122
+ /** The array of series in this collection */
941
1123
  readonly series: Array<Series<T>>;
942
1124
 
1125
+ /**
1126
+ * The MultiSeries constructor accepts an optional array of Series instances. All series
1127
+ * in the collection must have the same data type.
1128
+ *
1129
+ * @example
1130
+ * // Create an empty MultiSeries
1131
+ * const ms1 = new MultiSeries();
1132
+ *
1133
+ * @example
1134
+ * // Create a MultiSeries from multiple numeric series
1135
+ * const s1 = new Series(new Float32Array([1, 2, 3]));
1136
+ * const s2 = new Series(new Float32Array([4, 5, 6]));
1137
+ * const ms2 = new MultiSeries([s1, s2]);
1138
+ *
1139
+ * @example
1140
+ * // Create a MultiSeries from string series
1141
+ * const s3 = new Series(["apple", "banana"]);
1142
+ * const s4 = new Series(["carrot", "date"]);
1143
+ * const ms3 = new MultiSeries([s3, s4]);
1144
+ *
1145
+ * @example
1146
+ * // Create a MultiSeries from JSON series
1147
+ * const s5 = new Series([{ a: 1, b: "apple" }]);
1148
+ * const s6 = new Series([{ a: 2, b: "banana" }]);
1149
+ * const ms4 = new MultiSeries([s5, s6]);
1150
+ *
1151
+ * @example
1152
+ * // Add series to an existing MultiSeries
1153
+ * const ms5 = new MultiSeries();
1154
+ * ms5.push(s1);
1155
+ * ms5.push(s2);
1156
+ *
1157
+ * @example
1158
+ * // Combine two MultiSeries
1159
+ * const ms6 = new MultiSeries([s1]);
1160
+ * const ms7 = new MultiSeries([s2]);
1161
+ * ms6.push(ms7);
1162
+ *
1163
+ * @throws Error if attempting to add a series with a different data type
1164
+ */
943
1165
  constructor(series: Array<Series<T>> = []) {
944
1166
  if (series.length !== 0) {
945
1167
  const type = series[0].dataType;
@@ -950,10 +1172,23 @@ export class MultiSeries<T extends TelemValue = TelemValue> implements Iterable<
950
1172
  this.series = series;
951
1173
  }
952
1174
 
1175
+ /**
1176
+ * Reinterprets the series as containing strings as its JS primitive type.
1177
+ * @throws if the series does not have a data type of STRING or JSON.
1178
+ */
953
1179
  as(jsType: "string"): MultiSeries<string>;
954
1180
 
1181
+ /**
1182
+ * Reinterprets the series as containing numbers as its JS primitive type.
1183
+ * @throws if the series does not have a numeric data type.
1184
+ */
955
1185
  as(jsType: "number"): MultiSeries<number>;
956
1186
 
1187
+ /**
1188
+ * Reinterprets the series as containing bigints as its JS primitive type.
1189
+ * @throws if the series does not have a data type that requires bigints i.e.
1190
+ * INT64 and UINT64.
1191
+ */
957
1192
  as(jsType: "bigint"): MultiSeries<bigint>;
958
1193
 
959
1194
  as<T extends TelemValue>(jsType: "string" | "number" | "bigint"): MultiSeries<T> {
@@ -961,11 +1196,20 @@ export class MultiSeries<T extends TelemValue = TelemValue> implements Iterable<
961
1196
  return this as unknown as MultiSeries<T>;
962
1197
  }
963
1198
 
1199
+ /**
1200
+ * Returns the data type of the series in this collection. If the collection is empty,
1201
+ * returns DataType.UNKNOWN.
1202
+ */
964
1203
  get dataType(): DataType {
965
1204
  if (this.series.length === 0) return DataType.UNKNOWN;
966
1205
  return this.series[0].dataType;
967
1206
  }
968
1207
 
1208
+ /**
1209
+ * Returns the combined time range of all series in the collection. If the collection
1210
+ * is empty, returns TimeRange.ZERO. The time range spans from the start of the first
1211
+ * series to the end of the last series.
1212
+ */
969
1213
  get timeRange(): TimeRange {
970
1214
  if (this.series.length === 0) return TimeRange.ZERO;
971
1215
  return new TimeRange(
@@ -974,11 +1218,20 @@ export class MultiSeries<T extends TelemValue = TelemValue> implements Iterable<
974
1218
  );
975
1219
  }
976
1220
 
1221
+ /**
1222
+ * Returns the alignment of the first series in the collection. If the collection is
1223
+ * empty, returns 0n.
1224
+ */
977
1225
  get alignment(): bigint {
978
1226
  if (this.series.length === 0) return 0n;
979
1227
  return this.series[0].alignment;
980
1228
  }
981
1229
 
1230
+ /**
1231
+ * Returns the alignment bounds of the entire collection. The lower bound is the
1232
+ * alignment of the first series, and the upper bound is the alignment of the last
1233
+ * series + its length. If the collection is empty, returns bounds.construct(0n, 0n).
1234
+ */
982
1235
  get alignmentBounds(): bounds.Bounds<bigint> {
983
1236
  if (this.series.length === 0) return bounds.construct(0n, 0n);
984
1237
  return bounds.construct(
@@ -987,27 +1240,49 @@ export class MultiSeries<T extends TelemValue = TelemValue> implements Iterable<
987
1240
  );
988
1241
  }
989
1242
 
1243
+ /**
1244
+ * Adds a series or another MultiSeries to this collection.
1245
+ * @param series - The series or MultiSeries to add. Must have the same data type
1246
+ * as the existing series in this collection.
1247
+ * @throws Error if the series being added has a different data type
1248
+ */
990
1249
  push(series: Series<T>): void;
991
1250
  push(series: MultiSeries<T>): void;
992
1251
 
993
1252
  push(series: Series<T> | MultiSeries<T>): void {
994
- if ("isSynnaxSeries" in series && series.isSynnaxSeries) this.series.push(series);
995
- else this.series.push(...(series as MultiSeries<T>).series);
1253
+ const invalidDataTypeError = () =>
1254
+ new Error(
1255
+ `cannot push a ${series.dataType.toString()} series to a ${this.dataType.toString()} multi-series`,
1256
+ );
1257
+ const dtsEqual = series.dataType.equals(this.dataType);
1258
+ if (isSeries(series)) {
1259
+ if (this.series.length !== 0 && !dtsEqual) throw invalidDataTypeError();
1260
+ this.series.push(series);
1261
+ } else {
1262
+ if (this.series.length !== 0 && series.series.length !== 0 && !dtsEqual)
1263
+ throw invalidDataTypeError();
1264
+ this.series.push(...series.series);
1265
+ }
996
1266
  }
997
1267
 
1268
+ /**
1269
+ * Returns the total length of all series in the collection.
1270
+ * @returns The sum of the lengths of all series.
1271
+ */
998
1272
  get length(): number {
999
1273
  return this.series.reduce((a, b) => a + b.length, 0);
1000
1274
  }
1001
1275
 
1276
+ /**
1277
+ * Returns the value at the specified alignment.
1278
+ * @param alignment - The alignment to get the value at.
1279
+ * @param required - If true, throws an error if the value is not found.
1280
+ * @returns The value at the specified alignment, or undefined if not found.
1281
+ * @throws Error if required is true and the value is not found.
1282
+ */
1002
1283
  atAlignment(alignment: bigint, required: true): T;
1003
-
1004
1284
  atAlignment(alignment: bigint, required?: false): T | undefined;
1005
-
1006
1285
  atAlignment(alignment: bigint, required?: boolean): T | undefined {
1007
- if (this.series.length === 0) {
1008
- if (required) throw new Error(`[series] - no value at alignment ${alignment}`);
1009
- return undefined;
1010
- }
1011
1286
  for (const ser of this.series)
1012
1287
  if (bounds.contains(ser.alignmentBounds, alignment))
1013
1288
  return ser.atAlignment(alignment, required as true);
@@ -1015,10 +1290,15 @@ export class MultiSeries<T extends TelemValue = TelemValue> implements Iterable<
1015
1290
  return undefined;
1016
1291
  }
1017
1292
 
1293
+ /**
1294
+ * Returns the value at the specified index.
1295
+ * @param index - The index to get the value at.
1296
+ * @param required - If true, throws an error if the value is not found.
1297
+ * @returns The value at the specified index, or undefined if not found.
1298
+ * @throws Error if required is true and the value is not found.
1299
+ */
1018
1300
  at(index: number, required: true): T;
1019
-
1020
1301
  at(index: number, required?: false): T | undefined;
1021
-
1022
1302
  at(index: number, required: boolean = false): T | undefined {
1023
1303
  if (index < 0) index = this.length + index;
1024
1304
  for (const ser of this.series) {
@@ -1029,10 +1309,22 @@ export class MultiSeries<T extends TelemValue = TelemValue> implements Iterable<
1029
1309
  return undefined;
1030
1310
  }
1031
1311
 
1312
+ /**
1313
+ * Returns an iterator over a portion of the multi-series.
1314
+ * @param start - The start index (inclusive).
1315
+ * @param end - The end index (exclusive).
1316
+ * @returns An iterator over the specified range.
1317
+ */
1032
1318
  subIterator(start: number, end?: number): IterableIterator<T> {
1033
1319
  return new MultiSubIterator(this, start, end ?? this.length);
1034
1320
  }
1035
1321
 
1322
+ /**
1323
+ * Returns an iterator over a portion of the multi-series based on alignment.
1324
+ * @param start - The start alignment (inclusive).
1325
+ * @param end - The end alignment (exclusive).
1326
+ * @returns An iterator over the specified alignment range.
1327
+ */
1036
1328
  subAlignmentIterator(start: bigint, end: bigint): IterableIterator<T> {
1037
1329
  if (start >= this.alignmentBounds.upper || end <= this.alignmentBounds.lower)
1038
1330
  return noopIterableIterator;
@@ -1059,8 +1351,15 @@ export class MultiSeries<T extends TelemValue = TelemValue> implements Iterable<
1059
1351
  return new MultiSubIterator(this, startIdx, endIdx);
1060
1352
  }
1061
1353
 
1354
+ /**
1355
+ * Returns an iterator over the specified alignment range and span.
1356
+ * @param start - The start alignment (inclusive).
1357
+ * @param span - The number of samples to include.
1358
+ * @returns An iterator over the specified range.
1359
+ */
1062
1360
  subAlignmentSpanIterator(start: bigint, span: number): IterableIterator<T> {
1063
1361
  if (start >= this.alignmentBounds.upper) return noopIterableIterator;
1362
+ span = Math.min(span, Number(this.distance(start, this.alignmentBounds.upper)));
1064
1363
  let startIdx = 0;
1065
1364
  for (let i = 0; i < this.series.length; i++) {
1066
1365
  const ser = this.series[i];
@@ -1074,18 +1373,32 @@ export class MultiSeries<T extends TelemValue = TelemValue> implements Iterable<
1074
1373
  return new MultiSubIterator(this, startIdx, startIdx + span);
1075
1374
  }
1076
1375
 
1376
+ /**
1377
+ * Updates the WebGL buffer for all series in the collection.
1378
+ * @param gl - The WebGL buffer controller to use.
1379
+ */
1077
1380
  updateGLBuffer(gl: GLBufferController): void {
1078
1381
  this.series.forEach((s) => s.updateGLBuffer(gl));
1079
1382
  }
1080
1383
 
1384
+ /**
1385
+ * Returns the bounds containing the minimum and maximum values across all series.
1386
+ */
1081
1387
  get bounds(): bounds.Bounds {
1082
1388
  return bounds.max(this.series.map((s) => s.bounds));
1083
1389
  }
1084
1390
 
1391
+ /**
1392
+ * Returns the sum of the byte lengths of all series.
1393
+ */
1085
1394
  get byteLength(): Size {
1086
1395
  return new Size(this.series.reduce((a, b) => a + b.byteLength.valueOf(), 0));
1087
1396
  }
1088
1397
 
1398
+ /**
1399
+ * Returns a combined typed array containing all data from all series.
1400
+ * @returns A typed array containing all data from all series.
1401
+ */
1089
1402
  get data(): TypedArray {
1090
1403
  const buf = new this.dataType.Array(this.length);
1091
1404
  let offset = 0;
@@ -1096,30 +1409,60 @@ export class MultiSeries<T extends TelemValue = TelemValue> implements Iterable<
1096
1409
  return new this.dataType.Array(buf.buffer);
1097
1410
  }
1098
1411
 
1412
+ /**
1413
+ * Traverses the alignment space by a given distance from a start point.
1414
+ * @param start - The starting alignment.
1415
+ * @param dist - The distance to traverse.
1416
+ * @returns The resulting alignment after traversal.
1417
+ */
1099
1418
  traverseAlignment(start: bigint, dist: bigint): bigint {
1100
1419
  const b = this.series.map((s) => s.alignmentBounds);
1101
1420
  return bounds.traverse(b, start, dist);
1102
1421
  }
1103
1422
 
1104
- acquire(): void {
1105
- this.series.forEach((s) => s.acquire());
1423
+ /**
1424
+ * Acquires a reference to the WebGL buffer for all series.
1425
+ * @param gl - Optional WebGL buffer controller to use.
1426
+ */
1427
+ acquire(gl?: GLBufferController): void {
1428
+ this.series.forEach((s) => s.acquire(gl));
1106
1429
  }
1107
1430
 
1431
+ /**
1432
+ * Releases the WebGL buffer reference for all series.
1433
+ */
1108
1434
  release(): void {
1109
1435
  this.series.forEach((s) => s.release());
1110
1436
  }
1111
1437
 
1438
+ /**
1439
+ * Calculates the number of samples between two alignments in the multi-series.
1440
+ * @param start - The starting alignment.
1441
+ * @param end - The ending alignment.
1442
+ * @returns The distance between the alignments.
1443
+ */
1112
1444
  distance(start: bigint, end: bigint): bigint {
1113
1445
  const b = this.series.map((s) => s.alignmentBounds);
1114
1446
  return bounds.distance(b, start, end);
1115
1447
  }
1116
1448
 
1117
- parseJSON<Z extends z.ZodTypeAny>(schema: Z): Array<z.output<Z>> {
1449
+ /**
1450
+ * Parses a JSON multi-series into an array of values using the provided zod schema.
1451
+ * @template Z - The zod schema type.
1452
+ * @param schema - The zod schema to use to parse the JSON series.
1453
+ * @throws Error if the series does not have a data type of JSON.
1454
+ * @returns An array of values parsed from the JSON series.
1455
+ */
1456
+ parseJSON<Z extends z.ZodType>(schema: Z): Array<z.infer<Z>> {
1118
1457
  if (!this.dataType.equals(DataType.JSON))
1119
1458
  throw new Error("cannot parse non-JSON series as JSON");
1120
1459
  return this.series.flatMap((s) => s.parseJSON(schema));
1121
1460
  }
1122
1461
 
1462
+ /**
1463
+ * Returns an iterator over all values in the multi-series.
1464
+ * @returns An iterator that yields all values from all series in sequence.
1465
+ */
1123
1466
  [Symbol.iterator](): Iterator<T> {
1124
1467
  if (this.series.length === 0)
1125
1468
  return {
@@ -1154,8 +1497,6 @@ class MultiSeriesIterator<T extends TelemValue = TelemValue> implements Iterator
1154
1497
  [Symbol.iterator](): Iterator<TelemValue | unknown> {
1155
1498
  return this;
1156
1499
  }
1157
-
1158
- [Symbol.toStringTag] = "MultiSeriesIterator";
1159
1500
  }
1160
1501
 
1161
1502
  class MultiSubIterator<T extends TelemValue = TelemValue>