@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,115 @@
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 { TimeRange } from "@synnaxlabs/x";
11
+ import { describe, expect, it } from "vitest";
12
+
13
+ import { AuthError, NotFoundError } from "@/errors";
14
+ import { ranger } from "@/ranger";
15
+ import { createTestClientWithPolicy } from "@/testutil/access";
16
+ import { createTestClient } from "@/testutil/client";
17
+
18
+ const client = createTestClient();
19
+
20
+ describe("range", () => {
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 randomRange = await client.ranges.create({
29
+ name: "test",
30
+ timeRange: new TimeRange(1n, 1000n),
31
+ color: "#E774D0",
32
+ });
33
+ await expect(userClient.ranges.retrieve(randomRange.key)).rejects.toThrow(
34
+ AuthError,
35
+ );
36
+ });
37
+
38
+ it("should allow the caller to retrieve ranges with the correct policy", async () => {
39
+ const userClient = await createTestClientWithPolicy(client, {
40
+ name: "test",
41
+ objects: [ranger.ontologyID("")],
42
+ actions: ["retrieve"],
43
+ });
44
+ const randomRange = await client.ranges.create({
45
+ name: "test",
46
+ timeRange: new TimeRange(1n, 1000n),
47
+ color: "#E774D0",
48
+ });
49
+ const retrieved = await userClient.ranges.retrieve(randomRange.key);
50
+ expect(retrieved.key).toBe(randomRange.key);
51
+ expect(retrieved.name).toBe(randomRange.name);
52
+ });
53
+
54
+ it("should allow the caller to create ranges with the correct policy", async () => {
55
+ const userClient = await createTestClientWithPolicy(client, {
56
+ name: "test",
57
+ objects: [ranger.ontologyID("")],
58
+ actions: ["create"],
59
+ });
60
+ await userClient.ranges.create({
61
+ name: "test",
62
+ timeRange: new TimeRange(1n, 1000n),
63
+ color: "#E774D0",
64
+ });
65
+ });
66
+
67
+ it("should deny access when no create policy exists", async () => {
68
+ const userClient = await createTestClientWithPolicy(client, {
69
+ name: "test",
70
+ objects: [ranger.ontologyID("")],
71
+ actions: [],
72
+ });
73
+ await expect(
74
+ userClient.ranges.create({
75
+ name: "test",
76
+ timeRange: new TimeRange(1n, 1000n),
77
+ color: "#E774D0",
78
+ }),
79
+ ).rejects.toThrow(AuthError);
80
+ });
81
+
82
+ it("should allow the caller to delete ranges with the correct policy", async () => {
83
+ const userClient = await createTestClientWithPolicy(client, {
84
+ name: "test",
85
+ objects: [ranger.ontologyID("")],
86
+ actions: ["delete"],
87
+ });
88
+ const randomRange = await client.ranges.create({
89
+ name: "test",
90
+ timeRange: new TimeRange(1n, 1000n),
91
+ color: "#E774D0",
92
+ });
93
+ await userClient.ranges.delete(randomRange.key);
94
+ await expect(userClient.ranges.retrieve(randomRange.key)).rejects.toThrow(
95
+ NotFoundError,
96
+ );
97
+ });
98
+
99
+ it("should deny access when no delete policy exists", async () => {
100
+ const userClient = await createTestClientWithPolicy(client, {
101
+ name: "test",
102
+ objects: [ranger.ontologyID("")],
103
+ actions: [],
104
+ });
105
+ const randomRange = await client.ranges.create({
106
+ name: "test",
107
+ timeRange: new TimeRange(1n, 1000n),
108
+ color: "#E774D0",
109
+ });
110
+ await expect(userClient.ranges.delete(randomRange.key)).rejects.toThrow(
111
+ AuthError,
112
+ );
113
+ });
114
+ });
115
+ });
@@ -12,7 +12,6 @@ import { array, type change } from "@synnaxlabs/x";
12
12
  import { z } from "zod";
13
13
 
14
14
  import { channel } from "@/channel";
15
- import { type framer } from "@/framer";
16
15
  import { type Key, keyZ } from "@/ranger/payload";
17
16
 
18
17
  export const SET_ALIAS_CHANNEL_NAME = "sy_range_alias_set";
