@synnaxlabs/client 0.48.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 (294) hide show
  1. package/.turbo/turbo-build.log +6 -6
  2. package/dist/client.cjs +31 -31
  3. package/dist/client.js +4836 -4644
  4. package/dist/src/access/client.d.ts +3 -1
  5. package/dist/src/access/client.d.ts.map +1 -1
  6. package/dist/src/access/enforce.d.ts +35 -0
  7. package/dist/src/access/enforce.d.ts.map +1 -0
  8. package/dist/src/access/enforce.spec.d.ts +2 -0
  9. package/dist/src/access/enforce.spec.d.ts.map +1 -0
  10. package/dist/src/access/external.d.ts +3 -0
  11. package/dist/src/access/external.d.ts.map +1 -1
  12. package/dist/src/access/payload.d.ts +0 -6
  13. package/dist/src/access/payload.d.ts.map +1 -1
  14. package/dist/src/access/policy/access.spec.d.ts +2 -0
  15. package/dist/src/access/policy/access.spec.d.ts.map +1 -0
  16. package/dist/src/access/policy/client.d.ts +485 -31
  17. package/dist/src/access/policy/client.d.ts.map +1 -1
  18. package/dist/src/access/policy/payload.d.ts +36 -113
  19. package/dist/src/access/policy/payload.d.ts.map +1 -1
  20. package/dist/src/access/role/client.d.ts +135 -0
  21. package/dist/src/access/role/client.d.ts.map +1 -0
  22. package/dist/src/access/role/external.d.ts.map +1 -0
  23. package/dist/src/access/role/index.d.ts +2 -0
  24. package/dist/src/access/role/index.d.ts.map +1 -0
  25. package/dist/src/access/role/payload.d.ts +27 -0
  26. package/dist/src/access/role/payload.d.ts.map +1 -0
  27. package/dist/src/access/role/role.spec.d.ts +2 -0
  28. package/dist/src/access/role/role.spec.d.ts.map +1 -0
  29. package/dist/src/arc/access.spec.d.ts +2 -0
  30. package/dist/src/arc/access.spec.d.ts.map +1 -0
  31. package/dist/src/arc/client.d.ts +5 -14
  32. package/dist/src/arc/client.d.ts.map +1 -1
  33. package/dist/src/arc/payload.d.ts +11 -2
  34. package/dist/src/arc/payload.d.ts.map +1 -1
  35. package/dist/src/auth/auth.d.ts +5 -3
  36. package/dist/src/auth/auth.d.ts.map +1 -1
  37. package/dist/src/channel/access.spec.d.ts +2 -0
  38. package/dist/src/channel/access.spec.d.ts.map +1 -0
  39. package/dist/src/channel/client.d.ts +0 -1
  40. package/dist/src/channel/client.d.ts.map +1 -1
  41. package/dist/src/channel/payload.d.ts +18 -8
  42. package/dist/src/channel/payload.d.ts.map +1 -1
  43. package/dist/src/channel/payload.spec.d.ts +2 -0
  44. package/dist/src/channel/payload.spec.d.ts.map +1 -0
  45. package/dist/src/channel/retriever.d.ts +4 -6
  46. package/dist/src/channel/retriever.d.ts.map +1 -1
  47. package/dist/src/channel/writer.d.ts.map +1 -1
  48. package/dist/src/client.d.ts +9 -5
  49. package/dist/src/client.d.ts.map +1 -1
  50. package/dist/src/device/access.spec.d.ts +2 -0
  51. package/dist/src/device/access.spec.d.ts.map +1 -0
  52. package/dist/src/{hardware/device → device}/client.d.ts +14 -7
  53. package/dist/src/device/client.d.ts.map +1 -0
  54. package/dist/src/device/device.spec.d.ts.map +1 -0
  55. package/dist/src/device/external.d.ts.map +1 -0
  56. package/dist/src/device/index.d.ts.map +1 -0
  57. package/dist/src/{hardware/device → device}/payload.d.ts +1 -1
  58. package/dist/src/device/payload.d.ts.map +1 -0
  59. package/dist/src/errors.d.ts +3 -0
  60. package/dist/src/errors.d.ts.map +1 -1
  61. package/dist/src/framer/client.d.ts +8 -1
  62. package/dist/src/framer/client.d.ts.map +1 -1
  63. package/dist/src/framer/frame.d.ts +10 -5
  64. package/dist/src/framer/frame.d.ts.map +1 -1
  65. package/dist/src/framer/iterator.d.ts +3 -3
  66. package/dist/src/framer/streamer.d.ts +24 -21
  67. package/dist/src/framer/streamer.d.ts.map +1 -1
  68. package/dist/src/framer/writer.d.ts +13 -13
  69. package/dist/src/index.d.ts +4 -5
  70. package/dist/src/index.d.ts.map +1 -1
  71. package/dist/src/label/access.spec.d.ts +2 -0
  72. package/dist/src/label/access.spec.d.ts.map +1 -0
  73. package/dist/src/label/client.d.ts +20 -11
  74. package/dist/src/label/client.d.ts.map +1 -1
  75. package/dist/src/ontology/client.d.ts +6 -6
  76. package/dist/src/ontology/client.d.ts.map +1 -1
  77. package/dist/src/ontology/group/access.spec.d.ts +2 -0
  78. package/dist/src/ontology/group/access.spec.d.ts.map +1 -0
  79. package/dist/src/ontology/group/client.d.ts +2 -2
  80. package/dist/src/ontology/group/client.d.ts.map +1 -1
  81. package/dist/src/ontology/group/payload.d.ts +1 -2
  82. package/dist/src/ontology/group/payload.d.ts.map +1 -1
  83. package/dist/src/ontology/payload.d.ts +23 -17
  84. package/dist/src/ontology/payload.d.ts.map +1 -1
  85. package/dist/src/ontology/writer.d.ts +10 -10
  86. package/dist/src/ontology/writer.d.ts.map +1 -1
  87. package/dist/src/rack/access.spec.d.ts +2 -0
  88. package/dist/src/rack/access.spec.d.ts.map +1 -0
  89. package/dist/src/{hardware/rack → rack}/client.d.ts +15 -8
  90. package/dist/src/rack/client.d.ts.map +1 -0
  91. package/dist/src/rack/external.d.ts.map +1 -0
  92. package/dist/src/rack/index.d.ts.map +1 -0
  93. package/dist/src/{hardware/rack → rack}/payload.d.ts +1 -1
  94. package/dist/src/rack/payload.d.ts.map +1 -0
  95. package/dist/src/rack/rack.spec.d.ts.map +1 -0
  96. package/dist/src/ranger/access.spec.d.ts +2 -0
  97. package/dist/src/ranger/access.spec.d.ts.map +1 -0
  98. package/dist/src/ranger/alias.d.ts +1 -8
  99. package/dist/src/ranger/alias.d.ts.map +1 -1
  100. package/dist/src/ranger/client.d.ts +12 -5
  101. package/dist/src/ranger/client.d.ts.map +1 -1
  102. package/dist/src/ranger/kv.d.ts +0 -3
  103. package/dist/src/ranger/kv.d.ts.map +1 -1
  104. package/dist/src/ranger/writer.d.ts +2 -2
  105. package/dist/src/ranger/writer.d.ts.map +1 -1
  106. package/dist/src/status/access.spec.d.ts +2 -0
  107. package/dist/src/status/access.spec.d.ts.map +1 -0
  108. package/dist/src/status/client.d.ts +4 -4
  109. package/dist/src/status/client.d.ts.map +1 -1
  110. package/dist/src/status/payload.d.ts +9 -2
  111. package/dist/src/status/payload.d.ts.map +1 -1
  112. package/dist/src/task/access.spec.d.ts +2 -0
  113. package/dist/src/task/access.spec.d.ts.map +1 -0
  114. package/dist/src/{hardware/task → task}/client.d.ts +26 -15
  115. package/dist/src/task/client.d.ts.map +1 -0
  116. package/dist/src/task/external.d.ts +3 -0
  117. package/dist/src/task/external.d.ts.map +1 -0
  118. package/dist/src/task/index.d.ts.map +1 -0
  119. package/dist/src/{hardware/task → task}/payload.d.ts +45 -6
  120. package/dist/src/task/payload.d.ts.map +1 -0
  121. package/dist/src/task/task.spec.d.ts.map +1 -0
  122. package/dist/src/testutil/access.d.ts +4 -0
  123. package/dist/src/testutil/access.d.ts.map +1 -0
  124. package/dist/src/transport.d.ts.map +1 -1
  125. package/dist/src/user/access.spec.d.ts +2 -0
  126. package/dist/src/user/access.spec.d.ts.map +1 -0
  127. package/dist/src/user/client.d.ts +10 -1
  128. package/dist/src/user/client.d.ts.map +1 -1
  129. package/dist/src/user/external.d.ts +1 -1
  130. package/dist/src/user/external.d.ts.map +1 -1
  131. package/dist/src/user/payload.d.ts.map +1 -1
  132. package/dist/src/workspace/access.spec.d.ts +2 -0
  133. package/dist/src/workspace/access.spec.d.ts.map +1 -0
  134. package/dist/src/workspace/client.d.ts +10 -5
  135. package/dist/src/workspace/client.d.ts.map +1 -1
  136. package/dist/src/workspace/lineplot/access.spec.d.ts +2 -0
  137. package/dist/src/workspace/lineplot/access.spec.d.ts.map +1 -0
  138. package/dist/src/workspace/lineplot/client.d.ts +8 -1
  139. package/dist/src/workspace/lineplot/client.d.ts.map +1 -1
  140. package/dist/src/workspace/log/access.spec.d.ts +2 -0
  141. package/dist/src/workspace/log/access.spec.d.ts.map +1 -0
  142. package/dist/src/workspace/log/client.d.ts +8 -1
  143. package/dist/src/workspace/log/client.d.ts.map +1 -1
  144. package/dist/src/workspace/schematic/access.spec.d.ts +2 -0
  145. package/dist/src/workspace/schematic/access.spec.d.ts.map +1 -0
  146. package/dist/src/workspace/schematic/client.d.ts +8 -1
  147. package/dist/src/workspace/schematic/client.d.ts.map +1 -1
  148. package/dist/src/workspace/schematic/symbol/access.spec.d.ts +2 -0
  149. package/dist/src/workspace/schematic/symbol/access.spec.d.ts.map +1 -0
  150. package/dist/src/workspace/schematic/symbol/client.d.ts +1 -5
  151. package/dist/src/workspace/schematic/symbol/client.d.ts.map +1 -1
  152. package/dist/src/workspace/schematic/symbol/payload.d.ts +2 -2
  153. package/dist/src/workspace/table/access.spec.d.ts +2 -0
  154. package/dist/src/workspace/table/access.spec.d.ts.map +1 -0
  155. package/dist/src/workspace/table/client.d.ts +8 -1
  156. package/dist/src/workspace/table/client.d.ts.map +1 -1
  157. package/package.json +3 -3
  158. package/src/access/client.ts +5 -2
  159. package/src/access/enforce.spec.ts +189 -0
  160. package/src/access/enforce.ts +84 -0
  161. package/src/access/external.ts +3 -0
  162. package/src/access/payload.ts +1 -13
  163. package/src/access/policy/access.spec.ts +147 -0
  164. package/src/access/policy/client.ts +21 -25
  165. package/src/access/policy/payload.ts +9 -5
  166. package/src/access/role/client.ts +135 -0
  167. package/src/access/role/external.ts +11 -0
  168. package/src/{hardware → access/role}/index.ts +1 -1
  169. package/src/access/role/payload.ts +32 -0
  170. package/src/access/role/role.spec.ts +95 -0
  171. package/src/arc/access.spec.ts +143 -0
  172. package/src/arc/client.ts +7 -31
  173. package/src/arc/payload.ts +4 -0
  174. package/src/auth/auth.ts +33 -11
  175. package/src/channel/access.spec.ts +116 -0
  176. package/src/channel/channel.spec.ts +63 -73
  177. package/src/channel/client.ts +2 -8
  178. package/src/channel/payload.spec.ts +171 -0
  179. package/src/channel/payload.ts +35 -7
  180. package/src/channel/retriever.ts +10 -11
  181. package/src/channel/writer.ts +3 -7
  182. package/src/client.ts +14 -18
  183. package/src/device/access.spec.ts +159 -0
  184. package/src/{hardware/device → device}/client.ts +12 -21
  185. package/src/{hardware/device → device}/device.spec.ts +70 -34
  186. package/src/device/external.ts +11 -0
  187. package/src/{hardware/rack → device}/index.ts +1 -1
  188. package/src/{hardware/device → device}/payload.ts +3 -3
  189. package/src/errors.ts +2 -0
  190. package/src/framer/adapter.spec.ts +14 -14
  191. package/src/framer/client.spec.ts +14 -20
  192. package/src/framer/client.ts +3 -5
  193. package/src/framer/deleter.spec.ts +1 -1
  194. package/src/framer/frame.spec.ts +131 -0
  195. package/src/framer/frame.ts +10 -2
  196. package/src/framer/iterator.ts +3 -3
  197. package/src/framer/streamer.spec.ts +100 -12
  198. package/src/framer/streamer.ts +29 -9
  199. package/src/framer/writer.spec.ts +5 -5
  200. package/src/index.ts +4 -5
  201. package/src/label/access.spec.ts +109 -0
  202. package/src/label/client.ts +10 -14
  203. package/src/ontology/client.ts +4 -6
  204. package/src/ontology/group/access.spec.ts +77 -0
  205. package/src/ontology/group/client.ts +3 -7
  206. package/src/ontology/group/group.spec.ts +18 -0
  207. package/src/ontology/group/payload.ts +2 -2
  208. package/src/ontology/ontology.spec.ts +2 -0
  209. package/src/ontology/payload.ts +18 -2
  210. package/src/ontology/writer.ts +3 -7
  211. package/src/rack/access.spec.ts +102 -0
  212. package/src/{hardware/rack → rack}/client.ts +14 -19
  213. package/src/{hardware/device/index.ts → rack/external.ts} +2 -1
  214. package/src/{hardware/external.ts → rack/index.ts} +1 -1
  215. package/src/{hardware/rack → rack}/payload.ts +2 -2
  216. package/src/{hardware/rack → rack}/rack.spec.ts +43 -17
  217. package/src/ranger/access.spec.ts +115 -0
  218. package/src/ranger/alias.ts +6 -14
  219. package/src/ranger/client.ts +13 -14
  220. package/src/ranger/kv.ts +7 -9
  221. package/src/ranger/ranger.spec.ts +4 -4
  222. package/src/ranger/writer.ts +3 -7
  223. package/src/status/access.spec.ts +129 -0
  224. package/src/status/client.ts +5 -9
  225. package/src/status/payload.ts +3 -2
  226. package/src/task/access.spec.ts +131 -0
  227. package/src/{hardware/task → task}/client.ts +50 -25
  228. package/src/task/external.ts +11 -0
  229. package/src/{hardware/task → task}/index.ts +1 -1
  230. package/src/{hardware/task → task}/payload.ts +22 -3
  231. package/src/{hardware/task → task}/task.spec.ts +197 -34
  232. package/src/testutil/access.ts +34 -0
  233. package/src/testutil/channels.ts +3 -3
  234. package/src/transport.ts +1 -3
  235. package/src/user/access.spec.ts +107 -0
  236. package/src/user/client.ts +10 -12
  237. package/src/user/external.ts +12 -1
  238. package/src/user/payload.ts +3 -5
  239. package/src/workspace/access.spec.ts +108 -0
  240. package/src/workspace/client.ts +11 -27
  241. package/src/workspace/lineplot/access.spec.ts +134 -0
  242. package/src/workspace/lineplot/client.ts +8 -13
  243. package/src/workspace/log/access.spec.ts +134 -0
  244. package/src/workspace/log/client.ts +8 -13
  245. package/src/workspace/schematic/access.spec.ts +134 -0
  246. package/src/workspace/schematic/client.ts +9 -18
  247. package/src/workspace/schematic/symbol/access.spec.ts +172 -0
  248. package/src/workspace/schematic/symbol/client.ts +6 -17
  249. package/src/workspace/schematic/symbol/payload.ts +1 -1
  250. package/src/workspace/table/access.spec.ts +134 -0
  251. package/src/workspace/table/client.ts +8 -13
  252. package/dist/src/access/policy/policy.spec.d.ts +0 -2
  253. package/dist/src/access/policy/policy.spec.d.ts.map +0 -1
  254. package/dist/src/hardware/client.d.ts +0 -10
  255. package/dist/src/hardware/client.d.ts.map +0 -1
  256. package/dist/src/hardware/device/client.d.ts.map +0 -1
  257. package/dist/src/hardware/device/device.spec.d.ts.map +0 -1
  258. package/dist/src/hardware/device/external.d.ts.map +0 -1
  259. package/dist/src/hardware/device/index.d.ts.map +0 -1
  260. package/dist/src/hardware/device/payload.d.ts.map +0 -1
  261. package/dist/src/hardware/external.d.ts +0 -2
  262. package/dist/src/hardware/external.d.ts.map +0 -1
  263. package/dist/src/hardware/index.d.ts +0 -2
  264. package/dist/src/hardware/index.d.ts.map +0 -1
  265. package/dist/src/hardware/rack/client.d.ts.map +0 -1
  266. package/dist/src/hardware/rack/external.d.ts.map +0 -1
  267. package/dist/src/hardware/rack/index.d.ts.map +0 -1
  268. package/dist/src/hardware/rack/payload.d.ts.map +0 -1
  269. package/dist/src/hardware/rack/rack.spec.d.ts.map +0 -1
  270. package/dist/src/hardware/task/client.d.ts.map +0 -1
  271. package/dist/src/hardware/task/external.d.ts.map +0 -1
  272. package/dist/src/hardware/task/index.d.ts.map +0 -1
  273. package/dist/src/hardware/task/payload.d.ts.map +0 -1
  274. package/dist/src/hardware/task/task.spec.d.ts.map +0 -1
  275. package/dist/src/user/retriever.d.ts +0 -16
  276. package/dist/src/user/retriever.d.ts.map +0 -1
  277. package/dist/src/user/writer.d.ts +0 -11
  278. package/dist/src/user/writer.d.ts.map +0 -1
  279. package/src/access/policy/policy.spec.ts +0 -329
  280. package/src/hardware/client.ts +0 -24
  281. package/src/hardware/device/external.ts +0 -11
  282. package/src/hardware/rack/external.ts +0 -11
  283. package/src/hardware/task/external.ts +0 -11
  284. package/src/user/retriever.ts +0 -41
  285. package/src/user/writer.ts +0 -84
  286. /package/dist/src/{hardware/device → access/role}/external.d.ts +0 -0
  287. /package/dist/src/{hardware/device → device}/device.spec.d.ts +0 -0
  288. /package/dist/src/{hardware/rack → device}/external.d.ts +0 -0
  289. /package/dist/src/{hardware/device → device}/index.d.ts +0 -0
  290. /package/dist/src/{hardware/task → rack}/external.d.ts +0 -0
  291. /package/dist/src/{hardware/rack → rack}/index.d.ts +0 -0
  292. /package/dist/src/{hardware/rack → rack}/rack.spec.d.ts +0 -0
  293. /package/dist/src/{hardware/task → task}/index.d.ts +0 -0
  294. /package/dist/src/{hardware/task → task}/task.spec.d.ts +0 -0
