@synnaxlabs/client 0.20.0 → 0.22.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 (262) hide show
  1. package/.turbo/turbo-build.log +7 -7
  2. package/.vscode/settings.json +3 -0
  3. package/README.md +13 -0
  4. package/dist/auth/auth.d.ts +7 -6
  5. package/dist/auth/auth.d.ts.map +1 -1
  6. package/dist/channel/client.d.ts +14 -11
  7. package/dist/channel/client.d.ts.map +1 -1
  8. package/dist/channel/creator.d.ts +10 -0
  9. package/dist/channel/creator.d.ts.map +1 -0
  10. package/dist/channel/payload.d.ts +6 -2
  11. package/dist/channel/payload.d.ts.map +1 -1
  12. package/dist/channel/retriever.d.ts +65 -35
  13. package/dist/channel/retriever.d.ts.map +1 -1
  14. package/dist/channel/writer.d.ts +4 -3
  15. package/dist/client.cjs +19 -22
  16. package/dist/client.cjs.map +1 -1
  17. package/dist/client.d.ts +12 -10
  18. package/dist/client.d.ts.map +1 -1
  19. package/dist/client.js +6781 -6675
  20. package/dist/client.js.map +1 -1
  21. package/dist/connection/checker.d.ts +4 -3
  22. package/dist/connection/checker.d.ts.map +1 -1
  23. package/dist/control/authority.d.ts +2 -1
  24. package/dist/control/state.d.ts +6 -4
  25. package/dist/control/state.d.ts.map +1 -1
  26. package/dist/errors.d.ts +24 -20
  27. package/dist/errors.d.ts.map +1 -1
  28. package/dist/framer/adapter.d.ts +5 -4
  29. package/dist/framer/adapter.d.ts.map +1 -1
  30. package/dist/framer/client.d.ts +12 -12
  31. package/dist/framer/client.d.ts.map +1 -1
  32. package/dist/framer/frame.d.ts +13 -12
  33. package/dist/framer/frame.d.ts.map +1 -1
  34. package/dist/framer/iterator.d.ts +5 -4
  35. package/dist/framer/iterator.d.ts.map +1 -1
  36. package/dist/framer/streamProxy.d.ts +3 -2
  37. package/dist/framer/streamProxy.d.ts.map +1 -1
  38. package/dist/framer/streamer.d.ts +13 -4
  39. package/dist/framer/streamer.d.ts.map +1 -1
  40. package/dist/framer/writer.d.ts +67 -33
  41. package/dist/framer/writer.d.ts.map +1 -1
  42. package/dist/hardware/client.d.ts +4 -3
  43. package/dist/hardware/device/client.d.ts +107 -14
  44. package/dist/hardware/device/client.d.ts.map +1 -1
  45. package/dist/hardware/device/external.d.ts +0 -2
  46. package/dist/hardware/device/external.d.ts.map +1 -1
  47. package/dist/hardware/device/index.d.ts +1 -1
  48. package/dist/hardware/device/index.d.ts.map +1 -1
  49. package/dist/hardware/rack/client.d.ts +43 -20
  50. package/dist/hardware/rack/client.d.ts.map +1 -1
  51. package/dist/hardware/rack/external.d.ts +0 -2
  52. package/dist/hardware/rack/external.d.ts.map +1 -1
  53. package/dist/hardware/task/client.d.ts +197 -14
  54. package/dist/hardware/task/client.d.ts.map +1 -1
  55. package/dist/hardware/task/index.d.ts +1 -1
  56. package/dist/hardware/task/index.d.ts.map +1 -1
  57. package/dist/index.d.ts +4 -3
  58. package/dist/index.d.ts.map +1 -1
  59. package/dist/label/client.d.ts +8 -6
  60. package/dist/label/client.d.ts.map +1 -1
  61. package/dist/label/payload.d.ts +2 -1
  62. package/dist/label/retriever.d.ts +3 -2
  63. package/dist/label/writer.d.ts +4 -3
  64. package/dist/ontology/client.d.ts +82 -14
  65. package/dist/ontology/client.d.ts.map +1 -1
  66. package/dist/ontology/external.d.ts +0 -1
  67. package/dist/ontology/external.d.ts.map +1 -1
  68. package/dist/ontology/group/client.d.ts +3 -2
  69. package/dist/ontology/group/client.d.ts.map +1 -1
  70. package/dist/ontology/group/group.d.ts +1 -0
  71. package/dist/ontology/group/payload.d.ts +2 -1
  72. package/dist/ontology/group/writer.d.ts +4 -3
  73. package/dist/ontology/payload.d.ts +71 -60
  74. package/dist/ontology/payload.d.ts.map +1 -1
  75. package/dist/ontology/writer.d.ts +3 -2
  76. package/dist/ranger/active.d.ts +3 -2
  77. package/dist/ranger/alias.d.ts +7 -6
  78. package/dist/ranger/alias.d.ts.map +1 -1
  79. package/dist/ranger/client.d.ts +162 -11
  80. package/dist/ranger/client.d.ts.map +1 -1
  81. package/dist/ranger/external.d.ts +0 -1
  82. package/dist/ranger/external.d.ts.map +1 -1
  83. package/dist/ranger/kv.d.ts +5 -3
  84. package/dist/ranger/kv.d.ts.map +1 -1
  85. package/dist/ranger/payload.d.ts +57 -50
  86. package/dist/ranger/payload.d.ts.map +1 -1
  87. package/dist/ranger/range.d.ts +12 -10
  88. package/dist/ranger/range.d.ts.map +1 -1
  89. package/dist/ranger/writer.d.ts +3 -2
  90. package/dist/setupspecs.d.ts +2 -1
  91. package/dist/signals/observable.d.ts +8 -15
  92. package/dist/signals/observable.d.ts.map +1 -1
  93. package/dist/transport.d.ts +3 -2
  94. package/dist/transport.d.ts.map +1 -1
  95. package/dist/user/payload.d.ts +2 -1
  96. package/dist/util/retrieve.d.ts +24 -0
  97. package/dist/util/retrieve.d.ts.map +1 -0
  98. package/dist/util/retrieve.spec.d.ts +2 -0
  99. package/dist/util/retrieve.spec.d.ts.map +1 -0
  100. package/dist/util/telem.d.ts +2 -1
  101. package/dist/util/telem.d.ts.map +1 -1
  102. package/dist/util/zod.d.ts +4 -0
  103. package/dist/util/zod.d.ts.map +1 -0
  104. package/dist/workspace/client.d.ts +9 -6
  105. package/dist/workspace/client.d.ts.map +1 -1
  106. package/dist/workspace/lineplot/client.d.ts +6 -5
  107. package/dist/workspace/lineplot/client.d.ts.map +1 -1
  108. package/dist/workspace/lineplot/payload.d.ts +3 -2
  109. package/dist/workspace/lineplot/payload.d.ts.map +1 -1
  110. package/dist/workspace/lineplot/retriever.d.ts +3 -2
  111. package/dist/workspace/lineplot/retriever.d.ts.map +1 -1
  112. package/dist/workspace/lineplot/writer.d.ts +7 -6
  113. package/dist/workspace/lineplot/writer.d.ts.map +1 -1
  114. package/dist/workspace/payload.d.ts +3 -2
  115. package/dist/workspace/payload.d.ts.map +1 -1
  116. package/dist/workspace/retriever.d.ts +3 -2
  117. package/dist/workspace/schematic/client.d.ts +18 -0
  118. package/dist/workspace/schematic/client.d.ts.map +1 -0
  119. package/dist/workspace/schematic/external.d.ts.map +1 -0
  120. package/dist/workspace/schematic/index.d.ts +2 -0
  121. package/dist/workspace/schematic/index.d.ts.map +1 -0
  122. package/dist/workspace/{pid → schematic}/payload.d.ts +6 -5
  123. package/dist/workspace/schematic/payload.d.ts.map +1 -0
  124. package/dist/workspace/schematic/retriever.d.ts +10 -0
  125. package/dist/workspace/schematic/retriever.d.ts.map +1 -0
  126. package/dist/workspace/schematic/schematic.spec.d.ts +2 -0
  127. package/dist/workspace/schematic/schematic.spec.d.ts.map +1 -0
  128. package/dist/workspace/{pid → schematic}/writer.d.ts +11 -10
  129. package/dist/workspace/schematic/writer.d.ts.map +1 -0
  130. package/dist/workspace/writer.d.ts +5 -4
  131. package/dist/workspace/writer.d.ts.map +1 -1
  132. package/examples/node/package-lock.json +29 -12
  133. package/examples/node/package.json +2 -2
  134. package/examples/node/streamWrite.js +8 -11
  135. package/package.json +10 -9
  136. package/src/auth/auth.spec.ts +55 -15
  137. package/src/auth/auth.ts +41 -42
  138. package/src/channel/batchRetriever.spec.ts +37 -40
  139. package/src/channel/channel.spec.ts +4 -4
  140. package/src/channel/client.ts +42 -49
  141. package/src/channel/creator.ts +37 -0
  142. package/src/channel/payload.ts +2 -1
  143. package/src/channel/retriever.ts +55 -71
  144. package/src/client.ts +23 -20
  145. package/src/connection/checker.ts +1 -1
  146. package/src/connection/connection.spec.ts +1 -6
  147. package/src/control/state.ts +3 -1
  148. package/src/errors.ts +71 -67
  149. package/src/framer/adapter.spec.ts +33 -1
  150. package/src/framer/adapter.ts +10 -6
  151. package/src/framer/client.ts +13 -12
  152. package/src/framer/frame.spec.ts +1 -1
  153. package/src/framer/frame.ts +9 -6
  154. package/src/framer/iterator.spec.ts +1 -1
  155. package/src/framer/iterator.ts +1 -1
  156. package/src/framer/streamProxy.ts +12 -13
  157. package/src/framer/streamer.spec.ts +1 -1
  158. package/src/framer/streamer.ts +25 -1
  159. package/src/framer/writer.spec.ts +49 -2
  160. package/src/framer/writer.ts +27 -2
  161. package/src/hardware/device/client.ts +155 -28
  162. package/src/hardware/device/device.spec.ts +2 -2
  163. package/src/hardware/device/external.ts +0 -2
  164. package/src/hardware/device/index.ts +2 -2
  165. package/src/hardware/rack/client.ts +139 -56
  166. package/src/hardware/rack/external.ts +0 -2
  167. package/src/hardware/rack/rack.spec.ts +20 -1
  168. package/src/hardware/task/client.ts +324 -31
  169. package/src/hardware/task/index.ts +1 -1
  170. package/src/hardware/task/task.spec.ts +41 -0
  171. package/src/index.ts +3 -4
  172. package/src/label/client.ts +3 -2
  173. package/src/label/retriever.ts +1 -1
  174. package/src/label/writer.ts +1 -1
  175. package/src/ontology/client.ts +195 -41
  176. package/src/ontology/external.ts +0 -1
  177. package/src/ontology/group/client.ts +1 -2
  178. package/src/ontology/group/payload.ts +1 -1
  179. package/src/ontology/ontology.spec.ts +16 -0
  180. package/src/ontology/payload.ts +22 -13
  181. package/src/ranger/active.ts +5 -5
  182. package/src/ranger/alias.ts +2 -2
  183. package/src/ranger/client.ts +68 -17
  184. package/src/ranger/external.ts +0 -1
  185. package/src/ranger/kv.ts +6 -1
  186. package/src/ranger/payload.ts +6 -4
  187. package/src/ranger/range.ts +4 -1
  188. package/src/ranger/ranger.spec.ts +24 -2
  189. package/src/signals/observable.ts +24 -63
  190. package/src/transport.ts +2 -1
  191. package/src/util/retrieve.spec.ts +56 -0
  192. package/src/util/retrieve.ts +103 -0
  193. package/src/util/telem.ts +1 -1
  194. package/src/util/zod.ts +4 -0
  195. package/src/workspace/client.ts +6 -4
  196. package/src/workspace/lineplot/client.ts +3 -3
  197. package/src/workspace/lineplot/linePlot.spec.ts +11 -11
  198. package/src/workspace/lineplot/payload.ts +1 -1
  199. package/src/workspace/lineplot/retriever.ts +5 -13
  200. package/src/workspace/lineplot/writer.ts +8 -7
  201. package/src/workspace/payload.ts +6 -3
  202. package/src/workspace/retriever.ts +1 -1
  203. package/src/workspace/{pid → schematic}/client.ts +10 -10
  204. package/src/workspace/{pid → schematic}/external.ts +2 -2
  205. package/src/workspace/{pid → schematic}/index.ts +1 -1
  206. package/src/workspace/{pid → schematic}/payload.ts +4 -4
  207. package/src/workspace/{pid → schematic}/retriever.ts +10 -10
  208. package/src/workspace/{pid/pid.spec.ts → schematic/schematic.spec.ts} +35 -35
  209. package/src/workspace/{pid → schematic}/writer.ts +26 -25
  210. package/src/workspace/workspace.spec.ts +7 -7
  211. package/src/workspace/writer.ts +8 -2
  212. package/dist/hardware/device/payload.d.ts +0 -30
  213. package/dist/hardware/device/payload.d.ts.map +0 -1
  214. package/dist/hardware/device/retriever.d.ts +0 -10
  215. package/dist/hardware/device/retriever.d.ts.map +0 -1
  216. package/dist/hardware/device/writer.d.ts +0 -9
  217. package/dist/hardware/device/writer.d.ts.map +0 -1
  218. package/dist/hardware/rack/payload.d.ts +0 -26
  219. package/dist/hardware/rack/payload.d.ts.map +0 -1
  220. package/dist/hardware/rack/retriever.d.ts +0 -10
  221. package/dist/hardware/rack/retriever.d.ts.map +0 -1
  222. package/dist/hardware/rack/writer.d.ts +0 -9
  223. package/dist/hardware/rack/writer.d.ts.map +0 -1
  224. package/dist/hardware/task/external.d.ts +0 -4
  225. package/dist/hardware/task/external.d.ts.map +0 -1
  226. package/dist/hardware/task/payload.d.ts +0 -42
  227. package/dist/hardware/task/payload.d.ts.map +0 -1
  228. package/dist/hardware/task/retriever.d.ts +0 -29
  229. package/dist/hardware/task/retriever.d.ts.map +0 -1
  230. package/dist/hardware/task/writer.d.ts +0 -9
  231. package/dist/hardware/task/writer.d.ts.map +0 -1
  232. package/dist/ontology/retriever.d.ts +0 -13
  233. package/dist/ontology/retriever.d.ts.map +0 -1
  234. package/dist/ontology/signals.d.ts +0 -30
  235. package/dist/ontology/signals.d.ts.map +0 -1
  236. package/dist/ranger/retriever.d.ts +0 -11
  237. package/dist/ranger/retriever.d.ts.map +0 -1
  238. package/dist/workspace/pid/client.d.ts +0 -17
  239. package/dist/workspace/pid/client.d.ts.map +0 -1
  240. package/dist/workspace/pid/external.d.ts.map +0 -1
  241. package/dist/workspace/pid/index.d.ts +0 -2
  242. package/dist/workspace/pid/index.d.ts.map +0 -1
  243. package/dist/workspace/pid/payload.d.ts.map +0 -1
  244. package/dist/workspace/pid/pid.spec.d.ts +0 -2
  245. package/dist/workspace/pid/pid.spec.d.ts.map +0 -1
  246. package/dist/workspace/pid/retriever.d.ts +0 -9
  247. package/dist/workspace/pid/retriever.d.ts.map +0 -1
  248. package/dist/workspace/pid/writer.d.ts.map +0 -1
  249. package/src/hardware/device/payload.ts +0 -27
  250. package/src/hardware/device/retriever.ts +0 -60
  251. package/src/hardware/device/writer.ts +0 -59
  252. package/src/hardware/rack/payload.ts +0 -26
  253. package/src/hardware/rack/retriever.ts +0 -68
  254. package/src/hardware/rack/writer.ts +0 -59
  255. package/src/hardware/task/external.ts +0 -12
  256. package/src/hardware/task/payload.ts +0 -40
  257. package/src/hardware/task/retriever.ts +0 -70
  258. package/src/hardware/task/writer.ts +0 -65
  259. package/src/ontology/retriever.ts +0 -91
  260. package/src/ontology/signals.ts +0 -139
  261. package/src/ranger/retriever.ts +0 -50
  262. /package/dist/workspace/{pid → schematic}/external.d.ts +0 -0
