@synnaxlabs/client 0.26.7 → 0.28.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 +7 -7
- package/README.md +36 -10
- package/api/client.api.md +3121 -0
- package/api-extractor.json +7 -0
- package/dist/access/client.d.ts +0 -1
- package/dist/access/payload.d.ts +0 -1
- package/dist/auth/auth.d.ts +0 -1
- package/dist/channel/client.d.ts +6 -2
- package/dist/channel/client.d.ts.map +1 -1
- package/dist/channel/creator.d.ts +0 -1
- package/dist/channel/payload.d.ts +4 -1
- package/dist/channel/payload.d.ts.map +1 -1
- package/dist/channel/retriever.d.ts +0 -1
- package/dist/channel/writer.d.ts +0 -1
- package/dist/client.cjs +23 -19
- package/dist/client.d.ts +10 -7
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +2326 -1904
- package/dist/connection/checker.d.ts +21 -2
- package/dist/connection/checker.d.ts.map +1 -1
- package/dist/control/client.d.ts +0 -1
- package/dist/control/client.d.ts.map +1 -1
- package/dist/control/state.d.ts +0 -1
- package/dist/errors.d.ts +0 -1
- package/dist/framer/adapter.d.ts +0 -1
- package/dist/framer/client.d.ts +0 -1
- package/dist/framer/client.d.ts.map +1 -1
- package/dist/framer/deleter.d.ts +0 -1
- package/dist/framer/frame.d.ts +11 -12
- package/dist/framer/iterator.d.ts +0 -1
- package/dist/framer/streamProxy.d.ts +0 -1
- package/dist/framer/streamer.d.ts +0 -1
- package/dist/framer/writer.d.ts +0 -1
- package/dist/framer/writer.d.ts.map +1 -1
- package/dist/hardware/client.d.ts +0 -1
- package/dist/hardware/device/client.d.ts +0 -1
- package/dist/hardware/device/payload.d.ts +0 -1
- package/dist/hardware/device/payload.d.ts.map +1 -1
- package/dist/hardware/rack/client.d.ts +0 -1
- package/dist/hardware/rack/payload.d.ts +0 -1
- package/dist/hardware/rack/payload.d.ts.map +1 -1
- package/dist/hardware/task/client.d.ts +12 -3
- package/dist/hardware/task/client.d.ts.map +1 -1
- package/dist/hardware/task/ni/types.d.ts +14495 -0
- package/dist/hardware/task/ni/types.d.ts.map +1 -0
- package/dist/hardware/task/payload.d.ts +6 -1
- package/dist/hardware/task/payload.d.ts.map +1 -1
- package/dist/label/client.d.ts +8 -6
- package/dist/label/client.d.ts.map +1 -1
- package/dist/label/payload.d.ts +0 -1
- package/dist/label/retriever.d.ts +0 -1
- package/dist/label/retriever.d.ts.map +1 -1
- package/dist/label/writer.d.ts +32 -2
- package/dist/label/writer.d.ts.map +1 -1
- package/dist/ontology/client.d.ts +136 -12
- package/dist/ontology/client.d.ts.map +1 -1
- package/dist/ontology/group/client.d.ts +0 -1
- package/dist/ontology/group/group.d.ts +0 -1
- package/dist/ontology/group/payload.d.ts +0 -1
- package/dist/ontology/group/writer.d.ts +0 -1
- package/dist/ontology/group/writer.d.ts.map +1 -1
- package/dist/ontology/payload.d.ts +6 -3
- package/dist/ontology/payload.d.ts.map +1 -1
- package/dist/ontology/writer.d.ts +4 -5
- package/dist/ontology/writer.d.ts.map +1 -1
- package/dist/ranger/alias.d.ts +0 -1
- package/dist/ranger/client.d.ts +57 -14
- package/dist/ranger/client.d.ts.map +1 -1
- package/dist/ranger/external.d.ts +1 -1
- package/dist/ranger/external.d.ts.map +1 -1
- package/dist/ranger/kv.d.ts +42 -5
- package/dist/ranger/kv.d.ts.map +1 -1
- package/dist/ranger/payload.d.ts +5 -2
- package/dist/ranger/payload.d.ts.map +1 -1
- package/dist/ranger/writer.d.ts +107 -2
- package/dist/ranger/writer.d.ts.map +1 -1
- package/dist/setupspecs.d.ts +0 -1
- package/dist/signals/observable.d.ts +0 -1
- package/dist/transport.d.ts +0 -1
- package/dist/user/client.d.ts +0 -1
- package/dist/user/payload.d.ts +0 -1
- package/dist/util/retrieve.d.ts +0 -1
- package/dist/util/telem.d.ts +0 -1
- package/dist/util/zod.d.ts +0 -1
- package/dist/workspace/client.d.ts +0 -1
- package/dist/workspace/lineplot/client.d.ts +0 -1
- package/dist/workspace/lineplot/payload.d.ts +0 -1
- package/dist/workspace/lineplot/retriever.d.ts +0 -1
- package/dist/workspace/lineplot/writer.d.ts +0 -1
- package/dist/workspace/payload.d.ts +0 -1
- package/dist/workspace/retriever.d.ts +0 -1
- package/dist/workspace/schematic/client.d.ts +0 -1
- package/dist/workspace/schematic/payload.d.ts +0 -1
- package/dist/workspace/schematic/retriever.d.ts +0 -1
- package/dist/workspace/schematic/writer.d.ts +0 -1
- package/dist/workspace/writer.d.ts +0 -1
- package/package.json +14 -12
- package/src/access/access.spec.ts +11 -11
- package/src/channel/batchRetriever.spec.ts +2 -0
- package/src/channel/channel.spec.ts +51 -31
- package/src/channel/client.ts +7 -0
- package/src/channel/payload.ts +1 -0
- package/src/client.ts +17 -8
- package/src/connection/checker.ts +58 -1
- package/src/connection/connection.spec.ts +43 -3
- package/src/control/client.ts +9 -0
- package/src/errors.spec.ts +9 -0
- package/src/framer/client.ts +0 -1
- package/src/framer/frame.spec.ts +2 -2
- package/src/framer/frame.ts +22 -22
- package/src/framer/writer.ts +2 -1
- package/src/hardware/device/payload.ts +9 -0
- package/src/hardware/rack/payload.ts +9 -0
- package/src/hardware/task/client.ts +82 -6
- package/src/hardware/task/ni/types.ts +1716 -0
- package/src/hardware/task/payload.ts +10 -0
- package/src/hardware/task/task.spec.ts +45 -30
- package/src/label/client.ts +49 -19
- package/src/label/label.spec.ts +9 -0
- package/src/label/retriever.ts +2 -1
- package/src/label/writer.ts +11 -3
- package/src/ontology/client.ts +227 -14
- package/src/ontology/group/writer.ts +10 -12
- package/src/ontology/ontology.spec.ts +3 -5
- package/src/ontology/payload.ts +5 -1
- package/src/ontology/writer.ts +26 -12
- package/src/ranger/client.ts +223 -41
- package/src/ranger/external.ts +1 -1
- package/src/ranger/kv.ts +50 -11
- package/src/ranger/payload.ts +9 -5
- package/src/ranger/ranger.spec.ts +114 -49
- package/src/ranger/writer.ts +7 -2
- package/src/vite-env.d.ts +1 -1
- package/vite.config.ts +6 -1
- package/dist/ranger/active.d.ts +0 -11
- package/dist/ranger/active.d.ts.map +0 -1
- package/dist/ranger/range.d.ts +0 -32
- package/dist/ranger/range.d.ts.map +0 -1
- package/src/ranger/active.ts +0 -74
- package/src/ranger/range.ts +0 -98
|
@@ -23,7 +23,7 @@ const client = newClient();
|
|
|
23
23
|
|
|
24
24
|
describe("Policy", () => {
|
|
25
25
|
describe("create", () => {
|
|
26
|
-
test("create one", async () => {
|
|
26
|
+
test.skip("create one", async () => {
|
|
27
27
|
const policy = await client.access.create({
|
|
28
28
|
subjects: [{ type: UserOntologyType, key: "1" }],
|
|
29
29
|
objects: [
|
|
@@ -40,7 +40,7 @@ describe("Policy", () => {
|
|
|
40
40
|
]);
|
|
41
41
|
expect(policy.actions).toEqual(["update"]);
|
|
42
42
|
});
|
|
43
|
-
test("create many", async () => {
|
|
43
|
+
test.skip("create many", async () => {
|
|
44
44
|
const policies = await client.access.create([
|
|
45
45
|
{
|
|
46
46
|
subjects: [{ type: UserOntologyType, key: "10" }],
|
|
@@ -62,11 +62,11 @@ describe("Policy", () => {
|
|
|
62
62
|
actions: ["update"],
|
|
63
63
|
},
|
|
64
64
|
]);
|
|
65
|
-
expect(policies.length).toEqual(
|
|
65
|
+
expect(policies.length).toEqual(3);
|
|
66
66
|
expect(policies[0].subjects[0].key).toEqual("10");
|
|
67
67
|
expect(policies[1].subjects[1].key).toEqual("21");
|
|
68
68
|
});
|
|
69
|
-
test("create instances of policies", async () => {
|
|
69
|
+
test.skip("create instances of policies", async () => {
|
|
70
70
|
const policy = {
|
|
71
71
|
key: undefined,
|
|
72
72
|
subjects: [
|
|
@@ -85,7 +85,7 @@ describe("Policy", () => {
|
|
|
85
85
|
expect(p.key).not.toEqual(policy.key);
|
|
86
86
|
});
|
|
87
87
|
});
|
|
88
|
-
test("retrieve by subject", async () => {
|
|
88
|
+
test.skip("retrieve by subject", async () => {
|
|
89
89
|
const key1 = id.id();
|
|
90
90
|
const policies = [
|
|
91
91
|
{
|
|
@@ -120,13 +120,13 @@ describe("Policy", () => {
|
|
|
120
120
|
expect(p).toHaveLength(2);
|
|
121
121
|
expect([p[0].actions, p[1].actions].sort()).toEqual([["delete"], ["update"]]);
|
|
122
122
|
});
|
|
123
|
-
test("retrieve by subject - not found", async () => {
|
|
123
|
+
test.skip("retrieve by subject - not found", async () => {
|
|
124
124
|
const res = await client.access.retrieve({ type: UserOntologyType, key: "999" });
|
|
125
125
|
expect(res).toHaveLength(0);
|
|
126
126
|
});
|
|
127
127
|
|
|
128
128
|
describe("delete", async () => {
|
|
129
|
-
test("delete one", async () => {
|
|
129
|
+
test.skip("delete one", async () => {
|
|
130
130
|
const id1 = id.id();
|
|
131
131
|
const id2 = id.id();
|
|
132
132
|
const id3 = id.id();
|
|
@@ -163,7 +163,7 @@ describe("Policy", () => {
|
|
|
163
163
|
expect(res).toHaveLength(1);
|
|
164
164
|
expect(res[0].actions).toEqual(["delete"]);
|
|
165
165
|
});
|
|
166
|
-
test("delete many", async () => {
|
|
166
|
+
test.skip("delete many", async () => {
|
|
167
167
|
const id1 = id.id();
|
|
168
168
|
const id2 = id.id();
|
|
169
169
|
const id3 = id.id();
|
|
@@ -203,7 +203,7 @@ describe("Policy", () => {
|
|
|
203
203
|
});
|
|
204
204
|
});
|
|
205
205
|
describe("registration", async () => {
|
|
206
|
-
test("register a user", async () => {
|
|
206
|
+
test.skip("register a user", async () => {
|
|
207
207
|
const username = id.id();
|
|
208
208
|
await client.user.register(username, "pwd1");
|
|
209
209
|
new Synnax({
|
|
@@ -213,14 +213,14 @@ describe("Policy", () => {
|
|
|
213
213
|
password: "pwd1",
|
|
214
214
|
});
|
|
215
215
|
});
|
|
216
|
-
test("duplicate username", async () => {
|
|
216
|
+
test.skip("duplicate username", async () => {
|
|
217
217
|
const username = id.id();
|
|
218
218
|
await client.user.register(username, "pwd1");
|
|
219
219
|
await expect(client.user.register(username, "pwd1")).rejects.toThrow(AuthError);
|
|
220
220
|
});
|
|
221
221
|
});
|
|
222
222
|
describe("privilege", async () => {
|
|
223
|
-
test("new user", async () => {
|
|
223
|
+
test.skip("new user", async () => {
|
|
224
224
|
const username = id.id();
|
|
225
225
|
const user2 = await client.user.register(username, "pwd1");
|
|
226
226
|
expect(user2).toBeDefined();
|
|
@@ -55,6 +55,7 @@ describe("channelRetriever", () => {
|
|
|
55
55
|
rate: Rate.hz(1),
|
|
56
56
|
leaseholder: 1,
|
|
57
57
|
index: 0,
|
|
58
|
+
virtual: false,
|
|
58
59
|
}));
|
|
59
60
|
});
|
|
60
61
|
const retriever = new DebouncedBatchRetriever(base, 10);
|
|
@@ -81,6 +82,7 @@ describe("channelRetriever", () => {
|
|
|
81
82
|
rate: Rate.hz(1),
|
|
82
83
|
leaseholder: 1,
|
|
83
84
|
index: 0,
|
|
85
|
+
virtual: false,
|
|
84
86
|
}));
|
|
85
87
|
});
|
|
86
88
|
const retriever = new DebouncedBatchRetriever(base, 10);
|
|
@@ -30,6 +30,7 @@ describe("Channel", () => {
|
|
|
30
30
|
expect(channel.rate).toEqual(Rate.hz(1));
|
|
31
31
|
expect(channel.dataType).toEqual(DataType.FLOAT32);
|
|
32
32
|
});
|
|
33
|
+
|
|
33
34
|
test("create index and indexed pair", async () => {
|
|
34
35
|
const one = await client.channels.create({
|
|
35
36
|
name: "Time",
|
|
@@ -44,6 +45,7 @@ describe("Channel", () => {
|
|
|
44
45
|
});
|
|
45
46
|
expect(two.key).not.toEqual(0);
|
|
46
47
|
});
|
|
48
|
+
|
|
47
49
|
test("create many", async () => {
|
|
48
50
|
const channels = await client.channels.create([
|
|
49
51
|
{
|
|
@@ -63,6 +65,7 @@ describe("Channel", () => {
|
|
|
63
65
|
expect(channels[0].name).toEqual("test1");
|
|
64
66
|
expect(channels[1].name).toEqual("test2");
|
|
65
67
|
});
|
|
68
|
+
|
|
66
69
|
test("create instances of channels", async () => {
|
|
67
70
|
const timeIndexChannel = await client.channels.create({
|
|
68
71
|
name: "time",
|
|
@@ -89,6 +92,20 @@ describe("Channel", () => {
|
|
|
89
92
|
});
|
|
90
93
|
await client.channels.create([sensorOne, sensorTwo, sensorThree]);
|
|
91
94
|
});
|
|
95
|
+
|
|
96
|
+
describe("virtual", () => {
|
|
97
|
+
it("should create a virtual channel", async () => {
|
|
98
|
+
const channel = await client.channels.create({
|
|
99
|
+
name: "test",
|
|
100
|
+
dataType: DataType.JSON,
|
|
101
|
+
virtual: true,
|
|
102
|
+
});
|
|
103
|
+
expect(channel.virtual).toEqual(true);
|
|
104
|
+
const retrieved = await client.channels.retrieve(channel.key);
|
|
105
|
+
expect(retrieved.virtual).toBeTruthy();
|
|
106
|
+
});
|
|
107
|
+
});
|
|
108
|
+
|
|
92
109
|
describe("retrieveIfNameExists", () => {
|
|
93
110
|
it("should retrieve the existing channel when it exists", async () => {
|
|
94
111
|
const name = `test-${Math.random()}-${TimeStamp.now().valueOf()}`;
|
|
@@ -159,38 +176,41 @@ describe("Channel", () => {
|
|
|
159
176
|
});
|
|
160
177
|
});
|
|
161
178
|
});
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
179
|
+
|
|
180
|
+
describe("retrieve", () => {
|
|
181
|
+
test("retrieve by key", async () => {
|
|
182
|
+
const channel = await client.channels.create({
|
|
183
|
+
name: "test",
|
|
184
|
+
leaseholder: 1,
|
|
185
|
+
rate: Rate.hz(1),
|
|
186
|
+
dataType: DataType.FLOAT32,
|
|
187
|
+
});
|
|
188
|
+
const retrieved = await client.channels.retrieve(channel.key);
|
|
189
|
+
expect(retrieved.name).toEqual("test");
|
|
190
|
+
expect(retrieved.leaseholder).toEqual(1);
|
|
191
|
+
expect(retrieved.rate).toEqual(Rate.hz(1));
|
|
192
|
+
expect(retrieved.dataType).toEqual(DataType.FLOAT32);
|
|
193
|
+
});
|
|
194
|
+
test("retrieve by key - not found", async () => {
|
|
195
|
+
await expect(
|
|
196
|
+
async () => await client.channels.retrieve("1-1000"),
|
|
197
|
+
).rejects.toThrow(QueryError);
|
|
198
|
+
});
|
|
199
|
+
test("retrieve by name", async () => {
|
|
200
|
+
const retrieved = await client.channels.retrieve(["test"]);
|
|
201
|
+
expect(retrieved.length).toBeGreaterThan(0);
|
|
202
|
+
retrieved.forEach((ch) => expect(ch.name).toEqual("test"));
|
|
203
|
+
});
|
|
204
|
+
test("retrieve by key - not found", async () => {
|
|
205
|
+
await expect(
|
|
206
|
+
async () => await client.channels.retrieve("1-1000"),
|
|
207
|
+
).rejects.toThrow(NotFoundError);
|
|
208
|
+
});
|
|
209
|
+
test("retrieve by name", async () => {
|
|
210
|
+
const retrieved = await client.channels.retrieve(["test"]);
|
|
211
|
+
expect(retrieved.length).toBeGreaterThan(0);
|
|
212
|
+
retrieved.forEach((ch) => expect(ch.name).toEqual("test"));
|
|
168
213
|
});
|
|
169
|
-
const retrieved = await client.channels.retrieve(channel.key);
|
|
170
|
-
expect(retrieved.name).toEqual("test");
|
|
171
|
-
expect(retrieved.leaseholder).toEqual(1);
|
|
172
|
-
expect(retrieved.rate).toEqual(Rate.hz(1));
|
|
173
|
-
expect(retrieved.dataType).toEqual(DataType.FLOAT32);
|
|
174
|
-
});
|
|
175
|
-
test("retrieve by key - not found", async () => {
|
|
176
|
-
await expect(async () => await client.channels.retrieve("1-1000")).rejects.toThrow(
|
|
177
|
-
QueryError,
|
|
178
|
-
);
|
|
179
|
-
});
|
|
180
|
-
test("retrieve by name", async () => {
|
|
181
|
-
const retrieved = await client.channels.retrieve(["test"]);
|
|
182
|
-
expect(retrieved.length).toBeGreaterThan(0);
|
|
183
|
-
retrieved.forEach((ch) => expect(ch.name).toEqual("test"));
|
|
184
|
-
});
|
|
185
|
-
test("retrieve by key - not found", async () => {
|
|
186
|
-
await expect(async () => await client.channels.retrieve("1-1000")).rejects.toThrow(
|
|
187
|
-
NotFoundError,
|
|
188
|
-
);
|
|
189
|
-
});
|
|
190
|
-
test("retrieve by name", async () => {
|
|
191
|
-
const retrieved = await client.channels.retrieve(["test"]);
|
|
192
|
-
expect(retrieved.length).toBeGreaterThan(0);
|
|
193
|
-
retrieved.forEach((ch) => expect(ch.name).toEqual("test"));
|
|
194
214
|
});
|
|
195
215
|
|
|
196
216
|
describe("delete", async () => {
|
package/src/channel/client.ts
CHANGED
|
@@ -101,6 +101,11 @@ export class Channel {
|
|
|
101
101
|
* should not be relied upon in the current version of Synnax.
|
|
102
102
|
*/
|
|
103
103
|
readonly alias: string | undefined;
|
|
104
|
+
/**
|
|
105
|
+
* Whether the channel is virtual. Virtual channels do not store any data in the
|
|
106
|
+
* database, but can still be used for streaming purposes.
|
|
107
|
+
*/
|
|
108
|
+
readonly virtual: boolean;
|
|
104
109
|
|
|
105
110
|
constructor({
|
|
106
111
|
dataType,
|
|
@@ -111,6 +116,7 @@ export class Channel {
|
|
|
111
116
|
isIndex = false,
|
|
112
117
|
index = 0,
|
|
113
118
|
internal = false,
|
|
119
|
+
virtual = false,
|
|
114
120
|
frameClient,
|
|
115
121
|
alias,
|
|
116
122
|
}: NewPayload & {
|
|
@@ -126,6 +132,7 @@ export class Channel {
|
|
|
126
132
|
this.isIndex = isIndex;
|
|
127
133
|
this.internal = internal;
|
|
128
134
|
this.alias = alias;
|
|
135
|
+
this.virtual = virtual;
|
|
129
136
|
this._frameClient = frameClient ?? null;
|
|
130
137
|
}
|
|
131
138
|
|
package/src/channel/payload.ts
CHANGED
package/src/client.ts
CHANGED
|
@@ -44,8 +44,8 @@ export const synnaxPropsZ = z.object({
|
|
|
44
44
|
required_error: "Port is required",
|
|
45
45
|
}),
|
|
46
46
|
),
|
|
47
|
-
username: z.string().
|
|
48
|
-
password: z.string().
|
|
47
|
+
username: z.string().min(1, "Username is required"),
|
|
48
|
+
password: z.string().min(1, "Password is required"),
|
|
49
49
|
connectivityPollFrequency: TimeSpan.z.default(TimeSpan.seconds(30)),
|
|
50
50
|
secure: z.boolean().optional().default(false),
|
|
51
51
|
name: z.string().optional(),
|
|
@@ -79,6 +79,11 @@ export default class Synnax extends framer.Client {
|
|
|
79
79
|
static readonly connectivity = connection.Checker;
|
|
80
80
|
private readonly transport: Transport;
|
|
81
81
|
|
|
82
|
+
/**
|
|
83
|
+
* The version of the client.
|
|
84
|
+
*/
|
|
85
|
+
readonly clientVersion: string = __VERSION__;
|
|
86
|
+
|
|
82
87
|
/**
|
|
83
88
|
* @param props.host - Hostname of a node in the cluster.
|
|
84
89
|
* @param props.port - Port of the node in the cluster.
|
|
@@ -101,10 +106,7 @@ export default class Synnax extends framer.Client {
|
|
|
101
106
|
transport.use(errorsMiddleware);
|
|
102
107
|
let auth_: auth.Client | undefined;
|
|
103
108
|
if (username != null && password != null) {
|
|
104
|
-
auth_ = new auth.Client(transport.unary, {
|
|
105
|
-
username,
|
|
106
|
-
password,
|
|
107
|
-
});
|
|
109
|
+
auth_ = new auth.Client(transport.unary, { username, password });
|
|
108
110
|
transport.use(auth_.middleware());
|
|
109
111
|
}
|
|
110
112
|
const chRetriever = new channel.CacheRetriever(
|
|
@@ -120,24 +122,31 @@ export default class Synnax extends framer.Client {
|
|
|
120
122
|
this.connectivity = new connection.Checker(
|
|
121
123
|
transport.unary,
|
|
122
124
|
connectivityPollFrequency,
|
|
125
|
+
this.clientVersion,
|
|
123
126
|
props.name,
|
|
124
127
|
);
|
|
125
128
|
this.control = new control.Client(this);
|
|
126
129
|
this.ontology = new ontology.Client(transport.unary, this);
|
|
127
130
|
const rangeWriter = new ranger.Writer(this.transport.unary);
|
|
128
|
-
this.labels = new label.Client(this.transport.unary, this);
|
|
131
|
+
this.labels = new label.Client(this.transport.unary, this, this.ontology);
|
|
129
132
|
this.ranges = new ranger.Client(
|
|
130
133
|
this,
|
|
131
134
|
rangeWriter,
|
|
132
135
|
this.transport.unary,
|
|
133
136
|
chRetriever,
|
|
134
137
|
this.labels,
|
|
138
|
+
this.ontology,
|
|
135
139
|
);
|
|
136
140
|
this.access = new access.Client(this.transport.unary);
|
|
137
141
|
this.user = new user.Client(this.transport.unary);
|
|
138
142
|
this.workspaces = new workspace.Client(this.transport.unary);
|
|
139
143
|
const devices = new device.Client(this.transport.unary, this);
|
|
140
|
-
const tasks = new task.Client(
|
|
144
|
+
const tasks = new task.Client(
|
|
145
|
+
this.transport.unary,
|
|
146
|
+
this,
|
|
147
|
+
this.ontology,
|
|
148
|
+
this.ranges,
|
|
149
|
+
);
|
|
141
150
|
const racks = new rack.Client(this.transport.unary, this, tasks);
|
|
142
151
|
this.hardware = new hardware.Client(tasks, racks, devices);
|
|
143
152
|
}
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
// included in the file licenses/APL.txt.
|
|
9
9
|
|
|
10
10
|
import type { UnaryClient } from "@synnaxlabs/freighter";
|
|
11
|
+
import { migrate } from "@synnaxlabs/x";
|
|
11
12
|
import { TimeSpan } from "@synnaxlabs/x/telem";
|
|
12
13
|
import { z } from "zod";
|
|
13
14
|
|
|
@@ -20,12 +21,16 @@ export const state = z.object({
|
|
|
20
21
|
error: z.instanceof(Error).optional(),
|
|
21
22
|
message: z.string().optional(),
|
|
22
23
|
clusterKey: z.string(),
|
|
24
|
+
clientVersion: z.string(),
|
|
25
|
+
clientServerCompatible: z.boolean(),
|
|
26
|
+
nodeVersion: z.string().optional(),
|
|
23
27
|
});
|
|
24
28
|
|
|
25
29
|
export type State = z.infer<typeof state>;
|
|
26
30
|
|
|
27
31
|
const responseZ = z.object({
|
|
28
32
|
clusterKey: z.string(),
|
|
33
|
+
nodeVersion: z.string().optional(),
|
|
29
34
|
});
|
|
30
35
|
|
|
31
36
|
const DEFAULT: State = {
|
|
@@ -33,6 +38,19 @@ const DEFAULT: State = {
|
|
|
33
38
|
status: "disconnected",
|
|
34
39
|
error: undefined,
|
|
35
40
|
message: "Disconnected",
|
|
41
|
+
clientServerCompatible: false,
|
|
42
|
+
clientVersion: __VERSION__,
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const generateWarning = (
|
|
46
|
+
nodeVersion: string | null,
|
|
47
|
+
clientVersion: string,
|
|
48
|
+
clientIsNewer: boolean,
|
|
49
|
+
): string => {
|
|
50
|
+
const toUpgrade = clientIsNewer ? "cluster" : "client";
|
|
51
|
+
return `Synnax cluster node version ${nodeVersion != null ? nodeVersion + " " : ""}is too ${clientIsNewer ? "old" : "new"} for client version ${clientVersion}.
|
|
52
|
+
This may cause compatibility issues. We recommend updating the ${toUpgrade}. For more information, see
|
|
53
|
+
https://docs.synnaxlabs.com/reference/typescript-client/troubleshooting#old-${toUpgrade}-version`;
|
|
36
54
|
};
|
|
37
55
|
|
|
38
56
|
/** Polls a synnax cluster for connectivity information. */
|
|
@@ -44,8 +62,10 @@ export class Checker {
|
|
|
44
62
|
private readonly client: UnaryClient;
|
|
45
63
|
private readonly name?: string;
|
|
46
64
|
private interval?: NodeJS.Timeout;
|
|
65
|
+
private readonly clientVersion: string;
|
|
47
66
|
private readonly onChangeHandlers: Array<(state: State) => void> = [];
|
|
48
67
|
static readonly connectionStateZ = state;
|
|
68
|
+
private versionWarned = false;
|
|
49
69
|
|
|
50
70
|
/**
|
|
51
71
|
* @param client - The transport client to use for connectivity checks.
|
|
@@ -55,11 +75,13 @@ export class Checker {
|
|
|
55
75
|
constructor(
|
|
56
76
|
client: UnaryClient,
|
|
57
77
|
pollFreq: TimeSpan = TimeSpan.seconds(30),
|
|
78
|
+
clientVersion: string,
|
|
58
79
|
name?: string,
|
|
59
80
|
) {
|
|
60
81
|
this._state = { ...DEFAULT };
|
|
61
82
|
this.client = client;
|
|
62
83
|
this.pollFrequency = pollFreq;
|
|
84
|
+
this.clientVersion = clientVersion;
|
|
63
85
|
this.name = name;
|
|
64
86
|
void this.check();
|
|
65
87
|
this.startChecking();
|
|
@@ -77,11 +99,46 @@ export class Checker {
|
|
|
77
99
|
async check(): Promise<State> {
|
|
78
100
|
const prevStatus = this._state.status;
|
|
79
101
|
try {
|
|
80
|
-
const [res, err] = await this.client.send(
|
|
102
|
+
const [res, err] = await this.client.send(
|
|
103
|
+
Checker.ENDPOINT,
|
|
104
|
+
{},
|
|
105
|
+
z.object({}),
|
|
106
|
+
responseZ,
|
|
107
|
+
);
|
|
81
108
|
if (err != null) throw err;
|
|
109
|
+
const nodeVersion = res.nodeVersion;
|
|
110
|
+
const clientVersion = this.clientVersion;
|
|
111
|
+
const warned = this.versionWarned;
|
|
112
|
+
if (nodeVersion == null) {
|
|
113
|
+
this._state.clientServerCompatible = false;
|
|
114
|
+
if (!warned) {
|
|
115
|
+
console.warn(generateWarning(null, clientVersion, true));
|
|
116
|
+
this.versionWarned = true;
|
|
117
|
+
}
|
|
118
|
+
} else if (
|
|
119
|
+
!migrate.versionsEqual(clientVersion, nodeVersion, {
|
|
120
|
+
checkMajor: true,
|
|
121
|
+
checkMinor: true,
|
|
122
|
+
checkPatch: false,
|
|
123
|
+
})
|
|
124
|
+
) {
|
|
125
|
+
this._state.clientServerCompatible = false;
|
|
126
|
+
if (!warned) {
|
|
127
|
+
console.warn(
|
|
128
|
+
generateWarning(
|
|
129
|
+
nodeVersion,
|
|
130
|
+
clientVersion,
|
|
131
|
+
migrate.semVerNewer(clientVersion, nodeVersion),
|
|
132
|
+
),
|
|
133
|
+
);
|
|
134
|
+
this.versionWarned = true;
|
|
135
|
+
}
|
|
136
|
+
} else this._state.clientServerCompatible = true;
|
|
82
137
|
this._state.status = "connected";
|
|
83
138
|
this._state.message = `Connected to ${this.name ?? "cluster"}`;
|
|
84
139
|
this._state.clusterKey = res.clusterKey;
|
|
140
|
+
this._state.nodeVersion = res.nodeVersion;
|
|
141
|
+
this._state.clientVersion = this.clientVersion;
|
|
85
142
|
} catch (err) {
|
|
86
143
|
this._state.status = "failed";
|
|
87
144
|
this._state.error = err as Error;
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
|
|
10
10
|
import { URL } from "@synnaxlabs/x/url";
|
|
11
11
|
import { describe, expect, it } from "vitest";
|
|
12
|
+
import { z } from "zod";
|
|
12
13
|
|
|
13
14
|
import { auth } from "@/auth";
|
|
14
15
|
import { Checker } from "@/connection/checker";
|
|
@@ -23,8 +24,47 @@ describe("connectivity", () => {
|
|
|
23
24
|
password: "seldon",
|
|
24
25
|
});
|
|
25
26
|
transport.use(client.middleware());
|
|
26
|
-
const connectivity = new Checker(transport.unary);
|
|
27
|
-
await connectivity.check();
|
|
28
|
-
expect(
|
|
27
|
+
const connectivity = new Checker(transport.unary, undefined, __VERSION__);
|
|
28
|
+
const state = await connectivity.check();
|
|
29
|
+
expect(state.status).toEqual("connected");
|
|
30
|
+
expect(z.string().uuid().safeParse(state.clusterKey).success).toBe(true);
|
|
31
|
+
});
|
|
32
|
+
describe("version compatibility", () => {
|
|
33
|
+
it("should pull the server and client versions", async () => {
|
|
34
|
+
const transport = new Transport(new URL({ host: HOST, port: PORT }));
|
|
35
|
+
const client = new auth.Client(transport.unary, {
|
|
36
|
+
username: "synnax",
|
|
37
|
+
password: "seldon",
|
|
38
|
+
});
|
|
39
|
+
transport.use(client.middleware());
|
|
40
|
+
const connectivity = new Checker(transport.unary, undefined, __VERSION__);
|
|
41
|
+
const state = await connectivity.check();
|
|
42
|
+
expect(state.clientServerCompatible).toBe(true);
|
|
43
|
+
expect(state.clientVersion).toBe(__VERSION__);
|
|
44
|
+
});
|
|
45
|
+
it("should adjust state if the server is too old", async () => {
|
|
46
|
+
const transport = new Transport(new URL({ host: HOST, port: PORT }));
|
|
47
|
+
const client = new auth.Client(transport.unary, {
|
|
48
|
+
username: "synnax",
|
|
49
|
+
password: "seldon",
|
|
50
|
+
});
|
|
51
|
+
transport.use(client.middleware());
|
|
52
|
+
const connectivity = new Checker(transport.unary, undefined, "50000.0.0");
|
|
53
|
+
const state = await connectivity.check();
|
|
54
|
+
expect(state.clientServerCompatible).toBe(false);
|
|
55
|
+
expect(state.clientVersion).toBe("50000.0.0");
|
|
56
|
+
});
|
|
57
|
+
it("should adjust state if the server is too new", async () => {
|
|
58
|
+
const transport = new Transport(new URL({ host: HOST, port: PORT }));
|
|
59
|
+
const client = new auth.Client(transport.unary, {
|
|
60
|
+
username: "synnax",
|
|
61
|
+
password: "seldon",
|
|
62
|
+
});
|
|
63
|
+
transport.use(client.middleware());
|
|
64
|
+
const connectivity = new Checker(transport.unary, undefined, "0.0.0");
|
|
65
|
+
const state = await connectivity.check();
|
|
66
|
+
expect(state.clientServerCompatible).toBe(false);
|
|
67
|
+
expect(state.clientVersion).toBe("0.0.0");
|
|
68
|
+
});
|
|
29
69
|
});
|
|
30
70
|
});
|
package/src/control/client.ts
CHANGED
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
// Copyright 2024 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
|
+
|
|
1
10
|
import { StateTracker } from "@/control/state";
|
|
2
11
|
import { framer } from "@/framer";
|
|
3
12
|
|
package/src/errors.spec.ts
CHANGED
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
// Copyright 2024 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
|
+
|
|
1
10
|
import { MatchableErrorType } from "@synnaxlabs/freighter/src/errors";
|
|
2
11
|
import { describe, expect, test } from "vitest";
|
|
3
12
|
|
package/src/framer/client.ts
CHANGED
package/src/framer/frame.spec.ts
CHANGED
|
@@ -156,7 +156,7 @@ describe("framer.Frame", () => {
|
|
|
156
156
|
});
|
|
157
157
|
|
|
158
158
|
describe("weaklyAligned", () => {
|
|
159
|
-
it("should return true if all keys have the same
|
|
159
|
+
it("should return true if all keys have the same time range", () => {
|
|
160
160
|
const f = new framer.Frame(
|
|
161
161
|
new Map([
|
|
162
162
|
[
|
|
@@ -182,7 +182,7 @@ describe("framer.Frame", () => {
|
|
|
182
182
|
expect(f.isWeaklyAligned).toEqual(true);
|
|
183
183
|
});
|
|
184
184
|
|
|
185
|
-
it("should return false if any key has a different
|
|
185
|
+
it("should return false if any key has a different time range", () => {
|
|
186
186
|
const f = new framer.Frame(
|
|
187
187
|
new Map([
|
|
188
188
|
[
|