@synnaxlabs/client 0.34.0 → 0.36.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 (126) hide show
  1. package/.turbo/turbo-build.log +7 -7
  2. package/dist/access/policy/payload.d.ts +40 -40
  3. package/dist/access/policy/retriever.d.ts +5 -5
  4. package/dist/client.cjs +28 -28
  5. package/dist/client.d.ts +35 -0
  6. package/dist/client.d.ts.map +1 -1
  7. package/dist/client.js +3092 -2820
  8. package/dist/control/state.d.ts.map +1 -1
  9. package/dist/framer/deleter.d.ts.map +1 -1
  10. package/dist/framer/frame.d.ts +14 -7
  11. package/dist/framer/frame.d.ts.map +1 -1
  12. package/dist/framer/writer.d.ts +8 -8
  13. package/dist/framer/writer.d.ts.map +1 -1
  14. package/dist/hardware/device/client.d.ts +2 -2
  15. package/dist/hardware/device/client.d.ts.map +1 -1
  16. package/dist/hardware/device/payload.d.ts +2 -2
  17. package/dist/hardware/device/payload.d.ts.map +1 -1
  18. package/dist/hardware/task/client.d.ts +4 -2
  19. package/dist/hardware/task/client.d.ts.map +1 -1
  20. package/dist/hardware/task/payload.d.ts +9 -5
  21. package/dist/hardware/task/payload.d.ts.map +1 -1
  22. package/dist/index.d.ts +2 -0
  23. package/dist/index.d.ts.map +1 -1
  24. package/dist/label/writer.d.ts +5 -5
  25. package/dist/ontology/client.d.ts +8 -8
  26. package/dist/ontology/group/payload.d.ts +3 -0
  27. package/dist/ontology/group/payload.d.ts.map +1 -1
  28. package/dist/ontology/payload.d.ts +43 -43
  29. package/dist/ontology/payload.d.ts.map +1 -1
  30. package/dist/ranger/client.d.ts +1 -0
  31. package/dist/ranger/client.d.ts.map +1 -1
  32. package/dist/ranger/payload.d.ts.map +1 -1
  33. package/dist/ranger/writer.d.ts +5 -5
  34. package/dist/ranger/writer.d.ts.map +1 -1
  35. package/dist/transport.d.ts +2 -1
  36. package/dist/transport.d.ts.map +1 -1
  37. package/dist/workspace/client.d.ts +70 -11
  38. package/dist/workspace/client.d.ts.map +1 -1
  39. package/dist/workspace/index.d.ts +1 -1
  40. package/dist/workspace/index.d.ts.map +1 -1
  41. package/dist/workspace/lineplot/client.d.ts +48 -6
  42. package/dist/workspace/lineplot/client.d.ts.map +1 -1
  43. package/dist/workspace/lineplot/index.d.ts +1 -1
  44. package/dist/workspace/lineplot/index.d.ts.map +1 -1
  45. package/dist/workspace/log/client.d.ts +48 -6
  46. package/dist/workspace/log/client.d.ts.map +1 -1
  47. package/dist/workspace/log/index.d.ts +1 -1
  48. package/dist/workspace/log/index.d.ts.map +1 -1
  49. package/dist/workspace/schematic/client.d.ts +72 -6
  50. package/dist/workspace/schematic/client.d.ts.map +1 -1
  51. package/dist/workspace/schematic/index.d.ts +1 -1
  52. package/dist/workspace/schematic/index.d.ts.map +1 -1
  53. package/dist/workspace/table/client.d.ts +71 -0
  54. package/dist/workspace/table/client.d.ts.map +1 -0
  55. package/dist/workspace/table/index.d.ts +2 -0
  56. package/dist/workspace/table/index.d.ts.map +1 -0
  57. package/dist/workspace/table/table.spec.d.ts +2 -0
  58. package/dist/workspace/table/table.spec.d.ts.map +1 -0
  59. package/package.json +10 -10
  60. package/src/client.ts +16 -2
  61. package/src/control/state.ts +1 -1
  62. package/src/framer/frame.spec.ts +32 -3
  63. package/src/framer/frame.ts +17 -0
  64. package/src/hardware/device/client.ts +10 -4
  65. package/src/hardware/task/client.ts +51 -3
  66. package/src/hardware/task/payload.ts +21 -6
  67. package/src/index.ts +2 -0
  68. package/src/ontology/group/payload.ts +8 -7
  69. package/src/ontology/payload.ts +1 -0
  70. package/src/ranger/client.ts +4 -1
  71. package/src/transport.ts +8 -3
  72. package/src/workspace/client.ts +151 -47
  73. package/src/workspace/index.ts +1 -1
  74. package/src/workspace/lineplot/client.ts +96 -28
  75. package/src/workspace/lineplot/index.ts +1 -1
  76. package/src/workspace/log/client.ts +93 -28
  77. package/src/workspace/log/index.ts +1 -1
  78. package/src/workspace/schematic/client.ts +121 -29
  79. package/src/workspace/schematic/index.ts +1 -1
  80. package/src/workspace/table/client.ts +125 -0
  81. package/src/workspace/{external.ts → table/index.ts} +1 -2
  82. package/src/workspace/table/table.spec.ts +65 -0
  83. package/dist/workspace/external.d.ts +0 -3
  84. package/dist/workspace/external.d.ts.map +0 -1
  85. package/dist/workspace/lineplot/external.d.ts +0 -3
  86. package/dist/workspace/lineplot/external.d.ts.map +0 -1
  87. package/dist/workspace/lineplot/retriever.d.ts +0 -9
  88. package/dist/workspace/lineplot/retriever.d.ts.map +0 -1
  89. package/dist/workspace/lineplot/writer.d.ts +0 -35
  90. package/dist/workspace/lineplot/writer.d.ts.map +0 -1
  91. package/dist/workspace/log/external.d.ts +0 -3
  92. package/dist/workspace/log/external.d.ts.map +0 -1
  93. package/dist/workspace/log/payload.d.ts +0 -36
  94. package/dist/workspace/log/payload.d.ts.map +0 -1
  95. package/dist/workspace/log/retriever.d.ts +0 -9
  96. package/dist/workspace/log/retriever.d.ts.map +0 -1
  97. package/dist/workspace/log/writer.d.ts +0 -35
  98. package/dist/workspace/log/writer.d.ts.map +0 -1
  99. package/dist/workspace/payload.d.ts +0 -38
  100. package/dist/workspace/payload.d.ts.map +0 -1
  101. package/dist/workspace/retriever.d.ts +0 -13
  102. package/dist/workspace/retriever.d.ts.map +0 -1
  103. package/dist/workspace/schematic/external.d.ts +0 -3
  104. package/dist/workspace/schematic/external.d.ts.map +0 -1
  105. package/dist/workspace/schematic/payload.d.ts +0 -42
  106. package/dist/workspace/schematic/payload.d.ts.map +0 -1
  107. package/dist/workspace/schematic/retriever.d.ts +0 -9
  108. package/dist/workspace/schematic/retriever.d.ts.map +0 -1
  109. package/dist/workspace/schematic/writer.d.ts +0 -41
  110. package/dist/workspace/schematic/writer.d.ts.map +0 -1
  111. package/dist/workspace/writer.d.ts +0 -66
  112. package/dist/workspace/writer.d.ts.map +0 -1
  113. package/src/workspace/lineplot/external.ts +0 -11
  114. package/src/workspace/lineplot/retriever.ts +0 -34
  115. package/src/workspace/lineplot/writer.ts +0 -115
  116. package/src/workspace/log/external.ts +0 -11
  117. package/src/workspace/log/payload.ts +0 -36
  118. package/src/workspace/log/retriever.ts +0 -38
  119. package/src/workspace/log/writer.ts +0 -116
  120. package/src/workspace/payload.ts +0 -44
  121. package/src/workspace/retriever.ts +0 -70
  122. package/src/workspace/schematic/external.ts +0 -11
  123. package/src/workspace/schematic/payload.ts +0 -38
  124. package/src/workspace/schematic/retriever.ts +0 -42
  125. package/src/workspace/schematic/writer.ts +0 -140
  126. package/src/workspace/writer.ts +0 -111
