@synnaxlabs/client 0.23.1 → 0.25.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 (109) hide show
  1. package/.turbo/turbo-build.log +9 -10
  2. package/dist/auth/auth.d.ts +4 -4
  3. package/dist/channel/client.d.ts +13 -6
  4. package/dist/channel/client.d.ts.map +1 -1
  5. package/dist/channel/creator.d.ts +1 -1
  6. package/dist/channel/payload.d.ts +1 -1
  7. package/dist/channel/retriever.d.ts +8 -6
  8. package/dist/channel/retriever.d.ts.map +1 -1
  9. package/dist/channel/writer.d.ts +5 -3
  10. package/dist/channel/writer.d.ts.map +1 -1
  11. package/dist/client.cjs +22 -18
  12. package/dist/client.d.ts +12 -12
  13. package/dist/client.js +4406 -4865
  14. package/dist/connection/checker.d.ts +2 -2
  15. package/dist/control/client.d.ts +1 -1
  16. package/dist/control/state.d.ts +5 -5
  17. package/dist/errors.d.ts +33 -8
  18. package/dist/errors.d.ts.map +1 -1
  19. package/dist/errors.spec.d.ts +2 -0
  20. package/dist/errors.spec.d.ts.map +1 -0
  21. package/dist/framer/adapter.d.ts +3 -3
  22. package/dist/framer/client.d.ts +12 -11
  23. package/dist/framer/client.d.ts.map +1 -1
  24. package/dist/framer/deleter.d.ts +2 -2
  25. package/dist/framer/frame.d.ts +2 -2
  26. package/dist/framer/iterator.d.ts +16 -6
  27. package/dist/framer/iterator.d.ts.map +1 -1
  28. package/dist/framer/streamProxy.d.ts +1 -1
  29. package/dist/framer/streamer.d.ts +5 -5
  30. package/dist/framer/writer.d.ts +6 -6
  31. package/dist/hardware/client.d.ts +2 -2
  32. package/dist/hardware/device/client.d.ts +7 -7
  33. package/dist/hardware/device/client.d.ts.map +1 -1
  34. package/dist/hardware/rack/client.d.ts +5 -5
  35. package/dist/hardware/task/client.d.ts +22 -11
  36. package/dist/hardware/task/client.d.ts.map +1 -1
  37. package/dist/index.d.ts +2 -3
  38. package/dist/index.d.ts.map +1 -1
  39. package/dist/label/client.d.ts +6 -6
  40. package/dist/label/payload.d.ts +1 -1
  41. package/dist/label/payload.d.ts.map +1 -1
  42. package/dist/label/retriever.d.ts +2 -2
  43. package/dist/label/writer.d.ts +3 -3
  44. package/dist/ontology/client.d.ts +10 -10
  45. package/dist/ontology/group/client.d.ts +2 -2
  46. package/dist/ontology/group/external.d.ts +1 -0
  47. package/dist/ontology/group/external.d.ts.map +1 -1
  48. package/dist/ontology/group/payload.d.ts +2 -2
  49. package/dist/ontology/group/payload.d.ts.map +1 -1
  50. package/dist/ontology/group/writer.d.ts +2 -2
  51. package/dist/ontology/group/writer.d.ts.map +1 -1
  52. package/dist/ontology/payload.d.ts +35 -35
  53. package/dist/ontology/payload.d.ts.map +1 -1
  54. package/dist/ontology/writer.d.ts +1 -1
  55. package/dist/ranger/active.d.ts +1 -1
  56. package/dist/ranger/active.d.ts.map +1 -1
  57. package/dist/ranger/alias.d.ts +6 -6
  58. package/dist/ranger/client.d.ts +10 -10
  59. package/dist/ranger/kv.d.ts +2 -2
  60. package/dist/ranger/payload.d.ts +1 -1
  61. package/dist/ranger/range.d.ts +8 -8
  62. package/dist/ranger/range.d.ts.map +1 -1
  63. package/dist/ranger/writer.d.ts +1 -1
  64. package/dist/signals/observable.d.ts +4 -4
  65. package/dist/signals/observable.d.ts.map +1 -1
  66. package/dist/transport.d.ts +1 -1
  67. package/dist/util/retrieve.d.ts +1 -1
  68. package/dist/util/retrieve.d.ts.map +1 -1
  69. package/dist/util/zod.d.ts.map +1 -1
  70. package/dist/workspace/client.d.ts +6 -6
  71. package/dist/workspace/lineplot/client.d.ts +3 -3
  72. package/dist/workspace/lineplot/payload.d.ts +1 -1
  73. package/dist/workspace/lineplot/retriever.d.ts +1 -1
  74. package/dist/workspace/lineplot/writer.d.ts +3 -3
  75. package/dist/workspace/payload.d.ts +1 -1
  76. package/dist/workspace/retriever.d.ts +1 -1
  77. package/dist/workspace/schematic/client.d.ts +3 -3
  78. package/dist/workspace/schematic/payload.d.ts +1 -1
  79. package/dist/workspace/schematic/retriever.d.ts +1 -1
  80. package/dist/workspace/schematic/writer.d.ts +3 -3
  81. package/dist/workspace/writer.d.ts +3 -3
  82. package/examples/node/basicReadWrite.js +4 -4
  83. package/examples/node/package-lock.json +8 -8
  84. package/package.json +10 -11
  85. package/src/auth/auth.ts +1 -1
  86. package/src/channel/channel.spec.ts +6 -5
  87. package/src/channel/client.ts +30 -1
  88. package/src/channel/retriever.ts +60 -10
  89. package/src/channel/writer.ts +13 -10
  90. package/src/client.ts +2 -2
  91. package/src/errors.spec.ts +39 -0
  92. package/src/errors.ts +35 -7
  93. package/src/framer/client.spec.ts +6 -0
  94. package/src/framer/client.ts +25 -18
  95. package/src/framer/deleter.spec.ts +39 -38
  96. package/src/framer/iterator.spec.ts +26 -1
  97. package/src/framer/iterator.ts +15 -1
  98. package/src/framer/streamProxy.ts +1 -1
  99. package/src/framer/streamer.ts +1 -1
  100. package/src/framer/writer.spec.ts +29 -1
  101. package/src/hardware/device/client.ts +2 -2
  102. package/src/hardware/task/client.ts +45 -6
  103. package/src/hardware/task/task.spec.ts +14 -2
  104. package/src/index.ts +3 -2
  105. package/src/ontology/group/external.ts +1 -0
  106. package/src/ontology/group/payload.ts +2 -2
  107. package/src/ontology/group/writer.ts +2 -2
  108. package/src/ontology/ontology.spec.ts +1 -1
  109. package/src/ranger/active.ts +2 -2
