@synnaxlabs/client 0.15.0 → 0.15.2

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 (48) hide show
  1. package/.turbo/turbo-build.log +5 -5
  2. package/dist/client.cjs.js +8 -8
  3. package/dist/client.cjs.js.map +1 -1
  4. package/dist/client.es.js +2921 -2798
  5. package/dist/client.es.js.map +1 -1
  6. package/dist/control/authority.d.ts +2 -2
  7. package/dist/framer/client.d.ts +1 -1
  8. package/dist/framer/writer.d.ts +20 -7
  9. package/dist/hardware/device/client.d.ts +5 -2
  10. package/dist/hardware/device/retriever.d.ts +2 -0
  11. package/dist/hardware/external.d.ts +0 -1
  12. package/dist/hardware/rack/client.d.ts +7 -3
  13. package/dist/hardware/rack/retriever.d.ts +2 -0
  14. package/dist/hardware/task/client.d.ts +5 -2
  15. package/dist/hardware/task/payload.d.ts +4 -3
  16. package/dist/hardware/task/retriever.d.ts +8 -0
  17. package/dist/index.d.ts +3 -0
  18. package/dist/workspace/lineplot/payload.d.ts +3 -2
  19. package/dist/workspace/lineplot/writer.d.ts +2 -2
  20. package/dist/workspace/payload.d.ts +3 -2
  21. package/dist/workspace/pid/payload.d.ts +3 -2
  22. package/dist/workspace/pid/writer.d.ts +2 -2
  23. package/dist/workspace/writer.d.ts +3 -3
  24. package/package.json +5 -5
  25. package/src/client.ts +1 -0
  26. package/src/control/authority.ts +2 -2
  27. package/src/framer/adapter.ts +14 -7
  28. package/src/framer/client.ts +9 -3
  29. package/src/framer/writer.spec.ts +31 -3
  30. package/src/framer/writer.ts +23 -5
  31. package/src/hardware/device/client.ts +22 -3
  32. package/src/hardware/device/device.spec.ts +9 -0
  33. package/src/hardware/device/external.ts +9 -0
  34. package/src/hardware/device/index.ts +10 -1
  35. package/src/hardware/device/payload.ts +9 -0
  36. package/src/hardware/device/retriever.ts +22 -1
  37. package/src/hardware/device/writer.ts +9 -0
  38. package/src/hardware/external.ts +0 -1
  39. package/src/hardware/rack/client.ts +20 -4
  40. package/src/hardware/rack/external.ts +10 -1
  41. package/src/hardware/rack/index.ts +10 -1
  42. package/src/hardware/rack/payload.ts +10 -1
  43. package/src/hardware/rack/rack.spec.ts +9 -0
  44. package/src/hardware/rack/retriever.ts +34 -1
  45. package/src/hardware/task/client.ts +13 -3
  46. package/src/hardware/task/payload.ts +2 -0
  47. package/src/hardware/task/retriever.ts +22 -0
  48. package/src/index.ts +3 -0
@@ -1,6 +1,6 @@
1
1
  import { z } from "zod";
2
2
  export declare class Authority extends Number {
3
- static readonly ABSOLUTE = 255;
4
- static readonly DEFAULT = 1;
3
+ static readonly Absolute = 255;
4
+ static readonly Default = 1;
5
5
  static readonly z: z.ZodUnion<[z.ZodType<Authority, z.ZodTypeDef, Authority>, z.ZodEffects<z.ZodNumber, Authority, number>, z.ZodEffects<z.ZodType<Number, z.ZodTypeDef, Number>, Authority, Number>]>;
6
6
  }