@@ -19,6 +19,9 @@ import { framer } from "@/framer";
19
19
  import { type Frame } from "@/framer/frame";
20
20
  import { rack } from "@/hardware/rack";
21
21
  import {
22
+ type Command,
23
+ type CommandObservable,
24
+ commandZ,
22
25
  type NewTask,
23
26
  newTaskZ,
24
27
  type Payload,
@@ -46,7 +49,7 @@ export class Task<
46
49
  T extends string = string,
47
50
  > {
48
51
  readonly key: TaskKey;
49
- readonly name: string;
52
+ name: string;
50
53
  readonly internal: boolean;
51
54
  readonly type: T;
52
55
  config: C;
@@ -139,7 +142,10 @@ export class Task<
139
142
  const s = frame.get(TASK_STATE_CHANNEL);
140
143
  if (s.length === 0) return [null, false];
141
144
  const parse = stateZ.safeParse(s.at(-1));
142
- if (!parse.success) return [null, false];
145
+ if (!parse.success) {
146
+ console.error(parse.error);
147
+ return [null, false];
148
+ }
143
149
  const state = parse.data as State<D>;
144
150
  if (state.task !== this.key) return [null, false];
145
151
  return [state, true];
@@ -147,6 +153,27 @@ export class Task<
147
153
  );
148
154
  }
