@synnaxlabs/client 0.47.0 → 0.49.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 (311) hide show
  1. package/.turbo/turbo-build.log +7 -7
  2. package/dist/client.cjs +35 -35
  3. package/dist/client.js +6264 -5984
  4. package/dist/eslint.config.d.ts +3 -2
  5. package/dist/eslint.config.d.ts.map +1 -1
  6. package/dist/src/access/client.d.ts +3 -1
  7. package/dist/src/access/client.d.ts.map +1 -1
  8. package/dist/src/access/enforce.d.ts +35 -0
  9. package/dist/src/access/enforce.d.ts.map +1 -0
  10. package/dist/src/access/enforce.spec.d.ts +2 -0
  11. package/dist/src/access/enforce.spec.d.ts.map +1 -0
  12. package/dist/src/access/external.d.ts +3 -0
  13. package/dist/src/access/external.d.ts.map +1 -1
  14. package/dist/src/access/payload.d.ts +0 -6
  15. package/dist/src/access/payload.d.ts.map +1 -1
  16. package/dist/src/access/policy/access.spec.d.ts +2 -0
  17. package/dist/src/access/policy/access.spec.d.ts.map +1 -0
  18. package/dist/src/access/policy/client.d.ts +485 -31
  19. package/dist/src/access/policy/client.d.ts.map +1 -1
  20. package/dist/src/access/policy/payload.d.ts +36 -113
  21. package/dist/src/access/policy/payload.d.ts.map +1 -1
  22. package/dist/src/access/role/client.d.ts +135 -0
  23. package/dist/src/access/role/client.d.ts.map +1 -0
  24. package/dist/src/access/role/external.d.ts.map +1 -0
  25. package/dist/src/access/role/index.d.ts +2 -0
  26. package/dist/src/access/role/index.d.ts.map +1 -0
  27. package/dist/src/access/role/payload.d.ts +27 -0
  28. package/dist/src/access/role/payload.d.ts.map +1 -0
  29. package/dist/src/access/role/role.spec.d.ts +2 -0
  30. package/dist/src/access/role/role.spec.d.ts.map +1 -0
  31. package/dist/src/arc/access.spec.d.ts +2 -0
  32. package/dist/src/arc/access.spec.d.ts.map +1 -0
  33. package/dist/src/arc/client.d.ts +5 -14
  34. package/dist/src/arc/client.d.ts.map +1 -1
  35. package/dist/src/arc/payload.d.ts +11 -2
  36. package/dist/src/arc/payload.d.ts.map +1 -1
  37. package/dist/src/auth/auth.d.ts +5 -3
  38. package/dist/src/auth/auth.d.ts.map +1 -1
  39. package/dist/src/channel/access.spec.d.ts +2 -0
  40. package/dist/src/channel/access.spec.d.ts.map +1 -0
  41. package/dist/src/channel/client.d.ts +0 -1
  42. package/dist/src/channel/client.d.ts.map +1 -1
  43. package/dist/src/channel/payload.d.ts +19 -8
  44. package/dist/src/channel/payload.d.ts.map +1 -1
  45. package/dist/src/channel/payload.spec.d.ts +2 -0
  46. package/dist/src/channel/payload.spec.d.ts.map +1 -0
  47. package/dist/src/channel/retriever.d.ts +4 -6
  48. package/dist/src/channel/retriever.d.ts.map +1 -1
  49. package/dist/src/channel/writer.d.ts.map +1 -1
  50. package/dist/src/client.d.ts +18 -10
  51. package/dist/src/client.d.ts.map +1 -1
  52. package/dist/src/connection/checker.d.ts +2 -3
  53. package/dist/src/connection/checker.d.ts.map +1 -1
  54. package/dist/src/connection.spec.d.ts +2 -0
  55. package/dist/src/connection.spec.d.ts.map +1 -0
  56. package/dist/src/device/access.spec.d.ts +2 -0
  57. package/dist/src/device/access.spec.d.ts.map +1 -0
  58. package/dist/src/{hardware/device → device}/client.d.ts +14 -7
  59. package/dist/src/device/client.d.ts.map +1 -0
  60. package/dist/src/device/device.spec.d.ts.map +1 -0
  61. package/dist/src/device/external.d.ts.map +1 -0
  62. package/dist/src/device/index.d.ts.map +1 -0
  63. package/dist/src/{hardware/device → device}/payload.d.ts +1 -1
  64. package/dist/src/device/payload.d.ts.map +1 -0
  65. package/dist/src/errors.d.ts +3 -0
  66. package/dist/src/errors.d.ts.map +1 -1
  67. package/dist/src/framer/adapter.d.ts +2 -2
  68. package/dist/src/framer/adapter.d.ts.map +1 -1
  69. package/dist/src/framer/client.d.ts +8 -1
  70. package/dist/src/framer/client.d.ts.map +1 -1
  71. package/dist/src/framer/frame.d.ts +11 -5
  72. package/dist/src/framer/frame.d.ts.map +1 -1
  73. package/dist/src/framer/iterator.d.ts +3 -3
  74. package/dist/src/framer/streamer.d.ts +24 -21
  75. package/dist/src/framer/streamer.d.ts.map +1 -1
  76. package/dist/src/framer/writer.d.ts +13 -13
  77. package/dist/src/index.d.ts +6 -7
  78. package/dist/src/index.d.ts.map +1 -1
  79. package/dist/src/label/access.spec.d.ts +2 -0
  80. package/dist/src/label/access.spec.d.ts.map +1 -0
  81. package/dist/src/label/client.d.ts +20 -11
  82. package/dist/src/label/client.d.ts.map +1 -1
  83. package/dist/src/ontology/client.d.ts +6 -6
  84. package/dist/src/ontology/client.d.ts.map +1 -1
  85. package/dist/src/ontology/group/access.spec.d.ts +2 -0
  86. package/dist/src/ontology/group/access.spec.d.ts.map +1 -0
  87. package/dist/src/ontology/group/client.d.ts +2 -2
  88. package/dist/src/ontology/group/client.d.ts.map +1 -1
  89. package/dist/src/ontology/group/payload.d.ts +1 -2
  90. package/dist/src/ontology/group/payload.d.ts.map +1 -1
  91. package/dist/src/ontology/payload.d.ts +23 -17
  92. package/dist/src/ontology/payload.d.ts.map +1 -1
  93. package/dist/src/ontology/writer.d.ts +10 -10
  94. package/dist/src/ontology/writer.d.ts.map +1 -1
  95. package/dist/src/rack/access.spec.d.ts +2 -0
  96. package/dist/src/rack/access.spec.d.ts.map +1 -0
  97. package/dist/src/{hardware/rack → rack}/client.d.ts +15 -8
  98. package/dist/src/rack/client.d.ts.map +1 -0
  99. package/dist/src/rack/external.d.ts.map +1 -0
  100. package/dist/src/rack/index.d.ts.map +1 -0
  101. package/dist/src/{hardware/rack → rack}/payload.d.ts +1 -1
  102. package/dist/src/rack/payload.d.ts.map +1 -0
  103. package/dist/src/rack/rack.spec.d.ts.map +1 -0
  104. package/dist/src/ranger/access.spec.d.ts +2 -0
  105. package/dist/src/ranger/access.spec.d.ts.map +1 -0
  106. package/dist/src/ranger/alias.d.ts +1 -8
  107. package/dist/src/ranger/alias.d.ts.map +1 -1
  108. package/dist/src/ranger/client.d.ts +12 -5
  109. package/dist/src/ranger/client.d.ts.map +1 -1
  110. package/dist/src/ranger/kv.d.ts +0 -3
  111. package/dist/src/ranger/kv.d.ts.map +1 -1
  112. package/dist/src/ranger/writer.d.ts +2 -2
  113. package/dist/src/ranger/writer.d.ts.map +1 -1
  114. package/dist/src/status/access.spec.d.ts +2 -0
  115. package/dist/src/status/access.spec.d.ts.map +1 -0
  116. package/dist/src/status/client.d.ts +4 -4
  117. package/dist/src/status/client.d.ts.map +1 -1
  118. package/dist/src/status/payload.d.ts +9 -2
  119. package/dist/src/status/payload.d.ts.map +1 -1
  120. package/dist/src/task/access.spec.d.ts +2 -0
  121. package/dist/src/task/access.spec.d.ts.map +1 -0
  122. package/dist/src/{hardware/task → task}/client.d.ts +26 -15
  123. package/dist/src/task/client.d.ts.map +1 -0
  124. package/dist/src/task/external.d.ts +3 -0
  125. package/dist/src/task/external.d.ts.map +1 -0
  126. package/dist/src/task/index.d.ts.map +1 -0
  127. package/dist/src/{hardware/task → task}/payload.d.ts +45 -6
  128. package/dist/src/task/payload.d.ts.map +1 -0
  129. package/dist/src/task/task.spec.d.ts.map +1 -0
  130. package/dist/src/testutil/access.d.ts +4 -0
  131. package/dist/src/testutil/access.d.ts.map +1 -0
  132. package/dist/src/testutil/client.d.ts +3 -3
  133. package/dist/src/testutil/client.d.ts.map +1 -1
  134. package/dist/src/transport.d.ts.map +1 -1
  135. package/dist/src/user/access.spec.d.ts +2 -0
  136. package/dist/src/user/access.spec.d.ts.map +1 -0
  137. package/dist/src/user/client.d.ts +10 -1
  138. package/dist/src/user/client.d.ts.map +1 -1
  139. package/dist/src/user/external.d.ts +1 -1
  140. package/dist/src/user/external.d.ts.map +1 -1
  141. package/dist/src/user/payload.d.ts.map +1 -1
  142. package/dist/src/workspace/access.spec.d.ts +2 -0
  143. package/dist/src/workspace/access.spec.d.ts.map +1 -0
  144. package/dist/src/workspace/client.d.ts +10 -5
  145. package/dist/src/workspace/client.d.ts.map +1 -1
  146. package/dist/src/workspace/lineplot/access.spec.d.ts +2 -0
  147. package/dist/src/workspace/lineplot/access.spec.d.ts.map +1 -0
  148. package/dist/src/workspace/lineplot/client.d.ts +8 -1
  149. package/dist/src/workspace/lineplot/client.d.ts.map +1 -1
  150. package/dist/src/workspace/log/access.spec.d.ts +2 -0
  151. package/dist/src/workspace/log/access.spec.d.ts.map +1 -0
  152. package/dist/src/workspace/log/client.d.ts +8 -1
  153. package/dist/src/workspace/log/client.d.ts.map +1 -1
  154. package/dist/src/workspace/schematic/access.spec.d.ts +2 -0
  155. package/dist/src/workspace/schematic/access.spec.d.ts.map +1 -0
  156. package/dist/src/workspace/schematic/client.d.ts +8 -1
  157. package/dist/src/workspace/schematic/client.d.ts.map +1 -1
  158. package/dist/src/workspace/schematic/symbol/access.spec.d.ts +2 -0
  159. package/dist/src/workspace/schematic/symbol/access.spec.d.ts.map +1 -0
  160. package/dist/src/workspace/schematic/symbol/client.d.ts +1 -5
  161. package/dist/src/workspace/schematic/symbol/client.d.ts.map +1 -1
  162. package/dist/src/workspace/schematic/symbol/payload.d.ts +2 -2
  163. package/dist/src/workspace/table/access.spec.d.ts +2 -0
  164. package/dist/src/workspace/table/access.spec.d.ts.map +1 -0
  165. package/dist/src/workspace/table/client.d.ts +8 -1
  166. package/dist/src/workspace/table/client.d.ts.map +1 -1
  167. package/eslint.config.ts +3 -1
  168. package/package.json +8 -8
  169. package/src/access/client.ts +5 -2
  170. package/src/access/enforce.spec.ts +189 -0
  171. package/src/access/enforce.ts +84 -0
  172. package/src/access/external.ts +3 -0
  173. package/src/access/payload.ts +1 -13
  174. package/src/access/policy/access.spec.ts +147 -0
  175. package/src/access/policy/client.ts +21 -25
  176. package/src/access/policy/payload.ts +9 -5
  177. package/src/access/role/client.ts +135 -0
  178. package/src/access/role/external.ts +11 -0
  179. package/src/{hardware → access/role}/index.ts +1 -1
  180. package/src/access/role/payload.ts +32 -0
  181. package/src/access/role/role.spec.ts +95 -0
  182. package/src/arc/access.spec.ts +143 -0
  183. package/src/arc/client.ts +7 -31
  184. package/src/arc/payload.ts +4 -0
  185. package/src/auth/auth.spec.ts +13 -13
  186. package/src/auth/auth.ts +33 -11
  187. package/src/channel/access.spec.ts +116 -0
  188. package/src/channel/channel.spec.ts +63 -73
  189. package/src/channel/client.ts +2 -8
  190. package/src/channel/payload.spec.ts +171 -0
  191. package/src/channel/payload.ts +37 -8
  192. package/src/channel/retriever.ts +10 -11
  193. package/src/channel/writer.ts +3 -7
  194. package/src/client.ts +38 -28
  195. package/src/connection/checker.ts +10 -10
  196. package/src/connection/connection.spec.ts +13 -13
  197. package/src/connection.spec.ts +145 -0
  198. package/src/device/access.spec.ts +159 -0
  199. package/src/{hardware/device → device}/client.ts +12 -21
  200. package/src/{hardware/device → device}/device.spec.ts +70 -34
  201. package/src/device/external.ts +11 -0
  202. package/src/{hardware/rack → device}/index.ts +1 -1
  203. package/src/{hardware/device → device}/payload.ts +3 -3
  204. package/src/errors.ts +2 -0
  205. package/src/framer/adapter.spec.ts +351 -13
  206. package/src/framer/adapter.ts +23 -13
  207. package/src/framer/client.spec.ts +14 -20
  208. package/src/framer/client.ts +3 -5
  209. package/src/framer/deleter.spec.ts +1 -1
  210. package/src/framer/frame.spec.ts +427 -0
  211. package/src/framer/frame.ts +30 -3
  212. package/src/framer/iterator.ts +4 -4
  213. package/src/framer/streamer.spec.ts +155 -10
  214. package/src/framer/streamer.ts +35 -12
  215. package/src/framer/writer.spec.ts +5 -5
  216. package/src/index.ts +13 -7
  217. package/src/label/access.spec.ts +109 -0
  218. package/src/label/client.ts +10 -14
  219. package/src/ontology/client.ts +4 -6
  220. package/src/ontology/group/access.spec.ts +77 -0
  221. package/src/ontology/group/client.ts +3 -7
  222. package/src/ontology/group/group.spec.ts +18 -0
  223. package/src/ontology/group/payload.ts +2 -2
  224. package/src/ontology/ontology.spec.ts +2 -0
  225. package/src/ontology/payload.ts +18 -2
  226. package/src/ontology/writer.ts +3 -7
  227. package/src/rack/access.spec.ts +102 -0
  228. package/src/{hardware/rack → rack}/client.ts +14 -19
  229. package/src/{hardware/device/index.ts → rack/external.ts} +2 -1
  230. package/src/{hardware/external.ts → rack/index.ts} +1 -1
  231. package/src/{hardware/rack → rack}/payload.ts +2 -2
  232. package/src/{hardware/rack → rack}/rack.spec.ts +43 -17
  233. package/src/ranger/access.spec.ts +115 -0
  234. package/src/ranger/alias.ts +6 -14
  235. package/src/ranger/client.ts +13 -14
  236. package/src/ranger/kv.ts +7 -9
  237. package/src/ranger/ranger.spec.ts +4 -4
  238. package/src/ranger/writer.ts +3 -7
  239. package/src/status/access.spec.ts +129 -0
  240. package/src/status/client.ts +5 -9
  241. package/src/status/payload.ts +3 -2
  242. package/src/task/access.spec.ts +131 -0
  243. package/src/{hardware/task → task}/client.ts +50 -25
  244. package/src/task/external.ts +11 -0
  245. package/src/{hardware/task → task}/index.ts +1 -1
  246. package/src/{hardware/task → task}/payload.ts +22 -3
  247. package/src/{hardware/task → task}/task.spec.ts +197 -34
  248. package/src/testutil/access.ts +34 -0
  249. package/src/testutil/channels.ts +3 -3
  250. package/src/testutil/client.ts +4 -4
  251. package/src/transport.ts +1 -3
  252. package/src/user/access.spec.ts +107 -0
  253. package/src/user/client.ts +10 -12
  254. package/src/user/external.ts +12 -1
  255. package/src/user/payload.ts +3 -5
  256. package/src/workspace/access.spec.ts +108 -0
  257. package/src/workspace/client.ts +11 -27
  258. package/src/workspace/lineplot/access.spec.ts +134 -0
  259. package/src/workspace/lineplot/client.ts +8 -13
  260. package/src/workspace/log/access.spec.ts +134 -0
  261. package/src/workspace/log/client.ts +8 -13
  262. package/src/workspace/schematic/access.spec.ts +134 -0
  263. package/src/workspace/schematic/client.ts +9 -18
  264. package/src/workspace/schematic/symbol/access.spec.ts +172 -0
  265. package/src/workspace/schematic/symbol/client.ts +6 -17
  266. package/src/workspace/schematic/symbol/payload.ts +1 -1
  267. package/src/workspace/table/access.spec.ts +134 -0
  268. package/src/workspace/table/client.ts +8 -13
  269. package/dist/src/access/policy/policy.spec.d.ts +0 -2
  270. package/dist/src/access/policy/policy.spec.d.ts.map +0 -1
  271. package/dist/src/hardware/client.d.ts +0 -10
  272. package/dist/src/hardware/client.d.ts.map +0 -1
  273. package/dist/src/hardware/device/client.d.ts.map +0 -1
  274. package/dist/src/hardware/device/device.spec.d.ts.map +0 -1
  275. package/dist/src/hardware/device/external.d.ts.map +0 -1
  276. package/dist/src/hardware/device/index.d.ts.map +0 -1
  277. package/dist/src/hardware/device/payload.d.ts.map +0 -1
  278. package/dist/src/hardware/external.d.ts +0 -2
  279. package/dist/src/hardware/external.d.ts.map +0 -1
  280. package/dist/src/hardware/index.d.ts +0 -2
  281. package/dist/src/hardware/index.d.ts.map +0 -1
  282. package/dist/src/hardware/rack/client.d.ts.map +0 -1
  283. package/dist/src/hardware/rack/external.d.ts.map +0 -1
  284. package/dist/src/hardware/rack/index.d.ts.map +0 -1
  285. package/dist/src/hardware/rack/payload.d.ts.map +0 -1
  286. package/dist/src/hardware/rack/rack.spec.d.ts.map +0 -1
  287. package/dist/src/hardware/task/client.d.ts.map +0 -1
  288. package/dist/src/hardware/task/external.d.ts.map +0 -1
  289. package/dist/src/hardware/task/index.d.ts.map +0 -1
  290. package/dist/src/hardware/task/payload.d.ts.map +0 -1
  291. package/dist/src/hardware/task/task.spec.d.ts.map +0 -1
  292. package/dist/src/user/retriever.d.ts +0 -16
  293. package/dist/src/user/retriever.d.ts.map +0 -1
  294. package/dist/src/user/writer.d.ts +0 -11
  295. package/dist/src/user/writer.d.ts.map +0 -1
  296. package/src/access/policy/policy.spec.ts +0 -329
  297. package/src/hardware/client.ts +0 -24
  298. package/src/hardware/device/external.ts +0 -11
  299. package/src/hardware/rack/external.ts +0 -11
  300. package/src/hardware/task/external.ts +0 -11
  301. package/src/user/retriever.ts +0 -41
  302. package/src/user/writer.ts +0 -84
  303. /package/dist/src/{hardware/device → access/role}/external.d.ts +0 -0
  304. /package/dist/src/{hardware/device → device}/device.spec.d.ts +0 -0
  305. /package/dist/src/{hardware/rack → device}/external.d.ts +0 -0
  306. /package/dist/src/{hardware/device → device}/index.d.ts +0 -0
  307. /package/dist/src/{hardware/task → rack}/external.d.ts +0 -0
  308. /package/dist/src/{hardware/rack → rack}/index.d.ts +0 -0
  309. /package/dist/src/{hardware/rack → rack}/rack.spec.d.ts +0 -0
  310. /package/dist/src/{hardware/task → task}/index.d.ts +0 -0
  311. /package/dist/src/{hardware/task → task}/task.spec.d.ts +0 -0
