@synnaxlabs/client 0.17.3 → 0.17.5
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/.turbo/turbo-build.log +5 -5
- package/dist/channel/client.d.ts +3 -3
- package/dist/channel/retriever.d.ts +1 -0
- package/dist/client.cjs +26 -10
- package/dist/client.cjs.map +1 -1
- package/dist/client.js +4230 -4103
- package/dist/client.js.map +1 -1
- package/dist/framer/adapter.d.ts +10 -8
- package/dist/framer/adapter.spec.d.ts +1 -0
- package/dist/framer/client.d.ts +2 -2
- package/dist/framer/frame.d.ts +3 -2
- package/dist/framer/writer.d.ts +4 -3
- package/dist/index.d.ts +1 -1
- package/dist/util/telem.d.ts +2 -2
- package/examples/node/liveStream.js +7 -2
- package/examples/node/package-lock.json +51 -38
- package/examples/node/package.json +1 -1
- package/examples/node/streamWrite.js +15 -8
- package/package.json +4 -3
- package/src/channel/client.ts +7 -4
- package/src/channel/retriever.ts +35 -8
- package/src/framer/adapter.spec.ts +118 -0
- package/src/framer/adapter.ts +104 -45
- package/src/framer/client.ts +2 -6
- package/src/framer/frame.spec.ts +102 -31
- package/src/framer/frame.ts +21 -4
- package/src/framer/iterator.ts +4 -4
- package/src/framer/streamer.ts +4 -4
- package/src/framer/writer.spec.ts +1 -1
- package/src/framer/writer.ts +19 -16
- package/src/index.ts +1 -1
- package/src/util/telem.ts +2 -2
package/dist/framer/adapter.d.ts
CHANGED
|
@@ -1,21 +1,23 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type CrudeSeries } from "@synnaxlabs/x";
|
|
2
|
+
import { type Key, type Params, type KeyOrName } from '../channel/payload';
|
|
2
3
|
import { type Retriever } from '../channel/retriever';
|
|
3
|
-
import { type Frame } from './frame';
|
|
4
|
-
export declare class
|
|
4
|
+
import { type CrudeFrame, Frame } from './frame';
|
|
5
|
+
export declare class ReadFrameAdapter {
|
|
5
6
|
private adapter;
|
|
6
7
|
retriever: Retriever;
|
|
7
8
|
keys: Key[];
|
|
8
9
|
private constructor();
|
|
9
|
-
static open(retriever: Retriever, channels: Params): Promise<
|
|
10
|
+
static open(retriever: Retriever, channels: Params): Promise<ReadFrameAdapter>;
|
|
10
11
|
update(channels: Params): Promise<void>;
|
|
11
|
-
adapt(
|
|
12
|
+
adapt(columnsOrData: Frame): Frame;
|
|
12
13
|
}
|
|
13
|
-
export declare class
|
|
14
|
+
export declare class WriteFrameAdapter {
|
|
14
15
|
private adapter;
|
|
15
16
|
retriever: Retriever;
|
|
16
17
|
keys: Key[];
|
|
17
18
|
private constructor();
|
|
18
|
-
static open(retriever: Retriever, channels: Params): Promise<
|
|
19
|
+
static open(retriever: Retriever, channels: Params): Promise<WriteFrameAdapter>;
|
|
19
20
|
update(channels: Params): Promise<void>;
|
|
20
|
-
|
|
21
|
+
private fetchChannel;
|
|
22
|
+
adapt(columnsOrData: Params | Record<KeyOrName, CrudeSeries> | CrudeFrame, series?: CrudeSeries | CrudeSeries[]): Promise<Frame>;
|
|
21
23
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/framer/client.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type StreamClient } from "@synnaxlabs/freighter";
|
|
2
|
-
import { type
|
|
2
|
+
import { type TypedArray, type Series, type TimeRange, type CrudeTimeStamp, TimeStamp } from "@synnaxlabs/x";
|
|
3
3
|
import { type KeyOrName, type Params } from '../channel/payload';
|
|
4
4
|
import { type Retriever } from '../channel/retriever';
|
|
5
5
|
import { Frame } from './frame';
|
|
@@ -37,7 +37,7 @@ export declare class Client {
|
|
|
37
37
|
* data type as the channel.
|
|
38
38
|
* @throws if the channel does not exist.
|
|
39
39
|
*/
|
|
40
|
-
write(to: KeyOrName, start: CrudeTimeStamp, data:
|
|
40
|
+
write(to: KeyOrName, start: CrudeTimeStamp, data: TypedArray): Promise<void>;
|
|
41
41
|
read(tr: TimeRange, channel: KeyOrName): Promise<Series>;
|
|
42
42
|
read(tr: TimeRange, channels: Params): Promise<Frame>;
|
|
43
43
|
private readFrame;
|
package/dist/framer/frame.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Size, Series, TimeRange, DataType, TimeStamp } from "@synnaxlabs/x";
|
|
1
|
+
import { Size, Series, TimeRange, DataType, TimeStamp, type TelemValue } from "@synnaxlabs/x";
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
import { type KeyOrName, type Keys, type Names, type Params } from '../channel/payload';
|
|
4
4
|
type ColumnType = "key" | "name" | null;
|
|
@@ -14,7 +14,7 @@ export type CrudeFrame = Frame | FramePayload | Map<KeyOrName, Series[] | Series
|
|
|
14
14
|
*
|
|
15
15
|
* - A frame is weakly aligned if it meets the time range occupied by all arrays of a
|
|
16
16
|
* particular channel is the same for all channels in the frame. This means that the
|
|
17
|
-
* arrays for a particular channel can have gaps
|
|
17
|
+
* arrays for a particular channel can have gaps between them.
|
|
18
18
|
*
|
|
19
19
|
* - A strongly aligned frame means that all channels share the same rate/index and
|
|
20
20
|
* there are no gaps in time between arrays. Strongly aligned frames are natural
|
|
@@ -98,6 +98,7 @@ export declare class Frame {
|
|
|
98
98
|
*/
|
|
99
99
|
get isWeaklyAligned(): boolean;
|
|
100
100
|
timeRange(col?: KeyOrName): TimeRange;
|
|
101
|
+
latest(): Record<string, TelemValue>;
|
|
101
102
|
get timeRanges(): TimeRange[];
|
|
102
103
|
/**
|
|
103
104
|
* @returns lazy arrays matching the given channel key or name.
|
package/dist/framer/writer.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { StreamClient } from "@synnaxlabs/freighter";
|
|
2
|
-
import {
|
|
2
|
+
import { TimeStamp, type CrudeTimeStamp, type CrudeSeries } from "@synnaxlabs/x";
|
|
3
3
|
import { z } from "zod";
|
|
4
|
-
import { type Key, type KeyOrName, type Params } from '../channel/payload';
|
|
4
|
+
import { type KeysOrNames, type Key, type KeyOrName, type Params } from '../channel/payload';
|
|
5
5
|
import { type Retriever } from '../channel/retriever';
|
|
6
6
|
import { Authority } from '../control/authority';
|
|
7
7
|
import { type Subject as ControlSubject } from '../control/state';
|
|
@@ -428,7 +428,8 @@ export declare class Writer {
|
|
|
428
428
|
private readonly adapter;
|
|
429
429
|
private constructor();
|
|
430
430
|
static _open(retriever: Retriever, client: StreamClient, { channels, authorities, controlSubject: subject, start, mode, }: WriterConfig): Promise<Writer>;
|
|
431
|
-
write(channel: KeyOrName, data:
|
|
431
|
+
write(channel: KeyOrName, data: CrudeSeries): Promise<boolean>;
|
|
432
|
+
write(channel: KeysOrNames, data: CrudeSeries[]): Promise<boolean>;
|
|
432
433
|
write(frame: CrudeFrame): Promise<boolean>;
|
|
433
434
|
setAuthority(value: Record<Key, Authority>): Promise<boolean>;
|
|
434
435
|
setMode(mode: WriterMode): Promise<boolean>;
|
package/dist/index.d.ts
CHANGED
|
@@ -10,7 +10,7 @@ export { ontology } from './ontology';
|
|
|
10
10
|
export { control } from './control';
|
|
11
11
|
export { Authority } from './control/authority';
|
|
12
12
|
export { DataType, Density, Rate, Series, TimeRange, TimeSpan, TimeStamp, } from "@synnaxlabs/x";
|
|
13
|
-
export type {
|
|
13
|
+
export type { TypedArray, CrudeDataType, CrudeDensity, CrudeRate, CrudeSize, CrudeTimeSpan, CrudeTimeStamp, SampleValue, TimeStampStringFormat, TZInfo, } from "@synnaxlabs/x";
|
|
14
14
|
export { workspace } from './workspace';
|
|
15
15
|
export { ranger } from './ranger';
|
|
16
16
|
export { label } from './label';
|
package/dist/util/telem.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { DataType,
|
|
2
|
-
export declare const randomSeries: (length: number, dataType: DataType) =>
|
|
1
|
+
import { type DataType, type TypedArray } from "@synnaxlabs/x";
|
|
2
|
+
export declare const randomSeries: (length: number, dataType: DataType) => TypedArray;
|
|
@@ -18,7 +18,11 @@ const client = new Synnax({
|
|
|
18
18
|
});
|
|
19
19
|
|
|
20
20
|
// We can just specify the names of the channels we'd like to stream from.
|
|
21
|
-
const read_from = [
|
|
21
|
+
const read_from = [
|
|
22
|
+
"stream_write_example_time",
|
|
23
|
+
"stream_write_example_data_1",
|
|
24
|
+
"stream_write_example_data_2"
|
|
25
|
+
]
|
|
22
26
|
|
|
23
27
|
const streamer = await client.telem.newStreamer(read_from);
|
|
24
28
|
|
|
@@ -31,7 +35,8 @@ try {
|
|
|
31
35
|
for await (const frame of streamer)
|
|
32
36
|
console.log({
|
|
33
37
|
time: new TimeStamp(frame.get("stream_write_example_time")[0].at(0)).toString(),
|
|
34
|
-
|
|
38
|
+
data1: frame.get("stream_write_example_data_1")[0].at(0),
|
|
39
|
+
data2: frame.get("stream_write_example_data_2")[0].at(0)
|
|
35
40
|
})
|
|
36
41
|
} finally {
|
|
37
42
|
streamer.close();
|
|
@@ -9,13 +9,13 @@
|
|
|
9
9
|
"version": "1.0.0",
|
|
10
10
|
"license": "ISC",
|
|
11
11
|
"dependencies": {
|
|
12
|
-
"@synnaxlabs/client": "^0.17.
|
|
12
|
+
"@synnaxlabs/client": "^0.17.4"
|
|
13
13
|
}
|
|
14
14
|
},
|
|
15
15
|
"node_modules/@grpc/grpc-js": {
|
|
16
|
-
"version": "1.10.
|
|
17
|
-
"resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.10.
|
|
18
|
-
"integrity": "sha512-
|
|
16
|
+
"version": "1.10.6",
|
|
17
|
+
"resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.10.6.tgz",
|
|
18
|
+
"integrity": "sha512-xP58G7wDQ4TCmN/cMUHh00DS7SRDv/+lC+xFLrTkMIN8h55X5NhZMLYbvy7dSELP15qlI6hPhNCRWVMtZMwqLA==",
|
|
19
19
|
"dependencies": {
|
|
20
20
|
"@grpc/proto-loader": "^0.7.10",
|
|
21
21
|
"@js-sdsl/ordered-map": "^4.4.2"
|
|
@@ -3017,9 +3017,9 @@
|
|
|
3017
3017
|
}
|
|
3018
3018
|
},
|
|
3019
3019
|
"node_modules/@opentelemetry/propagation-utils": {
|
|
3020
|
-
"version": "0.30.
|
|
3021
|
-
"resolved": "https://registry.npmjs.org/@opentelemetry/propagation-utils/-/propagation-utils-0.30.
|
|
3022
|
-
"integrity": "sha512-
|
|
3020
|
+
"version": "0.30.8",
|
|
3021
|
+
"resolved": "https://registry.npmjs.org/@opentelemetry/propagation-utils/-/propagation-utils-0.30.8.tgz",
|
|
3022
|
+
"integrity": "sha512-ZKjsUm//SvL8I9JS+bJpAXGpe0Fb+sO+AiWS0fb7EKKLEm3GoNAO7CDMs8GMZBZ91ElR3tBjdUKf/9MzUdYHBA==",
|
|
3023
3023
|
"engines": {
|
|
3024
3024
|
"node": ">=14"
|
|
3025
3025
|
},
|
|
@@ -3122,12 +3122,12 @@
|
|
|
3122
3122
|
}
|
|
3123
3123
|
},
|
|
3124
3124
|
"node_modules/@opentelemetry/resource-detector-alibaba-cloud": {
|
|
3125
|
-
"version": "0.28.
|
|
3126
|
-
"resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-alibaba-cloud/-/resource-detector-alibaba-cloud-0.28.
|
|
3127
|
-
"integrity": "sha512-
|
|
3125
|
+
"version": "0.28.8",
|
|
3126
|
+
"resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-alibaba-cloud/-/resource-detector-alibaba-cloud-0.28.8.tgz",
|
|
3127
|
+
"integrity": "sha512-njdK7S90OX99H0nlDh45vmGU3Bn46JSNjciU18NefyU6R3Dq5ZUU13DWxSlrCBWAiH/+SFJyHQMFsXRnTOFp6w==",
|
|
3128
3128
|
"dependencies": {
|
|
3129
3129
|
"@opentelemetry/resources": "^1.0.0",
|
|
3130
|
-
"@opentelemetry/semantic-conventions": "^1.
|
|
3130
|
+
"@opentelemetry/semantic-conventions": "^1.22.0"
|
|
3131
3131
|
},
|
|
3132
3132
|
"engines": {
|
|
3133
3133
|
"node": ">=14"
|
|
@@ -3137,13 +3137,13 @@
|
|
|
3137
3137
|
}
|
|
3138
3138
|
},
|
|
3139
3139
|
"node_modules/@opentelemetry/resource-detector-aws": {
|
|
3140
|
-
"version": "1.4.
|
|
3141
|
-
"resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-aws/-/resource-detector-aws-1.4.
|
|
3142
|
-
"integrity": "sha512-
|
|
3140
|
+
"version": "1.4.1",
|
|
3141
|
+
"resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-aws/-/resource-detector-aws-1.4.1.tgz",
|
|
3142
|
+
"integrity": "sha512-QsPJwXDxlt+IWQazST7renk9KDOAK3hsywb0Mw6gEwgEoe12EvdVcT+mCknTc+hu5WzxiTmFMtnn1TWab7To1g==",
|
|
3143
3143
|
"dependencies": {
|
|
3144
3144
|
"@opentelemetry/core": "^1.0.0",
|
|
3145
3145
|
"@opentelemetry/resources": "^1.0.0",
|
|
3146
|
-
"@opentelemetry/semantic-conventions": "^1.
|
|
3146
|
+
"@opentelemetry/semantic-conventions": "^1.22.0"
|
|
3147
3147
|
},
|
|
3148
3148
|
"engines": {
|
|
3149
3149
|
"node": ">=14"
|
|
@@ -3153,12 +3153,12 @@
|
|
|
3153
3153
|
}
|
|
3154
3154
|
},
|
|
3155
3155
|
"node_modules/@opentelemetry/resource-detector-container": {
|
|
3156
|
-
"version": "0.3.
|
|
3157
|
-
"resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-container/-/resource-detector-container-0.3.
|
|
3158
|
-
"integrity": "sha512-
|
|
3156
|
+
"version": "0.3.8",
|
|
3157
|
+
"resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-container/-/resource-detector-container-0.3.8.tgz",
|
|
3158
|
+
"integrity": "sha512-YusLYSo8Rr1yKEhakaJ2XgbfbLses5t8YsSRHhO0b6SLliZOFq9ymxoWgtuwyabNCqMMJJYuvrE3Nq0AL6sbcQ==",
|
|
3159
3159
|
"dependencies": {
|
|
3160
3160
|
"@opentelemetry/resources": "^1.0.0",
|
|
3161
|
-
"@opentelemetry/semantic-conventions": "^1.
|
|
3161
|
+
"@opentelemetry/semantic-conventions": "^1.22.0"
|
|
3162
3162
|
},
|
|
3163
3163
|
"engines": {
|
|
3164
3164
|
"node": ">=14"
|
|
@@ -3168,9 +3168,9 @@
|
|
|
3168
3168
|
}
|
|
3169
3169
|
},
|
|
3170
3170
|
"node_modules/@opentelemetry/resource-detector-gcp": {
|
|
3171
|
-
"version": "0.29.
|
|
3172
|
-
"resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-gcp/-/resource-detector-gcp-0.29.
|
|
3173
|
-
"integrity": "sha512-
|
|
3171
|
+
"version": "0.29.8",
|
|
3172
|
+
"resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-gcp/-/resource-detector-gcp-0.29.8.tgz",
|
|
3173
|
+
"integrity": "sha512-jvFtsnrQI7CLDxehp6BxtudbZ3a0HZ+Dj/vFy251280efc15gQdlvFUqXbKBw6ODt/3o38ilw8e0qePJbm2eAg==",
|
|
3174
3174
|
"dependencies": {
|
|
3175
3175
|
"@opentelemetry/core": "^1.0.0",
|
|
3176
3176
|
"@opentelemetry/resources": "^1.0.0",
|
|
@@ -3653,23 +3653,23 @@
|
|
|
3653
3653
|
}
|
|
3654
3654
|
},
|
|
3655
3655
|
"node_modules/@synnaxlabs/client": {
|
|
3656
|
-
"version": "0.17.
|
|
3657
|
-
"resolved": "https://registry.npmjs.org/@synnaxlabs/client/-/client-0.17.
|
|
3658
|
-
"integrity": "sha512-
|
|
3656
|
+
"version": "0.17.4",
|
|
3657
|
+
"resolved": "https://registry.npmjs.org/@synnaxlabs/client/-/client-0.17.4.tgz",
|
|
3658
|
+
"integrity": "sha512-fQ2VvuHMI43BO3x2E7Nl51ZhBOyMbSWeGrid/+TkGNVYyyhVh8oDQE0O5n5mRr6h+kLeKwAv3fH84zDQXTow6w==",
|
|
3659
3659
|
"dependencies": {
|
|
3660
|
-
"@synnaxlabs/freighter": "0.9.
|
|
3661
|
-
"@synnaxlabs/x": "0.14.
|
|
3660
|
+
"@synnaxlabs/freighter": "0.9.2",
|
|
3661
|
+
"@synnaxlabs/x": "0.14.2",
|
|
3662
3662
|
"async-mutex": "^0.4.0",
|
|
3663
3663
|
"zod": "3.22.4"
|
|
3664
3664
|
}
|
|
3665
3665
|
},
|
|
3666
3666
|
"node_modules/@synnaxlabs/freighter": {
|
|
3667
|
-
"version": "0.9.
|
|
3668
|
-
"resolved": "https://registry.npmjs.org/@synnaxlabs/freighter/-/freighter-0.9.
|
|
3669
|
-
"integrity": "sha512-
|
|
3667
|
+
"version": "0.9.2",
|
|
3668
|
+
"resolved": "https://registry.npmjs.org/@synnaxlabs/freighter/-/freighter-0.9.2.tgz",
|
|
3669
|
+
"integrity": "sha512-GJ63tGtSKlqiDLI+82keEIKwKmCsMI7y5ibI1DGKS/cXgeYJFUmG4BjJFM4HfVQxt56o94UP+qP4acZcLDZwGg==",
|
|
3670
3670
|
"dependencies": {
|
|
3671
3671
|
"@synnaxlabs/alamos": "0.3.0",
|
|
3672
|
-
"@synnaxlabs/x": "0.14.
|
|
3672
|
+
"@synnaxlabs/x": "0.14.2",
|
|
3673
3673
|
"js-convert-case": "^4.2.0",
|
|
3674
3674
|
"node-fetch": "2.6.11",
|
|
3675
3675
|
"ws": "^8.15.1",
|
|
@@ -3680,9 +3680,9 @@
|
|
|
3680
3680
|
}
|
|
3681
3681
|
},
|
|
3682
3682
|
"node_modules/@synnaxlabs/x": {
|
|
3683
|
-
"version": "0.14.
|
|
3684
|
-
"resolved": "https://registry.npmjs.org/@synnaxlabs/x/-/x-0.14.
|
|
3685
|
-
"integrity": "sha512
|
|
3683
|
+
"version": "0.14.2",
|
|
3684
|
+
"resolved": "https://registry.npmjs.org/@synnaxlabs/x/-/x-0.14.2.tgz",
|
|
3685
|
+
"integrity": "sha512-Js5czEi7oMhaQkDe3xGuGLbho7xBMoNIWJmrXI20lu+VbB/FyO1wVtfcdIIIZOLc3tUAdNw9ceuZXMOieRoH1g==",
|
|
3686
3686
|
"dependencies": {
|
|
3687
3687
|
"async-mutex": "^0.4.0",
|
|
3688
3688
|
"js-convert-case": "^4.2.0",
|
|
@@ -4414,14 +4414,15 @@
|
|
|
4414
4414
|
}
|
|
4415
4415
|
},
|
|
4416
4416
|
"node_modules/gaxios": {
|
|
4417
|
-
"version": "6.
|
|
4418
|
-
"resolved": "https://registry.npmjs.org/gaxios/-/gaxios-6.
|
|
4419
|
-
"integrity": "sha512-
|
|
4417
|
+
"version": "6.4.0",
|
|
4418
|
+
"resolved": "https://registry.npmjs.org/gaxios/-/gaxios-6.4.0.tgz",
|
|
4419
|
+
"integrity": "sha512-apAloYrY4dlBGlhauDAYSZveafb5U6+L9titing1wox6BvWM0TSXBp603zTrLpyLMGkrcFgohnUN150dFN/zOA==",
|
|
4420
4420
|
"dependencies": {
|
|
4421
4421
|
"extend": "^3.0.2",
|
|
4422
4422
|
"https-proxy-agent": "^7.0.1",
|
|
4423
4423
|
"is-stream": "^2.0.0",
|
|
4424
|
-
"node-fetch": "^2.6.9"
|
|
4424
|
+
"node-fetch": "^2.6.9",
|
|
4425
|
+
"uuid": "^9.0.1"
|
|
4425
4426
|
},
|
|
4426
4427
|
"engines": {
|
|
4427
4428
|
"node": ">=14"
|
|
@@ -4857,6 +4858,18 @@
|
|
|
4857
4858
|
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
|
|
4858
4859
|
"integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="
|
|
4859
4860
|
},
|
|
4861
|
+
"node_modules/uuid": {
|
|
4862
|
+
"version": "9.0.1",
|
|
4863
|
+
"resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz",
|
|
4864
|
+
"integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==",
|
|
4865
|
+
"funding": [
|
|
4866
|
+
"https://github.com/sponsors/broofa",
|
|
4867
|
+
"https://github.com/sponsors/ctavan"
|
|
4868
|
+
],
|
|
4869
|
+
"bin": {
|
|
4870
|
+
"uuid": "dist/bin/uuid"
|
|
4871
|
+
}
|
|
4872
|
+
},
|
|
4860
4873
|
"node_modules/webidl-conversions": {
|
|
4861
4874
|
"version": "3.0.1",
|
|
4862
4875
|
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
|
|
@@ -23,12 +23,18 @@ const timeChannel = await client.channels.create({
|
|
|
23
23
|
}, { retrieveIfNameExists: true });
|
|
24
24
|
|
|
25
25
|
// Create a data channel that will be used to store our fake sensor data.
|
|
26
|
-
const
|
|
27
|
-
name: "
|
|
26
|
+
const dataChannel1 = await client.channels.create({
|
|
27
|
+
name: "stream_write_example_data_1",
|
|
28
28
|
dataType: "float32",
|
|
29
29
|
index: timeChannel.key,
|
|
30
30
|
}, { retrieveIfNameExists: true });
|
|
31
31
|
|
|
32
|
+
const dataChannel2 = await client.channels.create({
|
|
33
|
+
name: "stream_write_example_data_2",
|
|
34
|
+
dataType: "int32",
|
|
35
|
+
index: timeChannel.key,
|
|
36
|
+
}, { retrieveIfNameExists: true });
|
|
37
|
+
|
|
32
38
|
|
|
33
39
|
// We'll start our write at the current time. This timestamps should be the same as or
|
|
34
40
|
// just before the first timestamp we write.
|
|
@@ -44,7 +50,7 @@ const commitInterval = 500;
|
|
|
44
50
|
|
|
45
51
|
const writer = await client.telem.newWriter({
|
|
46
52
|
start,
|
|
47
|
-
channels: [timeChannel.key,
|
|
53
|
+
channels: [timeChannel.key, dataChannel1.key, dataChannel2.key]
|
|
48
54
|
});
|
|
49
55
|
|
|
50
56
|
try {
|
|
@@ -53,12 +59,13 @@ try {
|
|
|
53
59
|
await new Promise(resolve => setTimeout(resolve, roughRate.period.milliseconds));
|
|
54
60
|
i++;
|
|
55
61
|
const timestamp = TimeStamp.now();
|
|
56
|
-
const
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
[
|
|
62
|
+
const data2= i % 2;
|
|
63
|
+
const data1 = Math.sin(i / 10);
|
|
64
|
+
await writer.write({
|
|
65
|
+
[timeChannel.key]: timestamp,
|
|
66
|
+
[dataChannel1.key]: data1,
|
|
67
|
+
[dataChannel2.key]: data2,
|
|
60
68
|
});
|
|
61
|
-
await writer.write(fr);
|
|
62
69
|
|
|
63
70
|
if (i % 60 == 0)
|
|
64
71
|
console.log(`Writing sample ${i} at ${timestamp.toISOString()}`)
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@synnaxlabs/client",
|
|
3
3
|
"private": false,
|
|
4
|
-
"version": "0.17.
|
|
4
|
+
"version": "0.17.5",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "The Client Library for Synnax",
|
|
7
7
|
"repository": "https://github.com/synnaxlabs/synnax/tree/main/client/ts",
|
|
@@ -18,12 +18,13 @@
|
|
|
18
18
|
"dependencies": {
|
|
19
19
|
"async-mutex": "^0.4.0",
|
|
20
20
|
"zod": "3.22.4",
|
|
21
|
-
"@synnaxlabs/freighter": "0.9.
|
|
22
|
-
"@synnaxlabs/x": "0.14.
|
|
21
|
+
"@synnaxlabs/freighter": "0.9.2",
|
|
22
|
+
"@synnaxlabs/x": "0.14.2"
|
|
23
23
|
},
|
|
24
24
|
"devDependencies": {
|
|
25
25
|
"@types/node": "^20.10.5",
|
|
26
26
|
"@vitest/coverage-v8": "^1.2.2",
|
|
27
|
+
"eslint": "^8.55.0",
|
|
27
28
|
"typescript": "^5.3.3",
|
|
28
29
|
"vite": "^5.1.2",
|
|
29
30
|
"vitest": "^1.2.2",
|
package/src/channel/client.ts
CHANGED
|
@@ -11,7 +11,7 @@ import { type UnaryClient } from "@synnaxlabs/freighter";
|
|
|
11
11
|
import {
|
|
12
12
|
DataType,
|
|
13
13
|
Rate,
|
|
14
|
-
type
|
|
14
|
+
type TypedArray,
|
|
15
15
|
type CrudeDensity,
|
|
16
16
|
type Series,
|
|
17
17
|
type TimeRange,
|
|
@@ -158,7 +158,7 @@ export class Channel {
|
|
|
158
158
|
* @param start - The starting timestamp of the first sample in data.
|
|
159
159
|
* @param data - THe telemetry to write to the channel.
|
|
160
160
|
*/
|
|
161
|
-
async write(start: CrudeTimeStamp, data:
|
|
161
|
+
async write(start: CrudeTimeStamp, data: TypedArray): Promise<void> {
|
|
162
162
|
return await this.framer.write(this.key, start, data);
|
|
163
163
|
}
|
|
164
164
|
}
|
|
@@ -170,7 +170,7 @@ export class Channel {
|
|
|
170
170
|
*/
|
|
171
171
|
export class Client implements AsyncTermSearcher<string, Key, Channel> {
|
|
172
172
|
private readonly frameClient: framer.Client;
|
|
173
|
-
|
|
173
|
+
readonly retriever: Retriever;
|
|
174
174
|
private readonly creator: Creator;
|
|
175
175
|
private readonly client: UnaryClient;
|
|
176
176
|
|
|
@@ -247,7 +247,10 @@ export class Client implements AsyncTermSearcher<string, Key, Channel> {
|
|
|
247
247
|
*/
|
|
248
248
|
async create(channels: NewPayload[], options?: CreateOptions): Promise<Channel[]>;
|
|
249
249
|
|
|
250
|
-
async create(
|
|
250
|
+
async create(
|
|
251
|
+
channels: NewPayload | NewPayload[],
|
|
252
|
+
options: CreateOptions = {},
|
|
253
|
+
): Promise<Channel | Channel[]> {
|
|
251
254
|
const { retrieveIfNameExists = false } = options;
|
|
252
255
|
const single = !Array.isArray(channels);
|
|
253
256
|
let toCreate = toArray(channels);
|
package/src/channel/retriever.ts
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
// included in the file licenses/APL.txt.
|
|
9
9
|
import type { UnaryClient } from "@synnaxlabs/freighter";
|
|
10
10
|
import { debounce, toArray } from "@synnaxlabs/x";
|
|
11
|
+
import { Mutex } from "async-mutex";
|
|
11
12
|
import { z } from "zod";
|
|
12
13
|
|
|
13
14
|
import {
|
|
@@ -21,7 +22,7 @@ import {
|
|
|
21
22
|
type Payload,
|
|
22
23
|
payload,
|
|
23
24
|
} from "@/channel/payload";
|
|
24
|
-
import {
|
|
25
|
+
import { QueryError, ValidationError } from "@/errors";
|
|
25
26
|
|
|
26
27
|
const reqZ = z.object({
|
|
27
28
|
leaseholder: z.number().optional(),
|
|
@@ -59,15 +60,20 @@ export class ClusterRetriever implements Retriever {
|
|
|
59
60
|
|
|
60
61
|
async retrieve(channels: Params, rangeKey?: string): Promise<Payload[]> {
|
|
61
62
|
const { variant, normalized } = analyzeParams(channels);
|
|
62
|
-
return await this.execute({ [variant]: normalized, rangeKey});
|
|
63
|
+
return await this.execute({ [variant]: normalized, rangeKey });
|
|
63
64
|
}
|
|
64
65
|
|
|
65
66
|
async page(offset: number, limit: number, rangeKey?: string): Promise<Payload[]> {
|
|
66
|
-
return await this.execute({ offset, limit, rangeKey});
|
|
67
|
+
return await this.execute({ offset, limit, rangeKey });
|
|
67
68
|
}
|
|
68
69
|
|
|
69
70
|
private async execute(request: Request): Promise<Payload[]> {
|
|
70
|
-
const [res, err] = await this.client.send(
|
|
71
|
+
const [res, err] = await this.client.send(
|
|
72
|
+
ClusterRetriever.ENDPOINT,
|
|
73
|
+
request,
|
|
74
|
+
reqZ,
|
|
75
|
+
resZ,
|
|
76
|
+
);
|
|
71
77
|
if (err != null) throw err;
|
|
72
78
|
return res.channels;
|
|
73
79
|
}
|
|
@@ -148,10 +154,17 @@ export type ParamAnalysisResult =
|
|
|
148
154
|
};
|
|
149
155
|
|
|
150
156
|
export const analyzeParams = (channels: Params): ParamAnalysisResult => {
|
|
151
|
-
|
|
157
|
+
let normal = (toArray(channels) as KeysOrNames).filter((c) => c !== 0);
|
|
158
|
+
let variant: "names" | "keys" = "keys";
|
|
159
|
+
if (typeof normal[0] === "string") {
|
|
160
|
+
if (isNaN(parseInt(normal[0]))) variant = "names";
|
|
161
|
+
else {
|
|
162
|
+
normal = normal.map((v) => parseInt(v as string));
|
|
163
|
+
}
|
|
164
|
+
}
|
|
152
165
|
return {
|
|
153
166
|
single: !Array.isArray(channels),
|
|
154
|
-
variant
|
|
167
|
+
variant,
|
|
155
168
|
normalized: normal,
|
|
156
169
|
actual: channels,
|
|
157
170
|
} as const as ParamAnalysisResult;
|
|
@@ -187,8 +200,7 @@ export class DebouncedBatchRetriever implements Retriever {
|
|
|
187
200
|
async retrieve(channels: Params): Promise<Payload[]> {
|
|
188
201
|
const { normalized, variant } = analyzeParams(channels);
|
|
189
202
|
// Bypass on name fetches for now.
|
|
190
|
-
if (variant === "names")
|
|
191
|
-
return await this.wrapped.retrieve(normalized);
|
|
203
|
+
if (variant === "names") return await this.wrapped.retrieve(normalized);
|
|
192
204
|
// eslint-disable-next-line @typescript-eslint/promise-function-async
|
|
193
205
|
const a = new Promise<Payload[]>((resolve, reject) => {
|
|
194
206
|
void this.mu.runExclusive(() => {
|
|
@@ -216,3 +228,18 @@ export class DebouncedBatchRetriever implements Retriever {
|
|
|
216
228
|
});
|
|
217
229
|
}
|
|
218
230
|
}
|
|
231
|
+
|
|
232
|
+
export const retrieveRequired = async (
|
|
233
|
+
r: Retriever,
|
|
234
|
+
params: Params,
|
|
235
|
+
): Promise<Payload[]> => {
|
|
236
|
+
const { normalized } = analyzeParams(params);
|
|
237
|
+
const results = await r.retrieve(normalized);
|
|
238
|
+
const notFound: KeyOrName[] = [];
|
|
239
|
+
normalized.forEach((v) => {
|
|
240
|
+
if (results.find((c) => c.name === v || c.key === v) == null) notFound.push(v);
|
|
241
|
+
});
|
|
242
|
+
if (notFound.length > 0)
|
|
243
|
+
throw new QueryError(`Could not find channels: ${JSON.stringify(notFound)}`);
|
|
244
|
+
return results;
|
|
245
|
+
};
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { DataType, Series, TimeStamp } from "@synnaxlabs/x";
|
|
2
|
+
import { beforeAll, describe, expect, it } from "vitest";
|
|
3
|
+
|
|
4
|
+
import { type channel } from "@/channel";
|
|
5
|
+
import { WriteFrameAdapter } from "@/framer/adapter";
|
|
6
|
+
import { Frame } from "@/index";
|
|
7
|
+
import { newClient } from "@/setupspecs";
|
|
8
|
+
|
|
9
|
+
const client = newClient();
|
|
10
|
+
|
|
11
|
+
describe("WriteFrameAdapter", () => {
|
|
12
|
+
let timeCh: channel.Channel;
|
|
13
|
+
let dataCh: channel.Channel;
|
|
14
|
+
let adapter: WriteFrameAdapter;
|
|
15
|
+
|
|
16
|
+
beforeAll(async () => {
|
|
17
|
+
timeCh = await client.channels.create({
|
|
18
|
+
name: `time-${Math.random()}-${TimeStamp.now().toString()}`,
|
|
19
|
+
dataType: DataType.TIMESTAMP,
|
|
20
|
+
isIndex: true,
|
|
21
|
+
});
|
|
22
|
+
dataCh = await client.channels.create({
|
|
23
|
+
name: `data-${Math.random()}-${TimeStamp.now().toString()}`,
|
|
24
|
+
dataType: DataType.FLOAT32,
|
|
25
|
+
index: timeCh.key,
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
adapter = await WriteFrameAdapter.open(client.channels.retriever, [
|
|
29
|
+
timeCh.key,
|
|
30
|
+
dataCh.key,
|
|
31
|
+
]);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it("should correctly adapt a record of keys to single values", async () => {
|
|
35
|
+
const ts = TimeStamp.now().valueOf();
|
|
36
|
+
const res = await adapter.adapt({
|
|
37
|
+
[timeCh.key]: ts,
|
|
38
|
+
[dataCh.key]: 1,
|
|
39
|
+
});
|
|
40
|
+
expect(res.columns).toHaveLength(2);
|
|
41
|
+
expect(res.series).toHaveLength(2);
|
|
42
|
+
expect(res.get(timeCh.key)).toHaveLength(1);
|
|
43
|
+
expect(res.get(dataCh.key)).toHaveLength(1);
|
|
44
|
+
expect(res.get(timeCh.key)[0].at(0)).toEqual(Number(ts));
|
|
45
|
+
expect(res.get(dataCh.key)[0].at(0)).toEqual(1);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it("should correctly adapt a record of names to single values", async () => {
|
|
49
|
+
const ts = TimeStamp.now().valueOf();
|
|
50
|
+
const res2 = await adapter.adapt({
|
|
51
|
+
[timeCh.name]: ts,
|
|
52
|
+
[dataCh.name]: 1,
|
|
53
|
+
});
|
|
54
|
+
expect(res2.columns).toHaveLength(2);
|
|
55
|
+
expect(res2.series).toHaveLength(2);
|
|
56
|
+
expect(res2.get(timeCh.key)).toHaveLength(1);
|
|
57
|
+
expect(res2.get(dataCh.key)).toHaveLength(1);
|
|
58
|
+
expect(res2.get(timeCh.key)[0].at(0)).toEqual(Number(ts));
|
|
59
|
+
expect(res2.get(dataCh.key)[0].at(0)).toEqual(1);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it("should correctly adapt a single name to a single series", async () => {
|
|
63
|
+
const res3 = await adapter.adapt(dataCh.name, new Series(1));
|
|
64
|
+
expect(res3.columns).toHaveLength(1);
|
|
65
|
+
expect(res3.series).toHaveLength(1);
|
|
66
|
+
expect(res3.get(dataCh.key)).toHaveLength(1);
|
|
67
|
+
expect(res3.get(dataCh.key)[0].at(0)).toEqual(1);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it("should correctly adapt multiple names to multiple series", async () => {
|
|
71
|
+
const ts = TimeStamp.now().valueOf();
|
|
72
|
+
const res4 = await adapter.adapt(
|
|
73
|
+
[timeCh.name, dataCh.name],
|
|
74
|
+
[new Series(ts), new Series(1)],
|
|
75
|
+
);
|
|
76
|
+
expect(res4.get(timeCh.key)).toHaveLength(1);
|
|
77
|
+
expect(res4.get(dataCh.key)).toHaveLength(1);
|
|
78
|
+
expect(res4.get(timeCh.key)[0].at(0)).toEqual(Number(ts));
|
|
79
|
+
expect(res4.get(dataCh.key)[0].at(0)).toEqual(1);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it("should correctly adapt a frame keyed by name", async () => {
|
|
83
|
+
const ts = TimeStamp.now().valueOf();
|
|
84
|
+
const fr = new Frame({
|
|
85
|
+
[timeCh.name]: new Series(ts),
|
|
86
|
+
[dataCh.name]: new Series(1),
|
|
87
|
+
});
|
|
88
|
+
const res = await adapter.adapt(fr);
|
|
89
|
+
expect(res.columns).toHaveLength(2);
|
|
90
|
+
expect(res.series).toHaveLength(2);
|
|
91
|
+
expect(res.get(timeCh.key)[0].at(0)).toEqual(Number(ts));
|
|
92
|
+
expect(res.get(dataCh.key)[0].at(0)).toEqual(1);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it("should not modify a frame keyed by key", async () => {
|
|
96
|
+
const ts = TimeStamp.now().valueOf();
|
|
97
|
+
const fr = new Frame({
|
|
98
|
+
[timeCh.key]: new Series(ts),
|
|
99
|
+
[dataCh.key]: new Series(1),
|
|
100
|
+
});
|
|
101
|
+
const res = await adapter.adapt(fr);
|
|
102
|
+
expect(res.columns).toHaveLength(2);
|
|
103
|
+
expect(res.series).toHaveLength(2);
|
|
104
|
+
expect(res.get(timeCh.key)[0].at(0)).toEqual(Number(ts));
|
|
105
|
+
expect(res.get(dataCh.key)[0].at(0)).toEqual(1);
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
it("should correctly adapt a map of series", async () => {
|
|
109
|
+
const ts = TimeStamp.now().valueOf();
|
|
110
|
+
const m = new Map();
|
|
111
|
+
m.set(timeCh.key, new Series(ts));
|
|
112
|
+
const res = await adapter.adapt(m);
|
|
113
|
+
expect(res.columns).toHaveLength(1);
|
|
114
|
+
expect(res.series).toHaveLength(1);
|
|
115
|
+
expect(res.get(timeCh.key)).toHaveLength(1);
|
|
116
|
+
expect(res.get(timeCh.key)[0].at(0)).toEqual(Number(ts));
|
|
117
|
+
});
|
|
118
|
+
});
|