@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,240 @@
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 { errorZ, type Stream, type StreamClient } from "@synnaxlabs/freighter";
11
+ import {
12
+ type CrudeTimeSpan,
13
+ type CrudeTimeStamp,
14
+ TimeRange,
15
+ TimeSpan,
16
+ TimeStamp,
17
+ } from "@synnaxlabs/x";
18
+ import { z } from "zod";
19
+
20
+ import { type Params } from "@/channel/payload";
21
+ import { type Retriever } from "@/channel/retriever";
22
+ import { BackwardFrameAdapter } from "@/framer/adapter";
23
+ import { Frame, frameZ } from "@/framer/frame";
24
+ import { StreamProxy } from "@/framer/streamProxy";
25
+
26
+ export const AUTO_SPAN = new TimeSpan(-1);
27
+
28
+ enum Command {
29
+ Open = 0,
30
+ Next = 1,
31
+ Prev = 2,
32
+ SeekFirst = 3,
33
+ SeekLast = 4,
34
+ SeekLE = 5,
35
+ SeekGE = 6,
36
+ Valid = 7,
37
+ Error = 8,
38
+ }
39
+
40
+ enum ResponseVariant {
41
+ None = 0,
42
+ Ack = 1,
43
+ Data = 2,
44
+ }
45
+
46
+ const reqZ = z.object({
47
+ command: z.nativeEnum(Command),
48
+ span: TimeSpan.z.optional(),
49
+ bounds: TimeRange.z.optional(),
50
+ stamp: TimeStamp.z.optional(),
51
+ keys: z.number().array().optional(),
52
+ });
53
+
54
+ type Request = z.infer<typeof reqZ>;
55
+
56
+ const resZ = z.object({
57
+ variant: z.nativeEnum(ResponseVariant),
58
+ ack: z.boolean(),
59
+ command: z.nativeEnum(Command),
60
+ error: errorZ.optional().nullable(),
61
+ frame: frameZ.optional(),
62
+ });
63
+
64
+ /**
65
+ * Used to iterate over a clusters telemetry in time-order. It should not be
66
+ * instantiated directly, and should instead be instantiated via the SegmentClient.
67
+ *
68
+ * Using an iterator is ideal when querying/processing large ranges of data, but
69
+ * is relatively complex and difficult to use. If you're looking to retrieve
70
+ * telemetry between two timestamps, see the SegmentClient.read method.
71
+ */
72
+ export class Iterator {
73
+ private static readonly ENDPOINT = "/frame/iterate";
74
+ private readonly stream: StreamProxy<typeof reqZ, typeof resZ>;
75
+ private readonly adapter: BackwardFrameAdapter;
76
+ value: Frame;
77
+
78
+ private constructor(
79
+ stream: Stream<typeof reqZ, typeof resZ>,
80
+ adapter: BackwardFrameAdapter,
81
+ ) {
82
+ this.stream = new StreamProxy("Iterator", stream);
83
+ this.value = new Frame();
84
+ this.adapter = adapter;
85
+ }
86
+
87
+ /**
88
+ * Opens the iterator, configuring it to iterate over the telemetry in the
89
+ * channels with the given keys within the provided time range.
90
+ *
91
+ * @param tr - The time range to iterate over.
92
+ * @param keys - The keys of the channels to iterate over.
93
+ */
94
+ static async _open(
95
+ tr: TimeRange,
96
+ channels: Params,
97
+ retriever: Retriever,
98
+ client: StreamClient,
99
+ ): Promise<Iterator> {
100
+ const adapter = await BackwardFrameAdapter.open(retriever, channels);
101
+ const stream = await client.stream(Iterator.ENDPOINT, reqZ, resZ);
102
+ const iter = new Iterator(stream, adapter);
103
+ await iter.execute({ command: Command.Open, keys: adapter.keys, bounds: tr });
104
+ return iter;
105
+ }
106
+
107
+ /**
108
+ * Reads the next time span of telemetry for each channel in the iterator.
109
+ *
110
+ * @param span - The time span to read. A negative span is equivalent
111
+ * to calling prev with the absolute value of the span. If the span is
112
+ * AUTO_SPAN, the iterator will automatically determine the span to read.
113
+ * This is useful for iterating over an entire range efficiently.
114
+ *
115
+ * @returns false if a segment satisfying the request can't be found for a
116
+ * particular channel or the iterator has accumulated an error.
117
+ */
118
+ async next(span: CrudeTimeSpan = AUTO_SPAN): Promise<boolean> {
119
+ return await this.execute({ command: Command.Next, span: new TimeSpan(span) });
120
+ }
121
+
122
+ /**
123
+ * Reads the previous time span of telemetry for each channel in the iterator.
124
+ *
125
+ * @param span - The time span to read. A negative span is equivalent
126
+ * to calling next with the absolute value of the span. If the span is
127
+ * AUTO_SPAN, the iterator will automatically determine the span to read.
128
+ * This is useful for iterating over an entire range efficiently.
129
+ *
130
+ * @returns false if a segment satisfying the request can't be found for a particular
131
+ * channel or the iterator has accumulated an error.
132
+ */
133
+ async prev(span: CrudeTimeSpan = AUTO_SPAN): Promise<boolean> {
134
+ return await this.execute({ command: Command.Prev, span: new TimeSpan(span) });
135
+ }
136
+
137
+ /**
138
+ * Seeks the iterator to the first segment in the time range, but does not read
139
+ * it. Also invalidates the iterator. The iterator will not be considered valid
140
+ * until a call to next or prev.
141
+ *
142
+ * @returns false if the iterator is not pointing to a valid segment for a particular
143
+ * channel or has accumulated an error.
144
+ */
145
+ async seekFirst(): Promise<boolean> {
146
+ return await this.execute({ command: Command.SeekFirst });
147
+ }
148
+
149
+ /** Seeks the iterator to the last segment in the time range, but does not read it.
150
+ * Also invalidates the iterator. The iterator will not be considered valid
151
+ * until a call to next or prev.
152
+ *
153
+ * @returns false if the iterator is not pointing to a valid segment for a particular
154
+ * channel or has accumulated an error.
155
+ */
156
+ async seekLast(): Promise<boolean> {
157
+ return await this.execute({ command: Command.SeekLast });
158
+ }
159
+
160
+ /**
161
+ * Seeks the iterator to the first segment whose start is less than or equal to
162
+ * the provided timestamp. Also invalidates the iterator. The iterator will not be
163
+ * considered valid until a call to next or prev.
164
+ *
165
+ * @returns false if the iterator is not pointing to a valid segment for a particular
166
+ * channel or has accumulated an error.
167
+ */
168
+ async seekLE(stamp: CrudeTimeStamp): Promise<boolean> {
169
+ return await this.execute({ command: Command.SeekLE, stamp: new TimeStamp(stamp) });
170
+ }
171
+
172
+ /**
173
+ * Seeks the iterator to the first segment whose start is greater than or equal to
174
+ * the provided timestamp. Also invalidates the iterator. The iterator will not be
175
+ * considered valid until a call to next or prev.
176
+ *
177
+ * @returns false if the iterator is not pointing to a valid segment for a particular
178
+ * channel or has accumulated an error.
179
+ */
180
+ async seekGE(stamp: CrudeTimeStamp): Promise<boolean> {
181
+ return await this.execute({ command: Command.SeekGE, stamp: new TimeStamp(stamp) });
182
+ }
183
+
184
+ /**
185
+ * @returns true if the iterator value contains a valid segment, and fale otherwise.
186
+ * valid most commonly returns false when the iterator is exhausted or has
187
+ * accumulated an error.
188
+ */
189
+ async valid(): Promise<boolean> {
190
+ return await this.execute({ command: Command.Valid });
191
+ }
192
+
193
+ /**
194
+ * Closes the iterator. An iterator MUST be closed after use, and this method
195
+ * should probably be placed in a 'finally' block. If the iterator is not closed,
196
+ * it may leak resources.
197
+ */
198
+ async close(): Promise<void> {
199
+ await this.stream.closeAndAck();
200
+ }
201
+
202
+ [Symbol.asyncIterator](): AsyncIterator<Frame, any, undefined> {
203
+ return new IteratorIterator(this);
204
+ }
205
+
206
+ private async execute(request: Request): Promise<boolean> {
207
+ this.stream.send(request);
208
+ this.value = new Frame();
209
+ while (true) {
210
+ const res = await this.stream.receive();
211
+ if (res.variant === ResponseVariant.Ack) return res.ack;
212
+ this.value.push(this.adapter.adapt(new Frame(res.frame)));
213
+ }
214
+ }
215
+ }
216
+
217
+ class IteratorIterator implements AsyncIterator<Frame> {
218
+ private readonly iter: Iterator;
219
+ private open: boolean = false;
220
+
221
+ constructor(iter: Iterator) {
222
+ this.iter = iter;
223
+ }
224
+
225
+ async next(): Promise<IteratorResult<Frame, any>> {
226
+ try {
227
+ let ok = true;
228
+ if (!this.open) {
229
+ if (!(await this.iter.seekFirst())) ok = false;
230
+ this.open = true;
231
+ }
232
+ if (!(await this.iter.next())) ok = false;
233
+ if (!ok) await this.iter.close();
234
+ return { done: !ok, value: this.iter.value };
235
+ } catch (e) {
236
+ await this.iter.close();
237
+ throw e;
238
+ }
239
+ }
240
+ }
@@ -0,0 +1,59 @@
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 { type Stream, EOF } from "@synnaxlabs/freighter";
11
+ import { type z } from "zod";
12
+
13
+ import { UnexpectedError } from "@/errors";
14
+
15
+ export class StreamProxy<RQ extends z.ZodTypeAny, RS extends z.ZodTypeAny> {
16
+ readonly name: string;
17
+ private readonly stream: Stream<RQ, RS>;
18
+
19
+ constructor(name: string, stream: Stream<RQ, RS>) {
20
+ this.stream = stream;
21
+ this.name = name;
22
+ }
23
+
24
+ async receive(): Promise<z.output<RS>> {
25
+ const [res, err] = await this.stream.receive();
26
+ if (err != null) throw err;
27
+ return res;
28
+ }
29
+
30
+ received(): boolean {
31
+ return this.stream.received();
32
+ }
33
+
34
+ async closeAndAck(): Promise<void> {
35
+ this.stream.closeSend();
36
+ const [res, err] = await this.stream.receive();
37
+ if (res != null)
38
+ throw new UnexpectedError(
39
+ `${this.name} received unexpected response on closure.
40
+ Please report this error to the Synnax team.`,
41
+ );
42
+ if (err == null)
43
+ throw new UnexpectedError(
44
+ `${this.name} received unexpected null error on closure.
45
+ Please report this error to Synnax team.
46
+ `,
47
+ );
48
+ if (!(err instanceof EOF)) throw err;
49
+ }
50
+
51
+ closeSend(): void {
52
+ this.stream.closeSend();
53
+ }
54
+
55
+ send(req: z.input<RQ>): void {
56
+ const err = this.stream.send(req);
57
+ if (err != null) throw err;
58
+ }
59
+ }
@@ -0,0 +1,42 @@
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, TimeStamp } from "@synnaxlabs/x";
11
+ import { describe, test, expect } from "vitest";
12
+
13
+ import { type channel } from "@/channel";
14
+ import { newClient } from "@/setupspecs";
15
+
16
+ const client = newClient();
17
+
18
+ const newChannel = async (): Promise<channel.Channel> =>
19
+ await client.channels.create({
20
+ name: "test",
21
+ leaseholder: 1,
22
+ rate: Rate.hz(25),
23
+ dataType: DataType.FLOAT64,
24
+ });
25
+
26
+ describe("Streamer", () => {
27
+ test("happy path", async () => {
28
+ const ch = await newChannel();
29
+ const streamer = await client.telem.newStreamer(ch.key);
30
+ const writer = await client.telem.newWriter({
31
+ start: TimeStamp.now(),
32
+ channels: ch.key,
33
+ });
34
+ try {
35
+ await writer.write(ch.key, new Float64Array([1, 2, 3]));
36
+ } finally {
37
+ await writer.close();
38
+ }
39
+ const d = await streamer.read();
40
+ expect(d.get(ch.key)[0].data).toEqual(new Float64Array([1, 2, 3]));
41
+ });
42
+ });
@@ -0,0 +1,86 @@
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 { errorZ, type Stream, type StreamClient } from "@synnaxlabs/freighter";
11
+ import { TimeStamp, type CrudeTimeStamp } from "@synnaxlabs/x";
12
+ import { z } from "zod";
13
+
14
+ import { type Key, type Params } from "@/channel/payload";
15
+ import { type Retriever } from "@/channel/retriever";
16
+ import { BackwardFrameAdapter } from "@/framer/adapter";
17
+ import { Frame, frameZ } from "@/framer/frame";
18
+ import { StreamProxy } from "@/framer/streamProxy";
19
+
20
+ const reqZ = z.object({
21
+ start: TimeStamp.z.optional(),
22
+ keys: z.number().array(),
23
+ });
24
+
25
+ const resZ = z.object({
26
+ frame: frameZ,
27
+ error: errorZ.optional().nullable(),
28
+ });
29
+
30
+ const ENDPOINT = "/frame/stream";
31
+
32
+ export class Streamer implements AsyncIterator<Frame>, AsyncIterable<Frame> {
33
+ private readonly stream: StreamProxy<typeof reqZ, typeof resZ>;
34
+ private readonly adapter: BackwardFrameAdapter;
35
+
36
+ private constructor(
37
+ stream: Stream<typeof reqZ, typeof resZ>,
38
+ adapter: BackwardFrameAdapter,
39
+ ) {
40
+ this.stream = new StreamProxy("Streamer", stream);
41
+ this.adapter = adapter;
42
+ }
43
+
44
+ get keys(): Key[] {
45
+ return this.adapter.keys;
46
+ }
47
+
48
+ static async _open(
49
+ start: CrudeTimeStamp,
50
+ channels: Params,
51
+ retriever: Retriever,
52
+ client: StreamClient,
53
+ ): Promise<Streamer> {
54
+ const adapter = await BackwardFrameAdapter.open(retriever, channels);
55
+ const stream = await client.stream(ENDPOINT, reqZ, resZ);
56
+ const streamer = new Streamer(stream, adapter);
57
+ stream.send({ start: new TimeStamp(start), keys: adapter.keys });
58
+ return streamer;
59
+ }
60
+
61
+ async next(): Promise<IteratorResult<Frame, any>> {
62
+ try {
63
+ const frame = await this.read();
64
+ return { done: false, value: frame };
65
+ } catch (EOF) {
66
+ return { done: true, value: undefined };
67
+ }
68
+ }
69
+
70
+ async read(): Promise<Frame> {
71
+ return this.adapter.adapt(new Frame((await this.stream.receive()).frame));
72
+ }
73
+
74
+ async update(params: Params): Promise<void> {
75
+ await this.adapter.update(params);
76
+ this.stream.send({ keys: this.adapter.keys });
77
+ }
78
+
79
+ close(): void {
80
+ this.stream.closeSend();
81
+ }
82
+
83
+ [Symbol.asyncIterator](): AsyncIterator<Frame, any, undefined> {
84
+ return this;
85
+ }
86
+ }
@@ -0,0 +1,52 @@
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, TimeStamp } from "@synnaxlabs/x";
11
+ import { describe, expect, test } 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(1),
24
+ dataType: DataType.FLOAT64,
25
+ });
26
+ };
27
+
28
+ describe("Writer", () => {
29
+ describe("Writer", () => {
30
+ test("basic write", async () => {
31
+ const ch = await newChannel();
32
+ const writer = await client.telem.newWriter({ start: 0, channels: ch.key });
33
+ try {
34
+ await writer.write(ch.key, randomSeries(10, ch.dataType));
35
+ await writer.commit();
36
+ } finally {
37
+ await writer.close();
38
+ }
39
+ expect(true).toBeTruthy();
40
+ });
41
+ });
42
+ describe("Client", () => {
43
+ test("Client - basic write", async () => {
44
+ const ch = await newChannel();
45
+ const data = randomSeries(10, ch.dataType);
46
+ await client.telem.write(ch.key, TimeStamp.seconds(1), data);
47
+ const res = await client.telem.read(TimeRange.MAX, ch.key);
48
+ expect(res.length).toEqual(data.length);
49
+ expect(res.data).toEqual(data);
50
+ });
51
+ });
52
+ });