@@ -0,0 +1,145 @@
1
+ // Copyright 2025 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
+ import { z } from "zod";
12
+
13
+ import { checkConnection, newConnectionChecker } from "@/client";
14
+ import { TEST_CLIENT_PARAMS } from "@/testutil/client";
15
+
16
+ describe("checkConnection", () => {
17
+ it("should check connection to the server", async () => {
18
+ const state = await checkConnection({
19
+ host: TEST_CLIENT_PARAMS.host,
20
+ port: TEST_CLIENT_PARAMS.port,
21
+ secure: false,
22
+ });
23
+ expect(state.status).toEqual("connected");
24
+ expect(z.uuid().safeParse(state.clusterKey).success).toBe(true);
25
+ });
26
+
27
+ it("should include client version in the connection check", async () => {
28
+ const state = await checkConnection({
29
+ host: TEST_CLIENT_PARAMS.host,
30
+ port: TEST_CLIENT_PARAMS.port,
31
+ secure: false,
32
+ });
33
+ expect(state.clientVersion).toBeDefined();
34
+ expect(state.clientServerCompatible).toBe(true);
35
+ });
36
+
37
+ it("should support custom name parameter", async () => {
38
+ const state = await checkConnection({
39
+ host: TEST_CLIENT_PARAMS.host,
40
+ port: TEST_CLIENT_PARAMS.port,
41
+ secure: false,
42
+ name: "test-client",
43
+ });
44
+ expect(state.status).toEqual("connected");
45
+ });
46
+
47
+ it("should handle connection failure to invalid host", async () => {
48
+ const state = await checkConnection({
49
+ host: "invalid-host-that-does-not-exist",
50
+ port: 9999,
51
+ secure: false,
52
+ retry: {
53
+ maxRetries: 0, // Disable retries for faster test
54
+ },
55
+ });
56
+ expect(state.status).toEqual("failed");
57
+ });
58
+
59
+ it("should handle connection failure to invalid port", async () => {
60
+ const state = await checkConnection({
61
+ host: TEST_CLIENT_PARAMS.host,
62
+ port: 9999, // Wrong port
63
+ secure: false,
64
+ retry: {
65
+ maxRetries: 0, // Disable retries for faster test
66
+ },
67
+ });
68
+ expect(state.status).toEqual("failed");
69
+ });
70
+ });
71
+
72
+ describe("newConnectionChecker", () => {
73
+ it("should create a connection checker", () => {
74
+ const checker = newConnectionChecker({
75
+ host: TEST_CLIENT_PARAMS.host,
76
+ port: TEST_CLIENT_PARAMS.port,
77
+ secure: false,
78
+ });
79
+ expect(checker).toBeDefined();
80
+ });
81
+
82
+ it("should create a checker that can check connection", async () => {
83
+ const checker = newConnectionChecker({
84
+ host: TEST_CLIENT_PARAMS.host,
85
+ port: TEST_CLIENT_PARAMS.port,
86
+ secure: false,
87
+ });
88
+ const state = await checker.check();
89
+ expect(state.status).toEqual("connected");
90
+ expect(z.uuid().safeParse(state.clusterKey).success).toBe(true);
91
+ });
92
+
93
+ it("should support custom name parameter", async () => {
94
+ const checker = newConnectionChecker({
95
+ host: TEST_CLIENT_PARAMS.host,
96
+ port: TEST_CLIENT_PARAMS.port,
97
+ secure: false,
98
+ name: "custom-checker-name",
99
+ });
100
+ const state = await checker.check();
101
+ expect(state.status).toEqual("connected");
102
+ });
103
+
104
+ it("should support secure connection parameter", () => {
105
+ const checker = newConnectionChecker({
106
+ host: TEST_CLIENT_PARAMS.host,
107
+ port: TEST_CLIENT_PARAMS.port,
108
+ secure: true,
109
+ });
110
+ expect(checker).toBeDefined();
111
+ });
112
+
113
+ it("should create multiple independent checkers", async () => {
114
+ const checker1 = newConnectionChecker({
115
+ host: TEST_CLIENT_PARAMS.host,
116
+ port: TEST_CLIENT_PARAMS.port,
117
+ secure: false,
118
+ name: "checker-1",
119
+ });
120
+ const checker2 = newConnectionChecker({
121
+ host: TEST_CLIENT_PARAMS.host,
122
+ port: TEST_CLIENT_PARAMS.port,
123
+ secure: false,
124
+ name: "checker-2",
125
+ });
126
+
127
+ const state1 = await checker1.check();
128
+ const state2 = await checker2.check();
129
+
130
+ expect(state1.status).toEqual("connected");
131
+ expect(state2.status).toEqual("connected");
132
+ expect(checker1).not.toBe(checker2); // Different instances
133
+ });
134
+
135
+ it("should handle version compatibility checking", async () => {
136
+ const checker = newConnectionChecker({
137
+ host: TEST_CLIENT_PARAMS.host,
138
+ port: TEST_CLIENT_PARAMS.port,
139
+ secure: false,
140
+ });
141
+ const state = await checker.check();
142
+ expect(state.clientVersion).toBeDefined();
143
+ expect(state.clientServerCompatible).toBe(true);
144
+ });
145
+ });
@@ -0,0 +1,159 @@
1
+ // Copyright 2025 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 { id } from "@synnaxlabs/x";
11
+ import { describe, expect, it } from "vitest";
12
+
13
+ import { device } from "@/device";
14
+ import { AuthError, NotFoundError } from "@/errors";
15
+ import { createTestClientWithPolicy } from "@/testutil/access";
16
+ import { createTestClient } from "@/testutil/client";
17
+
18
+ const client = createTestClient();
19
+
20
+ describe("device", () => {
21
+ describe("access control", () => {
22
+ it("should deny access when no retrieve policy exists", async () => {
23
+ const userClient = await createTestClientWithPolicy(client, {
24
+ name: "test",
25
+ objects: [],
26
+ actions: [],
27
+ });
28
+ const rack = await client.racks.create({
29
+ name: "test",
30
+ });
31
+ const randomDevice = await client.devices.create({
32
+ key: id.create(),
33
+ rack: rack.key,
34
+ location: "Dev1",
35
+ name: "test",
36
+ make: "ni",
37
+ model: "test",
38
+ properties: {},
39
+ });
40
+ await expect(
41
+ userClient.devices.retrieve({ key: randomDevice.key }),
42
+ ).rejects.toThrow(AuthError);
43
+ });
44
+
45
+ it("should allow the caller to retrieve devices with the correct policy", async () => {
46
+ const userClient = await createTestClientWithPolicy(client, {
47
+ name: "test",
48
+ objects: [device.ontologyID("")],
49
+ actions: ["retrieve"],
50
+ });
51
+ const rack = await client.racks.create({
52
+ name: "test",
53
+ });
54
+ const randomDevice = await client.devices.create({
55
+ key: id.create(),
56
+ rack: rack.key,
57
+ location: "Dev1",
58
+ name: "test",
59
+ make: "ni",
60
+ model: "test",
61
+ properties: {},
62
+ });
63
+ const retrieved = await userClient.devices.retrieve({
64
+ key: randomDevice.key,
65
+ });
66
+ expect(retrieved.key).toBe(randomDevice.key);
67
+ expect(retrieved.name).toBe(randomDevice.name);
68
+ });
69
+
70
+ it("should allow the caller to create devices with the correct policy", async () => {
71
+ const userClient = await createTestClientWithPolicy(client, {
72
+ name: "test",
73
+ objects: [device.ontologyID("")],
74
+ actions: ["create"],
75
+ });
76
+ const rack = await client.racks.create({
77
+ name: "test",
78
+ });
79
+ await userClient.devices.create({
80
+ key: id.create(),
81
+ rack: rack.key,
82
+ location: "Dev1",
83
+ name: "test",
84
+ make: "ni",
85
+ model: "test",
86
+ properties: {},
87
+ });
88
+ });
89
+
90
+ it("should deny access when no create policy exists", async () => {
91
+ const userClient = await createTestClientWithPolicy(client, {
92
+ name: "test",
93
+ objects: [device.ontologyID("")],
94
+ actions: [],
95
+ });
96
+ const rack = await client.racks.create({
97
+ name: "test",
98
+ });
99
+ await expect(
100
+ userClient.devices.create({
101
+ key: id.create(),
102
+ rack: rack.key,
103
+ location: "Dev1",
104
+ name: "test",
105
+ make: "ni",
106
+ model: "test",
107
+ properties: {},
108
+ }),
109
+ ).rejects.toThrow(AuthError);
110
+ });
111
+
112
+ it("should allow the caller to delete devices with the correct policy", async () => {
113
+ const userClient = await createTestClientWithPolicy(client, {
114
+ name: "test",
115
+ objects: [device.ontologyID("")],
116
+ actions: ["delete", "retrieve"],
117
+ });
118
+ const rack = await client.racks.create({
119
+ name: "test",
120
+ });
121
+ const randomDevice = await client.devices.create({
122
+ key: id.create(),
123
+ rack: rack.key,
124
+ location: "Dev1",
125
+ name: "test",
126
+ make: "ni",
127
+ model: "test",
128
+ properties: {},
129
+ });
130
+ await userClient.devices.delete(randomDevice.key);
131
+ await expect(
132
+ userClient.devices.retrieve({ key: randomDevice.key }),
133
+ ).rejects.toThrow(NotFoundError);
134
+ });
135
+
136
+ it("should deny access when no delete policy exists", async () => {
137
+ const userClient = await createTestClientWithPolicy(client, {
138
+ name: "test",
139
+ objects: [device.ontologyID("")],
140
+ actions: [],
141
+ });
142
+ const rack = await client.racks.create({
143
+ name: "test",
144
+ });
145
+ const randomDevice = await client.devices.create({
146
+ key: id.create(),
147
+ rack: rack.key,
148
+ location: "Dev1",
149
+ name: "test",
150
+ make: "ni",
151
+ model: "test",
152
+ properties: {},
153
+ });
154
+ await expect(userClient.devices.delete(randomDevice.key)).rejects.toThrow(
155
+ AuthError,
156
+ );
157
+ });
158
+ });
159
+ });
@@ -11,25 +11,13 @@ import { sendRequired, type UnaryClient } from "@synnaxlabs/freighter";
11
11
  import { array, type record } from "@synnaxlabs/x";