@@ -18,6 +18,8 @@ import {
18
18
  } from "@synnaxlabs/x";
19
19
  import { z } from "zod";
20
20
 
21
+ import { ontology } from "@/ontology";
22
+
21
23
  const errorMessage = "Channel key must be a valid uint32.";
22
24
  export const keyZ = z.uint32().or(
23
25
  z
@@ -28,7 +30,14 @@ export const keyZ = z.uint32().or(
28
30
  );
29
31
  export type Key = z.infer<typeof keyZ>;
30
32
  export type Keys = Key[];
31
- export const nameZ = z.string();
33
+ const VALID_NAME_PATTERN = /^[a-zA-Z_][a-zA-Z0-9_]*$/;
34
+ export const nameZ = z
35
+ .string()
36
+ .min(1, "Name must not be empty")
37
+ .regex(
38
+ VALID_NAME_PATTERN,
39
+ "Name can only contain letters, digits, and underscores, and cannot start with a digit",
40
+ );
32
41
  export type Name = z.infer<typeof nameZ>;
33
42
  export type Names = Name[];
34
43
  export type KeyOrName = Key | Name;
@@ -50,10 +59,10 @@ export type Operation = z.infer<typeof operationZ>;
50
59
  export const statusZ = status.statusZ();
51
60
  export type Status = z.infer<typeof statusZ>;
52
61
  export const payloadZ = z.object({
53
- name: nameZ,
62
+ name: z.string(),
54
63
  key: keyZ,
55
64
  dataType: DataType.z,
56
- leaseholder: z.number(),
65
+ leaseholder: zod.uint12,
57
66
  index: keyZ,
58
67
  isIndex: z.boolean(),
59
68
  internal: z.boolean(),
@@ -68,12 +77,13 @@ export interface Payload extends z.infer<typeof payloadZ> {}
68
77
 
69
78
  export const newZ = payloadZ.extend({
70
79
  key: keyZ.optional(),
71
- leaseholder: z.number().optional(),
80
+ name: nameZ,
81
+ leaseholder: zod.uint12.optional(),
72
82
  index: keyZ.optional(),
73
83
  isIndex: z.boolean().optional(),
74
- internal: z.boolean().optional().default(false),
75
- virtual: z.boolean().optional().default(false),
76
- expression: z.string().optional().default(""),
84
+ internal: z.boolean().default(false),
85
+ virtual: z.boolean().default(false),
86
+ expression: z.string().default(""),
77
87
  operations: array.nullableZ(operationZ).optional(),
78
88
  requires: array.nullableZ(keyZ).optional(),
79
89
  });
@@ -89,3 +99,21 @@ export const paramsZ = z.union([
89
99
  zod.toArray(payloadZ).transform((p) => p.map((c) => c.key)),
90
100
  ]);
91
101
  export type Params = Key | Name | Keys | Names | Payload | Payload[];
102
+
103
+ export const ontologyID = ontology.createIDFactory<Key>("channel");
104
+ export const TYPE_ONTOLOGY_ID = ontologyID(0);
105
+
106
+ const CHAR_REGEX = /[a-zA-Z0-9_]/;
107
+
108
+ export const escapeInvalidName = (name: string, changeEmptyToUnderscore = false) => {
109
+ if (name === "") return changeEmptyToUnderscore ? "_" : "";
110
+ if (name.match(VALID_NAME_PATTERN)) return name;
111
+ // if it doesn't match, convert non-alphanumeric characters to underscores and prepend
112
+ // an underscore if the first character is a digit
113
+ let result = "";
114
+ for (const char of name)
115
+ if (char.match(CHAR_REGEX)) result += char;
116
+ else result += "_";
117
+ if (result[0].match(/^\d/)) result = `_${result}`;
118
+ return result;
119
+ };
@@ -7,8 +7,8 @@
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 { type UnaryClient } from "@synnaxlabs/freighter";
11
- import { array, DataType, debounce } from "@synnaxlabs/x";
10
+ import { sendRequired, type UnaryClient } from "@synnaxlabs/freighter";
11
+ import { array, DataType, debounce, zod } from "@synnaxlabs/x";
12
12
  import { Mutex } from "async-mutex";
13
13
  import { z } from "zod";
14
14
 
@@ -25,25 +25,25 @@ import {
25
25
  payloadZ,
26
26
  } from "@/channel/payload";
27
27
  import { QueryError } from "@/errors";
28
+ import { keyZ as rangeKeyZ } from "@/ranger/payload";
28
29
  import {
29
30
  analyzeParams as analyzeParameters,
30
31
  type ParamAnalysisResult,
31
32
  } from "@/util/retrieve";
32
33
 
33
34
  const reqZ = z.object({
34
- leaseholder: z.number().optional(),
35
+ nodeKey: zod.uint12.optional(),
35
36
  keys: keyZ.array().optional(),
36
37
  names: z.string().array().optional(),
37
38
  searchTerm: z.string().optional(),
38
- rangeKey: z.string().optional(),
39
- limit: z.number().optional(),
40
- offset: z.number().optional(),
39
+ rangeKey: rangeKeyZ.optional(),
40
+ limit: z.int().optional(),
41
+ offset: z.int().optional(),
41
42
  dataTypes: DataType.z.array().optional(),
42
43
  notDataTypes: DataType.z.array().optional(),
43
44
  virtual: z.boolean().optional(),
44
45
  isIndex: z.boolean().optional(),
45
46
  internal: z.boolean().optional(),
46
- calculated: z.boolean().optional(),
47
47
  legacyCalculated: z.boolean().optional(),
48
48
  });
49
49
  export interface RetrieveRequest extends z.input<typeof reqZ> {}
@@ -72,7 +72,6 @@ export interface Retriever {
72
72
  }
73
73
 
74
74
  export class ClusterRetriever implements Retriever {
75
- private static readonly ENDPOINT = "/channel/retrieve";
76
75
  private readonly client: UnaryClient;
77
76
 
78
77
  constructor(client: UnaryClient) {
@@ -95,13 +94,13 @@ export class ClusterRetriever implements Retriever {
95
94
  }
96
95
 
97
96
  private async execute(request: RetrieveRequest): Promise<Payload[]> {
98
- const [res, err] = await this.client.send(
99
- ClusterRetriever.ENDPOINT,
97
+ const res = await sendRequired(
98
+ this.client,
99
+ "/channel/retrieve",
100
100
  request,
101
101
  reqZ,
102
102
  resZ,
103
103
  );
104
- if (err != null) throw err;
105
104
  return res.channels;
106
105
  }
107
106
  }
@@ -34,10 +34,6 @@ const deleteResZ = z.object({});
34
34
  const renameReqZ = z.object({ keys: keyZ.array(), names: nameZ.array() });
35
35
  const renameResZ = z.object({});
36
36
 
37
- const CREATE_ENDPOINT = "/channel/create";
38
- const DELETE_ENDPOINT = "/channel/delete";
39
- const RENAME_ENDPOINT = "/channel/rename";
40
-
41
37
  export interface DeleteProps extends z.input<typeof deleteReqZ> {}
42
38
  export interface RenameProps extends z.input<typeof renameReqZ> {}
43
39
 
@@ -56,7 +52,7 @@ export class Writer {
56
52
  typeof createResZ
57
53
  >(
58
54
  this.client,
59
- CREATE_ENDPOINT,
55
+ "/channel/create",
60
56
  {
61
57
  channels: channels.map((c) => ({
62
58
  ...c,
@@ -74,7 +70,7 @@ export class Writer {
74
70
  const keys = keyZ.array().parse(props.keys ?? []);
75
71
  await sendRequired<typeof deleteReqZ, typeof deleteResZ>(
76
72
  this.client,
77
- DELETE_ENDPOINT,
73
+ "/channel/delete",
78
74
  props,
79
75
  deleteReqZ,
80
76
  deleteResZ,
@@ -86,7 +82,7 @@ export class Writer {
86
82
  async rename(keys: Key[], names: string[]): Promise<void> {
87
83
  await sendRequired<typeof renameReqZ, typeof renameResZ>(
88
84
  this.client,
89
- RENAME_ENDPOINT,
85
+ "/channel/rename",
90
86
  { keys, names },
91
87
  renameReqZ,
92
88
  renameResZ,
package/src/client.ts CHANGED
@@ -16,16 +16,15 @@ import { auth } from "@/auth";
16
16
  import { channel } from "@/channel";
17
17
  import { connection } from "@/connection";
18
18
  import { control } from "@/control";
19
+ import { device } from "@/device";
19
20
  import { errorsMiddleware } from "@/errors";
20
21
  import { framer } from "@/framer";
21
- import { hardware } from "@/hardware";
22
- import { device } from "@/hardware/device";
23
- import { rack } from "@/hardware/rack";
24
- import { task } from "@/hardware/task";
25
22
  import { label } from "@/label";
26
23
  import { ontology } from "@/ontology";
24
+ import { rack } from "@/rack";
27
25
  import { ranger } from "@/ranger";
28
26
  import { status } from "@/status";
27
+ import { task } from "@/task";
29
28
  import { Transport } from "@/transport";
30
29
  import { user } from "@/user";
31
30
  import { workspace } from "@/workspace";
@@ -38,7 +37,7 @@ export const synnaxParamsZ = z.object({
38
37
  username: z.string().min(1, "Username is required"),
39
38
  password: z.string().min(1, "Password is required"),
40
39
  connectivityPollFrequency: TimeSpan.z.default(TimeSpan.seconds(30)),
41
- secure: z.boolean().optional().default(false),
40
+ secure: z.boolean().default(false),
42
41
  name: z.string().optional(),
43
42
  retry: breaker.breakerConfigZ.optional(),
44
43
  });
@@ -59,7 +58,7 @@ export default class Synnax extends framer.Client {
59
58
  readonly params: ParsedSynnaxParams;
60
59
  readonly ranges: ranger.Client;
61
60
  readonly channels: channel.Client;
62
- readonly auth: auth.Client | undefined;
61
+ readonly auth: auth.Client;
63
62
  readonly users: user.Client;
64
63
  readonly access: access.Client;
65
64
  readonly connectivity: connection.Checker;
@@ -67,7 +66,9 @@ export default class Synnax extends framer.Client {
67
66
  readonly workspaces: workspace.Client;
68
67
  readonly labels: label.Client;
69
68
  readonly statuses: status.Client;
70
- readonly hardware: hardware.Client;
69
+ readonly tasks: task.Client;
70
+ readonly racks: rack.Client;
71
+ readonly devices: device.Client;
71
72
  readonly control: control.Client;
72
73
  readonly arcs: arc.Client;
73
74
  static readonly connectivity = connection.Checker;
@@ -110,19 +111,15 @@ export default class Synnax extends framer.Client {
110
111
  secure,
111
112
  );
112
113
  transport.use(errorsMiddleware);
113
- let auth_: auth.Client | undefined;
114
- if (username != null && password != null) {
115
- auth_ = new auth.Client(transport.unary, { username, password });
116
- transport.use(auth_.middleware());
117
- }
118
114
  const chRetriever = new channel.CacheRetriever(
119
115
  new channel.ClusterRetriever(transport.unary),
120
116
  );
121
- const chCreator = new channel.Writer(transport.unary, chRetriever);
122
117
  super(transport.stream, transport.unary, chRetriever);
118
+ this.auth = new auth.Client(transport.unary, { username, password });
119
+ transport.use(this.auth.middleware());
120
+ const chCreator = new channel.Writer(transport.unary, chRetriever);
123
121
  this.createdAt = TimeStamp.now();
124
122
  this.params = parsedParams;
125
- this.auth = auth_;
126
123
  this.transport = transport;
127
124
  this.channels = new channel.Client(this, chRetriever, transport.unary, chCreator);
128
125
  this.connectivity = new connection.Checker(
@@ -147,15 +144,14 @@ export default class Synnax extends framer.Client {
147
144
  this.access = new access.Client(this.transport.unary);
148
145
  this.users = new user.Client(this.transport.unary);
149
146
  this.workspaces = new workspace.Client(this.transport.unary);
150
- const devices = new device.Client(this.transport.unary);
151
- const tasks = new task.Client(
147
+ this.tasks = new task.Client(
152
148
  this.transport.unary,
153
149
  this,
154
150
  this.ontology,
155
151
  this.ranges,
156
152
  );
157
- const racks = new rack.Client(this.transport.unary, tasks);
158
- this.hardware = new hardware.Client(tasks, racks, devices);
153
+ this.racks = new rack.Client(this.transport.unary, this.tasks);
154
+ this.devices = new device.Client(this.transport.unary);
159
155
  this.arcs = new arc.Client(this.transport.unary, this.transport.stream);
160
156
  }
161
157
 
@@ -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));