@@ -8,7 +8,9 @@
8
8
  // included in the file licenses/APL.txt.
9
9
 
10
10
  import { describe, expect, it } from "vitest";
11
+ import { ZodError } from "zod";
11
12
 
13
+ import { NotFoundError } from "@/errors";
12
14
  import { newClient } from "@/setupspecs";
13
15
 
14
16
  const client = newClient();
@@ -19,6 +21,10 @@ describe("Rack", () => {
19
21
  const r = await client.hardware.racks.create({ name: "test" });
20
22
  expect(r.key).toBeGreaterThan(0n);
21
23
  });
24
+ it("should return an error if the rack doesn't have a name", async () => {
25
+ // @ts-expect-error
26
+ await expect(client.hardware.racks.create({})).rejects.toThrow(ZodError);
27
+ });
22
28
  });
23
29
  describe("retrieve", () => {
24
30
  it("should retrieve a rack by its key", async () => {
@@ -27,6 +33,13 @@ describe("Rack", () => {
27
33
  expect(retrieved.key).toBe(r.key);
28
34
  expect(retrieved.name).toBe("test");
29
35
  });
36
+ it("should retrieve a rack by its name", async () => {
37
+ const name = `TimeStamp.now().toString()}-${Math.random()}`;
38
+ const r = await client.hardware.racks.create({ name });
39
+ const retrieved = await client.hardware.racks.retrieve(name);
40
+ expect(retrieved.key).toBe(r.key);
41
+ expect(retrieved.name).toEqual(name);
42
+ });
30
43
  });
31
44
  describe("tasks", () => {
32
45
  it("should list the tasks on a rack", async () => {
@@ -34,5 +47,11 @@ describe("Rack", () => {
34
47
  const tasks = await r.listTasks();
35
48
  expect(tasks).toHaveLength(0);
36
49
  });
37
- })
50
+ it("should throw an error if a task cannot be found by name", async () => {
51
+ const r = await client.hardware.racks.create({ name: "test" });
52
+ await expect(
53
+ async () => await r.retrieveTaskByName("nonexistent"),
54
+ ).rejects.toThrow(NotFoundError);
55
+ });
56
+ });
38
57
  });