12
12
  import { z } from "zod";
13
13
 
14
- import {
15
- type Device,
16
- deviceZ,
17
- type Key,
18
- keyZ,
19
- type New,
20
- newZ,
21
- } from "@/hardware/device/payload";
22
- import { keyZ as rackKeyZ } from "@/hardware/rack/payload";
23
- import { type ontology } from "@/ontology";
14
+ import { type Device, deviceZ, type Key, keyZ, type New, newZ } from "@/device/payload";
15
+ import { ontology } from "@/ontology";
16
+ import { keyZ as rackKeyZ } from "@/rack/payload";
24
17
  import { checkForMultipleOrNoResults } from "@/util/retrieve";
25
18
 
26
19
  export const SET_CHANNEL_NAME = "sy_device_set";
27
20
  export const DELETE_CHANNEL_NAME = "sy_device_delete";
28
- export const STATUS_CHANNEL_NAME = "sy_device_status";
29
-
30
- const RETRIEVE_ENDPOINT = "/hardware/device/retrieve";
31
- const CREATE_ENDPOINT = "/hardware/device/create";
32
- const DELETE_ENDPOINT = "/hardware/device/delete";
33
21
 
34
22
  const createReqZ = z.object({ devices: newZ.array() });
35
23
  const createResZ = z.object({ devices: deviceZ.array() });
