@synnaxlabs/client 0.55.0 → 0.56.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 (197) hide show
  1. package/.turbo/turbo-build.log +10 -13
  2. package/dist/client.cjs +60 -36
  3. package/dist/client.js +6435 -4786
  4. package/dist/src/access/policy/client.d.ts +70 -80
  5. package/dist/src/access/policy/client.d.ts.map +1 -1
  6. package/dist/src/access/policy/types.gen.d.ts +18 -20
  7. package/dist/src/access/policy/types.gen.d.ts.map +1 -1
  8. package/dist/src/access/role/client.d.ts.map +1 -1
  9. package/dist/src/access/role/types.gen.d.ts +2 -2
  10. package/dist/src/actions/actions.d.ts +68 -0
  11. package/dist/src/actions/actions.d.ts.map +1 -0
  12. package/dist/src/actions/actions.spec.d.ts +2 -0
  13. package/dist/src/actions/actions.spec.d.ts.map +1 -0
  14. package/dist/src/actions/external.d.ts +2 -0
  15. package/dist/src/actions/external.d.ts.map +1 -0
  16. package/dist/src/actions/index.d.ts +2 -0
  17. package/dist/src/actions/index.d.ts.map +1 -0
  18. package/dist/src/arc/client.d.ts.map +1 -1
  19. package/dist/src/arc/compiler/types.gen.d.ts +1 -1
  20. package/dist/src/arc/compiler/types.gen.d.ts.map +1 -1
  21. package/dist/src/arc/graph/types.gen.d.ts +29 -29
  22. package/dist/src/arc/graph/types.gen.d.ts.map +1 -1
  23. package/dist/src/arc/ir/types.gen.d.ts +123 -123
  24. package/dist/src/arc/ir/types.gen.d.ts.map +1 -1
  25. package/dist/src/arc/module/types.gen.d.ts +45 -45
  26. package/dist/src/arc/program/types.gen.d.ts +45 -45
  27. package/dist/src/arc/types/types.gen.d.ts +11 -11
  28. package/dist/src/arc/types/types.gen.d.ts.map +1 -1
  29. package/dist/src/arc/types.gen.d.ts +99 -99
  30. package/dist/src/auth/auth.d.ts +3 -3
  31. package/dist/src/auth/auth.d.ts.map +1 -1
  32. package/dist/src/channel/client.d.ts +2 -2
  33. package/dist/src/channel/client.d.ts.map +1 -1
  34. package/dist/src/channel/retriever.d.ts +5 -8
  35. package/dist/src/channel/retriever.d.ts.map +1 -1
  36. package/dist/src/channel/types.gen.d.ts +3 -3
  37. package/dist/src/channel/writer.d.ts.map +1 -1
  38. package/dist/src/connection/checker.d.ts +1 -1
  39. package/dist/src/connection/checker.d.ts.map +1 -1
  40. package/dist/src/device/client.d.ts.map +1 -1
  41. package/dist/src/device/types.gen.d.ts +6 -8
  42. package/dist/src/device/types.gen.d.ts.map +1 -1
  43. package/dist/src/errors.d.ts +2 -0
  44. package/dist/src/errors.d.ts.map +1 -1
  45. package/dist/src/framer/adapter.d.ts.map +1 -1
  46. package/dist/src/framer/client.d.ts +2 -2
  47. package/dist/src/framer/codec.d.ts +9 -1
  48. package/dist/src/framer/codec.d.ts.map +1 -1
  49. package/dist/src/framer/deleter.d.ts.map +1 -1
  50. package/dist/src/framer/frame.d.ts +1 -1
  51. package/dist/src/framer/iterator.d.ts +84 -3
  52. package/dist/src/framer/iterator.d.ts.map +1 -1
  53. package/dist/src/framer/streamProxy.d.ts.map +1 -1
  54. package/dist/src/framer/streamer.d.ts +1 -3
  55. package/dist/src/framer/streamer.d.ts.map +1 -1
  56. package/dist/src/framer/types.gen.d.ts +18 -0
  57. package/dist/src/framer/types.gen.d.ts.map +1 -1
  58. package/dist/src/framer/writer.d.ts +8 -8
  59. package/dist/src/framer/writer.d.ts.map +1 -1
  60. package/dist/src/group/client.d.ts +1 -2
  61. package/dist/src/group/client.d.ts.map +1 -1
  62. package/dist/src/group/types.gen.d.ts +2 -2
  63. package/dist/src/index.d.ts +2 -1
  64. package/dist/src/index.d.ts.map +1 -1
  65. package/dist/src/label/client.d.ts +5 -8
  66. package/dist/src/label/client.d.ts.map +1 -1
  67. package/dist/src/lineplot/client.d.ts.map +1 -1
  68. package/dist/src/lineplot/types.gen.d.ts +2 -2
  69. package/dist/src/log/client.d.ts.map +1 -1
  70. package/dist/src/log/types.gen.d.ts +2 -2
  71. package/dist/src/ontology/client.d.ts +1 -3
  72. package/dist/src/ontology/client.d.ts.map +1 -1
  73. package/dist/src/ontology/payload.d.ts +12 -16
  74. package/dist/src/ontology/payload.d.ts.map +1 -1
  75. package/dist/src/ontology/types.gen.d.ts +1 -2
  76. package/dist/src/ontology/types.gen.d.ts.map +1 -1
  77. package/dist/src/ontology/writer.d.ts +5 -10
  78. package/dist/src/ontology/writer.d.ts.map +1 -1
  79. package/dist/src/rack/client.d.ts.map +1 -1
  80. package/dist/src/rack/types.gen.d.ts +3 -3
  81. package/dist/src/ranger/alias/client.d.ts.map +1 -1
  82. package/dist/src/ranger/client.d.ts.map +1 -1
  83. package/dist/src/ranger/kv/client.d.ts.map +1 -1
  84. package/dist/src/ranger/types.gen.d.ts +6 -6
  85. package/dist/src/ranger/types.gen.d.ts.map +1 -1
  86. package/dist/src/ranger/writer.d.ts +2 -3
  87. package/dist/src/ranger/writer.d.ts.map +1 -1
  88. package/dist/src/schematic/actions.d.ts +147 -0
  89. package/dist/src/schematic/actions.d.ts.map +1 -0
  90. package/dist/src/schematic/actions.gen.d.ts +484 -0
  91. package/dist/src/schematic/actions.gen.d.ts.map +1 -0
  92. package/dist/src/schematic/actions.spec.d.ts +2 -0
  93. package/dist/src/schematic/actions.spec.d.ts.map +1 -0
  94. package/dist/src/schematic/client.d.ts +53 -2
  95. package/dist/src/schematic/client.d.ts.map +1 -1
  96. package/dist/src/schematic/external.d.ts +2 -0
  97. package/dist/src/schematic/external.d.ts.map +1 -1
  98. package/dist/src/schematic/symbol/client.d.ts.map +1 -1
  99. package/dist/src/schematic/symbol/types.gen.d.ts +48 -58
  100. package/dist/src/schematic/symbol/types.gen.d.ts.map +1 -1
  101. package/dist/src/schematic/types.gen.d.ts +131 -5
  102. package/dist/src/schematic/types.gen.d.ts.map +1 -1
  103. package/dist/src/status/client.d.ts.map +1 -1
  104. package/dist/src/status/payload.d.ts +3 -3
  105. package/dist/src/table/actions.d.ts +156 -0
  106. package/dist/src/table/actions.d.ts.map +1 -0
  107. package/dist/src/table/actions.gen.d.ts +587 -0
  108. package/dist/src/table/actions.gen.d.ts.map +1 -0
  109. package/dist/src/table/client.d.ts +28 -2
  110. package/dist/src/table/client.d.ts.map +1 -1
  111. package/dist/src/table/external.d.ts +2 -0
  112. package/dist/src/table/external.d.ts.map +1 -1
  113. package/dist/src/table/types.gen.d.ts +71 -4
  114. package/dist/src/table/types.gen.d.ts.map +1 -1
  115. package/dist/src/task/client.d.ts.map +1 -1
  116. package/dist/src/task/types.gen.d.ts +7 -7
  117. package/dist/src/task/types.gen.d.ts.map +1 -1
  118. package/dist/src/user/client.d.ts +2 -2
  119. package/dist/src/user/client.d.ts.map +1 -1
  120. package/dist/src/user/types.gen.d.ts +2 -2
  121. package/dist/src/view/client.d.ts.map +1 -1
  122. package/dist/src/view/types.gen.d.ts +2 -2
  123. package/dist/src/workspace/client.d.ts.map +1 -1
  124. package/dist/src/workspace/types.gen.d.ts +3 -3
  125. package/dist/src/workspace/types.gen.d.ts.map +1 -1
  126. package/package.json +12 -11
  127. package/src/access/policy/client.ts +4 -7
  128. package/src/access/role/client.ts +6 -26
  129. package/src/actions/actions.spec.ts +229 -0
  130. package/src/actions/actions.ts +104 -0
  131. package/src/actions/external.ts +10 -0
  132. package/src/actions/index.ts +10 -0
  133. package/src/arc/client.ts +3 -7
  134. package/src/arc/compiler/types.gen.ts +2 -1
  135. package/src/arc/ir/types.gen.ts +2 -2
  136. package/src/arc/lsp.spec.ts +3 -7
  137. package/src/arc/types/types.gen.ts +3 -3
  138. package/src/auth/auth.spec.ts +12 -13
  139. package/src/auth/auth.ts +36 -34
  140. package/src/channel/batchRetriever.spec.ts +13 -4
  141. package/src/channel/client.ts +8 -6
  142. package/src/channel/retriever.ts +7 -16
  143. package/src/channel/writer.ts +4 -20
  144. package/src/connection/checker.ts +6 -6
  145. package/src/connection/connection.spec.ts +5 -8
  146. package/src/device/client.ts +5 -8
  147. package/src/device/types.gen.ts +4 -4
  148. package/src/errors.ts +9 -9
  149. package/src/framer/adapter.ts +2 -4
  150. package/src/framer/client.ts +1 -1
  151. package/src/framer/codec.spec.ts +53 -3
  152. package/src/framer/codec.ts +58 -25
  153. package/src/framer/deleter.ts +2 -8
  154. package/src/framer/iterator.ts +43 -40
  155. package/src/framer/streamProxy.ts +13 -13
  156. package/src/framer/streamer.spec.ts +12 -3
  157. package/src/framer/streamer.ts +7 -12
  158. package/src/framer/types.gen.ts +20 -0
  159. package/src/framer/writer.spec.ts +77 -0
  160. package/src/framer/writer.ts +51 -28
  161. package/src/group/client.ts +4 -7
  162. package/src/index.ts +3 -2
  163. package/src/label/client.ts +6 -16
  164. package/src/lineplot/client.ts +6 -21
  165. package/src/log/client.ts +6 -21
  166. package/src/ontology/client.ts +3 -4
  167. package/src/ontology/types.gen.ts +0 -1
  168. package/src/ontology/writer.ts +4 -7
  169. package/src/rack/client.ts +4 -7
  170. package/src/ranger/alias/client.ts +6 -11
  171. package/src/ranger/client.ts +3 -4
  172. package/src/ranger/kv/client.ts +5 -8
  173. package/src/ranger/writer.ts +4 -17
  174. package/src/schematic/access.spec.ts +6 -6
  175. package/src/schematic/actions.gen.ts +200 -0
  176. package/src/schematic/actions.spec.ts +699 -0
  177. package/src/schematic/actions.ts +168 -0
  178. package/src/schematic/client.ts +34 -30
  179. package/src/schematic/external.ts +2 -0
  180. package/src/schematic/schematic.spec.ts +233 -69
  181. package/src/schematic/symbol/client.ts +6 -11
  182. package/src/schematic/symbol/types.gen.ts +1 -10
  183. package/src/schematic/types.gen.ts +55 -6
  184. package/src/status/client.ts +4 -10
  185. package/src/table/access.spec.ts +0 -6
  186. package/src/table/actions.gen.ts +243 -0
  187. package/src/table/actions.ts +255 -0
  188. package/src/table/client.ts +21 -25
  189. package/src/table/external.ts +2 -0
  190. package/src/table/table.spec.ts +588 -43
  191. package/src/table/types.gen.ts +58 -5
  192. package/src/task/client.ts +7 -11
  193. package/src/task/types.gen.ts +8 -6
  194. package/src/user/client.ts +6 -11
  195. package/src/view/client.ts +4 -7
  196. package/src/workspace/client.ts +6 -16
  197. package/src/workspace/types.gen.ts +2 -1