@@ -7,51 +7,344 @@
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 { AsyncTermSearcher, toArray } from "@synnaxlabs/x";
10
+ import { type UnaryClient, sendRequired } from "@synnaxlabs/freighter";
11
+ import { binary, type observe } from "@synnaxlabs/x";
12
+ import { type UnknownRecord } from "@synnaxlabs/x/record";
13
+ import { type AsyncTermSearcher } from "@synnaxlabs/x/search";
14
+ import { TimeSpan, type CrudeTimeSpan } from "@synnaxlabs/x/telem";
15
+ import { toArray } from "@synnaxlabs/x/toArray";
16
+ import { nanoid } from "nanoid";
17
+ import { z } from "zod";
11
18
 
12
- import { TaskKey, type NewTask, type Task } from "@/hardware/task/payload";
13
- import { type RetrieveRequest, type Retriever } from "@/hardware/task/retriever";
14
- import { type Writer } from "@/hardware/task/writer";
19
+ import { framer } from "@/framer";
20
+ import { type Frame } from "@/framer/frame";
21
+ import { rack } from "@/hardware/rack";
22
+ import { analyzeParams, checkForMultipleOrNoResults } from "@/util/retrieve";
23
+ import { nullableArrayZ } from "@/util/zod";
15
24
 
16
- export class Client implements AsyncTermSearcher<string, TaskKey, Task> {
17
- private readonly retriever: Retriever;
18
- private readonly writer: Writer;
25
+ const TASK_STATE_CHANNEL = "sy_task_state";
26
+ const TASK_CMD_CHANNEL = "sy_task_cmd";
19
27
 
20
- constructor(retriever: Retriever, writer: Writer) {
21
- this.retriever = retriever;
22
- this.writer = writer;
28
+ export const taskKeyZ = z.union([
29
+ z.string(),
30
+ z.bigint().transform((k) => k.toString()),
31
+ z.number().transform((k) => k.toString()),
32
+ ]);
33
+
34
+ export type TaskKey = z.infer<typeof taskKeyZ>;
35
+
36
+ export const stateZ = z.object({
37
+ task: taskKeyZ,
38
+ variant: z.string(),
39
+ key: z.string().optional(),
40
+ details: z
41
+ .record(z.unknown())
42
+ .or(
43
+ z.string().transform((c) => {
44
+ if (c === "") return {};
45
+ return JSON.parse(c);
46
+ }),
47
+ )
48
+ .or(z.array(z.unknown()))
49
+ .or(z.null()),
50
+ });
51
+
52
+ export type State<D extends {} = UnknownRecord> = Omit<
53
+ z.infer<typeof stateZ>,
54
+ "details"
55
+ > & {
56
+ details?: D;
57
+ };
58
+
59
+ export const taskZ = z.object({
60
+ key: taskKeyZ,
61
+ name: z.string(),
62
+ type: z.string(),
63
+ config: z.record(z.unknown()).or(
64
+ z.string().transform((c) => {
65
+ if (c === "") return {};
66
+ return binary.JSON_ECD.decodeString(c);
67
+ }),
68
+ ) as z.ZodType<UnknownRecord>,
69
+ state: stateZ.optional().nullable(),
70
+ });
71
+
72
+ export const newTaskZ = taskZ.omit({ key: true }).extend({
73
+ key: taskKeyZ.transform((k) => k.toString()).optional(),
74
+ config: z.unknown().transform((c) => binary.JSON_ECD.encodeString(c)),
75
+ });
76
+
77
+ export type NewTask<
78
+ C extends UnknownRecord = UnknownRecord,
79
+ T extends string = string,
80
+ > = Omit<z.input<typeof newTaskZ>, "config" | "state"> & {
81
+ type: T;
82
+ config: C;
83
+ };
84
+
85
+ export type Payload<
86
+ C extends UnknownRecord = UnknownRecord,
87
+ D extends {} = UnknownRecord,
88
+ T extends string = string,
89
+ > = Omit<z.output<typeof taskZ>, "config" | "type" | "state"> & {
90
+ type: T;
91
+ config: C;
92
+ state?: State<D> | null;
93
+ };
94
+
95
+ export const commandZ = z.object({
96
+ task: taskKeyZ,
97
+ type: z.string(),
98
+ key: z.string(),
99
+ args: z.record(z.unknown()).or(
100
+ z.string().transform((c) => {
101
+ if (c === "") return {};
102
+ return JSON.parse(c);
103
+ }),
104
+ ) as z.ZodType<UnknownRecord>,
105
+ });
106
+
107
+ export type StateObservable<D extends UnknownRecord = UnknownRecord> =
108
+ observe.ObservableAsyncCloseable<State<D>>;
109
+
110
+ export class Task<
111
+ C extends UnknownRecord = UnknownRecord,
112
+ D extends {} = UnknownRecord,
113
+ T extends string = string,
114
+ > {
115
+ readonly key: TaskKey;
116
+ readonly name: string;
117
+ readonly type: T;
118
+ readonly config: C;
119
+ readonly state?: State<D>;
120
+ private readonly frameClient: framer.Client;
121
+
122
+ constructor(
123
+ key: TaskKey,
124
+ name: string,
125
+ type: T,
126
+ config: C,
127
+ frameClient: framer.Client,
128
+ state?: State<D> | null,
129
+ ) {
130
+ this.key = key;
131
+ this.name = name;
132
+ this.type = type;
133
+ this.config = config;
134
+ if (state !== null) this.state = state;
135
+ this.frameClient = frameClient;
23
136
  }
24
137
 
25
- async search(term: string): Promise<Task[]> {
26
- const res = await this.retriever.search(term);
27
- return res;
138
+ get payload(): Payload<C, D> {
139
+ return {
140
+ key: this.key,
141
+ name: this.name,
142
+ type: this.type,
143
+ config: this.config,
144
+ state: this.state,
145
+ };
28
146
  }
29
147
 
30
- async page(offset: number, limit: number): Promise<Task[]> {
31
- const res = await this.retriever.page(offset, limit);
148
+ async executeCommand(type: string, args?: UnknownRecord): Promise<string> {
149
+ const writer = await this.frameClient.openWriter(TASK_CMD_CHANNEL);
150
+ const key = nanoid();
151
+ await writer.write(TASK_CMD_CHANNEL, [{ task: this.key, type, key, args }]);
152
+ await writer.close();
153
+ return key;
154
+ }
155
+
156
+ async executeCommandSync<D extends UnknownRecord = UnknownRecord>(
157
+ type: string,
158
+ args: UnknownRecord,
159
+ timeout: CrudeTimeSpan,
160
+ ): Promise<State<D>> {
161
+ const streamer = await this.frameClient.openStreamer(TASK_STATE_CHANNEL);
162
+ const cmdKey = await this.executeCommand(type, args);
163
+ let res: State<D>;
164
+ const to = new Promise((resolve) =>
165
+ setTimeout(() => resolve(false), new TimeSpan(timeout).milliseconds),
166
+ );
167
+ while (true) {
168
+ const frame = (await Promise.any([streamer.read(), to])) as Frame | false;
169
+ if (frame === false) throw new Error("Command timed out");
170
+ const parsed = stateZ.safeParse(frame.at(-1).sy_task_state);
171
+ if (parsed.success) {
172
+ res = parsed.data as State<D>;
173
+ if (res.key === cmdKey) break;
174
+ } else {
175
+ console.error(parsed.error);
176
+ }
177
+ }
178
+ streamer.close();
32
179
  return res;
33
180
  }
34
181
 
35
- async create(task: NewTask): Promise<Task> {
36
- const res = await this.writer.create([task]);
37
- return res[0];
182
+ async openStateObserver<D extends UnknownRecord = UnknownRecord>(): Promise<
183
+ StateObservable<D>
184
+ > {
185
+ return new framer.ObservableStreamer<State<D>>(
186
+ await this.frameClient.openStreamer(TASK_STATE_CHANNEL),
187
+ (frame) => {
188
+ const s = frame.get(TASK_STATE_CHANNEL);
189
+ if (s.length === 0) return [null, false];
190
+ const parse = stateZ.safeParse(s.at(-1));
191
+ if (!parse.success) return [null, false];
192
+ const state = parse.data as State<D>;
193
+ if (state.task !== this.key) return [null, false];
194
+ return [state, true];
195
+ },
196
+ );
197
+ }
198
+ }
199
+
200
+ const retrieveReqZ = z.object({
201
+ rack: rack.rackKeyZ.optional(),
202
+ keys: z.string().array().optional(),
203
+ names: z.string().array().optional(),
204
+ offset: z.number().optional(),
205
+ limit: z.number().optional(),
206
+ includeState: z.boolean().optional(),
207
+ });
208
+
209
+ const retrieveResZ = z.object({
210
+ tasks: nullableArrayZ(taskZ),
211
+ });
212
+
213
+ export type RetrieveRequest = z.infer<typeof retrieveReqZ>;
214
+
215
+ export type RetrieveOptions = Pick<
216
+ RetrieveRequest,
217
+ "rack" | "offset" | "limit" | "includeState"
218
+ >;
219
+
220
+ const RETRIEVE_ENDPOINT = "/hardware/task/retrieve";
221
+ const CREATE_ENDPOINT = "/hardware/task/create";
222
+ const DELETE_ENDPOINT = "/hardware/task/delete";
223
+
224
+ const createReqZ = z.object({ tasks: newTaskZ.array() });
225
+ const createResZ = z.object({ tasks: taskZ.array() });
226
+ const deleteReqZ = z.object({ keys: taskKeyZ.array() });
227
+ const deleteResZ = z.object({});
228
+
229
+ export class Client implements AsyncTermSearcher<string, TaskKey, Payload> {
230
+ readonly type: string = "task";
231
+ private readonly client: UnaryClient;
232
+ private readonly frameClient: framer.Client;
233
+
234
+ constructor(client: UnaryClient, frameClient: framer.Client) {
235
+ this.client = client;
236
+ this.frameClient = frameClient;
237
+ }
238
+
239
+ async create<
240
+ C extends UnknownRecord = UnknownRecord,
241
+ D extends {} = UnknownRecord,
242
+ T extends string = string,
243
+ >(task: NewTask<C, T>): Promise<Task<C, D, T>>;
244
+
245
+ async create<
246
+ C extends UnknownRecord = UnknownRecord,
247
+ D extends {} = UnknownRecord,
248
+ T extends string = string,
249
+ >(tasks: NewTask<C, T>[]): Promise<Task<C, D, T>[]>;
250
+
251
+ async create<
252
+ C extends UnknownRecord = UnknownRecord,
253
+ D extends {} = UnknownRecord,
254
+ T extends string = string,
255
+ >(
256
+ task: NewTask<C, T> | Array<NewTask<C, T>>,
257
+ ): Promise<Task<C, D, T> | Array<Task<C, D, T>>> {
258
+ const isSingle = !Array.isArray(task);
259
+ const res = await sendRequired<typeof createReqZ, typeof createResZ>(
260
+ this.client,
261
+ CREATE_ENDPOINT,
262
+ { tasks: toArray(task) },
263
+ createReqZ,
264
+ createResZ,
265
+ );
266
+ const sugared = this.sugar(res.tasks) as Array<Task<C, D, T>>;
267
+ return isSingle ? sugared[0] : sugared;
268
+ }
269
+
270
+ async delete(keys: bigint | bigint[]): Promise<void> {
271
+ await sendRequired<typeof deleteReqZ, typeof deleteResZ>(
272
+ this.client,
273
+ DELETE_ENDPOINT,
274
+ { keys: toArray(keys) },
275
+ deleteReqZ,
276
+ deleteResZ,
277
+ );
38
278
  }
39
279
 
40
- async retrieve(rack: number): Promise<Task[]>;
280
+ async search(term: string): Promise<Payload[]> {
281
+ return await this.execRetrieve({ keys: [term] });
282
+ }
283
+
284
+ async page(offset: number, limit: number): Promise<Payload[]> {
285
+ return this.execRetrieve({ offset, limit });
286
+ }
287
+
288
+ async retrieve<
289
+ C extends UnknownRecord = UnknownRecord,
290
+ D extends {} = UnknownRecord,
291
+ T extends string = string,
292
+ >(rack: number, options?: RetrieveOptions): Promise<Task<C, D, T>[]>;
293
+
294
+ async retrieve<
295
+ C extends UnknownRecord = UnknownRecord,
296
+ D extends {} = UnknownRecord,
297
+ T extends string = string,
298
+ >(keys: string[], options?: RetrieveOptions): Promise<Task<C, D, T>[]>;
41
299
 
42
- async retrieve(keys: string[]): Promise<Task[]>;
300
+ async retrieve<
301
+ C extends UnknownRecord = UnknownRecord,
302
+ D extends {} = UnknownRecord,
303
+ T extends string = string,
304
+ >(key: string, options?: RetrieveOptions): Promise<Task<C, D, T>>;
43
305
 
44
- async retrieve(key: string): Promise<Task>;
306
+ async retrieve<
307
+ C extends UnknownRecord = UnknownRecord,
308
+ D extends {} = UnknownRecord,
309
+ T extends string = string,
310
+ >(
311
+ rack: number | string | string[],
312
+ options?: RetrieveOptions,
313
+ ): Promise<Task<C, D, T> | Task<C, D, T>[]> {
314
+ const { single, normalized, variant } = analyzeParams(
315
+ rack,
316
+ { number: "rack", string: "keys" },
317
+ { convertNumericStrings: false },
318
+ );
319
+ let req: RetrieveRequest = { ...options };
320
+ if (variant === "rack") req.rack = rack as number;
321
+ else req.keys = normalized as string[];
322
+ const tasks = await this.execRetrieve(req);
323
+ const sugared = this.sugar(tasks) as Array<Task<C, D, T>>;
324
+ return single && variant !== "rack" ? sugared[0] : sugared;
325
+ }
326
+
327
+ async retrieveByName(name: string, rack?: number): Promise<Task> {
328
+ const tasks = await this.execRetrieve({ names: [name], rack });
329
+ checkForMultipleOrNoResults("Task", name, tasks, true);
330
+ return this.sugar(tasks)[0];
331
+ }
332
+
333
+ private async execRetrieve(req: RetrieveRequest): Promise<Payload[]> {
334
+ const res = await sendRequired(
335
+ this.client,
336
+ RETRIEVE_ENDPOINT,
337
+ req,
338
+ retrieveReqZ,
339
+ retrieveResZ,
340
+ );
341
+ return res.tasks;
342
+ }
45
343
 
46
- async retrieve(rack: number | string | string[]): Promise<Task | Task[]> {
47
- const params: RetrieveRequest = {};
48
- let multi: boolean = true;
49
- if (typeof rack === "number") params.rack = rack;
50
- else if (typeof rack === "string") {
51
- multi = false;
52
- params.keys = [rack];
53
- } else params.keys = toArray(rack);
54
- const res = await this.retriever.retrieve(params);
55
- return multi ? res : res[0];
344
+ private sugar(payloads: Payload[]): Task[] {
345
+ return payloads.map(
346
+ ({ key, name, type, config, state }) =>
347
+ new Task(key, name, type, config, this.frameClient, state),
348
+ );
56
349
  }
57
350
  }
@@ -7,4 +7,4 @@
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
- export * as task from "@/hardware/task/external";
10
+ export * as task from "@/hardware/task/client";
@@ -10,6 +10,8 @@
10
10
  import { describe, expect, it } from "vitest";
11
11
 
12
12
  import { newClient } from "@/setupspecs";
13
+ import { task } from "@/hardware/task";
14
+ import { nanoid } from "nanoid";
13
15
 
14
16
  const client = newClient();
15
17
 
@@ -43,5 +45,44 @@ describe("Hardware", () => {
43
45
  expect(retrieved.type).toBe("ni");
44
46
  });
45
47
  });
48
+ describe("retrieveByName", () => {
49
+ it("should retrieve a task by its name", async () => {
50
+ const name = `test-${Date.now()}-${Math.random()}`;
51
+ const r = await client.hardware.racks.create({ name });
52
+ const m = await r.createTask({
53
+ name,
54
+ config: { a: "dog" },
55
+ type: "ni",
56
+ });
57
+ const retrieved = await client.hardware.tasks.retrieveByName(name);
58
+ expect(retrieved.key).toBe(m.key);
59
+ });
60
+ });
61
+ describe("retrieve with state", () => {
62
+ it("should also send the tasks state", async () => {
63
+ const r = await client.hardware.racks.create({ name: "test" });
64
+ const t = await r.createTask({
65
+ name: "test",
66
+ config: { a: "dog" },
67
+ type: "ni",
68
+ });
69
+ const w = await client.openWriter(["sy_task_state"]);
70
+ interface StateDetails {
71
+ dog: string;
72
+ }
73
+ const state: task.State<StateDetails> = {
74
+ key: nanoid(),
75
+ task: t.key,
76
+ variant: "success",
77
+ };
78
+ expect(await w.write("sy_task_state", [state])).toBeTruthy();
79
+ await w.close();
80
+ const retrieved = await client.hardware.tasks.retrieve(t.key, {
81
+ includeState: true,
82
+ });
83
+ expect(retrieved.state).not.toBeNull();
84
+ expect(retrieved.state?.variant).toBe(state.variant);
85
+ });
86
+ });
46
87
  });
47
88
  });
