@synnaxlabs/client 0.41.0 → 0.42.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 (133) hide show
  1. package/.turbo/turbo-build.log +6 -6
  2. package/dist/access/payload.d.ts +7 -1
  3. package/dist/access/payload.d.ts.map +1 -1
  4. package/dist/access/policy/payload.d.ts +182 -142
  5. package/dist/access/policy/payload.d.ts.map +1 -1
  6. package/dist/access/policy/retriever.d.ts +25 -22
  7. package/dist/access/policy/retriever.d.ts.map +1 -1
  8. package/dist/auth/auth.d.ts +1 -7
  9. package/dist/auth/auth.d.ts.map +1 -1
  10. package/dist/channel/client.d.ts +2 -7
  11. package/dist/channel/client.d.ts.map +1 -1
  12. package/dist/channel/payload.d.ts +13 -74
  13. package/dist/channel/payload.d.ts.map +1 -1
  14. package/dist/channel/retriever.d.ts +5 -31
  15. package/dist/channel/retriever.d.ts.map +1 -1
  16. package/dist/channel/writer.d.ts +6 -18
  17. package/dist/channel/writer.d.ts.map +1 -1
  18. package/dist/client.cjs +36 -31
  19. package/dist/client.d.ts +8 -56
  20. package/dist/client.d.ts.map +1 -1
  21. package/dist/client.js +6486 -3979
  22. package/dist/connection/checker.d.ts +22 -39
  23. package/dist/connection/checker.d.ts.map +1 -1
  24. package/dist/control/client.d.ts.map +1 -1
  25. package/dist/control/state.d.ts +6 -26
  26. package/dist/control/state.d.ts.map +1 -1
  27. package/dist/errors.d.ts +31 -56
  28. package/dist/errors.d.ts.map +1 -1
  29. package/dist/framer/adapter.d.ts +4 -0
  30. package/dist/framer/adapter.d.ts.map +1 -1
  31. package/dist/framer/client.d.ts +2 -2
  32. package/dist/framer/client.d.ts.map +1 -1
  33. package/dist/framer/codec.d.ts +34 -0
  34. package/dist/framer/codec.d.ts.map +1 -0
  35. package/dist/framer/codec.spec.d.ts +2 -0
  36. package/dist/framer/codec.spec.d.ts.map +1 -0
  37. package/dist/framer/deleter.d.ts +12 -49
  38. package/dist/framer/deleter.d.ts.map +1 -1
  39. package/dist/framer/frame.d.ts +26 -88
  40. package/dist/framer/frame.d.ts.map +1 -1
  41. package/dist/framer/iterator.d.ts.map +1 -1
  42. package/dist/framer/streamer.d.ts +69 -11
  43. package/dist/framer/streamer.d.ts.map +1 -1
  44. package/dist/framer/writer.d.ts +60 -257
  45. package/dist/framer/writer.d.ts.map +1 -1
  46. package/dist/hardware/device/client.d.ts +7 -31
  47. package/dist/hardware/device/client.d.ts.map +1 -1
  48. package/dist/hardware/device/payload.d.ts +11 -93
  49. package/dist/hardware/device/payload.d.ts.map +1 -1
  50. package/dist/hardware/rack/payload.d.ts +15 -103
  51. package/dist/hardware/rack/payload.d.ts.map +1 -1
  52. package/dist/hardware/task/client.d.ts +4 -20
  53. package/dist/hardware/task/client.d.ts.map +1 -1
  54. package/dist/hardware/task/payload.d.ts +41 -116
  55. package/dist/hardware/task/payload.d.ts.map +1 -1
  56. package/dist/index.d.ts +0 -2
  57. package/dist/index.d.ts.map +1 -1
  58. package/dist/label/payload.d.ts +1 -9
  59. package/dist/label/payload.d.ts.map +1 -1
  60. package/dist/label/writer.d.ts +27 -36
  61. package/dist/label/writer.d.ts.map +1 -1
  62. package/dist/ontology/client.d.ts +46 -36
  63. package/dist/ontology/client.d.ts.map +1 -1
  64. package/dist/ontology/group/payload.d.ts +1 -7
  65. package/dist/ontology/group/payload.d.ts.map +1 -1
  66. package/dist/ontology/payload.d.ts +239 -146
  67. package/dist/ontology/payload.d.ts.map +1 -1
  68. package/dist/ranger/client.d.ts +13 -58
  69. package/dist/ranger/client.d.ts.map +1 -1
  70. package/dist/ranger/kv.d.ts +7 -49
  71. package/dist/ranger/kv.d.ts.map +1 -1
  72. package/dist/ranger/payload.d.ts +21 -99
  73. package/dist/ranger/payload.d.ts.map +1 -1
  74. package/dist/ranger/writer.d.ts +35 -88
  75. package/dist/ranger/writer.d.ts.map +1 -1
  76. package/dist/testutil/indexedPair.d.ts +5 -0
  77. package/dist/testutil/indexedPair.d.ts.map +1 -0
  78. package/dist/testutil/telem.d.ts +3 -0
  79. package/dist/testutil/telem.d.ts.map +1 -0
  80. package/dist/transport.d.ts +2 -2
  81. package/dist/transport.d.ts.map +1 -1
  82. package/dist/user/payload.d.ts +3 -29
  83. package/dist/user/payload.d.ts.map +1 -1
  84. package/dist/user/retriever.d.ts +3 -9
  85. package/dist/user/retriever.d.ts.map +1 -1
  86. package/dist/util/decodeJSONString.d.ts.map +1 -1
  87. package/dist/util/zod.d.ts +1 -1
  88. package/dist/util/zod.d.ts.map +1 -1
  89. package/dist/workspace/lineplot/payload.d.ts +10 -26
  90. package/dist/workspace/lineplot/payload.d.ts.map +1 -1
  91. package/dist/workspace/log/payload.d.ts +10 -26
  92. package/dist/workspace/log/payload.d.ts.map +1 -1
  93. package/dist/workspace/payload.d.ts +14 -40
  94. package/dist/workspace/payload.d.ts.map +1 -1
  95. package/dist/workspace/schematic/payload.d.ts +13 -45
  96. package/dist/workspace/schematic/payload.d.ts.map +1 -1
  97. package/dist/workspace/table/payload.d.ts +13 -39
  98. package/dist/workspace/table/payload.d.ts.map +1 -1
  99. package/package.json +6 -6
  100. package/src/channel/channel.spec.ts +26 -27
  101. package/src/channel/client.ts +0 -9
  102. package/src/channel/payload.ts +22 -5
  103. package/src/channel/retriever.ts +12 -6
  104. package/src/client.ts +3 -3
  105. package/src/control/client.ts +5 -2
  106. package/src/control/state.ts +8 -3
  107. package/src/errors.spec.ts +5 -4
  108. package/src/errors.ts +21 -82
  109. package/src/framer/adapter.ts +22 -3
  110. package/src/framer/client.ts +38 -21
  111. package/src/framer/codec.spec.ts +303 -0
  112. package/src/framer/codec.ts +396 -0
  113. package/src/framer/deleter.spec.ts +51 -63
  114. package/src/framer/frame.ts +16 -5
  115. package/src/framer/iterator.spec.ts +45 -28
  116. package/src/framer/iterator.ts +6 -5
  117. package/src/framer/streamProxy.ts +1 -1
  118. package/src/framer/streamer.spec.ts +10 -18
  119. package/src/framer/streamer.ts +138 -22
  120. package/src/framer/writer.spec.ts +125 -150
  121. package/src/framer/writer.ts +74 -68
  122. package/src/hardware/device/payload.ts +3 -3
  123. package/src/hardware/task/payload.ts +9 -6
  124. package/src/hardware/task/task.spec.ts +1 -1
  125. package/src/index.ts +0 -2
  126. package/src/ontology/group/group.spec.ts +2 -2
  127. package/src/ontology/payload.ts +3 -3
  128. package/src/ranger/ranger.spec.ts +9 -7
  129. package/src/testutil/indexedPair.ts +40 -0
  130. package/src/testutil/telem.ts +13 -0
  131. package/src/transport.ts +1 -2
  132. package/src/util/decodeJSONString.ts +2 -2
  133. package/src/util/retrieve.spec.ts +1 -1
