@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,236 @@
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
+ /* eslint-disable @typescript-eslint/no-throw-literal */
11
+ import type { Stream, StreamClient } from "@synnaxlabs/freighter";
12
+ import { decodeError, errorZ } from "@synnaxlabs/freighter";
13
+ import {
14
+ type NativeTypedArray,
15
+ Series,
16
+ TimeStamp,
17
+ type CrudeTimeStamp,
18
+ toArray,
19
+ } from "@synnaxlabs/x";
20
+ import { z } from "zod";
21
+
22
+ import { type Key, type KeyOrName, type Params } from "@/channel/payload";
23
+ import { type Retriever } from "@/channel/retriever";
24
+ import { Authority } from "@/control/authority";
25
+ import {
26
+ subjectZ as controlSubjectZ,
27
+ type Subject as ControlSubject,
28
+ } from "@/control/state";
29
+ import { ForwardFrameAdapter } from "@/framer/adapter";
30
+ import { type CrudeFrame, Frame, frameZ } from "@/framer/frame";
31
+ import { StreamProxy } from "@/framer/streamProxy";
32
+
33
+ enum Command {
34
+ Open = 0,
35
+ Write = 1,
36
+ Commit = 2,
37
+ Error = 3,
38
+ SetAuthority = 4,
39
+ }
40
+
41
+ const netConfigZ = z.object({
42
+ start: TimeStamp.z.optional(),
43
+ controlSubject: controlSubjectZ.optional(),
44
+ keys: z.number().array(),
45
+ authorities: Authority.z.array().optional(),
46
+ });
47
+
48
+ const reqZ = z.object({
49
+ command: z.nativeEnum(Command),
50
+ config: netConfigZ.optional(),
51
+ frame: frameZ.optional(),
52
+ });
53
+
54
+ type Request = z.infer<typeof reqZ>;
55
+
56
+ const resZ = z.object({
57
+ ack: z.boolean(),
58
+ command: z.nativeEnum(Command),
59
+ error: errorZ.optional().nullable(),
60
+ });
61
+
62
+ type Response = z.infer<typeof resZ>;
63
+
64
+ export interface WriterConfig {
65
+ start: CrudeTimeStamp;
66
+ channels: Params;
67
+ controlSubject?: ControlSubject;
68
+ authorities?: Authority | Authority[];
69
+ }
70
+
71
+ /**
72
+ * Writer is used to write telemetry to a set of channels in time order.
73
+ * It should not be instantiated directly, and should instead be instantited via the
74
+ * FramerClient {@link FrameClient#openWriter}.
75
+ *
76
+ * The writer is a streaming protocol that is heavily optimized for prerformance. This
77
+ * comes at the cost of icnreased complexity, and should only be used directly when
78
+ * writing large volumes of data (such as recording telemetry from a sensor or ingsting
79
+ * data froma file). Simpler methods (such as the frame client's write method) should
80
+ * be used for most use cases.
81
+ *
82
+ * The protocol is as follows:
83
+ *
84
+ * 1. The writer is opened with a starting timestamp and a list of channel keys. The
85
+ * writer will fail to open if the starting timstamp overlaps with any existing telemetry
86
+ * for any channels specified. If the writer opens successfuly, the caller is then
87
+ * free to write frames to the writer.
88
+ *
89
+ * 2. To write a frame, the caller can use the write method and follow the validation
90
+ * rules described in its method's documentation. This process is asynchronous, meaning
91
+ * that write calls may return before teh frame has been written to the cluster. This
92
+ * also means that the writer can accumulate an error after write is called. If the writer
93
+ * accumulates an erorr, all subsequent write and commit calls will return False. The
94
+ * caller can check for errors by calling the error mehtod, which returns the accumulated
95
+ * error and resets the writer for future use. The caller can also check for errors by
96
+ * closing the writer, which will throw any accumulated error.
97
+ *
98
+ * 3. To commit the written frames to the cluster, the caller can call the commit method.
99
+ * Unlike write, commit is synchronous, meaning that it will not return until the frames
100
+ * have been written to the cluster. If the writer has accumulated an erorr, commit will
101
+ * return false. After the caller acknowledges the erorr, they can attempt to commit again.
102
+ * Commit can be called several times throughout a writer's lifetime, and will only
103
+ * commit the frames that have been written since the last commit.
104
+ *
105
+ * 4. A writer MUST be closed after use in order to prevent resource leaks. Close should
106
+ * typically be called in a 'finally' block. If the writer has accumulated an error,
107
+ * close will throw the error.
108
+ */
109
+ export class Writer {
110
+ private static readonly ENDPOINT = "/frame/write";
111
+ private readonly stream: StreamProxy<typeof reqZ, typeof resZ>;
112
+ private readonly adapter: ForwardFrameAdapter;
113
+
114
+ private constructor(
115
+ stream: Stream<typeof reqZ, typeof resZ>,
116
+ adapter: ForwardFrameAdapter,
117
+ ) {
118
+ this.stream = new StreamProxy("Writer", stream);
119
+ this.adapter = adapter;
120
+ }
121
+
122
+ static async _open(
123
+ retriever: Retriever,
124
+ client: StreamClient,
125
+ {
126
+ channels,
127
+ authorities = Authority.ABSOLUTE,
128
+ controlSubject: subject,
129
+ start,
130
+ }: WriterConfig,
131
+ ): Promise<Writer> {
132
+ const adapter = await ForwardFrameAdapter.open(retriever, channels);
133
+ const stream = await client.stream(Writer.ENDPOINT, reqZ, resZ);
134
+ const writer = new Writer(stream, adapter);
135
+ await writer.execute({
136
+ command: Command.Open,
137
+ config: {
138
+ start: new TimeStamp(start),
139
+ keys: adapter.keys,
140
+ controlSubject: subject,
141
+ authorities: toArray(authorities),
142
+ },
143
+ });
144
+ return writer;
145
+ }
146
+
147
+ async write(channel: KeyOrName, data: NativeTypedArray): Promise<boolean>;
148
+
149
+ async write(frame: CrudeFrame): Promise<boolean>;
150
+
151
+ /**
152
+ * Writes the given frame to the database.
153
+ *
154
+ * @param frame - The frame to write to the database. The frame must:
155
+ *
156
+ * 1. Have exactly one array for each key in the list of keys provided to the
157
+ * writer's open method.
158
+ * 2. Have equal length arrays for each key.
159
+ * 3. When writing to an index (i.e. TimeStamp) channel, the values must be
160
+ * monotonically increasing.
161
+ *
162
+ * @returns false if the writer has accumulated an error. In this case, the caller
163
+ * should acknowledge the error by calling the error method or closing the writer.
164
+ */
165
+ async write(
166
+ frame: CrudeFrame | KeyOrName,
167
+ data?: NativeTypedArray,
168
+ ): Promise<boolean> {
169
+ const isKeyOrName = ["string", "number"].includes(typeof frame);
170
+ if (isKeyOrName) {
171
+ frame = new Frame(frame, new Series(data as NativeTypedArray));
172
+ }
173
+ frame = this.adapter.adapt(new Frame(frame));
174
+ // @ts-expect-error
175
+ this.stream.send({ command: Command.Write, frame: frame.toPayload() });
176
+ return true;
177
+ }
178
+
179
+ async setAuthority(value: Record<Key, Authority>): Promise<boolean> {
180
+ const res = await this.execute({
181
+ command: Command.SetAuthority,
182
+ config: {
183
+ keys: Object.keys(value).map((k) => Number(k)),
184
+ authorities: Object.values(value),
185
+ },
186
+ });
187
+ return res.ack;
188
+ }
189
+
190
+ /**
191
+ * Commits the written frames to the database. Commit is synchronous, meaning that it
192
+ * will not return until all frames have been commited to the database.
193
+ *
194
+ * @returns false if the commit failed due to an error. In this case, the caller
195
+ * should acknowledge the error by calling the error method or closing the writer.
196
+ * After the caller acknowledges the error, they can attempt to commit again.
197
+ */
198
+ async commit(): Promise<boolean> {
199
+ if (this.errorAccumulated) return false;
200
+ const res = await this.execute({ command: Command.Commit });
201
+ return res.ack;
202
+ }
203
+
204
+ /**
205
+ * @returns The accumulated error, if any. This method will clear the writer's error
206
+ * state, allowing the writer to be used again.
207
+ */
208
+ async error(): Promise<Error | null> {
209
+ this.stream.send({ command: Command.Error });
210
+ const res = await this.execute({ command: Command.Error });
211
+ return res.error != null ? decodeError(res.error) : null;
212
+ }
213
+
214
+ /**
215
+ * Closes the writer, raising any accumulated error encountered during operation.
216
+ * A writer MUST be closed after use, and this method should probably be placed
217
+ * in a 'finally' block.
218
+ */
219
+ async close(): Promise<void> {
220
+ await this.stream.closeAndAck();
221
+ }
222
+
223
+ async execute(req: Request): Promise<Response> {
224
+ // @ts-expect-error
225
+ this.stream.send(req);
226
+ while (true) {
227
+ const res = await this.stream.receive();
228
+ if (res.command === req.command) return res;
229
+ console.warn("writer received unexpected response", res);
230
+ }
231
+ }
232
+
233
+ private get errorAccumulated(): boolean {
234
+ return this.stream.received();
235
+ }
236
+ }
package/src/index.ts ADDED
@@ -0,0 +1,53 @@
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 * from "@/channel";
11
+ export { default as Synnax, synnaxPropsZ } from "@/client";
12
+ export type { SynnaxProps } from "@/client";
13
+ export * from "@/connection";
14
+ export { Channel } from "@/channel/client";
15
+ export {
16
+ AuthError,
17
+ ContiguityError,
18
+ GeneralError,
19
+ ParseError,
20
+ QueryError,
21
+ RouteError,
22
+ UnexpectedError,
23
+ ValidationError,
24
+ } from "@/errors";
25
+ export { framer } from "@/framer";
26
+ export { Frame } from "@/framer/frame";
27
+ export { ontology } from "@/ontology";
28
+ export { control } from "@/control";
29
+ export { Authority } from "@/control/authority";
30
+ export {
31
+ DataType,
32
+ Density,
33
+ Rate,
34
+ Series,
35
+ TimeRange,
36
+ TimeSpan,
37
+ TimeStamp,
38
+ } from "@synnaxlabs/x";
39
+ export type {
40
+ NativeTypedArray,
41
+ CrudeDataType,
42
+ CrudeDensity,
43
+ CrudeRate,
44
+ CrudeSize,
45
+ CrudeTimeSpan,
46
+ CrudeTimeStamp,
47
+ SampleValue,
48
+ TimeStampStringFormat,
49
+ TZInfo,
50
+ } from "@synnaxlabs/x";
51
+ export { workspace } from "@/workspace";
52
+ export { ranger } from "@/ranger";
53
+ export { label } from "@/label";
@@ -0,0 +1,103 @@
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 UnaryClient } from "@synnaxlabs/freighter";
11
+ import { type AsyncTermSearcher } from "@synnaxlabs/x";
12
+
13
+ import { type framer } from "@/framer";
14
+ import { type Key, type Label, labelZ } from "@/label/payload";
15
+ import { Retriever } from "@/label/retriever";
16
+ import { Writer, type NewLabelPayload } from "@/label/writer";
17
+ import { type ontology } from "@/ontology";
18
+ import { signals } from "@/signals";
19
+
20
+ const LABEL_SET_NAME = "sy_label_set";
21
+ const LABEL_DELETE_NAME = "sy_label_delete";
22
+
23
+ export class Client implements AsyncTermSearcher<string, Key, Label> {
24
+ private readonly retriever: Retriever;
25
+ private readonly writer: Writer;
26
+ private readonly frameClient: framer.Client;
27
+
28
+ constructor(client: UnaryClient, frameClient: framer.Client) {
29
+ this.writer = new Writer(client);
30
+ this.retriever = new Retriever(client);
31
+ this.frameClient = frameClient;
32
+ }
33
+
34
+ async search(term: string): Promise<Label[]> {
35
+ return await this.retriever.search(term);
36
+ }
37
+
38
+ async retrieve(key: Key): Promise<Label>;
39
+
40
+ async retrieve(keys: Key[]): Promise<Label[]>;
41
+
42
+ async retrieve(keys: Key | Key[]): Promise<Label | Label[]> {
43
+ const isMany = Array.isArray(keys);
44
+ const res = await this.retriever.retrieve(keys);
45
+ return isMany ? res : res[0];
46
+ }
47
+
48
+ async retrieveFor(id: ontology.ID): Promise<Label[]> {
49
+ return await this.retriever.retrieveFor(id);
50
+ }
51
+
52
+ async label(id: ontology.ID, labels: Key[]): Promise<void> {
53
+ await this.writer.set(id, labels);
54
+ }
55
+
56
+ async removeLabels(id: ontology.ID, labels: Key[]): Promise<void> {
57
+ await this.writer.remove(id, labels);
58
+ }
59
+
60
+ async page(offset: number, limit: number): Promise<Label[]> {
61
+ return await this.retriever.page(offset, limit);
62
+ }
63
+
64
+ async create(label: NewLabelPayload): Promise<Label>;
65
+
66
+ async create(labels: NewLabelPayload[]): Promise<Label[]>;
67
+
68
+ async create(labels: NewLabelPayload | NewLabelPayload[]): Promise<Label | Label[]> {
69
+ const isMany = Array.isArray(labels);
70
+ const res = await this.writer.create(labels);
71
+ return isMany ? res : res[0];
72
+ }
73
+
74
+ async delete(key: Key): Promise<void>;
75
+
76
+ async delete(keys: Key[]): Promise<void>;
77
+
78
+ async delete(keys: Key | Key[]): Promise<void> {
79
+ await this.writer.delete(keys);
80
+ }
81
+
82
+ async openChangeTracker(): Promise<signals.Observable<string, Label>> {
83
+ return await signals.Observable.open<string, Label>(
84
+ this.frameClient,
85
+ LABEL_SET_NAME,
86
+ LABEL_DELETE_NAME,
87
+ decodeChanges,
88
+ );
89
+ }
90
+ }
91
+
92
+ const decodeChanges: signals.Decoder<string, Label> = (variant, data) => {
93
+ if (variant === "delete")
94
+ return data.toUUIDs().map((v) => ({
95
+ variant,
96
+ key: v,
97
+ }));
98
+ return data.parseJSON(labelZ).map((l) => ({
99
+ variant,
100
+ key: l.key,
101
+ value: l,
102
+ }));
103
+ };
@@ -0,0 +1,13 @@
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 * from "@/label/client";
11
+ export * from "@/label/writer";
12
+ export * from "@/label/retriever";
13
+ export * from "@/label/payload";
@@ -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 label from "@/label/external";
@@ -0,0 +1,51 @@
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 { describe, expect, it } from "vitest";
11
+
12
+ import { label } from "@/label";
13
+ import { newClient } from "@/setupspecs";
14
+
15
+ const client = newClient();
16
+
17
+ describe("Label", () => {
18
+ describe("create", () => {
19
+ it("should create a label", async () => {
20
+ const v = await client.labels.create({ name: "Label", color: "#E774D0" });
21
+ expect(v.key).not.toHaveLength(0);
22
+ });
23
+ });
24
+
25
+ describe("retrieve", () => {
26
+ it("should retrieve a label by its key", async () => {
27
+ const v = await client.labels.create({ name: "Label", color: "#E774D0" });
28
+ const retrieved = await client.labels.retrieve(v.key);
29
+ expect(retrieved).toEqual(v);
30
+ });
31
+ });
32
+
33
+ describe("delete", () => {
34
+ it("should delete a label by its key", async () => {
35
+ const v = await client.labels.create({ name: "Label", color: "#E774D0" });
36
+ await client.labels.delete(v.key);
37
+ await expect(async () => await client.labels.retrieve(v.key)).rejects.toThrow();
38
+ });
39
+ });
40
+
41
+ describe("label", () => {
42
+ it("should set a label on an item", async () => {
43
+ const l1 = await client.labels.create({ name: "Label One", color: "#E774D)" });
44
+ const l2 = await client.labels.create({ name: "Label Two", color: "#E774D)" });
45
+ await client.labels.label(label.ontologyID(l1.key), [l2.key]);
46
+ const labels = await client.labels.retrieveFor(label.ontologyID(l1.key));
47
+ expect(labels).toHaveLength(1);
48
+ expect(labels[0].key).toEqual(l2.key);
49
+ });
50
+ });
51
+ });
@@ -0,0 +1,29 @@
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 { z } from "zod";
11
+
12
+ import { ontology } from "@/ontology";
13
+
14
+ export const keyZ = z.string().uuid();
15
+
16
+ export type Key = z.infer<typeof keyZ>;
17
+
18
+ export type Params = Key | Key[];
19
+
20
+ export const labelZ = z.object({
21
+ key: keyZ,
22
+ name: z.string().min(1),
23
+ color: z.string(),
24
+ });
25
+
26
+ export type Label = z.infer<typeof labelZ>;
27
+
28
+ export const ontologyID = (key: Key): ontology.ID =>
29
+ new ontology.ID({ type: "label", key });
@@ -0,0 +1,65 @@
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 UnaryClient } from "@synnaxlabs/freighter";
11
+ import { toArray } from "@synnaxlabs/x";
12
+ import { z } from "zod";
13
+
14
+ import { keyZ, type Label, labelZ, type Params } from "@/label/payload";
15
+ import { ontology } from "@/ontology";
16
+
17
+ const reqZ = z.object({
18
+ keys: keyZ.array().optional(),
19
+ for: ontology.idZ.optional(),
20
+ search: z.string().optional(),
21
+ offset: z.number().optional(),
22
+ limit: z.number().optional(),
23
+ });
24
+
25
+ type Request = z.infer<typeof reqZ>;
26
+
27
+ const resZ = z.object({
28
+ labels: labelZ.array().optional().default([]),
29
+ });
30
+
31
+ export class Retriever {
32
+ private static readonly ENDPOINT = "/label/retrieve";
33
+ private readonly client: UnaryClient;
34
+
35
+ constructor(client: UnaryClient) {
36
+ this.client = client;
37
+ }
38
+
39
+ async retrieve(params: Params): Promise<Label[]> {
40
+ const normalized = toArray(params);
41
+ return await this.execute({ keys: normalized });
42
+ }
43
+
44
+ async retrieveFor(id: ontology.ID): Promise<Label[]> {
45
+ return await this.execute({ for: id });
46
+ }
47
+
48
+ async search(term: string): Promise<Label[]> {
49
+ return await this.execute({ search: term });
50
+ }
51
+
52
+ async page(offset: number, limit: number): Promise<Label[]> {
53
+ return await this.execute({ offset, limit });
54
+ }
55
+
56
+ private async execute(req: Request): Promise<Label[]> {
57
+ const [res, err] = await this.client.send<typeof reqZ, typeof resZ>(
58
+ Retriever.ENDPOINT,
59
+ req,
60
+ resZ,
61
+ );
62
+ if (err != null) throw err;
63
+ return res.labels;
64
+ }
65
+ }
@@ -0,0 +1,90 @@
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 { sendRequired, type UnaryClient } from "@synnaxlabs/freighter";
11
+ import { toArray } from "@synnaxlabs/x";
12
+ import { z } from "zod";
13
+
14
+ import { type Key, keyZ, type Label, labelZ } from "@/label/payload";
15
+ import { ontology } from "@/ontology";
16
+
17
+ export const newLabelPayloadZ = labelZ.extend({ key: keyZ.optional() });
18
+
19
+ export type NewLabelPayload = z.infer<typeof newLabelPayloadZ>;
20
+
21
+ const createReqZ = z.object({
22
+ labels: newLabelPayloadZ.array(),
23
+ });
24
+
25
+ const createResZ = z.object({
26
+ labels: labelZ.array(),
27
+ });
28
+
29
+ const deleteReqZ = z.object({
30
+ keys: keyZ.array(),
31
+ });
32
+
33
+ const setReqZ = z.object({
34
+ id: ontology.idZ,
35
+ labels: keyZ.array(),
36
+ });
37
+
38
+ const removeReqZ = setReqZ;
39
+
40
+ const emptyResZ = z.object({});
41
+
42
+ const CREATE_ENDPOINT = "/label/create";
43
+ const DELETE_ENDPOINT = "/label/delete";
44
+ const SET_ENDPOINT = "/label/set";
45
+ const REMOVE_ENDPOINT = "/label/remove";
46
+
47
+ export class Writer {
48
+ private readonly client: UnaryClient;
49
+
50
+ constructor(client: UnaryClient) {
51
+ this.client = client;
52
+ }
53
+
54
+ async create(labels: NewLabelPayload | NewLabelPayload[]): Promise<Label[]> {
55
+ const res = await sendRequired<typeof createReqZ, typeof createResZ>(
56
+ this.client,
57
+ CREATE_ENDPOINT,
58
+ { labels: toArray(labels) },
59
+ createResZ,
60
+ );
61
+ return res.labels;
62
+ }
63
+
64
+ async delete(keys: Key | Key[]): Promise<void> {
65
+ await sendRequired<typeof deleteReqZ, typeof emptyResZ>(
66
+ this.client,
67
+ DELETE_ENDPOINT,
68
+ { keys: toArray(keys) },
69
+ emptyResZ,
70
+ );
71
+ }
72
+
73
+ async set(id: ontology.ID, labels: Key[]): Promise<void> {
74
+ await sendRequired<typeof setReqZ, typeof emptyResZ>(
75
+ this.client,
76
+ SET_ENDPOINT,
77
+ { id, labels },
78
+ emptyResZ,
79
+ );
80
+ }
81
+
82
+ async remove(id: ontology.ID, labels: Key[]): Promise<void> {
83
+ await sendRequired<typeof removeReqZ, typeof emptyResZ>(
84
+ this.client,
85
+ CREATE_ENDPOINT,
86
+ { id, labels },
87
+ emptyResZ,
88
+ );
89
+ }
90
+ }