package/src/index.ts CHANGED
@@ -15,8 +15,6 @@ export { Channel } from "@/channel/client";
15
15
  export {
16
16
  AuthError,
17
17
  ContiguityError,
18
- GeneralError,
19
- ParseError,
20
18
  QueryError,
21
19
  RouteError,
22
20
  UnexpectedError,
@@ -36,7 +34,7 @@ export {
36
34
  TimeSpan,
37
35
  TimeStamp,
38
36
  MultiSeries,
39
- } from "@synnaxlabs/x";
37
+ } from "@synnaxlabs/x/telem";
40
38
  export type {
41
39
  TypedArray,
42
40
  CrudeDataType,
@@ -49,7 +47,7 @@ export type {
49
47
  NumericTelemValue,
50
48
  TimeStampStringFormat,
51
49
  TZInfo,
52
- } from "@synnaxlabs/x";
50
+ } from "@synnaxlabs/x/telem";
53
51
  export { workspace } from "@/workspace";
54
52
  export { ranger } from "@/ranger";
55
53
  export { label } from "@/label";
@@ -57,3 +55,4 @@ export { hardware } from "@/hardware";
57
55
  export { rack } from "@/hardware/rack";
58
56
  export { task } from "@/hardware/task";
59
57
  export { device } from "@/hardware/device";