149
155
 
156
+ async openCommandObserver<A extends UnknownRecord = UnknownRecord>(): Promise<
157
+ CommandObservable<A>
158
+ > {
159
+ if (this.frameClient == null) throw TASK_NOT_CREATED;
160
+ return new framer.ObservableStreamer<Command<A>>(
161
+ await this.frameClient.openStreamer(TASK_CMD_CHANNEL),
162
+ (frame) => {
163
+ const s = frame.get(TASK_CMD_CHANNEL);
164
+ if (s.length === 0) return [null, false];
165
+ const parse = commandZ.safeParse(s.at(-1));
166
+ if (!parse.success) {
167
+ console.error(parse.error);
168
+ return [null, false];
169
+ }
170
+ const cmd = parse.data as Command<A>;
171
+ if (cmd.task !== this.key) return [null, false];
172
+ return [cmd, true];
173
+ },
174
+ );
175
+ }
176
+
150
177
  async snapshottedTo(): Promise<ontology.Resource | null> {
151
178
  if (this.ontologyClient == null || this.rangeClient == null) throw TASK_NOT_CREATED;
152
179
  if (!this.snapshot) return null;
@@ -376,9 +403,30 @@ export class Client implements AsyncTermSearcher<string, TaskKey, Payload> {
376
403
  const s = frame.get(TASK_STATE_CHANNEL);
377
404
  if (s.length === 0) return [null, false];
378
405
  const parse = stateZ.safeParse(s.at(-1));
379
- if (!parse.success) return [null, false];
406
+ if (!parse.success) {
407
+ console.error(parse.error);
408
+ return [null, false];
409
+ }
380
410
  return [parse.data as State<D>, true];
381
411
  },
382
412
  );
383
413
  }
414
+
415
+ async openCommandObserver<A extends UnknownRecord = UnknownRecord>(): Promise<
416
+ CommandObservable<A>
417
+ > {
418
+ return new framer.ObservableStreamer<Command<A>>(
419
+ await this.frameClient.openStreamer(TASK_CMD_CHANNEL),
420
+ (frame) => {
421
+ const s = frame.get(TASK_CMD_CHANNEL);
422
+ if (s.length === 0) return [null, false];
423
+ const parse = commandZ.safeParse(s.at(-1));
424
+ if (!parse.success) {
425
+ console.error(parse.error);
426
+ return [null, false];
427
+ }
428
+ return [parse.data as Command<A>, true];
429
+ },
430
+ );
431
+ }
384
432
  }
@@ -85,17 +85,32 @@ export const commandZ = z.object({
85
85
  task: taskKeyZ,
86
86
  type: z.string(),
87
87
  key: z.string(),
88
- args: z.record(z.unknown()).or(
89
- z.string().transform((c) => {
90
- if (c === "") return {};
91
- return JSON.parse(c);
92
- }),
93
- ) as z.ZodType<UnknownRecord>,
88
+ args: z
89
+ .record(z.unknown())
90
+ .or(
91
+ z.string().transform((c) => {
92
+ if (c === "") return {};
93
+ return JSON.parse(c);
94
+ }),
95
+ )
96
+ .or(z.array(z.unknown()))
97
+ .or(z.null())
98
+ .optional() as z.ZodOptional<z.ZodType<UnknownRecord>>,
94
99
  });
95
100
 
101
+ export type Command<A extends {} = UnknownRecord> = Omit<
102
+ z.infer<typeof commandZ>,
103
+ "args"
104
+ > & {
105
+ args?: A;
106
+ };
107
+
96
108
  export type StateObservable<D extends UnknownRecord = UnknownRecord> =
97
109
  observe.ObservableAsyncCloseable<State<D>>;
98
110
 
