@synnaxlabs/client 0.43.0 → 0.44.1

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 (245) hide show
  1. package/.turbo/turbo-build.log +7 -7
  2. package/dist/access/payload.d.ts +1 -1
  3. package/dist/access/payload.d.ts.map +1 -1
  4. package/dist/access/policy/client.d.ts +263 -6
  5. package/dist/access/policy/client.d.ts.map +1 -1
  6. package/dist/access/policy/external.d.ts +0 -1
  7. package/dist/access/policy/external.d.ts.map +1 -1
  8. package/dist/access/policy/payload.d.ts +105 -93
  9. package/dist/access/policy/payload.d.ts.map +1 -1
  10. package/dist/auth/auth.d.ts +1 -1
  11. package/dist/auth/auth.d.ts.map +1 -1
  12. package/dist/channel/client.d.ts +12 -13
  13. package/dist/channel/client.d.ts.map +1 -1
  14. package/dist/channel/payload.d.ts +77 -19
  15. package/dist/channel/payload.d.ts.map +1 -1
  16. package/dist/channel/retriever.d.ts +9 -16
  17. package/dist/channel/retriever.d.ts.map +1 -1
  18. package/dist/channel/writer.d.ts +1 -1
  19. package/dist/channel/writer.d.ts.map +1 -1
  20. package/dist/client.cjs +27 -135
  21. package/dist/client.d.ts +3 -3
  22. package/dist/client.d.ts.map +1 -1
  23. package/dist/client.js +8619 -28938
  24. package/dist/connection/checker.d.ts +1 -1
  25. package/dist/connection/checker.d.ts.map +1 -1
  26. package/dist/control/client.d.ts +1 -0
  27. package/dist/control/client.d.ts.map +1 -1
  28. package/dist/control/state.d.ts +6 -6
  29. package/dist/control/state.d.ts.map +1 -1
  30. package/dist/errors.d.ts +18 -5
  31. package/dist/errors.d.ts.map +1 -1
  32. package/dist/framer/adapter.d.ts +3 -3
  33. package/dist/framer/adapter.d.ts.map +1 -1
  34. package/dist/framer/client.d.ts +4 -13
  35. package/dist/framer/client.d.ts.map +1 -1
  36. package/dist/framer/codec.d.ts +1 -1
  37. package/dist/framer/codec.d.ts.map +1 -1
  38. package/dist/framer/deleter.d.ts +5 -5
  39. package/dist/framer/deleter.d.ts.map +1 -1
  40. package/dist/framer/frame.d.ts +5 -7
  41. package/dist/framer/frame.d.ts.map +1 -1
  42. package/dist/framer/streamProxy.d.ts +1 -1
  43. package/dist/framer/streamProxy.d.ts.map +1 -1
  44. package/dist/framer/streamer.d.ts +139 -20
  45. package/dist/framer/streamer.d.ts.map +1 -1
  46. package/dist/framer/writer.d.ts +222 -33
  47. package/dist/framer/writer.d.ts.map +1 -1
  48. package/dist/hardware/device/client.d.ts +49 -28
  49. package/dist/hardware/device/client.d.ts.map +1 -1
  50. package/dist/hardware/device/payload.d.ts +126 -46
  51. package/dist/hardware/device/payload.d.ts.map +1 -1
  52. package/dist/hardware/rack/client.d.ts +78 -22
  53. package/dist/hardware/rack/client.d.ts.map +1 -1
  54. package/dist/hardware/rack/payload.d.ts +99 -56
  55. package/dist/hardware/rack/payload.d.ts.map +1 -1
  56. package/dist/hardware/task/client.d.ts +100 -41
  57. package/dist/hardware/task/client.d.ts.map +1 -1
  58. package/dist/hardware/task/payload.d.ts +83 -61
  59. package/dist/hardware/task/payload.d.ts.map +1 -1
  60. package/dist/index.d.ts +2 -2
  61. package/dist/index.d.ts.map +1 -1
  62. package/dist/label/client.d.ts +138 -20
  63. package/dist/label/client.d.ts.map +1 -1
  64. package/dist/label/external.d.ts +0 -2
  65. package/dist/label/external.d.ts.map +1 -1
  66. package/dist/label/payload.d.ts +4 -5
  67. package/dist/label/payload.d.ts.map +1 -1
  68. package/dist/ontology/client.d.ts +45 -135
  69. package/dist/ontology/client.d.ts.map +1 -1
  70. package/dist/ontology/group/group.d.ts +3 -3
  71. package/dist/ontology/group/group.d.ts.map +1 -1
  72. package/dist/ontology/group/payload.d.ts +3 -27
  73. package/dist/ontology/group/payload.d.ts.map +1 -1
  74. package/dist/ontology/payload.d.ts +113 -243
  75. package/dist/ontology/payload.d.ts.map +1 -1
  76. package/dist/ontology/writer.d.ts +4 -4
  77. package/dist/ontology/writer.d.ts.map +1 -1
  78. package/dist/ranger/alias.d.ts +11 -5
  79. package/dist/ranger/alias.d.ts.map +1 -1
  80. package/dist/ranger/client.d.ts +87 -30
  81. package/dist/ranger/client.d.ts.map +1 -1
  82. package/dist/ranger/external.d.ts +1 -1
  83. package/dist/ranger/external.d.ts.map +1 -1
  84. package/dist/ranger/kv.d.ts +10 -12
  85. package/dist/ranger/kv.d.ts.map +1 -1
  86. package/dist/ranger/payload.d.ts +23 -44
  87. package/dist/ranger/payload.d.ts.map +1 -1
  88. package/dist/ranger/writer.d.ts +22 -19
  89. package/dist/ranger/writer.d.ts.map +1 -1
  90. package/dist/testutil/client.d.ts +4 -0
  91. package/dist/testutil/client.d.ts.map +1 -0
  92. package/dist/user/client.d.ts +59 -6
  93. package/dist/user/client.d.ts.map +1 -1
  94. package/dist/user/payload.d.ts +4 -6
  95. package/dist/user/payload.d.ts.map +1 -1
  96. package/dist/user/retriever.d.ts +2 -2
  97. package/dist/user/retriever.d.ts.map +1 -1
  98. package/dist/util/decodeJSONString.d.ts +2 -2
  99. package/dist/util/decodeJSONString.d.ts.map +1 -1
  100. package/dist/util/parseWithoutKeyConversion.d.ts +2 -2
  101. package/dist/util/parseWithoutKeyConversion.d.ts.map +1 -1
  102. package/dist/util/retrieve.d.ts +1 -1
  103. package/dist/util/retrieve.d.ts.map +1 -1
  104. package/dist/util/zod.d.ts +1 -1
  105. package/dist/util/zod.d.ts.map +1 -1
  106. package/dist/workspace/client.d.ts +17 -6
  107. package/dist/workspace/client.d.ts.map +1 -1
  108. package/dist/workspace/lineplot/client.d.ts +2 -2
  109. package/dist/workspace/lineplot/client.d.ts.map +1 -1
  110. package/dist/workspace/lineplot/payload.d.ts +8 -9
  111. package/dist/workspace/lineplot/payload.d.ts.map +1 -1
  112. package/dist/workspace/log/client.d.ts +2 -2
  113. package/dist/workspace/log/client.d.ts.map +1 -1
  114. package/dist/workspace/log/payload.d.ts +8 -9
  115. package/dist/workspace/log/payload.d.ts.map +1 -1
  116. package/dist/workspace/payload.d.ts +10 -11
  117. package/dist/workspace/payload.d.ts.map +1 -1
  118. package/dist/workspace/schematic/client.d.ts +2 -2
  119. package/dist/workspace/schematic/client.d.ts.map +1 -1
  120. package/dist/workspace/schematic/payload.d.ts +10 -11
  121. package/dist/workspace/schematic/payload.d.ts.map +1 -1
  122. package/dist/workspace/table/client.d.ts +2 -2
  123. package/dist/workspace/table/client.d.ts.map +1 -1
  124. package/dist/workspace/table/payload.d.ts +10 -11
  125. package/dist/workspace/table/payload.d.ts.map +1 -1
  126. package/examples/node/package-lock.json +47 -39
  127. package/examples/node/package.json +2 -1
  128. package/examples/node/streamWrite.js +5 -11
  129. package/package.json +14 -13
  130. package/src/access/payload.ts +1 -1
  131. package/src/access/policy/client.ts +87 -32
  132. package/src/access/policy/external.ts +0 -1
  133. package/src/access/policy/payload.ts +4 -4
  134. package/src/access/policy/policy.spec.ts +86 -83
  135. package/src/auth/auth.spec.ts +29 -18
  136. package/src/auth/auth.ts +1 -1
  137. package/src/channel/batchRetriever.spec.ts +4 -9
  138. package/src/channel/channel.spec.ts +24 -6
  139. package/src/channel/client.ts +31 -46
  140. package/src/channel/payload.ts +13 -14
  141. package/src/channel/retriever.ts +26 -41
  142. package/src/channel/writer.ts +3 -3
  143. package/src/client.ts +4 -4
  144. package/src/connection/checker.ts +1 -1
  145. package/src/connection/connection.spec.ts +31 -23
  146. package/src/control/client.ts +2 -2
  147. package/src/control/state.spec.ts +3 -3
  148. package/src/control/state.ts +1 -1
  149. package/src/errors.spec.ts +9 -5
  150. package/src/errors.ts +28 -15
  151. package/src/framer/adapter.spec.ts +118 -9
  152. package/src/framer/adapter.ts +24 -11
  153. package/src/framer/client.spec.ts +125 -2
  154. package/src/framer/client.ts +41 -47
  155. package/src/framer/codec.ts +1 -1
  156. package/src/framer/deleter.spec.ts +2 -2
  157. package/src/framer/deleter.ts +1 -1
  158. package/src/framer/frame.ts +1 -4
  159. package/src/framer/iterator.spec.ts +8 -8
  160. package/src/framer/iterator.ts +1 -1
  161. package/src/framer/streamProxy.ts +1 -1
  162. package/src/framer/streamer.spec.ts +185 -36
  163. package/src/framer/streamer.ts +28 -36
  164. package/src/framer/writer.spec.ts +6 -6
  165. package/src/framer/writer.ts +97 -111
  166. package/src/hardware/device/client.ts +45 -131
  167. package/src/hardware/device/device.spec.ts +163 -52
  168. package/src/hardware/device/payload.ts +10 -21
  169. package/src/hardware/rack/client.ts +87 -105
  170. package/src/hardware/rack/payload.ts +4 -13
  171. package/src/hardware/rack/rack.spec.ts +28 -35
  172. package/src/hardware/task/client.ts +335 -291
  173. package/src/hardware/task/payload.ts +86 -62
  174. package/src/hardware/task/task.spec.ts +208 -32
  175. package/src/index.ts +2 -1
  176. package/src/label/client.ts +100 -95
  177. package/src/label/external.ts +0 -2
  178. package/src/label/label.spec.ts +8 -6
  179. package/src/label/payload.ts +3 -4
  180. package/src/ontology/client.ts +41 -324
  181. package/src/ontology/group/group.spec.ts +2 -2
  182. package/src/ontology/group/group.ts +4 -5
  183. package/src/ontology/group/payload.ts +2 -25
  184. package/src/ontology/group/writer.ts +1 -1
  185. package/src/ontology/ontology.spec.ts +355 -41
  186. package/src/ontology/payload.ts +74 -112
  187. package/src/ontology/writer.ts +8 -17
  188. package/src/ranger/alias.ts +19 -37
  189. package/src/ranger/client.ts +118 -150
  190. package/src/ranger/external.ts +9 -1
  191. package/src/ranger/kv.ts +6 -27
  192. package/src/ranger/payload.ts +21 -37
  193. package/src/ranger/ranger.spec.ts +37 -56
  194. package/src/ranger/writer.ts +1 -1
  195. package/src/{signals/index.ts → testutil/client.ts} +11 -1
  196. package/src/user/client.ts +122 -47
  197. package/src/user/payload.ts +2 -5
  198. package/src/user/retriever.ts +1 -1
  199. package/src/user/user.spec.ts +31 -31
  200. package/src/user/writer.ts +1 -1
  201. package/src/util/decodeJSONString.ts +3 -3
  202. package/src/util/parseWithoutKeyConversion.ts +2 -2
  203. package/src/util/retrieve.ts +1 -1
  204. package/src/util/zod.ts +1 -1
  205. package/src/workspace/client.ts +20 -36
  206. package/src/workspace/lineplot/client.ts +5 -7
  207. package/src/workspace/lineplot/lineplot.spec.ts +2 -2
  208. package/src/workspace/lineplot/payload.ts +4 -7
  209. package/src/workspace/log/client.ts +5 -7
  210. package/src/workspace/log/log.spec.ts +2 -2
  211. package/src/workspace/log/payload.ts +4 -7
  212. package/src/workspace/payload.ts +4 -7
  213. package/src/workspace/schematic/client.ts +5 -7
  214. package/src/workspace/schematic/payload.ts +4 -7
  215. package/src/workspace/schematic/schematic.spec.ts +2 -2
  216. package/src/workspace/table/client.ts +5 -7
  217. package/src/workspace/table/payload.ts +4 -7
  218. package/src/workspace/table/table.spec.ts +2 -2
  219. package/src/workspace/workspace.spec.ts +2 -2
  220. package/dist/access/policy/ontology.d.ts +0 -5
  221. package/dist/access/policy/ontology.d.ts.map +0 -1
  222. package/dist/access/policy/retriever.d.ts +0 -40
  223. package/dist/access/policy/retriever.d.ts.map +0 -1
  224. package/dist/access/policy/writer.d.ts +0 -9
  225. package/dist/access/policy/writer.d.ts.map +0 -1
  226. package/dist/label/retriever.d.ts +0 -14
  227. package/dist/label/retriever.d.ts.map +0 -1
  228. package/dist/label/writer.d.ts +0 -54
  229. package/dist/label/writer.d.ts.map +0 -1
  230. package/dist/setupspecs.d.ts +0 -5
  231. package/dist/setupspecs.d.ts.map +0 -1
  232. package/dist/signals/external.d.ts +0 -2
  233. package/dist/signals/external.d.ts.map +0 -1
  234. package/dist/signals/index.d.ts +0 -2
  235. package/dist/signals/index.d.ts.map +0 -1
  236. package/dist/signals/observable.d.ts +0 -12
  237. package/dist/signals/observable.d.ts.map +0 -1
  238. package/src/access/policy/ontology.ts +0 -17
  239. package/src/access/policy/retriever.ts +0 -44
  240. package/src/access/policy/writer.ts +0 -65
  241. package/src/label/retriever.ts +0 -63
  242. package/src/label/writer.ts +0 -95
  243. package/src/setupspecs.ts +0 -27
  244. package/src/signals/external.ts +0 -10
  245. package/src/signals/observable.ts +0 -42