@@ -7,7 +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 { DataType, Rate } from "@synnaxlabs/x/telem";
10
+ import { type Optional } from "@synnaxlabs/x";
11
+ import { type CrudeDataType, DataType } from "@synnaxlabs/x/telem";
11
12
  import { z } from "zod";
12
13
 
13
14
  import { nullableArrayZ } from "@/util/zod";
@@ -20,12 +21,12 @@ export type Name = z.infer<typeof nameZ>;
20
21
  export type Names = Name[];
21
22
  export type KeyOrName = Key | Name;
22
23
  export type KeysOrNames = Keys | Names;
23
- export type Params = Key | Name | Keys | Names;
24
+ export type PrimitiveParams = Key | Name | Keys | Names;
25
+ export type Params = Key | Name | Keys | Names | Payload | Payload[];
24
26
 
25
27
  export const channelZ = z.object({
26
28
  name: nameZ,
27
29
  key: keyZ,
28
- rate: Rate.z,
29
30
  dataType: DataType.z,
30
31
  leaseholder: z.number(),
31
32
  index: keyZ,
@@ -42,14 +43,30 @@ export const newZ = channelZ.extend({
42
43
  key: keyZ.optional(),
43
44
  leaseholder: z.number().optional(),
44
45
  index: keyZ.optional(),
45
- rate: Rate.z.optional().default(0),
46
46
  isIndex: z.boolean().optional(),
47
47
  internal: z.boolean().optional().default(false),
48
48
  virtual: z.boolean().optional().default(false),
49
49
  expression: z.string().optional().default(""),
50
50
  requires: nullableArrayZ(keyZ).optional().default([]),
51
51
  });
52
- export interface New extends z.input<typeof newZ> {}
52
+
53
+ export interface New
54
+ extends Omit<
55
+ Optional<
56
+ Payload,
57
+ | "key"
58
+ | "leaseholder"
59
+ | "index"
60
+ | "isIndex"
61
+ | "internal"
62
+ | "virtual"
63
+ | "expression"
64
+ | "requires"
65
+ >,
66
+ "dataType"
67
+ > {
68
+ dataType: CrudeDataType;
69
+ }
53
70
 
54
71
  export const ONTOLOGY_TYPE = "channel";
55
72
  export type OntologyType = typeof ONTOLOGY_TYPE;
@@ -20,6 +20,8 @@ import {
20
20
  type Keys,
21
21
  type KeysOrNames,
22
22
  keyZ,
23
+ type Name,
24
+ type Names,
23
25
  type Params,
24
26
  type Payload,
25
27
  } from "@/channel/payload";
@@ -53,8 +55,15 @@ const resZ = z.object({ channels: nullableArrayZ(channelZ) });
53
55
 
54
56
  export const analyzeParams = (
55
57
  channels: Params,
56
- ): ParamAnalysisResult<KeyOrName, { number: "keys"; string: "names" }> =>
57
- analyzeParameters(channels, { number: "keys", string: "names" });
58
+ ): ParamAnalysisResult<KeyOrName, { number: "keys"; string: "names" }> => {
59
+ if (Array.isArray(channels) && channels.length > 0 && typeof channels[0] === "object")
60
+ channels = (channels as Payload[]).map((c) => c.key);
61
+ else if (typeof channels === "object" && "key" in channels) channels = [channels.key];
62
+ return analyzeParameters(channels as Key | Name | Keys | Names, {
63
+ number: "keys",
64
+ string: "names",
65
+ });
66
+ };
58
67
 
59
68
  export interface Retriever {
60
69
  retrieve: (channels: Params, opts?: RetrieveOptions) => Promise<Payload[]>;
@@ -120,10 +129,7 @@ export class CacheRetriever implements Retriever {
120
129
  }
121
130
 
122
131
  async retrieve(channels: Params, options?: RetrieveOptions): Promise<Payload[]> {
123
- const { normalized } = analyzeParameters<string | number>(channels, {
124
- string: "names",
125
- number: "keys",
126
- });
132
+ const { normalized } = analyzeParams(channels);
127
133
  const results: Payload[] = [];
128
134
  const toFetch: KeysOrNames = [];
129
135
  normalized.forEach((keyOrName) => {
package/src/client.ts CHANGED
@@ -31,10 +31,10 @@ import { user } from "@/user";
31
31
  import { workspace } from "@/workspace";
32
32
 
33
33
  export const synnaxPropsZ = z.object({
34
- host: z.string({ required_error: "Host is required" }).min(1, "Host is required"),
34
+ host: z.string({ error: "Host is required" }).min(1, "Host is required"),
35
35
  port: z
36
- .number({ required_error: "Port is required" })
37
- .or(z.string({ required_error: "Port is required" })),
36
+ .number({ error: "Port is required" })
37
+ .or(z.string({ error: "Port is required" })),
38
38
  username: z.string().min(1, "Username is required"),
39
39
  password: z.string().min(1, "Password is required"),
40
40
  connectivityPollFrequency: TimeSpan.z.default(TimeSpan.seconds(30)),
@@ -8,7 +8,7 @@
8
8
  // included in the file licenses/APL.txt.
9
9
 
10
10
  import { StateTracker } from "@/control/state";
11
- import { type framer } from "@/framer";
11
+ import { framer } from "@/framer";
12
12
 
13
13
  const CONTROL_STATE_KEY = "sy_node_1_control";
14
14
 
@@ -20,7 +20,10 @@ export class Client {
20
20
  }
21
21
 
22
22
  async openStateTracker(): Promise<StateTracker> {
23
- const stream = await this.framer.openStreamer(CONTROL_STATE_KEY);
23
+ const stream = await framer.HardenedStreamer.open(
24
+ async () => await this.framer.openStreamer(CONTROL_STATE_KEY),
25
+ CONTROL_STATE_KEY,
26
+ );
24
27
  return new StateTracker(stream);
25
28
  }
26
29
  }
@@ -15,8 +15,9 @@ import { z } from "zod";
15
15
  import { type channel } from "@/channel";
16
16
  import { framer } from "@/framer";
17
17
 
18
- export interface Authority extends control.Authority {}
19
- export const Authority = control.Authority;
18
+ export type Authority = control.Authority;
19
+ export const ABSOLUTE_AUTHORITY = control.ABSOLUTE_AUTHORITY;
20
+ export const ZERO_AUTHORITY = control.ZERO_AUTHORITY;
20
21
  export type Transfer = control.Transfer<channel.Key>;
21
22
  export interface State extends control.State<channel.Key> {}
22
23
  export interface Subject extends control.Subject {}
@@ -33,6 +34,10 @@ export const transferString = (t: Transfer): string => {
33
34
  } (${t.to.authority.toString()})`;
34
35
  };
35
36
 
37
+ const updateZ = z.object({
38
+ transfers: z.array(control.transferZ),
39
+ });
40
+
36
41
  interface Update {
37
42
  transfers: control.Transfer<channel.Key>[];
38
43
  }
@@ -46,7 +51,7 @@ export class StateTracker
46
51
 
47
52
  constructor(streamer: framer.Streamer) {
48
53
  super(streamer, (frame) => {
49
- const update: Update = this.codec.decode(frame.series[0].buffer);
54
+ const update: Update = this.codec.decode(frame.series[0].buffer, updateZ);
50
55
  this.merge(update);
51
56
  return [update.transfers, true];
52
57
  });
@@ -7,8 +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 MatchableErrorType } from "@synnaxlabs/freighter/src/errors";
11
- import { id } from "@synnaxlabs/x";
10
+ import { type errors, id } from "@synnaxlabs/x";
12
11
  import { v4 as uuid } from "uuid";
13
12
  import { describe, expect, test } from "vitest";
14
13
 
@@ -30,7 +29,7 @@ import { newClient } from "@/setupspecs";
30
29
 
31
30
  describe("error", () => {
32
31
  describe("type matching", () => {
33
- const ERRORS: [string, Error, MatchableErrorType][] = [
32
+ const ERRORS: [string, Error, errors.Matchable][] = [
34
33
  [ValidationError.TYPE, new ValidationError(), ValidationError],
35
34
  [FieldError.TYPE, new FieldError("field", "message"), FieldError],
36
35
  [AuthError.TYPE, new AuthError(), AuthError],
@@ -45,7 +44,9 @@ describe("error", () => {
45
44
  [ContiguityError.TYPE, new ContiguityError("message"), ContiguityError],
46
45
  ];
47
46
  ERRORS.forEach(([typeName, error, type]) =>
48
- test(`matches ${typeName}`, () => expect(type.matches(error)).toBeTruthy()),
47
+ test(`matches ${typeName}`, () => {
48
+ expect(type.matches(error)).toBeTruthy();
49
+ }),
49
50
  );
50
51
  });
51
52
  });
package/src/errors.ts CHANGED
@@ -7,35 +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 {
11
- BaseTypedError,
12
- errorMatcher,
13
- type ErrorPayload,
14
- type Middleware,
15
- registerError,
16
- Unreachable,
17
- } from "@synnaxlabs/freighter";
18
-
19
- const _FREIGHTER_EXCEPTION_PREFIX = "sy.";
20
-
21
- export interface Field {
22
- field: string;
23
- message: string;
24
- }
10
+ import { type Middleware, Unreachable } from "@synnaxlabs/freighter";
11
+ import { errors } from "@synnaxlabs/x";
12
+
13
+ export class SynnaxError extends errors.createTyped("sy") {}
25
14
 
26
15
  /**
27
16
  * Raised when a validation error occurs.
28
17
  */
29
- export class ValidationError extends BaseTypedError {
30
- static readonly TYPE: string = `${_FREIGHTER_EXCEPTION_PREFIX}validation`;
31
- type = ValidationError.TYPE;
32
- static readonly matches = errorMatcher(ValidationError.TYPE);
33
- }
18
+ export class ValidationError extends SynnaxError.sub("validation") {}
34
19
 
35
- export class FieldError extends ValidationError {
36
- static readonly TYPE = `${ValidationError.TYPE}.field`;
37
- type = FieldError.TYPE;
38
- static readonly matches = errorMatcher(FieldError.TYPE);
20
+ export class FieldError extends ValidationError.sub("field") {
39
21
  readonly field: string;
40
22
  readonly message: string;
41
23
 
@@ -49,35 +31,19 @@ export class FieldError extends ValidationError {
49
31
  /**
50
32
  * AuthError is raised when an authentication error occurs.
51
33
  */
52
- export class AuthError extends BaseTypedError {
53
- static readonly TYPE: string = `${_FREIGHTER_EXCEPTION_PREFIX}auth`;
54
- type = AuthError.TYPE;
55
- static readonly matches = errorMatcher(AuthError.TYPE);
56
- }
34
+ export class AuthError extends SynnaxError.sub("auth") {}
57
35
 
58
36
  /**
59
37
  * InvalidTokenError is raised when an authentication token is invalid.
60
38
  */
61
- export class InvalidTokenError extends AuthError {
62
- static readonly TYPE = `${AuthError.TYPE}.invalid-token`;
63
- type = InvalidTokenError.TYPE;
64
- static readonly matches = errorMatcher(InvalidTokenError.TYPE);
65
- }
39
+ export class InvalidTokenError extends AuthError.sub("invalid-token") {}
66
40
 
67
- export class ExpiredTokenError extends AuthError {
68
- static readonly TYPE = `${AuthError.TYPE}.expired-token`;
69
- type = ExpiredTokenError.TYPE;
70
- static readonly matches = errorMatcher(ExpiredTokenError.TYPE);
71
- }
41
+ export class ExpiredTokenError extends AuthError.sub("expired-token") {}
72
42
 
73
43
  /**
74
44
  * UnexpectedError is raised when an unexpected error occurs.
75
45
  */
76
- export class UnexpectedError extends BaseTypedError {
77
- static readonly TYPE = `${_FREIGHTER_EXCEPTION_PREFIX}unexpected`;
78
- type = UnexpectedError.TYPE;
79
- static readonly matches = errorMatcher(UnexpectedError.TYPE);
80
-
46
+ export class UnexpectedError extends SynnaxError.sub("unexpected") {
81
47
  constructor(message: string) {
82
48
  super(`
83
49
  Unexpected error encountered:
@@ -92,31 +58,16 @@ export class UnexpectedError extends BaseTypedError {
92
58
  /**
93
59
  * QueryError is raised when a query error occurs.
94
60
  */
95
- export class QueryError extends BaseTypedError {
96
- static readonly TYPE: string = `${_FREIGHTER_EXCEPTION_PREFIX}query`;
97
- type = QueryError.TYPE;
98
- static readonly matches = errorMatcher(QueryError.TYPE);
99
- }
61
+ export class QueryError extends SynnaxError.sub("query") {}
100
62
 
101
- export class NotFoundError extends QueryError {
102
- static readonly TYPE = `${QueryError.TYPE}.not_found`;
103
- type = NotFoundError.TYPE;
104
- static readonly matches = errorMatcher(NotFoundError.TYPE);
105
- }
63
+ export class NotFoundError extends QueryError.sub("not_found") {}
106
64
 
107
- export class MultipleFoundError extends QueryError {
108
- static readonly TYPE = `${QueryError.TYPE}.multiple_results`;
109
- type = MultipleFoundError.TYPE;
110
- static readonly matches = errorMatcher(MultipleFoundError.TYPE);
111
- }
65
+ export class MultipleFoundError extends QueryError.sub("multiple_results") {}
112
66
 
113
67
  /**
114
68
  * RouteError is raised when a routing error occurs.
115
69
  */
116
- export class RouteError extends BaseTypedError {
117
- static readonly TYPE = `${_FREIGHTER_EXCEPTION_PREFIX}route`;
118
- type = RouteError.TYPE;
119
- static readonly matches = errorMatcher(RouteError.TYPE);
70
+ export class RouteError extends SynnaxError.sub("route") {
120
71
  path: string;
121
72
 
122
73
  constructor(message: string, path: string) {
@@ -125,29 +76,17 @@ export class RouteError extends BaseTypedError {
125
76
  }
126
77
  }
127
78
 
128
- export class ControlError extends BaseTypedError {
129
- static readonly TYPE: string = `${_FREIGHTER_EXCEPTION_PREFIX}control`;
130
- type = ControlError.TYPE;
131
- static readonly matches = errorMatcher(ControlError.TYPE);
132
- }
79
+ export class ControlError extends SynnaxError.sub("control") {}
133
80
 
134
- export class UnauthorizedError extends ControlError {
135
- static readonly TYPE = `${ControlError.TYPE}.unauthorized`;
136
- type = UnauthorizedError.TYPE;
137
- static readonly matches = errorMatcher(UnauthorizedError.TYPE);
138
- }
81
+ export class UnauthorizedError extends ControlError.sub("unauthorized") {}
139
82
 
140
83
  /**
141
84
  * Raised when time-series data is not contiguous.
142
85
  */
143
- export class ContiguityError extends BaseTypedError {
144
- static readonly TYPE = `${_FREIGHTER_EXCEPTION_PREFIX}contiguity`;
145
- type = ContiguityError.TYPE;
146
- static readonly matches = errorMatcher(ContiguityError.TYPE);
147
- }
86
+ export class ContiguityError extends SynnaxError.sub("contiguity") {}
148
87
 
149
- const decode = (payload: ErrorPayload): Error | null => {
150
- if (!payload.type.startsWith(_FREIGHTER_EXCEPTION_PREFIX)) return null;
88
+ const decode = (payload: errors.Payload): Error | null => {
89
+ if (!payload.type.startsWith(SynnaxError.TYPE)) return null;
151
90
  if (payload.type.startsWith(ValidationError.TYPE)) {
152
91
  if (payload.type === FieldError.TYPE) {
153
92
  const values = payload.data.split(": ");
@@ -188,11 +127,11 @@ const decode = (payload: ErrorPayload): Error | null => {
188
127
  return new UnexpectedError(payload.data);
189
128
  };
190
129
 
191
- const encode = (): ErrorPayload => {
130
+ const encode = (): errors.Payload => {
192
131
  throw new Error("Not implemented");
193
132
  };
194
133
 
195
- registerError({ encode, decode });
134
+ errors.register({ encode, decode });
196
135
 
197
136
  export const validateFieldNotNull = (
198
137
  key: string,
@@ -11,17 +11,20 @@ import { type CrudeSeries, Series } from "@synnaxlabs/x/telem";
11
11
 
12
12
  import { channel } from "@/channel";
13
13
  import { ValidationError } from "@/errors";
14
+ import { Codec } from "@/framer/codec";
14
15
  import { type Crude, Frame } from "@/framer/frame";
15
16
 
16
17
  export class ReadAdapter {
17
18
  private adapter: Map<channel.Key, channel.Name> | null;
18
19
  retriever: channel.Retriever;
19
20
  keys: channel.Key[];
21
+ codec: Codec;
20
22
 
21
23
  private constructor(retriever: channel.Retriever) {
22
24
  this.retriever = retriever;
23
25
  this.adapter = null;
24
26
  this.keys = [];
27
+ this.codec = new Codec();
25
28
  }
26
29
 
27
30
  static async open(
@@ -35,12 +38,16 @@ export class ReadAdapter {
35
38
 
36
39
  async update(channels: channel.Params): Promise<void> {
37
40
  const { variant, normalized } = channel.analyzeParams(channels);
41
+ const fetched = await this.retriever.retrieve(normalized);
42
+ this.codec.update(
43
+ fetched.map((c) => c.key),
44
+ fetched.map((c) => c.dataType),
45
+ );
38
46
  if (variant === "keys") {
39
47
  this.adapter = null;
40
48
  this.keys = normalized as channel.Key[];
41
49
  return;
42
50
  }
43
- const fetched = await this.retriever.retrieve(normalized);
44
51
  const a = new Map<channel.Key, channel.Name>();
45
52
  this.adapter = a;
46
53
  normalized.forEach((name) => {
@@ -69,11 +76,13 @@ export class WriteAdapter {
69
76
  private adapter: Map<channel.Name, channel.Key> | null;
70
77
  retriever: channel.Retriever;
71
78
  keys: channel.Key[];
79
+ codec: Codec;
72
80
 
73
81
  private constructor(retriever: channel.Retriever) {
74
82
  this.retriever = retriever;
75
83
  this.adapter = null;
76
84
  this.keys = [];
85
+ this.codec = new Codec();
77
86
  }
78
87
 
79
88
  static async open(
@@ -99,11 +108,17 @@ export class WriteAdapter {
99
108
  results.map((c) => [c.name, c.key]),
100
109
  );
101
110
  this.keys = results.map((c) => c.key);
111
+ this.codec.update(
112
+ this.keys,
113
+ results.map((c) => c.dataType),
114
+ );
102
115
  }
103
116
 
104
- private async fetchChannel(ch: channel.Key | channel.Name): Promise<channel.Payload> {
117
+ private async fetchChannel(
118
+ ch: channel.Key | channel.Name | channel.Payload,
119
+ ): Promise<channel.Payload> {
105
120
  const res = await this.retriever.retrieve(ch);
106
- if (res.length === 0) throw new Error(`Channel ${ch} not found`);
121
+ if (res.length === 0) throw new Error(`Channel ${JSON.stringify(ch)} not found`);
107
122
  return res[0];
108
123
  }
109
124
 
@@ -113,6 +128,10 @@ export class WriteAdapter {
113
128
  return res.key;
114
129
  }
115
130
 
131
+ encode(frame: Frame): Uint8Array {
132
+ return this.codec.encode(frame.toPayload());
133
+ }
134
+
116
135
  async adapt(
117
136
  columnsOrData: channel.Params | Record<channel.KeyOrName, CrudeSeries> | Crude,
118
137
  series?: CrudeSeries | CrudeSeries[],
@@ -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, type UnaryClient } from "@synnaxlabs/freighter";
10
+ import { type UnaryClient, type WebSocketClient } from "@synnaxlabs/freighter";
11
11
  import {
12
12
  type CrudeSeries,
13
13
  type CrudeTimeRange,
@@ -21,19 +21,35 @@ import { channel } from "@/channel";
21
21
  import { Deleter } from "@/framer/deleter";
22
22
  import { Frame, ONTOLOGY_TYPE } from "@/framer/frame";
23
23
  import { Iterator, type IteratorConfig } from "@/framer/iterator";
24
- import { Streamer, type StreamerConfig } from "@/framer/streamer";
24
+ import { openStreamer, type Streamer, type StreamerConfig } from "@/framer/streamer";
25
25
  import { Writer, type WriterConfig, WriterMode } from "@/framer/writer";
26
26
  import { ontology } from "@/ontology";
27
27
 
28
28
  export const ontologyID = (key: channel.Key): ontology.ID =>
29
29
  new ontology.ID({ type: ONTOLOGY_TYPE, key: key.toString() });
30
30
 
31
+ const normalizeConfig = <T extends { channels: channel.Params }>(
32
+ config: T | channel.Params,
33
+ ): T => {
34
+ if (
35
+ Array.isArray(config) ||
36
+ typeof config !== "object" ||
37
+ (typeof config === "object" && "key" in config)
38
+ )
39
+ return { channels: config } as T;
40
+ return config;
41
+ };
42
+
31
43
  export class Client {
32
- private readonly streamClient: StreamClient;
44
+ private readonly streamClient: WebSocketClient;
33
45
  private readonly retriever: channel.Retriever;
34
46
  private readonly deleter: Deleter;
35
47
 
36
- constructor(stream: StreamClient, unary: UnaryClient, retriever: channel.Retriever) {
48
+ constructor(
49
+ stream: WebSocketClient,
50
+ unary: UnaryClient,
51
+ retriever: channel.Retriever,
52
+ ) {
37
53
  this.streamClient = stream;
38
54
  this.retriever = retriever;
39
55
  this.deleter = new Deleter(unary);
@@ -63,9 +79,11 @@ export class Client {
63
79
  * @returns a new {@link Writer}.
64
80
  */
65
81
  async openWriter(config: WriterConfig | channel.Params): Promise<Writer> {
66
- if (Array.isArray(config) || typeof config !== "object")
67
- config = { channels: config as channel.Params };
68
- return await Writer._open(this.retriever, this.streamClient, config);
82
+ return await Writer._open(
83
+ this.retriever,
84
+ this.streamClient,
85
+ normalizeConfig<WriterConfig>(config),
86
+ );
69
87
  }
70
88
 
71
89
  /***
@@ -93,9 +111,11 @@ export class Client {
93
111
  async openStreamer(config: StreamerConfig | channel.Params): Promise<Streamer>;
94
112
 
95
113
  async openStreamer(config: StreamerConfig | channel.Params): Promise<Streamer> {
96
- if (Array.isArray(config) || typeof config !== "object")
97
- config = { channels: config as channel.Params, downsampleFactor: 1 };
98
- return await Streamer._open(this.retriever, this.streamClient, config);
114
+ return await openStreamer(
115
+ this.retriever,
116
+ this.streamClient,
117
+ normalizeConfig<StreamerConfig>(config),
118
+ );
99
119
  }
100
120
 
101
121
  async write(
@@ -135,13 +155,13 @@ export class Client {
135
155
  start,
136
156
  channels: Object.keys(data_),
137
157
  mode: WriterMode.Persist,
158
+ errOnUnauthorized: true,
159
+ enableAutoCommit: true,
160
+ autoIndexPersistInterval: TimeSpan.MAX,
138
161
  });
139
- try {
140
- await w.write(data_);
141
- await w.commit();
142
- } finally {
143
- await w.close();
144
- }
162
+ await w.write(data_);
163
+ await w.commit();
164
+ await w.close();
145
165
  return;
146
166
  }
147
167
  const w = await this.openWriter({
@@ -152,11 +172,8 @@ export class Client {
152
172
  enableAutoCommit: true,
153
173
  autoIndexPersistInterval: TimeSpan.MAX,
154
174
  });
155
- try {
156
- await w.write(channels as channel.Params, data);
157
- } finally {
158
- await w.close();
159
- }
175
+ await w.write(channels as channel.Params, data);
176
+ await w.close();
160
177
  }
161
178
 
162
179
  async read(tr: CrudeTimeRange, channel: channel.KeyOrName): Promise<MultiSeries>;