111
+ export type CommandObservable<A extends UnknownRecord = UnknownRecord> =
112
+ observe.ObservableAsyncCloseable<Command<A>>;
113
+
99
114
  export const ONTOLOGY_TYPE: ontology.ResourceType = "task";
100
115
 
101
116
  export const ontologyID = (key: TaskKey): ontology.ID =>
package/src/index.ts CHANGED
@@ -32,6 +32,7 @@ export { rack } from "@/hardware/rack";
32
32
  export { task } from "@/hardware/task";
33
33
  export { label } from "@/label";
34
34
  export { ontology } from "@/ontology";
35
+ export { group } from "@/ontology/group";
35
36
  export { ranger } from "@/ranger";
36
37
  export { signals } from "@/signals";
37
38
  export { user } from "@/user";
@@ -39,6 +40,7 @@ export { workspace } from "@/workspace";
39
40
  export { linePlot } from "@/workspace/lineplot";
40
41
  export { log } from "@/workspace/log";
41
42
  export { schematic } from "@/workspace/schematic";
43
+ export { table } from "@/workspace/table";
42
44
  export {
43
45
  type CrudeDataType,
44
46
  type CrudeDensity,
@@ -9,6 +9,7 @@
9
9
 
10
10
  import { toArray } from "@synnaxlabs/x/toArray";
11
11
  import { z } from "zod";
12
+ import { ontology } from "@/ontology";
12
13
 
13
14
  export const keyZ = z.string().uuid();
14
15
  export type Key = z.infer<typeof keyZ>;
@@ -17,10 +18,7 @@ export type Keys = Key[];
17
18
  export type Names = Name[];
18
19
  export type Params = Key | Name | Keys | Names;
19
20
 
20
- export const groupZ = z.object({
21
- key: keyZ,
22
- name: z.string(),
23
- });
21
+ export const groupZ = z.object({ key: keyZ, name: z.string() });
24
22
 
25
23
  export type Payload = z.infer<typeof groupZ>;
26
24
 
@@ -52,9 +50,7 @@ export type ParamAnalysisResult =
52
50
 
53
51
  export const analyzeParams = (groups: Params): ParamAnalysisResult => {
54
52
  const normal = toArray(groups) as Keys | Names;
55
- if (normal.length === 0)
56
- throw new Error("No groups specified");
57
-
53
+ if (normal.length === 0) throw new Error("No groups specified");
58
54
  const isKey = keyZ.safeParse(normal[0]).success;
59
55
  return {
60
56
  single: !Array.isArray(groups),
@@ -63,3 +59,8 @@ export const analyzeParams = (groups: Params): ParamAnalysisResult => {
63
59
  actual: groups,
64
60
  } as const as ParamAnalysisResult;
65
61
  };
62
+
63
+ export const ONTOLOGY_TYPE: ontology.ResourceType = "group";
64
+
65
+ export const ontologyID = (key: Key): ontology.ID =>
66
+ new ontology.ID({ type: ONTOLOGY_TYPE, key });
@@ -37,6 +37,7 @@ export const resourceTypeZ = z.union([
37
37
  z.literal("device"),
38
38
  z.literal("task"),
39
39
  z.literal("policy"),
40
+ z.literal("table"),
40
41
  ]);
41
42
  export type ResourceType = z.infer<typeof resourceTypeZ>;
42
43
 
@@ -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 { type CrudeTimeRange, observe, TimeRange } from "@synnaxlabs/x";
11
+ import { type CrudeTimeRange, observe, sortTimeRange, TimeRange } from "@synnaxlabs/x";
12
12
  import { type AsyncTermSearcher } from "@synnaxlabs/x/search";
13
13
  import { type Series } from "@synnaxlabs/x/telem";
14
14
  import { toArray } from "@synnaxlabs/x/toArray";
@@ -192,6 +192,9 @@ export class Range {
192
192
  }
193
193
  }
194
194
 
195
+ export const sort = (a: Range, b: Range): -1 | 0 | 1 =>
196
+ sortTimeRange(a.timeRange, b.timeRange);
197
+
195
198
  const retrieveReqZ = z.object({
196
199
  keys: keyZ.array().optional(),
197
200
  names: z.array(z.string()).optional(),
package/src/transport.ts CHANGED
@@ -12,8 +12,10 @@ import {
12
12
  type Middleware,
13
13
  type StreamClient,
14
14
  type UnaryClient,
15
+ unaryWithBreaker,
15
16
  WebSocketClient,
16
17
  } from "@synnaxlabs/freighter";
18
+ import { breaker } from "@synnaxlabs/x";
17
19
  import { binary } from "@synnaxlabs/x/binary";
18
20
  import { type URL } from "@synnaxlabs/x/url";
19
21
 
@@ -25,12 +27,15 @@ export class Transport {
25
27
  readonly stream: StreamClient;
26
28
  readonly secure: boolean;
27
29
 
28
- constructor(url: URL, secure: boolean = false) {
30
+ constructor(url: URL, breakerCfg: breaker.Config = {}, secure: boolean = false) {
29
31
  this.secure = secure;
30
32
  this.url = url.child(baseAPIEndpoint);
31
33
  const codec = new binary.JSONCodec();
32
- this.unary = new HTTPClient(this.url, codec, this.secure);
33
- this.stream = new WebSocketClient(this.url, codec, this.secure);
34
+ (this.unary = unaryWithBreaker(
35
+ new HTTPClient(this.url, codec, this.secure),
36
+ breakerCfg,
37
+ )),
38
+ (this.stream = new WebSocketClient(this.url, codec, this.secure));
34
39
  }
35
40
 
36
41
  use(...middleware: Middleware[]): void {
@@ -1,80 +1,184 @@
1
- // Copyright 2024 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 { type UnaryClient } from "@synnaxlabs/freighter";
11
- import { type UnknownRecord } from "@synnaxlabs/x/record";
12
- import { type AsyncTermSearcher } from "@synnaxlabs/x/search";
13
-
1
+ import { ontology } from "@/ontology";
2
+ import { sendRequired, UnaryClient } from "@synnaxlabs/freighter";
3
+ import { toArray, UnknownRecord } from "@synnaxlabs/x";
4
+ import { unknownRecordZ } from "@synnaxlabs/x/record";
5
+ import { z } from "zod";
6
+ import { AsyncTermSearcher } from "@synnaxlabs/x/search";
14
7
  import { linePlot } from "@/workspace/lineplot";
15
8
  import { log } from "@/workspace/log";
16
- import { type Key, type Workspace } from "@/workspace/payload";
17
- import { Retriever } from "@/workspace/retriever";
18
9
  import { schematic } from "@/workspace/schematic";
19
- import { type NewWorkspace, Writer } from "@/workspace/writer";
10
+ import { table } from "@/workspace/table";
11
+ import { nullableArrayZ } from "@/util/zod";
12
+
13
+ export const keyZ = z.string().uuid();
14
+ export type Key = z.infer<typeof keyZ>;
15
+ export type Params = Key | Key[];
16
+
17
+ // --- VERY IMPORTANT ---
18
+ // Synnax's encoders (in the binary package inside x) automatically convert the case
19
+ // of keys in objects to snake_case and back to camelCase when encoding and decoding
20
+ // respectively. This is done to ensure that the keys are consistent across all
21
+ // languages and platforms. Sometimes workspaces have keys that are uuids, which have
22
+ // dashes, and those get messed up. So we just use regular JSON for workspaces.
23
+ const parse = (s: string): UnknownRecord => JSON.parse(s) as UnknownRecord;
24
+
25
+ export const workspaceZ = z.object({
26
+ key: z.string(),
27
+ name: z.string(),
28
+ layout: unknownRecordZ.or(z.string().transform((s) => parse(s) as UnknownRecord)),
29
+ });
30
+
31
+ export type Workspace = z.infer<typeof workspaceZ>;
32
+
33
+ export const ONTOLOGY_TYPE: ontology.ResourceType = "workspace";
34
+
35
+ export const ontologyID = (key: Key): ontology.ID =>
36
+ new ontology.ID({ type: ONTOLOGY_TYPE, key });
37
+
38
+ const RETRIEVE_ENDPOINT = "/workspace/retrieve";
39
+ const CREATE_ENDPOINT = "/workspace/create";
40
+ const RENAME_ENDPOINT = "/workspace/rename";
41
+ const SET_LAYOUT_ENDPOINT = "/workspace/set-layout";
42
+ const DELETE_ENDPOINT = "/workspace/delete";
43
+
44
+ export const newWorkspaceZ = workspaceZ.partial({ key: true }).transform((p) => ({
45
+ ...p,
46
+ layout: JSON.stringify(p.layout),
47
+ }));
48
+
49
+ export const workspaceRemoteZ = workspaceZ.omit({ layout: true }).extend({
50
+ layout: z.string().transform((s) => parse(s) as UnknownRecord),
51
+ });
52
+
53
+ export type NewWorkspace = z.input<typeof newWorkspaceZ>;
54
+
55
+ const retrieveReqZ = z.object({
56
+ keys: z.string().array().optional(),
57
+ search: z.string().optional(),
58
+ author: z.string().uuid().optional(),
59
+ offset: z.number().optional(),
60
+ limit: z.number().optional(),
61
+ });
62
+ const createReqZ = z.object({ workspaces: newWorkspaceZ.array() });
63
+ const renameReqZ = z.object({ key: z.string(), name: z.string() });
64
+ const setLayoutReqZ = z.object({ key: z.string(), layout: z.string() });
65
+ const deleteReqZ = z.object({ keys: z.string().array() });
66
+
67
+ const retrieveResZ = z.object({ workspaces: nullableArrayZ(workspaceZ) });
68
+ const createResZ = z.object({ workspaces: workspaceRemoteZ.array() });
69
+ const emptyResZ = z.object({});
20
70
 
21
71
  export class Client implements AsyncTermSearcher<string, Key, Workspace> {
22
72
  readonly type = "workspace";
23
73
  readonly schematic: schematic.Client;
24
74
  readonly linePlot: linePlot.Client;
25
75
  readonly log: log.Client;
26
- private readonly retriever: Retriever;
27
- private readonly writer: Writer;
76
+ readonly table: table.Client;
77
+ private readonly client: UnaryClient;
28
78
 
29
79
  constructor(client: UnaryClient) {
80
+ this.client = client;
30
81
  this.schematic = new schematic.Client(client);
31
82
  this.linePlot = new linePlot.Client(client);
32
- this.retriever = new Retriever(client);
33
83
  this.log = new log.Client(client);
34
- this.writer = new Writer(client);
84
+ this.table = new table.Client(client);
35
85
  }
36
86
 
37
- async search(term: string): Promise<Workspace[]> {
38
- return await this.retriever.search(term);
87
+ async create(workspace: NewWorkspace): Promise<Workspace>;
88
+ async create(workspaces: NewWorkspace[]): Promise<Workspace[]>;
89
+ async create(
90
+ workspaces: NewWorkspace | NewWorkspace[],
91
+ ): Promise<Workspace | Workspace[]> {
92
+ const isMany = Array.isArray(workspaces);
93
+ const normalized = toArray(workspaces);
94
+ const res = await sendRequired(
95
+ this.client,
96
+ CREATE_ENDPOINT,
97
+ { workspaces: normalized },
98
+ createReqZ,
99
+ createResZ,
100
+ );
101
+ return isMany ? res.workspaces : res.workspaces[0];
39
102
  }
40
103
 
41
- async retrieveByAuthor(author: string): Promise<Workspace[]> {
42
- return await this.retriever.retrieveByAuthor(author);
104
+ async rename(key: Key, name: string): Promise<void> {
105
+ await sendRequired(
106
+ this.client,
107
+ RENAME_ENDPOINT,
108
+ { key, name },
109
+ renameReqZ,
110
+ emptyResZ,
111
+ );
43
112
  }
44
113
 
45
- async retrieve(key: Key): Promise<Workspace>;
114
+ async setLayout(key: Key, layout: UnknownRecord): Promise<void> {
115
+ await sendRequired(
116
+ this.client,
117
+ SET_LAYOUT_ENDPOINT,
118
+ { key, layout: JSON.stringify(layout) },
119
+ setLayoutReqZ,
120
+ emptyResZ,
121
+ );
122
+ }
46
123
 
124
+ async retrieve(key: Key): Promise<Workspace>;
47
125
  async retrieve(keys: Key[]): Promise<Workspace[]>;
48
-
49
- async retrieve(keys: Key | Key[]): Promise<Workspace | Workspace[]> {
126
+ async retrieve(keys: Params): Promise<Workspace | Workspace[]> {
50
127
  const isMany = Array.isArray(keys);
51
- const res = await this.retriever.retrieve(keys);
52
- return isMany ? res : res[0];
128
+ const normalized = toArray(keys);
129
+ const res = await sendRequired(
130
+ this.client,
131
+ RETRIEVE_ENDPOINT,
132
+ { keys: normalized },
133
+ retrieveReqZ,
134
+ retrieveResZ,
135
+ );
136
+ return isMany ? res.workspaces : res.workspaces[0];
53
137
  }
54
138
 
55
- async page(offset: number, limit: number): Promise<Workspace[]> {
56
- return await this.retriever.page(offset, limit);
57
- }
58
-
59
- async create(workspace: NewWorkspace): Promise<Workspace>;
60
-
61
- async create(
62
- workspaces: NewWorkspace | NewWorkspace[],
63
- ): Promise<Workspace | Workspace[]> {
64
- const isMany = Array.isArray(workspaces);
65
- const res = await this.writer.create(workspaces);
66
- return isMany ? res : res[0];
139
+ async retrieveByAuthor(author: string): Promise<Workspace[]> {
140
+ const res = await sendRequired(
141
+ this.client,
142
+ RETRIEVE_ENDPOINT,
143
+ { author },
144
+ retrieveReqZ,
145
+ retrieveResZ,
146
+ );
147
+ return res.workspaces;
67
148
  }
68
149
 
69
- async rename(key: Key, name: string): Promise<void> {
70
- await this.writer.rename(key, name);
150
+ async search(term: string): Promise<Workspace[]> {
151
+ const res = await sendRequired(
152
+ this.client,
153
+ RETRIEVE_ENDPOINT,
154
+ { search: term },
155
+ retrieveReqZ,
156
+ retrieveResZ,
157
+ );
158
+ return res.workspaces;
71
159
  }
72
160
 
73
- async setLayout(key: Key, layout: UnknownRecord): Promise<void> {
74
- await this.writer.setLayout(key, layout);
161
+ async page(offset: number, limit: number): Promise<Workspace[]> {
162
+ const res = await sendRequired(
163
+ this.client,
164
+ RETRIEVE_ENDPOINT,
165
+ { offset, limit },
166
+ retrieveReqZ,
167
+ retrieveResZ,
168
+ );
169
+ return res.workspaces;
75
170
  }
76
171
 
77
- async delete(...keys: Key[]): Promise<void> {
78
- await this.writer.delete(keys);
172
+ async delete(key: Key): Promise<void>;
173
+ async delete(keys: Key[]): Promise<void>;
174
+ async delete(keys: Params): Promise<void> {
175
+ const normalized = toArray(keys);
176
+ await sendRequired(
177
+ this.client,
178
+ DELETE_ENDPOINT,
179
+ { keys: normalized },
180
+ deleteReqZ,
181
+ emptyResZ,
182
+ );
79
183
  }
80
184
  }
@@ -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 workspace from "@/workspace/external";
10
+ export * as workspace from "@/workspace/client";
@@ -1,51 +1,119 @@
1
- // Copyright 2024 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 { type UnaryClient } from "@synnaxlabs/freighter";
11
- import { type UnknownRecord } from "@synnaxlabs/x/record";
12
-
13
- import { type Key, type LinePlot, type Params } from "@/workspace/lineplot/payload";
14
- import { Retriever } from "@/workspace/lineplot/retriever";
15
- import { type NewLinePlot, Writer } from "@/workspace/lineplot/writer";
1
+ import { ontology } from "@/ontology";
2
+ import { nullableArrayZ } from "@/util/zod";
3
+ import { sendRequired, UnaryClient } from "@synnaxlabs/freighter";
4
+ import { breaker, TimeSpan, toArray, UnknownRecord } from "@synnaxlabs/x";
5
+ import { unknownRecordZ } from "@synnaxlabs/x/record";
6
+ import { z } from "zod";
7
+
8
+ export const keyZ = z.string().uuid();
9
+ export type Key = z.infer<typeof keyZ>;
10
+ export type Params = Key | Key[];
11
+
12
+ export const linePlotZ = z.object({
13
+ key: z.string(),
14
+ name: z.string(),
15
+ data: unknownRecordZ.or(z.string().transform((s) => JSON.parse(s) as UnknownRecord)),
16
+ });
17
+
18
+ export type LinePlot = z.infer<typeof linePlotZ>;
19
+
20
+ export const ONTOLOGY_TYPE: ontology.ResourceType = "lineplot";
21
+
22
+ export const ontologyID = (key: Key): ontology.ID =>
23
+ new ontology.ID({ type: ONTOLOGY_TYPE, key });
24
+
25
+ const RETRIEVE_ENDPOINT = "/workspace/lineplot/retrieve";
26
+ const CREATE_ENDPOINT = "/workspace/lineplot/create";
27
+ const RENAME_ENDPOINT = "/workspace/lineplot/rename";
28
+ const SET_DATA_ENDPOINT = "/workspace/lineplot/set-data";
29
+ const DELETE_ENDPOINT = "/workspace/lineplot/delete";
30
+
31
+ export const newLinePlotZ = linePlotZ.partial({ key: true }).transform((p) => ({
32
+ ...p,
33
+ data: JSON.stringify(p.data),
34
+ }));
35
+
36
+ export type NewLinePlot = z.input<typeof newLinePlotZ>;
37
+
38
+ const retrieveReqZ = z.object({ keys: keyZ.array() });
39
+ const createReqZ = z.object({ workspace: z.string(), linePlots: newLinePlotZ.array() });
40
+ const renameReqZ = z.object({ key: keyZ, name: z.string() });
41
+ const setDataReqZ = z.object({ key: keyZ, data: z.string() });
42
+ const deleteReqZ = z.object({ keys: keyZ.array() });
43
+ const retrieveResZ = z.object({ linePlots: nullableArrayZ(linePlotZ) });
44
+ const createResZ = z.object({ linePlots: linePlotZ.array() });
45
+ const emptyResZ = z.object({});
16
46
 
17
47
  export class Client {
18
- private readonly writer: Writer;
19
- private readonly retriever: Retriever;
48
+ private readonly client: UnaryClient;
20
49
 
21
50
  constructor(client: UnaryClient) {
22
- this.writer = new Writer(client);
23
- this.retriever = new Retriever(client);
51
+ this.client = client;
24
52
  }
25
53
 
26
- async create(workspace: string, linePlot: NewLinePlot): Promise<LinePlot> {
27
- return await this.writer.create(workspace, linePlot);
54
+ async create(workspace: string, linePlot: NewLinePlot): Promise<LinePlot>;
55
+ async create(workspace: string, linePlots: NewLinePlot[]): Promise<LinePlot[]>;
56
+ async create(
57
+ workspace: string,
58
+ linePlots: NewLinePlot | NewLinePlot[],
59
+ ): Promise<LinePlot | LinePlot[]> {
60
+ const isMany = Array.isArray(linePlots);
61
+ const normalized = toArray(linePlots);
62
+ const res = await sendRequired(
63
+ this.client,
64
+ CREATE_ENDPOINT,
65
+ { workspace, linePlots: normalized },
66
+ createReqZ,
67
+ createResZ,
68
+ );
69
+ return isMany ? res.linePlots : res.linePlots[0];
28
70
  }
29
71
 
30
72
  async rename(key: Key, name: string): Promise<void> {
31
- await this.writer.rename(key, name);
73
+ await sendRequired(
74
+ this.client,
75
+ RENAME_ENDPOINT,
76
+ { key, name },
77
+ renameReqZ,
78
+ emptyResZ,
79
+ );
32
80
  }
33
81
 
34
82
  async setData(key: Key, data: UnknownRecord): Promise<void> {
35
- await this.writer.setData(key, data);
83
+ await sendRequired(
84
+ this.client,
85
+ SET_DATA_ENDPOINT,
86
+ { key, data: JSON.stringify(data) },
87
+ setDataReqZ,
88
+ emptyResZ,
89
+ );
36
90
  }
37
91
 
38
92
  async retrieve(key: Key): Promise<LinePlot>;
39
-
40
93
  async retrieve(keys: Key[]): Promise<LinePlot[]>;
41
-
42
94
  async retrieve(keys: Params): Promise<LinePlot | LinePlot[]> {
43
95
  const isMany = Array.isArray(keys);
44
- const res = await this.retriever.retrieve(keys);
45
- return isMany ? res : res[0];
96
+ const normalized = toArray(keys);
97
+ const res = await sendRequired(
98
+ this.client,
99
+ RETRIEVE_ENDPOINT,
100
+ { keys: normalized },
101
+ retrieveReqZ,
102
+ retrieveResZ,
103
+ );
104
+ return isMany ? res.linePlots : res.linePlots[0];
46
105
  }
47
106
 
107
+ async delete(key: Key): Promise<void>;
108
+ async delete(keys: Key[]): Promise<void>;
48
109
  async delete(keys: Params): Promise<void> {
49
- await this.writer.delete(keys);
110
+ const normalized = toArray(keys);
111
+ await sendRequired(
112
+ this.client,
113
+ DELETE_ENDPOINT,
114
+ { keys: normalized },
115
+ deleteReqZ,
116
+ emptyResZ,
117
+ );
50
118
  }
51
119
  }
@@ -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 linePlot from "@/workspace/lineplot/external";
10
+ export * as linePlot from "@/workspace/lineplot/client";