@@ -8,96 +8,114 @@
8
8
  // included in the file licenses/APL.txt.
9
9
 
10
10
  import { sendRequired, type UnaryClient } from "@synnaxlabs/freighter";
11
- import { id } from "@synnaxlabs/x";
11
+ import { caseconv, id } from "@synnaxlabs/x";
12
12
  import { array } from "@synnaxlabs/x/array";
13
- import { type UnknownRecord } from "@synnaxlabs/x/record";
14
- import { type AsyncTermSearcher } from "@synnaxlabs/x/search";
15
13
  import { type CrudeTimeSpan, TimeSpan } from "@synnaxlabs/x/telem";
16
- import { z } from "zod/v4";
14
+ import { z } from "zod";
17
15
 
18
- import { framer } from "@/framer";
16
+ import { type framer } from "@/framer";
19
17
  import { keyZ as rackKeyZ } from "@/hardware/rack/payload";
20
18
  import {
21
- type Command,
22
- type CommandObservable,
23
- commandZ,
24
19
  type Key,
25
20
  keyZ,
26
21
  type New,
27
22
  newZ,
28
- ONTOLOGY_TYPE,
29
23
  type Payload,
30
- type State,
31
- type StateObservable,
32
- stateZ,
24
+ type Schemas,
25
+ type Status,
26
+ statusZ,
33
27
  taskZ,
34
28
  } from "@/hardware/task/payload";
