@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
@@ -11,7 +11,7 @@ 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 { type ontology } from "@/ontology";
14
+ import { ontology } from "@/ontology";
15
15
  import { checkForMultipleOrNoResults } from "@/util/retrieve";
16
16
  import {
17
17
  type Key,
@@ -24,12 +24,6 @@ import {
24
24
  } from "@/workspace/log/payload";
25
25
  import { type Key as WorkspaceKey, keyZ as workspaceKeyZ } from "@/workspace/payload";
26
26
 
27
- const RETRIEVE_ENDPOINT = "/workspace/log/retrieve";
28
- const CREATE_ENDPOINT = "/workspace/log/create";
29
- const RENAME_ENDPOINT = "/workspace/log/rename";
30
- const SET_DATA_ENDPOINT = "/workspace/log/set-data";
31
- const DELETE_ENDPOINT = "/workspace/log/delete";
32
-
33
27
  const renameReqZ = z.object({ key: keyZ, name: z.string() });
34
28
 
35
29
  const setDataReqZ = z.object({ key: keyZ, data: z.string() });
@@ -65,7 +59,7 @@ export class Client {
65
59
  const isMany = Array.isArray(logs);
66
60
  const res = await sendRequired(
67
61
  this.client,
68
- CREATE_ENDPOINT,
62
+ "/workspace/log/create",
69
63
  { workspace, logs: array.toArray(logs) },
70
64
  createReqZ,
71
65
  createResZ,
@@ -76,7 +70,7 @@ export class Client {
76
70
  async rename(key: Key, name: string): Promise<void> {
77
71
  await sendRequired(
78
72
  this.client,
79
- RENAME_ENDPOINT,
73
+ "/workspace/log/rename",
80
74
  { key, name },
81
75
  renameReqZ,
82
76
  emptyResZ,
@@ -86,7 +80,7 @@ export class Client {
86
80
  async setData(key: Key, data: record.Unknown): Promise<void> {
87
81
  await sendRequired(
88
82
  this.client,
89
- SET_DATA_ENDPOINT,
83
+ "/workspace/log/set-data",
90
84
  { key, data: JSON.stringify(data) },
91
85
  setDataReqZ,
92
86
  emptyResZ,
@@ -101,7 +95,7 @@ export class Client {
101
95
  const isSingle = singleRetrieveArgsZ.safeParse(args).success;
102
96
  const res = await sendRequired(
103
97
  this.client,
104
- RETRIEVE_ENDPOINT,
98
+ "/workspace/log/retrieve",
105
99
  args,
106
100
  retrieveArgsZ,
107
101
  retrieveResZ,
@@ -113,7 +107,7 @@ export class Client {
113
107
  async delete(keys: Params): Promise<void> {
114
108
  await sendRequired(
115
109
  this.client,
116
- DELETE_ENDPOINT,
110
+ "/workspace/log/delete",
117
111
  { keys: array.toArray(keys) },
118
112
  deleteReqZ,
119
113
  emptyResZ,
@@ -121,4 +115,5 @@ export class Client {
121
115
  }
122
116
  }
123
117
 
124
- export const ontologyID = (key: Key): ontology.ID => ({ type: "log", key });
118
+ export const ontologyID = ontology.createIDFactory<Key>("log");
119
+ export const TYPE_ONTOLOGY_ID = ontologyID("");
@@ -0,0 +1,134 @@
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
+
12
+ import { AuthError, NotFoundError } from "@/errors";
13
+ import { createTestClientWithPolicy } from "@/testutil/access";
14
+ import { createTestClient } from "@/testutil/client";
15
+ import { schematic } from "@/workspace/schematic";
16
+
17
+ const client = createTestClient();
18
+
19
+ describe("schematic", () => {
20
+ describe("access control", () => {
21
+ it("should deny access when no retrieve policy exists", async () => {
22
+ const userClient = await createTestClientWithPolicy(client, {
23
+ name: "test",
24
+ objects: [],
25
+ actions: [],
26
+ });
27
+ const ws = await client.workspaces.create({
28
+ name: "test",
29
+ layout: {},
30
+ });
31
+ const randomSchematic = await client.workspaces.schematics.create(ws.key, {
32
+ name: "test",
33
+ data: {},
34
+ });
35
+ await expect(
36
+ userClient.workspaces.schematics.retrieve({ key: randomSchematic.key }),
37
+ ).rejects.toThrow(AuthError);
38
+ });
39
+
40
+ it("should allow the caller to retrieve schematics with the correct policy", async () => {
41
+ const userClient = await createTestClientWithPolicy(client, {
42
+ name: "test",
43
+ objects: [schematic.ontologyID("")],
44
+ actions: ["retrieve"],
45
+ });
46
+ const ws = await client.workspaces.create({
47
+ name: "test",
48
+ layout: {},
49
+ });
50
+ const randomSchematic = await client.workspaces.schematics.create(ws.key, {
51
+ name: "test",
52
+ data: {},
53
+ });
54
+ const retrieved = await userClient.workspaces.schematics.retrieve({
55
+ key: randomSchematic.key,
56
+ });
57
+ expect(retrieved.key).toBe(randomSchematic.key);
58
+ expect(retrieved.name).toBe(randomSchematic.name);
59
+ });
60
+
61
+ it("should allow the caller to create schematics with the correct policy", async () => {
62
+ const userClient = await createTestClientWithPolicy(client, {
63
+ name: "test",
64
+ objects: [schematic.ontologyID("")],
65
+ actions: ["create"],
66
+ });
67
+ const ws = await client.workspaces.create({
68
+ name: "test",
69
+ layout: {},
70
+ });
71
+ await userClient.workspaces.schematics.create(ws.key, {
72
+ name: "test",
73
+ data: {},
74
+ });
75
+ });
76
+
77
+ it("should deny access when no create policy exists", async () => {
78
+ const userClient = await createTestClientWithPolicy(client, {
79
+ name: "test",
80
+ objects: [schematic.ontologyID("")],
81
+ actions: [],
82
+ });
83
+ const ws = await client.workspaces.create({
84
+ name: "test",
85
+ layout: {},
86
+ });
87
+ await expect(
88
+ userClient.workspaces.schematics.create(ws.key, {
89
+ name: "test",
90
+ data: {},
91
+ }),
92
+ ).rejects.toThrow(AuthError);
93
+ });
94
+
95
+ it("should allow the caller to delete schematics with the correct policy", async () => {
96
+ const userClient = await createTestClientWithPolicy(client, {
97
+ name: "test",
98
+ objects: [schematic.ontologyID("")],
99
+ actions: ["delete", "retrieve"],
100
+ });
101
+ const ws = await client.workspaces.create({
102
+ name: "test",
103
+ layout: {},
104
+ });
105
+ const randomSchematic = await client.workspaces.schematics.create(ws.key, {
106
+ name: "test",
107
+ data: {},
108
+ });
109
+ await userClient.workspaces.schematics.delete(randomSchematic.key);
110
+ await expect(
111
+ userClient.workspaces.schematics.retrieve({ key: randomSchematic.key }),
112
+ ).rejects.toThrow(NotFoundError);
113
+ });
114
+
115
+ it("should deny access when no delete policy exists", async () => {
116
+ const userClient = await createTestClientWithPolicy(client, {
117
+ name: "test",
118
+ objects: [schematic.ontologyID("")],
119
+ actions: [],
120
+ });
121
+ const ws = await client.workspaces.create({
122
+ name: "test",
123
+ layout: {},
124
+ });
125
+ const randomSchematic = await client.workspaces.schematics.create(ws.key, {
126
+ name: "test",
127
+ data: {},
128
+ });
129
+ await expect(
130
+ userClient.workspaces.schematics.delete(randomSchematic.key),
131
+ ).rejects.toThrow(AuthError);
132
+ });
133
+ });
134
+ });
@@ -11,7 +11,7 @@ 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 { type ontology } from "@/ontology";
14
+ import { ontology } from "@/ontology";
15
15
  import { checkForMultipleOrNoResults } from "@/util/retrieve";
16
16
  import { type Key as WorkspaceKey, keyZ as workspaceKeyZ } from "@/workspace/payload";
17
17
  import {
@@ -26,13 +26,6 @@ import {
26
26
  } from "@/workspace/schematic/payload";
27
27
  import { symbol } from "@/workspace/schematic/symbol";
28
28
 
29
- const RETRIEVE_ENDPOINT = "/workspace/schematic/retrieve";
30
- const CREATE_ENDPOINT = "/workspace/schematic/create";
31
- const RENAME_ENDPOINT = "/workspace/schematic/rename";
32
- const SET_DATA_ENDPOINT = "/workspace/schematic/set-data";
33
- const DELETE_ENDPOINT = "/workspace/schematic/delete";
34
- const COPY_ENDPOINT = "/workspace/schematic/copy";
35
-
36
29
  const renameReqZ = z.object({ key: keyZ, name: z.string() });
37
30
 
38
31
  const setDataReqZ = z.object({ key: keyZ, data: z.string() });
@@ -84,7 +77,7 @@ export class Client {
84
77
  const isMany = Array.isArray(schematics);
85
78
  const res = await sendRequired(
86
79
  this.client,
87
- CREATE_ENDPOINT,
80
+ "/workspace/schematic/create",
88
81
  { workspace, schematics: array.toArray(schematics) },
89
82
  createReqZ,
90
83
  createResZ,
@@ -95,7 +88,7 @@ export class Client {
95
88
  async rename(key: Key, name: string): Promise<void> {
96
89
  await sendRequired(
97
90
  this.client,
98
- RENAME_ENDPOINT,
91
+ "/workspace/schematic/rename",
99
92
  { key, name },
100
93
  renameReqZ,
101
94
  emptyResZ,
@@ -105,7 +98,7 @@ export class Client {
105
98
  async setData(key: Key, data: record.Unknown): Promise<void> {
106
99
  await sendRequired(
107
100
  this.client,
108
- SET_DATA_ENDPOINT,
101
+ "/workspace/schematic/set-data",
109
102
  { key, data: JSON.stringify(data) },
110
103
  setDataReqZ,
111
104
  emptyResZ,
@@ -120,7 +113,7 @@ export class Client {
120
113
  const isSingle = singleRetrieveArgsZ.safeParse(args).success;
121
114
  const res = await sendRequired(
122
115
  this.client,
123
- RETRIEVE_ENDPOINT,
116
+ "/workspace/schematic/retrieve",
124
117
  args,
125
118
  retrieveArgsZ,
126
119
  retrieveResZ,
@@ -132,7 +125,7 @@ export class Client {
132
125
  async delete(keys: Params): Promise<void> {
133
126
  await sendRequired(
134
127
  this.client,
135
- DELETE_ENDPOINT,
128
+ "/workspace/schematic/delete",
136
129
  { keys: array.toArray(keys) },
137
130
  deleteReqZ,
138
131
  emptyResZ,
@@ -142,7 +135,7 @@ export class Client {
142
135
  async copy(args: CopyArgs): Promise<Schematic> {
143
136
  const res = await sendRequired(
144
137
  this.client,
145
- COPY_ENDPOINT,
138
+ "/workspace/schematic/copy",
146
139
  args,
147
140
  copyReqZ,
148
141
  copyResZ,
@@ -151,7 +144,5 @@ export class Client {
151
144
  }
152
145
  }
153
146
 
154
- export const ontologyID = (key: Key): ontology.ID => ({
155
- type: "schematic",
156
- key,
157
- });
147
+ export const ontologyID = ontology.createIDFactory<Key>("schematic");
148
+ export const TYPE_ONTOLOGY_ID = ontologyID("");
@@ -0,0 +1,172 @@
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
+
12
+ import { AuthError, NotFoundError } from "@/errors";
13
+ import { ontology } from "@/ontology";
14
+ import { group } from "@/ontology/group";
15
+ import { createTestClientWithPolicy } from "@/testutil/access";
16
+ import { createTestClient } from "@/testutil/client";
17
+ import { symbol } from "@/workspace/schematic/symbol";
18
+
19
+ const client = createTestClient();
20
+
21
+ describe("schematic_symbol", () => {
22
+ describe("access control", () => {
23
+ it("should deny access when no retrieve policy exists", async () => {
24
+ const userClient = await createTestClientWithPolicy(client, {
25
+ name: "test",
26
+ objects: [],
27
+ actions: [],
28
+ });
29
+ const symbolGroup = await client.ontology.groups.create({
30
+ parent: ontology.ROOT_ID,
31
+ name: "Test Symbols",
32
+ });
33
+ const randomSymbol = await client.workspaces.schematics.symbols.create({
34
+ name: "test",
35
+ data: {
36
+ svg: "<svg></svg>",
37
+ states: [],
38
+ handles: [],
39
+ variant: "sensor",
40
+ },
41
+ parent: group.ontologyID(symbolGroup.key),
42
+ });
43
+ await expect(
44
+ userClient.workspaces.schematics.symbols.retrieve({ key: randomSymbol.key }),
45
+ ).rejects.toThrow(AuthError);
46
+ });
47
+
48
+ it("should allow the caller to retrieve symbols with the correct policy", async () => {
49
+ const userClient = await createTestClientWithPolicy(client, {
50
+ name: "test",
51
+ objects: [symbol.ontologyID("")],
52
+ actions: ["retrieve"],
53
+ });
54
+ const symbolGroup = await client.ontology.groups.create({
55
+ parent: ontology.ROOT_ID,
56
+ name: "Test Symbols",
57
+ });
58
+ const randomSymbol = await client.workspaces.schematics.symbols.create({
59
+ name: "test",
60
+ data: {
61
+ svg: "<svg></svg>",
62
+ states: [],
63
+ handles: [],
64
+ variant: "sensor",
65
+ },
66
+ parent: group.ontologyID(symbolGroup.key),
67
+ });
68
+ const retrieved = await userClient.workspaces.schematics.symbols.retrieve({
69
+ key: randomSymbol.key,
70
+ });
71
+ expect(retrieved.key).toBe(randomSymbol.key);
72
+ expect(retrieved.name).toBe(randomSymbol.name);
73
+ });
74
+
75
+ it("should allow the caller to create symbols with the correct policy", async () => {
76
+ const userClient = await createTestClientWithPolicy(client, {
77
+ name: "test",
78
+ objects: [symbol.ontologyID("")],
79
+ actions: ["create"],
80
+ });
81
+ const symbolGroup = await client.ontology.groups.create({
82
+ parent: ontology.ROOT_ID,
83
+ name: "Test Symbols",
84
+ });
85
+ await userClient.workspaces.schematics.symbols.create({
86
+ name: "test",
87
+ data: {
88
+ svg: "<svg></svg>",
89
+ states: [],
90
+ handles: [],
91
+ variant: "sensor",
92
+ },
93
+ parent: group.ontologyID(symbolGroup.key),
94
+ });
95
+ });
96
+
97
+ it("should deny access when no create policy exists", async () => {
98
+ const userClient = await createTestClientWithPolicy(client, {
99
+ name: "test",
100
+ objects: [symbol.ontologyID("")],
101
+ actions: [],
102
+ });
103
+ const symbolGroup = await client.ontology.groups.create({
104
+ parent: ontology.ROOT_ID,
105
+ name: "Test Symbols",
106
+ });
107
+ await expect(
108
+ userClient.workspaces.schematics.symbols.create({
109
+ name: "test",
110
+ data: {
111
+ svg: "<svg></svg>",
112
+ states: [],
113
+ handles: [],
114
+ variant: "sensor",
115
+ },
116
+ parent: group.ontologyID(symbolGroup.key),
117
+ }),
118
+ ).rejects.toThrow(AuthError);
119
+ });
120
+
121
+ it("should allow the caller to delete symbols with the correct policy", async () => {
122
+ const userClient = await createTestClientWithPolicy(client, {
123
+ name: "test",
124
+ objects: [symbol.ontologyID("")],
125
+ actions: ["delete", "retrieve"],
126
+ });
127
+ const symbolGroup = await client.ontology.groups.create({
128
+ parent: ontology.ROOT_ID,
129
+ name: "Test Symbols",
130
+ });
131
+ const randomSymbol = await client.workspaces.schematics.symbols.create({
132
+ name: "test",
133
+ data: {
134
+ svg: "<svg></svg>",
135
+ states: [],
136
+ handles: [],
137
+ variant: "sensor",
138
+ },
139
+ parent: group.ontologyID(symbolGroup.key),
140
+ });
141
+ await userClient.workspaces.schematics.symbols.delete(randomSymbol.key);
142
+ await expect(
143
+ userClient.workspaces.schematics.symbols.retrieve({ key: randomSymbol.key }),
144
+ ).rejects.toThrow(NotFoundError);
145
+ });
146
+
147
+ it("should deny access when no delete policy exists", async () => {
148
+ const userClient = await createTestClientWithPolicy(client, {
149
+ name: "test",
150
+ objects: [symbol.ontologyID("")],
151
+ actions: [],
152
+ });
153
+ const symbolGroup = await client.ontology.groups.create({
154
+ parent: ontology.ROOT_ID,
155
+ name: "Test Symbols",
156
+ });
157
+ const randomSymbol = await client.workspaces.schematics.symbols.create({
158
+ name: "test",
159
+ data: {
160
+ svg: "<svg></svg>",
161
+ states: [],
162
+ handles: [],
163
+ variant: "sensor",
164
+ },
165
+ parent: group.ontologyID(symbolGroup.key),
166
+ });
167
+ await expect(
168
+ userClient.workspaces.schematics.symbols.delete(randomSymbol.key),
169
+ ).rejects.toThrow(AuthError);
170
+ });
171
+ });
172
+ });
@@ -23,12 +23,6 @@ import {
23
23
  symbolZ,
24
24
  } from "@/workspace/schematic/symbol/payload";
25
25
 
26
- const RETRIEVE_ENDPOINT = "/workspace/schematic/symbol/retrieve";
27
- const CREATE_ENDPOINT = "/workspace/schematic/symbol/create";
28
- const RENAME_ENDPOINT = "/workspace/schematic/symbol/rename";
29
- const DELETE_ENDPOINT = "/workspace/schematic/symbol/delete";
30
- const RETRIEVE_GROUP_ENDPOINT = "/workspace/schematic/symbol/retrieve_group";
31
-
32
26
  const createReqZ = z.object({ symbols: newZ.array(), parent: ontology.idZ });
33
27
  const renameReqZ = z.object({ key: keyZ, name: z.string() });
34
28
  const deleteReqZ = z.object({ keys: keyZ.array() });
@@ -36,8 +30,6 @@ const deleteReqZ = z.object({ keys: keyZ.array() });
36
30
  const retrieveRequestZ = z.object({
37
31
  keys: keyZ.array().optional(),
38
32
  searchTerm: z.string().optional(),
39
- offset: z.number().optional(),
40
- limit: z.number().optional(),
41
33
  });
42
34
 
43
35
  const singleRetrieveArgsZ = z
@@ -82,7 +74,7 @@ export class Client {
82
74
  const symbols = isMany ? options.symbols : [options];
83
75
  const res = await sendRequired(
84
76
  this.client,
85
- CREATE_ENDPOINT,
77
+ "/workspace/schematic/symbol/create",
86
78
  { symbols, parent: options.parent },
87
79
  createReqZ,
88
80
  createResZ,
@@ -93,7 +85,7 @@ export class Client {
93
85
  async rename(key: Key, name: string): Promise<void> {
94
86
  await sendRequired(
95
87
  this.client,
96
- RENAME_ENDPOINT,
88
+ "/workspace/schematic/symbol/rename",
97
89
  { key, name },
98
90
  renameReqZ,
99
91
  emptyResZ,
@@ -106,7 +98,7 @@ export class Client {
106
98
  const isSingle = "key" in args;
107
99
  const res = await sendRequired(
108
100
  this.client,
109
- RETRIEVE_ENDPOINT,
101
+ "/workspace/schematic/symbol/retrieve",
110
102
  args,
111
103
  retrieveArgsZ,
112
104
  retrieveResZ,
@@ -118,7 +110,7 @@ export class Client {
118
110
  async delete(keys: Key | Key[]): Promise<void> {
119
111
  await sendRequired(
120
112
  this.client,
121
- DELETE_ENDPOINT,
113
+ "/workspace/schematic/symbol/delete",
122
114
  { keys: array.toArray(keys) },
123
115
  deleteReqZ,
124
116
  emptyResZ,
@@ -128,7 +120,7 @@ export class Client {
128
120
  async retrieveGroup(): Promise<group.Group> {
129
121
  const res = await sendRequired(
130
122
  this.client,
131
- RETRIEVE_GROUP_ENDPOINT,
123
+ "/workspace/schematic/symbol/retrieve_group",
132
124
  {},
133
125
  retrieveGroupReqZ,
134
126
  retrieveGroupResZ,
@@ -137,7 +129,4 @@ export class Client {
137
129
  }
138
130
  }
139
131
 
140
- export const ontologyID = (key: Key): ontology.ID => ({
141
- type: "schematic_symbol",
142
- key,
143
- });
132
+ export const ontologyID = ontology.createIDFactory<Key>("schematic_symbol");
@@ -59,7 +59,7 @@ export interface Spec extends z.infer<typeof specZ> {}
59
59
 
60
60
  export const symbolZ = z.object({
61
61
  key: keyZ,
62
- version: z.literal(1).optional().default(1),
62
+ version: z.literal(1).default(1),
63
63
  name: z.string().min(1, "Name is required"),
64
64
  data: specZ,
65
65
  });
@@ -0,0 +1,134 @@
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
+
12
+ import { AuthError, NotFoundError } from "@/errors";
13
+ import { createTestClientWithPolicy } from "@/testutil/access";
14
+ import { createTestClient } from "@/testutil/client";
15
+ import { table } from "@/workspace/table";
16
+
17
+ const client = createTestClient();
18
+
19
+ describe("table", () => {
20
+ describe("access control", () => {
21
+ it("should deny access when no retrieve policy exists", async () => {
22
+ const userClient = await createTestClientWithPolicy(client, {
23
+ name: "test",
24
+ objects: [],
25
+ actions: [],
26
+ });
27
+ const ws = await client.workspaces.create({
28
+ name: "test",
29
+ layout: {},
30
+ });
31
+ const randomTable = await client.workspaces.tables.create(ws.key, {
32
+ name: "test",
33
+ data: {},
34
+ });
35
+ await expect(
36
+ userClient.workspaces.tables.retrieve({ key: randomTable.key }),
37
+ ).rejects.toThrow(AuthError);
38
+ });
39
+
40
+ it("should allow the caller to retrieve tables with the correct policy", async () => {
41
+ const userClient = await createTestClientWithPolicy(client, {
42
+ name: "test",
43
+ objects: [table.ontologyID("")],
44
+ actions: ["retrieve"],
45
+ });
46
+ const ws = await client.workspaces.create({
47
+ name: "test",
48
+ layout: {},
49
+ });
50
+ const randomTable = await client.workspaces.tables.create(ws.key, {
51
+ name: "test",
52
+ data: {},
53
+ });
54
+ const retrieved = await userClient.workspaces.tables.retrieve({
55
+ key: randomTable.key,
56
+ });
57
+ expect(retrieved.key).toBe(randomTable.key);
58
+ expect(retrieved.name).toBe(randomTable.name);
59
+ });
60
+
61
+ it("should allow the caller to create tables with the correct policy", async () => {
62
+ const userClient = await createTestClientWithPolicy(client, {
63
+ name: "test",
64
+ objects: [table.ontologyID("")],
65
+ actions: ["create"],
66
+ });
67
+ const ws = await client.workspaces.create({
68
+ name: "test",
69
+ layout: {},
70
+ });
71
+ await userClient.workspaces.tables.create(ws.key, {
72
+ name: "test",
73
+ data: {},
74
+ });
75
+ });
76
+
77
+ it("should deny access when no create policy exists", async () => {
78
+ const userClient = await createTestClientWithPolicy(client, {
79
+ name: "test",
80
+ objects: [table.ontologyID("")],
81
+ actions: [],
82
+ });
83
+ const ws = await client.workspaces.create({
84
+ name: "test",
85
+ layout: {},
86
+ });
87
+ await expect(
88
+ userClient.workspaces.tables.create(ws.key, {
89
+ name: "test",
90
+ data: {},
91
+ }),
92
+ ).rejects.toThrow(AuthError);
93
+ });
94
+
95
+ it("should allow the caller to delete tables with the correct policy", async () => {
96
+ const userClient = await createTestClientWithPolicy(client, {
97
+ name: "test",
98
+ objects: [table.ontologyID("")],
99
+ actions: ["delete", "retrieve"],
100
+ });
101
+ const ws = await client.workspaces.create({
102
+ name: "test",
103
+ layout: {},
104
+ });
105
+ const randomTable = await client.workspaces.tables.create(ws.key, {
106
+ name: "test",
107
+ data: {},
108
+ });
109
+ await userClient.workspaces.tables.delete(randomTable.key);
110
+ await expect(
111
+ userClient.workspaces.tables.retrieve({ key: randomTable.key }),
112
+ ).rejects.toThrow(NotFoundError);
113
+ });
114
+
115
+ it("should deny access when no delete policy exists", async () => {
116
+ const userClient = await createTestClientWithPolicy(client, {
117
+ name: "test",
118
+ objects: [table.ontologyID("")],
119
+ actions: [],
120
+ });
121
+ const ws = await client.workspaces.create({
122
+ name: "test",
123
+ layout: {},
124
+ });
125
+ const randomTable = await client.workspaces.tables.create(ws.key, {
126
+ name: "test",
127
+ data: {},
128
+ });
129
+ await expect(
130
+ userClient.workspaces.tables.delete(randomTable.key),
131
+ ).rejects.toThrow(AuthError);
132
+ });
133
+ });
134
+ });