@synnaxlabs/client 0.54.2 → 0.56.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +10 -13
- package/dist/client.cjs +60 -42
- package/dist/client.js +8037 -6265
- package/dist/src/access/policy/client.d.ts +70 -80
- package/dist/src/access/policy/client.d.ts.map +1 -1
- package/dist/src/access/policy/types.gen.d.ts +18 -20
- package/dist/src/access/policy/types.gen.d.ts.map +1 -1
- package/dist/src/access/role/client.d.ts.map +1 -1
- package/dist/src/access/role/types.gen.d.ts +2 -2
- package/dist/src/actions/actions.d.ts +68 -0
- package/dist/src/actions/actions.d.ts.map +1 -0
- package/dist/src/actions/actions.spec.d.ts +2 -0
- package/dist/src/actions/actions.spec.d.ts.map +1 -0
- package/dist/src/actions/external.d.ts +2 -0
- package/dist/src/actions/external.d.ts.map +1 -0
- package/dist/src/actions/index.d.ts +2 -0
- package/dist/src/actions/index.d.ts.map +1 -0
- package/dist/src/arc/arc.spec.d.ts +2 -0
- package/dist/src/arc/arc.spec.d.ts.map +1 -0
- package/dist/src/arc/client.d.ts.map +1 -1
- package/dist/src/arc/compiler/types.gen.d.ts +1 -1
- package/dist/src/arc/compiler/types.gen.d.ts.map +1 -1
- package/dist/src/arc/graph/types.gen.d.ts +40 -40
- package/dist/src/arc/graph/types.gen.d.ts.map +1 -1
- package/dist/src/arc/ir/types.gen.d.ts +202 -233
- package/dist/src/arc/ir/types.gen.d.ts.map +1 -1
- package/dist/src/arc/module/types.gen.d.ts +63 -82
- package/dist/src/arc/module/types.gen.d.ts.map +1 -1
- package/dist/src/arc/program/types.gen.d.ts +63 -82
- package/dist/src/arc/program/types.gen.d.ts.map +1 -1
- package/dist/src/arc/types/types.gen.d.ts +11 -11
- package/dist/src/arc/types/types.gen.d.ts.map +1 -1
- package/dist/src/arc/types.gen.d.ts +139 -158
- package/dist/src/arc/types.gen.d.ts.map +1 -1
- package/dist/src/auth/auth.d.ts +3 -3
- package/dist/src/auth/auth.d.ts.map +1 -1
- package/dist/src/channel/client.d.ts +2 -2
- package/dist/src/channel/client.d.ts.map +1 -1
- package/dist/src/channel/retriever.d.ts +5 -8
- package/dist/src/channel/retriever.d.ts.map +1 -1
- package/dist/src/channel/types.gen.d.ts +3 -3
- package/dist/src/channel/types.gen.d.ts.map +1 -1
- package/dist/src/channel/writer.d.ts.map +1 -1
- package/dist/src/client.d.ts +5 -0
- package/dist/src/client.d.ts.map +1 -1
- package/dist/src/connection/checker.d.ts +17 -2
- package/dist/src/connection/checker.d.ts.map +1 -1
- package/dist/src/control/state.d.ts.map +1 -1
- package/dist/src/device/client.d.ts.map +1 -1
- package/dist/src/device/types.gen.d.ts +6 -8
- package/dist/src/device/types.gen.d.ts.map +1 -1
- package/dist/src/errors.d.ts +2 -0
- package/dist/src/errors.d.ts.map +1 -1
- package/dist/src/framer/adapter.d.ts.map +1 -1
- package/dist/src/framer/client.d.ts +2 -2
- package/dist/src/framer/client.d.ts.map +1 -1
- package/dist/src/framer/codec.d.ts +9 -1
- package/dist/src/framer/codec.d.ts.map +1 -1
- package/dist/src/framer/deleter.d.ts.map +1 -1
- package/dist/src/framer/frame.d.ts +1 -1
- package/dist/src/framer/iterator.d.ts +84 -3
- package/dist/src/framer/iterator.d.ts.map +1 -1
- package/dist/src/framer/streamProxy.d.ts.map +1 -1
- package/dist/src/framer/streamer.d.ts +1 -3
- package/dist/src/framer/streamer.d.ts.map +1 -1
- package/dist/src/framer/types.gen.d.ts +18 -0
- package/dist/src/framer/types.gen.d.ts.map +1 -1
- package/dist/src/framer/writer.d.ts +8 -8
- package/dist/src/framer/writer.d.ts.map +1 -1
- package/dist/src/group/client.d.ts +1 -2
- package/dist/src/group/client.d.ts.map +1 -1
- package/dist/src/group/types.gen.d.ts +2 -2
- package/dist/src/index.d.ts +2 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/label/client.d.ts +5 -8
- package/dist/src/label/client.d.ts.map +1 -1
- package/dist/src/lineplot/client.d.ts.map +1 -1
- package/dist/src/lineplot/types.gen.d.ts +2 -2
- package/dist/src/log/client.d.ts.map +1 -1
- package/dist/src/log/types.gen.d.ts +2 -2
- package/dist/src/ontology/client.d.ts +1 -3
- package/dist/src/ontology/client.d.ts.map +1 -1
- package/dist/src/ontology/payload.d.ts +12 -16
- package/dist/src/ontology/payload.d.ts.map +1 -1
- package/dist/src/ontology/types.gen.d.ts +1 -2
- package/dist/src/ontology/types.gen.d.ts.map +1 -1
- package/dist/src/ontology/writer.d.ts +5 -10
- package/dist/src/ontology/writer.d.ts.map +1 -1
- package/dist/src/rack/client.d.ts.map +1 -1
- package/dist/src/rack/types.gen.d.ts +3 -3
- package/dist/src/ranger/alias/client.d.ts.map +1 -1
- package/dist/src/ranger/client.d.ts.map +1 -1
- package/dist/src/ranger/kv/client.d.ts.map +1 -1
- package/dist/src/ranger/types.gen.d.ts +6 -6
- package/dist/src/ranger/types.gen.d.ts.map +1 -1
- package/dist/src/ranger/writer.d.ts +2 -3
- package/dist/src/ranger/writer.d.ts.map +1 -1
- package/dist/src/schematic/actions.d.ts +147 -0
- package/dist/src/schematic/actions.d.ts.map +1 -0
- package/dist/src/schematic/actions.gen.d.ts +484 -0
- package/dist/src/schematic/actions.gen.d.ts.map +1 -0
- package/dist/src/schematic/actions.spec.d.ts +2 -0
- package/dist/src/schematic/actions.spec.d.ts.map +1 -0
- package/dist/src/schematic/client.d.ts +53 -2
- package/dist/src/schematic/client.d.ts.map +1 -1
- package/dist/src/schematic/external.d.ts +2 -0
- package/dist/src/schematic/external.d.ts.map +1 -1
- package/dist/src/schematic/symbol/client.d.ts.map +1 -1
- package/dist/src/schematic/symbol/types.gen.d.ts +48 -58
- package/dist/src/schematic/symbol/types.gen.d.ts.map +1 -1
- package/dist/src/schematic/types.gen.d.ts +131 -5
- package/dist/src/schematic/types.gen.d.ts.map +1 -1
- package/dist/src/status/client.d.ts.map +1 -1
- package/dist/src/status/payload.d.ts +3 -3
- package/dist/src/table/actions.d.ts +156 -0
- package/dist/src/table/actions.d.ts.map +1 -0
- package/dist/src/table/actions.gen.d.ts +587 -0
- package/dist/src/table/actions.gen.d.ts.map +1 -0
- package/dist/src/table/client.d.ts +28 -2
- package/dist/src/table/client.d.ts.map +1 -1
- package/dist/src/table/external.d.ts +2 -0
- package/dist/src/table/external.d.ts.map +1 -1
- package/dist/src/table/types.gen.d.ts +71 -4
- package/dist/src/table/types.gen.d.ts.map +1 -1
- package/dist/src/task/client.d.ts.map +1 -1
- package/dist/src/task/types.gen.d.ts +7 -7
- package/dist/src/task/types.gen.d.ts.map +1 -1
- package/dist/src/user/client.d.ts +2 -2
- package/dist/src/user/client.d.ts.map +1 -1
- package/dist/src/user/types.gen.d.ts +2 -2
- package/dist/src/view/client.d.ts.map +1 -1
- package/dist/src/view/types.gen.d.ts +2 -2
- package/dist/src/workspace/client.d.ts.map +1 -1
- package/dist/src/workspace/types.gen.d.ts +3 -3
- package/dist/src/workspace/types.gen.d.ts.map +1 -1
- package/package.json +12 -11
- package/src/access/policy/client.ts +4 -7
- package/src/access/role/client.ts +6 -26
- package/src/actions/actions.spec.ts +229 -0
- package/src/actions/actions.ts +104 -0
- package/src/actions/external.ts +10 -0
- package/src/actions/index.ts +10 -0
- package/src/arc/arc.spec.ts +44 -0
- package/src/arc/client.ts +3 -7
- package/src/arc/compiler/types.gen.ts +2 -1
- package/src/arc/ir/types.gen.ts +102 -48
- package/src/arc/lsp.spec.ts +3 -7
- package/src/arc/types/types.gen.ts +3 -3
- package/src/auth/auth.spec.ts +12 -13
- package/src/auth/auth.ts +48 -34
- package/src/channel/batchRetriever.spec.ts +13 -4
- package/src/channel/channel.spec.ts +13 -0
- package/src/channel/client.ts +8 -6
- package/src/channel/retriever.ts +7 -16
- package/src/channel/types.gen.ts +1 -2
- package/src/channel/writer.ts +4 -20
- package/src/client.ts +3 -0
- package/src/connection/checker.ts +48 -10
- package/src/connection/connection.spec.ts +64 -2
- package/src/control/state.ts +5 -4
- package/src/device/client.ts +5 -8
- package/src/device/device.spec.ts +7 -5
- package/src/device/types.gen.ts +4 -4
- package/src/errors.ts +8 -9
- package/src/framer/adapter.ts +2 -4
- package/src/framer/client.ts +12 -0
- package/src/framer/codec.spec.ts +53 -3
- package/src/framer/codec.ts +58 -25
- package/src/framer/deleter.ts +2 -8
- package/src/framer/iterator.ts +42 -39
- package/src/framer/streamProxy.ts +11 -12
- package/src/framer/streamer.spec.ts +1 -1
- package/src/framer/streamer.ts +2 -7
- package/src/framer/types.gen.ts +20 -0
- package/src/framer/writer.spec.ts +221 -1
- package/src/framer/writer.ts +53 -28
- package/src/group/client.ts +4 -7
- package/src/index.ts +3 -2
- package/src/label/client.ts +6 -16
- package/src/label/label.spec.ts +12 -0
- package/src/lineplot/client.ts +6 -21
- package/src/log/client.ts +6 -21
- package/src/ontology/client.ts +2 -3
- package/src/ontology/ontology.spec.ts +10 -0
- package/src/ontology/types.gen.ts +0 -1
- package/src/ontology/writer.ts +4 -7
- package/src/rack/client.ts +4 -7
- package/src/rack/rack.spec.ts +12 -1
- package/src/ranger/alias/client.ts +6 -11
- package/src/ranger/client.ts +2 -3
- package/src/ranger/kv/client.ts +4 -7
- package/src/ranger/ranger.spec.ts +12 -0
- package/src/ranger/writer.ts +4 -17
- package/src/schematic/access.spec.ts +6 -6
- package/src/schematic/actions.gen.ts +200 -0
- package/src/schematic/actions.spec.ts +699 -0
- package/src/schematic/actions.ts +168 -0
- package/src/schematic/client.ts +34 -30
- package/src/schematic/external.ts +2 -0
- package/src/schematic/schematic.spec.ts +233 -69
- package/src/schematic/symbol/client.spec.ts +33 -9
- package/src/schematic/symbol/client.ts +6 -11
- package/src/schematic/symbol/types.gen.ts +1 -10
- package/src/schematic/types.gen.ts +55 -6
- package/src/status/client.ts +4 -10
- package/src/status/status.spec.ts +7 -6
- package/src/table/access.spec.ts +0 -6
- package/src/table/actions.gen.ts +243 -0
- package/src/table/actions.ts +255 -0
- package/src/table/client.ts +21 -25
- package/src/table/external.ts +2 -0
- package/src/table/table.spec.ts +588 -43
- package/src/table/types.gen.ts +58 -5
- package/src/task/client.ts +14 -20
- package/src/task/task.spec.ts +15 -1
- package/src/task/types.gen.ts +8 -6
- package/src/user/client.ts +6 -11
- package/src/view/client.ts +4 -7
- package/src/view/view.spec.ts +9 -5
- package/src/workspace/client.ts +6 -16
- package/src/workspace/types.gen.ts +2 -1
- package/src/workspace/workspace.spec.ts +14 -1
package/src/arc/lsp.spec.ts
CHANGED
|
@@ -27,7 +27,7 @@ type JSONRPCResponse =
|
|
|
27
27
|
};
|
|
28
28
|
|
|
29
29
|
type LSPReceiver = {
|
|
30
|
-
receive: () => Promise<
|
|
30
|
+
receive: () => Promise<{ content: string }>;
|
|
31
31
|
};
|
|
32
32
|
|
|
33
33
|
const MAX_DRAIN = 50;
|
|
@@ -38,9 +38,7 @@ const receiveResponse = async (
|
|
|
38
38
|
expectedId: number,
|
|
39
39
|
): Promise<JSONRPCResponse> => {
|
|
40
40
|
for (let i = 0; i < MAX_DRAIN; i++) {
|
|
41
|
-
const
|
|
42
|
-
if (err != null) throw err;
|
|
43
|
-
if (res == null) throw new Error("Expected response");
|
|
41
|
+
const res = await stream.receive();
|
|
44
42
|
const msg = JSON.parse(res.content);
|
|
45
43
|
if (!("method" in msg) && "id" in msg && msg.id === expectedId)
|
|
46
44
|
return msg as JSONRPCResponse;
|
|
@@ -56,9 +54,7 @@ const receiveNotification = async (
|
|
|
56
54
|
expectedMethod: string,
|
|
57
55
|
): Promise<JSONRPCRequest> => {
|
|
58
56
|
for (let i = 0; i < MAX_DRAIN; i++) {
|
|
59
|
-
const
|
|
60
|
-
if (err != null) throw err;
|
|
61
|
-
if (res == null) throw new Error("Expected message");
|
|
57
|
+
const res = await stream.receive();
|
|
62
58
|
const msg = JSON.parse(res.content);
|
|
63
59
|
if ("method" in msg && msg.method === expectedMethod) return msg as JSONRPCRequest;
|
|
64
60
|
}
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
|
|
10
10
|
// Code generated by Oracle. DO NOT EDIT.
|
|
11
11
|
|
|
12
|
-
import { array, zod } from "@synnaxlabs/x";
|
|
12
|
+
import { array, record, zod } from "@synnaxlabs/x";
|
|
13
13
|
import { z } from "zod";
|
|
14
14
|
|
|
15
15
|
export enum Kind {
|
|
@@ -48,9 +48,9 @@ export const chanDirectionZ = z.enum(ChanDirection);
|
|
|
48
48
|
/** Channels contains channel declarations for reading from and writing to Synnax channels. */
|
|
49
49
|
export const channelsZ = z.object({
|
|
50
50
|
/** read contains readable channel indices mapped to parameter names. */
|
|
51
|
-
read:
|
|
51
|
+
read: record.nullishToEmpty(z.uint32(), z.string()),
|
|
52
52
|
/** write contains writable channel indices mapped to parameter names. */
|
|
53
|
-
write:
|
|
53
|
+
write: record.nullishToEmpty(z.uint32(), z.string()),
|
|
54
54
|
});
|
|
55
55
|
export interface Channels extends z.infer<typeof channelsZ> {}
|
|
56
56
|
|
package/src/auth/auth.spec.ts
CHANGED
|
@@ -33,8 +33,8 @@ describe("auth", () => {
|
|
|
33
33
|
);
|
|
34
34
|
const client = new auth.Client(transport.unary, TEST_CLIENT_PARAMS);
|
|
35
35
|
const mw = client.middleware();
|
|
36
|
-
const res = await mw(DUMMY_CTX, async () =>
|
|
37
|
-
expect(res).toEqual(
|
|
36
|
+
const res = await mw(DUMMY_CTX, async () => DUMMY_CTX);
|
|
37
|
+
expect(res).toEqual(DUMMY_CTX);
|
|
38
38
|
});
|
|
39
39
|
|
|
40
40
|
test("invalid credentials", async () => {
|
|
@@ -49,8 +49,7 @@ describe("auth", () => {
|
|
|
49
49
|
password: "wrong",
|
|
50
50
|
});
|
|
51
51
|
const mw = client.middleware();
|
|
52
|
-
|
|
53
|
-
expect(err).toBeInstanceOf(AuthError);
|
|
52
|
+
await expect(mw(DUMMY_CTX, async () => DUMMY_CTX)).rejects.toThrow(AuthError);
|
|
54
53
|
});
|
|
55
54
|
|
|
56
55
|
describe("token retry", () => {
|
|
@@ -68,16 +67,16 @@ describe("auth", () => {
|
|
|
68
67
|
let isFirst = true;
|
|
69
68
|
let tkOne: string | undefined;
|
|
70
69
|
let tkTwo: string | undefined;
|
|
71
|
-
const
|
|
70
|
+
const res = await mw(DUMMY_CTX, async () => {
|
|
72
71
|
if (isFirst) {
|
|
73
72
|
isFirst = false;
|
|
74
73
|
tkOne = client.token;
|
|
75
|
-
|
|
74
|
+
throw new ErrorType();
|
|
76
75
|
}
|
|
77
76
|
tkTwo = client.token;
|
|
78
|
-
return
|
|
77
|
+
return DUMMY_CTX;
|
|
79
78
|
});
|
|
80
|
-
expect(
|
|
79
|
+
expect(res).toEqual(DUMMY_CTX);
|
|
81
80
|
expect(tkOne).toBeDefined();
|
|
82
81
|
expect(tkTwo).toBeDefined();
|
|
83
82
|
});
|
|
@@ -92,11 +91,11 @@ describe("auth", () => {
|
|
|
92
91
|
);
|
|
93
92
|
const client = new auth.Client(transport.unary, TEST_CLIENT_PARAMS);
|
|
94
93
|
const mw = client.middleware();
|
|
95
|
-
|
|
96
|
-
DUMMY_CTX,
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
94
|
+
await expect(
|
|
95
|
+
mw(DUMMY_CTX, async () => {
|
|
96
|
+
throw new InvalidTokenError();
|
|
97
|
+
}),
|
|
98
|
+
).rejects.toThrow(InvalidTokenError);
|
|
100
99
|
});
|
|
101
100
|
});
|
|
102
101
|
});
|
package/src/auth/auth.ts
CHANGED
|
@@ -7,16 +7,28 @@
|
|
|
7
7
|
// License, use of this software will be governed by the Apache License, Version 2.0,
|
|
8
8
|
// included in the file licenses/APL.txt.
|
|
9
9
|
|
|
10
|
-
import { type Middleware,
|
|
10
|
+
import { type Middleware, type UnaryClient } from "@synnaxlabs/freighter";
|
|
11
|
+
import { TimeStamp } from "@synnaxlabs/x";
|
|
11
12
|
import { z } from "zod";
|
|
12
13
|
|
|
13
14
|
import { ExpiredTokenError, InvalidTokenError } from "@/errors";
|
|
14
15
|
import { user } from "@/user";
|
|
15
16
|
|
|
16
|
-
const
|
|
17
|
-
interface
|
|
17
|
+
const credentialsZ = z.object({ username: z.string(), password: z.string() });
|
|
18
|
+
interface Credentials extends z.infer<typeof credentialsZ> {}
|
|
18
19
|
|
|
19
|
-
const
|
|
20
|
+
const clusterInfoZ = z.object({
|
|
21
|
+
clusterKey: z.string(),
|
|
22
|
+
nodeVersion: z.string().optional(),
|
|
23
|
+
nodeKey: z.number().optional(),
|
|
24
|
+
nodeTime: TimeStamp.z,
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
const tokenResponseZ = z.object({
|
|
28
|
+
token: z.string(),
|
|
29
|
+
user: user.userZ,
|
|
30
|
+
clusterInfo: clusterInfoZ.optional(),
|
|
31
|
+
});
|
|
20
32
|
|
|
21
33
|
const LOGIN_ENDPOINT = "/auth/login";
|
|
22
34
|
|
|
@@ -37,12 +49,12 @@ type AuthState =
|
|
|
37
49
|
|
|
38
50
|
export class Client {
|
|
39
51
|
private readonly client: UnaryClient;
|
|
40
|
-
private readonly credentials:
|
|
52
|
+
private readonly credentials: Credentials;
|
|
41
53
|
private authState: AuthState = { authenticated: false };
|
|
42
54
|
authenticating: Promise<Error | null> | undefined;
|
|
43
55
|
private retryCount: number;
|
|
44
56
|
|
|
45
|
-
constructor(client: UnaryClient, credentials:
|
|
57
|
+
constructor(client: UnaryClient, credentials: Credentials) {
|
|
46
58
|
this.client = client;
|
|
47
59
|
this.credentials = credentials;
|
|
48
60
|
this.retryCount = 0;
|
|
@@ -70,8 +82,7 @@ export class Client {
|
|
|
70
82
|
|
|
71
83
|
async changePassword(newPassword: string): Promise<void> {
|
|
72
84
|
if (!this.authenticated) throw new Error("Not authenticated");
|
|
73
|
-
await
|
|
74
|
-
this.client,
|
|
85
|
+
await this.client.send(
|
|
75
86
|
"/auth/change-password",
|
|
76
87
|
{
|
|
77
88
|
username: this.credentials.username,
|
|
@@ -87,39 +98,42 @@ export class Client {
|
|
|
87
98
|
middleware(): Middleware {
|
|
88
99
|
const mw: Middleware = async (reqCtx, next) => {
|
|
89
100
|
if (!this.authenticated && !reqCtx.target.endsWith(LOGIN_ENDPOINT)) {
|
|
90
|
-
this.authenticating ??=
|
|
91
|
-
|
|
92
|
-
.send(
|
|
101
|
+
this.authenticating ??= (async (): Promise<Error | null> => {
|
|
102
|
+
try {
|
|
103
|
+
const res = await this.client.send(
|
|
93
104
|
LOGIN_ENDPOINT,
|
|
94
105
|
this.credentials,
|
|
95
|
-
|
|
106
|
+
credentialsZ,
|
|
96
107
|
tokenResponseZ,
|
|
97
|
-
)
|
|
98
|
-
.
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
.catch(reject);
|
|
109
|
-
});
|
|
108
|
+
);
|
|
109
|
+
this.authState = {
|
|
110
|
+
authenticated: true,
|
|
111
|
+
user: res.user,
|
|
112
|
+
token: res.token,
|
|
113
|
+
};
|
|
114
|
+
return null;
|
|
115
|
+
} catch (err) {
|
|
116
|
+
return err instanceof Error ? err : new Error(String(err));
|
|
117
|
+
}
|
|
118
|
+
})();
|
|
110
119
|
const err = await this.authenticating;
|
|
111
|
-
if (err != null)
|
|
120
|
+
if (err != null) throw err;
|
|
112
121
|
}
|
|
113
122
|
reqCtx.params.Authorization = `Bearer ${this.token}`;
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
this.
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
123
|
+
try {
|
|
124
|
+
const resCtx = await next(reqCtx);
|
|
125
|
+
this.retryCount = 0;
|
|
126
|
+
return resCtx;
|
|
127
|
+
} catch (err) {
|
|
128
|
+
if (RETRY_ON.some((e) => e.matches(err)) && this.retryCount < MAX_RETRIES) {
|
|
129
|
+
this.authState = { authenticated: false };
|
|
130
|
+
this.authenticating = undefined;
|
|
131
|
+
this.retryCount += 1;
|
|
132
|
+
return mw(reqCtx, next);
|
|
133
|
+
}
|
|
134
|
+
this.retryCount = 0;
|
|
135
|
+
throw err;
|
|
120
136
|
}
|
|
121
|
-
this.retryCount = 0;
|
|
122
|
-
return [resCtx, err];
|
|
123
137
|
};
|
|
124
138
|
return mw;
|
|
125
139
|
}
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
// License, use of this software will be governed by the Apache License, Version 2.0,
|
|
8
8
|
// included in the file licenses/APL.txt.
|
|
9
9
|
|
|
10
|
-
import { DataType, errors, Rate } from "@synnaxlabs/x";
|
|
10
|
+
import { DataType, errors, Rate, TimeSpan } from "@synnaxlabs/x";
|
|
11
11
|
import { describe, expect, it, vi } from "vitest";
|
|
12
12
|
|
|
13
13
|
import { channel } from "@/channel";
|
|
@@ -57,7 +57,10 @@ describe("channelchannel.Retriever", () => {
|
|
|
57
57
|
operations: [],
|
|
58
58
|
}));
|
|
59
59
|
});
|
|
60
|
-
const retriever = new channel.DebouncedBatchRetriever(
|
|
60
|
+
const retriever = new channel.DebouncedBatchRetriever(
|
|
61
|
+
base,
|
|
62
|
+
TimeSpan.milliseconds(10),
|
|
63
|
+
);
|
|
61
64
|
const res = await Promise.all([
|
|
62
65
|
retriever.retrieve([1]),
|
|
63
66
|
retriever.retrieve([2]),
|
|
@@ -86,7 +89,10 @@ describe("channelchannel.Retriever", () => {
|
|
|
86
89
|
operations: [],
|
|
87
90
|
}));
|
|
88
91
|
});
|
|
89
|
-
const retriever = new channel.DebouncedBatchRetriever(
|
|
92
|
+
const retriever = new channel.DebouncedBatchRetriever(
|
|
93
|
+
base,
|
|
94
|
+
TimeSpan.milliseconds(10),
|
|
95
|
+
);
|
|
90
96
|
const res = await Promise.all([
|
|
91
97
|
retriever.retrieve([1]),
|
|
92
98
|
retriever.retrieve([2]),
|
|
@@ -100,7 +106,10 @@ describe("channelchannel.Retriever", () => {
|
|
|
100
106
|
const base = new MockRetriever(async (): Promise<channel.Payload[]> => {
|
|
101
107
|
throw new Error("failed to fetch");
|
|
102
108
|
});
|
|
103
|
-
const retriever = new channel.DebouncedBatchRetriever(
|
|
109
|
+
const retriever = new channel.DebouncedBatchRetriever(
|
|
110
|
+
base,
|
|
111
|
+
TimeSpan.milliseconds(10),
|
|
112
|
+
);
|
|
104
113
|
await expect(retriever.retrieve([1])).rejects.toThrow("failed to fetch");
|
|
105
114
|
});
|
|
106
115
|
});
|
|
@@ -226,6 +226,19 @@ describe("Channel", () => {
|
|
|
226
226
|
async () => await client.channels.retrieve("1-1000"),
|
|
227
227
|
).rejects.toThrow(NotFoundError);
|
|
228
228
|
});
|
|
229
|
+
test("retrieve by search term", async () => {
|
|
230
|
+
const prefix = id.create();
|
|
231
|
+
const names = [`${prefix}_1`, `${prefix}_2`];
|
|
232
|
+
await client.channels.create(
|
|
233
|
+
names.map((name) => ({ name, virtual: true, dataType: DataType.FLOAT32 })),
|
|
234
|
+
);
|
|
235
|
+
await expect
|
|
236
|
+
.poll(async () => {
|
|
237
|
+
const results = await client.channels.retrieve({ searchTerm: prefix });
|
|
238
|
+
return results.map((c) => c.name).sort();
|
|
239
|
+
})
|
|
240
|
+
.toEqual(names);
|
|
241
|
+
});
|
|
229
242
|
});
|
|
230
243
|
|
|
231
244
|
describe("delete", async () => {
|
package/src/channel/client.ts
CHANGED
|
@@ -7,15 +7,17 @@
|
|
|
7
7
|
// License, use of this software will be governed by the Apache License, Version 2.0,
|
|
8
8
|
// included in the file licenses/APL.txt.
|
|
9
9
|
|
|
10
|
-
import {
|
|
10
|
+
import { type UnaryClient } from "@synnaxlabs/freighter";
|
|
11
11
|
import {
|
|
12
12
|
array,
|
|
13
13
|
type CrudeDensity,
|
|
14
14
|
type CrudeTimeRange,
|
|
15
|
+
type CrudeTimeSpan,
|
|
15
16
|
type CrudeTimeStamp,
|
|
16
17
|
DataType,
|
|
17
18
|
type MultiSeries,
|
|
18
19
|
status,
|
|
20
|
+
TimeSpan,
|
|
19
21
|
type TypedArray,
|
|
20
22
|
} from "@synnaxlabs/x";
|
|
21
23
|
import { z } from "zod";
|
|
@@ -387,8 +389,7 @@ export class Client {
|
|
|
387
389
|
*/
|
|
388
390
|
async delete(params: Params): Promise<void> {
|
|
389
391
|
const { normalized, variant } = analyzeParams(params);
|
|
390
|
-
if (variant === "keys")
|
|
391
|
-
return await this.writer.delete({ keys: normalized as Key[] });
|
|
392
|
+
if (variant === "keys") return await this.writer.delete({ keys: normalized });
|
|
392
393
|
return await this.writer.delete({ names: normalized as string[] });
|
|
393
394
|
}
|
|
394
395
|
|
|
@@ -398,7 +399,9 @@ export class Client {
|
|
|
398
399
|
return await this.writer.rename(array.toArray(keys), array.toArray(names));
|
|
399
400
|
}
|
|
400
401
|
|
|
401
|
-
createDebouncedBatchRetriever(
|
|
402
|
+
createDebouncedBatchRetriever(
|
|
403
|
+
deb: CrudeTimeSpan = TimeSpan.milliseconds(10),
|
|
404
|
+
): Retriever {
|
|
402
405
|
return new CacheRetriever(
|
|
403
406
|
new DebouncedBatchRetriever(new ClusterRetriever(this.client), deb),
|
|
404
407
|
);
|
|
@@ -414,8 +417,7 @@ export class Client {
|
|
|
414
417
|
}
|
|
415
418
|
|
|
416
419
|
async retrieveGroup(): Promise<group.Group> {
|
|
417
|
-
const res = await
|
|
418
|
-
this.client,
|
|
420
|
+
const res = await this.client.send(
|
|
419
421
|
"/channel/retrieve-group",
|
|
420
422
|
{},
|
|
421
423
|
retrieveGroupReqZ,
|
package/src/channel/retriever.ts
CHANGED
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
// License, use of this software will be governed by the Apache License, Version 2.0,
|
|
8
8
|
// included in the file licenses/APL.txt.
|
|
9
9
|
|
|
10
|
-
import {
|
|
11
|
-
import { array, DataType, debounce, zod } from "@synnaxlabs/x";
|
|
10
|
+
import { type UnaryClient } from "@synnaxlabs/freighter";
|
|
11
|
+
import { array, type CrudeTimeSpan, DataType, debounce, zod } from "@synnaxlabs/x";
|
|
12
12
|
import { Mutex } from "async-mutex";
|
|
13
13
|
import { z } from "zod";
|
|
14
14
|
|
|
@@ -36,13 +36,10 @@ const reqZ = z.object({
|
|
|
36
36
|
internal: z.boolean().optional(),
|
|
37
37
|
legacyCalculated: z.boolean().optional(),
|
|
38
38
|
});
|
|
39
|
-
export
|
|
39
|
+
export type RetrieveRequest = z.input<typeof reqZ>;
|
|
40
40
|
|
|
41
|
-
export
|
|
42
|
-
|
|
43
|
-
"keys" | "names" | "search"
|
|
44
|
-
> {}
|
|
45
|
-
export interface PageOptions extends Omit<RetrieveOptions, "offset" | "limit"> {}
|
|
41
|
+
export type RetrieveOptions = Omit<RetrieveRequest, "keys" | "names" | "search">;
|
|
42
|
+
export type PageOptions = Omit<RetrieveOptions, "offset" | "limit">;
|
|
46
43
|
|
|
47
44
|
const resZ = z.object({ channels: array.nullishToEmpty(payloadZ) });
|
|
48
45
|
|
|
@@ -86,13 +83,7 @@ export class ClusterRetriever implements Retriever {
|
|
|
86
83
|
}
|
|
87
84
|
|
|
88
85
|
private async execute(request: RetrieveRequest): Promise<Payload[]> {
|
|
89
|
-
const res = await
|
|
90
|
-
this.client,
|
|
91
|
-
"/channel/retrieve",
|
|
92
|
-
request,
|
|
93
|
-
reqZ,
|
|
94
|
-
resZ,
|
|
95
|
-
);
|
|
86
|
+
const res = await this.client.send("/channel/retrieve", request, reqZ, resZ);
|
|
96
87
|
return res.channels;
|
|
97
88
|
}
|
|
98
89
|
}
|
|
@@ -204,7 +195,7 @@ export class DebouncedBatchRetriever implements Retriever {
|
|
|
204
195
|
private readonly wrapped: Retriever;
|
|
205
196
|
private readonly debouncedRun: () => void;
|
|
206
197
|
|
|
207
|
-
constructor(wrapped: Retriever, deb:
|
|
198
|
+
constructor(wrapped: Retriever, deb: CrudeTimeSpan) {
|
|
208
199
|
this.wrapped = wrapped;
|
|
209
200
|
this.debouncedRun = debounce(() => {
|
|
210
201
|
void this.run();
|
package/src/channel/types.gen.ts
CHANGED
|
@@ -85,8 +85,7 @@ export const payloadZ = z.object({
|
|
|
85
85
|
isIndex: z.boolean(),
|
|
86
86
|
/**
|
|
87
87
|
* index is the channel used to index this channel's values, associating
|
|
88
|
-
* each value with a timestamp.
|
|
89
|
-
* indexed using its rate.
|
|
88
|
+
* each value with a timestamp.
|
|
90
89
|
*/
|
|
91
90
|
index: keyZ,
|
|
92
91
|
/** alias is an optional alternate name for the channel within a specific context. */
|
package/src/channel/writer.ts
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
// License, use of this software will be governed by the Apache License, Version 2.0,
|
|
8
8
|
// included in the file licenses/APL.txt.
|
|
9
9
|
|
|
10
|
-
import {
|
|
10
|
+
import { type UnaryClient } from "@synnaxlabs/freighter";
|
|
11
11
|
import { type DataType } from "@synnaxlabs/x";
|
|
12
12
|
import { z } from "zod";
|
|
13
13
|
|
|
@@ -47,11 +47,7 @@ export class Writer {
|
|
|
47
47
|
}
|
|
48
48
|
|
|
49
49
|
async create(channels: New[]): Promise<Payload[]> {
|
|
50
|
-
const { channels: created } = await
|
|
51
|
-
typeof createReqZ,
|
|
52
|
-
typeof createResZ
|
|
53
|
-
>(
|
|
54
|
-
this.client,
|
|
50
|
+
const { channels: created } = await this.client.send(
|
|
55
51
|
"/channel/create",
|
|
56
52
|
{
|
|
57
53
|
channels: channels.map((c) => ({
|
|
@@ -68,25 +64,13 @@ export class Writer {
|
|
|
68
64
|
|
|
69
65
|
async delete(props: DeleteProps): Promise<void> {
|
|
70
66
|
const keys = keyZ.array().parse(props.keys ?? []);
|
|
71
|
-
await
|
|
72
|
-
this.client,
|
|
73
|
-
"/channel/delete",
|
|
74
|
-
props,
|
|
75
|
-
deleteReqZ,
|
|
76
|
-
deleteResZ,
|
|
77
|
-
);
|
|
67
|
+
await this.client.send("/channel/delete", props, deleteReqZ, deleteResZ);
|
|
78
68
|
if (keys.length > 0) this.cache.delete(keys);
|
|
79
69
|
if (props.names != null) this.cache.delete(props.names);
|
|
80
70
|
}
|
|
81
71
|
|
|
82
72
|
async rename(keys: Key[], names: string[]): Promise<void> {
|
|
83
|
-
await
|
|
84
|
-
this.client,
|
|
85
|
-
"/channel/rename",
|
|
86
|
-
{ keys, names },
|
|
87
|
-
renameReqZ,
|
|
88
|
-
renameResZ,
|
|
89
|
-
);
|
|
73
|
+
await this.client.send("/channel/rename", { keys, names }, renameReqZ, renameResZ);
|
|
90
74
|
this.cache.rename(keys, names);
|
|
91
75
|
}
|
|
92
76
|
}
|
package/src/client.ts
CHANGED
|
@@ -45,6 +45,7 @@ export const synnaxParamsZ = z.object({
|
|
|
45
45
|
username: z.string().min(1, "Username is required"),
|
|
46
46
|
password: z.string().min(1, "Password is required"),
|
|
47
47
|
connectivityPollFrequency: TimeSpan.z.default(TimeSpan.seconds(30)),
|
|
48
|
+
clockSkewThreshold: TimeSpan.z.default(TimeSpan.seconds(1)),
|
|
48
49
|
secure: z.boolean().default(false),
|
|
49
50
|
name: z.string().optional(),
|
|
50
51
|
retry: breaker.breakerConfigZ.optional(),
|
|
@@ -116,6 +117,7 @@ export default class Synnax extends framer.Client {
|
|
|
116
117
|
username,
|
|
117
118
|
password,
|
|
118
119
|
connectivityPollFrequency,
|
|
120
|
+
clockSkewThreshold,
|
|
119
121
|
secure,
|
|
120
122
|
retry: breaker,
|
|
121
123
|
} = parsedParams;
|
|
@@ -141,6 +143,7 @@ export default class Synnax extends framer.Client {
|
|
|
141
143
|
connectivityPollFrequency,
|
|
142
144
|
this.clientVersion,
|
|
143
145
|
parsedParams.name,
|
|
146
|
+
clockSkewThreshold,
|
|
144
147
|
);
|
|
145
148
|
this.control = new control.Client(this);
|
|
146
149
|
this.ontology = new ontology.Client(this.transport.unary);
|
|
@@ -7,8 +7,14 @@
|
|
|
7
7
|
// License, use of this software will be governed by the Apache License, Version 2.0,
|
|
8
8
|
// included in the file licenses/APL.txt.
|
|
9
9
|
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
10
|
+
import { type UnaryClient } from "@synnaxlabs/freighter";
|
|
11
|
+
import {
|
|
12
|
+
ClockSkewCalculator,
|
|
13
|
+
type CrudeTimeSpan,
|
|
14
|
+
migrate,
|
|
15
|
+
TimeSpan,
|
|
16
|
+
TimeStamp,
|
|
17
|
+
} from "@synnaxlabs/x";
|
|
12
18
|
import { z } from "zod";
|
|
13
19
|
|
|
14
20
|
export const statusZ = z.enum(["disconnected", "connecting", "connected", "failed"]);
|
|
@@ -22,12 +28,15 @@ export const stateZ = z.object({
|
|
|
22
28
|
clientVersion: z.string(),
|
|
23
29
|
clientServerCompatible: z.boolean(),
|
|
24
30
|
nodeVersion: z.string().optional(),
|
|
31
|
+
clockSkew: TimeSpan.z.default(TimeSpan.ZERO),
|
|
32
|
+
clockSkewExceeded: z.boolean().default(false),
|
|
25
33
|
});
|
|
26
34
|
export interface State extends z.infer<typeof stateZ> {}
|
|
27
35
|
|
|
28
36
|
const responseZ = z.object({
|
|
29
37
|
clusterKey: z.string(),
|
|
30
38
|
nodeVersion: z.string().optional(),
|
|
39
|
+
nodeTime: TimeStamp.z,
|
|
31
40
|
});
|
|
32
41
|
const requestZ = z.void();
|
|
33
42
|
|
|
@@ -38,6 +47,8 @@ const DEFAULT: State = {
|
|
|
38
47
|
message: "Disconnected",
|
|
39
48
|
clientServerCompatible: false,
|
|
40
49
|
clientVersion: __VERSION__,
|
|
50
|
+
clockSkew: TimeSpan.ZERO,
|
|
51
|
+
clockSkewExceeded: false,
|
|
41
52
|
};
|
|
42
53
|
|
|
43
54
|
const createWarning = (
|
|
@@ -46,16 +57,16 @@ const createWarning = (
|
|
|
46
57
|
clientIsNewer: boolean,
|
|
47
58
|
): string => {
|
|
48
59
|
const toUpgrade = clientIsNewer ? "Core" : "client";
|
|
49
|
-
return `Synnax Core version ${nodeVersion != null ? `${nodeVersion} ` : ""}is too ${clientIsNewer ? "old" : "new"} for client version ${clientVersion}.
|
|
60
|
+
return `The Synnax Core version ${nodeVersion != null ? `${nodeVersion} ` : ""}is too ${clientIsNewer ? "old" : "new"} for client version ${clientVersion}.
|
|
50
61
|
This may cause compatibility issues. We recommend updating the ${toUpgrade}. For more information, see
|
|
51
|
-
https://docs.synnaxlabs.com/reference/client/resources/troubleshooting#old-${toUpgrade}-version`;
|
|
62
|
+
https://docs.synnaxlabs.com/reference/client/resources/troubleshooting#old-${toUpgrade.toLowerCase()}-version`;
|
|
52
63
|
};
|
|
53
64
|
|
|
54
65
|
/** Polls a synnax cluster for connectivity information. */
|
|
55
66
|
export class Checker {
|
|
56
67
|
static readonly DEFAULT: State = DEFAULT;
|
|
57
68
|
private readonly _state: State;
|
|
58
|
-
private readonly pollFrequency
|
|
69
|
+
private readonly pollFrequency: TimeSpan;
|
|
59
70
|
private readonly client: UnaryClient;
|
|
60
71
|
private readonly name?: string;
|
|
61
72
|
private interval?: NodeJS.Timeout;
|
|
@@ -63,6 +74,9 @@ export class Checker {
|
|
|
63
74
|
private readonly onChangeHandlers: Array<(state: State) => void> = [];
|
|
64
75
|
static readonly connectionStateZ = stateZ;
|
|
65
76
|
private versionWarned = false;
|
|
77
|
+
private readonly skewCalc: ClockSkewCalculator;
|
|
78
|
+
private readonly clockSkewThreshold: TimeSpan;
|
|
79
|
+
private checking = false;
|
|
66
80
|
|
|
67
81
|
/**
|
|
68
82
|
* @param client - The transport client to use for connectivity checks.
|
|
@@ -71,15 +85,18 @@ export class Checker {
|
|
|
71
85
|
*/
|
|
72
86
|
constructor(
|
|
73
87
|
client: UnaryClient,
|
|
74
|
-
pollFreq:
|
|
88
|
+
pollFreq: CrudeTimeSpan = TimeSpan.seconds(30),
|
|
75
89
|
clientVersion: string,
|
|
76
90
|
name?: string,
|
|
91
|
+
clockSkewThreshold: CrudeTimeSpan = TimeSpan.seconds(1),
|
|
77
92
|
) {
|
|
78
93
|
this._state = { ...DEFAULT };
|
|
79
94
|
this.client = client;
|
|
80
|
-
this.pollFrequency = pollFreq;
|
|
95
|
+
this.pollFrequency = new TimeSpan(pollFreq);
|
|
81
96
|
this.clientVersion = clientVersion;
|
|
82
97
|
this.name = name;
|
|
98
|
+
this.skewCalc = new ClockSkewCalculator();
|
|
99
|
+
this.clockSkewThreshold = new TimeSpan(clockSkewThreshold).abs();
|
|
83
100
|
void this.check();
|
|
84
101
|
this.start();
|
|
85
102
|
}
|
|
@@ -95,14 +112,30 @@ export class Checker {
|
|
|
95
112
|
*/
|
|
96
113
|
async check(): Promise<State> {
|
|
97
114
|
const prevStatus = this._state.status;
|
|
115
|
+
const prevSkewExceeded = this._state.clockSkewExceeded;
|
|
116
|
+
const measureSkew = !this.checking;
|
|
117
|
+
this.checking = true;
|
|
98
118
|
try {
|
|
99
|
-
|
|
100
|
-
|
|
119
|
+
if (measureSkew) this.skewCalc.start();
|
|
120
|
+
const res = await this.client.send(
|
|
101
121
|
"/connectivity/check",
|
|
102
122
|
undefined,
|
|
103
123
|
requestZ,
|
|
104
124
|
responseZ,
|
|
105
125
|
);
|
|
126
|
+
if (measureSkew) {
|
|
127
|
+
this.skewCalc.end(res.nodeTime);
|
|
128
|
+
this._state.clockSkew = this.skewCalc.skew;
|
|
129
|
+
this._state.clockSkewExceeded = this.skewCalc.exceeds(this.clockSkewThreshold);
|
|
130
|
+
if (this._state.clockSkewExceeded) {
|
|
131
|
+
const direction = this.skewCalc.skew.valueOf() > 0n ? "ahead of" : "behind";
|
|
132
|
+
console.warn(
|
|
133
|
+
`Measured excessive clock skew between this host and ` +
|
|
134
|
+
`the Synnax Core. This host is ${direction} the Synnax Core ` +
|
|
135
|
+
`by approximately ${this.skewCalc.skew.abs().toString()}.`,
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
106
139
|
const nodeVersion = res.nodeVersion;
|
|
107
140
|
const clientVersion = this.clientVersion;
|
|
108
141
|
const warned = this.versionWarned;
|
|
@@ -140,8 +173,13 @@ export class Checker {
|
|
|
140
173
|
this._state.status = "failed";
|
|
141
174
|
this._state.error = err as Error;
|
|
142
175
|
this._state.message = this.state.error?.message;
|
|
176
|
+
} finally {
|
|
177
|
+
this.checking = false;
|
|
143
178
|
}
|
|
144
|
-
|
|
179
|
+
const changed =
|
|
180
|
+
prevStatus !== this._state.status ||
|
|
181
|
+
prevSkewExceeded !== this._state.clockSkewExceeded;
|
|
182
|
+
if (this.onChangeHandlers.length > 0 && changed)
|
|
145
183
|
this.onChangeHandlers.forEach((handler) => handler(this.state));
|
|
146
184
|
return this.state;
|
|
147
185
|
}
|