@@ -7,7 +7,7 @@
7
7
  // License, use of this software will be governed by the Apache License, Version 2.0,
8
8
  // included in the file licenses/APL.txt.
9
9
 
10
- import {type StreamClient, UnaryClient } from "@synnaxlabs/freighter";
10
+ import { type StreamClient, UnaryClient } from "@synnaxlabs/freighter";
11
11
  import {
12
12
  type CrudeSeries,
13
13
  type CrudeTimeRange,
@@ -17,13 +17,13 @@ import {
17
17
  TimeSpan,
18
18
  } from "@synnaxlabs/x";
19
19
 
20
- import { type Key, type KeyOrName, KeysOrNames, type Params} from "@/channel/payload";
21
- import { analyzeChannelParams,type Retriever } from "@/channel/retriever";
20
+ import { type Key, type KeyOrName, KeysOrNames, type Params } from "@/channel/payload";
21
+ import { analyzeChannelParams, type Retriever } from "@/channel/retriever";
22
+ import { Deleter } from "@/framer/deleter";
22
23
  import { Frame } from "@/framer/frame";
23
- import { Iterator } from "@/framer/iterator";
24
+ import { Iterator, IteratorConfig } from "@/framer/iterator";
24
25
  import { Streamer, type StreamerConfig } from "@/framer/streamer";
25
- import { Writer, type WriterConfig,WriterMode } from "@/framer/writer";
26
- import { Deleter } from "@/framer/deleter";
26
+ import { Writer, type WriterConfig, WriterMode } from "@/framer/writer";
27
27
 
28
28
  export class Client {
29
29
  private readonly streamClient: StreamClient;
@@ -42,11 +42,16 @@ export class Client {
42
42
  * Opens a new iterator over the given channels within the provided time range.
43
43
  *
44
44
  * @param tr - A time range to iterate over.
45
- * @param keys - A list of channel keys to iterate over.
46
- * @returns a new {@link TypedIterator}.
45
+ * @param channels - A list of channels (by name or key) to iterate over.
46
+ * @param opts - see {@link IteratorConfig}
47
+ * @returns a new {@link Iterator}.
47
48
  */
48
- async openIterator(tr: CrudeTimeRange, channels: Params): Promise<Iterator> {
49
- return await Iterator._open(tr, channels, this.retriever, this.streamClient);
49
+ async openIterator(
50
+ tr: CrudeTimeRange,
51
+ channels: Params,
52
+ opts?: IteratorConfig,
53
+ ): Promise<Iterator> {
54
+ return await Iterator._open(tr, channels, this.retriever, this.streamClient, opts);
50
55
  }
51
56
 
52
57
  /**
@@ -54,7 +59,7 @@ export class Client {
54
59
  *
55
60
  * @param config - The configuration for the created writer, see documentation for
56
61
  * writerConfig for more detail.
57
- * @returns a new {@link RecordWriter}.
62
+ * @returns a new {@link Writer}.
58
63
  */
59
64
  async openWriter(config: WriterConfig | Params): Promise<Writer> {
60
65
  if (Array.isArray(config) || typeof config !== "object")
@@ -175,14 +180,16 @@ export class Client {
175
180
  return frame;
176
181
  }
177
182
 
178
- async delete(
179
- channels: Params,
180
- timeRange : TimeRange,
181
- ): Promise<void> {
182
-
183
+ async delete(channels: Params, timeRange: TimeRange): Promise<void> {
183
184
  const { normalized, variant } = analyzeChannelParams(channels);
184
185
  if (variant === "keys")
185
- return await this.deleter.delete({ keys: normalized as Key[], bounds: timeRange });
186
- return await this.deleter.delete({ names: normalized as string[], bounds: timeRange });
186
+ return await this.deleter.delete({
187
+ keys: normalized as Key[],
188
+ bounds: timeRange,
189
+ });
190
+ return await this.deleter.delete({
191
+ names: normalized as string[],
192
+ bounds: timeRange,
193
+ });
187
194
  }
188
195
  }
@@ -9,9 +9,9 @@
9
9
 
10
10
  import { DataType, Rate, TimeRange, TimeStamp } from "@synnaxlabs/x/telem";
11
11
  import { describe, expect, test } from "vitest";
12
- import { NotFoundError, UnauthorizedError } from "@/errors"
13
12
 
14
13
  import { type channel } from "@/channel";
14
+ import { NotFoundError, UnauthorizedError } from "@/errors";
15
15
  import { newClient } from "@/setupspecs";
16
16
  import { randomSeries } from "@/util/telem";
17
17
 
@@ -31,16 +31,16 @@ const newIndexDataChannelPair = async (): Promise<channel.Channel[]> => {
31
31
  leaseholder: 1,
32
32
  isIndex: true,
33
33
  dataType: DataType.TIMESTAMP,
34
- })
34
+ });
35
35
  const data = await client.channels.create({
36
36
  name: "data",
37
37
  leaseholder: 1,
38
38
  index: ind.key,
39
39
  dataType: DataType.INT64,
40
- })
40
+ });
41
41
 