@@ -26,7 +26,7 @@ export declare class Client {
26
26
  * for more information.
27
27
  * @returns a new {@link RecordWriter}.
28
28
  */
29
- newWriter({ start, channels, controlSubject, authorities, }: WriterConfig): Promise<Writer>;
29
+ newWriter({ start, channels, controlSubject, authorities, mode, }: WriterConfig): Promise<Writer>;
30
30
  newStreamer(params: Params, from?: TimeStamp): Promise<Streamer>;
31
31
  /**
32
32
  * Writes telemetry to the given channel starting at the given timestamp.
@@ -11,7 +11,13 @@ declare enum Command {
11
11
  Write = 1,
12
12
  Commit = 2,
13
13
  Error = 3,
14
- SetAuthority = 4
14
+ SetAuthority = 4,
15
+ SetMode = 5
16
+ }
17
+ export declare enum WriterMode {
18
+ PersistStream = 1,
19
+ PersistOnly = 2,
20
+ StreamOnly = 3
15
21
  }
16
22
  declare const reqZ: z.ZodObject<{
17
23
  command: z.ZodNativeEnum<typeof Command>;
@@ -27,24 +33,27 @@ declare const reqZ: z.ZodObject<{
27
33
  name: string;
28
34
  key: string;
29
35
  }>>;
30
- keys: z.ZodArray<z.ZodNumber, "many">;
36
+ keys: z.ZodOptional<z.ZodArray<z.ZodNumber, "many">>;
31
37
  authorities: z.ZodOptional<z.ZodArray<z.ZodUnion<[z.ZodType<Authority, z.ZodTypeDef, Authority>, z.ZodEffects<z.ZodNumber, Authority, number>, z.ZodEffects<z.ZodType<Number, z.ZodTypeDef, Number>, Authority, Number>]>, "many">>;
38
+ mode: z.ZodOptional<z.ZodNativeEnum<typeof WriterMode>>;
32
39
  }, "strip", z.ZodTypeAny, {
33
- keys: number[];
34
40
  start?: TimeStamp | undefined;
35
41
  controlSubject?: {
36
42
  name: string;
37
43
  key: string;
38
44
  } | undefined;
45
+ keys?: number[] | undefined;
39
46
  authorities?: Authority[] | undefined;
47
+ mode?: WriterMode | undefined;
40
48
  }, {
41
- keys: number[];
42
49
  start?: number | Number | TimeStamp | undefined;
43
50
  controlSubject?: {
44
51
  name: string;
45
52
  key: string;
46
53
  } | undefined;
54
+ keys?: number[] | undefined;
47
55
  authorities?: (number | Number | Authority)[] | undefined;
56
+ mode?: WriterMode | undefined;
48
57
  }>>;
49
58
  frame: z.ZodOptional<z.ZodObject<{
50
59
  keys: z.ZodUnion<[z.ZodEffects<z.ZodNull, number[], null>, z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodNumber, "many">>>]>;
@@ -107,13 +116,14 @@ declare const reqZ: z.ZodObject<{
107
116
  }, "strip", z.ZodTypeAny, {
108
117
  command: Command;
109
118
  config?: {
110
- keys: number[];
111
119
  start?: TimeStamp | undefined;
112
120
  controlSubject?: {
113
121
  name: string;
114
122
  key: string;
115
123
  } | undefined;
124
+ keys?: number[] | undefined;
116
125
  authorities?: Authority[] | undefined;
126
+ mode?: WriterMode | undefined;
117
127
  } | undefined;
118
128
  frame?: {
119
129
  keys: number[];
@@ -127,13 +137,14 @@ declare const reqZ: z.ZodObject<{
127
137
  }, {
128
138
  command: Command;
129
139
  config?: {
130
- keys: number[];
131
140
  start?: number | Number | TimeStamp | undefined;
132
141
  controlSubject?: {
133
142
  name: string;
134
143
  key: string;
135
144
  } | undefined;
145
+ keys?: number[] | undefined;
136
146
  authorities?: (number | Number | Authority)[] | undefined;
147
+ mode?: WriterMode | undefined;
137
148
  } | undefined;
138
149
  frame?: {
139
150
  keys?: number[] | null | undefined;
@@ -183,6 +194,7 @@ export interface WriterConfig {
183
194
  channels: Params;
184
195
  controlSubject?: ControlSubject;
185
196
  authorities?: Authority | Authority[];
197
+ mode?: WriterMode;
186
198
  }
187
199
  /**
188
200
  * Writer is used to write telemetry to a set of channels in time order.
@@ -227,10 +239,11 @@ export declare class Writer {
227
239
  private readonly stream;
228
240
  private readonly adapter;
229
241
  private constructor();
230
- static _open(retriever: Retriever, client: StreamClient, { channels, authorities, controlSubject: subject, start, }: WriterConfig): Promise<Writer>;
242
+ static _open(retriever: Retriever, client: StreamClient, { channels, authorities, controlSubject: subject, start, mode, }: WriterConfig): Promise<Writer>;
231
243
  write(channel: KeyOrName, data: NativeTypedArray): Promise<boolean>;
232
244
  write(frame: CrudeFrame): Promise<boolean>;
233
245
  setAuthority(value: Record<Key, Authority>): Promise<boolean>;
246
+ setMode(mode: WriterMode): Promise<boolean>;
234
247
  /**
235
248
  * Commits the written frames to the database. Commit is synchronous, meaning that it
236
249
  * will not return until all frames have been commited to the database.
@@ -1,9 +1,10 @@
1
+ import { AsyncTermSearcher } from "@synnaxlabs/x";
1
2
  import { type framer } from '../../framer';
2
- import { type Device } from './payload';
3
+ import { type Device, DeviceKey } from './payload';
3
4
  import { type Retriever } from './retriever';
4
5
  import { type Writer } from './writer';
5
6
  import { signals } from '../../signals';
6
- export declare class Client {
7
+ export declare class Client implements AsyncTermSearcher<string, DeviceKey, Device> {
7
8
  private readonly retriever;
8
9
  private readonly writer;
9
10
  private readonly frameClient;
@@ -11,6 +12,8 @@ export declare class Client {
11
12
  create(device: Device): Promise<Device>;
12
13
  retrieve(key: string): Promise<Device>;
13
14
  retrieve(keys: string[]): Promise<Device[]>;
15
+ search(term: string): Promise<Device[]>;
16
+ page(offset: number, limit: number): Promise<Device[]>;
14
17
  delete(keys: string[]): Promise<void>;
15
18
  openDeviceTracker(): Promise<signals.Observable<string, Device>>;
16
19
  }
@@ -3,5 +3,7 @@ import { type Device } from './payload';
3
3
  export declare class Retriever {
4
4
  private readonly client;
5
5
  constructor(client: UnaryClient);
6
+ search(term: string): Promise<Device[]>;
7
+ page(offset: number, limit: number): Promise<Device[]>;
6
8
  retrieve(keys: string[]): Promise<Device[]>;
7
9
  }
@@ -1,2 +1 @@
1
- export * from './rack';
2
1
  export * from './client';
@@ -1,11 +1,12 @@
1
1
  import { type framer } from '../../framer';
2
- import { type NewRack } from './payload';
2
+ import { RackKey, type NewRack } from './payload';
3
3
  import { type Retriever } from './retriever';
4
4
  import { type Writer } from './writer';
5
5
  import { type NewTask, type Task } from '../task/payload';
6
6
  import { type Retriever as TaskRetriever } from '../task/retriever';
7
7
  import { type Writer as TaskWriter } from '../task/writer';
8
- export declare class Client {
8
+ import { AsyncTermSearcher } from "@synnaxlabs/x";
9
+ export declare class Client implements AsyncTermSearcher<string, RackKey, Rack> {
9
10
  private readonly retriever;
10
11
  private readonly writer;
11
12
  private readonly frameClient;
@@ -13,7 +14,10 @@ export declare class Client {
13
14
  private readonly taskRetriever;
14
15
  constructor(retriever: Retriever, writer: Writer, frameClient: framer.Client, taskWriter: TaskWriter, taskRetriever: TaskRetriever);
15
16
  create(rack: NewRack): Promise<Rack>;
16
- retrieve(key: number): Promise<Rack>;
17
+ search(term: string): Promise<Rack[]>;
18
+ page(offset: number, limit: number): Promise<Rack[]>;
19
+ retrieve(key: RackKey): Promise<Rack>;
20
+ retrieve(keys: RackKey[]): Promise<Rack[]>;
17
21
  private sugar;
18
22
  }
19
23
  export declare class Rack {
@@ -3,5 +3,7 @@ import { type RackPayload } from './payload';
3
3
  export declare class Retriever {
4
4
  private readonly client;
5
5
  constructor(client: UnaryClient);
6
+ page(offset: number, limit: number): Promise<RackPayload[]>;
7
+ search(term: string): Promise<RackPayload[]>;
6
8
  retrieve(keys: number[]): Promise<RackPayload[]>;
7
9
  }
@@ -1,10 +1,13 @@
1
- import { type NewTask, type Task } from './payload';
1
+ import { AsyncTermSearcher } from "@synnaxlabs/x";
2
+ import { TaskKey, type NewTask, type Task } from './payload';
2
3
  import { type Retriever } from './retriever';
3
4
  import { type Writer } from './writer';
4
- export declare class Client {
5
+ export declare class Client implements AsyncTermSearcher<string, TaskKey, Task> {
5
6
  private readonly retriever;
6
7
  private readonly writer;
7
8
  constructor(retriever: Retriever, writer: Writer);
9
+ search(term: string): Promise<Task[]>;
10
+ page(offset: number, limit: number): Promise<Task[]>;
8
11
  create(task: NewTask): Promise<Task>;
9
12
  retrieve(rack: number): Promise<Task[]>;
10
13
  retrieve(keys: string[]): Promise<Task[]>;
@@ -1,21 +1,22 @@
1
1
  import { type UnknownRecord } from "@synnaxlabs/x";
2
2
  import { z } from "zod";
3
3
  export declare const taskKeyZ: z.ZodEffects<z.ZodUnion<[z.ZodBigInt, z.ZodNumber]>, string, number | bigint>;
4
+ export type TaskKey = z.infer<typeof taskKeyZ>;
4
5
  export declare const taskZ: z.ZodObject<{
5
6
  key: z.ZodEffects<z.ZodUnion<[z.ZodBigInt, z.ZodNumber]>, string, number | bigint>;
6
7
  name: z.ZodString;
7
8
  type: z.ZodString;
8
- config: z.ZodType<Partial<Record<import("@synnaxlabs/x").Key, unknown>>, z.ZodTypeDef, Partial<Record<import("@synnaxlabs/x").Key, unknown>>>;
9
+ config: z.ZodType<UnknownRecord, z.ZodTypeDef, UnknownRecord>;
9
10
  }, "strip", z.ZodTypeAny, {
10
11
  name: string;
11
12
  key: string;
12
13
  type: string;
13
- config: Partial<Record<import("@synnaxlabs/x").Key, unknown>>;
14
+ config: UnknownRecord;
14
15
  }, {
15
16
  name: string;
16
17
  key: number | bigint;
17
18
  type: string;
18
- config: Partial<Record<import("@synnaxlabs/x").Key, unknown>>;
19
+ config: UnknownRecord;
19
20
  }>;
20
21
  export declare const newTaskZ: z.ZodObject<{
21
22
  name: z.ZodString;
@@ -4,17 +4,25 @@ import { type Task } from './payload';
4
4
  declare const retrieveReqZ: z.ZodObject<{
5
5
  rack: z.ZodOptional<z.ZodNumber>;
6
6
  keys: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
7
+ offset: z.ZodOptional<z.ZodNumber>;
8
+ limit: z.ZodOptional<z.ZodNumber>;
7
9
  }, "strip", z.ZodTypeAny, {
8
10
  rack?: number | undefined;
9
11
  keys?: string[] | undefined;
12
+ offset?: number | undefined;
13
+ limit?: number | undefined;
10
14
  }, {
11
15
  rack?: number | undefined;
12
16
  keys?: string[] | undefined;
17
+ offset?: number | undefined;
18
+ limit?: number | undefined;
13
19
  }>;
14
20
  export type RetrieveRequest = z.infer<typeof retrieveReqZ>;
15
21
  export declare class Retriever {
16
22
  private readonly client;
17
23
  constructor(client: UnaryClient);
18
24
  retrieve(params: RetrieveRequest): Promise<Task[]>;
25
+ search(term: string): Promise<Task[]>;
26
+ page(offset: number, limit: number): Promise<Task[]>;
19
27
  }
20
28
  export {};
package/dist/index.d.ts CHANGED
@@ -15,3 +15,6 @@ export { workspace } from './workspace';
15
15
  export { ranger } from './ranger';
16
16
  export { label } from './label';
17
17
  export { hardware } from './hardware';
18
+ export { rack } from './hardware/rack';
19
+ export { task } from './hardware/task';
20
+ export { device } from './hardware/device';
@@ -1,3 +1,4 @@
1
+ import { type UnknownRecord } from "@synnaxlabs/x";
1
2
  import { z } from "zod";
2
3
  export declare const keyZ: z.ZodString;
3
4
  export type Key = z.infer<typeof keyZ>;
@@ -18,11 +19,11 @@ export declare const linePlotZ: z.ZodObject<{
18
19
  export declare const linePlotRemoteZ: z.ZodObject<{
19
20
  key: z.ZodString;
20
21
  name: z.ZodString;
21
- data: z.ZodEffects<z.ZodString, Partial<Record<import("@synnaxlabs/x").Key, unknown>>, string>;
22
+ data: z.ZodEffects<z.ZodString, UnknownRecord, string>;
22
23
  }, "strip", z.ZodTypeAny, {
23
24
  name: string;
24
25
  key: string;
25
- data: Partial<Record<import("@synnaxlabs/x").Key, unknown>>;
26
+ data: UnknownRecord;
26
27
  }, {
27
28
  name: string;
28
29
  key: string;
@@ -18,10 +18,10 @@ export declare const crudeLinePlotZ: z.ZodObject<{
18
18
  export declare const linePlotWriteZ: z.ZodObject<{
19
19
  name: z.ZodString;
20
20
  key: z.ZodOptional<z.ZodString>;
21
- data: z.ZodEffects<z.ZodString, Partial<Record<import("@synnaxlabs/x").Key, unknown>>, string>;
21
+ data: z.ZodEffects<z.ZodString, UnknownRecord, string>;
22
22
  }, "strip", z.ZodTypeAny, {
23
23
  name: string;
24
- data: Partial<Record<import("@synnaxlabs/x").Key, unknown>>;
24
+ data: UnknownRecord;
25
25
  key?: string | undefined;
26
26
  }, {
27
27
  name: string;
@@ -1,3 +1,4 @@
1
+ import { type UnknownRecord } from "@synnaxlabs/x";
1
2
  import { z } from "zod";
2
3
  export declare const keyZ: z.ZodString;
3
4
  export type Key = z.infer<typeof keyZ>;
@@ -18,11 +19,11 @@ export declare const workspaceZ: z.ZodObject<{
18
19
  export declare const workspaceRemoteZ: z.ZodObject<{
19
20
  name: z.ZodString;
20
21
  key: z.ZodString;
21
- layout: z.ZodEffects<z.ZodString, Partial<Record<import("@synnaxlabs/x").Key, unknown>>, string>;
22
+ layout: z.ZodEffects<z.ZodString, UnknownRecord, string>;
22
23
  }, "strip", z.ZodTypeAny, {
23
24
  name: string;
24
25
  key: string;
25
- layout: Partial<Record<import("@synnaxlabs/x").Key, unknown>>;
26
+ layout: UnknownRecord;
26
27
  }, {
27
28
  name: string;
28
29
  key: string;
@@ -1,3 +1,4 @@
1
+ import { type UnknownRecord } from "@synnaxlabs/x";
1
2
  import { z } from "zod";
2
3
  export declare const keyZ: z.ZodString;
3
4
  export type Key = z.infer<typeof keyZ>;
@@ -22,11 +23,11 @@ export declare const pidRemoteZ: z.ZodObject<{
22
23
  key: z.ZodString;
23
24
  name: z.ZodString;
24
25
  snapshot: z.ZodBoolean;
25
- data: z.ZodEffects<z.ZodString, Partial<Record<import("@synnaxlabs/x").Key, unknown>>, string>;
26
+ data: z.ZodEffects<z.ZodString, UnknownRecord, string>;
26
27
  }, "strip", z.ZodTypeAny, {
27
28
  name: string;
28
29
  key: string;
29
- data: Partial<Record<import("@synnaxlabs/x").Key, unknown>>;
30
+ data: UnknownRecord;
30
31
  snapshot: boolean;
31
32
  }, {
32
33
  name: string;
@@ -21,11 +21,11 @@ export declare const crudePIDz: z.ZodObject<{
21
21
  export declare const pidWriteZ: z.ZodObject<{
22
22
  name: z.ZodString;
23
23
  key: z.ZodOptional<z.ZodString>;
24
- data: z.ZodEffects<z.ZodString, Partial<Record<import("@synnaxlabs/x").Key, unknown>>, string>;
24
+ data: z.ZodEffects<z.ZodString, UnknownRecord, string>;
25
25
  snapshot: z.ZodOptional<z.ZodBoolean>;
26
26
  }, "strip", z.ZodTypeAny, {
27
27
  name: string;
28
- data: Partial<Record<import("@synnaxlabs/x").Key, unknown>>;
28
+ data: UnknownRecord;
29
29
  key?: string | undefined;
30
30
  snapshot?: boolean | undefined;
31
31
  }, {
@@ -20,11 +20,11 @@ declare const createResZ: z.ZodObject<{
20
20
  workspaces: z.ZodArray<z.ZodObject<{
21
21
  name: z.ZodString;
22
22
  key: z.ZodString;
23
- layout: z.ZodEffects<z.ZodString, Partial<Record<import("@synnaxlabs/x").Key, unknown>>, string>;
23
+ layout: z.ZodEffects<z.ZodString, UnknownRecord, string>;
24
24
  }, "strip", z.ZodTypeAny, {
25
25
  name: string;
26
26
  key: string;
27
- layout: Partial<Record<import("@synnaxlabs/x").Key, unknown>>;
27
+ layout: UnknownRecord;
28
28
  }, {
29
29
  name: string;
30
30
  key: string;
@@ -34,7 +34,7 @@ declare const createResZ: z.ZodObject<{
34
34
  workspaces: {
35
35
  name: string;
36
36
  key: string;
37
- layout: Partial<Record<import("@synnaxlabs/x").Key, unknown>>;
37
+ layout: UnknownRecord;
38
38
  }[];
39
39
  }, {
40
40
  workspaces: {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@synnaxlabs/client",
3
3
  "private": false,
4
- "version": "0.15.0",
4
+ "version": "0.15.2",
5
5
  "type": "module",
6
6
  "description": "The Client Library for Synnax",
7
7
  "repository": "https://github.com/synnaxlabs/synnax/tree/main/client/ts",
@@ -17,8 +17,8 @@
17
17
  ],
18
18
  "dependencies": {
19
19
  "zod": "3.22.4",
20
- "@synnaxlabs/freighter": "0.7.0",
21
- "@synnaxlabs/x": "0.10.0"
20
+ "@synnaxlabs/freighter": "0.7.1",
21
+ "@synnaxlabs/x": "0.12.0"
22
22
  },
23
23
  "devDependencies": {
24
24
  "@types/node": "^20.10.5",
@@ -27,8 +27,8 @@
27
27
  "vite": "^5.1.2",
28
28
  "vitest": "^1.2.2",
29
29
  "@synnaxlabs/vite-plugin": "0.0.1",
30
- "@synnaxlabs/tsconfig": "0.0.2",
31
- "eslint-config-synnaxlabs": "0.0.1"
30
+ "eslint-config-synnaxlabs": "0.0.1",
31
+ "@synnaxlabs/tsconfig": "0.0.2"
32
32
  },
33
33
  "main": "dist/client.cjs.js",
34
34
  "module": "dist/client.es.js",
package/src/client.ts CHANGED
@@ -24,6 +24,7 @@ import { hardware } from "./hardware";
24
24
  import { device } from "./hardware/device";
25
25
  import { rack } from "./hardware/rack";
26
26
  import { task } from "./hardware/task";
27
+ import { randomUUID } from "crypto";
27
28
 
28
29
  export const synnaxPropsZ = z.object({
29
30
  host: z.string().min(1),
@@ -10,8 +10,8 @@
10
10
  import { z } from "zod";
11
11
 
12
12
  export class Authority extends Number {
13
- static readonly ABSOLUTE = 255;
14
- static readonly DEFAULT = 1;
13
+ static readonly Absolute = 255;
14
+ static readonly Default = 1;
15
15
 
16
16
  static readonly z = z.union([
17
17
  z.instanceof(Authority),
@@ -9,6 +9,7 @@
9
9
 
10
10
  import { type Key, type Name, type Params } from "@/channel/payload";
11
11
  import { type Retriever, analyzeParams } from "@/channel/retriever";
12
+ import { ValidationError } from "@/errors";
12
13
  import { type Frame } from "@/framer/frame";
13
14
 
14
15
  export class BackwardFrameAdapter {
@@ -95,22 +96,28 @@ export class ForwardFrameAdapter {
95
96
  this.adapter = a;
96
97
  normalized.forEach((name) => {
97
98
  const channel = fetched.find((channel) => channel.name === name);
98
- if (channel == null) throw new Error(`Channel ${name} not found`);
99
+ if (channel == null) throw new ValidationError(`Channel ${name} was not provided in the list of channels when opening the writer`);
99
100
  a.set(channel.name, channel.key);
100
101
  });
101
102
  this.keys = fetched.map((c) => c.key);
102
103
  }
103
104
 
104
105
  adapt(fr: Frame): Frame {
105
- if (this.adapter == null) return fr;
106
+ if (this.adapter == null) {
107
+ // assert that every col if of type number
108
+ fr.columns.forEach((col) => {
109
+ if (typeof col !== "number") throw new ValidationError(`Channel ${col} was not provided in the list of channels when opening the writer`);
110
+ });
111
+ return fr;
112
+ }
106
113
  const a = this.adapter;
107
- return fr.map((k, arr) => {
108
- if (typeof k === "string") {
109
- const key = a.get(k);
110
- if (key == null) throw new Error(`Channel ${k} not found`);
114
+ return fr.map((col, arr) => {
115
+ if (typeof col === "string") {
116
+ const key = a.get(col);
117
+ if (key == null) throw new Error(`Channel ${col} not found`);
111
118
  return [key, arr];
112
119
  }
113
- return [k, arr];
120
+ return [col, arr];
114
121
  });
115
122
  }
116
123
  }
@@ -22,7 +22,7 @@ import { Authority } from "@/control/authority";
22
22
  import { Frame } from "@/framer/frame";
23
23
  import { Iterator } from "@/framer/iterator";
24
24
  import { Streamer } from "@/framer/streamer";
25
- import { Writer, type WriterConfig } from "@/framer/writer";
25
+ import { Writer, WriterMode, type WriterConfig } from "@/framer/writer";
26
26
 
27
27
  export class Client {
28
28
  private readonly stream: StreamClient;
@@ -56,13 +56,15 @@ export class Client {
56
56
  start,
57
57
  channels,
58
58
  controlSubject,
59
- authorities = Authority.ABSOLUTE,
59
+ authorities = Authority.Absolute,
60
+ mode = WriterMode.PersistStream,
60
61
  }: WriterConfig): Promise<Writer> {
61
62
  return await Writer._open(this.retriever, this.stream, {
62
63
  start: start ?? TimeStamp.now(),
63
64
  controlSubject,
64
65
  channels,
65
66
  authorities,
67
+ mode,
66
68
  });
67
69
  }
68
70
 
@@ -87,7 +89,11 @@ export class Client {
87
89
  start: CrudeTimeStamp,
88
90
  data: NativeTypedArray,
89
91
  ): Promise<void> {
90
- const w = await this.newWriter({ start, channels: to });
92
+ const w = await this.newWriter({
93
+ start,
94
+ channels: to,
95
+ mode: WriterMode.PersistOnly,
96
+ });
91
97
  try {
92
98
  await w.write(to, data);
93
99
  if (!(await w.commit())) throw (await w.error()) as Error;
@@ -11,19 +11,19 @@ import { DataType, Rate, TimeRange, TimeStamp } from "@synnaxlabs/x";
11
11
  import { describe, expect, test } from "vitest";
12
12
 
13
13
  import { type channel } from "@/channel";
14
+ import { WriterMode } from "@/framer/writer";
14
15
  import { newClient } from "@/setupspecs";
15
16
  import { randomSeries } from "@/util/telem";
16
17
 
17
18
  const client = newClient();
18
19
 
19
- const newChannel = async (): Promise<channel.Channel> => {
20
- return await client.channels.create({
20
+ const newChannel = async (): Promise<channel.Channel> =>
21
+ await client.channels.create({
21
22
  name: "test",
22
23
  leaseholder: 1,
23
24
  rate: Rate.hz(1),
24
25
  dataType: DataType.FLOAT64,
25
26
  });
26
- };
27
27
 
28
28
  describe("Writer", () => {
29
29
  describe("Writer", () => {
@@ -38,6 +38,34 @@ describe("Writer", () => {
38
38
  }
39
39
  expect(true).toBeTruthy();
40
40
  });
41
+ test("write to unknown channel key", async () => {
42
+ const ch = await newChannel();
43
+ const writer = await client.telem.newWriter({ start: 0, channels: ch.key });
44
+ await expect(
45
+ writer.write("billy bob", randomSeries(10, DataType.FLOAT64)),
46
+ ).rejects.toThrow("Channel billy bob was not provided");
47
+ await writer.close();
48
+ });
49
+ test("stream when mode is set ot persist only", async () => {
50
+ const ch = await newChannel();
51
+ const stream = await client.telem.newStreamer(ch.key);
52
+ const writer = await client.telem.newWriter({
53
+ start: 0,
54
+ channels: ch.key,
55
+ mode: WriterMode.PersistOnly,
56
+ });
57
+ try {
58
+ await writer.write(ch.key, randomSeries(10, ch.dataType));
59
+ } finally {
60
+ await writer.close();
61
+ }
62
+ // Simulating a timeout.
63
+ const v = await Promise.race([
64
+ stream.read(),
65
+ new Promise((resolve) => setTimeout(() => resolve(123), 250)),
66
+ ]);
67
+ expect(v).toEqual(123);
68
+ });
41
69
  });
42
70
  describe("Client", () => {
43
71
  test("Client - basic write", async () => {
@@ -36,13 +36,21 @@ enum Command {
36
36
  Commit = 2,
37
37
  Error = 3,
38
38
  SetAuthority = 4,
39
+ SetMode = 5,
40
+ }
41
+
42
+ export enum WriterMode {
43
+ PersistStream = 1,
44
+ PersistOnly = 2,
45
+ StreamOnly = 3,
39
46
  }
40
47
 
41
48
  const netConfigZ = z.object({
42
49
  start: TimeStamp.z.optional(),
43
50
  controlSubject: controlSubjectZ.optional(),
44
- keys: z.number().array(),
51
+ keys: z.number().array().optional(),
45
52
  authorities: Authority.z.array().optional(),
53
+ mode: z.nativeEnum(WriterMode).optional(),
46
54
  });
47
55
 
48
56
  const reqZ = z.object({
@@ -66,6 +74,7 @@ export interface WriterConfig {
66
74
  channels: Params;
67
75
  controlSubject?: ControlSubject;
68
76
  authorities?: Authority | Authority[];
77
+ mode?: WriterMode;
69
78
  }
70
79
 
71
80
  /**
@@ -124,9 +133,10 @@ export class Writer {
124
133
  client: StreamClient,
125
134
  {
126
135
  channels,
127
- authorities = Authority.ABSOLUTE,
136
+ authorities = Authority.Absolute,
128
137
  controlSubject: subject,
129
138
  start,
139
+ mode,
130
140
  }: WriterConfig,
131
141
  ): Promise<Writer> {
132
142
  const adapter = await ForwardFrameAdapter.open(retriever, channels);
@@ -139,6 +149,7 @@ export class Writer {
139
149
  keys: adapter.keys,
140
150
  controlSubject: subject,
141
151
  authorities: toArray(authorities),
152
+ mode,
142
153
  },
143
154
  });
144
155
  return writer;
@@ -167,9 +178,8 @@ export class Writer {
167
178
  data?: NativeTypedArray,
168
179
  ): Promise<boolean> {
169
180
  const isKeyOrName = ["string", "number"].includes(typeof frame);
170
- if (isKeyOrName) {
171
- frame = new Frame(frame, new Series({data: data as NativeTypedArray}));
172
- }
181
+ if (isKeyOrName)
182
+ frame = new Frame(frame, new Series({ data: data as NativeTypedArray }));
173
183
  frame = this.adapter.adapt(new Frame(frame));
174
184
  // @ts-expect-error
175
185
  this.stream.send({ command: Command.Write, frame: frame.toPayload() });
@@ -187,6 +197,14 @@ export class Writer {
187
197
  return res.ack;
188
198
  }
189
199
 
200
+ async setMode(mode: WriterMode): Promise<boolean> {
201
+ const res = await this.execute({
202
+ command: Command.SetMode,
203
+ config: { mode },
204
+ });
205
+ return res.ack;
206
+ }
207
+
190
208
  /**
191
209
  * Commits the written frames to the database. Commit is synchronous, meaning that it
192
210
  * will not return until all frames have been commited to the database.