@synnaxlabs/client 0.44.1 → 0.44.3

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.
Files changed (73) hide show
  1. package/.turbo/turbo-build.log +5 -5
  2. package/dist/channel/client.d.ts +11 -4
  3. package/dist/channel/client.d.ts.map +1 -1
  4. package/dist/channel/payload.d.ts +86 -14
  5. package/dist/channel/payload.d.ts.map +1 -1
  6. package/dist/channel/writer.d.ts.map +1 -1
  7. package/dist/client.cjs +24 -24
  8. package/dist/client.d.ts +2 -2
  9. package/dist/client.js +2953 -2600
  10. package/dist/framer/streamer.d.ts +98 -2
  11. package/dist/framer/streamer.d.ts.map +1 -1
  12. package/dist/framer/writer.d.ts +87 -7
  13. package/dist/framer/writer.d.ts.map +1 -1
  14. package/dist/hardware/device/payload.d.ts +4 -4
  15. package/dist/hardware/rack/payload.d.ts +4 -4
  16. package/dist/hardware/task/client.d.ts +7 -0
  17. package/dist/hardware/task/client.d.ts.map +1 -1
  18. package/dist/index.d.ts +1 -1
  19. package/dist/index.d.ts.map +1 -1
  20. package/dist/ontology/payload.d.ts +1 -0
  21. package/dist/ontology/payload.d.ts.map +1 -1
  22. package/dist/ranger/alias.d.ts +5 -1
  23. package/dist/ranger/alias.d.ts.map +1 -1
  24. package/dist/ranger/client.d.ts +5 -1
  25. package/dist/ranger/client.d.ts.map +1 -1
  26. package/dist/ranger/external.d.ts +1 -1
  27. package/dist/ranger/external.d.ts.map +1 -1
  28. package/dist/ranger/kv.d.ts +1 -0
  29. package/dist/ranger/kv.d.ts.map +1 -1
  30. package/dist/ranger/payload.d.ts +3 -7
  31. package/dist/ranger/payload.d.ts.map +1 -1
  32. package/dist/ranger/writer.d.ts +1 -1
  33. package/dist/testutil/client.d.ts +1 -1
  34. package/dist/testutil/client.d.ts.map +1 -1
  35. package/dist/workspace/payload.d.ts +1 -1
  36. package/package.json +3 -3
  37. package/src/access/policy/policy.spec.ts +3 -3
  38. package/src/channel/channel.spec.ts +2 -2
  39. package/src/channel/client.ts +21 -5
  40. package/src/channel/payload.ts +4 -4
  41. package/src/channel/writer.ts +4 -1
  42. package/src/control/state.spec.ts +2 -2
  43. package/src/errors.spec.ts +2 -2
  44. package/src/framer/adapter.spec.ts +2 -2
  45. package/src/framer/client.spec.ts +2 -2
  46. package/src/framer/deleter.spec.ts +2 -2
  47. package/src/framer/iterator.spec.ts +2 -2
  48. package/src/framer/streamer.spec.ts +3 -3
  49. package/src/framer/streamer.ts +4 -3
  50. package/src/framer/writer.spec.ts +3 -2
  51. package/src/framer/writer.ts +26 -16
  52. package/src/hardware/device/device.spec.ts +2 -2
  53. package/src/hardware/rack/rack.spec.ts +2 -2
  54. package/src/hardware/task/client.ts +93 -20
  55. package/src/hardware/task/task.spec.ts +2 -2
  56. package/src/index.ts +1 -1
  57. package/src/label/label.spec.ts +2 -2
  58. package/src/ontology/group/group.spec.ts +2 -2
  59. package/src/ontology/ontology.spec.ts +2 -2
  60. package/src/ontology/payload.ts +3 -0
  61. package/src/ranger/alias.ts +27 -1
  62. package/src/ranger/client.ts +30 -3
  63. package/src/ranger/external.ts +1 -9
  64. package/src/ranger/kv.ts +3 -0
  65. package/src/ranger/payload.ts +4 -2
  66. package/src/ranger/ranger.spec.ts +3 -3
  67. package/src/testutil/client.ts +8 -1
  68. package/src/user/user.spec.ts +2 -2
  69. package/src/workspace/lineplot/lineplot.spec.ts +2 -2
  70. package/src/workspace/log/log.spec.ts +2 -2
  71. package/src/workspace/schematic/schematic.spec.ts +2 -2
  72. package/src/workspace/table/table.spec.ts +2 -2
  73. package/src/workspace/workspace.spec.ts +2 -2
