@synnaxlabs/client 0.40.0 → 0.41.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.
@@ -27,7 +27,6 @@ describe("Streamer", () => {
27
27
  test("happy path", async () => {
28
28
  const ch = await newChannel();
29
29
  const streamer = await client.openStreamer(ch.key);
30
- await new Promise((resolve) => setTimeout(resolve, 100));
31
30
  const writer = await client.openWriter({
32
31
  start: TimeStamp.now(),
33
32
  channels: ch.key,
@@ -56,7 +55,6 @@ describe("Streamer", () => {
56
55
  channels: ch.key,
57
56
  downsampleFactor: 1,
58
57
  });
59
- await new Promise((resolve) => setTimeout(resolve, 100));
60
58
  const writer = await client.openWriter({
61
59
  start: TimeStamp.now(),
62
60
  channels: ch.key,
@@ -75,7 +73,6 @@ describe("Streamer", () => {
75
73
  channels: ch.key,
76
74
  downsampleFactor: 2,
77
75
  });
78
- await new Promise((resolve) => setTimeout(resolve, 100));
79
76
  const writer = await client.openWriter({
80
77
  start: TimeStamp.now(),
81
78
  channels: ch.key,
@@ -94,7 +91,6 @@ describe("Streamer", () => {
94
91
  channels: ch.key,
95
92
  downsampleFactor: 10,
96
93
  });
97
- await new Promise((resolve) => setTimeout(resolve, 100));
98
94
  const writer = await client.openWriter({
99
95
  start: TimeStamp.now(),
100
96
  channels: ch.key,
@@ -145,9 +141,6 @@ describe("Streamer - Calculated Channels", () => {
145
141
  // Set up streamer to listen for calculated results
146
142
  const streamer = await client.openStreamer(calcChannel.key);
147
143
 
148
- // Give streamer time to initialize
149
- await new Promise((resolve) => setTimeout(resolve, 100));
150
-
151
144
  // Write test data
152
145
  const startTime = TimeStamp.now();
153
146
  const writer = await client.openWriter({
@@ -200,7 +193,6 @@ describe("Streamer - Calculated Channels", () => {
200
193
  });
201
194
 
202
195
  const streamer = await client.openStreamer(calcChannel.key);
203
- await new Promise((resolve) => setTimeout(resolve, 100));
204
196
 
205
197
  const startTime = TimeStamp.now();
206
198
  const writer = await client.openWriter({
@@ -254,7 +246,6 @@ describe("Streamer - Calculated Channels", () => {
254
246
  });
255
247
 
256
248
  const streamer = await client.openStreamer(calcChannel.key);
257
- await new Promise((resolve) => setTimeout(resolve, 100));
258
249
 
259
250
  const startTime = TimeStamp.now();
260
251
  const writer = await client.openWriter({
@@ -12,7 +12,7 @@ import { toArray, type UnknownRecord } from "@synnaxlabs/x";
12
12
  import { type AsyncTermSearcher } from "@synnaxlabs/x/search";
13
13
  import { z } from "zod";
14
14
 
15
- import { type framer } from "@/framer";
15
+ import { framer } from "@/framer";
16
16
  import {
17
17
  type Device,
18
18
  deviceZ,
@@ -21,7 +21,10 @@ import {
21
21
  type New,
22
22
  newZ,
23
23
  ONTOLOGY_TYPE,
24
+ type State,
25
+ stateZ,
24
26
  } from "@/hardware/device/payload";
27
+ import { keyZ as rackKeyZ } from "@/hardware/rack/payload";
25
28
  import { ontology } from "@/ontology";
26
29
  import { signals } from "@/signals";
27
30
  import { checkForMultipleOrNoResults } from "@/util/retrieve";
@@ -29,6 +32,7 @@ import { nullableArrayZ } from "@/util/zod";
29
32
 
30
33
  const SET_CHANNEL_NAME = "sy_device_set";
31
34
  const DELETE_CHANNEL_NAME = "sy_device_delete";
35
+ const STATE_CHANNEL_NAME = "sy_device_state";
32
36
 
33
37
  const RETRIEVE_ENDPOINT = "/hardware/device/retrieve";
34
38
  const CREATE_ENDPOINT = "/hardware/device/create";
@@ -43,19 +47,26 @@ const deleteReqZ = z.object({ keys: keyZ.array() });
43
47
  const deleteResZ = z.object({});
44
48
 
45
49
  const retrieveReqZ = z.object({
46
- search: z.string().optional(),
47
- limit: z.number().optional(),
48
- offset: z.number().optional(),
49
50
  keys: keyZ.array().optional(),
50
51
  names: z.string().array().optional(),
51
52
  makes: z.string().array().optional(),
53
+ models: z.string().array().optional(),
54
+ locations: z.string().array().optional(),
55
+ racks: rackKeyZ.array().optional(),
56
+ search: z.string().optional(),
57
+ limit: z.number().optional(),
58
+ offset: z.number().optional(),
52
59
  ignoreNotFound: z.boolean().optional(),
60
+ includeState: z.boolean().optional(),
53
61
  });
54
62
 
55
63
  interface RetrieveRequest extends z.input<typeof retrieveReqZ> {}
56
64
 
57
65
  export interface RetrieveOptions
58
- extends Pick<RetrieveRequest, "limit" | "offset" | "makes" | "ignoreNotFound"> {}
66
+ extends Pick<
67
+ RetrieveRequest,
68
+ "limit" | "offset" | "makes" | "ignoreNotFound" | "includeState"
69
+ > {}
59
70
 
60
71
  interface PageOptions extends Pick<RetrieveOptions, "makes"> {}
61
72
 
@@ -75,26 +86,33 @@ export class Client implements AsyncTermSearcher<string, Key, Device> {
75
86
  Properties extends UnknownRecord = UnknownRecord,
76
87
  Make extends string = string,
77
88
  Model extends string = string,
78
- >(key: string, options?: RetrieveOptions): Promise<Device<Properties, Make, Model>>;
89
+ StateDetails extends {} = UnknownRecord,
90
+ >(
91
+ key: string,
92
+ options?: RetrieveOptions,
93
+ ): Promise<Device<Properties, Make, Model, StateDetails>>;
79
94
 
80
95
  async retrieve<
81
96
  Properties extends UnknownRecord = UnknownRecord,
82
97
  Make extends string = string,
83
98
  Model extends string = string,
99
+ StateDetails extends {} = UnknownRecord,
84
100
  >(
85
101
  keys: string[],
86
102
  options?: RetrieveOptions,
87
- ): Promise<Array<Device<Properties, Make, Model>>>;
103
+ ): Promise<Array<Device<Properties, Make, Model, StateDetails>>>;
88
104
 
89
105
  async retrieve<
90
106
  Properties extends UnknownRecord = UnknownRecord,
91
107
  Make extends string = string,
92
108
  Model extends string = string,
109
+ StateDetails extends {} = UnknownRecord,
93
110
  >(
94
111
  keys: string | string[],
95
112
  options?: RetrieveOptions,
96
113
  ): Promise<
97
- Device<Properties, Make, Model> | Array<Device<Properties, Make, Model>> | null
114
+ | Device<Properties, Make, Model, StateDetails>
115
+ | Array<Device<Properties, Make, Model, StateDetails>>
98
116
  > {
99
117
  const isSingle = !Array.isArray(keys);
100
118
  const res = await sendRequired(
@@ -106,8 +124,8 @@ export class Client implements AsyncTermSearcher<string, Key, Device> {
106
124
  );
107
125
  checkForMultipleOrNoResults("Device", keys, res.devices, isSingle);
108
126
  return isSingle
109
- ? (res.devices[0] as Device<Properties, Make, Model>)
110
- : (res.devices as Array<Device<Properties, Make, Model>>);
127
+ ? (res.devices[0] as Device<Properties, Make, Model, StateDetails>)
128
+ : (res.devices as Array<Device<Properties, Make, Model, StateDetails>>);
111
129
  }
112
130
 
113
131
  async search(term: string, options?: RetrieveOptions): Promise<Device[]> {
@@ -183,6 +201,20 @@ export class Client implements AsyncTermSearcher<string, Key, Device> {
183
201
  );
184
202
  }
185
203
 
204
+ async openStateObserver<Details extends {} = UnknownRecord>(): Promise<
205
+ framer.ObservableStreamer<State<Details>[]>
206
+ > {
207
+ return new framer.ObservableStreamer<State<Details>[]>(
208
+ await this.frameClient.openStreamer(STATE_CHANNEL_NAME),
209
+ (frame) => {
210
+ const s = frame.get(STATE_CHANNEL_NAME);
211
+ if (s.length === 0) return [null, false];
212
+ const states = s.parseJSON(stateZ);
213
+ return [states as State<Details>[], true];
214
+ },
215
+ );
216
+ }
217
+
186
218
  newSearcherWithOptions(
187
219
  options: RetrieveOptions,
188
220
  ): AsyncTermSearcher<string, Key, Device> {
@@ -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 { id } from "@synnaxlabs/x";
10
+ import { id, type UnknownRecord } from "@synnaxlabs/x";
11
11
  import { describe, expect, it } from "vitest";
12
12
 
13
13
  import { NotFoundError } from "@/errors";
@@ -108,5 +108,113 @@ describe("Device", async () => {
108
108
  }),
109
109
  ).rejects.toThrow(NotFoundError);
110
110
  });
111
+
112
+ describe("state", () => {
113
+ it("should not include state by default", async () => {
114
+ const d = await client.hardware.devices.create({
115
+ key: "SN_STATE_TEST1",
116
+ rack: testRack.key,
117
+ location: "Dev1",
118
+ name: "state_test1",
119
+ make: "ni",
120
+ model: "dog",
121
+ properties: { cat: "dog" },
122
+ });
123
+
124
+ const retrieved = await client.hardware.devices.retrieve(d.key);
125
+ expect(retrieved.state?.key).toHaveLength(0);
126
+ });
127
+
128
+ it("should include state when includeState is true", async () => {
129
+ const d = await client.hardware.devices.create({
130
+ key: "SN_STATE_TEST2",
131
+ rack: testRack.key,
132
+ location: "Dev1",
133
+ name: "state_test2",
134
+ make: "ni",
135
+ model: "dog",
136
+ properties: { cat: "dog" },
137
+ });
138
+
139
+ const retrieved = await client.hardware.devices.retrieve(d.key, {
140
+ includeState: true,
141
+ });
142
+ expect(retrieved.state).toBeDefined();
143
+ if (retrieved.state) {
144
+ expect(retrieved.state.variant).toBeDefined();
145
+ expect(retrieved.state.key).toBeDefined();
146
+ expect(retrieved.state.details).toBeDefined();
147
+ }
148
+ });
149
+
150
+ it("should include state for multiple devices", async () => {
151
+ const d1 = await client.hardware.devices.create({
152
+ key: "SN_STATE_TEST3",
153
+ rack: testRack.key,
154
+ location: "Dev1",
155
+ name: "state_test3",
156
+ make: "ni",
157
+ model: "dog",
158
+ properties: { cat: "dog" },
159
+ });
160
+
161
+ const d2 = await client.hardware.devices.create({
162
+ key: "SN_STATE_TEST4",
163
+ rack: testRack.key,
164
+ location: "Dev2",
165
+ name: "state_test4",
166
+ make: "ni",
167
+ model: "dog",
168
+ properties: { cat: "dog" },
169
+ });
170
+
171
+ const retrieved = await client.hardware.devices.retrieve([d1.key, d2.key], {
172
+ includeState: true,
173
+ });
174
+ expect(retrieved).toHaveLength(2);
175
+ retrieved.forEach((device) => {
176
+ expect(device.state).toBeDefined();
177
+ if (device.state) {
178
+ expect(device.state.variant).toBeDefined();
179
+ expect(device.state.key).toBeDefined();
180
+ expect(device.state.details).toBeDefined();
181
+ }
182
+ });
183
+ });
184
+
185
+ it("should handle state with type-safe details", async () => {
186
+ interface DeviceStateDetails {
187
+ status: string;
188
+ temperature: number;
189
+ }
190
+
191
+ const key = id.create();
192
+ await client.hardware.devices.create({
193
+ key,
194
+ rack: testRack.key,
195
+ location: "Dev1",
196
+ name: "state_test5",
197
+ make: "ni",
198
+ model: "dog",
199
+ properties: { cat: "dog" },
200
+ });
201
+
202
+ await expect
203
+ .poll(async () => {
204
+ const retrieved = await client.hardware.devices.retrieve<
205
+ UnknownRecord,
206
+ string,
207
+ string,
208
+ DeviceStateDetails
209
+ >(key, { includeState: true });
210
+ return (
211
+ retrieved.state !== undefined &&
212
+ retrieved.state.variant === "info" &&
213
+ retrieved.state.key === key
214
+ );
215
+ })
216
+ .toBeTruthy();
217
+ });
218
+ });
111
219
  });
112
220
  });
@@ -16,6 +16,17 @@ import { decodeJSONString } from "@/util/decodeJSONString";
16
16
  export const keyZ = z.string();
17
17
  export type Key = z.infer<typeof keyZ>;
18
18
 
19
+ export const stateZ = z.object({
20
+ key: keyZ,
21
+ variant: z.string(),
22
+ details: z.record(z.unknown()).or(z.string().transform(decodeJSONString)),
23
+ });
24
+
25
+ export interface State<Details extends {} = UnknownRecord>
26
+ extends Omit<z.infer<typeof stateZ>, "details"> {
27
+ details: Details;
28
+ }
29
+
19
30
  export const deviceZ = z.object({
20
31
  key: keyZ,
21
32
  rack: rackKeyZ,
@@ -25,16 +36,19 @@ export const deviceZ = z.object({
25
36
  location: z.string(),
26
37
  configured: z.boolean().optional(),
27
38
  properties: z.record(z.unknown()).or(z.string().transform(decodeJSONString)),
39
+ state: stateZ.optional(),
28
40
  });
29
41
 
30
42
  export interface Device<
31
43
  Properties extends UnknownRecord = UnknownRecord,
32
44
  Make extends string = string,
33
45
  Model extends string = string,
34
- > extends Omit<z.output<typeof deviceZ>, "properties"> {
46
+ StateDetails extends {} = UnknownRecord,
47
+ > extends Omit<z.output<typeof deviceZ>, "properties" | "state"> {
35
48
  properties: Properties;
36
49
  make: Make;
37
50
  model: Model;
51
+ state?: State<StateDetails>;
38
52
  }
39
53
 
40
54
  export const newZ = deviceZ.extend({
@@ -13,6 +13,7 @@ import { type AsyncTermSearcher } from "@synnaxlabs/x/search";
13
13
  import { toArray } from "@synnaxlabs/x/toArray";
14
14
  import { z } from "zod";
15
15
 
16
+ import { framer } from "@/framer";
16
17
  import {
17
18
  type Key,
18
19
  keyZ,
@@ -21,6 +22,8 @@ import {
21
22
  ONTOLOGY_TYPE,
22
23
  type Payload,
23
24
  rackZ,
25
+ type State,
26
+ stateZ,
24
27
  } from "@/hardware/rack/payload";
25
28
  import { type task } from "@/hardware/task";
26
29
  import { ontology } from "@/ontology";
@@ -31,12 +34,17 @@ const RETRIEVE_ENDPOINT = "/hardware/rack/retrieve";
31
34
  const CREATE_ENDPOINT = "/hardware/rack/create";
32
35
  const DELETE_ENDPOINT = "/hardware/rack/delete";
33
36
 
37
+ const STATE_CHANNEL_NAME = "sy_rack_state";
38
+
34
39
  const retrieveReqZ = z.object({
35
40
  keys: keyZ.array().optional(),
36
41
  names: z.string().array().optional(),
37
42
  search: z.string().optional(),
38
- offset: z.number().optional(),
43
+ embedded: z.boolean().optional(),
44
+ hostIsNode: z.boolean().optional(),
39
45
  limit: z.number().optional(),
46
+ offset: z.number().optional(),
47
+ includeState: z.boolean().optional(),
40
48
  });
41
49
 
42
50
  const retrieveResZ = z.object({ racks: nullableArrayZ(rackZ) });
@@ -49,14 +57,24 @@ const deleteReqZ = z.object({ keys: keyZ.array() });
49
57
 
50
58
  const deleteResZ = z.object({});
51
59
 
60
+ export interface RetrieveOptions {
61
+ includeState?: boolean;
62
+ }
63
+
52
64
  export class Client implements AsyncTermSearcher<string, Key, Payload> {
53
65
  readonly type = ONTOLOGY_TYPE;
54
66
  private readonly client: UnaryClient;
55
67
  private readonly tasks: task.Client;
68
+ private readonly frameClient: framer.Client;
56
69
 
57
- constructor(client: UnaryClient, taskClient: task.Client) {
70
+ constructor(
71
+ client: UnaryClient,
72
+ taskClient: task.Client,
73
+ frameClient: framer.Client,
74
+ ) {
58
75
  this.client = client;
59
76
  this.tasks = taskClient;
77
+ this.frameClient = frameClient;
60
78
  }
61
79
 
62
80
  async delete(keys: Key | Key[]): Promise<void> {
@@ -107,9 +125,12 @@ export class Client implements AsyncTermSearcher<string, Key, Payload> {
107
125
  return this.sugar(res.racks);
108
126
  }
109
127
 
110
- async retrieve(key: string | Key): Promise<Rack>;
111
- async retrieve(keys: Key[]): Promise<Rack[]>;
112
- async retrieve(racks: string | Key | Key[]): Promise<Rack | Rack[]> {
128
+ async retrieve(key: string | Key, options?: RetrieveOptions): Promise<Rack>;
129
+ async retrieve(keys: Key[], options?: RetrieveOptions): Promise<Rack[]>;
130
+ async retrieve(
131
+ racks: string | Key | Key[],
132
+ options?: RetrieveOptions,
133
+ ): Promise<Rack | Rack[]> {
113
134
  const { variant, normalized, single } = analyzeParams(racks, {
114
135
  string: "names",
115
136
  number: "keys",
@@ -117,7 +138,10 @@ export class Client implements AsyncTermSearcher<string, Key, Payload> {
117
138
  const res = await sendRequired<typeof retrieveReqZ, typeof retrieveResZ>(
118
139
  this.client,
119
140
  RETRIEVE_ENDPOINT,
120
- { [variant]: normalized },
141
+ {
142
+ [variant]: normalized,
143
+ includeState: options?.includeState,
144
+ },
121
145
  retrieveReqZ,
122
146
  retrieveResZ,
123
147
  );
@@ -126,20 +150,36 @@ export class Client implements AsyncTermSearcher<string, Key, Payload> {
126
150
  return single ? sugared[0] : sugared;
127
151
  }
128
152
 
153
+ async openStateObserver(): Promise<framer.ObservableStreamer<State[]>> {
154
+ return new framer.ObservableStreamer<State[]>(
155
+ await this.frameClient.openStreamer(STATE_CHANNEL_NAME),
156
+ (fr) => {
157
+ const data = fr.get(STATE_CHANNEL_NAME);
158
+ if (data.length === 0) return [[], false];
159
+ const states = data.parseJSON(stateZ);
160
+ return [states, true];
161
+ },
162
+ );
163
+ }
164
+
129
165
  private sugar(payloads: Payload[]): Rack[] {
130
- return payloads.map(({ key, name }) => new Rack(key, name, this.tasks));
166
+ return payloads.map(
167
+ ({ key, name, state }) => new Rack(key, name, this.tasks, state),
168
+ );
131
169
  }
132
170
  }
133
171
 
134
172
  export class Rack {
135
173
  key: Key;
136
174
  name: string;
175
+ state?: State;
137
176
  private readonly tasks: task.Client;
138
177
 
139
- constructor(key: Key, name: string, taskClient: task.Client) {
178
+ constructor(key: Key, name: string, taskClient: task.Client, state?: State) {
140
179
  this.key = key;
141
180
  this.name = name;
142
181
  this.tasks = taskClient;
182
+ this.state = state;
143
183
  }
144
184
 
145
185
  async listTasks(): Promise<task.Task[]> {
@@ -8,12 +8,27 @@
8
8
  // included in the file licenses/APL.txt.
9
9
 
10
10
  import { zod } from "@synnaxlabs/x";
11
+ import { TimeStamp } from "@synnaxlabs/x/telem";
11
12
  import { z } from "zod";
12
13
 
13
14
  export const keyZ = zod.uint32;
14
15
  export type Key = z.infer<typeof keyZ>;
15
16
 
16
- export const rackZ = z.object({ key: keyZ, name: z.string() });
17
+ export const stateZ = z.object({
18
+ key: keyZ,
19
+ variant: z.string(),
20
+ message: z.string(),
21
+ lastReceived: TimeStamp.z.optional(),
22
+ });
23
+
24
+ export interface State extends z.infer<typeof stateZ> {}
25
+
26
+ export const rackZ = z.object({
27
+ key: keyZ,
28
+ name: z.string(),
29
+ state: stateZ.optional(),
30
+ });
31
+
17
32
  export interface Payload extends z.infer<typeof rackZ> {}
18
33
 
19
34
  export const newZ = rackZ.partial({ key: true });
@@ -7,6 +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 { TimeStamp } from "@synnaxlabs/x";
10
11
  import { describe, expect, it } from "vitest";
11
12
  import { ZodError } from "zod";
12
13
 
@@ -66,4 +67,39 @@ describe("Rack", () => {
66
67
  ).rejects.toThrow(NotFoundError);
67
68
  });
68
69
  });
70
+ describe("state", () => {
71
+ it("should include state when includeState is true", async () => {
72
+ const r = await client.hardware.racks.create({ name: "test" });
73
+ await expect
74
+ .poll(async () => {
75
+ const retrieved = await client.hardware.racks.retrieve(r.key, {
76
+ includeState: true,
77
+ });
78
+ return (
79
+ retrieved.state !== undefined &&
80
+ retrieved.state.lastReceived instanceof TimeStamp &&
81
+ retrieved.state.key === r.key
82
+ );
83
+ })
84
+ .toBeTruthy();
85
+ });
86
+ it("should include state for multiple racks", async () => {
87
+ const r1 = await client.hardware.racks.create({ name: "test1" });
88
+ const r2 = await client.hardware.racks.create({ name: "test2" });
89
+
90
+ await expect
91
+ .poll(async () => {
92
+ const retrieved = await client.hardware.racks.retrieve([r1.key, r2.key], {
93
+ includeState: true,
94
+ });
95
+ return retrieved.every(
96
+ (rack) =>
97
+ rack.state !== undefined &&
98
+ rack.state.lastReceived instanceof TimeStamp &&
99
+ rack.state.key === rack.key,
100
+ );
101
+ })
102
+ .toBeTruthy();
103
+ });
104
+ });
69
105
  });
@@ -100,11 +100,14 @@ describe("Task", async () => {
100
100
  };
101
101
  expect(await w.write("sy_task_state", [state])).toBeTruthy();
102
102
  await w.close();
103
- const retrieved = await client.hardware.tasks.retrieve(t.key, {
104
- includeState: true,
105
- });
106
- expect(retrieved.state).not.toBeNull();
107
- expect(retrieved.state?.variant).toBe(state.variant);
103
+ await expect
104
+ .poll(async () => {
105
+ const retrieved = await client.hardware.tasks.retrieve(t.key, {
106
+ includeState: true,
107
+ });
108
+ return retrieved.state?.variant === state.variant;
109
+ })
110
+ .toBeTruthy();
108
111
  });
109
112
  });
110
113
  });
@@ -17,7 +17,7 @@ import { z } from "zod";
17
17
  import { type channel } from "@/channel";
18
18
  import { MultipleFoundError, NotFoundError, QueryError } from "@/errors";
19
19
  import { type framer } from "@/framer";
20
- import { type label } from "@/label";
20
+ import { label } from "@/label";
21
21
  import { ontology } from "@/ontology";
22
22
  import { type Alias, Aliaser } from "@/ranger/alias";
23
23
  import { KV } from "@/ranger/kv";
@@ -195,6 +195,7 @@ const retrieveReqZ = z.object({
195
195
  overlapsWith: TimeRange.z.optional(),
196
196
  limit: z.number().int().optional(),
197
197
  offset: z.number().int().optional(),
198
+ hasLabels: label.keyZ.array().optional(),
198
199
  });
199
200
 
200
201
  export interface RetrieveRequest extends z.infer<typeof retrieveReqZ> {}
@@ -321,7 +322,7 @@ export class Client implements AsyncTermSearcher<string, Key, Range> {
321
322
  "sy_range_delete",
322
323
  (variant, data) => {
323
324
  if (variant === "delete")
324
- return data.toStrings().map((k) => ({ variant, key: k, value: undefined }));
325
+ return data.toUUIDs().map((k) => ({ variant, key: k, value: undefined }));
325
326
  const sugared = this.sugarMany(data.parseJSON(payloadZ));
326
327
  return sugared.map((r) => ({ variant, key: r.key, value: r }));
327
328
  },