@synnaxlabs/client 0.2.1 → 0.13.6

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 (299) hide show
  1. package/.eslintrc.cjs +18 -0
  2. package/.pytest_cache/README.md +8 -0
  3. package/.turbo/turbo-build.log +16 -0
  4. package/LICENSE +4 -21
  5. package/{build/module/lib → dist/auth}/auth.d.ts +16 -19
  6. package/dist/auth/index.d.ts +1 -0
  7. package/dist/cdc/external.d.ts +1 -0
  8. package/dist/cdc/index.d.ts +1 -0
  9. package/dist/cdc/observable.d.ts +17 -0
  10. package/dist/channel/client.d.ts +58 -0
  11. package/dist/channel/creator.d.ts +8 -0
  12. package/dist/channel/external.d.ts +4 -0
  13. package/dist/channel/index.d.ts +1 -0
  14. package/dist/channel/payload.d.ts +63 -0
  15. package/dist/channel/retriever.d.ts +49 -0
  16. package/dist/client.cjs.js +23050 -0
  17. package/dist/client.cjs.js.map +1 -0
  18. package/dist/client.d.ts +73 -0
  19. package/dist/client.es.js +23050 -0
  20. package/dist/client.es.js.map +1 -0
  21. package/dist/connection/checker.d.ts +66 -0
  22. package/dist/connection/index.d.ts +1 -0
  23. package/dist/control/authority.d.ts +6 -0
  24. package/dist/control/external.d.ts +2 -0
  25. package/dist/control/index.d.ts +1 -0
  26. package/dist/control/state.d.ts +81 -0
  27. package/{build/main/lib → dist}/errors.d.ts +6 -3
  28. package/dist/framer/adapter.d.ts +21 -0
  29. package/dist/framer/client.d.ts +44 -0
  30. package/dist/framer/external.d.ts +5 -0
  31. package/dist/framer/frame.d.ts +251 -0
  32. package/dist/framer/index.d.ts +1 -0
  33. package/{build/module/lib/segment → dist/framer}/iterator.d.ts +32 -64
  34. package/dist/framer/streamProxy.d.ts +12 -0
  35. package/dist/framer/streamer.d.ts +17 -0
  36. package/dist/framer/writer.d.ts +257 -0
  37. package/dist/index.d.ts +16 -0
  38. package/dist/label/client.d.ts +25 -0
  39. package/dist/label/external.d.ts +4 -0
  40. package/dist/label/index.d.ts +1 -0
  41. package/dist/label/payload.d.ts +20 -0
  42. package/dist/label/retriever.d.ts +13 -0
  43. package/dist/label/writer.d.ts +26 -0
  44. package/dist/ontology/cdc.d.ts +25 -0
  45. package/dist/ontology/client.d.ts +25 -0
  46. package/dist/ontology/external.d.ts +3 -0
  47. package/dist/ontology/group/client.d.ts +11 -0
  48. package/dist/ontology/group/external.d.ts +2 -0
  49. package/dist/ontology/group/group.d.ts +7 -0
  50. package/dist/ontology/group/index.d.ts +1 -0
  51. package/dist/ontology/group/payload.d.ts +40 -0
  52. package/dist/ontology/group/writer.d.ts +13 -0
  53. package/dist/ontology/index.d.ts +1 -0
  54. package/dist/ontology/ontology.spec.d.ts +1 -0
  55. package/dist/ontology/payload.d.ts +235 -0
  56. package/dist/ontology/retriever.d.ts +12 -0
  57. package/dist/ontology/signals.d.ts +25 -0
  58. package/dist/ontology/writer.d.ts +9 -0
  59. package/dist/ranger/active.d.ts +9 -0
  60. package/dist/ranger/alias.d.ts +32 -0
  61. package/dist/ranger/client.d.ts +31 -0
  62. package/dist/ranger/external.d.ts +6 -0
  63. package/dist/ranger/index.d.ts +1 -0
  64. package/dist/ranger/kv.d.ts +50 -0
  65. package/dist/ranger/payload.d.ts +94 -0
  66. package/dist/ranger/range.d.ts +29 -0
  67. package/dist/ranger/ranger.spec.d.ts +1 -0
  68. package/dist/ranger/retriever.d.ts +10 -0
  69. package/dist/ranger/writer.d.ts +9 -0
  70. package/{build/main → dist}/setupspecs.d.ts +2 -2
  71. package/dist/signals/external.d.ts +1 -0
  72. package/dist/signals/index.d.ts +1 -0
  73. package/dist/signals/observable.d.ts +17 -0
  74. package/dist/transport.d.ts +10 -0
  75. package/dist/user/index.d.ts +1 -0
  76. package/{build/main/lib → dist}/user/payload.d.ts +3 -3
  77. package/dist/util/telem.d.ts +2 -0
  78. package/dist/workspace/client.d.ts +22 -0
  79. package/dist/workspace/external.d.ts +2 -0
  80. package/dist/workspace/index.d.ts +1 -0
  81. package/dist/workspace/lineplot/client.d.ts +15 -0
  82. package/dist/workspace/lineplot/external.d.ts +2 -0
  83. package/dist/workspace/lineplot/index.d.ts +1 -0
  84. package/dist/workspace/lineplot/linePlot.spec.d.ts +1 -0
  85. package/dist/workspace/lineplot/payload.d.ts +31 -0
  86. package/dist/workspace/lineplot/retriever.d.ts +9 -0
  87. package/dist/workspace/lineplot/writer.d.ts +39 -0
  88. package/dist/workspace/payload.d.ts +31 -0
  89. package/dist/workspace/pid/client.d.ts +16 -0
  90. package/dist/workspace/pid/external.d.ts +2 -0
  91. package/dist/workspace/pid/index.d.ts +1 -0
  92. package/dist/workspace/pid/payload.d.ts +37 -0
  93. package/dist/workspace/pid/pid.spec.d.ts +1 -0
  94. package/dist/workspace/pid/retriever.d.ts +9 -0
  95. package/dist/workspace/pid/writer.d.ts +46 -0
  96. package/dist/workspace/retriever.d.ts +12 -0
  97. package/dist/workspace/workspace.spec.d.ts +1 -0
  98. package/dist/workspace/writer.d.ts +55 -0
  99. package/package.json +27 -98
  100. package/src/auth/auth.spec.ts +46 -0
  101. package/src/auth/auth.ts +83 -0
  102. package/src/auth/index.ts +10 -0
  103. package/src/channel/channel.spec.ts +82 -0
  104. package/src/channel/client.ts +209 -0
  105. package/src/channel/creator.ts +43 -0
  106. package/src/channel/external.ts +13 -0
  107. package/src/channel/index.ts +10 -0
  108. package/src/channel/payload.ts +52 -0
  109. package/src/channel/retriever.ts +160 -0
  110. package/src/client.ts +116 -0
  111. package/src/connection/checker.ts +104 -0
  112. package/src/connection/connection.spec.ts +35 -0
  113. package/src/connection/index.ts +10 -0
  114. package/src/control/authority.ts +26 -0
  115. package/src/control/external.ts +11 -0
  116. package/src/control/index.ts +10 -0
  117. package/src/control/state.spec.ts +24 -0
  118. package/src/control/state.ts +133 -0
  119. package/src/errors.ts +163 -0
  120. package/src/framer/adapter.ts +116 -0
  121. package/src/framer/client.ts +116 -0
  122. package/src/framer/external.ts +14 -0
  123. package/src/framer/frame.spec.ts +317 -0
  124. package/src/framer/frame.ts +412 -0
  125. package/src/framer/index.ts +10 -0
  126. package/src/framer/iterator.spec.ts +62 -0
  127. package/src/framer/iterator.ts +240 -0
  128. package/src/framer/streamProxy.ts +59 -0
  129. package/src/framer/streamer.spec.ts +42 -0
  130. package/src/framer/streamer.ts +86 -0
  131. package/src/framer/writer.spec.ts +52 -0
  132. package/src/framer/writer.ts +236 -0
  133. package/src/index.ts +53 -0
  134. package/src/label/client.ts +103 -0
  135. package/src/label/external.ts +13 -0
  136. package/src/label/index.ts +10 -0
  137. package/src/label/label.spec.ts +51 -0
  138. package/src/label/payload.ts +29 -0
  139. package/src/label/retriever.ts +65 -0
  140. package/src/label/writer.ts +90 -0
  141. package/src/ontology/client.ts +104 -0
  142. package/src/ontology/external.ts +12 -0
  143. package/src/ontology/group/client.ts +40 -0
  144. package/src/ontology/group/external.ts +11 -0
  145. package/src/ontology/group/group.spec.ts +46 -0
  146. package/src/ontology/group/group.ts +27 -0
  147. package/src/ontology/group/index.ts +10 -0
  148. package/src/ontology/group/payload.ts +65 -0
  149. package/src/ontology/group/writer.ts +48 -0
  150. package/src/ontology/index.ts +10 -0
  151. package/src/ontology/ontology.spec.ts +114 -0
  152. package/src/ontology/payload.ts +118 -0
  153. package/src/ontology/retriever.ts +91 -0
  154. package/src/ontology/signals.ts +135 -0
  155. package/src/ontology/writer.ts +49 -0
  156. package/src/ranger/active.ts +56 -0
  157. package/src/ranger/alias.ts +183 -0
  158. package/src/ranger/client.ts +129 -0
  159. package/src/ranger/external.ts +15 -0
  160. package/src/ranger/index.ts +10 -0
  161. package/src/ranger/kv.ts +91 -0
  162. package/src/ranger/payload.ts +70 -0
  163. package/src/ranger/range.ts +95 -0
  164. package/src/ranger/ranger.spec.ts +201 -0
  165. package/src/ranger/retriever.ts +50 -0
  166. package/src/ranger/writer.ts +80 -0
  167. package/src/setupspecs.ts +25 -0
  168. package/src/signals/external.ts +10 -0
  169. package/src/signals/index.ts +10 -0
  170. package/src/signals/observable.ts +80 -0
  171. package/src/transport.ts +39 -0
  172. package/src/user/index.ts +10 -0
  173. package/src/user/payload.ts +17 -0
  174. package/src/util/telem.ts +19 -0
  175. package/src/vite-env.d.ts +11 -0
  176. package/src/workspace/client.ts +75 -0
  177. package/src/workspace/external.ts +11 -0
  178. package/src/workspace/index.ts +10 -0
  179. package/src/workspace/lineplot/client.ts +51 -0
  180. package/src/workspace/lineplot/external.ts +11 -0
  181. package/src/workspace/lineplot/index.ts +10 -0
  182. package/src/workspace/lineplot/linePlot.spec.ts +78 -0
  183. package/src/workspace/lineplot/payload.ts +29 -0
  184. package/src/workspace/lineplot/retriever.ts +49 -0
  185. package/src/workspace/lineplot/writer.ts +109 -0
  186. package/src/workspace/payload.ts +29 -0
  187. package/src/workspace/pid/client.ts +55 -0
  188. package/src/workspace/pid/external.ts +11 -0
  189. package/src/workspace/pid/index.ts +10 -0
  190. package/src/workspace/pid/payload.ts +31 -0
  191. package/src/workspace/pid/pid.spec.ts +111 -0
  192. package/src/workspace/pid/retriever.ts +45 -0
  193. package/src/workspace/pid/writer.ts +130 -0
  194. package/src/workspace/retriever.ts +66 -0
  195. package/src/workspace/workspace.spec.ts +62 -0
  196. package/src/workspace/writer.ts +103 -0
  197. package/tsconfig.json +7 -0
  198. package/tsconfig.vite.json +4 -0
  199. package/vite.config.ts +25 -0
  200. package/CHANGELOG.md +0 -5
  201. package/build/main/index.d.ts +0 -4
  202. package/build/main/index.js +0 -35
  203. package/build/main/lib/auth.d.ts +0 -54
  204. package/build/main/lib/auth.js +0 -62
  205. package/build/main/lib/auth.spec.js +0 -39
  206. package/build/main/lib/channel/channel.spec.js +0 -49
  207. package/build/main/lib/channel/client.d.ts +0 -94
  208. package/build/main/lib/channel/client.js +0 -134
  209. package/build/main/lib/channel/creator.d.ts +0 -19
  210. package/build/main/lib/channel/creator.js +0 -44
  211. package/build/main/lib/channel/payload.d.ts +0 -25
  212. package/build/main/lib/channel/payload.js +0 -18
  213. package/build/main/lib/channel/registry.d.ts +0 -9
  214. package/build/main/lib/channel/registry.js +0 -37
  215. package/build/main/lib/channel/retriever.d.ts +0 -11
  216. package/build/main/lib/channel/retriever.js +0 -39
  217. package/build/main/lib/client.d.ts +0 -30
  218. package/build/main/lib/client.js +0 -46
  219. package/build/main/lib/errors.js +0 -122
  220. package/build/main/lib/segment/client.d.ts +0 -62
  221. package/build/main/lib/segment/client.js +0 -95
  222. package/build/main/lib/segment/iterator.d.ts +0 -134
  223. package/build/main/lib/segment/iterator.js +0 -253
  224. package/build/main/lib/segment/iterator.spec.js +0 -73
  225. package/build/main/lib/segment/payload.d.ts +0 -16
  226. package/build/main/lib/segment/payload.js +0 -13
  227. package/build/main/lib/segment/splitter.d.ts +0 -7
  228. package/build/main/lib/segment/splitter.js +0 -25
  229. package/build/main/lib/segment/typed.d.ts +0 -15
  230. package/build/main/lib/segment/typed.js +0 -49
  231. package/build/main/lib/segment/validator.d.ts +0 -22
  232. package/build/main/lib/segment/validator.js +0 -64
  233. package/build/main/lib/segment/writer.d.ts +0 -98
  234. package/build/main/lib/segment/writer.js +0 -183
  235. package/build/main/lib/segment/writer.spec.js +0 -90
  236. package/build/main/lib/telem.d.ts +0 -395
  237. package/build/main/lib/telem.js +0 -553
  238. package/build/main/lib/telem.spec.js +0 -152
  239. package/build/main/lib/transport.d.ts +0 -10
  240. package/build/main/lib/transport.js +0 -22
  241. package/build/main/lib/user/payload.js +0 -9
  242. package/build/main/lib/util/telem.d.ts +0 -2
  243. package/build/main/lib/util/telem.js +0 -13
  244. package/build/main/setupspecs.js +0 -17
  245. package/build/module/index.d.ts +0 -4
  246. package/build/module/index.js +0 -5
  247. package/build/module/lib/auth.js +0 -63
  248. package/build/module/lib/auth.spec.js +0 -34
  249. package/build/module/lib/channel/channel.spec.js +0 -44
  250. package/build/module/lib/channel/client.d.ts +0 -94
  251. package/build/module/lib/channel/client.js +0 -134
  252. package/build/module/lib/channel/creator.d.ts +0 -19
  253. package/build/module/lib/channel/creator.js +0 -42
  254. package/build/module/lib/channel/payload.d.ts +0 -25
  255. package/build/module/lib/channel/payload.js +0 -15
  256. package/build/module/lib/channel/registry.d.ts +0 -9
  257. package/build/module/lib/channel/registry.js +0 -36
  258. package/build/module/lib/channel/retriever.d.ts +0 -11
  259. package/build/module/lib/channel/retriever.js +0 -37
  260. package/build/module/lib/client.d.ts +0 -30
  261. package/build/module/lib/client.js +0 -44
  262. package/build/module/lib/errors.d.ts +0 -53
  263. package/build/module/lib/errors.js +0 -113
  264. package/build/module/lib/segment/client.d.ts +0 -62
  265. package/build/module/lib/segment/client.js +0 -94
  266. package/build/module/lib/segment/iterator.js +0 -248
  267. package/build/module/lib/segment/iterator.spec.js +0 -68
  268. package/build/module/lib/segment/payload.d.ts +0 -16
  269. package/build/module/lib/segment/payload.js +0 -10
  270. package/build/module/lib/segment/splitter.d.ts +0 -7
  271. package/build/module/lib/segment/splitter.js +0 -26
  272. package/build/module/lib/segment/typed.d.ts +0 -15
  273. package/build/module/lib/segment/typed.js +0 -49
  274. package/build/module/lib/segment/validator.d.ts +0 -22
  275. package/build/module/lib/segment/validator.js +0 -60
  276. package/build/module/lib/segment/writer.d.ts +0 -98
  277. package/build/module/lib/segment/writer.js +0 -183
  278. package/build/module/lib/segment/writer.spec.js +0 -85
  279. package/build/module/lib/telem.d.ts +0 -395
  280. package/build/module/lib/telem.js +0 -545
  281. package/build/module/lib/telem.spec.js +0 -147
  282. package/build/module/lib/transport.d.ts +0 -10
  283. package/build/module/lib/transport.js +0 -22
  284. package/build/module/lib/user/payload.d.ts +0 -12
  285. package/build/module/lib/user/payload.js +0 -6
  286. package/build/module/lib/util/telem.d.ts +0 -2
  287. package/build/module/lib/util/telem.js +0 -9
  288. package/build/module/setupspecs.d.ts +0 -4
  289. package/build/module/setupspecs.js +0 -16
  290. /package/{build/main/lib → dist/auth}/auth.spec.d.ts +0 -0
  291. /package/{build/main/lib → dist}/channel/channel.spec.d.ts +0 -0
  292. /package/{build/main/lib/segment/iterator.spec.d.ts → dist/connection/connection.spec.d.ts} +0 -0
  293. /package/{build/main/lib/segment/writer.spec.d.ts → dist/control/state.spec.d.ts} +0 -0
  294. /package/{build/main/lib/telem.spec.d.ts → dist/framer/frame.spec.d.ts} +0 -0
  295. /package/{build/module/lib/segment → dist/framer}/iterator.spec.d.ts +0 -0
  296. /package/{build/module/lib/auth.spec.d.ts → dist/framer/streamer.spec.d.ts} +0 -0
  297. /package/{build/module/lib/segment → dist/framer}/writer.spec.d.ts +0 -0
  298. /package/{build/module/lib/channel/channel.spec.d.ts → dist/label/label.spec.d.ts} +0 -0
  299. /package/{build/module/lib/telem.spec.d.ts → dist/ontology/group/group.spec.d.ts} +0 -0