@@ -8,7 +8,7 @@
8
8
  // included in the file licenses/APL.txt.
9
9
 
10
10
  import { sendRequired, type UnaryClient } from "@synnaxlabs/freighter";
11
- import { caseconv, id } from "@synnaxlabs/x";
11
+ import { caseconv, id, strings } from "@synnaxlabs/x";
12
12
  import { array } from "@synnaxlabs/x/array";
13
13
  import { type CrudeTimeSpan, TimeSpan } from "@synnaxlabs/x/telem";
14
14
  import { z } from "zod";
@@ -409,7 +409,17 @@ export class Client {
409
409
  return isSingle ? res[0] : res;
410
410
  }
411
411
 
412
- async executeCommand(task: Key, type: string, args?: {}): Promise<string> {
412
+ async executeCommand(task: Key, type: string, args?: {}): Promise<string>;
413
+
414
+ async executeCommand(commands: NewCommand[]): Promise<string[]>;
415
+
416
+ async executeCommand(
417
+ task: Key | NewCommand[],
418
+ type?: string,
419
+ args?: {},
420
+ ): Promise<string | string[]> {
421
+ if (Array.isArray(task)) return await executeCommands(this.frameClient, task);
422
+ if (type == null) throw new Error("Type is required");
413
423
  return await executeCommand(this.frameClient, task, type, args);
414
424
  }
415
425
 
@@ -419,8 +429,35 @@ export class Client {
419
429
  timeout: CrudeTimeSpan,
420
430
  args?: {},
421
431
  name?: string,
432
+ statusDataZ?: StatusData,
433
+ ): Promise<Status<StatusData>>;
434
+ async executeCommandSync<StatusData extends z.ZodType = z.ZodType>(
435
+ commands: NewCommand[],
436
+ timeout: CrudeTimeSpan,
437
+ statusDataZ?: StatusData,
438
+ ): Promise<Status<StatusData>[]>;
439
+
440
+ async executeCommandSync<StatusData extends z.ZodType = z.ZodType>(
441
+ task: Key | NewCommand[],
442
+ type: string | CrudeTimeSpan,
443
+ timeout?: CrudeTimeSpan | StatusData,
444
+ args?: {},
445
+ name?: string,
422
446
  statusDataZ: StatusData = z.unknown() as unknown as StatusData,
423
- ): Promise<Status<StatusData>> {
447
+ ): Promise<Status<StatusData> | Status<StatusData>[]> {
448
+ if (Array.isArray(task)) {
449
+ const retrieveNames = async () => {
450
+ const ts = await this.retrieve({ keys: task.map((t) => t.task) });
451
+ return ts.map((t) => t.name);
452
+ };
453
+ return await executeCommandsSync(
454
+ this.frameClient,
455
+ task,
456
+ type as CrudeTimeSpan,
457
+ statusDataZ,
458
+ retrieveNames,
459
+ );
460
+ }
424
461
  const retrieveName = async () => {
425
462
  const t = await this.retrieve({ key: task });
426
463
  return t.name;
@@ -428,8 +465,8 @@ export class Client {
428
465
  return await executeCommandSync(
429
466
  this.frameClient,
430
467
  task,
431
- type,
432
- timeout,
468
+ type as string,
469
+ timeout as CrudeTimeSpan,
433
470
  name ?? retrieveName,
434
471
  statusDataZ,
435
472
  args,
@@ -444,13 +481,24 @@ const executeCommand = async (
444
481
  task: Key,
445
482
  type: string,
446
483
  args?: {},
447
- ): Promise<string> => {
484
+ ): Promise<string> => (await executeCommands(frameClient, [{ args, task, type }]))[0];
485
+
486
+ export interface NewCommand {
487
+ task: Key;
488
+ type: string;
489
+ args?: {};
490
+ }
491
+
492
+ const executeCommands = async (
493
+ frameClient: framer.Client | null,
494
+ commands: NewCommand[],
495
+ ): Promise<string[]> => {
448
496
  if (frameClient == null) throw NOT_CREATED_ERROR;
449
- const key = id.create();
450
497
  const w = await frameClient.openWriter(COMMAND_CHANNEL_NAME);
451
- await w.write(COMMAND_CHANNEL_NAME, [{ args, key, task, type }]);
498
+ const cmds = commands.map((c) => ({ ...c, key: id.create() }));
499
+ await w.write(COMMAND_CHANNEL_NAME, cmds);
452
500
  await w.close();
453
- return key;
501
+ return cmds.map((c) => c.key);
454
502
  };
455
503
 
456
504
  const executeCommandSync = async <StatusData extends z.ZodType = z.ZodType>(
@@ -461,24 +509,45 @@ const executeCommandSync = async <StatusData extends z.ZodType = z.ZodType>(
461
509
  tskName: string | (() => Promise<string>),
462
510
  statusDataZ: StatusData,
463
511
  args?: {},
464
- ): Promise<Status<StatusData>> => {
512
+ ): Promise<Status<StatusData>> =>
513
+ (
514
+ await executeCommandsSync(
515
+ frameClient,
516
+ [{ args, task, type }],
517
+ timeout,
518
+ statusDataZ,
519
+ tskName,
520
+ )
521
+ )[0];
522
+
523
+ const executeCommandsSync = async <StatusData extends z.ZodType = z.ZodType>(
524
+ frameClient: framer.Client | null,
525
+ commands: NewCommand[],
526
+ timeout: CrudeTimeSpan,
527
+ statusDataZ: StatusData,
528
+ tskName: string | string[] | (() => Promise<string | string[]>),
529
+ ): Promise<Status<StatusData>[]> => {
465
530
  if (frameClient == null) throw NOT_CREATED_ERROR;
466
531
  const streamer = await frameClient.openStreamer(STATUS_CHANNEL_NAME);
467
- const cmdKey = await executeCommand(frameClient, task, type, args);
532
+ const cmdKeys = await executeCommands(frameClient, commands);
468
533
  const parsedTimeout = new TimeSpan(timeout);
469
-
534
+ let states: Status<StatusData>[] = [];
470
535
  let timeoutID: NodeJS.Timeout | undefined;
471
536
  const timeoutPromise = new Promise<never>((_, reject) => {
472
537
  timeoutID = setTimeout(() => {
473
- void (async () =>
474
- reject(await formatTimeoutError(type, tskName, parsedTimeout, task)))();
538
+ void (async () => {
539
+ const taskKeys = commands.map((c) => c.task);
540
+ reject(await formatTimeoutError("command", tskName, parsedTimeout, taskKeys));
541
+ })();
475
542
  }, parsedTimeout.milliseconds);
476
543
  });
477
544
  try {
478
545
  while (true) {
479
546
  const frame = await Promise.race([streamer.read(), timeoutPromise]);
480
547
  const state = statusZ(statusDataZ).parse(frame.at(-1)[STATUS_CHANNEL_NAME]);
481
- if (state.key === cmdKey) return state;
548
+ if (!cmdKeys.includes(state.key)) continue;
549
+ states = [...states.filter((s) => s.key !== state.key), state];
550
+ if (states.length === cmdKeys.length) return states;
482
551
  }
483
552
  } finally {
484
553
  clearTimeout(timeoutID);
@@ -488,21 +557,25 @@ const executeCommandSync = async <StatusData extends z.ZodType = z.ZodType>(
488
557
 
489
558
  const formatTimeoutError = async (
490
559
  type: string,
491
- name: string | (() => Promise<string>),
560
+ name: string | string[] | (() => Promise<string | string[]>),
492
561
  timeout: TimeSpan,
493
- key: Key,
562
+ key: Key | Key[],
494
563
  ): Promise<Error> => {
495
564
  const formattedType = caseconv.capitalize(type);
496
565
  const formattedTimeout = timeout.toString();
497
566
  try {
498
- const name_ = typeof name === "string" ? name : await name();
567
+ let names: string[];
568
+ if (typeof name === "string") names = [name];
569
+ else if (Array.isArray(name)) names = name;
570
+ else names = array.toArray(await name());
571
+ const formattedName = strings.naturalLanguageJoin(names);
499
572
  return new Error(
500
- `${formattedType} command to ${name_} timed out after ${formattedTimeout}`,
573
+ `${formattedType} command to ${formattedName} timed out after ${formattedTimeout}`,
501
574
  );
502
575
  } catch (e) {
503
576
  console.error("Failed to retrieve task name for timeout error:", e);
504
577
  return new Error(
505
- `${formattedType} command to task with key ${key} timed out after ${formattedTimeout}`,
578
+ `${formattedType} command to task with key ${strings.naturalLanguageJoin(key)} timed out after ${formattedTimeout}`,
506
579
  );
507
580
  }
508
581
  };
@@ -11,9 +11,9 @@ import { id, TimeStamp } from "@synnaxlabs/x";
11
11
  import { beforeAll, describe, expect, it } from "vitest";
12
12
 
13
13
  import { task } from "@/hardware/task";
14
- import { newTestClient } from "@/testutil/client";
14
+ import { createTestClient } from "@/testutil/client";
15
15
 
16
- const client = newTestClient();
16
+ const client = createTestClient();
17
17
 
18
18
  describe("Task", async () => {
19
19
  const testRack = await client.hardware.racks.create({ name: "test" });
package/src/index.ts CHANGED
@@ -35,7 +35,7 @@ export { label } from "@/label";
35
35
  export { ontology } from "@/ontology";
36
36
  export { group } from "@/ontology/group";
37
37
  export { ranger } from "@/ranger";
38
- export { newTestClient, TEST_CLIENT_PROPS } from "@/testutil/client";
38
+ export { createTestClient, TEST_CLIENT_PROPS } from "@/testutil/client";
39
39
  export { user } from "@/user";
40
40
  export { workspace } from "@/workspace";
41
41
  export { linePlot } from "@/workspace/lineplot";
@@ -10,9 +10,9 @@
10
10
  import { describe, expect, it } from "vitest";
11
11
 
12
12
  import { label } from "@/label";
13
- import { newTestClient } from "@/testutil/client";
13
+ import { createTestClient } from "@/testutil/client";
14
14
 
15
- const client = newTestClient();
15
+ const client = createTestClient();
16
16
 
17
17
  describe("Label", () => {
18
18
  describe("create", () => {
@@ -11,9 +11,9 @@ import { describe, expect, it } from "vitest";
11
11
 
12
12
  import { NotFoundError } from "@/errors";
13
13
  import { ontology } from "@/ontology";
14
- import { newTestClient } from "@/testutil/client";
14
+ import { createTestClient } from "@/testutil/client";
15
15
 
16
- const client = newTestClient();
16
+ const client = createTestClient();
17
17
 
18
18
  describe("Group", () => {
19
19
  describe("create", () => {
@@ -10,9 +10,9 @@
10
10
  import { describe, expect, it, test } from "vitest";
11
11
 
12
12
  import { ontology } from "@/ontology";
13
- import { newTestClient } from "@/testutil/client";
13
+ import { createTestClient } from "@/testutil/client";
14
14
 
15
- const client = newTestClient();
15
+ const client = createTestClient();
16
16
 
17
17
  const randomName = (): string => `group-${Math.random()}`;
18
18
 
@@ -96,6 +96,9 @@ export const relationshipZ = z.object({ from: idZ, type: z.string(), to: idZ }).
96
96
  );
97
97
  export type Relationship = z.infer<typeof relationshipZ>;
98
98
 
99
+ export const relationshipToString = (relationship: Relationship) =>
100
+ `${idToString(relationship.from)}->${relationship.type}->${idToString(relationship.to)}`;
101
+
99
102
  export const PARENT_OF_RELATIONSHIP_TYPE = "parent";
100
103
 
101
104
  export interface MatchRelationshipArgs {
@@ -8,6 +8,7 @@
8
8
  // included in the file licenses/APL.txt.
9
9
 
10
10
  import { sendRequired, type UnaryClient } from "@synnaxlabs/freighter";
11
+ import { array } from "@synnaxlabs/x";
11
12
  import { type change } from "@synnaxlabs/x/change";
12
13
  import { z } from "zod";
13
14
 
@@ -37,10 +38,15 @@ const listReqZ = z.object({ range: keyZ });
37
38
 
38
39
  const listResZ = z.object({ aliases: z.record(z.string(), z.string()) });
39
40
 
41
+ const retrieveReqZ = z.object({ range: keyZ, channels: channel.keyZ.array() });
42
+
43
+ const retrieveResZ = z.object({ aliases: z.record(z.string(), z.string()) });
44
+
40
45
  export class Aliaser {
41
46
  private static readonly SET_ENDPOINT = "/range/alias/set";
42
47
  private static readonly RESOLVE_ENDPOINT = "/range/alias/resolve";
43
48
  private static readonly LIST_ENDPOINT = "/range/alias/list";
49
+ private static readonly RETRIEVE_ENDPOINT = "/range/alias/retrieve";
44
50
  private static readonly DELETE_ENDPOINT = "/range/alias/delete";
45
51
  private readonly frameClient: framer.Client;
46
52
  private readonly cache = new Map<string, channel.Key>();
@@ -108,6 +114,23 @@ export class Aliaser {
108
114
  ).aliases;
109
115
  }
110
116
 
117
+ async retrieve(alias: channel.Key): Promise<string>;
118
+ async retrieve(aliases: channel.Key[]): Promise<Record<channel.Key, string>>;
119
+
120
+ async retrieve(
121
+ alias: channel.Key | channel.Key[],
122
+ ): Promise<string | Record<channel.Key, string>> {
123
+ const isSingle = typeof alias === "number";
124
+ const res = await sendRequired<typeof retrieveReqZ, typeof retrieveResZ>(
125
+ this.client,
126
+ Aliaser.RETRIEVE_ENDPOINT,
127
+ { range: this.rangeKey, channels: array.toArray(alias) },
128
+ retrieveReqZ,
129
+ retrieveResZ,
130
+ );
131
+ return isSingle ? res.aliases[alias] : res.aliases;
132
+ }
133
+
111
134
  async delete(aliases: channel.Key[]): Promise<void> {
112
135
  await sendRequired<typeof deleteReqZ, typeof deleteResZ>(
113
136
  this.client,
@@ -120,7 +143,7 @@ export class Aliaser {
120
143
  }
121
144
 
122
145
  export const aliasZ = z.object({
123
- alias: z.string(),
146
+ alias: z.string().optional(),
124
147
  channel: channel.keyZ,
125
148
  range: keyZ,
126
149
  });
@@ -130,6 +153,9 @@ export type AliasChange = change.Change<string, Alias>;
130
153
 
131
154
  const SEPARATOR = "---";
132
155
 
156
+ export const aliasKey = (alias: Pick<Alias, "range" | "channel">): string =>
157
+ `${alias.range}${SEPARATOR}${alias.channel}`;
158
+
133
159
  export interface DecodedDeleteAliasChange {
134
160
  range: Key;
135
161
  channel: channel.Key;
@@ -54,7 +54,7 @@ export class Range {
54
54
  readonly timeRange: TimeRange;
55
55
  readonly color: string | undefined;
56
56
  readonly parent: Payload | null;
57
- readonly labels: label.Label[];
57
+ readonly labels?: label.Label[];
58
58
  readonly channels: channel.Retriever;
59
59
  private readonly aliaser: Aliaser;
60
60
  private readonly frameClient: framer.Client;
@@ -63,7 +63,7 @@ export class Range {
63
63
  private readonly rangeClient: Client;
64
64
 
65
65
  constructor(
66
- { name, timeRange = TimeRange.ZERO, key, color, parent, labels = [] }: Payload,
66
+ { name, timeRange = TimeRange.ZERO, key, color, parent, labels }: Payload,
67
67
  {
68
68
  frameClient,
69
69
  kv,
@@ -94,13 +94,17 @@ export class Range {
94
94
  }
95
95
 
96
96
  get payload(): Payload {
97
+ let parent: Payload | null = null;
98
+ if (this.parent != null)
99
+ if ("payload" in this.parent) parent = (this.parent as Range).payload;
100
+ else parent = this.parent;
97
101
  return {
98
102
  key: this.key,
99
103
  name: this.name,
100
104
  timeRange: this.timeRange,
101
105
  color: this.color,
102
106
  labels: this.labels,
103
- parent: this.parent,
107
+ parent,
104
108
  };
105
109
  }
106
110
 
@@ -268,6 +272,29 @@ export class Client {
268
272
  return this.sugarOne(convertOntologyResourceToPayload(resource));
269
273
  }
270
274
 
275
+ async retrieveAlias(range: Key, channel: channel.Key): Promise<string> {
276
+ const aliaser = new Aliaser(range, this.frameClient, this.unaryClient);
277
+ return await aliaser.retrieve(channel);
278
+ }
279
+
280
+ async retrieveAliases(
281
+ range: Key,
282
+ channels: channel.Key[],
283
+ ): Promise<Record<channel.Key, string>> {
284
+ const aliaser = new Aliaser(range, this.frameClient, this.unaryClient);
285
+ return await aliaser.retrieve(channels);
286
+ }
287
+
288
+ async listAliases(range: Key): Promise<Record<channel.Key, string>> {
289
+ const aliaser = new Aliaser(range, this.frameClient, this.unaryClient);
290
+ return await aliaser.list();
291
+ }
292
+
293
+ async setAlias(range: Key, channel: channel.Key, alias: string): Promise<void> {
294
+ const aliaser = new Aliaser(range, this.frameClient, this.unaryClient);
295
+ await aliaser.set({ [channel]: alias });
296
+ }
297
+
271
298
  sugarOne(payload: Payload): Range {
272
299
  return new Range(payload, {
273
300
  frameClient: this.frameClient,
@@ -7,15 +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
- export {
11
- type Alias,
12
- type AliasChange,
13
- aliasZ,
14
- type DecodedDeleteAliasChange,
15
- decodeDeleteAliasChange,
16
- DELETE_ALIAS_CHANNEL_NAME,
17
- SET_ALIAS_CHANNEL_NAME,
18
- } from "@/ranger/alias";
10
+ export * from "@/ranger/alias";
19
11
  export * from "@/ranger/client";
20
12
  export * from "@/ranger/kv";
21
13
  export * from "@/ranger/payload";
package/src/ranger/kv.ts CHANGED
@@ -21,6 +21,9 @@ export const KV_DELETE_CHANNEL = "sy_range_kv_delete";
21
21
  export const kvPairZ = z.object({ range: keyZ, key: z.string(), value: z.string() });
22
22
  export interface KVPair extends z.infer<typeof kvPairZ> {}
23
23
 
24
+ export const kvPairKey = ({ range, key }: Omit<KVPair, "value">) =>
25
+ `${range}<--->${key}`;
26
+
24
27
  const getReqZ = z.object({ range: keyZ, keys: z.string().array() });
25
28
  export interface GetRequest extends z.infer<typeof getReqZ> {}
26
29
 
@@ -11,7 +11,6 @@ import { TimeRange } from "@synnaxlabs/x/telem";
11
11
  import { z } from "zod";
12
12
 
13
13
  import { label } from "@/label";
14
- import { nullableArrayZ } from "@/util/zod";
15
14
 
16
15
  export const keyZ = z.uuid();
17
16
  export type Key = z.infer<typeof keyZ>;
@@ -26,7 +25,10 @@ export const payloadZ = z.object({
26
25
  name: nameZ,
27
26
  timeRange: TimeRange.z,
28
27
  color: z.string().optional(),
29
- labels: nullableArrayZ(label.labelZ),
28
+ labels: label.labelZ
29
+ .array()
30
+ .or(z.null().transform(() => undefined))
31
+ .optional(),
30
32
  get parent(): z.ZodUnion<readonly [z.ZodNull, typeof payloadZ]> {
31
33
  // Using as unknown is bad, but unfortunately resolving the output type of this
32
34
  // transform is nearly impossible.
@@ -12,9 +12,9 @@ import { describe, expect, it } from "vitest";
12
12
 
13
13
  import { NotFoundError } from "@/errors";
14
14
  import { type ranger } from "@/ranger";
15
- import { newTestClient } from "@/testutil/client";
15
+ import { createTestClient } from "@/testutil/client";
16
16
 
17
- const client = newTestClient();
17
+ const client = createTestClient();
18
18
 
19
19
  describe("Ranger", () => {
20
20
  describe("create", () => {
@@ -194,7 +194,7 @@ describe("Ranger", () => {
194
194
  includeLabels: true,
195
195
  });
196
196
  expect(newRange[0].labels).toHaveLength(1);
197
- expect(newRange[0].labels[0]).toEqual(label);
197
+ expect(newRange[0].labels?.[0]).toEqual(label);
198
198
  });
199
199
  });
200
200
 
@@ -7,6 +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 { TimeSpan } from "@synnaxlabs/x";
11
+
10
12
  import Synnax, { type SynnaxProps } from "@/client";
11
13
 
12
14
  export const TEST_CLIENT_PROPS: SynnaxProps = {
@@ -14,7 +16,12 @@ export const TEST_CLIENT_PROPS: SynnaxProps = {
14
16
  port: 9090,
15
17
  username: "synnax",
16
18
  password: "seldon",
19
+ retry: {
20
+ maxRetries: 4,
21
+ baseInterval: TimeSpan.seconds(1),
22
+ scale: 1.5,
23
+ },
17
24
  };
18
25
 
19
- export const newTestClient = (props?: Partial<SynnaxProps>): Synnax =>
26
+ export const createTestClient = (props?: Partial<SynnaxProps>): Synnax =>
20
27
  new Synnax({ ...TEST_CLIENT_PROPS, ...props });
@@ -11,7 +11,7 @@ import { id } from "@synnaxlabs/x";
11
11
  import { describe, expect, test } from "vitest";
12
12
 
13
13
  import { AuthError, NotFoundError } from "@/errors";
14
- import { newTestClient } from "@/testutil/client";
14
+ import { createTestClient } from "@/testutil/client";
15
15
  import { type user } from "@/user";
16
16
 
17
17
  interface SortType {
@@ -20,7 +20,7 @@ interface SortType {
20
20
 
21
21
  const sort = (a: SortType, b: SortType) => a.username.localeCompare(b.username);
22
22
 
23
- const client = newTestClient();
23
+ const client = createTestClient();
24
24
 
25
25
  const userOne: user.New = {
26
26
  username: id.create(),
@@ -10,9 +10,9 @@
10
10
  import { uuid } from "@synnaxlabs/x";
11
11
  import { describe, expect, test } from "vitest";
12
12
 
13
- import { newTestClient } from "@/testutil/client";
13
+ import { createTestClient } from "@/testutil/client";
14
14
 
15
- const client = newTestClient();
15
+ const client = createTestClient();
16
16
 
17
17
  describe("LinePlot", () => {
18
18
  describe("create", () => {
@@ -10,9 +10,9 @@
10
10
  import { uuid } from "@synnaxlabs/x";
11
11
  import { describe, expect, test } from "vitest";
12
12
 
13
- import { newTestClient } from "@/testutil/client";
13
+ import { createTestClient } from "@/testutil/client";
14
14
 
15
- const client = newTestClient();
15
+ const client = createTestClient();
16
16
 
17
17
  describe("Log", () => {
18
18
  describe("create", () => {
@@ -11,9 +11,9 @@ import { uuid } from "@synnaxlabs/x";
11
11
  import { describe, expect, it, test } from "vitest";
12
12
 
13
13
  import { ValidationError } from "@/errors";
14
- import { newTestClient } from "@/testutil/client";
14
+ import { createTestClient } from "@/testutil/client";
15
15
 
16
- const client = newTestClient();
16
+ const client = createTestClient();
17
17
 
18
18
  describe("Schematic", () => {
19
19
  describe("create", () => {
@@ -10,9 +10,9 @@
10
10
  import { uuid } from "@synnaxlabs/x";
11
11
  import { describe, expect, test } from "vitest";
12
12
 
13
- import { newTestClient } from "@/testutil/client";
13
+ import { createTestClient } from "@/testutil/client";
14
14
 
15
- const client = newTestClient();
15
+ const client = createTestClient();
16
16
 
17
17
  describe("Table", () => {
18
18
  describe("create", () => {
@@ -10,9 +10,9 @@
10
10
  import { uuid } from "@synnaxlabs/x";
11
11
  import { describe, expect, test } from "vitest";
12
12
 
13
- import { newTestClient } from "@/testutil/client";
13
+ import { createTestClient } from "@/testutil/client";
14
14
 
15
- const client = newTestClient();
15
+ const client = createTestClient();
16
16
 
17
17
  describe("Workspace", () => {
18
18
  describe("create", () => {