42
- return [ind, data]
43
- }
42
+ return [ind, data];
43
+ };
44
44
 
45
45
  describe("Deleter", () => {
46
46
  test("Client - basic delete", async () => {
@@ -48,49 +48,49 @@ describe("Deleter", () => {
48
48
  const data = randomSeries(10, ch.dataType);
49
49
  await client.write(TimeStamp.seconds(0), ch.key, data);
50
50
 
51
- await client.delete(ch.key, TimeStamp.seconds(2).range(TimeStamp.seconds(5)))
51
+ await client.delete(ch.key, TimeStamp.seconds(2).range(TimeStamp.seconds(5)));
52
52
 
53
53
  const res = await client.read(TimeRange.MAX, ch.key);
54
54
  expect(res.length).toEqual(data.length - 3);
55
- expect(res.data.slice(0, 2)).toEqual(data.slice(0, 2))
56
- expect(res.data.slice(2)).toEqual(data.slice(5))
55
+ expect(res.data.slice(0, 2)).toEqual(data.slice(0, 2));
56
+ expect(res.data.slice(2)).toEqual(data.slice(5));
57
57
  });
58
58
  test("Client - basic delete by name", async () => {
59
- const ch = await newChannel()
59
+ const ch = await newChannel();
60
60
  const data = randomSeries(10, ch.dataType);
61
61
  await client.write(TimeStamp.seconds(0), ch.key, data);
62
62
 
63
- await client.delete(ch.name, TimeStamp.seconds(2).range(TimeStamp.seconds(5)))
63
+ await client.delete(ch.name, TimeStamp.seconds(2).range(TimeStamp.seconds(5)));
64
64
 
65
65
  const res = await client.read(TimeRange.MAX, ch.key);
66
66
  expect(res.length).toEqual(data.length - 3);
67
- expect(res.data.slice(0, 2)).toEqual(data.slice(0, 2))
68
- expect(res.data.slice(2)).toEqual(data.slice(5))
69
- })
67
+ expect(res.data.slice(0, 2)).toEqual(data.slice(0, 2));
68
+ expect(res.data.slice(2)).toEqual(data.slice(5));
69
+ });
70
70
  test("Client - delete name not found", async () => {
71
71
  const ch = await newChannel();
72
72
  const data = randomSeries(10, ch.dataType);
73
73
  await client.write(TimeStamp.seconds(0), ch.key, data);
74
74
 
75
- await expect(
76
- client.delete(["billy bob", ch.name], TimeRange.MAX)
77
- ).rejects.toThrow(NotFoundError)
75
+ await expect(client.delete(["billy bob", ch.name], TimeRange.MAX)).rejects.toThrow(
76
+ NotFoundError,
77
+ );
78
78
 
79
79
  const res = await client.read(TimeRange.MAX, ch.key);
80
80
  expect(res.data).toEqual(data);
81
- })
81
+ });
82
82
  test("Client - delete key not found", async () => {
83
83
  const ch = await newChannel();
84
84
  const data = randomSeries(10, ch.dataType);
85
85
  await client.write(TimeStamp.seconds(0), ch.key, data);
86
86
 
87
- await expect(
88
- client.delete([ch.key, 1232], TimeRange.MAX)
89
- ).rejects.toThrow(NotFoundError)
87
+ await expect(client.delete([ch.key, 1232], TimeRange.MAX)).rejects.toThrow(
88
+ NotFoundError,
89
+ );
90
90
 
91
91
  const res = await client.read(TimeRange.MAX, ch.key);
92
92
  expect(res.data).toEqual(data);
93
- })
93
+ });
94
94
 