58
+ export { signals } from "@/signals";
@@ -8,7 +8,7 @@
8
8
  // included in the file licenses/APL.txt.
9
9
 
10
10
  import { type UnaryClient } from "@synnaxlabs/freighter";
11
- import { type AsyncTermSearcher } from "@synnaxlabs/x";
11
+ import { type AsyncTermSearcher } from "@synnaxlabs/x/search";
12
12
 
13
13
  import { type framer } from "@/framer";
14
14
  import { type Key, type Label, labelZ } from "@/label/payload";
@@ -21,6 +21,7 @@ const LABEL_SET_NAME = "sy_label_set";
21
21
  const LABEL_DELETE_NAME = "sy_label_delete";
22
22
 
23
23
  export class Client implements AsyncTermSearcher<string, Key, Label> {
24
+ readonly type: string = "label";
24
25
  private readonly retriever: Retriever;
25
26
  private readonly writer: Writer;
26
27
  private readonly frameClient: framer.Client;
@@ -80,7 +81,7 @@ export class Client implements AsyncTermSearcher<string, Key, Label> {
80
81
  }
81
82
 
82
83
  async openChangeTracker(): Promise<signals.Observable<string, Label>> {
83
- return await signals.Observable.open<string, Label>(
84
+ return await signals.openObservable<string, Label>(
84
85
  this.frameClient,
85
86
  LABEL_SET_NAME,
86
87
  LABEL_DELETE_NAME,
@@ -8,7 +8,7 @@
8
8
  // included in the file licenses/APL.txt.
9
9
 
10
10
  import { type UnaryClient } from "@synnaxlabs/freighter";
11
- import { toArray } from "@synnaxlabs/x";
11
+ import { toArray } from "@synnaxlabs/x/toArray";
12
12
  import { z } from "zod";
13
13
 
14
14
  import { keyZ, type Label, labelZ, type Params } from "@/label/payload";
@@ -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 { toArray } from "@synnaxlabs/x";
11
+ import { toArray } from "@synnaxlabs/x/toArray";
12
12
  import { z } from "zod";
13
13
 
14
14
  import { type Key, keyZ, type Label, labelZ } from "@/label/payload";