35
- import { ontology } from "@/ontology";
29
+ import { type ontology } from "@/ontology";
36
30
  import { type ranger } from "@/ranger";
37
- import { signals } from "@/signals";
38
- import { analyzeParams, checkForMultipleOrNoResults } from "@/util/retrieve";
31
+ import { checkForMultipleOrNoResults } from "@/util/retrieve";
39
32
  import { nullableArrayZ } from "@/util/zod";
40
33
 
41
- const STATE_CHANNEL_NAME = "sy_task_state";
42
- const COMMAND_CHANNEL_NAME = "sy_task_cmd";
43
- const SET_CHANNEL_NAME = "sy_task_set";
44
- const DELETE_CHANNEL_NAME = "sy_task_delete";
34
+ export const STATUS_CHANNEL_NAME = "sy_task_status";
35
+ export const COMMAND_CHANNEL_NAME = "sy_task_cmd";
36
+ export const SET_CHANNEL_NAME = "sy_task_set";
37
+ export const DELETE_CHANNEL_NAME = "sy_task_delete";
45
38
 
46
39
  const NOT_CREATED_ERROR = new Error("Task not created");
47
40
 
48
41
  const retrieveSnapshottedTo = async (taskKey: Key, ontologyClient: ontology.Client) => {
49
- const task = await ontologyClient.retrieveParents(taskKey);
50
- if (task.length === 0) return null;
51
- return task[0];
42
+ const parents = await ontologyClient.retrieveParents(ontologyID(taskKey));
43
+ if (parents.length === 0) return null;
44
+ return parents[0];
52
45
  };
53
46
 
54
47
  export class Task<