@@ -0,0 +1,412 @@
1
+ // Copyright 2023 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
+ import { Size, Series, TimeRange, toArray, DataType, unique } from "@synnaxlabs/x";
11
+ import { z } from "zod";
12
+
13
+ import {
14
+ type Key,
15
+ type KeyOrName,
16
+ type Keys,
17
+ type Names,
18
+ type Params,
19
+ } from "@/channel/payload";
20
+ import { UnexpectedError, ValidationError } from "@/errors";
21
+
22
+ type ColumnType = "key" | "name" | null;
23
+
24
+ const columnType = (columns: Params): ColumnType => {
25
+ const arrKeys = toArray(columns);
26
+ if (arrKeys.length === 0) return null;
27
+ if (typeof arrKeys[0] === "number") return "key";
28
+ return "name";
29
+ };
30
+
31
+ const validateMatchedColsAndArrays = (columns: Params, arrays: Series[]): void => {
32
+ const colsArr = toArray(columns);
33
+ if (colsArr.length === arrays.length) return;
34
+ const colType = columnType(columns);
35
+ if (columnType === null)
36
+ throw new ValidationError(
37
+ "[Frame] - channel keys or names must be provided when constructing a frame.",
38
+ );
39
+ throw new ValidationError(
40
+ `[Frame] - ${colType as string}s and arrays must be the same length.
41
+ Got ${colsArr.length} ${colType as string}s and ${arrays.length} arrays.`,
42
+ );
43
+ };
44
+
45
+ export type CrudeFrame =
46
+ | Frame
47
+ | FramePayload
48
+ | Map<KeyOrName, Series[] | Series>
49
+ | Record<KeyOrName, Series[] | Series>;
50
+
51
+ /**
52
+ * A frame is a collection of related typed arrays keyed to a particular channel. Frames
53
+ * can be keyed by channel name or channel key, but not both.
54
+ *
55
+ * Frames have two important characteristics: alignment and orientation.
56
+ *
57
+ * A frame's alignment defines how correlated the arrays for different channels in the
58
+ * frame are:
59
+ *
60
+ * - A frame is weakly aligned if it meets the time range occupied by all arrays of a
61
+ * particular channel is the same for all channels in the frame. This means that the
62
+ * arrays for a particular channel can have gaps betwen them.
63
+ *
64
+ * - A strongly aligned frame means that all channels share the same rate/index and
65
+ * there are no gaps in time between arrays. Strongly aligned frames are natural
66
+ * to interpret, as the values in a particular 'row' of the frame share the same
67
+ * timestamp. All frames written to Synnax must be strongly aligned.
68
+ *
69
+ * - Unaligned frames don't meet the requirements for weakly or strongly aligned frames.
70
+ * Unaligned frames are common when reading from channels that don't have the same
71
+ * index/rate and are continuous in different periods of time.
72
+ *
73
+ * Frames also have an orientation: horizontal, vertical, or square.
74
+ *
75
+ * - Horizontal frames have a single channel, and are strongly aligned by default.
76
+ * A horizontal frame typically has a single array (in which case, it's also 'square'),
77
+ * although it can have multiple arrays if all the arrays are continuous in time.
78
+ *
79
+ * - Vertical frames are strongly aligned and have on or more channels, but ONLY a single
80
+ * array per channel. Synnax requires that all frames written to the database are
81
+ * vertical.
82
+ *
83
+ * - Square frames are both horizontal and vertical. Only a frame with a single channel
84
+ * and array can be square.
85
+ */
86
+ export class Frame {
87
+ readonly columns: Keys | Names = [];
88
+ readonly series: Series[] = [];
89
+
90
+ constructor(columnsOrData: Params | CrudeFrame = [], arrays: Series | Series[] = []) {
91
+ if (columnsOrData instanceof Frame) {
92
+ this.columns = columnsOrData.columns;
93
+ this.series = columnsOrData.series;
94
+ return;
95
+ }
96
+
97
+ // Construction from a map.
98
+ if (columnsOrData instanceof Map) {
99
+ columnsOrData.forEach((v, k) => this.push(k, ...toArray(v)));
100
+ return;
101
+ }
102
+
103
+ const isObject = typeof columnsOrData === "object" && !Array.isArray(columnsOrData);
104
+
105
+ // Construction from a payload.
106
+ if (isObject) {
107
+ if ("keys" in columnsOrData && "series" in columnsOrData) {
108
+ const data_ = columnsOrData as FramePayload;
109
+ const arrays = data_.series.map((a) => seriesFromPayload(a));
110
+ validateMatchedColsAndArrays(data_.keys, arrays);
111
+ data_.keys.forEach((key, i) => this.push(key, arrays[i]));
112
+ } else
113
+ Object.entries(columnsOrData).forEach(([k, v]) => this.push(k, ...toArray(v)));
114
+ return;
115
+ }
116
+
117
+ // Construction from a set of arrays and columns.
118
+ if (
119
+ Array.isArray(columnsOrData) ||
120
+ ["string", "number"].includes(typeof columnsOrData)
121
+ ) {
122
+ const data_ = toArray(arrays);
123
+ const cols = toArray(columnsOrData) as Keys | Names;
124
+ validateMatchedColsAndArrays(cols, data_);
125
+ data_.forEach((d, i) => this.push(cols[i], d));
126
+ return;
127
+ }
128
+
129
+ throw new ValidationError(
130
+ `[Frame] - invalid frame construction parameters. data parameter ust be a frame
131
+ payload, a list of lazy arrays, a lazy array, a map, or a record keyed by channel
132
+ name. keys parameter must be a set of channel keys or channel names.`,
133
+ );
134
+ }
135
+
136
+ /**
137
+ * @returns "key" if the frame columns are channel keys, "name" if the columns are
138
+ * channel names, and null if the frame has no columns.
139
+ */
140
+ get colType(): ColumnType {
141
+ if (this.columns.length === 0) return null;
142
+ const firstKey = this.columns[0];
143
+ return typeof firstKey === "string" ? "name" : "key";
144
+ }
145
+
146
+ /**
147
+ * @returns the channel keys if the frame is keyed by channel key, and throws an error
148
+ * otherwise.
149
+ */
150
+ get keys(): Keys {
151
+ if (this.colType === "name") throw new UnexpectedError("colType is not key");
152
+ return (this.columns as Keys) ?? [];
153
+ }
154
+
155
+ /**
156
+ * @returns the unique channel keys if the frame is keyed by channel key, and throws an
157
+ * error otherwise.
158
+ */
159
+ get uniqueKeys(): Keys {
160
+ return unique(this.keys);
161
+ }
162
+
163
+ /**
164
+ * @returns the channel names if the frame is keyed by channel name, and throws an error
165
+ * otherwise.
166
+ */
167
+ get names(): Names {
168
+ if (this.colType === "key") throw new UnexpectedError("colType is not name");
169
+ return (this.columns as Names) ?? [];
170
+ }
171
+
172
+ /**
173
+ * @returns the unique channel names if the frame is keyed by channel name, and throws an
174
+ * otherwise.
175
+ */
176
+ get uniqueNames(): Names {
177
+ return unique(this.names);
178
+ }
179
+
180
+ /**
181
+ * @returns the unique columns in the frame.
182
+ */
183
+ get uniqueColumns(): Keys | Names {
184
+ return this.colType === "key" ? this.uniqueKeys : this.uniqueNames;
185
+ }
186
+
187
+ toPayload(): FramePayload {
188
+ return {
189
+ series: this.series.map((a) => seriesToPayload(a)),
190
+ keys: this.keys,
191
+ };
192
+ }
193
+
194
+ /**
195
+ * @returns true if the frame is vertical. Vertical frames are strongly aligned and
196
+ * have on or more channels, but ONLY a single array per channel. Synnax requires
197
+ * that all frames written to the database are vertical.
198
+ */
199
+ get isVertical(): boolean {
200
+ return this.uniqueColumns.length === this.columns.length;
201
+ }
202
+
203
+ /**
204
+ * @returns true if the frame is horizontal. Horizontal frames have a single channel,
205
+ * and are strongly aligned by default.A horizontal frame typically has a single array
206
+ * (in which case, it's also 'square'), although it can have multiple arrays if all
207
+ * the arrays are continuous in time.
208
+ */
209
+ get isHorizontal(): boolean {
210
+ return this.uniqueColumns.length === 1;
211
+ }
212
+
213
+ /**
214
+ * @returns true if the frame is square. Square frames are both horizontal and vertical.
215
+ * Only a frame with a single channel and array can be square.
216
+ */
217
+ get isSquare(): boolean {
218
+ return this.isHorizontal && this.isVertical;
219
+ }
220
+
221
+ /**
222
+ * @returns true if the frame is weakly aligned. A frame is weakly aligned if it meets
223
+ * the time range occupied by all arrays of a particular channel is the same for all
224
+ * channels in the frame. This means that the arrays for a particular channel can have
225
+ * gaps betwen them.
226
+ */
227
+ get isWeaklyAligned(): boolean {
228
+ if (this.columns.length <= 1) return true;
229
+ const ranges = this.timeRanges;
230
+ return ranges.every((tr) => tr.equals(ranges[0]));
231
+ }
232
+
233
+ timeRange(col?: KeyOrName): TimeRange {
234
+ if (col == null) {
235
+ if (this.columns.length === 0) return TimeRange.ZERO;
236
+ const start = Math.min(...this.series.map((a) => a.timeRange.start.valueOf()));
237
+ const end = Math.max(...this.series.map((a) => a.timeRange.end.valueOf()));
238
+ return new TimeRange(start, end);
239
+ }
240
+ const group = this.get(col);
241
+ if (group == null) return TimeRange.ZERO;
242
+ return new TimeRange(
243
+ group[0].timeRange.start,
244
+ group[group.length - 1].timeRange.end,
245
+ );
246
+ }
247
+
248
+ get timeRanges(): TimeRange[] {
249
+ return this.uniqueColumns.map((col) => this.timeRange(col));
250
+ }
251
+
252
+ /**
253
+ * @returns lazy arrays matching the given channel key or name.
254
+ * @param key the channel key or name.
255
+ */
256
+ get(key: KeyOrName): Series[];
257
+
258
+ /**
259
+ * @returns a frame with the given channel keys or names.
260
+ * @param keys the channel keys or names.
261
+ */
262
+ get(keys: Keys | Names): Frame;
263
+
264
+ get(key: KeyOrName | Keys | Names): Series[] | Frame {
265
+ if (Array.isArray(key)) return this.filter((k) => (key as Keys).includes(k as Key));
266
+ return this.series.filter((_, i) => this.columns[i] === key);
267
+ }
268
+
269
+ /**
270
+ * Pushes a set of typed arrays for the given channel onto the frame.
271
+ *
272
+ * @param key the channel key or name;
273
+ * @param v the typed arrays to push.
274
+ */
275
+ push(key: KeyOrName, ...v: Series[]): void;
276
+
277
+ /**
278
+ * Pushes the frame onto the current frame.
279
+ *
280
+ * @param frame - the frame to push.
281
+ */
282
+ push(frame: Frame): void;
283
+
284
+ push(keyOrFrame: KeyOrName | Frame, ...v: Series[]): void {
285
+ if (keyOrFrame instanceof Frame) {
286
+ if (this.colType !== null && keyOrFrame.colType !== this.colType)
287
+ throw new ValidationError("keyVariant must match");
288
+ this.series.push(...keyOrFrame.series);
289
+ (this.columns as Keys).push(...(keyOrFrame.columns as Keys));
290
+ } else {
291
+ this.series.push(...v);
292
+ if (typeof keyOrFrame === "string" && this.colType === "key")
293
+ throw new ValidationError("keyVariant must match");
294
+ else if (typeof keyOrFrame !== "string" && this.colType === "name")
295
+ throw new ValidationError("keyVariant must match");
296
+ (this.columns as Keys).push(
297
+ ...(Array.from({ length: v.length }, () => keyOrFrame) as Keys),
298
+ );
299
+ }
300
+ }
301
+
302
+ /**
303
+ * @returns a shallow copy of this frame containing all typed arrays in the current frame and the
304
+ * provided frame.
305
+ */
306
+ concat(frame: Frame): Frame {
307
+ return new Frame([...this.columns, ...frame.columns] as Keys, [
308
+ ...this.series,
309
+ ...frame.series,
310
+ ]);
311
+ }
312
+
313
+ /**
314
+ * @returns true if the frame contains the provided channel key or name.
315
+ * @param channel the channel key or name to check.
316
+ */
317
+ has(channel: KeyOrName): boolean {
318
+ if (typeof channel === "string" && this.colType === "key") return false;
319
+ else if (typeof channel === "number" && this.colType === "name") return false;
320
+ return (this.columns as Keys).includes(channel as Key);
321
+ }
322
+
323
+ /**
324
+ * @returns a new frame containing the mapped output of the provided function.
325
+ * @param fn a function that takes a channel key and typed array and returns a
326
+ * boolean.
327
+ */
328
+ map(fn: (k: KeyOrName, arr: Series, i: number) => [KeyOrName, Series]): Frame {
329
+ const frame = new Frame();
330
+ this.forEach((k, arr, i) => frame.push(...fn(k, arr, i)));
331
+ return frame;
332
+ }
333
+
334
+ /**
335
+ * Iterates over all typed arrays in the current frame.
336
+ *
337
+ * @param fn a function that takes a channel key and typed array.
338
+ */
339
+ forEach(fn: (k: KeyOrName, arr: Series, i: number) => void): void {
340
+ this.columns.forEach((k, i) => {
341
+ const a = this.series[i];
342
+ fn(k, a, i);
343
+ });
344
+ }
345
+
346
+ /**
347
+ * @returns a new frame containing all typed arrays in the current frame that pass
348
+ * the provided filter function.
349
+ * @param fn a function that takes a channel key and typed array and returns a boolean.
350
+ */
351
+ filter(fn: (k: KeyOrName, arr: Series, i: number) => boolean): Frame {
352
+ const frame = new Frame();
353
+ this.columns.forEach((k, i) => {
354
+ const a = this.series[i];
355
+ if (fn(k, a, i)) frame.push(k, a);
356
+ });
357
+ return frame;
358
+ }
359
+
360
+ /** @returns the total number of bytes in the frame. */
361
+ get byteLength(): Size {
362
+ return new Size(this.series.reduce((acc, v) => acc.add(v.byteLength), Size.ZERO));
363
+ }
364
+
365
+ /** @returns the total number of samples in the frame. */
366
+ get length(): number {
367
+ return this.series.reduce((acc, v) => acc + v.length, 0);
368
+ }
369
+ }
370
+
371
+ export const series = z.object({
372
+ timeRange: TimeRange.z.optional(),
373
+ alignment: z.number().optional(),
374
+ dataType: DataType.z,
375
+ data: z.string().transform(
376
+ (s) =>
377
+ new Uint8Array(
378
+ atob(s)
379
+ .split("")
380
+ .map((c) => c.charCodeAt(0)),
381
+ ).buffer,
382
+ ),
383
+ });
384
+
385
+ export type SeriesPayload = z.infer<typeof series>;
386
+
387
+ export const frameZ = z.object({
388
+ keys: z.union([
389
+ z.null().transform(() => [] as number[]),
390
+ z.number().array().optional().default([]),
391
+ ]),
392
+ series: z.union([
393
+ z.null().transform(() => [] as Array<z.infer<typeof series>>),
394
+ series.array().optional().default([]),
395
+ ]),
396
+ });
397
+
398
+ export type FramePayload = z.infer<typeof frameZ>;
399
+
400
+ export const seriesFromPayload = (series: SeriesPayload): Series => {
401
+ const { dataType, data, timeRange, alignment } = series;
402
+ return new Series(data, dataType, timeRange, 0, "static", alignment);
403
+ };
404
+
405
+ export const seriesToPayload = (series: Series): SeriesPayload => {
406
+ return {
407
+ timeRange: series._timeRange,
408
+ dataType: series.dataType,
409
+ data: new Uint8Array(series.data.buffer),
410
+ alignment: series.alignment,
411
+ };
412
+ };
@@ -0,0 +1,10 @@
1
+ // Copyright 2023 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 framer from "@/framer/external";
@@ -0,0 +1,62 @@
1
+ // Copyright 2023 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
+ import { DataType, Rate, TimeRange, TimeSpan, TimeStamp } from "@synnaxlabs/x";
11
+ import { describe, test, expect } from "vitest";
12
+
13
+ import { type channel } from "@/channel";
14
+ import { newClient } from "@/setupspecs";
15
+ import { randomSeries } from "@/util/telem";
16
+
17
+ const client = newClient();
18
+
19
+ const newChannel = async (): Promise<channel.Channel> => {
20
+ return await client.channels.create({
21
+ name: "test",
22
+ leaseholder: 1,
23
+ rate: Rate.hz(25),
24
+ dataType: DataType.FLOAT64,
25
+ });
26
+ };
27
+
28
+ describe("Iterator", () => {
29
+ test("happy path", async () => {
30
+ const ch = await newChannel();
31
+ const writer = await client.telem.newWriter({
32
+ start: TimeStamp.SECOND,
33
+ channels: ch.key,
34
+ });
35
+ const data = randomSeries(25, ch.dataType);
36
+ try {
37
+ await writer.write(ch.key, data);
38
+ await writer.write(ch.key, data);
39
+ await writer.write(ch.key, data);
40
+ await writer.commit();
41
+ } finally {
42
+ await writer.close();
43
+ }
44
+
45
+ const iter = await client.telem.newIterator(
46
+ new TimeRange(TimeSpan.ZERO, TimeSpan.seconds(4)),
47
+ [ch.key],
48
+ );
49
+
50
+ try {
51
+ expect(await iter.seekFirst()).toBeTruthy();
52
+ let c = 0;
53
+ while (await iter.next(TimeSpan.seconds(1))) {
54
+ c++;
55
+ expect(iter.value.get(ch.key)[0]).toHaveLength(25);
56
+ }
57
+ expect(c).toEqual(3);
58
+ } finally {
59
+ await iter.close();
60
+ }
61
+ });
62
+ });