@@ -45,8 +33,8 @@ const retrieveRequestZ = z.object({
45
33
  locations: z.string().array().optional(),
46
34
  racks: rackKeyZ.array().optional(),
47
35
  searchTerm: z.string().optional(),
48
- limit: z.number().optional(),
49
- offset: z.number().optional(),
36
+ limit: z.int().optional(),
37
+ offset: z.int().optional(),
50
38
  includeStatus: z.boolean().optional(),
51
39
  });
52
40
  const retrieveResZ = z.object({ devices: array.nullableZ(deviceZ) });
@@ -97,7 +85,7 @@ export class Client {
97
85
  const isSingle = typeof args === "object" && "key" in args;
98
86
  const res = await sendRequired(
99
87
  this.client,
100
- RETRIEVE_ENDPOINT,
88
+ "/device/retrieve",
101
89
  args,
102
90
  retrieveArgsZ,
103
91
  retrieveResZ,
@@ -127,7 +115,7 @@ export class Client {
127
115
  const isSingle = !Array.isArray(devices);
128
116
  const res = await sendRequired(
129
117
  this.client,
130
- CREATE_ENDPOINT,
118
+ "/device/create",
131
119
  { devices: array.toArray(devices) },
132
120
  createReqZ,
133
121
  createResZ,
@@ -139,7 +127,7 @@ export class Client {
139
127
  async delete(keys: Key | Key[]): Promise<void> {
140
128
  await sendRequired(
141
129
  this.client,
142
- DELETE_ENDPOINT,
130
+ "/device/delete",
143
131
  { keys: array.toArray(keys) },
144
132
  deleteReqZ,
145
133
  deleteResZ,
@@ -147,4 +135,7 @@ export class Client {
147
135
  }
148
136
  }
149
137
 
150
- export const ontologyID = (key: Key): ontology.ID => ({ type: "device", key });
138
+ export const ontologyID = ontology.createIDFactory<Key>("device");
139
+ export const TYPE_ONTOLOGY_ID = ontologyID("");
140
+
141
+ export const statusKey = (key: Key): string => ontology.idToString(ontologyID(key));
@@ -7,19 +7,20 @@
7
7
  // License, use of this software will be governed by the Apache License, Version 2.0,
8
8
  // included in the file licenses/APL.txt.
9
9
 
10
- import { id, unique } from "@synnaxlabs/x";
10
+ import { id, TimeStamp, unique } from "@synnaxlabs/x";
11
11
  import { beforeAll, describe, expect, it } from "vitest";
12
12
 
13
+ import { type device } from "@/device";
13
14
  import { createTestClient } from "@/testutil/client";
14
15
 
15
16
  const client = createTestClient();
16
17
 
17
18
  describe("Device", async () => {
18
- const testRack = await client.hardware.racks.create({ name: "test" });
19
+ const testRack = await client.racks.create({ name: "test" });
19
20
  describe("create", () => {
20
21
  it("should create a device on a rack", async () => {
21
22
  const key = id.create();
22
- const d = await client.hardware.devices.create({
23
+ const d = await client.devices.create({
23
24
  rack: testRack.key,
24
25
  location: "Dev1",
25
26
  key,
@@ -32,7 +33,40 @@ describe("Device", async () => {
32
33
  expect(d.name).toBe("test");
33
34
  expect(d.make).toBe("ni");
34
35
  });
36
+ it("should create a device with a custom status", async () => {
37
+ const key = id.create();
38
+ const customStatus: device.Status = {
39
+ key: "",
40
+ name: "",
41
+ variant: "success",
42
+ message: "Custom device status",
43
+ description: "Device is connected",
44
+ time: TimeStamp.now(),
45
+ details: { rack: 0, device: "" },
46
+ };
47
+ const d = await client.devices.create({
48
+ rack: testRack.key,
49
+ location: "Dev1",
50
+ key,
51
+ name: "device-with-status",
52
+ make: "ni",
53
+ model: "dog",
54
+ properties: { cat: "dog" },
55
+ status: customStatus,
56
+ });
57
+ expect(d.key).toEqual(key);
58
+ const retrieved = await client.devices.retrieve({
59
+ key: d.key,
60
+ includeStatus: true,
61
+ });
62
+ expect(retrieved.status).toBeDefined();
63
+ expect(retrieved.status?.variant).toBe("success");
64
+ expect(retrieved.status?.message).toBe("Custom device status");
65
+ expect(retrieved.status?.description).toBe("Device is connected");
66
+ expect(retrieved.status?.details?.device).toBe(d.key);
67
+ });
35
68
  });
69
+
36
70
  it("should properly encode and decode properties", async () => {
37
71
  const properties = {
38
72
  rate: 10,
@@ -40,7 +74,7 @@ describe("Device", async () => {
40
74
  inputChannels: { port1: 34214 },
41
75
  outputChannels: [{ port2: 232 }],
42
76
  };
43
- const d = await client.hardware.devices.create({
77
+ const d = await client.devices.create({
44
78
  key: id.create(),
45
79
  rack: testRack.key,
46
80
  location: "Dev1",
@@ -49,13 +83,14 @@ describe("Device", async () => {
49
83
  model: "dog",
50
84
  properties,
51
85
  });
52
- const retrieved = await client.hardware.devices.retrieve({ key: d.key });
86
+ const retrieved = await client.devices.retrieve({ key: d.key });
53
87
  expect(retrieved.key).toEqual(d.key);
54
88
  expect(retrieved.properties).toEqual(properties);
55
89
  });
90
+
56
91
  describe("retrieve", () => {
57
92
  it("should retrieve a device by its key", async () => {
58
- const d = await client.hardware.devices.create({
93
+ const d = await client.devices.create({
59
94
  key: id.create(),
60
95
  rack: testRack.key,
61
96
  location: "Dev1",
@@ -64,13 +99,14 @@ describe("Device", async () => {
64
99
  model: "dog",
65
100
  properties: { cat: "dog" },
66
101
  });
67
- const retrieved = await client.hardware.devices.retrieve({ key: d.key });
102
+ const retrieved = await client.devices.retrieve({ key: d.key });
68
103
  expect(retrieved.key).toBe(d.key);
69
104
  expect(retrieved.name).toBe("test");
70
105
  expect(retrieved.make).toBe("ni");
71
106
  });
107
+
72
108
  it("should retrieve multiple devices by their keys", async () => {
73
- const d1 = await client.hardware.devices.create({
109
+ const d1 = await client.devices.create({
74
110
  key: id.create(),
75
111
  rack: testRack.key,
76
112
  location: "Dev1",
@@ -79,7 +115,7 @@ describe("Device", async () => {
79
115
  model: "dog",
80
116
  properties: { cat: "dog" },
81
117
  });
82
- const d2 = await client.hardware.devices.create({
118
+ const d2 = await client.devices.create({
83
119
  key: id.create(),
84
120
  rack: testRack.key,
85
121
  location: "Dev2",
@@ -88,7 +124,7 @@ describe("Device", async () => {
88
124
  model: "dog",
89
125
  properties: { cat: "dog" },
90
126
  });
91
- const retrieved = await client.hardware.devices.retrieve({
127
+ const retrieved = await client.devices.retrieve({
92
128
  keys: [d1.key, d2.key],
93
129
  });
94
130
  expect(retrieved.length).toBe(2);
@@ -96,9 +132,9 @@ describe("Device", async () => {
96
132
  expect(retrieved[1].key).toBe(d2.key);
97
133
  });
98
134
 
99
- describe("state", () => {
100
- it("should not include state by default", async () => {
101
- const d = await client.hardware.devices.create({
135
+ describe("status", () => {
136
+ it("should not include status by default", async () => {
137
+ const d = await client.devices.create({
102
138
  key: id.create(),
103
139
  rack: testRack.key,
104
140
  location: "Dev1",
@@ -108,12 +144,12 @@ describe("Device", async () => {
108
144
  properties: { cat: "dog" },
109
145
  });
110
146
 
111
- const retrieved = await client.hardware.devices.retrieve({ key: d.key });
147
+ const retrieved = await client.devices.retrieve({ key: d.key });
112
148
  expect(retrieved.status).toBeUndefined();
113
149
  });
114
150
 
115
151
  it("should include status when includeStatus is true", async () => {
116
- const d = await client.hardware.devices.create({
152
+ const d = await client.devices.create({
117
153
  key: id.create(),
118
154
  rack: testRack.key,
119
155
  location: "Dev1",
@@ -125,7 +161,7 @@ describe("Device", async () => {
125
161
 
126
162
  await expect
127
163
  .poll(async () => {
128
- const { status } = await client.hardware.devices.retrieve({
164
+ const { status } = await client.devices.retrieve({
129
165
  key: d.key,
130
166
  includeStatus: true,
131
167
  });
@@ -134,8 +170,8 @@ describe("Device", async () => {
134
170
  .toBe(true);
135
171
  });
136
172
 
137
- it("should include state for multiple devices", async () => {
138
- const d1 = await client.hardware.devices.create({
173
+ it("should include status for multiple devices", async () => {
174
+ const d1 = await client.devices.create({
139
175
  key: id.create(),
140
176
  rack: testRack.key,
141
177
  location: "Dev1",
@@ -145,7 +181,7 @@ describe("Device", async () => {
145
181
  properties: { cat: "dog" },
146
182
  });
147
183
 
148
- const d2 = await client.hardware.devices.create({
184
+ const d2 = await client.devices.create({
149
185
  key: id.create(),
150
186
  rack: testRack.key,
151
187
  location: "Dev2",
@@ -157,7 +193,7 @@ describe("Device", async () => {
157
193
 
158
194
  await expect
159
195
  .poll(async () => {
160
- const retrievedDevices = await client.hardware.devices.retrieve({
196
+ const retrievedDevices = await client.devices.retrieve({
161
197
  keys: [d1.key, d2.key],
162
198
  includeStatus: true,
163
199
  });
@@ -169,7 +205,7 @@ describe("Device", async () => {
169
205
 
170
206
  it("should handle state with type-safe details", async () => {
171
207
  const key = id.create();
172
- await client.hardware.devices.create({
208
+ await client.devices.create({
173
209
  key,
174
210
  rack: testRack.key,
175
211
  location: "Dev1",
@@ -181,13 +217,13 @@ describe("Device", async () => {
181
217
 
182
218
  await expect
183
219
  .poll(async () => {
184
- const retrieved = await client.hardware.devices.retrieve({
220
+ const retrieved = await client.devices.retrieve({
185
221
  key,
186
222
  includeStatus: true,
187
223
  });
188
224
  return (
189
225
  retrieved.status !== undefined &&
190
- retrieved.status.variant === "info" &&
226
+ retrieved.status.variant === "warning" &&
191
227
  retrieved.status.details.device === key
192
228
  );
193
229
  })
@@ -215,7 +251,7 @@ describe("Device", async () => {
215
251
 
216
252
  for (const config of deviceConfigs) {
217
253
  const key = id.create();
218
- await client.hardware.devices.create({
254
+ await client.devices.create({
219
255
  key,
220
256
  rack: testRack.key,
221
257
  location: config.location,
@@ -229,7 +265,7 @@ describe("Device", async () => {
229
265
  });
230
266
 
231
267
  it("should retrieve devices by names", async () => {
232
- const result = await client.hardware.devices.retrieve({
268
+ const result = await client.devices.retrieve({
233
269
  names: ["sensor1", "actuator1"],
234
270
  });
235
271
  expect(result.length).toBeGreaterThanOrEqual(2);
@@ -240,7 +276,7 @@ describe("Device", async () => {
240
276
  });
241
277
 
242
278
  it("should retrieve devices by makes", async () => {
243
- const result = await client.hardware.devices.retrieve({
279
+ const result = await client.devices.retrieve({
244
280
  makes: ["ni"],
245
281
  });
246
282
  expect(result.length).toBeGreaterThanOrEqual(2);
@@ -248,7 +284,7 @@ describe("Device", async () => {
248
284
  });
249
285
 
250
286
  it("should retrieve devices by models", async () => {
251
- const result = await client.hardware.devices.retrieve({
287
+ const result = await client.devices.retrieve({
252
288
  models: ["pxi-6281", "t7"],
253
289
  });
254
290
  expect(result.length).toBeGreaterThanOrEqual(2);
@@ -259,7 +295,7 @@ describe("Device", async () => {
259
295
  });
260
296
 
261
297
  it("should retrieve devices by locations", async () => {
262
- const result = await client.hardware.devices.retrieve({
298
+ const result = await client.devices.retrieve({
263
299
  locations: ["Lab1"],
264
300
  });
265
301
  expect(result.length).toBeGreaterThanOrEqual(2);
@@ -267,7 +303,7 @@ describe("Device", async () => {
267
303
  });
268
304
 
269
305
  it("should retrieve devices by racks", async () => {
270
- const result = await client.hardware.devices.retrieve({
306
+ const result = await client.devices.retrieve({
271
307
  racks: [testRack.key],
272
308
  });
273
309
  expect(result.length).toBeGreaterThanOrEqual(5);
@@ -275,7 +311,7 @@ describe("Device", async () => {
275
311
  });
276
312
 
277
313
  it("should retrieve devices by search term", async () => {
278
- const result = await client.hardware.devices.retrieve({
314
+ const result = await client.devices.retrieve({
279
315
  searchTerm: "sensor1",
280
316
  });
281
317
  expect(result.length).toBeGreaterThanOrEqual(2);
@@ -283,14 +319,14 @@ describe("Device", async () => {
283
319
  });
284
320
 
285
321
  it("should support pagination with limit and offset", async () => {
286
- const firstPage = await client.hardware.devices.retrieve({
322
+ const firstPage = await client.devices.retrieve({
287
323
  racks: [testRack.key],
288
324
  limit: 2,
289
325
  offset: 0,
290
326
  });
291
327
  expect(firstPage).toHaveLength(2);
292
328
 
293
- const secondPage = await client.hardware.devices.retrieve({
329
+ const secondPage = await client.devices.retrieve({
294
330
  racks: [testRack.key],
295
331
  limit: 2,
296
332
  offset: 2,
@@ -303,7 +339,7 @@ describe("Device", async () => {
303
339
  });
304
340
 
305
341
  it("should support combined filters", async () => {
306
- const result = await client.hardware.devices.retrieve({
342
+ const result = await client.devices.retrieve({
307
343
  makes: ["ni"],
308
344
  locations: ["Lab1", "Lab2"],
309
345
  includeStatus: true,
@@ -315,7 +351,7 @@ describe("Device", async () => {
315
351
 
316
352
  await expect
317
353
  .poll(async () => {
318
- const devices = await client.hardware.devices.retrieve({
354
+ const devices = await client.devices.retrieve({
319
355
  makes: ["ni"],
320
356
  locations: ["Lab1", "Lab2"],
321
357
  includeStatus: true,
@@ -0,0 +1,11 @@
1
+ // Copyright 2025 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 "@/device/client";
11
+ export * from "@/device/payload";