55
- Config extends UnknownRecord = UnknownRecord,
56
- Details extends {} = UnknownRecord,
57
- Type extends string = string,
48
+ Type extends z.ZodLiteral<string> = z.ZodLiteral<string>,
49
+ Config extends z.ZodType = z.ZodType,
50
+ StatusData extends z.ZodType = z.ZodType,
58
51
  > {
59
52
  readonly key: Key;
60
53
  name: string;
61
- readonly internal: boolean;
62
- readonly type: Type;
63
- config: Config;
64
- readonly snapshot: boolean;
65
- state?: State<Details>;
66
- private readonly frameClient: framer.Client | null;
67
- private readonly ontologyClient: ontology.Client | null;
68
- private readonly rangeClient: ranger.Client | null;
54
+ internal: boolean;
55
+ type: z.infer<Type>;
56
+ snapshot: boolean;
57
+ config: z.infer<Config>;
58
+ status?: Status<StatusData>;
59
+
60
+ readonly schemas: Schemas<Type, Config, StatusData>;
61
+ private readonly frameClient_?: framer.Client;
62
+ private readonly ontologyClient_?: ontology.Client;
63
+ private readonly rangeClient_?: ranger.Client;
64
+
65
+ get frameClient(): framer.Client {
66
+ if (this.frameClient_ == null) throw NOT_CREATED_ERROR;
67
+ return this.frameClient_;
68
+ }
69
+
70
+ get ontologyClient(): ontology.Client {
71
+ if (this.ontologyClient_ == null) throw NOT_CREATED_ERROR;
72
+ return this.ontologyClient_;
73
+ }
74
+
75
+ get rangeClient(): ranger.Client {
76
+ if (this.rangeClient_ == null) throw NOT_CREATED_ERROR;
77
+ return this.rangeClient_;
78
+ }
69
79
 
70
80
  constructor(
71
- key: Key,
72
- name: string,
73
- type: Type,
74
- config: Config,
75
- internal: boolean = false,
76
- snapshot: boolean = false,
77
- state?: State<Details> | null,
78
- frameClient: framer.Client | null = null,
79
- ontologyClient: ontology.Client | null = null,
80
- rangeClient: ranger.Client | null = null,
81
+ {
82
+ key,
83
+ type,
84
+ name,
85
+ config,
86
+ internal = false,
87
+ snapshot = false,
88
+ status,
89
+ }: Payload<Type, Config, StatusData>,
90
+ schemas?: Schemas<Type, Config, StatusData>,
91
+ frameClient?: framer.Client,
92
+ ontologyClient?: ontology.Client,
93
+ rangeClient?: ranger.Client,
81
94
  ) {
82
95
  this.key = key;
83
96
  this.name = name;
84
97
  this.type = type;
85
98
  this.config = config;
99
+ this.schemas = schemas ?? {
100
+ typeSchema: z.string() as unknown as Type,
101
+ configSchema: z.unknown() as unknown as Config,
102
+ statusDataSchema: z.unknown() as unknown as StatusData,
103
+ };
86
104
  this.internal = internal;
87
105
  this.snapshot = snapshot;
88
- if (state !== null) this.state = state;
89
- this.frameClient = frameClient;
90
- this.ontologyClient = ontologyClient;
91
- this.rangeClient = rangeClient;
106
+ this.status = status;
107
+ this.frameClient_ = frameClient;
108
+ this.ontologyClient_ = ontologyClient;
109
+ this.rangeClient_ = rangeClient;
92
110
  }
93
111
 
94
- get payload(): Payload<Config, Details, Type> {
112
+ get payload(): Payload<Type, Config, StatusData> {
95
113
  return {
96
114
  key: this.key,
97
115
  name: this.name,
98
116
  type: this.type,
99
117
  config: this.config,
100
- state: this.state,
118
+ status: this.status,
101
119
  internal: this.internal,
102
120
  };
103
121
  }
@@ -106,79 +124,23 @@ export class Task<
106
124
  return ontologyID(this.key);
107
125
  }
108
126
 
109
- async executeCommand<Args>(type: string, args?: Args): Promise<string> {
110
- if (this.frameClient == null) throw NOT_CREATED_ERROR;
111
- const writer = await this.frameClient.openWriter(COMMAND_CHANNEL_NAME);
112
- const key = id.create();
113
- await writer.write(COMMAND_CHANNEL_NAME, [{ task: this.key, type, key, args }]);
114
- await writer.close();
115
- return key;
127
+ async executeCommand(type: string, args?: {}): Promise<string> {
128
+ return await executeCommand(this.frameClient, this.key, type, args);
116
129
  }
117
130
 
118
- async executeCommandSync<Details extends {} = UnknownRecord>(
131
+ async executeCommandSync(
119
132
  type: string,
120
- args: UnknownRecord,
121
133
  timeout: CrudeTimeSpan,
122
- ): Promise<State<Details>> {
123
- if (this.frameClient == null) throw NOT_CREATED_ERROR;
124
- const streamer = await this.frameClient.openStreamer(STATE_CHANNEL_NAME);
125
- const cmdKey = await this.executeCommand(type, args);
126
- let res: State<Details>;
127
- const to = new Promise((resolve) =>
128
- setTimeout(() => resolve(false), new TimeSpan(timeout).milliseconds),
129
- );
130
- while (true) {
131
- const frame = (await Promise.any([streamer.read(), to])) as framer.Frame | false;
132
- if (frame === false) throw new Error("Command timed out");
133
- const parsed = stateZ.safeParse(frame.at(-1).sy_task_state);
134
- if (parsed.success) {
135
- res = parsed.data as State<Details>;
136
- if (res.key === cmdKey) break;
137
- } else throw parsed.error;
138
- }
139
- streamer.close();
140
- return res;
141
- }
142
-
143
- async openStateObserver<Details extends {} = UnknownRecord>(): Promise<
144
- StateObservable<Details>
145
- > {
146
- if (this.frameClient == null) throw NOT_CREATED_ERROR;
147
- return new framer.ObservableStreamer<State<Details>>(
148
- await this.frameClient.openStreamer(STATE_CHANNEL_NAME),
149
- (frame) => {
150
- const s = frame.get(STATE_CHANNEL_NAME);
151
- if (s.length === 0) return [null, false];
152
- const parse = stateZ.safeParse(s.at(-1));
153
- if (!parse.success) {
154
- console.error(parse.error);
155
- return [null, false];
156
- }
157
- const state = parse.data;
158
- if (state.task !== this.key) return [null, false];
159
- return [state as State<Details>, true];
160
- },
161
- );
162
- }
163
-
164
- async openCommandObserver<Args extends {} = UnknownRecord>(): Promise<
165
- CommandObservable<Args>
166
- > {
167
- if (this.frameClient == null) throw NOT_CREATED_ERROR;
168
- return new framer.ObservableStreamer<Command<Args>>(
169
- await this.frameClient.openStreamer(COMMAND_CHANNEL_NAME),
170
- (frame) => {
171
- const s = frame.get(COMMAND_CHANNEL_NAME);
172
- if (s.length === 0) return [null, false];
173
- const parse = commandZ.safeParse(s.at(-1));
174
- if (!parse.success) {
175
- console.error(parse.error);
176
- return [null, false];
177
- }
178
- const cmd = parse.data;
179
- if (cmd.task !== this.key) return [null, false];
180
- return [cmd as Command<Args>, true];
181
- },
134
+ args?: {},
135
+ ): Promise<Status<StatusData>> {
136
+ return await executeCommandSync<StatusData>(
137
+ this.frameClient,
138
+ this.key,
139
+ type,
140
+ timeout,
141
+ this.name,
142
+ this.schemas?.statusDataSchema,
143
+ args,
182
144
  );
183
145
  }
184
146
 
@@ -195,35 +157,80 @@ const retrieveReqZ = z.object({
195
157
  keys: keyZ.array().optional(),
196
158
  names: z.string().array().optional(),
197
159
  types: z.string().array().optional(),
198
- includeState: z.boolean().optional(),
160
+ includeStatus: z.boolean().optional(),
199
161
  offset: z.number().optional(),
200
162
  limit: z.number().optional(),
201
163
  });
202
164
 
203
- const retrieveResZ = z.object({ tasks: nullableArrayZ(taskZ) });
165
+ const singleRetrieveArgsZ = z.union([
166
+ z
167
+ .object({ key: keyZ, includeStatus: z.boolean().optional() })
168
+ .transform(({ key, includeStatus }) => ({ keys: [key], includeStatus })),
169
+ z
170
+ .object({ name: z.string(), includeStatus: z.boolean().optional() })
171
+ .transform(({ name, includeStatus }) => ({ names: [name], includeStatus })),
172
+ ]);
173
+ export type SingleRetrieveArgs = z.input<typeof singleRetrieveArgsZ>;
174
+
175
+ const multiRetrieveArgsZ = retrieveReqZ;
176
+ export type MultiRetrieveArgs = z.input<typeof multiRetrieveArgsZ>;
177
+
178
+ const retrieveArgsZ = z.union([singleRetrieveArgsZ, multiRetrieveArgsZ]);
179
+ export type RetrieveArgs = z.input<typeof retrieveArgsZ>;
180
+
181
+ type RetrieveSchemas<
182
+ Type extends z.ZodLiteral<string> = z.ZodLiteral<string>,
183
+ Config extends z.ZodType = z.ZodType,
184
+ StatusData extends z.ZodType = z.ZodType,
185
+ > = {
186
+ schemas?: Schemas<Type, Config, StatusData>;
187
+ };
204
188
 
205
- export interface RetrieveRequest extends z.infer<typeof retrieveReqZ> {}
189
+ const retrieveResZ = <
190
+ Type extends z.ZodLiteral<string> = z.ZodLiteral<string>,
191
+ Config extends z.ZodType = z.ZodType,
192
+ StatusData extends z.ZodType = z.ZodType,
193
+ >(
194
+ schemas?: Schemas<Type, Config, StatusData>,
195
+ ) =>
196
+ z.object({
197
+ tasks: nullableArrayZ(taskZ(schemas)),
198
+ });
206
199
 
207
- export interface RetrieveOptions
208
- extends Pick<
209
- RetrieveRequest,
210
- "rack" | "offset" | "limit" | "includeState" | "types"
211
- > {}
200
+ export interface RetrieveRequest extends z.infer<typeof retrieveReqZ> {}
212
201
 
213
202
  const RETRIEVE_ENDPOINT = "/hardware/task/retrieve";
214
203
  const CREATE_ENDPOINT = "/hardware/task/create";
215
204
  const DELETE_ENDPOINT = "/hardware/task/delete";
216
205
  const COPY_ENDPOINT = "/hardware/task/copy";
217
206
 
218
- const createReqZ = z.object({ tasks: newZ.array() });
219
- const createResZ = z.object({ tasks: taskZ.array() });
207
+ const createReqZ = <
208
+ Type extends z.ZodLiteral<string> = z.ZodLiteral<string>,
209
+ Config extends z.ZodType = z.ZodType,
210
+ StatusData extends z.ZodType = z.ZodType,
211
+ >(
212
+ schemas?: Schemas<Type, Config, StatusData>,
213
+ ) => z.object({ tasks: newZ(schemas).array() });
214
+ const createResZ = <
215
+ Type extends z.ZodLiteral<string> = z.ZodLiteral<string>,
216
+ Config extends z.ZodType = z.ZodType,
217
+ StatusData extends z.ZodType = z.ZodType,
218
+ >(
219
+ schemas?: Schemas<Type, Config, StatusData>,
220
+ ) => z.object({ tasks: taskZ(schemas).array() });
220
221
  const deleteReqZ = z.object({ keys: keyZ.array() });
221
222
  const deleteResZ = z.object({});
222
223
  const copyReqZ = z.object({ key: keyZ, name: z.string(), snapshot: z.boolean() });
223
- const copyResZ = z.object({ task: taskZ });
224
-
225
- export class Client implements AsyncTermSearcher<string, Key, Payload> {
226
- readonly type: string = ONTOLOGY_TYPE;
224
+ const copyResZ = <
225
+ Type extends z.ZodLiteral<string> = z.ZodLiteral<string>,
226
+ Config extends z.ZodType = z.ZodType,
227
+ StatusData extends z.ZodType = z.ZodType,
228
+ >(
229
+ schemas?: Schemas<Type, Config, StatusData>,
230
+ ) => z.object({ task: taskZ(schemas) });
231
+
232
+ export class Client {
233
+ readonly type: string = "task";
227
234
  private readonly client: UnaryClient;
228
235
  private readonly frameClient: framer.Client;
229
236
  private readonly ontologyClient: ontology.Client;
@@ -241,38 +248,53 @@ export class Client implements AsyncTermSearcher<string, Key, Payload> {
241
248
  this.rangeClient = rangeClient;
242
249
  }
243
250
 
244
- async create<
245
- Config extends UnknownRecord = UnknownRecord,
246
- Details extends {} = UnknownRecord,
247
- Type extends string = string,
248
- >(task: New<Config, Type>): Promise<Task<Config, Details, Type>>;
251
+ async create(task: New): Promise<Task>;
252
+ async create(tasks: New[]): Promise<Task[]>;
253
+ async create(task: New | New[]): Promise<Task | Task[]>;
249
254
 
250
255
  async create<
251
- Config extends UnknownRecord = UnknownRecord,
252
- Details extends {} = UnknownRecord,
253
- Type extends string = string,
254
- >(tasks: New<Config, Type>[]): Promise<Task<Config, Details, Type>[]>;
256
+ Type extends z.ZodLiteral<string> = z.ZodLiteral<string>,
257
+ Config extends z.ZodType = z.ZodType,
258
+ StatusData extends z.ZodType = z.ZodType,
259
+ >(
260
+ task: New<Type, Config>,
261
+ schemas: Schemas<Type, Config, StatusData>,
262
+ ): Promise<Task<Type, Config, StatusData>>;
263
+ async create<
264
+ Type extends z.ZodLiteral<string> = z.ZodLiteral<string>,
265
+ Config extends z.ZodType = z.ZodType,
266
+ StatusData extends z.ZodType = z.ZodType,
267
+ >(
268
+ tasks: New<Type, Config>[],
269
+ schemas: Schemas<Type, Config, StatusData>,
270
+ ): Promise<Task<Type, Config, StatusData>[]>;
255
271
 
256
272
  async create<
257
- Config extends UnknownRecord = UnknownRecord,
258
- Details extends {} = UnknownRecord,
259
- Type extends string = string,
273
+ Type extends z.ZodLiteral<string> = z.ZodLiteral<string>,
274
+ Config extends z.ZodType = z.ZodType,
275
+ StatusData extends z.ZodType = z.ZodType,
260
276
  >(
261
- task: New<Config, Type> | Array<New<Config, Type>>,
262
- ): Promise<Task<Config, Details, Type> | Array<Task<Config, Details, Type>>> {
277
+ task: New<Type, Config> | Array<New<Type, Config>>,
278
+ schemas?: Schemas<Type, Config, StatusData>,
279
+ ): Promise<Task<Type, Config, StatusData> | Array<Task<Type, Config, StatusData>>> {
263
280
  const isSingle = !Array.isArray(task);
264
- const res = await sendRequired<typeof createReqZ, typeof createResZ>(
281
+ const createReq = createReqZ(schemas);
282
+ const createRes = createResZ(schemas);
283
+ const res = await sendRequired(
265
284
  this.client,
266
285
  CREATE_ENDPOINT,
267
- { tasks: array.toArray(task) },
268
- createReqZ,
269
- createResZ,
286
+ { tasks: array.toArray(task) } as z.infer<typeof createReq>,
287
+ createReq,
288
+ createRes,
289
+ );
290
+ const sugared = this.sugar<Type, Config, StatusData>(
291
+ res.tasks as Payload<Type, Config, StatusData>[],
292
+ schemas,
270
293
  );
271
- const sugared = this.sugar(res.tasks) as Array<Task<Config, Details, Type>>;
272
294
  return isSingle ? sugared[0] : sugared;
273
295
  }
274
296
 
275
- async delete(keys: bigint | bigint[]): Promise<void> {
297
+ async delete(keys: Key | Key[]): Promise<void> {
276
298
  await sendRequired<typeof deleteReqZ, typeof deleteResZ>(
277
299
  this.client,
278
300
  DELETE_ENDPOINT,
@@ -282,85 +304,56 @@ export class Client implements AsyncTermSearcher<string, Key, Payload> {
282
304
  );
283
305
  }
284
306
 
285
- async search(term: string): Promise<Payload[]> {
286
- return await this.execRetrieve({ keys: [term] });
287
- }
288
-
289
- async page(offset: number, limit: number): Promise<Payload[]> {
290
- return await this.execRetrieve({ offset, limit });
291
- }
292
-
293
- async list(options: RetrieveOptions = {}): Promise<Task[]> {
294
- return this.sugar(await this.execRetrieve(options));
295
- }
296
-
297
- async retrieve<
298
- Config extends UnknownRecord = UnknownRecord,
299
- Details extends {} = UnknownRecord,
300
- Type extends string = string,
301
- >(rack: number, options?: RetrieveOptions): Promise<Task<Config, Details, Type>[]>;
302
-
303
- async retrieve<
304
- Config extends UnknownRecord = UnknownRecord,
305
- Details extends {} = UnknownRecord,
306
- Type extends string = string,
307
- >(keys: string[], options?: RetrieveOptions): Promise<Task<Config, Details, Type>[]>;
308
-
309
307
  async retrieve<
310
- Config extends UnknownRecord = UnknownRecord,
311
- Details extends {} = UnknownRecord,
312
- Type extends string = string,
313
- >(key: string, options?: RetrieveOptions): Promise<Task<Config, Details, Type>>;
314
-
308
+ Type extends z.ZodLiteral<string>,
309
+ Config extends z.ZodType,
310
+ StatusData extends z.ZodType,
311
+ >(
312
+ args: SingleRetrieveArgs & RetrieveSchemas<Type, Config, StatusData>,
313
+ ): Promise<Task<Type, Config, StatusData>>;
314
+ async retrieve(args: SingleRetrieveArgs): Promise<Task>;
315
315
  async retrieve<
316
- Config extends UnknownRecord = UnknownRecord,
317
- Details extends {} = UnknownRecord,
318
- Type extends string = string,
316
+ Type extends z.ZodLiteral<string>,
317
+ Config extends z.ZodType,
318
+ StatusData extends z.ZodType,
319
319
  >(
320
- rack: number | string | string[],
321
- options?: RetrieveOptions,
322
- ): Promise<Task<Config, Details, Type> | Task<Config, Details, Type>[]> {
323
- const { single, normalized, variant } = analyzeParams(
324
- rack,
325
- { number: "rack", string: "keys" },
326
- { convertNumericStrings: false },
320
+ args: MultiRetrieveArgs & RetrieveSchemas<Type, Config, StatusData>,
321
+ ): Promise<Task<Type, Config, StatusData>[]>;
322
+ async retrieve(args: MultiRetrieveArgs): Promise<Task[]>;
323
+ async retrieve<
324
+ Type extends z.ZodLiteral<string>,
325
+ Config extends z.ZodType,
326
+ StatusData extends z.ZodType,
327
+ >({
328
+ schemas,
329
+ ...args
330
+ }: RetrieveArgs & RetrieveSchemas<Type, Config, StatusData>): Promise<
331
+ Task<Type, Config, StatusData> | Task<Type, Config, StatusData>[]
332
+ > {
333
+ const isSingle = "key" in args || "name" in args;
334
+ const res = await sendRequired(
335
+ this.client,
336
+ RETRIEVE_ENDPOINT,
337
+ args,
338
+ retrieveArgsZ,
339
+ retrieveResZ(schemas),
327
340
  );
328
- const req: RetrieveRequest = { ...options };
329
- if (variant === "rack") req.rack = rack as number;
330
- else req.keys = normalized as string[];
331
- const tasks = await this.execRetrieve(req);
332
- const sugared = this.sugar(tasks) as Array<Task<Config, Details, Type>>;
333
- return single && variant !== "rack" ? sugared[0] : sugared;
341
+ const tasks = res.tasks as Payload<Type, Config, StatusData>[];
342
+ const sugared = this.sugar<Type, Config, StatusData>(tasks, schemas);
343
+ checkForMultipleOrNoResults("Task", args, sugared, isSingle);
344
+ return isSingle ? sugared[0] : sugared;
334
345
  }
335
346
 
336
347
  async copy(key: string, name: string, snapshot: boolean): Promise<Task> {
337
- const res = await sendRequired(
348
+ const copyRes = copyResZ();
349
+ const response = await sendRequired(
338
350
  this.client,
339
351
  COPY_ENDPOINT,
340
352
  { key, name, snapshot },
341
353
  copyReqZ,
342
- copyResZ,
354
+ copyRes,
343
355
  );
344
- return this.sugar([res.task])[0];
345
- }
346
-
347
- async retrieveByName<
348
- Config extends UnknownRecord = UnknownRecord,
349
- Details extends {} = UnknownRecord,
350
- Type extends string = string,
351
- >(name: string, rack?: number): Promise<Task<Config, Details, Type>> {
352
- const tasks = await this.execRetrieve({ names: [name], rack });
353
- checkForMultipleOrNoResults("Task", name, tasks, true);
354
- return this.sugar(tasks)[0] as Task<Config, Details, Type>;
355
- }
356
-
357
- async retrieveByType<
358
- Config extends UnknownRecord = UnknownRecord,
359
- Details extends {} = UnknownRecord,
360
- Type extends string = string,
361
- >(type: Type, rack?: number): Promise<Task<Config, Details, Type>[]> {
362
- const tasks = await this.execRetrieve({ types: [type], rack });
363
- return this.sugar(tasks) as Task<Config, Details, Type>[];
356
+ return this.sugar(response.task as Payload);
364
357
  }
365
358
 
366
359
  async retrieveSnapshottedTo(taskKey: Key): Promise<ontology.Resource | null> {
@@ -368,97 +361,148 @@ export class Client implements AsyncTermSearcher<string, Key, Payload> {
368
361
  return await retrieveSnapshottedTo(taskKey, this.ontologyClient);
369
362
  }
370
363
 
371
- private async execRetrieve(req: RetrieveRequest): Promise<Payload[]> {
372
- const res = await sendRequired(
373
- this.client,
374
- RETRIEVE_ENDPOINT,
375
- req,
376
- retrieveReqZ,
377
- retrieveResZ,
378
- );
379
- return res.tasks;
380
- }
381
-
382
364
  sugar<
383
- Config extends UnknownRecord = UnknownRecord,
384
- Details extends {} = UnknownRecord,
385
- Type extends string = string,
386
- >(payload: Payload<Config, Details, Type>): Task<Config, Details, Type>;
365
+ Type extends z.ZodLiteral<string> = z.ZodLiteral<string>,
366
+ Config extends z.ZodType = z.ZodType,
367
+ StatusData extends z.ZodType = z.ZodType,
368
+ >(
369
+ payloads: Payload<Type, Config, StatusData>[],
370
+ schemas?: Schemas<Type, Config, StatusData>,
371
+ ): Task<Type, Config, StatusData>[];
387
372
 
388
373
  sugar<
389
- Config extends UnknownRecord = UnknownRecord,
390
- Details extends {} = UnknownRecord,
391
- Type extends string = string,
392
- >(payloads: Payload<Config, Details, Type>[]): Task<Config, Details, Type>[];
374
+ Type extends z.ZodLiteral<string> = z.ZodLiteral<string>,
375
+ Config extends z.ZodType = z.ZodType,
376
+ StatusData extends z.ZodType = z.ZodType,
377
+ >(
378
+ payload: Payload<Type, Config, StatusData>,
379
+ schemas?: Schemas<Type, Config, StatusData>,
380
+ ): Task<Type, Config, StatusData>;
393
381
 
394
- sugar(payloads: Payload | Payload[]): Task | Task[] {
382
+ sugar<
383
+ Type extends z.ZodLiteral<string> = z.ZodLiteral<string>,
384
+ Config extends z.ZodType = z.ZodType,
385
+ StatusData extends z.ZodType = z.ZodType,
386
+ >(
387
+ payloads: Payload<Type, Config, StatusData> | Payload<Type, Config, StatusData>[],
388
+ schemas?: Schemas<Type, Config, StatusData>,
389
+ ): Task<Type, Config, StatusData>[] | Task<Type, Config, StatusData> {
395
390
  const isSingle = !Array.isArray(payloads);
396
- const res = array
397
- .toArray(payloads)
398
- .map(
399
- ({ key, name, type, config, state, internal, snapshot }) =>
400
- new Task(
391
+ const res = array.toArray(payloads).map(
392
+ ({ key, name, type, config, status, internal, snapshot }) =>
393
+ new Task(
394
+ {
401
395
  key,
402
396
  name,
403
397
  type,
404
398
  config,
405
399
  internal,
406
400
  snapshot,
407
- state,
408
- this.frameClient,
409
- this.ontologyClient,
410
- this.rangeClient,
411
- ),
412
- );
401
+ status,
402
+ },
403
+ schemas,
404
+ this.frameClient,
405
+ this.ontologyClient,
406
+ this.rangeClient,
407
+ ),
408
+ );
413
409
  return isSingle ? res[0] : res;
414
410
  }
415
411
 
416
- async openTracker(): Promise<signals.Observable<string, string>> {
417
- return await signals.openObservable<string, string>(
412
+ async executeCommand(task: Key, type: string, args?: {}): Promise<string> {
413
+ return await executeCommand(this.frameClient, task, type, args);
414
+ }
415
+
416
+ async executeCommandSync<StatusData extends z.ZodType = z.ZodType>(
417
+ task: Key,
418
+ type: string,
419
+ timeout: CrudeTimeSpan,
420
+ args?: {},
421
+ name?: string,
422
+ statusDataZ: StatusData = z.unknown() as unknown as StatusData,
423
+ ): Promise<Status<StatusData>> {
424
+ const retrieveName = async () => {
425
+ const t = await this.retrieve({ key: task });
426
+ return t.name;
427
+ };
428
+ return await executeCommandSync(
418
429
  this.frameClient,
419
- SET_CHANNEL_NAME,
420
- DELETE_CHANNEL_NAME,
421
- (variant, data) =>
422
- Array.from(data).map((k) => ({
423
- variant,
424
- key: k.toString(),
425
- value: k.toString(),
426
- })),
430
+ task,
431
+ type,
432
+ timeout,
433
+ name ?? retrieveName,
434
+ statusDataZ,
435
+ args,
427
436
  );
428
437
  }
438
+ }
429
439
 
430
- async openStateObserver(): Promise<StateObservable> {
431
- return new framer.ObservableStreamer<State>(
432
- await this.frameClient.openStreamer(STATE_CHANNEL_NAME),
433
- (frame) => {
434
- const s = frame.get(STATE_CHANNEL_NAME);
435
- if (s.length === 0) return [null, false];
436
- const parse = stateZ.safeParse(s.at(-1));
437
- if (!parse.success) {
438
- console.error(parse.error);
439
- return [null, false];
440
- }
441
- return [parse.data, true];
442
- },
443
- );
440
+ export const ontologyID = (key: Key): ontology.ID => ({ type: "task", key });
441
+
442
+ const executeCommand = async (
443
+ frameClient: framer.Client | null,
444
+ task: Key,
445
+ type: string,
446
+ args?: {},
447
+ ): Promise<string> => {
448
+ if (frameClient == null) throw NOT_CREATED_ERROR;
449
+ const key = id.create();
450
+ const w = await frameClient.openWriter(COMMAND_CHANNEL_NAME);
451
+ await w.write(COMMAND_CHANNEL_NAME, [{ args, key, task, type }]);
452
+ await w.close();
453
+ return key;
454
+ };
455
+
456
+ const executeCommandSync = async <StatusData extends z.ZodType = z.ZodType>(
457
+ frameClient: framer.Client | null,
458
+ task: Key,
459
+ type: string,
460
+ timeout: CrudeTimeSpan,
461
+ tskName: string | (() => Promise<string>),
462
+ statusDataZ: StatusData,
463
+ args?: {},
464
+ ): Promise<Status<StatusData>> => {
465
+ if (frameClient == null) throw NOT_CREATED_ERROR;
466
+ const streamer = await frameClient.openStreamer(STATUS_CHANNEL_NAME);
467
+ const cmdKey = await executeCommand(frameClient, task, type, args);
468
+ const parsedTimeout = new TimeSpan(timeout);
469
+
470
+ let timeoutID: NodeJS.Timeout | undefined;
471
+ const timeoutPromise = new Promise<never>((_, reject) => {
472
+ timeoutID = setTimeout(() => {
473
+ void (async () =>
474
+ reject(await formatTimeoutError(type, tskName, parsedTimeout, task)))();
475
+ }, parsedTimeout.milliseconds);
476
+ });
477
+ try {
478
+ while (true) {
479
+ const frame = await Promise.race([streamer.read(), timeoutPromise]);
480
+ const state = statusZ(statusDataZ).parse(frame.at(-1)[STATUS_CHANNEL_NAME]);
481
+ if (state.key === cmdKey) return state;
482
+ }
483
+ } finally {
484
+ clearTimeout(timeoutID);
485
+ streamer.close();
444
486
  }
487
+ };
445
488
 
446
- async openCommandObserver(): Promise<CommandObservable> {
447
- return new framer.ObservableStreamer<Command>(
448
- await this.frameClient.openStreamer(COMMAND_CHANNEL_NAME),
449
- (frame) => {
450
- const s = frame.get(COMMAND_CHANNEL_NAME);
451
- if (s.length === 0) return [null, false];
452
- const parse = commandZ.safeParse(s.at(-1));
453
- if (!parse.success) {
454
- console.error(parse.error);
455
- return [null, false];
456
- }
457
- return [parse.data, true];
458
- },
489
+ const formatTimeoutError = async (
490
+ type: string,
491
+ name: string | (() => Promise<string>),
492
+ timeout: TimeSpan,
493
+ key: Key,
494
+ ): Promise<Error> => {
495
+ const formattedType = caseconv.capitalize(type);
496
+ const formattedTimeout = timeout.toString();
497
+ try {
498
+ const name_ = typeof name === "string" ? name : await name();
499
+ return new Error(
500
+ `${formattedType} command to ${name_} timed out after ${formattedTimeout}`,
501
+ );
502
+ } catch (e) {
503
+ console.error("Failed to retrieve task name for timeout error:", e);
504
+ return new Error(
505
+ `${formattedType} command to task with key ${key} timed out after ${formattedTimeout}`,
459
506
  );
460
507
  }
461
- }
462
-
463
- export const ontologyID = (key: Key): ontology.ID =>
464
- new ontology.ID({ type: ONTOLOGY_TYPE, key });
508
+ };