@synnaxlabs/client 0.15.2 → 0.16.3
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.
- package/.pytest_cache/README.md +8 -0
- package/.turbo/turbo-build.log +10 -10
- package/README.md +11 -2
- package/dist/channel/batchRetriever.spec.d.ts +1 -0
- package/dist/channel/client.d.ts +149 -14
- package/dist/channel/retriever.d.ts +15 -0
- package/dist/client.cjs +21 -0
- package/dist/client.cjs.map +1 -0
- package/dist/client.d.ts +1 -1
- package/dist/{client.es.js → client.js} +5076 -4731
- package/dist/client.js.map +1 -0
- package/dist/errors.d.ts +4 -0
- package/dist/ontology/payload.d.ts +38 -38
- package/examples/index.js +1 -0
- package/examples/package.json +16 -0
- package/package.json +6 -5
- package/src/channel/batchRetriever.spec.ts +95 -0
- package/src/channel/channel.spec.ts +19 -2
- package/src/channel/client.ts +177 -33
- package/src/channel/payload.ts +2 -2
- package/src/channel/retriever.ts +62 -2
- package/src/client.ts +10 -6
- package/src/errors.ts +4 -0
- package/src/framer/streamer.spec.ts +1 -0
- package/src/hardware/task/retriever.ts +1 -1
- package/dist/client.cjs.js +0 -21
- package/dist/client.cjs.js.map +0 -1
- package/dist/client.es.js.map +0 -1
package/src/channel/client.ts
CHANGED
|
@@ -7,6 +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 UnaryClient } from "@synnaxlabs/freighter";
|
|
10
11
|
import {
|
|
11
12
|
DataType,
|
|
12
13
|
Rate,
|
|
@@ -16,7 +17,7 @@ import {
|
|
|
16
17
|
type TimeRange,
|
|
17
18
|
type AsyncTermSearcher,
|
|
18
19
|
toArray,
|
|
19
|
-
type
|
|
20
|
+
type CrudeTimeStamp,
|
|
20
21
|
} from "@synnaxlabs/x";
|
|
21
22
|
|
|
22
23
|
import { type Creator } from "@/channel/creator";
|
|
@@ -28,24 +29,65 @@ import {
|
|
|
28
29
|
payload,
|
|
29
30
|
type NewPayload,
|
|
30
31
|
} from "@/channel/payload";
|
|
31
|
-
import {
|
|
32
|
-
|
|
32
|
+
import {
|
|
33
|
+
analyzeParams,
|
|
34
|
+
CacheRetriever,
|
|
35
|
+
ClusterRetriever,
|
|
36
|
+
DebouncedBatchRetriever,
|
|
37
|
+
type Retriever,
|
|
38
|
+
} from "@/channel/retriever";
|
|
39
|
+
import { MultipleResultsError, NoResultsError, ValidationError } from "@/errors";
|
|
33
40
|
import { type framer } from "@/framer";
|
|
34
41
|
|
|
35
42
|
/**
|
|
36
|
-
* Represents a Channel in a Synnax database.
|
|
37
|
-
* directly, but
|
|
43
|
+
* Represents a Channel in a Synnax database. Typically, channels should not be
|
|
44
|
+
* instantiated directly, but instead created via the `.channels.create` or retrieved
|
|
45
|
+
* via the `.channels.retrieve` method on a Synnax client.
|
|
46
|
+
*
|
|
47
|
+
* Please refer to the [Synnax documentation](https://docs.synnaxlabs.com) for detailed
|
|
48
|
+
* information on what channels are and how to use them.
|
|
38
49
|
*/
|
|
39
50
|
export class Channel {
|
|
40
51
|
private readonly _frameClient: framer.Client | null;
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
52
|
+
/**
|
|
53
|
+
* A unique key identifying the channel in the Synnax database. This key is
|
|
54
|
+
* automatically assigned by Synnax.
|
|
55
|
+
*/
|
|
56
|
+
readonly key: Key;
|
|
57
|
+
/**
|
|
58
|
+
* A human-readable name for the channel. This name is not guaranteed to be
|
|
59
|
+
* unique.
|
|
60
|
+
*/
|
|
61
|
+
readonly name: string;
|
|
62
|
+
/**
|
|
63
|
+
* The rate at which the channel samples telemetry. This only applies to fixed rate
|
|
64
|
+
* channels, and will be 0 if the channel is indexed.
|
|
65
|
+
*/
|
|
66
|
+
readonly rate: Rate;
|
|
67
|
+
/**
|
|
68
|
+
* The data type of the channel.
|
|
69
|
+
*/
|
|
70
|
+
readonly dataType: DataType;
|
|
71
|
+
/**
|
|
72
|
+
* The key of the node in the Synnax cluster that holds the 'lease' over the channel
|
|
73
|
+
* i.e. it's the only node in the cluster allowed to accept writes to the channel. This
|
|
74
|
+
* property is mostly for internal use.
|
|
75
|
+
*/
|
|
76
|
+
readonly leaseholder: number;
|
|
77
|
+
/**
|
|
78
|
+
* The key of the index channel that this channel is associated with i.e. the channel
|
|
79
|
+
* that stores its timestamps.
|
|
80
|
+
*/
|
|
81
|
+
readonly index: Key;
|
|
82
|
+
/**
|
|
83
|
+
* This is set to true if the channel is an index channel, and false otherwise.
|
|
84
|
+
*/
|
|
85
|
+
readonly isIndex: boolean;
|
|
86
|
+
/**
|
|
87
|
+
* An alias for the channel under a specific range. This parameter is unstable and
|
|
88
|
+
* should not be relied upon in the current version of Synnax.
|
|
89
|
+
*/
|
|
90
|
+
readonly alias: string | undefined;
|
|
49
91
|
|
|
50
92
|
constructor({
|
|
51
93
|
dataType,
|
|
@@ -74,10 +116,15 @@ export class Channel {
|
|
|
74
116
|
|
|
75
117
|
private get framer(): framer.Client {
|
|
76
118
|
if (this._frameClient == null)
|
|
77
|
-
throw new
|
|
119
|
+
throw new ValidationError("cannot read from a channel that has not been created");
|
|
78
120
|
return this._frameClient;
|
|
79
121
|
}
|
|
80
122
|
|
|
123
|
+
/**
|
|
124
|
+
* Returns the payload representation of this channel i.e. a pure JS object with
|
|
125
|
+
* all of the channel fields but without any methods. This is used internally for
|
|
126
|
+
* network transportation, but also provided to you as a convenience.
|
|
127
|
+
*/
|
|
81
128
|
get payload(): Payload {
|
|
82
129
|
return payload.parse({
|
|
83
130
|
key: this.key,
|
|
@@ -107,54 +154,144 @@ export class Channel {
|
|
|
107
154
|
* @param start - The starting timestamp of the first sample in data.
|
|
108
155
|
* @param data - THe telemetry to write to the channel.
|
|
109
156
|
*/
|
|
110
|
-
async write(start:
|
|
157
|
+
async write(start: CrudeTimeStamp, data: NativeTypedArray): Promise<void> {
|
|
111
158
|
return await this.framer.write(this.key, start, data);
|
|
112
159
|
}
|
|
113
160
|
}
|
|
114
161
|
|
|
115
162
|
/**
|
|
116
163
|
* The core client class for executing channel operations against a Synnax
|
|
117
|
-
* cluster.
|
|
164
|
+
* cluster. This class should not be instantiated directly, and instead should be used
|
|
165
|
+
* through the `channels` property of an {@link Synnax} client.
|
|
118
166
|
*/
|
|
119
167
|
export class Client implements AsyncTermSearcher<string, Key, Channel> {
|
|
120
168
|
private readonly frameClient: framer.Client;
|
|
121
169
|
private readonly retriever: Retriever;
|
|
122
170
|
private readonly creator: Creator;
|
|
171
|
+
private readonly client: UnaryClient;
|
|
123
172
|
|
|
124
|
-
constructor(
|
|
125
|
-
|
|
173
|
+
constructor(
|
|
174
|
+
frameClient: framer.Client,
|
|
175
|
+
retriever: Retriever,
|
|
176
|
+
client: UnaryClient,
|
|
177
|
+
creator: Creator,
|
|
178
|
+
) {
|
|
179
|
+
this.frameClient = frameClient;
|
|
126
180
|
this.retriever = retriever;
|
|
181
|
+
this.client = client;
|
|
127
182
|
this.creator = creator;
|
|
128
183
|
}
|
|
129
184
|
|
|
185
|
+
/**
|
|
186
|
+
* Creates a single channel with the given properties.
|
|
187
|
+
*
|
|
188
|
+
* @param name - A human-readable name for the channel.
|
|
189
|
+
* @param rate - The rate of the channel. This only applies to fixed rate channels.
|
|
190
|
+
* @param dataType - The data type for the samples stored in the channel.
|
|
191
|
+
* @param index - The key of the index channel that this channel should be associated
|
|
192
|
+
* with. An 'index' channel is a channel that stores timestamps for other channels. Refer
|
|
193
|
+
* to the Synnax documentation (https://docs.synnaxlabs.com) for more information. The
|
|
194
|
+
* index channel must have already been created. This field does not need to be specified
|
|
195
|
+
* if the channel is an index channel, or the channel is a fixed rate channel. If this
|
|
196
|
+
* value is specified, the 'rate' parameter will be ignored.
|
|
197
|
+
* @param isIndex - Set to true if the channel is an index channel, and false otherwise.
|
|
198
|
+
* Index channels must have a data type of `DataType.TIMESTAMP`.
|
|
199
|
+
* @returns the created channel. {@see Channel}
|
|
200
|
+
* @throws {ValidationError} if any of the parameters for creating the channel are
|
|
201
|
+
* invalid.
|
|
202
|
+
*
|
|
203
|
+
* @example
|
|
204
|
+
* ```typescript
|
|
205
|
+
* const indexChannel = await client.channels.create({
|
|
206
|
+
* name: "time",
|
|
207
|
+
* dataType: DataType.TIMESTAMP,
|
|
208
|
+
* isIndex: true,
|
|
209
|
+
* })
|
|
210
|
+
*
|
|
211
|
+
*
|
|
212
|
+
* const dataChannel = await client.channels.create({
|
|
213
|
+
* name: "temperature",
|
|
214
|
+
* dataType: DataType.FLOAT,
|
|
215
|
+
* index: indexChannel.key,
|
|
216
|
+
* });
|
|
217
|
+
* ```
|
|
218
|
+
*/
|
|
130
219
|
async create(channel: NewPayload): Promise<Channel>;
|
|
131
220
|
|
|
132
|
-
async create(channels: NewPayload[]): Promise<Channel[]>;
|
|
133
|
-
|
|
134
221
|
/**
|
|
135
|
-
* Creates
|
|
222
|
+
* Creates multiple channels with the given properties. The order of the channels
|
|
223
|
+
* returned is guaranteed to match the order of the channels passed in.
|
|
224
|
+
*
|
|
225
|
+
* @param channels - An array of channel properties to create.
|
|
226
|
+
* For each channel, the following properties should be considered:
|
|
227
|
+
*
|
|
228
|
+
* @param name - A human-readable name for the channel.
|
|
229
|
+
* @param rate - The rate of the channel. This only applies to fixed rate channels. If
|
|
230
|
+
* the 'index' parameter is specified or 'isIndex' is set to true, this parameter will
|
|
231
|
+
* be ignored.
|
|
232
|
+
* @param dataType - The data type for the samples stored in the channel.
|
|
233
|
+
* @param index - The key of the index channel that this channel should be associated
|
|
234
|
+
* with. An 'index' channel is a channel that stores timestamps for other channels. Refer
|
|
235
|
+
* to the Synnax documentation (https://docs.synnaxlabs.com) for more information. The
|
|
236
|
+
* index channel must have already been created. This field does not need to be specified
|
|
237
|
+
* if the channel is an index channel, or the channel is a fixed rate channel. If this
|
|
238
|
+
* value is specified, the 'rate' parameter will be ignored.
|
|
239
|
+
* @param isIndex - Set to true if the channel is an index channel, and false otherwise.
|
|
240
|
+
* Index channels must have a data type of `DataType.TIMESTAMP`.
|
|
136
241
|
*
|
|
137
|
-
* @param
|
|
138
|
-
* @param props.dataType - The data type of the channel.
|
|
139
|
-
* @param props.name - The name of the channel. Optional.
|
|
140
|
-
* @param props.nodeKey - The ID of the node that holds the lease on the
|
|
141
|
-
* channel. If you don't know what this is, don't worry about it.
|
|
142
|
-
* @returns The created channel.
|
|
242
|
+
* @param channels
|
|
143
243
|
*/
|
|
244
|
+
async create(channels: NewPayload[]): Promise<Channel[]>;
|
|
245
|
+
|
|
144
246
|
async create(channels: NewPayload | NewPayload[]): Promise<Channel | Channel[]> {
|
|
247
|
+
console.log("ABC");
|
|
145
248
|
const single = !Array.isArray(channels);
|
|
146
|
-
const
|
|
249
|
+
const payloads = await this.creator.create(toArray(channels));
|
|
250
|
+
const res = this.sugar(payloads);
|
|
147
251
|
return single ? res[0] : res;
|
|
148
252
|
}
|
|
149
253
|
|
|
254
|
+
/**
|
|
255
|
+
* Retrieves a channel from the database using the given key or name.
|
|
256
|
+
*
|
|
257
|
+
* @param channel - The key or name of the channel to retrieve.
|
|
258
|
+
* @param options - Optional parameters to control the retrieval process.
|
|
259
|
+
* @param options.dataTypes - Limits the query to only channels with the specified data
|
|
260
|
+
* type.
|
|
261
|
+
* @param options.notDataTypes - Limits the query to only channels without the specified
|
|
262
|
+
* data type.
|
|
263
|
+
*
|
|
264
|
+
* @returns The retrieved channel.
|
|
265
|
+
* @throws {NotFoundError} if the channel does not exist in the cluster.
|
|
266
|
+
* @throws {MultipleResultsError} is only thrown if the channel is retrieved by name,
|
|
267
|
+
* and multiple channels with the same name exist in the cluster.
|
|
268
|
+
*
|
|
269
|
+
* @example
|
|
270
|
+
*
|
|
271
|
+
* ```typescript
|
|
272
|
+
* const channel = await client.channels.retrieve("temperature");
|
|
273
|
+
* const channel = await client.channels.retrieve(1);
|
|
274
|
+
* ```
|
|
275
|
+
*/
|
|
150
276
|
async retrieve(channel: KeyOrName, rangeKey?: string): Promise<Channel>;
|
|
151
277
|
|
|
278
|
+
/**
|
|
279
|
+
* Retrieves multiple channels from the database using the provided keys or the
|
|
280
|
+
* provided names.
|
|
281
|
+
*
|
|
282
|
+
* @param channels - The keys or the names of the channels to retrieve. Note that
|
|
283
|
+
* this method does not support mixing keys and names in the same call.
|
|
284
|
+
* @param options - Optional parameters to control the retrieval process.
|
|
285
|
+
* @param options.dataTypes - Limits the query to only channels with the specified data
|
|
286
|
+
* type.
|
|
287
|
+
* @param options.notDataTypes - Limits the query to only channels without the specified
|
|
288
|
+
*
|
|
289
|
+
*/
|
|
152
290
|
async retrieve(channels: Params, rangeKey?: string): Promise<Channel[]>;
|
|
153
291
|
|
|
154
292
|
/**
|
|
155
293
|
* Retrieves a channel from the database using the given parameters.
|
|
156
|
-
*
|
|
157
|
-
* @param props.name - The name of the channel to retrieve. If props.key is set,
|
|
294
|
+
*
|
|
158
295
|
* this will be ignored.
|
|
159
296
|
* @returns The retrieved channel.
|
|
160
297
|
* @raises {QueryError} If the channel does not exist or if multiple results are returned.
|
|
@@ -164,9 +301,10 @@ export class Client implements AsyncTermSearcher<string, Key, Channel> {
|
|
|
164
301
|
if (normalized.length === 0) return [];
|
|
165
302
|
const res = this.sugar(await this.retriever.retrieve(channels, rangeKey));
|
|
166
303
|
if (!single) return res;
|
|
167
|
-
if (res.length === 0)
|
|
304
|
+
if (res.length === 0)
|
|
305
|
+
throw new NoResultsError(`channel matching ${actual} not found`);
|
|
168
306
|
if (res.length > 1)
|
|
169
|
-
throw new
|
|
307
|
+
throw new MultipleResultsError(`multiple channels matching ${actual} found`);
|
|
170
308
|
return res[0];
|
|
171
309
|
}
|
|
172
310
|
|
|
@@ -182,6 +320,12 @@ export class Client implements AsyncTermSearcher<string, Key, Channel> {
|
|
|
182
320
|
return this.sugar(await this.retriever.page(offset, limit, rangeKey));
|
|
183
321
|
}
|
|
184
322
|
|
|
323
|
+
createDebouncedBatchRetriever(deb: number = 10): Retriever {
|
|
324
|
+
return new CacheRetriever(
|
|
325
|
+
new DebouncedBatchRetriever(new ClusterRetriever(this.client), deb),
|
|
326
|
+
);
|
|
327
|
+
}
|
|
328
|
+
|
|
185
329
|
private sugar(payloads: Payload[]): Channel[] {
|
|
186
330
|
const { frameClient } = this;
|
|
187
331
|
return payloads.map((p) => new Channel({ ...p, frameClient }));
|
|
@@ -208,4 +352,4 @@ class SearcherUnderRange implements AsyncTermSearcher<string, Key, Channel> {
|
|
|
208
352
|
async retrieve(channels: Key[]): Promise<Channel[]> {
|
|
209
353
|
return await this.client.retrieve(channels, this.rangeKey);
|
|
210
354
|
}
|
|
211
|
-
}
|
|
355
|
+
}
|
package/src/channel/payload.ts
CHANGED
|
@@ -45,8 +45,8 @@ export type NewPayload = z.input<typeof newPayload>;
|
|
|
45
45
|
export const parseChannels = (channels: NewPayload[]): NewPayload[] =>
|
|
46
46
|
channels.map((channel) => ({
|
|
47
47
|
name: channel.name,
|
|
48
|
-
dataType:
|
|
49
|
-
rate:
|
|
48
|
+
dataType: channel.dataType,
|
|
49
|
+
rate: channel.rate ?? 0,
|
|
50
50
|
leaseholder: channel.leaseholder,
|
|
51
51
|
index: channel.index,
|
|
52
52
|
isIndex: channel.isIndex,
|
package/src/channel/retriever.ts
CHANGED
|
@@ -6,9 +6,8 @@
|
|
|
6
6
|
// As of the Change Date specified in that file, in accordance with the Business Source
|
|
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
|
-
|
|
10
9
|
import type { UnaryClient } from "@synnaxlabs/freighter";
|
|
11
|
-
import { toArray } from "@synnaxlabs/x";
|
|
10
|
+
import { debounce, toArray } from "@synnaxlabs/x";
|
|
12
11
|
import { z } from "zod";
|
|
13
12
|
|
|
14
13
|
import {
|
|
@@ -22,6 +21,7 @@ import {
|
|
|
22
21
|
type Payload,
|
|
23
22
|
payload,
|
|
24
23
|
} from "@/channel/payload";
|
|
24
|
+
import { Mutex } from "async-mutex";
|
|
25
25
|
|
|
26
26
|
const reqZ = z.object({
|
|
27
27
|
leaseholder: z.number().optional(),
|
|
@@ -156,3 +156,63 @@ export const analyzeParams = (channels: Params): ParamAnalysisResult => {
|
|
|
156
156
|
actual: channels,
|
|
157
157
|
} as const as ParamAnalysisResult;
|
|
158
158
|
};
|
|
159
|
+
|
|
160
|
+
export interface PromiseFns<T> {
|
|
161
|
+
resolve: (value: T) => void;
|
|
162
|
+
reject: (reason?: any) => void;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// no interval
|
|
166
|
+
export class DebouncedBatchRetriever implements Retriever {
|
|
167
|
+
private readonly mu = new Mutex();
|
|
168
|
+
private readonly requests = new Map<Keys, PromiseFns<Payload[]>>();
|
|
169
|
+
private readonly wrapped: Retriever;
|
|
170
|
+
private readonly debouncedRun: () => void;
|
|
171
|
+
|
|
172
|
+
constructor(wrapped: Retriever, deb: number) {
|
|
173
|
+
this.wrapped = wrapped;
|
|
174
|
+
this.debouncedRun = debounce(() => {
|
|
175
|
+
void this.run();
|
|
176
|
+
}, deb);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
async search(term: string, rangeKey?: string): Promise<Payload[]> {
|
|
180
|
+
return await this.wrapped.search(term, rangeKey);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
async page(offset: number, limit: number, rangeKey?: string): Promise<Payload[]> {
|
|
184
|
+
return await this.wrapped.page(offset, limit, rangeKey);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
async retrieve(channels: Params): Promise<Payload[]> {
|
|
188
|
+
const { normalized, variant } = analyzeParams(channels);
|
|
189
|
+
// Bypass on name fetches for now.
|
|
190
|
+
if (variant === "names")
|
|
191
|
+
return await this.wrapped.retrieve(normalized);
|
|
192
|
+
// eslint-disable-next-line @typescript-eslint/promise-function-async
|
|
193
|
+
const a = new Promise<Payload[]>((resolve, reject) => {
|
|
194
|
+
void this.mu.runExclusive(() => {
|
|
195
|
+
this.requests.set(normalized, { resolve, reject });
|
|
196
|
+
this.debouncedRun();
|
|
197
|
+
});
|
|
198
|
+
});
|
|
199
|
+
return await a;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
async run(): Promise<void> {
|
|
203
|
+
await this.mu.runExclusive(async () => {
|
|
204
|
+
const allKeys = new Set<Key>();
|
|
205
|
+
this.requests.forEach((_, keys) => keys.forEach((k) => allKeys.add(k)));
|
|
206
|
+
try {
|
|
207
|
+
const channels = await this.wrapped.retrieve(Array.from(allKeys));
|
|
208
|
+
this.requests.forEach((fns, keys) =>
|
|
209
|
+
fns.resolve(channels.filter((c) => keys.includes(c.key))),
|
|
210
|
+
);
|
|
211
|
+
} catch (e) {
|
|
212
|
+
this.requests.forEach((fns) => fns.reject(e));
|
|
213
|
+
} finally {
|
|
214
|
+
this.requests.clear();
|
|
215
|
+
}
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
}
|
package/src/client.ts
CHANGED
|
@@ -15,16 +15,15 @@ import { channel } from "@/channel";
|
|
|
15
15
|
import { connection } from "@/connection";
|
|
16
16
|
import { errorsMiddleware } from "@/errors";
|
|
17
17
|
import { framer } from "@/framer";
|
|
18
|
+
import { hardware } from "@/hardware";
|
|
19
|
+
import { device } from "@/hardware/device";
|
|
20
|
+
import { rack } from "@/hardware/rack";
|
|
21
|
+
import { task } from "@/hardware/task";
|
|
18
22
|
import { label } from "@/label";
|
|
19
23
|
import { ontology } from "@/ontology";
|
|
20
24
|
import { ranger } from "@/ranger";
|
|
21
25
|
import { Transport } from "@/transport";
|
|
22
26
|
import { workspace } from "@/workspace";
|
|
23
|
-
import { hardware } from "./hardware";
|
|
24
|
-
import { device } from "./hardware/device";
|
|
25
|
-
import { rack } from "./hardware/rack";
|
|
26
|
-
import { task } from "./hardware/task";
|
|
27
|
-
import { randomUUID } from "crypto";
|
|
28
27
|
|
|
29
28
|
export const synnaxPropsZ = z.object({
|
|
30
29
|
host: z.string().min(1),
|
|
@@ -97,7 +96,12 @@ export default class Synnax {
|
|
|
97
96
|
);
|
|
98
97
|
const chCreator = new channel.Creator(this.transport.unary);
|
|
99
98
|
this.telem = new framer.Client(this.transport.stream, chRetriever);
|
|
100
|
-
this.channels = new channel.Client(
|
|
99
|
+
this.channels = new channel.Client(
|
|
100
|
+
this.telem,
|
|
101
|
+
chRetriever,
|
|
102
|
+
this.transport.unary,
|
|
103
|
+
chCreator,
|
|
104
|
+
);
|
|
101
105
|
this.connectivity = new connection.Checker(
|
|
102
106
|
this.transport.unary,
|
|
103
107
|
connectivityPollFrequency,
|
package/src/errors.ts
CHANGED
|
@@ -95,6 +95,10 @@ export class UnexpectedError extends BaseError {
|
|
|
95
95
|
*/
|
|
96
96
|
export class QueryError extends BaseError {}
|
|
97
97
|
|
|
98
|
+
export class NoResultsError extends QueryError {}
|
|
99
|
+
|
|
100
|
+
export class MultipleResultsError extends QueryError {}
|
|
101
|
+
|
|
98
102
|
/**
|
|
99
103
|
* RouteError is raised when a routing error occurs.
|
|
100
104
|
*/
|
|
@@ -27,6 +27,7 @@ describe("Streamer", () => {
|
|
|
27
27
|
test("happy path", async () => {
|
|
28
28
|
const ch = await newChannel();
|
|
29
29
|
const streamer = await client.telem.newStreamer(ch.key);
|
|
30
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
30
31
|
const writer = await client.telem.newWriter({
|
|
31
32
|
start: TimeStamp.now(),
|
|
32
33
|
channels: ch.key,
|