@@ -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 { DataType, errors, Rate } from "@synnaxlabs/x";
10
+ import { DataType, errors, Rate, TimeSpan } from "@synnaxlabs/x";
11
11
  import { describe, expect, it, vi } from "vitest";
12
12
 
13
13
  import { channel } from "@/channel";
@@ -57,7 +57,10 @@ describe("channelchannel.Retriever", () => {
57
57
  operations: [],
58
58
  }));
59
59
  });
60
- const retriever = new channel.DebouncedBatchRetriever(base, 10);
60
+ const retriever = new channel.DebouncedBatchRetriever(
61
+ base,
62
+ TimeSpan.milliseconds(10),
63
+ );
61
64
  const res = await Promise.all([
62
65
  retriever.retrieve([1]),
63
66
  retriever.retrieve([2]),
@@ -86,7 +89,10 @@ describe("channelchannel.Retriever", () => {
86
89
  operations: [],
87
90
  }));
88
91
  });
89
- const retriever = new channel.DebouncedBatchRetriever(base, 10);
92
+ const retriever = new channel.DebouncedBatchRetriever(
93
+ base,
94
+ TimeSpan.milliseconds(10),
95
+ );
90
96
  const res = await Promise.all([
91
97
  retriever.retrieve([1]),
92
98
  retriever.retrieve([2]),
@@ -100,7 +106,10 @@ describe("channelchannel.Retriever", () => {
100
106
  const base = new MockRetriever(async (): Promise<channel.Payload[]> => {
101
107
  throw new Error("failed to fetch");
102
108
  });
103
- const retriever = new channel.DebouncedBatchRetriever(base, 10);
109
+ const retriever = new channel.DebouncedBatchRetriever(
110
+ base,
111
+ TimeSpan.milliseconds(10),
112
+ );
104
113
  await expect(retriever.retrieve([1])).rejects.toThrow("failed to fetch");
105
114
  });
106
115
  });