95
95
  test("Client - delete with writer", async () => {
96
96
  const ch = await newChannel();
@@ -101,29 +101,30 @@ describe("Deleter", () => {
101
101
  });
102
102
 
103
103
  await expect(
104
- client.delete(
105
- [ch.key], TimeStamp.seconds(12).range(TimeStamp.seconds(30)))
106
- ).rejects.toThrow(UnauthorizedError)
104
+ client.delete([ch.key], TimeStamp.seconds(12).range(TimeStamp.seconds(30))),
105
+ ).rejects.toThrow(UnauthorizedError);
107
106
 
108
- await writer.close()
109
- })
107
+ await writer.close();
108
+ });
110
109
 
111
110
  test("Client - delete index channel alone", async () => {
112
- const chs = await newIndexDataChannelPair()
113
- const index = chs[0]
114
- const dat = chs[1]
115
- const data = randomSeries(10, dat.dataType)
111
+ const chs = await newIndexDataChannelPair();
112
+ const index = chs[0];
113
+ const dat = chs[1];
114
+ const data = randomSeries(10, dat.dataType);
116
115
 
117
- const time = BigInt64Array.from({ length: 10 },
118
- (_, i) => (TimeStamp.milliseconds(i)).valueOf());
116
+ const time = BigInt64Array.from({ length: 10 }, (_, i) =>
117
+ TimeStamp.milliseconds(i).valueOf(),
118
+ );
119
119
 
120
- await index.write(0, time)
121
- await dat.write(0, data)
120
+ await index.write(0, time);
121
+ await dat.write(0, data);
122
122
 
123
123
  await expect(
124
124
  client.delete(
125
- [index.key], TimeStamp.milliseconds(2).range(TimeStamp.milliseconds(5))
126
- )
127
- ).rejects.toThrow()
128
- })
125
+ [index.key],
126
+ TimeStamp.milliseconds(2).range(TimeStamp.milliseconds(5)),
127
+ ),
128
+ ).rejects.toThrow();
129
+ });
129
130
  });
