@synnaxlabs/client 0.38.0 → 0.39.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (304) hide show
  1. package/.turbo/turbo-build.log +7 -7
  2. package/dist/access/payload.d.ts +6 -6
  3. package/dist/access/payload.d.ts.map +1 -1
  4. package/dist/access/policy/client.d.ts +5 -3
  5. package/dist/access/policy/client.d.ts.map +1 -1
  6. package/dist/access/policy/external.d.ts +1 -0
  7. package/dist/access/policy/external.d.ts.map +1 -1
  8. package/dist/access/policy/ontology.d.ts +5 -0
  9. package/dist/access/policy/ontology.d.ts.map +1 -0
  10. package/dist/access/policy/payload.d.ts +86 -89
  11. package/dist/access/policy/payload.d.ts.map +1 -1
  12. package/dist/access/policy/retriever.d.ts +7 -6
  13. package/dist/access/policy/retriever.d.ts.map +1 -1
  14. package/dist/access/policy/writer.d.ts +2 -2
  15. package/dist/access/policy/writer.d.ts.map +1 -1
  16. package/dist/auth/auth.d.ts +2 -1
  17. package/dist/auth/auth.d.ts.map +1 -1
  18. package/dist/channel/client.d.ts +8 -7
  19. package/dist/channel/client.d.ts.map +1 -1
  20. package/dist/channel/payload.d.ts +13 -11
  21. package/dist/channel/payload.d.ts.map +1 -1
  22. package/dist/channel/retriever.d.ts +9 -6
  23. package/dist/channel/retriever.d.ts.map +1 -1
  24. package/dist/channel/writer.d.ts +6 -4
  25. package/dist/channel/writer.d.ts.map +1 -1
  26. package/dist/client.cjs +30 -30
  27. package/dist/client.d.ts +4 -2
  28. package/dist/client.d.ts.map +1 -1
  29. package/dist/client.js +3488 -3500
  30. package/dist/connection/checker.d.ts +5 -4
  31. package/dist/connection/checker.d.ts.map +1 -1
  32. package/dist/control/state.d.ts +10 -8
  33. package/dist/control/state.d.ts.map +1 -1
  34. package/dist/errors.d.ts +5 -0
  35. package/dist/errors.d.ts.map +1 -1
  36. package/dist/framer/adapter.d.ts +14 -15
  37. package/dist/framer/adapter.d.ts.map +1 -1
  38. package/dist/framer/client.d.ts +13 -15
  39. package/dist/framer/client.d.ts.map +1 -1
  40. package/dist/framer/deleter.d.ts +3 -2
  41. package/dist/framer/deleter.d.ts.map +1 -1
  42. package/dist/framer/frame.d.ts +31 -27
  43. package/dist/framer/frame.d.ts.map +1 -1
  44. package/dist/framer/iterator.d.ts +4 -5
  45. package/dist/framer/iterator.d.ts.map +1 -1
  46. package/dist/framer/streamer.d.ts +5 -6
  47. package/dist/framer/streamer.d.ts.map +1 -1
  48. package/dist/framer/writer.d.ts +42 -39
  49. package/dist/framer/writer.d.ts.map +1 -1
  50. package/dist/hardware/device/client.d.ts +17 -12
  51. package/dist/hardware/device/client.d.ts.map +1 -1
  52. package/dist/hardware/device/payload.d.ts +19 -16
  53. package/dist/hardware/device/payload.d.ts.map +1 -1
  54. package/dist/hardware/rack/client.d.ts +15 -15
  55. package/dist/hardware/rack/client.d.ts.map +1 -1
  56. package/dist/hardware/rack/payload.d.ts +9 -8
  57. package/dist/hardware/rack/payload.d.ts.map +1 -1
  58. package/dist/hardware/task/client.d.ts +38 -29
  59. package/dist/hardware/task/client.d.ts.map +1 -1
  60. package/dist/hardware/task/payload.d.ts +58 -53
  61. package/dist/hardware/task/payload.d.ts.map +1 -1
  62. package/dist/label/client.d.ts +4 -3
  63. package/dist/label/client.d.ts.map +1 -1
  64. package/dist/label/payload.d.ts +4 -4
  65. package/dist/label/payload.d.ts.map +1 -1
  66. package/dist/label/retriever.d.ts.map +1 -1
  67. package/dist/label/writer.d.ts +13 -10
  68. package/dist/label/writer.d.ts.map +1 -1
  69. package/dist/ontology/client.d.ts +12 -10
  70. package/dist/ontology/client.d.ts.map +1 -1
  71. package/dist/ontology/group/client.d.ts +5 -4
  72. package/dist/ontology/group/client.d.ts.map +1 -1
  73. package/dist/ontology/group/group.d.ts +7 -5
  74. package/dist/ontology/group/group.d.ts.map +1 -1
  75. package/dist/ontology/group/payload.d.ts +6 -5
  76. package/dist/ontology/group/payload.d.ts.map +1 -1
  77. package/dist/ontology/group/writer.d.ts +8 -8
  78. package/dist/ontology/group/writer.d.ts.map +1 -1
  79. package/dist/ontology/payload.d.ts +72 -62
  80. package/dist/ontology/payload.d.ts.map +1 -1
  81. package/dist/ontology/writer.d.ts.map +1 -1
  82. package/dist/ranger/alias.d.ts +9 -10
  83. package/dist/ranger/alias.d.ts.map +1 -1
  84. package/dist/ranger/client.d.ts +18 -18
  85. package/dist/ranger/client.d.ts.map +1 -1
  86. package/dist/ranger/external.d.ts +1 -1
  87. package/dist/ranger/external.d.ts.map +1 -1
  88. package/dist/ranger/kv.d.ts +18 -14
  89. package/dist/ranger/kv.d.ts.map +1 -1
  90. package/dist/ranger/payload.d.ts +13 -13
  91. package/dist/ranger/payload.d.ts.map +1 -1
  92. package/dist/ranger/writer.d.ts +14 -14
  93. package/dist/ranger/writer.d.ts.map +1 -1
  94. package/dist/setupspecs.d.ts.map +1 -1
  95. package/dist/signals/observable.d.ts +3 -1
  96. package/dist/signals/observable.d.ts.map +1 -1
  97. package/dist/user/client.d.ts +5 -3
  98. package/dist/user/client.d.ts.map +1 -1
  99. package/dist/user/payload.d.ts +7 -6
  100. package/dist/user/payload.d.ts.map +1 -1
  101. package/dist/user/retriever.d.ts +2 -1
  102. package/dist/user/retriever.d.ts.map +1 -1
  103. package/dist/user/writer.d.ts +2 -2
  104. package/dist/user/writer.d.ts.map +1 -1
  105. package/dist/util/decodeJSONString.d.ts +3 -0
  106. package/dist/util/decodeJSONString.d.ts.map +1 -0
  107. package/dist/util/parseWithoutKeyConversion.d.ts +3 -0
  108. package/dist/util/parseWithoutKeyConversion.d.ts.map +1 -0
  109. package/dist/util/retrieve.d.ts +1 -1
  110. package/dist/util/retrieve.d.ts.map +1 -1
  111. package/dist/util/telem.d.ts.map +1 -1
  112. package/dist/util/zod.d.ts.map +1 -1
  113. package/dist/workspace/client.d.ts +6 -60
  114. package/dist/workspace/client.d.ts.map +1 -1
  115. package/dist/workspace/external.d.ts +3 -0
  116. package/dist/workspace/external.d.ts.map +1 -0
  117. package/dist/workspace/index.d.ts +1 -1
  118. package/dist/workspace/index.d.ts.map +1 -1
  119. package/dist/workspace/lineplot/client.d.ts +5 -44
  120. package/dist/workspace/lineplot/client.d.ts.map +1 -1
  121. package/dist/workspace/lineplot/external.d.ts +3 -0
  122. package/dist/workspace/lineplot/external.d.ts.map +1 -0
  123. package/dist/workspace/lineplot/index.d.ts +1 -1
  124. package/dist/workspace/lineplot/index.d.ts.map +1 -1
  125. package/dist/workspace/lineplot/payload.d.ts +45 -0
  126. package/dist/workspace/lineplot/payload.d.ts.map +1 -0
  127. package/dist/workspace/log/client.d.ts +5 -44
  128. package/dist/workspace/log/client.d.ts.map +1 -1
  129. package/dist/workspace/log/external.d.ts +3 -0
  130. package/dist/workspace/log/external.d.ts.map +1 -0
  131. package/dist/workspace/log/index.d.ts +1 -1
  132. package/dist/workspace/log/index.d.ts.map +1 -1
  133. package/dist/workspace/log/payload.d.ts +45 -0
  134. package/dist/workspace/log/payload.d.ts.map +1 -0
  135. package/dist/workspace/payload.d.ts +60 -0
  136. package/dist/workspace/payload.d.ts.map +1 -0
  137. package/dist/workspace/schematic/client.d.ts +5 -68
  138. package/dist/workspace/schematic/client.d.ts.map +1 -1
  139. package/dist/workspace/schematic/external.d.ts +3 -0
  140. package/dist/workspace/schematic/external.d.ts.map +1 -0
  141. package/dist/workspace/schematic/index.d.ts +1 -1
  142. package/dist/workspace/schematic/index.d.ts.map +1 -1
  143. package/dist/workspace/schematic/payload.d.ts +71 -0
  144. package/dist/workspace/schematic/payload.d.ts.map +1 -0
  145. package/dist/workspace/table/client.d.ts +5 -57
  146. package/dist/workspace/table/client.d.ts.map +1 -1
  147. package/dist/workspace/table/external.d.ts +3 -0
  148. package/dist/workspace/table/external.d.ts.map +1 -0
  149. package/dist/workspace/table/index.d.ts +1 -1
  150. package/dist/workspace/table/index.d.ts.map +1 -1
  151. package/dist/workspace/table/payload.d.ts +60 -0
  152. package/dist/workspace/table/payload.d.ts.map +1 -0
  153. package/eslint.config.js +1 -1
  154. package/examples/node/basicReadWrite.js +27 -27
  155. package/examples/node/liveStream.js +16 -16
  156. package/examples/node/seriesAndFrames.js +39 -39
  157. package/examples/node/streamWrite.js +48 -46
  158. package/package.json +14 -12
  159. package/src/access/client.ts +1 -1
  160. package/src/access/external.ts +1 -1
  161. package/src/access/index.ts +1 -1
  162. package/src/access/payload.ts +13 -13
  163. package/src/access/policy/client.ts +14 -13
  164. package/src/access/policy/external.ts +2 -1
  165. package/src/access/policy/index.ts +1 -1
  166. package/src/access/policy/ontology.ts +17 -0
  167. package/src/access/policy/payload.ts +8 -20
  168. package/src/access/policy/policy.spec.ts +17 -17
  169. package/src/access/policy/retriever.ts +3 -2
  170. package/src/access/policy/writer.ts +5 -5
  171. package/src/auth/auth.spec.ts +28 -24
  172. package/src/auth/auth.ts +8 -12
  173. package/src/auth/index.ts +1 -1
  174. package/src/channel/batchRetriever.spec.ts +26 -23
  175. package/src/channel/channel.spec.ts +1 -1
  176. package/src/channel/client.ts +24 -24
  177. package/src/channel/external.ts +1 -1
  178. package/src/channel/index.ts +1 -1
  179. package/src/channel/payload.ts +17 -21
  180. package/src/channel/retriever.ts +21 -22
  181. package/src/channel/writer.ts +12 -14
  182. package/src/client.ts +7 -17
  183. package/src/connection/checker.ts +10 -12
  184. package/src/connection/connection.spec.ts +18 -6
  185. package/src/connection/index.ts +1 -1
  186. package/src/control/client.ts +1 -1
  187. package/src/control/external.ts +1 -1
  188. package/src/control/index.ts +1 -1
  189. package/src/control/state.spec.ts +1 -1
  190. package/src/control/state.ts +9 -10
  191. package/src/errors.spec.ts +2 -2
  192. package/src/errors.ts +9 -1
  193. package/src/framer/adapter.spec.ts +29 -24
  194. package/src/framer/adapter.ts +38 -42
  195. package/src/framer/client.spec.ts +6 -12
  196. package/src/framer/client.ts +35 -39
  197. package/src/framer/deleter.spec.ts +1 -1
  198. package/src/framer/deleter.ts +6 -7
  199. package/src/framer/external.ts +1 -1
  200. package/src/framer/frame.spec.ts +1 -1
  201. package/src/framer/frame.ts +63 -51
  202. package/src/framer/index.ts +1 -1
  203. package/src/framer/iterator.spec.ts +1 -1
  204. package/src/framer/iterator.ts +12 -17
  205. package/src/framer/streamProxy.ts +2 -2
  206. package/src/framer/streamer.spec.ts +3 -11
  207. package/src/framer/streamer.ts +16 -20
  208. package/src/framer/writer.spec.ts +49 -8
  209. package/src/framer/writer.ts +40 -32
  210. package/src/hardware/client.ts +1 -1
  211. package/src/hardware/device/client.ts +65 -40
  212. package/src/hardware/device/device.spec.ts +50 -34
  213. package/src/hardware/device/external.ts +1 -1
  214. package/src/hardware/device/index.ts +1 -1
  215. package/src/hardware/device/payload.ts +30 -30
  216. package/src/hardware/external.ts +1 -1
  217. package/src/hardware/index.ts +1 -1
  218. package/src/hardware/rack/client.ts +53 -66
  219. package/src/hardware/rack/external.ts +1 -1
  220. package/src/hardware/rack/index.ts +1 -1
  221. package/src/hardware/rack/payload.ts +10 -19
  222. package/src/hardware/rack/rack.spec.ts +13 -1
  223. package/src/hardware/task/client.ts +161 -132
  224. package/src/hardware/task/external.ts +1 -1
  225. package/src/hardware/task/index.ts +1 -1
  226. package/src/hardware/task/payload.ts +50 -69
  227. package/src/hardware/task/task.spec.ts +99 -82
  228. package/src/index.ts +1 -1
  229. package/src/label/client.ts +13 -16
  230. package/src/label/external.ts +1 -1
  231. package/src/label/index.ts +1 -1
  232. package/src/label/label.spec.ts +1 -1
  233. package/src/label/payload.ts +4 -10
  234. package/src/label/retriever.ts +4 -8
  235. package/src/label/writer.ts +9 -16
  236. package/src/ontology/client.ts +31 -29
  237. package/src/ontology/external.ts +1 -1
  238. package/src/ontology/group/client.ts +6 -6
  239. package/src/ontology/group/external.ts +1 -1
  240. package/src/ontology/group/group.spec.ts +5 -5
  241. package/src/ontology/group/group.ts +11 -8
  242. package/src/ontology/group/index.ts +1 -1
  243. package/src/ontology/group/payload.ts +12 -36
  244. package/src/ontology/group/writer.ts +23 -27
  245. package/src/ontology/index.ts +1 -1
  246. package/src/ontology/ontology.spec.ts +16 -16
  247. package/src/ontology/payload.ts +68 -44
  248. package/src/ontology/writer.ts +17 -24
  249. package/src/ranger/alias.ts +26 -43
  250. package/src/ranger/client.ts +34 -41
  251. package/src/ranger/external.ts +2 -2
  252. package/src/ranger/index.ts +1 -1
  253. package/src/ranger/kv.ts +10 -33
  254. package/src/ranger/payload.ts +15 -37
  255. package/src/ranger/ranger.spec.ts +2 -3
  256. package/src/ranger/writer.ts +9 -27
  257. package/src/setupspecs.ts +1 -1
  258. package/src/signals/external.ts +1 -1
  259. package/src/signals/index.ts +1 -1
  260. package/src/signals/observable.ts +4 -5
  261. package/src/transport.ts +1 -1
  262. package/src/user/client.ts +9 -5
  263. package/src/user/external.ts +1 -1
  264. package/src/user/index.ts +1 -1
  265. package/src/user/payload.ts +6 -10
  266. package/src/user/retriever.ts +2 -2
  267. package/src/user/user.spec.ts +18 -16
  268. package/src/user/writer.ts +4 -11
  269. package/src/util/decodeJSONString.ts +13 -0
  270. package/src/util/parseWithoutKeyConversion.ts +19 -0
  271. package/src/util/retrieve.spec.ts +4 -14
  272. package/src/util/retrieve.ts +3 -13
  273. package/src/util/telem.ts +2 -2
  274. package/src/util/zod.ts +1 -1
  275. package/src/vite-env.d.ts +2 -1
  276. package/src/workspace/client.ts +39 -57
  277. package/src/workspace/external.ts +11 -0
  278. package/src/workspace/index.ts +2 -2
  279. package/src/workspace/lineplot/client.ts +31 -36
  280. package/src/workspace/lineplot/external.ts +11 -0
  281. package/src/workspace/lineplot/index.ts +2 -2
  282. package/src/workspace/lineplot/linePlot.spec.ts +2 -3
  283. package/src/workspace/lineplot/payload.ts +32 -0
  284. package/src/workspace/log/client.ts +34 -39
  285. package/src/workspace/log/external.ts +11 -0
  286. package/src/workspace/log/index.ts +2 -2
  287. package/src/workspace/log/log.spec.ts +6 -19
  288. package/src/workspace/log/payload.ts +32 -0
  289. package/src/workspace/payload.ts +36 -0
  290. package/src/workspace/schematic/client.ts +39 -56
  291. package/src/workspace/schematic/external.ts +11 -0
  292. package/src/workspace/schematic/index.ts +2 -2
  293. package/src/workspace/schematic/payload.ts +37 -0
  294. package/src/workspace/schematic/schematic.spec.ts +16 -7
  295. package/src/workspace/table/client.ts +36 -50
  296. package/src/workspace/table/external.ts +11 -0
  297. package/src/workspace/table/index.ts +2 -2
  298. package/src/workspace/table/payload.ts +36 -0
  299. package/src/workspace/table/table.spec.ts +1 -1
  300. package/src/workspace/workspace.spec.ts +2 -3
  301. package/vite.config.ts +1 -1
  302. package/dist/channel/creator.d.ts +0 -9
  303. package/dist/channel/creator.d.ts.map +0 -1
  304. package/src/channel/creator.ts +0 -37
