@synnaxlabs/client 0.2.1 → 0.13.6
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/.eslintrc.cjs +18 -0
- package/.pytest_cache/README.md +8 -0
- package/.turbo/turbo-build.log +16 -0
- package/LICENSE +4 -21
- package/{build/module/lib → dist/auth}/auth.d.ts +16 -19
- package/dist/auth/index.d.ts +1 -0
- package/dist/cdc/external.d.ts +1 -0
- package/dist/cdc/index.d.ts +1 -0
- package/dist/cdc/observable.d.ts +17 -0
- package/dist/channel/client.d.ts +58 -0
- package/dist/channel/creator.d.ts +8 -0
- package/dist/channel/external.d.ts +4 -0
- package/dist/channel/index.d.ts +1 -0
- package/dist/channel/payload.d.ts +63 -0
- package/dist/channel/retriever.d.ts +49 -0
- package/dist/client.cjs.js +23050 -0
- package/dist/client.cjs.js.map +1 -0
- package/dist/client.d.ts +73 -0
- package/dist/client.es.js +23050 -0
- package/dist/client.es.js.map +1 -0
- package/dist/connection/checker.d.ts +66 -0
- package/dist/connection/index.d.ts +1 -0
- package/dist/control/authority.d.ts +6 -0
- package/dist/control/external.d.ts +2 -0
- package/dist/control/index.d.ts +1 -0
- package/dist/control/state.d.ts +81 -0
- package/{build/main/lib → dist}/errors.d.ts +6 -3
- package/dist/framer/adapter.d.ts +21 -0
- package/dist/framer/client.d.ts +44 -0
- package/dist/framer/external.d.ts +5 -0
- package/dist/framer/frame.d.ts +251 -0
- package/dist/framer/index.d.ts +1 -0
- package/{build/module/lib/segment → dist/framer}/iterator.d.ts +32 -64
- package/dist/framer/streamProxy.d.ts +12 -0
- package/dist/framer/streamer.d.ts +17 -0
- package/dist/framer/writer.d.ts +257 -0
- package/dist/index.d.ts +16 -0
- package/dist/label/client.d.ts +25 -0
- package/dist/label/external.d.ts +4 -0
- package/dist/label/index.d.ts +1 -0
- package/dist/label/payload.d.ts +20 -0
- package/dist/label/retriever.d.ts +13 -0
- package/dist/label/writer.d.ts +26 -0
- package/dist/ontology/cdc.d.ts +25 -0
- package/dist/ontology/client.d.ts +25 -0
- package/dist/ontology/external.d.ts +3 -0
- package/dist/ontology/group/client.d.ts +11 -0
- package/dist/ontology/group/external.d.ts +2 -0
- package/dist/ontology/group/group.d.ts +7 -0
- package/dist/ontology/group/index.d.ts +1 -0
- package/dist/ontology/group/payload.d.ts +40 -0
- package/dist/ontology/group/writer.d.ts +13 -0
- package/dist/ontology/index.d.ts +1 -0
- package/dist/ontology/ontology.spec.d.ts +1 -0
- package/dist/ontology/payload.d.ts +235 -0
- package/dist/ontology/retriever.d.ts +12 -0
- package/dist/ontology/signals.d.ts +25 -0
- package/dist/ontology/writer.d.ts +9 -0
- package/dist/ranger/active.d.ts +9 -0
- package/dist/ranger/alias.d.ts +32 -0
- package/dist/ranger/client.d.ts +31 -0
- package/dist/ranger/external.d.ts +6 -0
- package/dist/ranger/index.d.ts +1 -0
- package/dist/ranger/kv.d.ts +50 -0
- package/dist/ranger/payload.d.ts +94 -0
- package/dist/ranger/range.d.ts +29 -0
- package/dist/ranger/ranger.spec.d.ts +1 -0
- package/dist/ranger/retriever.d.ts +10 -0
- package/dist/ranger/writer.d.ts +9 -0
- package/{build/main → dist}/setupspecs.d.ts +2 -2
- package/dist/signals/external.d.ts +1 -0
- package/dist/signals/index.d.ts +1 -0
- package/dist/signals/observable.d.ts +17 -0
- package/dist/transport.d.ts +10 -0
- package/dist/user/index.d.ts +1 -0
- package/{build/main/lib → dist}/user/payload.d.ts +3 -3
- package/dist/util/telem.d.ts +2 -0
- package/dist/workspace/client.d.ts +22 -0
- package/dist/workspace/external.d.ts +2 -0
- package/dist/workspace/index.d.ts +1 -0
- package/dist/workspace/lineplot/client.d.ts +15 -0
- package/dist/workspace/lineplot/external.d.ts +2 -0
- package/dist/workspace/lineplot/index.d.ts +1 -0
- package/dist/workspace/lineplot/linePlot.spec.d.ts +1 -0
- package/dist/workspace/lineplot/payload.d.ts +31 -0
- package/dist/workspace/lineplot/retriever.d.ts +9 -0
- package/dist/workspace/lineplot/writer.d.ts +39 -0
- package/dist/workspace/payload.d.ts +31 -0
- package/dist/workspace/pid/client.d.ts +16 -0
- package/dist/workspace/pid/external.d.ts +2 -0
- package/dist/workspace/pid/index.d.ts +1 -0
- package/dist/workspace/pid/payload.d.ts +37 -0
- package/dist/workspace/pid/pid.spec.d.ts +1 -0
- package/dist/workspace/pid/retriever.d.ts +9 -0
- package/dist/workspace/pid/writer.d.ts +46 -0
- package/dist/workspace/retriever.d.ts +12 -0
- package/dist/workspace/workspace.spec.d.ts +1 -0
- package/dist/workspace/writer.d.ts +55 -0
- package/package.json +27 -98
- package/src/auth/auth.spec.ts +46 -0
- package/src/auth/auth.ts +83 -0
- package/src/auth/index.ts +10 -0
- package/src/channel/channel.spec.ts +82 -0
- package/src/channel/client.ts +209 -0
- package/src/channel/creator.ts +43 -0
- package/src/channel/external.ts +13 -0
- package/src/channel/index.ts +10 -0
- package/src/channel/payload.ts +52 -0
- package/src/channel/retriever.ts +160 -0
- package/src/client.ts +116 -0
- package/src/connection/checker.ts +104 -0
- package/src/connection/connection.spec.ts +35 -0
- package/src/connection/index.ts +10 -0
- package/src/control/authority.ts +26 -0
- package/src/control/external.ts +11 -0
- package/src/control/index.ts +10 -0
- package/src/control/state.spec.ts +24 -0
- package/src/control/state.ts +133 -0
- package/src/errors.ts +163 -0
- package/src/framer/adapter.ts +116 -0
- package/src/framer/client.ts +116 -0
- package/src/framer/external.ts +14 -0
- package/src/framer/frame.spec.ts +317 -0
- package/src/framer/frame.ts +412 -0
- package/src/framer/index.ts +10 -0
- package/src/framer/iterator.spec.ts +62 -0
- package/src/framer/iterator.ts +240 -0
- package/src/framer/streamProxy.ts +59 -0
- package/src/framer/streamer.spec.ts +42 -0
- package/src/framer/streamer.ts +86 -0
- package/src/framer/writer.spec.ts +52 -0
- package/src/framer/writer.ts +236 -0
- package/src/index.ts +53 -0
- package/src/label/client.ts +103 -0
- package/src/label/external.ts +13 -0
- package/src/label/index.ts +10 -0
- package/src/label/label.spec.ts +51 -0
- package/src/label/payload.ts +29 -0
- package/src/label/retriever.ts +65 -0
- package/src/label/writer.ts +90 -0
- package/src/ontology/client.ts +104 -0
- package/src/ontology/external.ts +12 -0
- package/src/ontology/group/client.ts +40 -0
- package/src/ontology/group/external.ts +11 -0
- package/src/ontology/group/group.spec.ts +46 -0
- package/src/ontology/group/group.ts +27 -0
- package/src/ontology/group/index.ts +10 -0
- package/src/ontology/group/payload.ts +65 -0
- package/src/ontology/group/writer.ts +48 -0
- package/src/ontology/index.ts +10 -0
- package/src/ontology/ontology.spec.ts +114 -0
- package/src/ontology/payload.ts +118 -0
- package/src/ontology/retriever.ts +91 -0
- package/src/ontology/signals.ts +135 -0
- package/src/ontology/writer.ts +49 -0
- package/src/ranger/active.ts +56 -0
- package/src/ranger/alias.ts +183 -0
- package/src/ranger/client.ts +129 -0
- package/src/ranger/external.ts +15 -0
- package/src/ranger/index.ts +10 -0
- package/src/ranger/kv.ts +91 -0
- package/src/ranger/payload.ts +70 -0
- package/src/ranger/range.ts +95 -0
- package/src/ranger/ranger.spec.ts +201 -0
- package/src/ranger/retriever.ts +50 -0
- package/src/ranger/writer.ts +80 -0
- package/src/setupspecs.ts +25 -0
- package/src/signals/external.ts +10 -0
- package/src/signals/index.ts +10 -0
- package/src/signals/observable.ts +80 -0
- package/src/transport.ts +39 -0
- package/src/user/index.ts +10 -0
- package/src/user/payload.ts +17 -0
- package/src/util/telem.ts +19 -0
- package/src/vite-env.d.ts +11 -0
- package/src/workspace/client.ts +75 -0
- package/src/workspace/external.ts +11 -0
- package/src/workspace/index.ts +10 -0
- package/src/workspace/lineplot/client.ts +51 -0
- package/src/workspace/lineplot/external.ts +11 -0
- package/src/workspace/lineplot/index.ts +10 -0
- package/src/workspace/lineplot/linePlot.spec.ts +78 -0
- package/src/workspace/lineplot/payload.ts +29 -0
- package/src/workspace/lineplot/retriever.ts +49 -0
- package/src/workspace/lineplot/writer.ts +109 -0
- package/src/workspace/payload.ts +29 -0
- package/src/workspace/pid/client.ts +55 -0
- package/src/workspace/pid/external.ts +11 -0
- package/src/workspace/pid/index.ts +10 -0
- package/src/workspace/pid/payload.ts +31 -0
- package/src/workspace/pid/pid.spec.ts +111 -0
- package/src/workspace/pid/retriever.ts +45 -0
- package/src/workspace/pid/writer.ts +130 -0
- package/src/workspace/retriever.ts +66 -0
- package/src/workspace/workspace.spec.ts +62 -0
- package/src/workspace/writer.ts +103 -0
- package/tsconfig.json +7 -0
- package/tsconfig.vite.json +4 -0
- package/vite.config.ts +25 -0
- package/CHANGELOG.md +0 -5
- package/build/main/index.d.ts +0 -4
- package/build/main/index.js +0 -35
- package/build/main/lib/auth.d.ts +0 -54
- package/build/main/lib/auth.js +0 -62
- package/build/main/lib/auth.spec.js +0 -39
- package/build/main/lib/channel/channel.spec.js +0 -49
- package/build/main/lib/channel/client.d.ts +0 -94
- package/build/main/lib/channel/client.js +0 -134
- package/build/main/lib/channel/creator.d.ts +0 -19
- package/build/main/lib/channel/creator.js +0 -44
- package/build/main/lib/channel/payload.d.ts +0 -25
- package/build/main/lib/channel/payload.js +0 -18
- package/build/main/lib/channel/registry.d.ts +0 -9
- package/build/main/lib/channel/registry.js +0 -37
- package/build/main/lib/channel/retriever.d.ts +0 -11
- package/build/main/lib/channel/retriever.js +0 -39
- package/build/main/lib/client.d.ts +0 -30
- package/build/main/lib/client.js +0 -46
- package/build/main/lib/errors.js +0 -122
- package/build/main/lib/segment/client.d.ts +0 -62
- package/build/main/lib/segment/client.js +0 -95
- package/build/main/lib/segment/iterator.d.ts +0 -134
- package/build/main/lib/segment/iterator.js +0 -253
- package/build/main/lib/segment/iterator.spec.js +0 -73
- package/build/main/lib/segment/payload.d.ts +0 -16
- package/build/main/lib/segment/payload.js +0 -13
- package/build/main/lib/segment/splitter.d.ts +0 -7
- package/build/main/lib/segment/splitter.js +0 -25
- package/build/main/lib/segment/typed.d.ts +0 -15
- package/build/main/lib/segment/typed.js +0 -49
- package/build/main/lib/segment/validator.d.ts +0 -22
- package/build/main/lib/segment/validator.js +0 -64
- package/build/main/lib/segment/writer.d.ts +0 -98
- package/build/main/lib/segment/writer.js +0 -183
- package/build/main/lib/segment/writer.spec.js +0 -90
- package/build/main/lib/telem.d.ts +0 -395
- package/build/main/lib/telem.js +0 -553
- package/build/main/lib/telem.spec.js +0 -152
- package/build/main/lib/transport.d.ts +0 -10
- package/build/main/lib/transport.js +0 -22
- package/build/main/lib/user/payload.js +0 -9
- package/build/main/lib/util/telem.d.ts +0 -2
- package/build/main/lib/util/telem.js +0 -13
- package/build/main/setupspecs.js +0 -17
- package/build/module/index.d.ts +0 -4
- package/build/module/index.js +0 -5
- package/build/module/lib/auth.js +0 -63
- package/build/module/lib/auth.spec.js +0 -34
- package/build/module/lib/channel/channel.spec.js +0 -44
- package/build/module/lib/channel/client.d.ts +0 -94
- package/build/module/lib/channel/client.js +0 -134
- package/build/module/lib/channel/creator.d.ts +0 -19
- package/build/module/lib/channel/creator.js +0 -42
- package/build/module/lib/channel/payload.d.ts +0 -25
- package/build/module/lib/channel/payload.js +0 -15
- package/build/module/lib/channel/registry.d.ts +0 -9
- package/build/module/lib/channel/registry.js +0 -36
- package/build/module/lib/channel/retriever.d.ts +0 -11
- package/build/module/lib/channel/retriever.js +0 -37
- package/build/module/lib/client.d.ts +0 -30
- package/build/module/lib/client.js +0 -44
- package/build/module/lib/errors.d.ts +0 -53
- package/build/module/lib/errors.js +0 -113
- package/build/module/lib/segment/client.d.ts +0 -62
- package/build/module/lib/segment/client.js +0 -94
- package/build/module/lib/segment/iterator.js +0 -248
- package/build/module/lib/segment/iterator.spec.js +0 -68
- package/build/module/lib/segment/payload.d.ts +0 -16
- package/build/module/lib/segment/payload.js +0 -10
- package/build/module/lib/segment/splitter.d.ts +0 -7
- package/build/module/lib/segment/splitter.js +0 -26
- package/build/module/lib/segment/typed.d.ts +0 -15
- package/build/module/lib/segment/typed.js +0 -49
- package/build/module/lib/segment/validator.d.ts +0 -22
- package/build/module/lib/segment/validator.js +0 -60
- package/build/module/lib/segment/writer.d.ts +0 -98
- package/build/module/lib/segment/writer.js +0 -183
- package/build/module/lib/segment/writer.spec.js +0 -85
- package/build/module/lib/telem.d.ts +0 -395
- package/build/module/lib/telem.js +0 -545
- package/build/module/lib/telem.spec.js +0 -147
- package/build/module/lib/transport.d.ts +0 -10
- package/build/module/lib/transport.js +0 -22
- package/build/module/lib/user/payload.d.ts +0 -12
- package/build/module/lib/user/payload.js +0 -6
- package/build/module/lib/util/telem.d.ts +0 -2
- package/build/module/lib/util/telem.js +0 -9
- package/build/module/setupspecs.d.ts +0 -4
- package/build/module/setupspecs.js +0 -16
- /package/{build/main/lib → dist/auth}/auth.spec.d.ts +0 -0
- /package/{build/main/lib → dist}/channel/channel.spec.d.ts +0 -0
- /package/{build/main/lib/segment/iterator.spec.d.ts → dist/connection/connection.spec.d.ts} +0 -0
- /package/{build/main/lib/segment/writer.spec.d.ts → dist/control/state.spec.d.ts} +0 -0
- /package/{build/main/lib/telem.spec.d.ts → dist/framer/frame.spec.d.ts} +0 -0
- /package/{build/module/lib/segment → dist/framer}/iterator.spec.d.ts +0 -0
- /package/{build/module/lib/auth.spec.d.ts → dist/framer/streamer.spec.d.ts} +0 -0
- /package/{build/module/lib/segment → dist/framer}/writer.spec.d.ts +0 -0
- /package/{build/module/lib/channel/channel.spec.d.ts → dist/label/label.spec.d.ts} +0 -0
- /package/{build/module/lib/telem.spec.d.ts → dist/ontology/group/group.spec.d.ts} +0 -0
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
// Copyright 2023 Synnax Labs, Inc.
|
|
2
|
+
//
|
|
3
|
+
// Use of this software is governed by the Business Source License included in the file
|
|
4
|
+
// licenses/BSL.txt.
|
|
5
|
+
//
|
|
6
|
+
// As of the Change Date specified in that file, in accordance with the Business Source
|
|
7
|
+
// License, use of this software will be governed by the Apache License, Version 2.0,
|
|
8
|
+
// included in the file licenses/APL.txt.
|
|
9
|
+
|
|
10
|
+
import { errorZ, type Stream, type StreamClient } from "@synnaxlabs/freighter";
|
|
11
|
+
import {
|
|
12
|
+
type CrudeTimeSpan,
|
|
13
|
+
type CrudeTimeStamp,
|
|
14
|
+
TimeRange,
|
|
15
|
+
TimeSpan,
|
|
16
|
+
TimeStamp,
|
|
17
|
+
} from "@synnaxlabs/x";
|
|
18
|
+
import { z } from "zod";
|
|
19
|
+
|
|
20
|
+
import { type Params } from "@/channel/payload";
|
|
21
|
+
import { type Retriever } from "@/channel/retriever";
|
|
22
|
+
import { BackwardFrameAdapter } from "@/framer/adapter";
|
|
23
|
+
import { Frame, frameZ } from "@/framer/frame";
|
|
24
|
+
import { StreamProxy } from "@/framer/streamProxy";
|
|
25
|
+
|
|
26
|
+
export const AUTO_SPAN = new TimeSpan(-1);
|
|
27
|
+
|
|
28
|
+
enum Command {
|
|
29
|
+
Open = 0,
|
|
30
|
+
Next = 1,
|
|
31
|
+
Prev = 2,
|
|
32
|
+
SeekFirst = 3,
|
|
33
|
+
SeekLast = 4,
|
|
34
|
+
SeekLE = 5,
|
|
35
|
+
SeekGE = 6,
|
|
36
|
+
Valid = 7,
|
|
37
|
+
Error = 8,
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
enum ResponseVariant {
|
|
41
|
+
None = 0,
|
|
42
|
+
Ack = 1,
|
|
43
|
+
Data = 2,
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const reqZ = z.object({
|
|
47
|
+
command: z.nativeEnum(Command),
|
|
48
|
+
span: TimeSpan.z.optional(),
|
|
49
|
+
bounds: TimeRange.z.optional(),
|
|
50
|
+
stamp: TimeStamp.z.optional(),
|
|
51
|
+
keys: z.number().array().optional(),
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
type Request = z.infer<typeof reqZ>;
|
|
55
|
+
|
|
56
|
+
const resZ = z.object({
|
|
57
|
+
variant: z.nativeEnum(ResponseVariant),
|
|
58
|
+
ack: z.boolean(),
|
|
59
|
+
command: z.nativeEnum(Command),
|
|
60
|
+
error: errorZ.optional().nullable(),
|
|
61
|
+
frame: frameZ.optional(),
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Used to iterate over a clusters telemetry in time-order. It should not be
|
|
66
|
+
* instantiated directly, and should instead be instantiated via the SegmentClient.
|
|
67
|
+
*
|
|
68
|
+
* Using an iterator is ideal when querying/processing large ranges of data, but
|
|
69
|
+
* is relatively complex and difficult to use. If you're looking to retrieve
|
|
70
|
+
* telemetry between two timestamps, see the SegmentClient.read method.
|
|
71
|
+
*/
|
|
72
|
+
export class Iterator {
|
|
73
|
+
private static readonly ENDPOINT = "/frame/iterate";
|
|
74
|
+
private readonly stream: StreamProxy<typeof reqZ, typeof resZ>;
|
|
75
|
+
private readonly adapter: BackwardFrameAdapter;
|
|
76
|
+
value: Frame;
|
|
77
|
+
|
|
78
|
+
private constructor(
|
|
79
|
+
stream: Stream<typeof reqZ, typeof resZ>,
|
|
80
|
+
adapter: BackwardFrameAdapter,
|
|
81
|
+
) {
|
|
82
|
+
this.stream = new StreamProxy("Iterator", stream);
|
|
83
|
+
this.value = new Frame();
|
|
84
|
+
this.adapter = adapter;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Opens the iterator, configuring it to iterate over the telemetry in the
|
|
89
|
+
* channels with the given keys within the provided time range.
|
|
90
|
+
*
|
|
91
|
+
* @param tr - The time range to iterate over.
|
|
92
|
+
* @param keys - The keys of the channels to iterate over.
|
|
93
|
+
*/
|
|
94
|
+
static async _open(
|
|
95
|
+
tr: TimeRange,
|
|
96
|
+
channels: Params,
|
|
97
|
+
retriever: Retriever,
|
|
98
|
+
client: StreamClient,
|
|
99
|
+
): Promise<Iterator> {
|
|
100
|
+
const adapter = await BackwardFrameAdapter.open(retriever, channels);
|
|
101
|
+
const stream = await client.stream(Iterator.ENDPOINT, reqZ, resZ);
|
|
102
|
+
const iter = new Iterator(stream, adapter);
|
|
103
|
+
await iter.execute({ command: Command.Open, keys: adapter.keys, bounds: tr });
|
|
104
|
+
return iter;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Reads the next time span of telemetry for each channel in the iterator.
|
|
109
|
+
*
|
|
110
|
+
* @param span - The time span to read. A negative span is equivalent
|
|
111
|
+
* to calling prev with the absolute value of the span. If the span is
|
|
112
|
+
* AUTO_SPAN, the iterator will automatically determine the span to read.
|
|
113
|
+
* This is useful for iterating over an entire range efficiently.
|
|
114
|
+
*
|
|
115
|
+
* @returns false if a segment satisfying the request can't be found for a
|
|
116
|
+
* particular channel or the iterator has accumulated an error.
|
|
117
|
+
*/
|
|
118
|
+
async next(span: CrudeTimeSpan = AUTO_SPAN): Promise<boolean> {
|
|
119
|
+
return await this.execute({ command: Command.Next, span: new TimeSpan(span) });
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Reads the previous time span of telemetry for each channel in the iterator.
|
|
124
|
+
*
|
|
125
|
+
* @param span - The time span to read. A negative span is equivalent
|
|
126
|
+
* to calling next with the absolute value of the span. If the span is
|
|
127
|
+
* AUTO_SPAN, the iterator will automatically determine the span to read.
|
|
128
|
+
* This is useful for iterating over an entire range efficiently.
|
|
129
|
+
*
|
|
130
|
+
* @returns false if a segment satisfying the request can't be found for a particular
|
|
131
|
+
* channel or the iterator has accumulated an error.
|
|
132
|
+
*/
|
|
133
|
+
async prev(span: CrudeTimeSpan = AUTO_SPAN): Promise<boolean> {
|
|
134
|
+
return await this.execute({ command: Command.Prev, span: new TimeSpan(span) });
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Seeks the iterator to the first segment in the time range, but does not read
|
|
139
|
+
* it. Also invalidates the iterator. The iterator will not be considered valid
|
|
140
|
+
* until a call to next or prev.
|
|
141
|
+
*
|
|
142
|
+
* @returns false if the iterator is not pointing to a valid segment for a particular
|
|
143
|
+
* channel or has accumulated an error.
|
|
144
|
+
*/
|
|
145
|
+
async seekFirst(): Promise<boolean> {
|
|
146
|
+
return await this.execute({ command: Command.SeekFirst });
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/** Seeks the iterator to the last segment in the time range, but does not read it.
|
|
150
|
+
* Also invalidates the iterator. The iterator will not be considered valid
|
|
151
|
+
* until a call to next or prev.
|
|
152
|
+
*
|
|
153
|
+
* @returns false if the iterator is not pointing to a valid segment for a particular
|
|
154
|
+
* channel or has accumulated an error.
|
|
155
|
+
*/
|
|
156
|
+
async seekLast(): Promise<boolean> {
|
|
157
|
+
return await this.execute({ command: Command.SeekLast });
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Seeks the iterator to the first segment whose start is less than or equal to
|
|
162
|
+
* the provided timestamp. Also invalidates the iterator. The iterator will not be
|
|
163
|
+
* considered valid until a call to next or prev.
|
|
164
|
+
*
|
|
165
|
+
* @returns false if the iterator is not pointing to a valid segment for a particular
|
|
166
|
+
* channel or has accumulated an error.
|
|
167
|
+
*/
|
|
168
|
+
async seekLE(stamp: CrudeTimeStamp): Promise<boolean> {
|
|
169
|
+
return await this.execute({ command: Command.SeekLE, stamp: new TimeStamp(stamp) });
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Seeks the iterator to the first segment whose start is greater than or equal to
|
|
174
|
+
* the provided timestamp. Also invalidates the iterator. The iterator will not be
|
|
175
|
+
* considered valid until a call to next or prev.
|
|
176
|
+
*
|
|
177
|
+
* @returns false if the iterator is not pointing to a valid segment for a particular
|
|
178
|
+
* channel or has accumulated an error.
|
|
179
|
+
*/
|
|
180
|
+
async seekGE(stamp: CrudeTimeStamp): Promise<boolean> {
|
|
181
|
+
return await this.execute({ command: Command.SeekGE, stamp: new TimeStamp(stamp) });
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* @returns true if the iterator value contains a valid segment, and fale otherwise.
|
|
186
|
+
* valid most commonly returns false when the iterator is exhausted or has
|
|
187
|
+
* accumulated an error.
|
|
188
|
+
*/
|
|
189
|
+
async valid(): Promise<boolean> {
|
|
190
|
+
return await this.execute({ command: Command.Valid });
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Closes the iterator. An iterator MUST be closed after use, and this method
|
|
195
|
+
* should probably be placed in a 'finally' block. If the iterator is not closed,
|
|
196
|
+
* it may leak resources.
|
|
197
|
+
*/
|
|
198
|
+
async close(): Promise<void> {
|
|
199
|
+
await this.stream.closeAndAck();
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
[Symbol.asyncIterator](): AsyncIterator<Frame, any, undefined> {
|
|
203
|
+
return new IteratorIterator(this);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
private async execute(request: Request): Promise<boolean> {
|
|
207
|
+
this.stream.send(request);
|
|
208
|
+
this.value = new Frame();
|
|
209
|
+
while (true) {
|
|
210
|
+
const res = await this.stream.receive();
|
|
211
|
+
if (res.variant === ResponseVariant.Ack) return res.ack;
|
|
212
|
+
this.value.push(this.adapter.adapt(new Frame(res.frame)));
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
class IteratorIterator implements AsyncIterator<Frame> {
|
|
218
|
+
private readonly iter: Iterator;
|
|
219
|
+
private open: boolean = false;
|
|
220
|
+
|
|
221
|
+
constructor(iter: Iterator) {
|
|
222
|
+
this.iter = iter;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
async next(): Promise<IteratorResult<Frame, any>> {
|
|
226
|
+
try {
|
|
227
|
+
let ok = true;
|
|
228
|
+
if (!this.open) {
|
|
229
|
+
if (!(await this.iter.seekFirst())) ok = false;
|
|
230
|
+
this.open = true;
|
|
231
|
+
}
|
|
232
|
+
if (!(await this.iter.next())) ok = false;
|
|
233
|
+
if (!ok) await this.iter.close();
|
|
234
|
+
return { done: !ok, value: this.iter.value };
|
|
235
|
+
} catch (e) {
|
|
236
|
+
await this.iter.close();
|
|
237
|
+
throw e;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
// Copyright 2023 Synnax Labs, Inc.
|
|
2
|
+
//
|
|
3
|
+
// Use of this software is governed by the Business Source License included in the file
|
|
4
|
+
// licenses/BSL.txt.
|
|
5
|
+
//
|
|
6
|
+
// As of the Change Date specified in that file, in accordance with the Business Source
|
|
7
|
+
// License, use of this software will be governed by the Apache License, Version 2.0,
|
|
8
|
+
// included in the file licenses/APL.txt.
|
|
9
|
+
|
|
10
|
+
import { type Stream, EOF } from "@synnaxlabs/freighter";
|
|
11
|
+
import { type z } from "zod";
|
|
12
|
+
|
|
13
|
+
import { UnexpectedError } from "@/errors";
|
|
14
|
+
|
|
15
|
+
export class StreamProxy<RQ extends z.ZodTypeAny, RS extends z.ZodTypeAny> {
|
|
16
|
+
readonly name: string;
|
|
17
|
+
private readonly stream: Stream<RQ, RS>;
|
|
18
|
+
|
|
19
|
+
constructor(name: string, stream: Stream<RQ, RS>) {
|
|
20
|
+
this.stream = stream;
|
|
21
|
+
this.name = name;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async receive(): Promise<z.output<RS>> {
|
|
25
|
+
const [res, err] = await this.stream.receive();
|
|
26
|
+
if (err != null) throw err;
|
|
27
|
+
return res;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
received(): boolean {
|
|
31
|
+
return this.stream.received();
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async closeAndAck(): Promise<void> {
|
|
35
|
+
this.stream.closeSend();
|
|
36
|
+
const [res, err] = await this.stream.receive();
|
|
37
|
+
if (res != null)
|
|
38
|
+
throw new UnexpectedError(
|
|
39
|
+
`${this.name} received unexpected response on closure.
|
|
40
|
+
Please report this error to the Synnax team.`,
|
|
41
|
+
);
|
|
42
|
+
if (err == null)
|
|
43
|
+
throw new UnexpectedError(
|
|
44
|
+
`${this.name} received unexpected null error on closure.
|
|
45
|
+
Please report this error to Synnax team.
|
|
46
|
+
`,
|
|
47
|
+
);
|
|
48
|
+
if (!(err instanceof EOF)) throw err;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
closeSend(): void {
|
|
52
|
+
this.stream.closeSend();
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
send(req: z.input<RQ>): void {
|
|
56
|
+
const err = this.stream.send(req);
|
|
57
|
+
if (err != null) throw err;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
// Copyright 2023 Synnax Labs, Inc.
|
|
2
|
+
//
|
|
3
|
+
// Use of this software is governed by the Business Source License included in the file
|
|
4
|
+
// licenses/BSL.txt.
|
|
5
|
+
//
|
|
6
|
+
// As of the Change Date specified in that file, in accordance with the Business Source
|
|
7
|
+
// License, use of this software will be governed by the Apache License, Version 2.0,
|
|
8
|
+
// included in the file licenses/APL.txt.
|
|
9
|
+
|
|
10
|
+
import { DataType, Rate, TimeStamp } from "@synnaxlabs/x";
|
|
11
|
+
import { describe, test, expect } from "vitest";
|
|
12
|
+
|
|
13
|
+
import { type channel } from "@/channel";
|
|
14
|
+
import { newClient } from "@/setupspecs";
|
|
15
|
+
|
|
16
|
+
const client = newClient();
|
|
17
|
+
|
|
18
|
+
const newChannel = async (): Promise<channel.Channel> =>
|
|
19
|
+
await client.channels.create({
|
|
20
|
+
name: "test",
|
|
21
|
+
leaseholder: 1,
|
|
22
|
+
rate: Rate.hz(25),
|
|
23
|
+
dataType: DataType.FLOAT64,
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
describe("Streamer", () => {
|
|
27
|
+
test("happy path", async () => {
|
|
28
|
+
const ch = await newChannel();
|
|
29
|
+
const streamer = await client.telem.newStreamer(ch.key);
|
|
30
|
+
const writer = await client.telem.newWriter({
|
|
31
|
+
start: TimeStamp.now(),
|
|
32
|
+
channels: ch.key,
|
|
33
|
+
});
|
|
34
|
+
try {
|
|
35
|
+
await writer.write(ch.key, new Float64Array([1, 2, 3]));
|
|
36
|
+
} finally {
|
|
37
|
+
await writer.close();
|
|
38
|
+
}
|
|
39
|
+
const d = await streamer.read();
|
|
40
|
+
expect(d.get(ch.key)[0].data).toEqual(new Float64Array([1, 2, 3]));
|
|
41
|
+
});
|
|
42
|
+
});
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
// Copyright 2023 Synnax Labs, Inc.
|
|
2
|
+
//
|
|
3
|
+
// Use of this software is governed by the Business Source License included in the file
|
|
4
|
+
// licenses/BSL.txt.
|
|
5
|
+
//
|
|
6
|
+
// As of the Change Date specified in that file, in accordance with the Business Source
|
|
7
|
+
// License, use of this software will be governed by the Apache License, Version 2.0,
|
|
8
|
+
// included in the file licenses/APL.txt.
|
|
9
|
+
|
|
10
|
+
import { errorZ, type Stream, type StreamClient } from "@synnaxlabs/freighter";
|
|
11
|
+
import { TimeStamp, type CrudeTimeStamp } from "@synnaxlabs/x";
|
|
12
|
+
import { z } from "zod";
|
|
13
|
+
|
|
14
|
+
import { type Key, type Params } from "@/channel/payload";
|
|
15
|
+
import { type Retriever } from "@/channel/retriever";
|
|
16
|
+
import { BackwardFrameAdapter } from "@/framer/adapter";
|
|
17
|
+
import { Frame, frameZ } from "@/framer/frame";
|
|
18
|
+
import { StreamProxy } from "@/framer/streamProxy";
|
|
19
|
+
|
|
20
|
+
const reqZ = z.object({
|
|
21
|
+
start: TimeStamp.z.optional(),
|
|
22
|
+
keys: z.number().array(),
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
const resZ = z.object({
|
|
26
|
+
frame: frameZ,
|
|
27
|
+
error: errorZ.optional().nullable(),
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
const ENDPOINT = "/frame/stream";
|
|
31
|
+
|
|
32
|
+
export class Streamer implements AsyncIterator<Frame>, AsyncIterable<Frame> {
|
|
33
|
+
private readonly stream: StreamProxy<typeof reqZ, typeof resZ>;
|
|
34
|
+
private readonly adapter: BackwardFrameAdapter;
|
|
35
|
+
|
|
36
|
+
private constructor(
|
|
37
|
+
stream: Stream<typeof reqZ, typeof resZ>,
|
|
38
|
+
adapter: BackwardFrameAdapter,
|
|
39
|
+
) {
|
|
40
|
+
this.stream = new StreamProxy("Streamer", stream);
|
|
41
|
+
this.adapter = adapter;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
get keys(): Key[] {
|
|
45
|
+
return this.adapter.keys;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
static async _open(
|
|
49
|
+
start: CrudeTimeStamp,
|
|
50
|
+
channels: Params,
|
|
51
|
+
retriever: Retriever,
|
|
52
|
+
client: StreamClient,
|
|
53
|
+
): Promise<Streamer> {
|
|
54
|
+
const adapter = await BackwardFrameAdapter.open(retriever, channels);
|
|
55
|
+
const stream = await client.stream(ENDPOINT, reqZ, resZ);
|
|
56
|
+
const streamer = new Streamer(stream, adapter);
|
|
57
|
+
stream.send({ start: new TimeStamp(start), keys: adapter.keys });
|
|
58
|
+
return streamer;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
async next(): Promise<IteratorResult<Frame, any>> {
|
|
62
|
+
try {
|
|
63
|
+
const frame = await this.read();
|
|
64
|
+
return { done: false, value: frame };
|
|
65
|
+
} catch (EOF) {
|
|
66
|
+
return { done: true, value: undefined };
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
async read(): Promise<Frame> {
|
|
71
|
+
return this.adapter.adapt(new Frame((await this.stream.receive()).frame));
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
async update(params: Params): Promise<void> {
|
|
75
|
+
await this.adapter.update(params);
|
|
76
|
+
this.stream.send({ keys: this.adapter.keys });
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
close(): void {
|
|
80
|
+
this.stream.closeSend();
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
[Symbol.asyncIterator](): AsyncIterator<Frame, any, undefined> {
|
|
84
|
+
return this;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
// Copyright 2023 Synnax Labs, Inc.
|
|
2
|
+
//
|
|
3
|
+
// Use of this software is governed by the Business Source License included in the file
|
|
4
|
+
// licenses/BSL.txt.
|
|
5
|
+
//
|
|
6
|
+
// As of the Change Date specified in that file, in accordance with the Business Source
|
|
7
|
+
// License, use of this software will be governed by the Apache License, Version 2.0,
|
|
8
|
+
// included in the file licenses/APL.txt.
|
|
9
|
+
|
|
10
|
+
import { DataType, Rate, TimeRange, TimeStamp } from "@synnaxlabs/x";
|
|
11
|
+
import { describe, expect, test } from "vitest";
|
|
12
|
+
|
|
13
|
+
import { type channel } from "@/channel";
|
|
14
|
+
import { newClient } from "@/setupspecs";
|
|
15
|
+
import { randomSeries } from "@/util/telem";
|
|
16
|
+
|
|
17
|
+
const client = newClient();
|
|
18
|
+
|
|
19
|
+
const newChannel = async (): Promise<channel.Channel> => {
|
|
20
|
+
return await client.channels.create({
|
|
21
|
+
name: "test",
|
|
22
|
+
leaseholder: 1,
|
|
23
|
+
rate: Rate.hz(1),
|
|
24
|
+
dataType: DataType.FLOAT64,
|
|
25
|
+
});
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
describe("Writer", () => {
|
|
29
|
+
describe("Writer", () => {
|
|
30
|
+
test("basic write", async () => {
|
|
31
|
+
const ch = await newChannel();
|
|
32
|
+
const writer = await client.telem.newWriter({ start: 0, channels: ch.key });
|
|
33
|
+
try {
|
|
34
|
+
await writer.write(ch.key, randomSeries(10, ch.dataType));
|
|
35
|
+
await writer.commit();
|
|
36
|
+
} finally {
|
|
37
|
+
await writer.close();
|
|
38
|
+
}
|
|
39
|
+
expect(true).toBeTruthy();
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
describe("Client", () => {
|
|
43
|
+
test("Client - basic write", async () => {
|
|
44
|
+
const ch = await newChannel();
|
|
45
|
+
const data = randomSeries(10, ch.dataType);
|
|
46
|
+
await client.telem.write(ch.key, TimeStamp.seconds(1), data);
|
|
47
|
+
const res = await client.telem.read(TimeRange.MAX, ch.key);
|
|
48
|
+
expect(res.length).toEqual(data.length);
|
|
49
|
+
expect(res.data).toEqual(data);
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
});
|