@@ -8,9 +8,10 @@
8
8
  // included in the file licenses/APL.txt.
9
9
 
10
10
  import { DataType, Rate, TimeRange, TimeSpan, TimeStamp } from "@synnaxlabs/x/telem";
11
- import { describe, expect,test } from "vitest";
11
+ import { describe, expect, test } from "vitest";
12
12
 
13
13
  import { type channel } from "@/channel";
14
+ import { AUTO_SPAN } from "@/framer/iterator";
14
15
  import { newClient } from "@/setupspecs";
15
16
  import { randomSeries } from "@/util/telem";
16
17
 
@@ -59,4 +60,28 @@ describe("Iterator", () => {
59
60
  await iter.close();
60
61
  }
61
62
  });
63
+ test("chunk size", async () => {
64
+ const ch = await newChannel();
65
+ const data = Float64Array.of(0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
66
+ await ch.write(0, data);
67
+
68
+ const iter = await client.openIterator(TimeRange.MAX, [ch.key], { chunkSize: 4 });
69
+
70
+ try {
71
+ expect(await iter.seekFirst()).toBeTruthy();
72
+
73
+ expect(await iter.next(AUTO_SPAN)).toBeTruthy();
74
+ expect(iter.value.get(ch.key).data).toEqual(Float64Array.of(0, 1, 2, 3));
75
+
76
+ expect(await iter.next(AUTO_SPAN)).toBeTruthy();
77
+ expect(iter.value.get(ch.key).data).toEqual(Float64Array.of(4, 5, 6, 7));
78
+
79
+ expect(await iter.next(AUTO_SPAN)).toBeTruthy();
80
+ expect(iter.value.get(ch.key).data).toEqual(Float64Array.of(8, 9));
81
+
82
+ expect(await iter.next(AUTO_SPAN)).toBeFalsy();
83
+ } finally {
84
+ await iter.close();
85
+ }
86
+ });
62
87
  });
@@ -50,6 +50,7 @@ const reqZ = z.object({
50
50
  bounds: TimeRange.z.optional(),
51
51
  stamp: TimeStamp.z.optional(),
52
52
  keys: z.number().array().optional(),
53
+ chunkSize: z.number().optional(),
53
54
  });
54
55
 
55
56
  type Request = z.infer<typeof reqZ>;
@@ -62,6 +63,13 @@ const resZ = z.object({
62
63
  frame: frameZ.optional(),
63
64
  });
64
65
 
66
+ export interface IteratorConfig {
67
+ /** chunkSize is the maximum number of samples contained per channel in the frame
68
+ * resulting from a call to next with {@link AUTO_SPAN}.
69
+ */
70
+ chunkSize?: number;
71
+ }
72
+
65
73
  /**
66
74
  * Used to iterate over a clusters telemetry in time-order. It should not be
67
75
  * instantiated directly, and should instead be instantiated via the SegmentClient.
@@ -90,13 +98,18 @@ export class Iterator {
90
98
  * channels with the given keys within the provided time range.
91
99
  *
92
100
  * @param tr - The time range to iterate over.
93
- * @param keys - The keys of the channels to iterate over.
101
+ * @param channels - The channels for the iterator to iterate over (can be provided
102
+ * in keys or names).
103
+ * @param retriever - Retriever used to retrieve channel keys from names.
104
+ * @param client - The stream client allowing streaming of iterated data.
105
+ * @param opts - See {@link IteratorConfig}.
94
106
  */
95
107
  static async _open(
96
108
  tr: CrudeTimeRange,
97
109
  channels: Params,
98
110
  retriever: Retriever,
99
111
  client: StreamClient,
112
+ opts: IteratorConfig = {},
100
113
  ): Promise<Iterator> {
101
114
  const adapter = await ReadFrameAdapter.open(retriever, channels);
102
115
  const stream = await client.stream(Iterator.ENDPOINT, reqZ, resZ);
@@ -105,6 +118,7 @@ export class Iterator {
105
118
  command: Command.Open,
106
119
  keys: adapter.keys,
107
120
  bounds: new TimeRange(tr),
121
+ chunkSize: opts.chunkSize ?? 1e5,
108
122
  });
109
123
  return iter;
110
124
  }