@@ -39,21 +38,14 @@ const retrieveReqZ = z.object({ range: keyZ, channels: channel.keyZ.array() });
39
38
  const retrieveResZ = z.object({ aliases: z.record(z.string(), z.string()) });
40
39
 
41
40
  export class Aliaser {
42
- private static readonly SET_ENDPOINT = "/range/alias/set";
43
- private static readonly RESOLVE_ENDPOINT = "/range/alias/resolve";
44
- private static readonly LIST_ENDPOINT = "/range/alias/list";
45
- private static readonly RETRIEVE_ENDPOINT = "/range/alias/retrieve";
46
- private static readonly DELETE_ENDPOINT = "/range/alias/delete";
47
- private readonly frameClient: framer.Client;
48
41
  private readonly cache = new Map<string, channel.Key>();
49
42
  private readonly client: UnaryClient;
50
43
  private readonly rangeKey: Key;
51
44
 
52
- constructor(rangeKey: Key, frameClient: framer.Client, client: UnaryClient) {
45
+ constructor(rangeKey: Key, client: UnaryClient) {
53
46
  this.rangeKey = rangeKey;
54
47
  this.cache = new Map();
55
48
  this.client = client;
56
- this.frameClient = frameClient;
57
49
  }
58
50
 
59
51
  resolve(aliases: string): Promise<channel.Key>;
@@ -79,7 +71,7 @@ export class Aliaser {
79
71
  if (toFetch.length === 0) return cached;
80
72
  const res = await sendRequired<typeof resolveReqZ, typeof resolveResZ>(
81
73
  this.client,
82
- Aliaser.RESOLVE_ENDPOINT,
74
+ "/range/alias/resolve",
83
75
  { range: this.rangeKey, aliases: toFetch },
84
76
  resolveReqZ,
85
77
  resolveResZ,
@@ -91,7 +83,7 @@ export class Aliaser {
91
83
  async set(aliases: Record<channel.Key, string>): Promise<void> {
92
84
  await sendRequired<typeof setReqZ, typeof setResZ>(
93
85
  this.client,
94
- Aliaser.SET_ENDPOINT,
86
+ "/range/alias/set",
95
87
  { range: this.rangeKey, aliases },
96
88
  setReqZ,
97
89
  setResZ,
@@ -102,7 +94,7 @@ export class Aliaser {
102
94
  return (
103
95
  await sendRequired<typeof listReqZ, typeof listResZ>(
104
96
  this.client,
105
- Aliaser.LIST_ENDPOINT,
97
+ "/range/alias/list",
106
98
  { range: this.rangeKey },
107
99
  listReqZ,
108
100
  listResZ,
@@ -119,7 +111,7 @@ export class Aliaser {
119
111
  const isSingle = typeof alias === "number";
120
112
  const res = await sendRequired<typeof retrieveReqZ, typeof retrieveResZ>(
121
113
  this.client,
122
- Aliaser.RETRIEVE_ENDPOINT,
114
+ "/range/alias/retrieve",
123
115
  { range: this.rangeKey, channels: array.toArray(alias) },
124
116
  retrieveReqZ,
125
117
  retrieveResZ,
@@ -130,7 +122,7 @@ export class Aliaser {
130
122
  async delete(aliases: channel.Key | channel.Key[]): Promise<void> {
131
123
  await sendRequired<typeof deleteReqZ, typeof deleteResZ>(
132
124
  this.client,
133
- Aliaser.DELETE_ENDPOINT,
125
+ "/range/alias/delete",
134
126
  { range: this.rangeKey, channels: array.toArray(aliases) },
135
127
  deleteReqZ,
136
128
  deleteResZ,
@@ -15,7 +15,7 @@ import { type channel } from "@/channel";
15
15
  import { QueryError } from "@/errors";
16
16
  import { type framer } from "@/framer";
17
17
  import { label } from "@/label";
18
- import { type ontology } from "@/ontology";
18
+ import { ontology } from "@/ontology";
19
19
  import { Aliaser } from "@/ranger/alias";
20
20
  import { KV } from "@/ranger/kv";
21
21
  import {
@@ -163,12 +163,12 @@ export class Range {
163
163
 
164
164
  const retrieveRequestZ = z.object({
165
165
  keys: keyZ.array().optional(),
166
- names: z.array(z.string()).optional(),
166
+ names: z.string().array().optional(),
167
167
  searchTerm: z.string().optional(),
168
168
  overlapsWith: TimeRange.z.optional(),
169
- limit: z.number().int().optional(),
170
- offset: z.number().int().optional(),
171
169
  hasLabels: label.keyZ.array().optional(),
170
+ limit: z.int().optional(),
171
+ offset: z.int().optional(),
172
172
  includeLabels: z.boolean().optional(),
173
173
  includeParent: z.boolean().optional(),
174
174
  });
@@ -189,8 +189,6 @@ const retrieveArgsZ = retrieveRequestZ
189
189
 
190
190
  export type RetrieveArgs = z.input<typeof retrieveArgsZ>;
191
191
 
192
- const RETRIEVE_ENDPOINT = "/range/retrieve";
193
-
194
192
  const retrieveResZ = z.object({ ranges: array.nullableZ(payloadZ) });
195
193
 
196
194
  export class Client {
@@ -244,7 +242,7 @@ export class Client {
244
242
  const isSingle = typeof params === "string";
245
243
  const { ranges } = await sendRequired(
246
244
  this.unaryClient,
247
- RETRIEVE_ENDPOINT,
245
+ "/range/retrieve",
248
246
  params,
249
247
  retrieveArgsZ,
250
248
  retrieveResZ,
@@ -271,7 +269,7 @@ export class Client {
271
269
  }
272
270
 
273
271
  async retrieveAlias(range: Key, channel: channel.Key): Promise<string> {
274
- const aliaser = new Aliaser(range, this.frameClient, this.unaryClient);
272
+ const aliaser = new Aliaser(range, this.unaryClient);
275
273
  return await aliaser.retrieve(channel);
276
274
  }
277
275
 
@@ -279,22 +277,22 @@ export class Client {
279
277
  range: Key,
280
278
  channels: channel.Key[],
281
279
  ): Promise<Record<channel.Key, string>> {
282
- const aliaser = new Aliaser(range, this.frameClient, this.unaryClient);
280
+ const aliaser = new Aliaser(range, this.unaryClient);
283
281
  return await aliaser.retrieve(channels);
284
282
  }
285
283
 
286
284
  async listAliases(range: Key): Promise<Record<channel.Key, string>> {
287
- const aliaser = new Aliaser(range, this.frameClient, this.unaryClient);
285
+ const aliaser = new Aliaser(range, this.unaryClient);
288
286
  return await aliaser.list();
289
287
  }
290
288
 
291
289
  async setAlias(range: Key, channel: channel.Key, alias: string): Promise<void> {
292
- const aliaser = new Aliaser(range, this.frameClient, this.unaryClient);
290
+ const aliaser = new Aliaser(range, this.unaryClient);
293
291
  await aliaser.set({ [channel]: alias });
294
292
  }
295
293
 
296
294
  async deleteAlias(range: Key, channels: channel.Key | channel.Key[]): Promise<void> {
297
- const aliaser = new Aliaser(range, this.frameClient, this.unaryClient);
295
+ const aliaser = new Aliaser(range, this.unaryClient);
298
296
  await aliaser.delete(channels);
299
297
  }
300
298
 
@@ -302,7 +300,7 @@ export class Client {
302
300
  return new Range(payload, {
303
301
  frameClient: this.frameClient,
304
302
  kv: new KV(payload.key, this.unaryClient),
305
- aliaser: new Aliaser(payload.key, this.frameClient, this.unaryClient),
303
+ aliaser: new Aliaser(payload.key, this.unaryClient),
306
304
  channels: this.channels,
307
305
  labelClient: this.labelClient,
308
306
  ontologyClient: this.ontologyClient,
@@ -326,7 +324,8 @@ export class Client {
326
324
  }
327
325
  }
328
326
 
329
- export const ontologyID = (key: Key): ontology.ID => ({ type: "range", key });
327
+ export const ontologyID = ontology.createIDFactory<Key>("range");
328
+ export const TYPE_ONTOLOGY_ID = ontologyID("");
330
329
 
331
330
  export const aliasOntologyID = (key: Key): ontology.ID => ({
332
331
  type: "range-alias",
package/src/ranger/kv.ts CHANGED
@@ -8,7 +8,7 @@
8
8
  // included in the file licenses/APL.txt.
9
9
 
10
10
  import { sendRequired, type UnaryClient } from "@synnaxlabs/freighter";
11
- import { array, isObject } from "@synnaxlabs/x";
11
+ import { array } from "@synnaxlabs/x";
12
12
  import { z } from "zod";
13
13
 
14
14
  import { type Key, keyZ } from "@/ranger/payload";
@@ -34,9 +34,6 @@ const deleteReqZ = z.object({ range: keyZ, keys: z.string().array() });
34
34
  export interface DeleteRequest extends z.infer<typeof deleteReqZ> {}
35
35
 
36
36
  export class KV {
37
- private static readonly GET_ENDPOINT = "/range/kv/get";
38
- private static readonly SET_ENDPOINT = "/range/kv/set";
39
- private static readonly DELETE_ENDPOINT = "/range/kv/delete";
40
37
  private readonly rangeKey: Key;
41
38
  private readonly client: UnaryClient;
42
39
 
@@ -50,7 +47,7 @@ export class KV {
50
47
  async get(keys: string | string[]): Promise<string | Record<string, string>> {
51
48
  const res = await sendRequired(
52
49
  this.client,
53
- KV.GET_ENDPOINT,
50
+ "/range/kv/get",
54
51
  { range: this.rangeKey, keys: array.toArray(keys) },
55
52
  getReqZ,
56
53
  getResZ,
@@ -67,16 +64,17 @@ export class KV {
67
64
  async set(kv: Record<string, string>): Promise<void>;
68
65
  async set(key: string | Record<string, string>, value: string = ""): Promise<void> {
69
66
  let pairs: KVPair[];
70
- if (isObject(key))
67
+ if (typeof key == "string") pairs = [{ range: this.rangeKey, key, value }];
68
+ else
71
69
  pairs = Object.entries(key).map(([k, v]) => ({
72
70
  range: this.rangeKey,
73
71
  key: k,
74
72
  value: v,
75
73
  }));
76
- else pairs = [{ range: this.rangeKey, key, value }];
74
+
77
75
  await sendRequired(
78
76
  this.client,
79
- KV.SET_ENDPOINT,
77
+ "/range/kv/set",
80
78
  { range: this.rangeKey, pairs },
81
79
  setReqZ,
82
80
  z.unknown(),
@@ -86,7 +84,7 @@ export class KV {
86
84
  async delete(key: string | string[]): Promise<void> {
87
85
  await sendRequired(
88
86
  this.client,
89
- KV.DELETE_ENDPOINT,
87
+ "/range/kv/delete",
90
88
  { range: this.rangeKey, keys: array.toArray(key) },
91
89
  deleteReqZ,
92
90
  z.unknown(),
@@ -7,7 +7,7 @@
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 { DataType, math, TimeSpan, TimeStamp, uuid } from "@synnaxlabs/x";
10
+ import { DataType, id, math, TimeSpan, TimeStamp, uuid } from "@synnaxlabs/x";
11
11
  import { describe, expect, it } from "vitest";
12
12
 
13
13
  import { NotFoundError } from "@/errors";
@@ -284,7 +284,7 @@ describe("Ranger", () => {
284
284
  describe("set + resolve", () => {
285
285
  it("should set and resolve an alias for the range", async () => {
286
286
  const ch = await client.channels.create({
287
- name: "My New Channel",
287
+ name: id.create(),
288
288
  dataType: DataType.FLOAT32,
289
289
  virtual: true,
290
290
  });
@@ -300,7 +300,7 @@ describe("Ranger", () => {
300
300
  describe("deleteAlias", () => {
301
301
  it("should remove an alias for the range", async () => {
302
302
  const ch = await client.channels.create({
303
- name: "My New Channel",
303
+ name: id.create(),
304
304
  dataType: DataType.FLOAT32,
305
305
  virtual: true,
306
306
  });
@@ -316,7 +316,7 @@ describe("Ranger", () => {
316
316
  describe("list", () => {
317
317
  it("should list the aliases for the range", async () => {
318
318
  const ch = await client.channels.create({
319
- name: "My New Channel",
319
+ name: id.create(),
320
320
  dataType: DataType.FLOAT32,
321
321
  virtual: true,
322
322
  });
@@ -26,10 +26,6 @@ const deleteResZ = z.object({});
26
26
  const renameReqZ = z.object({ key: keyZ, name: nameZ });
27
27
  const renameResZ = z.object({});
28
28
 
29
- const CREATE_ENDPOINT = "/range/create";
30
- const DELETE_ENDPOINT = "/range/delete";
31
- const RENAME_ENDPOINT = "/range/rename";
32
-
33
29
  export class Writer {
34
30
  client: UnaryClient;
35
31
 
@@ -40,7 +36,7 @@ export class Writer {
40
36
  async rename(key: string, name: string): Promise<void> {
41
37
  await sendRequired<typeof renameReqZ, typeof renameResZ>(
42
38
  this.client,
43
- RENAME_ENDPOINT,
39
+ "/range/rename",
44
40
  { key, name },
45
41
  renameReqZ,
46
42
  renameResZ,
@@ -50,7 +46,7 @@ export class Writer {
50
46
  async create(ranges: New[], options?: CreateOptions): Promise<Payload[]> {
51
47
  const res = await sendRequired<typeof createReqZ, typeof createResZ>(
52
48
  this.client,
53
- CREATE_ENDPOINT,
49
+ "/range/create",
54
50
  { ranges, ...options },
55
51
  createReqZ,
56
52
  createResZ,
@@ -61,7 +57,7 @@ export class Writer {
61
57
  async delete(keys: string[]): Promise<void> {
62
58
  await sendRequired<typeof deleteReqZ, typeof deleteResZ>(
63
59
  this.client,
64
- DELETE_ENDPOINT,
60
+ "/range/delete",
65
61
  { keys },
66
62
  deleteReqZ,
67
63
  deleteResZ,
@@ -0,0 +1,129 @@
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 { TimeStamp, uuid } from "@synnaxlabs/x";
11
+ import { describe, expect, it } from "vitest";
12
+
13
+ import { AuthError, NotFoundError } from "@/errors";
14
+ import { status } from "@/status";
15
+ import { createTestClientWithPolicy } from "@/testutil/access";
16
+ import { createTestClient } from "@/testutil/client";
17
+
18
+ const client = createTestClient();
19
+
20
+ describe("status", () => {
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 randomStatus = await client.statuses.set({
29
+ name: "test",
30
+ key: uuid.create(),
31
+ variant: "info",
32
+ message: "test",
33
+ time: TimeStamp.now(),
34
+ });
35
+ await expect(
36
+ userClient.statuses.retrieve({ key: randomStatus.key }),
37
+ ).rejects.toThrow(AuthError);
38
+ });
39
+
40
+ it("should allow the caller to retrieve statuses with the correct policy", async () => {
41
+ const userClient = await createTestClientWithPolicy(client, {
42
+ name: "test",
43
+ objects: [status.ontologyID("")],
44
+ actions: ["retrieve"],
45
+ });
46
+ const randomStatus = await client.statuses.set({
47
+ name: "test",
48
+ key: uuid.create(),
49
+ variant: "info",
50
+ message: "test",
51
+ time: TimeStamp.now(),
52
+ });
53
+ const retrieved = await userClient.statuses.retrieve({
54
+ key: randomStatus.key,
55
+ });
56
+ expect(retrieved.key).toBe(randomStatus.key);
57
+ expect(retrieved.name).toBe(randomStatus.name);
58
+ });
59
+
60
+ it("should allow the caller to set statuses with the correct policy", async () => {
61
+ const userClient = await createTestClientWithPolicy(client, {
62
+ name: "test",
63
+ objects: [status.ontologyID("")],
64
+ actions: ["create"],
65
+ });
66
+ await userClient.statuses.set({
67
+ name: "test",
68
+ key: uuid.create(),
69
+ variant: "info",
70
+ message: "test",
71
+ time: TimeStamp.now(),
72
+ });
73
+ });
74
+
75
+ it("should deny access when no create policy exists", async () => {
76
+ const userClient = await createTestClientWithPolicy(client, {
77
+ name: "test",
78
+ objects: [status.ontologyID("")],
79
+ actions: [],
80
+ });
81
+ await expect(
82
+ userClient.statuses.set({
83
+ name: "test",
84
+ key: uuid.create(),
85
+ variant: "info",
86
+ message: "test",
87
+ time: TimeStamp.now(),
88
+ }),
89
+ ).rejects.toThrow(AuthError);
90
+ });
91
+
92
+ it("should allow the caller to delete statuses with the correct policy", async () => {
93
+ const userClient = await createTestClientWithPolicy(client, {
94
+ name: "test",
95
+ objects: [status.ontologyID("")],
96
+ actions: ["delete", "retrieve"],
97
+ });
98
+ const randomStatus = await client.statuses.set({
99
+ name: "test",
100
+ key: uuid.create(),
101
+ variant: "info",
102
+ message: "test",
103
+ time: TimeStamp.now(),
104
+ });
105
+ await userClient.statuses.delete(randomStatus.key);
106
+ await expect(
107
+ userClient.statuses.retrieve({ key: randomStatus.key }),
108
+ ).rejects.toThrow(NotFoundError);
109
+ });
110
+
111
+ it("should deny access when no delete policy exists", async () => {
112
+ const userClient = await createTestClientWithPolicy(client, {
113
+ name: "test",
114
+ objects: [status.ontologyID("")],
115
+ actions: [],
116
+ });
117
+ const randomStatus = await client.statuses.set({
118
+ name: "test",
119
+ key: uuid.create(),
120
+ variant: "info",
121
+ message: "test",
122
+ time: TimeStamp.now(),
123
+ });
124
+ await expect(userClient.statuses.delete(randomStatus.key)).rejects.toThrow(
125
+ AuthError,
126
+ );
127
+ });
128
+ });
129
+ });
@@ -29,15 +29,11 @@ const setResZ = <DetailsSchema extends z.ZodType = z.ZodNever>(
29
29
  const deleteReqZ = z.object({ keys: keyZ.array() });
30
30
  const emptyResZ = z.object({});
31
31
 
32
- const SET_ENDPOINT = "/status/set";
33
- const DELETE_ENDPOINT = "/status/delete";
34
- const RETRIEVE_ENDPOINT = "/status/retrieve";
35
-
36
32
  const retrieveRequestZ = z.object({
37
33
  keys: keyZ.array().optional(),
38
34
  searchTerm: z.string().optional(),
39
- offset: z.number().optional(),
40
- limit: z.number().optional(),
35
+ offset: z.int().optional(),
36
+ limit: z.int().optional(),
41
37
  includeLabels: z.boolean().optional(),
42
38
  hasLabels: label.keyZ.array().optional(),
43
39
  });
@@ -79,7 +75,7 @@ export class Client {
79
75
  const isSingle = "key" in args;
80
76
  const res = await sendRequired(
81
77
  this.client,
82
- RETRIEVE_ENDPOINT,
78
+ "/status/retrieve",
83
79
  args,
84
80
  retrieveArgsZ,
85
81
  retrieveResponseZ<DetailsSchema>(args.detailsSchema),
@@ -105,7 +101,7 @@ export class Client {
105
101
  ReturnType<typeof setResZ<DetailsSchema>>
106
102
  >(
107
103
  this.client,
108
- SET_ENDPOINT,
104
+ "/status/set",
109
105
  {
110
106
  statuses: array.toArray(statuses),
111
107
  parent: opts.parent,
@@ -120,7 +116,7 @@ export class Client {
120
116
  async delete(keys: Key | Key[]): Promise<void> {
121
117
  await sendRequired<typeof deleteReqZ, typeof emptyResZ>(
122
118
  this.client,
123
- DELETE_ENDPOINT,
119
+ "/status/delete",
124
120
  { keys: array.toArray(keys) },
125
121
  deleteReqZ,
126
122
  emptyResZ,
@@ -10,7 +10,7 @@
10
10
  import { status } from "@synnaxlabs/x";
11
11
  import { z } from "zod";
12
12
 
13
- import { type ontology } from "@/ontology";
13
+ import { ontology } from "@/ontology";
14
14
 
15
15
  export const keyZ = z.string();
16
16
  export type Key = z.infer<typeof keyZ>;
@@ -34,4 +34,5 @@ export type Status<Details extends z.ZodType = z.ZodNever> = status.Status<Detai
34
34
  export const SET_CHANNEL_NAME = "sy_status_set";
35
35
  export const DELETE_CHANNEL_NAME = "sy_status_delete";
36
36
 
37
- export const ontologyID = (key: Key): ontology.ID => ({ type: "status", key });
37
+ export const ontologyID = ontology.createIDFactory<Key>("status");
38
+ export const TYPE_ONTOLOGY_ID = ontologyID("");