@@ -1,4 +1,4 @@
1
- // Copyright 2024 Synnax Labs, Inc.
1
+ // Copyright 2025 Synnax Labs, Inc.
2
2
  //
3
3
  // Use of this software is governed by the Business Source License included in the file
4
4
  // licenses/BSL.txt.
@@ -23,10 +23,9 @@ import {
23
23
  import { toArray } from "@synnaxlabs/x/toArray";
24
24
  import { z } from "zod";
25
25
 
26
- import { type KeyOrName, type KeysOrNames, type Params } from "@/channel/payload";
27
- import { type Retriever } from "@/channel/retriever";
28
- import { WriteFrameAdapter } from "@/framer/adapter";
29
- import { type CrudeFrame, frameZ } from "@/framer/frame";
26
+ import { channel } from "@/channel";
27
+ import { WriteAdapter } from "@/framer/adapter";
28
+ import { type Crude, frameZ } from "@/framer/frame";
30
29
  import { StreamProxy } from "@/framer/streamProxy";
31
30
 
32
31
  enum Command {
@@ -64,7 +63,7 @@ export const ALWAYS_INDEX_PERSIST_ON_AUTO_COMMIT: TimeSpan = new TimeSpan(-1);
64
63
  const netConfigZ = z.object({
65
64
  start: TimeStamp.z.optional(),
66
65
  controlSubject: control.subjectZ.optional(),
67
- keys: z.number().array().optional(),
66
+ keys: channel.keyZ.array().optional(),
68
67
  authorities: control.Authority.z.array().optional(),
69
68
  mode: z.nativeEnum(WriterMode).optional(),
70
69
  errOnUnauthorized: z.boolean().optional(),
@@ -72,7 +71,7 @@ const netConfigZ = z.object({
72
71
  autoIndexPersistInterval: TimeSpan.z.optional(),
73
72
  });
74
73
 
75
- type Config = z.infer<typeof netConfigZ>;
74
+ interface Config extends z.infer<typeof netConfigZ> {}
76
75
 
77
76
  const reqZ = z.object({
78
77
  command: z.nativeEnum(Command),
@@ -80,7 +79,7 @@ const reqZ = z.object({
80
79
  frame: frameZ.optional(),
81
80
  });
82
81
 
83
- type Request = z.infer<typeof reqZ>;
82
+ interface Request extends z.infer<typeof reqZ> {}
84
83
 
85
84
  const resZ = z.object({
86
85
  ack: z.boolean(),
@@ -88,11 +87,11 @@ const resZ = z.object({
88
87
  error: errorZ.optional().nullable(),
89
88
  });
90
89
 
91
- type Response = z.infer<typeof resZ>;
90
+ interface Response extends z.infer<typeof resZ> {}
92
91
 
93
92
  export interface WriterConfig {
94
93
  // channels denote the channels to write to.
95
- channels: Params;
94
+ channels: channel.Params;
96
95
  // start sets the starting timestamp for the first sample in the writer.
97
96
  start?: CrudeTimeStamp;
98
97
  // controlSubject sets the control subject of the writer.
@@ -158,23 +157,21 @@ export interface WriterConfig {
158
157
  export class Writer {
159
158
  private static readonly ENDPOINT = "/frame/write";
160
159
  private readonly stream: StreamProxy<typeof reqZ, typeof resZ>;
161
- private readonly adapter: WriteFrameAdapter;
160
+ private readonly adapter: WriteAdapter;
161
+ private errAccumulated: boolean = false;
162
162
 
163
- private constructor(
164
- stream: Stream<typeof reqZ, typeof resZ>,
165
- adapter: WriteFrameAdapter,
166
- ) {
163
+ private constructor(stream: Stream<typeof reqZ, typeof resZ>, adapter: WriteAdapter) {
167
164
  this.stream = new StreamProxy("Writer", stream);
168
165
  this.adapter = adapter;
169
166
  }
170
167
 
171
168
  static async _open(
172
- retriever: Retriever,
169
+ retriever: channel.Retriever,
173
170
  client: StreamClient,
174
171
  {
175
172
  channels,
176
173
  start = TimeStamp.now(),
177
- authorities = control.Authority.Absolute,
174
+ authorities = control.Authority.ABSOLUTE,
178
175
  controlSubject: subject,
179
176
  mode = WriterMode.PersistStream,
180
177
  errOnUnauthorized = false,
@@ -182,7 +179,7 @@ export class Writer {
182
179
  autoIndexPersistInterval = TimeSpan.SECOND,
183
180
  }: WriterConfig,
184
181
  ): Promise<Writer> {
185
- const adapter = await WriteFrameAdapter.open(retriever, channels);
182
+ const adapter = await WriteAdapter.open(retriever, channels);
186
183
  const stream = await client.stream(Writer.ENDPOINT, reqZ, resZ);
187
184
  const writer = new Writer(stream, adapter);
188
185
  await writer.execute({
@@ -201,14 +198,19 @@ export class Writer {
201
198
  return writer;
202
199
  }
203
200
 
204
- async write(channel: KeyOrName, data: CrudeSeries): Promise<boolean>;
205
-
206
- async write(channel: KeysOrNames, data: CrudeSeries[]): Promise<boolean>;
207
-
208
- async write(frame: CrudeFrame | Record<KeyOrName, CrudeSeries>): Promise<boolean>;
201
+ private async checkForAccumulatedError(): Promise<boolean> {
202
+ if (!this.errAccumulated && this.stream.received()) {
203
+ this.errAccumulated = true;
204
+ while (this.stream.received()) await this.stream.receive();
205
+ }
206
+ return this.errAccumulated;
207
+ }
209
208
 
209
+ async write(channel: channel.KeyOrName, data: CrudeSeries): Promise<boolean>;
210
+ async write(channel: channel.KeysOrNames, data: CrudeSeries[]): Promise<boolean>;
211
+ async write(frame: Crude | Record<channel.KeyOrName, CrudeSeries>): Promise<boolean>;
210
212
  async write(
211
- channelsOrData: Params | Record<KeyOrName, CrudeSeries> | CrudeFrame,
213
+ channelsOrData: channel.Params | Record<channel.KeyOrName, CrudeSeries> | Crude,
212
214
  series?: CrudeSeries | CrudeSeries[],
213
215
  ): Promise<boolean>;
214
216
 
@@ -227,9 +229,10 @@ export class Writer {
227
229
  * should acknowledge the error by calling the error method or closing the writer.
228
230
  */
229
231
  async write(
230
- channelsOrData: Params | Record<KeyOrName, CrudeSeries> | CrudeFrame,
232
+ channelsOrData: channel.Params | Record<channel.KeyOrName, CrudeSeries> | Crude,
231
233
  series?: CrudeSeries | CrudeSeries[],
232
234
  ): Promise<boolean> {
235
+ if (await this.checkForAccumulatedError()) return false;
233
236
  const frame = await this.adapter.adapt(channelsOrData, series);
234
237
  this.stream.send({ command: Command.Write, frame: frame.toPayload() });
235
238
  return true;
@@ -237,21 +240,27 @@ export class Writer {
237
240
 
238
241
  async setAuthority(value: number): Promise<boolean>;
239
242
 
240
- async setAuthority(key: KeyOrName, authority: control.Authority): Promise<boolean>;
243
+ async setAuthority(
244
+ key: channel.KeyOrName,
245
+ authority: control.Authority,
246
+ ): Promise<boolean>;
241
247
 
242
- async setAuthority(value: Record<KeyOrName, control.Authority>): Promise<boolean>;
248
+ async setAuthority(
249
+ value: Record<channel.KeyOrName, control.Authority>,
250
+ ): Promise<boolean>;
243
251
 
244
252
  async setAuthority(
245
- value: Record<KeyOrName, control.Authority> | KeyOrName | number,
253
+ value: Record<channel.KeyOrName, control.Authority> | channel.KeyOrName | number,
246
254
  authority?: control.Authority,
247
255
  ): Promise<boolean> {
256
+ if (await this.checkForAccumulatedError()) return false;
248
257
  let config: Config;
249
258
  if (typeof value === "number" && authority == null)
250
259
  config = { keys: [], authorities: [value] };
251
260
  else {
252
- let oValue: Record<KeyOrName, control.Authority>;
261
+ let oValue: Record<channel.KeyOrName, control.Authority>;
253
262
  if (typeof value === "string" || typeof value === "number")
254
- oValue = { [value]: authority } as Record<KeyOrName, control.Authority>;
263
+ oValue = { [value]: authority } as Record<channel.KeyOrName, control.Authority>;
255
264
  else oValue = value;
256
265
  oValue = await this.adapter.adaptObjectKeys(oValue);
257
266
  config = {
@@ -272,7 +281,7 @@ export class Writer {
272
281
  * After the caller acknowledges the error, they can attempt to commit again.
273
282
  */
274
283
  async commit(): Promise<boolean> {
275
- if (this.errorAccumulated) return false;
284
+ if (await this.checkForAccumulatedError()) return false;
276
285
  const res = await this.execute({ command: Command.Commit });
277
286
  return res.ack;
278
287
  }
@@ -282,7 +291,6 @@ export class Writer {
282
291
  * state, allowing the writer to be used again.
283
292
  */
284
293
  async error(): Promise<Error | null> {
285
- this.stream.send({ command: Command.Error });
286
294
  const res = await this.execute({ command: Command.Error });
287
295
  return res.error != null ? decodeError(res.error) : null;
288
296
  }
@@ -1,4 +1,4 @@
1
- // Copyright 2024 Synnax Labs, Inc.
1
+ // Copyright 2025 Synnax Labs, Inc.
2
2
  //
3
3
  // Use of this software is governed by the Business Source License included in the file
4
4
  // licenses/BSL.txt.
@@ -1,4 +1,4 @@
1
- // Copyright 2024 Synnax Labs, Inc.
1
+ // Copyright 2025 Synnax Labs, Inc.
2
2
  //
3
3
  // Use of this software is governed by the Business Source License included in the file
4
4
  // licenses/BSL.txt.
@@ -15,28 +15,30 @@ import { z } from "zod";
15
15
  import { type framer } from "@/framer";
16
16
  import {
17
17
  type Device,
18
- type DeviceKey,
19
- deviceKeyZ,
20
18
  deviceZ,
21
- type NewDevice,
22
- newDeviceZ,
19
+ type Key,
20
+ keyZ,
21
+ type New,
22
+ newZ,
23
+ ONTOLOGY_TYPE,
23
24
  } from "@/hardware/device/payload";
25
+ import { ontology } from "@/ontology";
24
26
  import { signals } from "@/signals";
25
27
  import { checkForMultipleOrNoResults } from "@/util/retrieve";
26
28
  import { nullableArrayZ } from "@/util/zod";
27
29
 
28
- const DEVICE_SET_NAME = "sy_device_set";
29
- const DEVICE_DELETE_NAME = "sy_device_delete";
30
+ const SET_CHANNEL_NAME = "sy_device_set";
31
+ const DELETE_CHANNEL_NAME = "sy_device_delete";
30
32
 
31
33
  const RETRIEVE_ENDPOINT = "/hardware/device/retrieve";
32
34
  const CREATE_ENDPOINT = "/hardware/device/create";
33
35
  const DELETE_ENDPOINT = "/hardware/device/delete";
34
36
 
35
- const createReqZ = z.object({ devices: newDeviceZ.array() });
37
+ const createReqZ = z.object({ devices: newZ.array() });
36
38
 
37
39
  const createResZ = z.object({ devices: deviceZ.array() });
38
40
 
39
- const deleteReqZ = z.object({ keys: deviceKeyZ.array() });
41
+ const deleteReqZ = z.object({ keys: keyZ.array() });
40
42
 
41
43
  const deleteResZ = z.object({});
42
44
 
@@ -44,21 +46,22 @@ const retrieveReqZ = z.object({
44
46
  search: z.string().optional(),
45
47
  limit: z.number().optional(),
46
48
  offset: z.number().optional(),
47
- keys: deviceKeyZ.array().optional(),
49
+ keys: keyZ.array().optional(),
48
50
  names: z.string().array().optional(),
49
51
  makes: z.string().array().optional(),
50
52
  });
51
53
 
52
- type RetrieveRequest = z.input<typeof retrieveReqZ>;
54
+ interface RetrieveRequest extends z.input<typeof retrieveReqZ> {}
53
55
 
54
- export type RetrieveOptions = Pick<RetrieveRequest, "limit" | "offset" | "makes">;
56
+ export interface RetrieveOptions
57
+ extends Pick<RetrieveRequest, "limit" | "offset" | "makes"> {}
55
58
 
56
- type PageOptions = Pick<RetrieveOptions, "makes">;
59
+ interface PageOptions extends Pick<RetrieveOptions, "makes"> {}
57
60
 
58
61
  const retrieveResZ = z.object({ devices: nullableArrayZ(deviceZ) });
59
62
 
60
- export class Client implements AsyncTermSearcher<string, DeviceKey, Device> {
61
- readonly type = "device";
63
+ export class Client implements AsyncTermSearcher<string, Key, Device> {
64
+ readonly type = ONTOLOGY_TYPE;
62
65
  private readonly client: UnaryClient;
63
66
  private readonly frameClient: framer.Client;
64
67
 
@@ -67,20 +70,29 @@ export class Client implements AsyncTermSearcher<string, DeviceKey, Device> {
67
70
  this.frameClient = frameClient;
68
71
  }
69
72
 
70
- async retrieve<P extends UnknownRecord = UnknownRecord>(
71
- key: string,
72
- options?: RetrieveOptions,
73
- ): Promise<Device<P>>;
74
-
75
- async retrieve<P extends UnknownRecord = UnknownRecord>(
73
+ async retrieve<
74
+ Properties extends UnknownRecord = UnknownRecord,
75
+ Make extends string = string,
76
+ Model extends string = string,
77
+ >(key: string, options?: RetrieveOptions): Promise<Device<Properties, Make, Model>>;
78
+
79
+ async retrieve<
80
+ Properties extends UnknownRecord = UnknownRecord,
81
+ Make extends string = string,
82
+ Model extends string = string,
83
+ >(
76
84
  keys: string[],
77
85
  options?: RetrieveOptions,
78
- ): Promise<Array<Device<P>>>;
86
+ ): Promise<Array<Device<Properties, Make, Model>>>;
79
87
 
80
- async retrieve<P extends UnknownRecord = UnknownRecord>(
88
+ async retrieve<
89
+ Properties extends UnknownRecord = UnknownRecord,
90
+ Make extends string = string,
91
+ Model extends string = string,
92
+ >(
81
93
  keys: string | string[],
82
94
  options?: RetrieveOptions,
83
- ): Promise<Device<P> | Array<Device<P>>> {
95
+ ): Promise<Device<Properties, Make, Model> | Array<Device<Properties, Make, Model>>> {
84
96
  const isSingle = !Array.isArray(keys);
85
97
  const res = await sendRequired(
86
98
  this.client,
@@ -90,7 +102,9 @@ export class Client implements AsyncTermSearcher<string, DeviceKey, Device> {
90
102
  retrieveResZ,
91
103
  );
92
104
  checkForMultipleOrNoResults("Device", keys, res.devices, isSingle);
93
- return (isSingle ? res.devices[0] : res.devices) as Device<P> | Array<Device<P>>;
105
+ return isSingle
106
+ ? (res.devices[0] as Device<Properties, Make, Model>)
107
+ : (res.devices as Array<Device<Properties, Make, Model>>);
94
108
  }
95
109
 
96
110
  async search(term: string, options?: RetrieveOptions): Promise<Device[]> {
@@ -117,17 +131,23 @@ export class Client implements AsyncTermSearcher<string, DeviceKey, Device> {
117
131
  ).devices;
118
132
  }
119
133
 
120
- async create<P extends UnknownRecord = UnknownRecord>(
121
- device: NewDevice<P>,
122
- ): Promise<Device<P>>;
123
-
124
- async create<P extends UnknownRecord = UnknownRecord>(
125
- devices: NewDevice<P>[],
126
- ): Promise<Device<P>[]>;
127
-
128
- async create<P extends UnknownRecord = UnknownRecord>(
129
- devices: NewDevice<P> | NewDevice<P>[],
130
- ): Promise<Device<P> | Device<P>[]> {
134
+ async create<
135
+ Properties extends UnknownRecord = UnknownRecord,
136
+ Make extends string = string,
137
+ Model extends string = string,
138
+ >(device: New<Properties, Make>): Promise<Device<Properties, Make, Model>>;
139
+ async create<
140
+ Properties extends UnknownRecord = UnknownRecord,
141
+ Make extends string = string,
142
+ Model extends string = string,
143
+ >(devices: New<Properties, Make>[]): Promise<Device<Properties, Make, Model>[]>;
144
+ async create<
145
+ Properties extends UnknownRecord = UnknownRecord,
146
+ Make extends string = string,
147
+ Model extends string = string,
148
+ >(
149
+ devices: New<Properties, Make> | New<Properties, Make>[],
150
+ ): Promise<Device<Properties, Make, Model> | Device<Properties, Make, Model>[]> {
131
151
  const isSingle = !Array.isArray(devices);
132
152
  const res = await sendRequired(
133
153
  this.client,
@@ -136,7 +156,9 @@ export class Client implements AsyncTermSearcher<string, DeviceKey, Device> {
136
156
  createReqZ,
137
157
  createResZ,
138
158
  );
139
- return isSingle ? (res.devices[0] as Device<P>) : (res.devices as Device<P>[]);
159
+ return isSingle
160
+ ? (res.devices[0] as Device<Properties, Make, Model>)
161
+ : (res.devices as Device<Properties, Make, Model>[]);
140
162
  }
141
163
 
142
164
  async delete(keys: string | string[]): Promise<void> {
@@ -152,15 +174,15 @@ export class Client implements AsyncTermSearcher<string, DeviceKey, Device> {
152
174
  async openDeviceTracker(): Promise<signals.Observable<string, Device>> {
153
175
  return await signals.openObservable<string, Device>(
154
176
  this.frameClient,
155
- DEVICE_SET_NAME,
156
- DEVICE_DELETE_NAME,
177
+ SET_CHANNEL_NAME,
178
+ DELETE_CHANNEL_NAME,
157
179
  decodeDeviceChanges,
158
180
  );
159
181
  }
160
182
 
161
183
  newSearcherWithOptions(
162
184
  options: RetrieveOptions,
163
- ): AsyncTermSearcher<string, DeviceKey, Device> {
185
+ ): AsyncTermSearcher<string, Key, Device> {
164
186
  return {
165
187
  type: this.type,
166
188
  search: async (term: string) => await this.search(term, options),
@@ -176,3 +198,6 @@ const decodeDeviceChanges: signals.Decoder<string, Device> = (variant, data) =>
176
198
  return data.toStrings().map((k) => ({ variant, key: k, value: undefined }));
177
199
  return data.parseJSON(deviceZ).map((d) => ({ variant, key: d.key, value: d }));
178
200
  };
201
+
202
+ export const ontologyID = (key: Key): ontology.ID =>
203
+ new ontology.ID({ type: ONTOLOGY_TYPE, key });
@@ -1,4 +1,4 @@
1
- // Copyright 2024 Synnax Labs, Inc.
1
+ // Copyright 2025 Synnax Labs, Inc.
2
2
  //
3
3
  // Use of this software is governed by the Business Source License included in the file
4
4
  // licenses/BSL.txt.
@@ -13,42 +13,58 @@ import { newClient } from "@/setupspecs";
13
13
 
14
14
  const client = newClient();
15
15
 
16
- describe("Device", () => {
17
- describe("Device", () => {
18
- describe("create", () => {
19
- it("should create a device on a rack", async () => {
20
- const rack = await client.hardware.racks.create({ name: "test" });
21
- const d = await client.hardware.devices.create({
22
- rack: rack.key,
23
- location: "Dev1",
24
- key: "SN222",
25
- name: "test",
26
- make: "ni",
27
- model: "dog",
28
- properties: { cat: "dog" },
29
- });
30
- expect(d.key).toEqual("SN222");
31
- expect(d.name).toBe("test");
32
- expect(d.make).toBe("ni");
16
+ describe("Device", async () => {
17
+ const testRack = await client.hardware.racks.create({ name: "test" });
18
+ describe("create", () => {
19
+ it("should create a device on a rack", async () => {
20
+ const d = await client.hardware.devices.create({
21
+ rack: testRack.key,
22
+ location: "Dev1",
23
+ key: "SN222",
24
+ name: "test",
25
+ make: "ni",
26
+ model: "dog",
27
+ properties: { cat: "dog" },
33
28
  });
29
+ expect(d.key).toEqual("SN222");
30
+ expect(d.name).toBe("test");
31
+ expect(d.make).toBe("ni");
34
32
  });
35
- describe("retrieve", () => {
36
- it("should retrieve a device by its key", async () => {
37
- const rack = await client.hardware.racks.create({ name: "test" });
38
- const d = await client.hardware.devices.create({
39
- key: "SN222",
40
- rack: rack.key,
41
- location: "Dev1",
42
- name: "test",
43
- make: "ni",
44
- model: "dog",
45
- properties: { cat: "dog" },
46
- });
47
- const retrieved = await client.hardware.devices.retrieve(d.key);
48
- expect(retrieved.key).toBe(d.key);
49
- expect(retrieved.name).toBe("test");
50
- expect(retrieved.make).toBe("ni");
33
+ });
34
+ it("should properly encode and decode properties", async () => {
35
+ const properties = {
36
+ rate: 10,
37
+ stateIndexChannel: 234,
38
+ inputChannels: { port1: 34214 },
39
+ outputChannels: [{ port2: 232 }],
40
+ };
41
+ const d = await client.hardware.devices.create({
42
+ key: "SN222",
43
+ rack: testRack.key,
44
+ location: "Dev1",
45
+ name: "test",
46
+ make: "ni",
47
+ model: "dog",
48
+ properties,
49
+ });
50
+ const retrieved = await client.hardware.devices.retrieve(d.key);
51
+ expect(retrieved.properties).toEqual(properties);
52
+ });
53
+ describe("retrieve", () => {
54
+ it("should retrieve a device by its key", async () => {
55
+ const d = await client.hardware.devices.create({
56
+ key: "SN222",
57
+ rack: testRack.key,
58
+ location: "Dev1",
59
+ name: "test",
60
+ make: "ni",
61
+ model: "dog",
62
+ properties: { cat: "dog" },
51
63
  });
64
+ const retrieved = await client.hardware.devices.retrieve(d.key);
65
+ expect(retrieved.key).toBe(d.key);
66
+ expect(retrieved.name).toBe("test");
67
+ expect(retrieved.make).toBe("ni");
52
68
  });
53
69
  });
54
70
  });
@@ -1,4 +1,4 @@
1
- // Copyright 2024 Synnax Labs, Inc.
1
+ // Copyright 2025 Synnax Labs, Inc.
2
2
  //
3
3
  // Use of this software is governed by the Business Source License included in the file
4
4
  // licenses/BSL.txt.
@@ -1,4 +1,4 @@
1
- // Copyright 2024 Synnax Labs, Inc.
1
+ // Copyright 2025 Synnax Labs, Inc.
2
2
  //
3
3
  // Use of this software is governed by the Business Source License included in the file
4
4
  // licenses/BSL.txt.
@@ -1,4 +1,4 @@
1
- // Copyright 2024 Synnax Labs, Inc.
1
+ // Copyright 2025 Synnax Labs, Inc.
2
2
  //
3
3
  // Use of this software is governed by the Business Source License included in the file
4
4
  // licenses/BSL.txt.
@@ -10,44 +10,44 @@
10
10
  import { binary, type UnknownRecord } from "@synnaxlabs/x";
11
11
  import { z } from "zod";
12
12
 
13
- import { rackKeyZ } from "@/hardware/rack/payload";
14
- import { ontology } from "@/ontology";
13
+ import { keyZ as rackKeyZ } from "@/hardware/rack/payload";
14
+ import { decodeJSONString } from "@/util/decodeJSONString";
15
15
 
16
- export const deviceKeyZ = z.string();
16
+ export const keyZ = z.string();
17
+ export type Key = z.infer<typeof keyZ>;
17
18
 
18
19
  export const deviceZ = z.object({
19
- key: deviceKeyZ,
20
+ key: keyZ,
20
21
  rack: rackKeyZ,
21
22
  name: z.string(),
22
23
  make: z.string(),
23
24
  model: z.string(),
24
25
  location: z.string(),
25
26
  configured: z.boolean().optional(),
26
- properties: z.record(z.unknown()).or(
27
- z.string().transform((c) => {
28
- if (c === "") return {};
29
- return binary.JSON_CODEC.decodeString(c);
30
- }),
31
- ) as z.ZodType<UnknownRecord>,
27
+ properties: z.record(z.unknown()).or(z.string().transform(decodeJSONString)),
32
28
  });
33
-
34
- export type Device<P extends UnknownRecord = UnknownRecord> = Omit<
35
- z.output<typeof deviceZ>,
36
- "properties"
37
- > & { properties: P };
38
-
39
- export type DeviceKey = z.infer<typeof deviceKeyZ>;
40
-
41
- export const newDeviceZ = deviceZ.extend({
29
+ export interface Device<
30
+ Properties extends UnknownRecord = UnknownRecord,
31
+ Make extends string = string,
32
+ Model extends string = string,
33
+ > extends Omit<z.output<typeof deviceZ>, "properties"> {
34
+ properties: Properties;
35
+ make: Make;
36
+ model: Model;
37
+ }
38
+
39
+ export const newZ = deviceZ.extend({
42
40
  properties: z.unknown().transform((c) => binary.JSON_CODEC.encodeString(c)),
43
41
  });
44
-
45
- export type NewDevice<P extends UnknownRecord = UnknownRecord> = Omit<
46
- z.input<typeof newDeviceZ>,
47
- "properties"
48
- > & { properties: P };
49
-
50
- export const ONTOLOGY_TYPE: ontology.ResourceType = "device";
51
-
52
- export const ontologyID = (key: DeviceKey): ontology.ID =>
53
- new ontology.ID({ type: ONTOLOGY_TYPE, key: key.toString() });
42
+ export interface New<
43
+ Properties extends UnknownRecord = UnknownRecord,
44
+ Make extends string = string,
45
+ Model extends string = string,
46
+ > extends Omit<z.input<typeof newZ>, "properties"> {
47
+ properties: Properties;
48
+ make: Make;
49
+ model: Model;
50
+ }
51
+
52
+ export const ONTOLOGY_TYPE = "device";
53
+ export type OntologyType = typeof ONTOLOGY_TYPE;
@@ -1,4 +1,4 @@
1
- // Copyright 2024 Synnax Labs, Inc.
1
+ // Copyright 2025 Synnax Labs, Inc.
2
2
  //
3
3
  // Use of this software is governed by the Business Source License included in the file
4
4
  // licenses/BSL.txt.
@@ -1,4 +1,4 @@
1
- // Copyright 2024 Synnax Labs, Inc.
1
+ // Copyright 2025 Synnax Labs, Inc.
2
2
  //
3
3
  // Use of this software is governed by the Business Source License included in the file
4
4
  // licenses/BSL.txt.