@@ -39,7 +39,7 @@ export class StreamProxy<RQ extends z.ZodTypeAny, RS extends z.ZodTypeAny> {
39
39
  Please report this error to the Synnax team. ${JSON.stringify(res)}`,
40
40
  );
41
41
  if (err != null) {
42
- if (err instanceof EOF) return;
42
+ if (EOF.matches(err)) return;
43
43
  throw err;
44
44
  }
45
45
  }
@@ -68,7 +68,7 @@ export class Streamer implements AsyncIterator<Frame>, AsyncIterable<Frame> {
68
68
  const frame = await this.read();
69
69
  return { done: false, value: frame };
70
70
  } catch (err) {
71
- if (err instanceof EOF) return { done: true, value: undefined };
71
+ if (EOF.matches(err)) return { done: true, value: undefined };
72
72
  throw err;
73
73
  }
74
74
  }
@@ -117,7 +117,7 @@ describe("Writer", () => {
117
117
  test("write with errOnUnauthorized", async () => {
118
118
  const ch = await newChannel();
119
119
  const w1 = await client.openWriter({
120
- start: 0,
120
+ start: new TimeStamp(TimeSpan.milliseconds(500)),
121
121
  channels: ch.key,
122
122
  });
123
123
 
@@ -130,6 +130,34 @@ describe("Writer", () => {
130
130
  ).rejects.toThrow(UnauthorizedError);
131
131
  await w1.close();
132
132
  });
133
+ test("setAuthority", async () => {
134
+ const ch = await newChannel();
135
+ const w1 = await client.openWriter({
136
+ start: 0,
137
+ channels: ch.key,
138
+ authorities: 10,
139
+ enableAutoCommit: true,
140
+ })
141
+ const w2 = await client.openWriter({
142
+ start: 0,
143
+ channels: ch.key,
144
+ authorities: 20,
145
+ enableAutoCommit: true,
146
+ })
147
+
148
+ await w1.write(ch.key, randomSeries(10, ch.dataType))
149
+ let f = await ch.read(TimeRange.MAX)
150
+ expect(f.length).toEqual(0)
151
+
152
+ await w1.setAuthority({[ch.key]: 100});
153
+ await w1.write(ch.key, randomSeries(10, ch.dataType))
154
+
155
+ f = await ch.read(TimeRange.MAX)
156
+ expect(f.length).toEqual(10)
157
+
158
+ await w1.close()
159
+ await w2.close()
160
+ })
133
161
  });
134
162
  describe("Client", () => {
135
163
  test("Client - basic write", async () => {
@@ -7,8 +7,8 @@
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 { sendRequired,type UnaryClient } from "@synnaxlabs/freighter";
11
- import { toArray,type UnknownRecord } from "@synnaxlabs/x";
10
+ import { sendRequired, type UnaryClient } from "@synnaxlabs/freighter";
11
+ import { toArray, type UnknownRecord } from "@synnaxlabs/x";
12
12
  import { binary } from "@synnaxlabs/x/binary";
13
13
  import { type AsyncTermSearcher } from "@synnaxlabs/x/search";
14
14
  import { z } from "zod";
@@ -9,16 +9,17 @@
9
9
 
10
10
  import { sendRequired, type UnaryClient } from "@synnaxlabs/freighter";
11
11
  import { binary, type observe } from "@synnaxlabs/x";
12
+ import { id } from "@synnaxlabs/x";
12
13
  import { type UnknownRecord } from "@synnaxlabs/x/record";
13
14
  import { type AsyncTermSearcher } from "@synnaxlabs/x/search";
14
15
  import { type CrudeTimeSpan, TimeSpan } from "@synnaxlabs/x/telem";
15
16
  import { toArray } from "@synnaxlabs/x/toArray";
16
- import { nanoid } from "nanoid/non-secure";
17
17
  import { z } from "zod";
18
18
 
19
19
  import { framer } from "@/framer";
20
20
  import { type Frame } from "@/framer/frame";
21
21
  import { rack } from "@/hardware/rack";
22
+ import { signals } from "@/signals";
22
23
  import { analyzeParams, checkForMultipleOrNoResults } from "@/util/retrieve";
23
24
  import { nullableArrayZ } from "@/util/zod";
24
25
 
@@ -60,6 +61,7 @@ export const taskZ = z.object({
60
61
  key: taskKeyZ,
61
62
  name: z.string(),
62
63
  type: z.string(),
64
+ internal: z.boolean().optional(),
63
65
  config: z.record(z.unknown()).or(
64
66
  z.string().transform((c) => {
65
67
  if (c === "") return {};
@@ -114,9 +116,10 @@ export class Task<
114
116
  > {
115
117
  readonly key: TaskKey;
116
118
  readonly name: string;
119
+ readonly internal: boolean;
117
120
  readonly type: T;
118
121
  readonly config: C;
119
- readonly state?: State<D>;
122
+ state?: State<D>;
120
123
  private readonly frameClient: framer.Client;
121
124
 
122
125
  constructor(
@@ -125,12 +128,14 @@ export class Task<
125
128
  type: T,
126
129
  config: C,
127
130
  frameClient: framer.Client,
131
+ internal: boolean = false,
128
132
  state?: State<D> | null,
129
133
  ) {
130
134
  this.key = key;
131
135
  this.name = name;
132
136
  this.type = type;
133
137
  this.config = config;
138
+ this.internal = internal;
134
139
  if (state !== null) this.state = state;
135
140
  this.frameClient = frameClient;
136
141
  }
@@ -142,12 +147,13 @@ export class Task<
142
147
  type: this.type,
143
148
  config: this.config,
144
149
  state: this.state,
150
+ internal: this.internal,
145
151
  };
146
152
  }
147
153
 
148
154
  async executeCommand(type: string, args?: UnknownRecord): Promise<string> {
149
155
  const writer = await this.frameClient.openWriter(TASK_CMD_CHANNEL);
150
- const key = nanoid();
156
+ const key = id.id();
151
157
  await writer.write(TASK_CMD_CHANNEL, [{ task: this.key, type, key, args }]);
152
158
  await writer.close();
153
159
  return key;
@@ -282,7 +288,11 @@ export class Client implements AsyncTermSearcher<string, TaskKey, Payload> {
282
288
  }
283
289
 
284
290
  async page(offset: number, limit: number): Promise<Payload[]> {
285
- return this.execRetrieve({ offset, limit });
291
+ return await this.execRetrieve({ offset, limit });
292
+ }
293
+
294
+ async list(options: RetrieveOptions = {}): Promise<Task[]> {
295
+ return this.sugar(await this.execRetrieve(options));
286
296
  }
287
297
 
288
298
  async retrieve<
@@ -343,8 +353,37 @@ export class Client implements AsyncTermSearcher<string, TaskKey, Payload> {
343
353
 
344
354
  private sugar(payloads: Payload[]): Task[] {
345
355
  return payloads.map(
346
- ({ key, name, type, config, state }) =>
347
- new Task(key, name, type, config, this.frameClient, state),
356
+ ({ key, name, type, config, state, internal }) =>
357
+ new Task(key, name, type, config, this.frameClient, internal, state),
358
+ );
359
+ }
360
+
361
+ async openTracker(): Promise<signals.Observable<string, string>> {
362
+ return await signals.openObservable<string, string>(
363
+ this.frameClient,
364
+ "sy_task_set",
365
+ "sy_task_delete",
366
+ (variant, data) =>
367
+ Array.from(data).map((k) => ({
368
+ variant,
369
+ key: k.toString(),
370
+ value: k.toString(),
371
+ })),
372
+ );
373
+ }
374
+
375
+ async openStateObserver<D extends UnknownRecord = UnknownRecord>(): Promise<
376
+ StateObservable<D>
377
+ > {
378
+ return new framer.ObservableStreamer<State<D>>(
379
+ await this.frameClient.openStreamer(TASK_STATE_CHANNEL),
380
+ (frame) => {
381
+ const s = frame.get(TASK_STATE_CHANNEL);
382
+ if (s.length === 0) return [null, false];
383
+ const parse = stateZ.safeParse(s.at(-1));
384
+ if (!parse.success) return [null, false];
385
+ return [parse.data as State<D>, true];
386
+ },
348
387
  );
349
388
  }
350
389
  }
@@ -7,7 +7,7 @@
7
7
  // License, use of this software will be governed by the Apache License, Version 2.0,
8
8
  // included in the file licenses/APL.txt.
9
9
 
10
- import { nanoid } from "nanoid";
10
+ import { id } from "@synnaxlabs/x";
11
11
  import { describe, expect, it } from "vitest";
12
12
 
13
13
  import { task } from "@/hardware/task";
@@ -71,7 +71,7 @@ describe("Hardware", () => {
71
71
  dog: string;
72
72
  }
73
73
  const state: task.State<StateDetails> = {
74
- key: nanoid(),
74
+ key: id.id(),
75
75
  task: t.key,
76
76
  variant: "success",
77
77
  };
@@ -84,5 +84,17 @@ describe("Hardware", () => {
84
84
  expect(retrieved.state?.variant).toBe(state.variant);
85
85
  });
86
86
  });
87
+ describe("list", () => {
88
+ it("should list all tasks", async () => {
89
+ const t = await client.hardware.racks.create({ name: "test" });
90
+ await t.createTask({
91
+ name: "test",
92
+ config: { a: "dog" },
93
+ type: "ni",
94
+ });
95
+ const tasks = await client.hardware.tasks.list();
96
+ expect(tasks.length).toBeGreaterThan(0);
97
+ });
98
+ });
87
99
  });
88
100
  });
package/src/index.ts CHANGED
@@ -9,13 +9,14 @@
9
9
 
10
10
  export * from "@/channel";
11
11
  export { Channel } from "@/channel/client";
12
- export type { SynnaxProps } from "@/client";
13
- export { default as Synnax, synnaxPropsZ } from "@/client";
12
+ export { default as Synnax, type SynnaxProps, synnaxPropsZ } from "@/client";
14
13
  export * from "@/connection";
15
14
  export { control } from "@/control";
16
15
  export {
17
16
  AuthError,
18
17
  ContiguityError,
18
+ MultipleFoundError,
19
+ NotFoundError,
19
20
  QueryError,
20
21
  RouteError,
21
22
  UnexpectedError,
@@ -9,3 +9,4 @@
9
9
 
10
10
  export * from "@/ontology/group/client";
11
11
  export * from "@/ontology/group/group";
12
+ export * from "@/ontology/group/payload";
@@ -17,12 +17,12 @@ export type Keys = Key[];
17
17
  export type Names = Name[];
18
18
  export type Params = Key | Name | Keys | Names;
19
19
 
20
- export const payloadZ = z.object({
20
+ export const groupZ = z.object({
21
21
  key: keyZ,
22
22
  name: z.string(),
23
23
  });
24
24
 
25
- export type Payload = z.infer<typeof payloadZ>;
25
+ export type Payload = z.infer<typeof groupZ>;
26
26
 
27
27
  export type ParamAnalysisResult =
28
28
  | {
@@ -10,11 +10,11 @@
10
10
  import { type UnaryClient } from "@synnaxlabs/freighter";
11
11
  import { z } from "zod";
12
12
 
13
- import { type Payload, payloadZ } from "@/ontology/group/payload";
13
+ import { groupZ,type Payload } from "@/ontology/group/payload";
14
14
  import { type ID, idZ } from "@/ontology/payload";
15
15
 
16
16
  const resZ = z.object({
17
- group: payloadZ,
17
+ group: groupZ,
18
18
  });
19
19
 
20
20
  const createReqZ = z.object({
@@ -7,7 +7,7 @@
7
7
  // License, use of this software will be governed by the Apache License, Version 2.0,
8
8
  // included in the file licenses/APL.txt.
9
9
 
10
- import { describe, expect, it,test } from "vitest";
10
+ import { describe, expect, it, test } from "vitest";
11
11
 
12
12
  import { ontology } from "@/ontology";
13
13
  import { newClient } from "@/setupspecs";
@@ -11,7 +11,7 @@ import { sendRequired, type UnaryClient } from "@synnaxlabs/freighter";
11
11
  import { z } from "zod";
12
12
 
13
13
  import { QueryError } from "@/errors";
14
- import { type Key, keyZ,type Payload, payloadZ } from "@/ranger/payload";
14
+ import { type Key, keyZ, type Payload, payloadZ } from "@/ranger/payload";
15
15
 
16
16
  const setActiveResZ = z.object({});
17
17
 
@@ -57,7 +57,7 @@ export class Active {
57
57
  z.object({}),
58
58
  retrieveActiveResZ,
59
59
  );
60
- if (err instanceof QueryError) return null;
60
+ if (QueryError.matches(err)) return null;
61
61
  if (err != null) throw err;
62
62
  return res.range;
63
63
  }