@@ -7,15 +7,17 @@
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";
10
+ import { type UnaryClient } from "@synnaxlabs/freighter";
11
11
  import {
12
12
  array,
13
13
  type CrudeDensity,
14
14
  type CrudeTimeRange,
15
+ type CrudeTimeSpan,
15
16
  type CrudeTimeStamp,
16
17
  DataType,
17
18
  type MultiSeries,
18
19
  status,
20
+ TimeSpan,
19
21
  type TypedArray,
20
22
  } from "@synnaxlabs/x";
21
23
  import { z } from "zod";
@@ -387,8 +389,7 @@ export class Client {
387
389
  */
388
390
  async delete(params: Params): Promise<void> {
389
391
  const { normalized, variant } = analyzeParams(params);
390
- if (variant === "keys")
391
- return await this.writer.delete({ keys: normalized as Key[] });
392
+ if (variant === "keys") return await this.writer.delete({ keys: normalized });
392
393
  return await this.writer.delete({ names: normalized as string[] });
393
394
  }
394
395
 
@@ -398,7 +399,9 @@ export class Client {
398
399
  return await this.writer.rename(array.toArray(keys), array.toArray(names));
399
400
  }
400
401
 
401
- createDebouncedBatchRetriever(deb: number = 10): Retriever {
402
+ createDebouncedBatchRetriever(
403
+ deb: CrudeTimeSpan = TimeSpan.milliseconds(10),
404
+ ): Retriever {
402
405
  return new CacheRetriever(
403
406
  new DebouncedBatchRetriever(new ClusterRetriever(this.client), deb),
404
407
  );
@@ -414,8 +417,7 @@ export class Client {
414
417
  }
415
418
 
416
419
  async retrieveGroup(): Promise<group.Group> {
417
- const res = await sendRequired(
418
- this.client,
420
+ const res = await this.client.send(
419
421
  "/channel/retrieve-group",
420
422
  {},
421
423
  retrieveGroupReqZ,
@@ -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 { array, DataType, debounce, zod } from "@synnaxlabs/x";
10
+ import { type UnaryClient } from "@synnaxlabs/freighter";
11
+ import { array, type CrudeTimeSpan, DataType, debounce, zod } from "@synnaxlabs/x";
12
12
  import { Mutex } from "async-mutex";
13
13
  import { z } from "zod";
14
14
 
@@ -36,13 +36,10 @@ const reqZ = z.object({
36
36
  internal: z.boolean().optional(),
37
37
  legacyCalculated: z.boolean().optional(),
38
38
  });
39
- export interface RetrieveRequest extends z.input<typeof reqZ> {}
39
+ export type RetrieveRequest = z.input<typeof reqZ>;
40
40
 
41
- export interface RetrieveOptions extends Omit<
42
- RetrieveRequest,
43
- "keys" | "names" | "search"
44
- > {}
45
- export interface PageOptions extends Omit<RetrieveOptions, "offset" | "limit"> {}
41
+ export type RetrieveOptions = Omit<RetrieveRequest, "keys" | "names" | "search">;
42
+ export type PageOptions = Omit<RetrieveOptions, "offset" | "limit">;
46
43
 
47
44
  const resZ = z.object({ channels: array.nullishToEmpty(payloadZ) });
48
45
 
@@ -86,13 +83,7 @@ export class ClusterRetriever implements Retriever {
86
83
  }
87
84
 
88
85
  private async execute(request: RetrieveRequest): Promise<Payload[]> {
89
- const res = await sendRequired(
90
- this.client,
91
- "/channel/retrieve",
92
- request,
93
- reqZ,
94
- resZ,
95
- );
86
+ const res = await this.client.send("/channel/retrieve", request, reqZ, resZ);
96
87
  return res.channels;
97
88
  }
98
89
  }
@@ -204,7 +195,7 @@ export class DebouncedBatchRetriever implements Retriever {
204
195
  private readonly wrapped: Retriever;
205
196
  private readonly debouncedRun: () => void;
206
197
 
207
- constructor(wrapped: Retriever, deb: number) {
198
+ constructor(wrapped: Retriever, deb: CrudeTimeSpan) {
208
199
  this.wrapped = wrapped;
209
200
  this.debouncedRun = debounce(() => {
210
201
  void this.run();
@@ -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 { sendRequired, type UnaryClient } from "@synnaxlabs/freighter";
10
+ import { type UnaryClient } from "@synnaxlabs/freighter";
11
11
  import { type DataType } from "@synnaxlabs/x";
12
12
  import { z } from "zod";
13
13
 
@@ -47,11 +47,7 @@ export class Writer {
47
47
  }
48
48
 
49
49
  async create(channels: New[]): Promise<Payload[]> {
50
- const { channels: created } = await sendRequired<
51
- typeof createReqZ,
52
- typeof createResZ
53
- >(
54
- this.client,
50
+ const { channels: created } = await this.client.send(
55
51
  "/channel/create",
56
52
  {
57
53
  channels: channels.map((c) => ({
@@ -68,25 +64,13 @@ export class Writer {
68
64
 
69
65
  async delete(props: DeleteProps): Promise<void> {
70
66
  const keys = keyZ.array().parse(props.keys ?? []);
71
- await sendRequired<typeof deleteReqZ, typeof deleteResZ>(
72
- this.client,
73
- "/channel/delete",
74
- props,
75
- deleteReqZ,
76
- deleteResZ,
77
- );
67
+ await this.client.send("/channel/delete", props, deleteReqZ, deleteResZ);
78
68
  if (keys.length > 0) this.cache.delete(keys);
79
69
  if (props.names != null) this.cache.delete(props.names);
80
70
  }
81
71
 
82
72
  async rename(keys: Key[], names: string[]): Promise<void> {
83
- await sendRequired<typeof renameReqZ, typeof renameResZ>(
84
- this.client,
85
- "/channel/rename",
86
- { keys, names },
87
- renameReqZ,
88
- renameResZ,
89
- );
73
+ await this.client.send("/channel/rename", { keys, names }, renameReqZ, renameResZ);
90
74
  this.cache.rename(keys, names);
91
75
  }
92
76
  }
@@ -7,10 +7,11 @@
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";
10
+ import { type UnaryClient } from "@synnaxlabs/freighter";
11
11
  import {
12
12
  ClockSkewCalculator,
13
13
  type CrudeTimeSpan,
14
+ errors,
14
15
  migrate,
15
16
  TimeSpan,
16
17
  TimeStamp,
@@ -85,14 +86,14 @@ export class Checker {
85
86
  */
86
87
  constructor(
87
88
  client: UnaryClient,
88
- pollFreq: TimeSpan = TimeSpan.seconds(30),
89
+ pollFreq: CrudeTimeSpan = TimeSpan.seconds(30),
89
90
  clientVersion: string,
90
91
  name?: string,
91
92
  clockSkewThreshold: CrudeTimeSpan = TimeSpan.seconds(1),
92
93
  ) {
93
94
  this._state = { ...DEFAULT };
94
95
  this.client = client;
95
- this.pollFrequency = pollFreq;
96
+ this.pollFrequency = new TimeSpan(pollFreq);
96
97
  this.clientVersion = clientVersion;
97
98
  this.name = name;
98
99
  this.skewCalc = new ClockSkewCalculator();
@@ -117,8 +118,7 @@ export class Checker {
117
118
  this.checking = true;
118
119
  try {
119
120
  if (measureSkew) this.skewCalc.start();
120
- const res = await sendRequired(
121
- this.client,
121
+ const res = await this.client.send(
122
122
  "/connectivity/check",
123
123
  undefined,
124
124
  requestZ,
@@ -172,7 +172,7 @@ export class Checker {
172
172
  this._state.clientVersion = this.clientVersion;
173
173
  } catch (err) {
174
174
  this._state.status = "failed";
175
- this._state.error = err as Error;
175
+ this._state.error = errors.fromUnknown(err);
176
176
  this._state.message = this.state.error?.message;
177
177
  } finally {
178
178
  this.checking = false;
@@ -90,14 +90,11 @@ describe("connectivity", () => {
90
90
  });
91
91
  describe("clock skew", () => {
92
92
  const createMockClient = (nodeTime: TimeStamp): UnaryClient => ({
93
- send: vi.fn().mockResolvedValue([
94
- {
95
- clusterKey: "test-cluster",
96
- nodeVersion: __VERSION__,
97
- nodeTime,
98
- },
99
- null,
100
- ]) as UnaryClient["send"],
93
+ send: vi.fn().mockResolvedValue({
94
+ clusterKey: "test-cluster",
95
+ nodeVersion: __VERSION__,
96
+ nodeTime,
97
+ }),
101
98
  use: vi.fn(),
102
99
  });
103
100
 
@@ -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 { sendRequired, type UnaryClient } from "@synnaxlabs/freighter";
10
+ import { type UnaryClient } from "@synnaxlabs/freighter";
11
11
  import { array, type record, zod } from "@synnaxlabs/x";
12
12
  import { z } from "zod";
13
13
 
@@ -122,10 +122,9 @@ export class Client {
122
122
  async retrieve(
123
123
  args: RetrieveArgs & { schemas?: DeviceSchemas },
124
124
  ): Promise<Device | Array<Device>> {
125
- const { schemas, ...rest } = args as RetrieveArgs & { schemas?: DeviceSchemas };
125
+ const { schemas, ...rest } = args;
126
126
  const isSingle = typeof rest === "object" && "key" in rest;
127
- const res = await sendRequired(
128
- this.client,
127
+ const res = await this.client.send(
129
128
  "/device/retrieve",
130
129
  rest,
131
130
  retrieveArgsZ,
@@ -166,8 +165,7 @@ export class Client {
166
165
  schemas?: DeviceSchemas,
167
166
  ): Promise<Device | Device[]> {
168
167
  const isSingle = !Array.isArray(devices);
169
- const res = await sendRequired(
170
- this.client,
168
+ const res = await this.client.send(
171
169
  "/device/create",
172
170
  { devices: array.toArray(devices) },
173
171
  createReqZ(schemas),
@@ -177,8 +175,7 @@ export class Client {
177
175
  }
178
176
 
179
177
  async delete(keys: Key | Key[]): Promise<void> {
180
- await sendRequired(
181
- this.client,
178
+ await this.client.send(
182
179
  "/device/delete",
183
180
  { keys: array.toArray(keys) },
184
181
  deleteReqZ,
@@ -15,18 +15,18 @@ import { z } from "zod";
15
15
  import { ontology } from "@/ontology";
16
16
  import { rack } from "@/rack";
17
17
 
18
+ export const keyZ = z.string();
19
+ export type Key = z.infer<typeof keyZ>;
20
+
18
21
  /** StatusDetails contains device-specific status details identifying the device and its associated rack. */
19
22
  export const statusDetailsZ = z.object({
20
23
  /** rack is the key of the rack this device belongs to. */
21
24
  rack: rack.keyZ,
22
25
  /** device is the device identifier. */
23
- device: z.string(),
26
+ device: keyZ,
24
27
  });
25
28
  export interface StatusDetails extends z.infer<typeof statusDetailsZ> {}
26
29
 
27
- export const keyZ = z.string();
28
- export type Key = z.infer<typeof keyZ>;
29
-
30
30
  export const statusZ = status.statusZ({ details: statusDetailsZ });
31
31
  export type Status = z.infer<typeof statusZ>;
32
32
 
package/src/errors.ts CHANGED
@@ -157,15 +157,15 @@ export const validateFieldNotNull = (
157
157
  };
158
158
 
159
159
  export const errorsMiddleware: Middleware = async (ctx, next) => {
160
- const [res, err] = await next(ctx);
161
- if (err == null) return [res, err];
162
- if (err instanceof Unreachable)
163
- return [
164
- res,
165
- new Unreachable({
160
+ try {
161
+ return await next(ctx);
162
+ } catch (err) {
163
+ if (err instanceof Unreachable)
164
+ throw new Unreachable({
166
165
  message: `Cannot reach Core at ${err.url.host}:${err.url.port}`,
167
166
  url: err.url,
168
- }),
169
- ];
170
- return [res, err];
167
+ cause: err.cause ?? err,
168
+ });
169
+ throw errors.fromUnknown(err);
170
+ }
171
171
  };
@@ -164,16 +164,14 @@ export class WriteAdapter {
164
164
  throw new ValidationError(`
165
165
  Received a single channel name or key but no series.
166
166
  `);
167
- if (Array.isArray(series)) {
167
+ if (Array.isArray(series))
168
168
  if (series.some((s) => s instanceof Series || Array.isArray(s)))
169
169
  throw new ValidationError(`
170
170
  Received a single channel name or key but multiple series.
171
171
  `);
172
172
 
173
- series = series as CrudeSeries;
174
- }
175
173
  const pld = await this.fetchChannel(columnsOrData);
176
- const s = new Series({ data: series as CrudeSeries, dataType: pld.dataType });
174
+ const s = new Series({ data: series, dataType: pld.dataType });
177
175
  return new Frame(pld.key, s);
178
176
  }
179
177
 
@@ -152,7 +152,7 @@ export class Client {
152
152
  tr: CrudeTimeRange | ReadRequest,
153
153
  channels?: channel.Params,
154
154
  ): Promise<MultiSeries | Frame | ReadableStream<Uint8Array>> {
155
- if (!("start" in tr)) return this.reader.read(tr);
155
+ if (!("start" in tr)) return await this.reader.read(tr);
156
156
  const { single } = channel.analyzeParams(channels!);
157
157
  const fr = await this.readFrame(tr, channels!);
158
158
  if (single) return fr.get(channels as channel.Key | channel.Name);
@@ -17,11 +17,13 @@ import { framer } from "@/framer";
17
17
  import {
18
18
  Codec,
19
19
  HIGH_PERF_SPECIAL_CHAR,
20
- LOW_PER_SPECIAL_CHAR,
20
+ LOW_PERF_SPECIAL_CHAR,
21
+ WSIteratorCodec,
21
22
  WSWriterCodec,
22
23
  } from "@/framer/codec";
23
24
  import { Frame } from "@/framer/frame";
24
- import { WriterCommand } from "@/framer/types.gen";
25
+ import { type IteratorResponse } from "@/framer/iterator";
26
+ import { IteratorResponseVariant, WriterCommand } from "@/framer/types.gen";
25
27
  import { type WriteRequest } from "@/framer/writer";
26
28
 
27
29
  describe("encoder", () => {
@@ -325,9 +327,57 @@ describe("encoder", () => {
325
327
  const codec = new WSWriterCodec(baseCodec);
326
328
  const msg: WebsocketMessage<WriteRequest> = { type: "data", payload: writeReq };
327
329
  const encoded = codec.encode(msg);
328
- expect(encoded[0]).toEqual(LOW_PER_SPECIAL_CHAR);
330
+ expect(encoded[0]).toEqual(LOW_PERF_SPECIAL_CHAR);
329
331
  const decoded = codec.decode(encoded);
330
332
  expect(decoded).toEqual(msg);
331
333
  });
332
334
  });
335
+
336
+ describe("websocket iterator codec", () => {
337
+ it("should JSON-encode an iterator request without a special char prefix", () => {
338
+ const baseCodec = new Codec([1], [DataType.INT32]);
339
+ const codec = new WSIteratorCodec(baseCodec);
340
+ const msg: WebsocketMessage<unknown> = {
341
+ type: "data",
342
+ payload: { command: 1, span: 0 },
343
+ };
344
+ const encoded = codec.encode(msg);
345
+ // Iterator requests are sent as raw JSON; no special-char prefix.
346
+ // Confirm the first byte is JSON ('{' or '[').
347
+ expect([0x7b, 0x5b]).toContain(encoded[0]);
348
+ });
349
+
350
+ it("should binary-decode a data variant response and synthesize variant=Data", () => {
351
+ const baseCodec = new Codec([1], [DataType.INT32]);
352
+ const fr = new framer.Frame([1], [new Series(new Int32Array([1, 2, 3]))]);
353
+ const codec = new WSIteratorCodec(baseCodec);
354
+ const encoded = new Uint8Array(baseCodec.encode(fr.toPayload(), 1));
355
+ encoded[0] = HIGH_PERF_SPECIAL_CHAR;
356
+ const decoded = codec.decode(encoded) as WebsocketMessage<IteratorResponse>;
357
+ expect(decoded.payload?.variant).toEqual(IteratorResponseVariant.Data);
358
+ const decodedFr = new Frame(decoded.payload?.frame);
359
+ expect(decodedFr.series[0].data).toEqual(fr.series[0].data);
360
+ });
361
+
362
+ it("should JSON-decode an ack variant response prefixed with the low-perf char", () => {
363
+ const baseCodec = new Codec([1], [DataType.INT32]);
364
+ const codec = new WSIteratorCodec(baseCodec);
365
+ const ackMsg: WebsocketMessage<IteratorResponse> = {
366
+ type: "data",
367
+ payload: {
368
+ variant: IteratorResponseVariant.Ack,
369
+ ack: true,
370
+ command: 1,
371
+ },
372
+ };
373
+ // Simulate what the server sends: LOW_PERF_SPECIAL_CHAR + JSON-encoded message.
374
+ const json = new TextEncoder().encode(JSON.stringify(ackMsg));
375
+ const encoded = new Uint8Array(json.byteLength + 1);
376
+ encoded[0] = LOW_PERF_SPECIAL_CHAR;
377
+ encoded.set(json, 1);
378
+ const decoded = codec.decode(encoded) as WebsocketMessage<IteratorResponse>;
379
+ expect(decoded.payload?.variant).toEqual(IteratorResponseVariant.Ack);
380
+ expect(decoded.payload?.ack).toEqual(true);
381
+ });
382
+ });
333
383
  });
@@ -21,7 +21,7 @@ import { type channel } from "@/channel";
21
21
  import { ValidationError } from "@/errors";
22
22
  import { type Frame, type Payload } from "@/framer/frame";
23
23
  import { type StreamerResponse } from "@/framer/streamer";
24
- import { WriterCommand } from "@/framer/types.gen";
24
+ import { IteratorResponseVariant, WriterCommand } from "@/framer/types.gen";
25
25
  import { type WriteRequest } from "@/framer/writer";
26
26
 
27
27
  const seriesPldLength = (series: SeriesPayload): number =>
@@ -63,7 +63,7 @@ interface CodecState {
63
63
  }
64
64
 
65
65
  export class Codec {
66
- contentType: string = "application/sy-framer";
66
+ contentType: string = CONTENT_TYPE;
67
67
  private states: Map<number, CodecState> = new Map();
68
68
  private currState: CodecState | undefined;
69
69
  private seqNum: number = 0;
@@ -269,36 +269,26 @@ export class Codec {
269
269
  index += ALIGNMENT_SIZE;
270
270
  }
271
271
 
272
- if (channelFlag) returnFrame.keys = [...state.keys];
273
- state.keys.forEach((k, i) => {
274
- if (!channelFlag) {
275
- if (index >= view.byteLength) return;
276
- const frameKey = view.getUint32(index, true);
277
- if (frameKey !== k) return;
278
- index += KEY_SIZE;
279
- returnFrame.keys.push(k);
280
- }
281
- const dataType = state.keyDataTypes.get(k) as DataType;
272
+ const decodeSeries = (k: channel.Key): boolean => {
273
+ const dataType = state.keyDataTypes.get(k);
274
+ if (dataType == null) return false;
282
275
  currSize = 0;
283
276
  if (!sizeFlag) {
284
- if (index + DATA_LENGTH_SIZE > view.byteLength) return;
277
+ if (index + DATA_LENGTH_SIZE > view.byteLength) return false;
285
278
  currSize = view.getUint32(index, true);
286
279
  index += DATA_LENGTH_SIZE;
287
280
  } else currSize = sizeRepresentation;
288
281
 
289
282
  let dataByteLength = currSize;
290
283
  if (!dataType.isVariable) dataByteLength *= dataType.density.valueOf();
291
- if (index + dataByteLength > view.byteLength) {
292
- returnFrame.keys.splice(i, 1);
293
- return;
294
- }
284
+ if (index + dataByteLength > view.byteLength) return false;
295
285
  const currSeries: SeriesPayload = {
296
286
  dataType,
297
287
  data: src.slice(index, index + dataByteLength).buffer,
298
288
  };
299
289
  index += dataByteLength;
300
290
  if (!equalTimeRangesFlag && !timeRangesZeroFlag) {
301
- if (index + TIMESTAMP_SIZE * 2 > view.byteLength) return;
291
+ if (index + TIMESTAMP_SIZE * 2 > view.byteLength) return false;
302
292
  const start = view.getBigUint64(index, true);
303
293
  index += TIMESTAMP_SIZE;
304
294
  const end = view.getBigUint64(index, true);
@@ -312,7 +302,7 @@ export class Codec {
312
302
  else currSeries.timeRange = new TimeRange({ start: 0n, end: 0n });
313
303
 
314
304
  if (!equalAlignmentsFlag && !zeroAlignmentsFlag) {
315
- if (index + ALIGNMENT_SIZE > view.byteLength) return;
305
+ if (index + ALIGNMENT_SIZE > view.byteLength) return false;
316
306
  currAlignment = view.getBigUint64(index, true);
317
307
  index += ALIGNMENT_SIZE;
318
308
  currSeries.alignment = currAlignment;
@@ -320,16 +310,27 @@ export class Codec {
320
310
  else currSeries.alignment = 0n;
321
311
 
322
312
  returnFrame.series.push(currSeries);
323
- });
313
+ returnFrame.keys.push(k);
314
+ return true;
315
+ };
316
+
317
+ if (channelFlag) state.keys.forEach((k) => decodeSeries(k));
318
+ else
319
+ while (index < view.byteLength) {
320
+ if (index + KEY_SIZE > view.byteLength) break;
321
+ const frameKey = view.getUint32(index, true);
322
+ index += KEY_SIZE;
323
+ if (!decodeSeries(frameKey)) break;
324
+ }
324
325
  return returnFrame;
325
326
  }
326
327
  }
327
328
 
328
- export const LOW_PER_SPECIAL_CHAR = 254;
329
- const LOW_PERF_SPECIAL_CHAR_BUF = new Uint8Array([LOW_PER_SPECIAL_CHAR]);
329
+ export const LOW_PERF_SPECIAL_CHAR = 254;
330
+ const LOW_PERF_SPECIAL_CHAR_BUF = new Uint8Array([LOW_PERF_SPECIAL_CHAR]);
330
331
  export const HIGH_PERF_SPECIAL_CHAR = 255;
331
332
  const HIGH_PERF_SPECIAL_CHAR_BUF = new Uint8Array([HIGH_PERF_SPECIAL_CHAR]);
332
- const CONTENT_TYPE = "application/sy-framer";
333
+ const CONTENT_TYPE = "application/vnd.synnax.frame";
333
334
 
334
335
  export class WSWriterCodec implements binary.Codec {
335
336
  contentType = CONTENT_TYPE;
@@ -358,7 +359,7 @@ export class WSWriterCodec implements binary.Codec {
358
359
  decode<P extends z.ZodType>(data: Uint8Array | ArrayBuffer, schema?: P): z.infer<P> {
359
360
  const dv = new DataView(data instanceof Uint8Array ? data.buffer : data);
360
361
  const codec = dv.getUint8(0);
361
- if (codec === LOW_PER_SPECIAL_CHAR)
362
+ if (codec === LOW_PERF_SPECIAL_CHAR)
362
363
  return this.lowPerfCodec.decode(data.slice(1), schema);
363
364
  const v: WebsocketMessage<WriteRequest> = { type: "data" };
364
365
  const frame = this.base.decode(data, 1);
@@ -384,7 +385,7 @@ export class WSStreamerCodec implements binary.Codec {
384
385
  decode<P extends z.ZodType>(data: Uint8Array | ArrayBuffer, schema?: P): z.infer<P> {
385
386
  const dv = new DataView(data instanceof Uint8Array ? data.buffer : data);
386
387
  const codec = dv.getUint8(0);
387
- if (codec === LOW_PER_SPECIAL_CHAR)
388
+ if (codec === LOW_PERF_SPECIAL_CHAR)
388
389
  return this.lowPerfCodec.decode(data.slice(1), schema);
389
390
  const v: WebsocketMessage<StreamerResponse> = {
390
391
  type: "data",
@@ -393,3 +394,35 @@ export class WSStreamerCodec implements binary.Codec {
393
394
  return v as z.infer<P>;
394
395
  }
395
396
  }
397
+
398
+ export class WSIteratorCodec implements binary.Codec {
399
+ contentType = CONTENT_TYPE;
400
+ private base: Codec;
401
+ private lowPerfCodec: binary.Codec;
402
+
403
+ constructor(base: Codec) {
404
+ this.base = base;
405
+ this.lowPerfCodec = binary.JSON_CODEC;
406
+ }
407
+
408
+ encode(payload: unknown): Uint8Array<ArrayBuffer> {
409
+ return this.lowPerfCodec.encode(payload);
410
+ }
411
+
412
+ decode<P extends z.ZodType>(data: Uint8Array | ArrayBuffer, schema?: P): z.infer<P> {
413
+ const dv = new DataView(data instanceof Uint8Array ? data.buffer : data);
414
+ const codec = dv.getUint8(0);
415
+ if (codec === LOW_PERF_SPECIAL_CHAR)
416
+ return this.lowPerfCodec.decode(data.slice(1), schema);
417
+ const v: WebsocketMessage<unknown> = {
418
+ type: "data",
419
+ payload: {
420
+ variant: IteratorResponseVariant.Data,
421
+ ack: false,
422
+ command: 0,
423
+ frame: this.base.decode(data, 1),
424
+ },
425
+ };
426
+ return v as z.infer<P>;
427
+ }
428
+ }
@@ -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 { sendRequired, type UnaryClient } from "@synnaxlabs/freighter";
10
+ import { type UnaryClient } from "@synnaxlabs/freighter";
11
11
  import { TimeRange } from "@synnaxlabs/x";
12
12
  import { z } from "zod";
13
13
 
@@ -33,12 +33,6 @@ export class Deleter {
33
33
  }
34
34
 
35
35
  async delete(props: Request): Promise<void> {
36
- await sendRequired<typeof reqZ, typeof resZ>(
37
- this.client,
38
- "/frame/delete",
39
- props,
40
- reqZ,
41
- resZ,
42
- );
36
+ await this.client.send("/frame/delete", props, reqZ, resZ);
43
37
  }
44
38
  }