@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,412 @@
|
|
|
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 { Size, Series, TimeRange, toArray, DataType, unique } from "@synnaxlabs/x";
|
|
11
|
+
import { z } from "zod";
|
|
12
|
+
|
|
13
|
+
import {
|
|
14
|
+
type Key,
|
|
15
|
+
type KeyOrName,
|
|
16
|
+
type Keys,
|
|
17
|
+
type Names,
|
|
18
|
+
type Params,
|
|
19
|
+
} from "@/channel/payload";
|
|
20
|
+
import { UnexpectedError, ValidationError } from "@/errors";
|
|
21
|
+
|
|
22
|
+
type ColumnType = "key" | "name" | null;
|
|
23
|
+
|
|
24
|
+
const columnType = (columns: Params): ColumnType => {
|
|
25
|
+
const arrKeys = toArray(columns);
|
|
26
|
+
if (arrKeys.length === 0) return null;
|
|
27
|
+
if (typeof arrKeys[0] === "number") return "key";
|
|
28
|
+
return "name";
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const validateMatchedColsAndArrays = (columns: Params, arrays: Series[]): void => {
|
|
32
|
+
const colsArr = toArray(columns);
|
|
33
|
+
if (colsArr.length === arrays.length) return;
|
|
34
|
+
const colType = columnType(columns);
|
|
35
|
+
if (columnType === null)
|
|
36
|
+
throw new ValidationError(
|
|
37
|
+
"[Frame] - channel keys or names must be provided when constructing a frame.",
|
|
38
|
+
);
|
|
39
|
+
throw new ValidationError(
|
|
40
|
+
`[Frame] - ${colType as string}s and arrays must be the same length.
|
|
41
|
+
Got ${colsArr.length} ${colType as string}s and ${arrays.length} arrays.`,
|
|
42
|
+
);
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export type CrudeFrame =
|
|
46
|
+
| Frame
|
|
47
|
+
| FramePayload
|
|
48
|
+
| Map<KeyOrName, Series[] | Series>
|
|
49
|
+
| Record<KeyOrName, Series[] | Series>;
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* A frame is a collection of related typed arrays keyed to a particular channel. Frames
|
|
53
|
+
* can be keyed by channel name or channel key, but not both.
|
|
54
|
+
*
|
|
55
|
+
* Frames have two important characteristics: alignment and orientation.
|
|
56
|
+
*
|
|
57
|
+
* A frame's alignment defines how correlated the arrays for different channels in the
|
|
58
|
+
* frame are:
|
|
59
|
+
*
|
|
60
|
+
* - A frame is weakly aligned if it meets the time range occupied by all arrays of a
|
|
61
|
+
* particular channel is the same for all channels in the frame. This means that the
|
|
62
|
+
* arrays for a particular channel can have gaps betwen them.
|
|
63
|
+
*
|
|
64
|
+
* - A strongly aligned frame means that all channels share the same rate/index and
|
|
65
|
+
* there are no gaps in time between arrays. Strongly aligned frames are natural
|
|
66
|
+
* to interpret, as the values in a particular 'row' of the frame share the same
|
|
67
|
+
* timestamp. All frames written to Synnax must be strongly aligned.
|
|
68
|
+
*
|
|
69
|
+
* - Unaligned frames don't meet the requirements for weakly or strongly aligned frames.
|
|
70
|
+
* Unaligned frames are common when reading from channels that don't have the same
|
|
71
|
+
* index/rate and are continuous in different periods of time.
|
|
72
|
+
*
|
|
73
|
+
* Frames also have an orientation: horizontal, vertical, or square.
|
|
74
|
+
*
|
|
75
|
+
* - Horizontal frames have a single channel, and are strongly aligned by default.
|
|
76
|
+
* A horizontal frame typically has a single array (in which case, it's also 'square'),
|
|
77
|
+
* although it can have multiple arrays if all the arrays are continuous in time.
|
|
78
|
+
*
|
|
79
|
+
* - Vertical frames are strongly aligned and have on or more channels, but ONLY a single
|
|
80
|
+
* array per channel. Synnax requires that all frames written to the database are
|
|
81
|
+
* vertical.
|
|
82
|
+
*
|
|
83
|
+
* - Square frames are both horizontal and vertical. Only a frame with a single channel
|
|
84
|
+
* and array can be square.
|
|
85
|
+
*/
|
|
86
|
+
export class Frame {
|
|
87
|
+
readonly columns: Keys | Names = [];
|
|
88
|
+
readonly series: Series[] = [];
|
|
89
|
+
|
|
90
|
+
constructor(columnsOrData: Params | CrudeFrame = [], arrays: Series | Series[] = []) {
|
|
91
|
+
if (columnsOrData instanceof Frame) {
|
|
92
|
+
this.columns = columnsOrData.columns;
|
|
93
|
+
this.series = columnsOrData.series;
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Construction from a map.
|
|
98
|
+
if (columnsOrData instanceof Map) {
|
|
99
|
+
columnsOrData.forEach((v, k) => this.push(k, ...toArray(v)));
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const isObject = typeof columnsOrData === "object" && !Array.isArray(columnsOrData);
|
|
104
|
+
|
|
105
|
+
// Construction from a payload.
|
|
106
|
+
if (isObject) {
|
|
107
|
+
if ("keys" in columnsOrData && "series" in columnsOrData) {
|
|
108
|
+
const data_ = columnsOrData as FramePayload;
|
|
109
|
+
const arrays = data_.series.map((a) => seriesFromPayload(a));
|
|
110
|
+
validateMatchedColsAndArrays(data_.keys, arrays);
|
|
111
|
+
data_.keys.forEach((key, i) => this.push(key, arrays[i]));
|
|
112
|
+
} else
|
|
113
|
+
Object.entries(columnsOrData).forEach(([k, v]) => this.push(k, ...toArray(v)));
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Construction from a set of arrays and columns.
|
|
118
|
+
if (
|
|
119
|
+
Array.isArray(columnsOrData) ||
|
|
120
|
+
["string", "number"].includes(typeof columnsOrData)
|
|
121
|
+
) {
|
|
122
|
+
const data_ = toArray(arrays);
|
|
123
|
+
const cols = toArray(columnsOrData) as Keys | Names;
|
|
124
|
+
validateMatchedColsAndArrays(cols, data_);
|
|
125
|
+
data_.forEach((d, i) => this.push(cols[i], d));
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
throw new ValidationError(
|
|
130
|
+
`[Frame] - invalid frame construction parameters. data parameter ust be a frame
|
|
131
|
+
payload, a list of lazy arrays, a lazy array, a map, or a record keyed by channel
|
|
132
|
+
name. keys parameter must be a set of channel keys or channel names.`,
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* @returns "key" if the frame columns are channel keys, "name" if the columns are
|
|
138
|
+
* channel names, and null if the frame has no columns.
|
|
139
|
+
*/
|
|
140
|
+
get colType(): ColumnType {
|
|
141
|
+
if (this.columns.length === 0) return null;
|
|
142
|
+
const firstKey = this.columns[0];
|
|
143
|
+
return typeof firstKey === "string" ? "name" : "key";
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* @returns the channel keys if the frame is keyed by channel key, and throws an error
|
|
148
|
+
* otherwise.
|
|
149
|
+
*/
|
|
150
|
+
get keys(): Keys {
|
|
151
|
+
if (this.colType === "name") throw new UnexpectedError("colType is not key");
|
|
152
|
+
return (this.columns as Keys) ?? [];
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* @returns the unique channel keys if the frame is keyed by channel key, and throws an
|
|
157
|
+
* error otherwise.
|
|
158
|
+
*/
|
|
159
|
+
get uniqueKeys(): Keys {
|
|
160
|
+
return unique(this.keys);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* @returns the channel names if the frame is keyed by channel name, and throws an error
|
|
165
|
+
* otherwise.
|
|
166
|
+
*/
|
|
167
|
+
get names(): Names {
|
|
168
|
+
if (this.colType === "key") throw new UnexpectedError("colType is not name");
|
|
169
|
+
return (this.columns as Names) ?? [];
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* @returns the unique channel names if the frame is keyed by channel name, and throws an
|
|
174
|
+
* otherwise.
|
|
175
|
+
*/
|
|
176
|
+
get uniqueNames(): Names {
|
|
177
|
+
return unique(this.names);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* @returns the unique columns in the frame.
|
|
182
|
+
*/
|
|
183
|
+
get uniqueColumns(): Keys | Names {
|
|
184
|
+
return this.colType === "key" ? this.uniqueKeys : this.uniqueNames;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
toPayload(): FramePayload {
|
|
188
|
+
return {
|
|
189
|
+
series: this.series.map((a) => seriesToPayload(a)),
|
|
190
|
+
keys: this.keys,
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* @returns true if the frame is vertical. Vertical frames are strongly aligned and
|
|
196
|
+
* have on or more channels, but ONLY a single array per channel. Synnax requires
|
|
197
|
+
* that all frames written to the database are vertical.
|
|
198
|
+
*/
|
|
199
|
+
get isVertical(): boolean {
|
|
200
|
+
return this.uniqueColumns.length === this.columns.length;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* @returns true if the frame is horizontal. Horizontal frames have a single channel,
|
|
205
|
+
* and are strongly aligned by default.A horizontal frame typically has a single array
|
|
206
|
+
* (in which case, it's also 'square'), although it can have multiple arrays if all
|
|
207
|
+
* the arrays are continuous in time.
|
|
208
|
+
*/
|
|
209
|
+
get isHorizontal(): boolean {
|
|
210
|
+
return this.uniqueColumns.length === 1;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* @returns true if the frame is square. Square frames are both horizontal and vertical.
|
|
215
|
+
* Only a frame with a single channel and array can be square.
|
|
216
|
+
*/
|
|
217
|
+
get isSquare(): boolean {
|
|
218
|
+
return this.isHorizontal && this.isVertical;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* @returns true if the frame is weakly aligned. A frame is weakly aligned if it meets
|
|
223
|
+
* the time range occupied by all arrays of a particular channel is the same for all
|
|
224
|
+
* channels in the frame. This means that the arrays for a particular channel can have
|
|
225
|
+
* gaps betwen them.
|
|
226
|
+
*/
|
|
227
|
+
get isWeaklyAligned(): boolean {
|
|
228
|
+
if (this.columns.length <= 1) return true;
|
|
229
|
+
const ranges = this.timeRanges;
|
|
230
|
+
return ranges.every((tr) => tr.equals(ranges[0]));
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
timeRange(col?: KeyOrName): TimeRange {
|
|
234
|
+
if (col == null) {
|
|
235
|
+
if (this.columns.length === 0) return TimeRange.ZERO;
|
|
236
|
+
const start = Math.min(...this.series.map((a) => a.timeRange.start.valueOf()));
|
|
237
|
+
const end = Math.max(...this.series.map((a) => a.timeRange.end.valueOf()));
|
|
238
|
+
return new TimeRange(start, end);
|
|
239
|
+
}
|
|
240
|
+
const group = this.get(col);
|
|
241
|
+
if (group == null) return TimeRange.ZERO;
|
|
242
|
+
return new TimeRange(
|
|
243
|
+
group[0].timeRange.start,
|
|
244
|
+
group[group.length - 1].timeRange.end,
|
|
245
|
+
);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
get timeRanges(): TimeRange[] {
|
|
249
|
+
return this.uniqueColumns.map((col) => this.timeRange(col));
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* @returns lazy arrays matching the given channel key or name.
|
|
254
|
+
* @param key the channel key or name.
|
|
255
|
+
*/
|
|
256
|
+
get(key: KeyOrName): Series[];
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* @returns a frame with the given channel keys or names.
|
|
260
|
+
* @param keys the channel keys or names.
|
|
261
|
+
*/
|
|
262
|
+
get(keys: Keys | Names): Frame;
|
|
263
|
+
|
|
264
|
+
get(key: KeyOrName | Keys | Names): Series[] | Frame {
|
|
265
|
+
if (Array.isArray(key)) return this.filter((k) => (key as Keys).includes(k as Key));
|
|
266
|
+
return this.series.filter((_, i) => this.columns[i] === key);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Pushes a set of typed arrays for the given channel onto the frame.
|
|
271
|
+
*
|
|
272
|
+
* @param key the channel key or name;
|
|
273
|
+
* @param v the typed arrays to push.
|
|
274
|
+
*/
|
|
275
|
+
push(key: KeyOrName, ...v: Series[]): void;
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* Pushes the frame onto the current frame.
|
|
279
|
+
*
|
|
280
|
+
* @param frame - the frame to push.
|
|
281
|
+
*/
|
|
282
|
+
push(frame: Frame): void;
|
|
283
|
+
|
|
284
|
+
push(keyOrFrame: KeyOrName | Frame, ...v: Series[]): void {
|
|
285
|
+
if (keyOrFrame instanceof Frame) {
|
|
286
|
+
if (this.colType !== null && keyOrFrame.colType !== this.colType)
|
|
287
|
+
throw new ValidationError("keyVariant must match");
|
|
288
|
+
this.series.push(...keyOrFrame.series);
|
|
289
|
+
(this.columns as Keys).push(...(keyOrFrame.columns as Keys));
|
|
290
|
+
} else {
|
|
291
|
+
this.series.push(...v);
|
|
292
|
+
if (typeof keyOrFrame === "string" && this.colType === "key")
|
|
293
|
+
throw new ValidationError("keyVariant must match");
|
|
294
|
+
else if (typeof keyOrFrame !== "string" && this.colType === "name")
|
|
295
|
+
throw new ValidationError("keyVariant must match");
|
|
296
|
+
(this.columns as Keys).push(
|
|
297
|
+
...(Array.from({ length: v.length }, () => keyOrFrame) as Keys),
|
|
298
|
+
);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* @returns a shallow copy of this frame containing all typed arrays in the current frame and the
|
|
304
|
+
* provided frame.
|
|
305
|
+
*/
|
|
306
|
+
concat(frame: Frame): Frame {
|
|
307
|
+
return new Frame([...this.columns, ...frame.columns] as Keys, [
|
|
308
|
+
...this.series,
|
|
309
|
+
...frame.series,
|
|
310
|
+
]);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* @returns true if the frame contains the provided channel key or name.
|
|
315
|
+
* @param channel the channel key or name to check.
|
|
316
|
+
*/
|
|
317
|
+
has(channel: KeyOrName): boolean {
|
|
318
|
+
if (typeof channel === "string" && this.colType === "key") return false;
|
|
319
|
+
else if (typeof channel === "number" && this.colType === "name") return false;
|
|
320
|
+
return (this.columns as Keys).includes(channel as Key);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
/**
|
|
324
|
+
* @returns a new frame containing the mapped output of the provided function.
|
|
325
|
+
* @param fn a function that takes a channel key and typed array and returns a
|
|
326
|
+
* boolean.
|
|
327
|
+
*/
|
|
328
|
+
map(fn: (k: KeyOrName, arr: Series, i: number) => [KeyOrName, Series]): Frame {
|
|
329
|
+
const frame = new Frame();
|
|
330
|
+
this.forEach((k, arr, i) => frame.push(...fn(k, arr, i)));
|
|
331
|
+
return frame;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
/**
|
|
335
|
+
* Iterates over all typed arrays in the current frame.
|
|
336
|
+
*
|
|
337
|
+
* @param fn a function that takes a channel key and typed array.
|
|
338
|
+
*/
|
|
339
|
+
forEach(fn: (k: KeyOrName, arr: Series, i: number) => void): void {
|
|
340
|
+
this.columns.forEach((k, i) => {
|
|
341
|
+
const a = this.series[i];
|
|
342
|
+
fn(k, a, i);
|
|
343
|
+
});
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
/**
|
|
347
|
+
* @returns a new frame containing all typed arrays in the current frame that pass
|
|
348
|
+
* the provided filter function.
|
|
349
|
+
* @param fn a function that takes a channel key and typed array and returns a boolean.
|
|
350
|
+
*/
|
|
351
|
+
filter(fn: (k: KeyOrName, arr: Series, i: number) => boolean): Frame {
|
|
352
|
+
const frame = new Frame();
|
|
353
|
+
this.columns.forEach((k, i) => {
|
|
354
|
+
const a = this.series[i];
|
|
355
|
+
if (fn(k, a, i)) frame.push(k, a);
|
|
356
|
+
});
|
|
357
|
+
return frame;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
/** @returns the total number of bytes in the frame. */
|
|
361
|
+
get byteLength(): Size {
|
|
362
|
+
return new Size(this.series.reduce((acc, v) => acc.add(v.byteLength), Size.ZERO));
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
/** @returns the total number of samples in the frame. */
|
|
366
|
+
get length(): number {
|
|
367
|
+
return this.series.reduce((acc, v) => acc + v.length, 0);
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
export const series = z.object({
|
|
372
|
+
timeRange: TimeRange.z.optional(),
|
|
373
|
+
alignment: z.number().optional(),
|
|
374
|
+
dataType: DataType.z,
|
|
375
|
+
data: z.string().transform(
|
|
376
|
+
(s) =>
|
|
377
|
+
new Uint8Array(
|
|
378
|
+
atob(s)
|
|
379
|
+
.split("")
|
|
380
|
+
.map((c) => c.charCodeAt(0)),
|
|
381
|
+
).buffer,
|
|
382
|
+
),
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
export type SeriesPayload = z.infer<typeof series>;
|
|
386
|
+
|
|
387
|
+
export const frameZ = z.object({
|
|
388
|
+
keys: z.union([
|
|
389
|
+
z.null().transform(() => [] as number[]),
|
|
390
|
+
z.number().array().optional().default([]),
|
|
391
|
+
]),
|
|
392
|
+
series: z.union([
|
|
393
|
+
z.null().transform(() => [] as Array<z.infer<typeof series>>),
|
|
394
|
+
series.array().optional().default([]),
|
|
395
|
+
]),
|
|
396
|
+
});
|
|
397
|
+
|
|
398
|
+
export type FramePayload = z.infer<typeof frameZ>;
|
|
399
|
+
|
|
400
|
+
export const seriesFromPayload = (series: SeriesPayload): Series => {
|
|
401
|
+
const { dataType, data, timeRange, alignment } = series;
|
|
402
|
+
return new Series(data, dataType, timeRange, 0, "static", alignment);
|
|
403
|
+
};
|
|
404
|
+
|
|
405
|
+
export const seriesToPayload = (series: Series): SeriesPayload => {
|
|
406
|
+
return {
|
|
407
|
+
timeRange: series._timeRange,
|
|
408
|
+
dataType: series.dataType,
|
|
409
|
+
data: new Uint8Array(series.data.buffer),
|
|
410
|
+
alignment: series.alignment,
|
|
411
|
+
};
|
|
412
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
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
|
+
export * as framer from "@/framer/external";
|
|
@@ -0,0 +1,62 @@
|
|
|
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, TimeSpan, TimeStamp } from "@synnaxlabs/x";
|
|
11
|
+
import { describe, test, expect } 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(25),
|
|
24
|
+
dataType: DataType.FLOAT64,
|
|
25
|
+
});
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
describe("Iterator", () => {
|
|
29
|
+
test("happy path", async () => {
|
|
30
|
+
const ch = await newChannel();
|
|
31
|
+
const writer = await client.telem.newWriter({
|
|
32
|
+
start: TimeStamp.SECOND,
|
|
33
|
+
channels: ch.key,
|
|
34
|
+
});
|
|
35
|
+
const data = randomSeries(25, ch.dataType);
|
|
36
|
+
try {
|
|
37
|
+
await writer.write(ch.key, data);
|
|
38
|
+
await writer.write(ch.key, data);
|
|
39
|
+
await writer.write(ch.key, data);
|
|
40
|
+
await writer.commit();
|
|
41
|
+
} finally {
|
|
42
|
+
await writer.close();
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const iter = await client.telem.newIterator(
|
|
46
|
+
new TimeRange(TimeSpan.ZERO, TimeSpan.seconds(4)),
|
|
47
|
+
[ch.key],
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
try {
|
|
51
|
+
expect(await iter.seekFirst()).toBeTruthy();
|
|
52
|
+
let c = 0;
|
|
53
|
+
while (await iter.next(TimeSpan.seconds(1))) {
|
|
54
|
+
c++;
|
|
55
|
+
expect(iter.value.get(ch.key)[0]).toHaveLength(25);
|
|
56
|
+
}
|
|
57
|
+
expect(c).toEqual(3);
|
|
58
|
+
} finally {
|
|
59
|
+
await iter.close();
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
});
|