@synnaxlabs/client 0.43.1 → 0.44.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.
- package/.turbo/turbo-build.log +7 -7
- package/dist/access/payload.d.ts +1 -1
- package/dist/access/payload.d.ts.map +1 -1
- package/dist/access/policy/client.d.ts +263 -6
- package/dist/access/policy/client.d.ts.map +1 -1
- package/dist/access/policy/external.d.ts +0 -1
- package/dist/access/policy/external.d.ts.map +1 -1
- package/dist/access/policy/payload.d.ts +105 -93
- package/dist/access/policy/payload.d.ts.map +1 -1
- package/dist/auth/auth.d.ts +1 -1
- package/dist/auth/auth.d.ts.map +1 -1
- package/dist/channel/client.d.ts +23 -17
- package/dist/channel/client.d.ts.map +1 -1
- package/dist/channel/payload.d.ts +151 -21
- package/dist/channel/payload.d.ts.map +1 -1
- package/dist/channel/retriever.d.ts +9 -16
- package/dist/channel/retriever.d.ts.map +1 -1
- package/dist/channel/writer.d.ts +1 -1
- package/dist/channel/writer.d.ts.map +1 -1
- package/dist/client.cjs +27 -135
- package/dist/client.d.ts +3 -3
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +8657 -28963
- package/dist/connection/checker.d.ts +1 -1
- package/dist/connection/checker.d.ts.map +1 -1
- package/dist/control/client.d.ts +1 -0
- package/dist/control/client.d.ts.map +1 -1
- package/dist/control/state.d.ts +6 -6
- package/dist/control/state.d.ts.map +1 -1
- package/dist/errors.d.ts +18 -5
- package/dist/errors.d.ts.map +1 -1
- package/dist/framer/adapter.d.ts +3 -3
- package/dist/framer/adapter.d.ts.map +1 -1
- package/dist/framer/client.d.ts +4 -13
- package/dist/framer/client.d.ts.map +1 -1
- package/dist/framer/codec.d.ts +1 -1
- package/dist/framer/codec.d.ts.map +1 -1
- package/dist/framer/deleter.d.ts +5 -5
- package/dist/framer/deleter.d.ts.map +1 -1
- package/dist/framer/frame.d.ts +5 -7
- package/dist/framer/frame.d.ts.map +1 -1
- package/dist/framer/streamProxy.d.ts +1 -1
- package/dist/framer/streamProxy.d.ts.map +1 -1
- package/dist/framer/streamer.d.ts +235 -20
- package/dist/framer/streamer.d.ts.map +1 -1
- package/dist/framer/writer.d.ts +302 -33
- package/dist/framer/writer.d.ts.map +1 -1
- package/dist/hardware/device/client.d.ts +49 -28
- package/dist/hardware/device/client.d.ts.map +1 -1
- package/dist/hardware/device/payload.d.ts +126 -46
- package/dist/hardware/device/payload.d.ts.map +1 -1
- package/dist/hardware/rack/client.d.ts +78 -22
- package/dist/hardware/rack/client.d.ts.map +1 -1
- package/dist/hardware/rack/payload.d.ts +99 -56
- package/dist/hardware/rack/payload.d.ts.map +1 -1
- package/dist/hardware/task/client.d.ts +100 -41
- package/dist/hardware/task/client.d.ts.map +1 -1
- package/dist/hardware/task/payload.d.ts +83 -61
- package/dist/hardware/task/payload.d.ts.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/label/client.d.ts +138 -20
- package/dist/label/client.d.ts.map +1 -1
- package/dist/label/external.d.ts +0 -2
- package/dist/label/external.d.ts.map +1 -1
- package/dist/label/payload.d.ts +4 -5
- package/dist/label/payload.d.ts.map +1 -1
- package/dist/ontology/client.d.ts +45 -135
- package/dist/ontology/client.d.ts.map +1 -1
- package/dist/ontology/group/group.d.ts +3 -3
- package/dist/ontology/group/group.d.ts.map +1 -1
- package/dist/ontology/group/payload.d.ts +3 -27
- package/dist/ontology/group/payload.d.ts.map +1 -1
- package/dist/ontology/payload.d.ts +114 -243
- package/dist/ontology/payload.d.ts.map +1 -1
- package/dist/ontology/writer.d.ts +4 -4
- package/dist/ontology/writer.d.ts.map +1 -1
- package/dist/ranger/alias.d.ts +15 -5
- package/dist/ranger/alias.d.ts.map +1 -1
- package/dist/ranger/client.d.ts +91 -30
- 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 +11 -12
- package/dist/ranger/kv.d.ts.map +1 -1
- package/dist/ranger/payload.d.ts +19 -44
- package/dist/ranger/payload.d.ts.map +1 -1
- package/dist/ranger/writer.d.ts +22 -19
- package/dist/ranger/writer.d.ts.map +1 -1
- package/dist/testutil/client.d.ts +4 -0
- package/dist/testutil/client.d.ts.map +1 -0
- package/dist/user/client.d.ts +59 -6
- package/dist/user/client.d.ts.map +1 -1
- package/dist/user/payload.d.ts +4 -6
- package/dist/user/payload.d.ts.map +1 -1
- package/dist/user/retriever.d.ts +2 -2
- package/dist/user/retriever.d.ts.map +1 -1
- package/dist/util/decodeJSONString.d.ts +2 -2
- package/dist/util/decodeJSONString.d.ts.map +1 -1
- package/dist/util/parseWithoutKeyConversion.d.ts +2 -2
- package/dist/util/parseWithoutKeyConversion.d.ts.map +1 -1
- package/dist/util/retrieve.d.ts +1 -1
- package/dist/util/retrieve.d.ts.map +1 -1
- package/dist/util/zod.d.ts +1 -1
- package/dist/util/zod.d.ts.map +1 -1
- package/dist/workspace/client.d.ts +17 -6
- package/dist/workspace/client.d.ts.map +1 -1
- package/dist/workspace/lineplot/client.d.ts +2 -2
- package/dist/workspace/lineplot/client.d.ts.map +1 -1
- package/dist/workspace/lineplot/payload.d.ts +8 -9
- package/dist/workspace/lineplot/payload.d.ts.map +1 -1
- package/dist/workspace/log/client.d.ts +2 -2
- package/dist/workspace/log/client.d.ts.map +1 -1
- package/dist/workspace/log/payload.d.ts +8 -9
- package/dist/workspace/log/payload.d.ts.map +1 -1
- package/dist/workspace/payload.d.ts +10 -11
- package/dist/workspace/payload.d.ts.map +1 -1
- package/dist/workspace/schematic/client.d.ts +2 -2
- package/dist/workspace/schematic/client.d.ts.map +1 -1
- package/dist/workspace/schematic/payload.d.ts +10 -11
- package/dist/workspace/schematic/payload.d.ts.map +1 -1
- package/dist/workspace/table/client.d.ts +2 -2
- package/dist/workspace/table/client.d.ts.map +1 -1
- package/dist/workspace/table/payload.d.ts +10 -11
- package/dist/workspace/table/payload.d.ts.map +1 -1
- package/examples/node/package-lock.json +47 -39
- package/examples/node/package.json +2 -1
- package/examples/node/streamWrite.js +5 -11
- package/package.json +14 -13
- package/src/access/payload.ts +1 -1
- package/src/access/policy/client.ts +87 -32
- package/src/access/policy/external.ts +0 -1
- package/src/access/policy/payload.ts +4 -4
- package/src/access/policy/policy.spec.ts +86 -83
- package/src/auth/auth.spec.ts +29 -18
- package/src/auth/auth.ts +1 -1
- package/src/channel/batchRetriever.spec.ts +4 -9
- package/src/channel/channel.spec.ts +24 -6
- package/src/channel/client.ts +52 -51
- package/src/channel/payload.ts +15 -16
- package/src/channel/retriever.ts +26 -41
- package/src/channel/writer.ts +7 -4
- package/src/client.ts +4 -4
- package/src/connection/checker.ts +1 -1
- package/src/connection/connection.spec.ts +31 -23
- package/src/control/client.ts +2 -2
- package/src/control/state.spec.ts +3 -3
- package/src/control/state.ts +1 -1
- package/src/errors.spec.ts +9 -5
- package/src/errors.ts +28 -15
- package/src/framer/adapter.spec.ts +118 -9
- package/src/framer/adapter.ts +24 -11
- package/src/framer/client.spec.ts +125 -2
- package/src/framer/client.ts +41 -47
- package/src/framer/codec.ts +1 -1
- package/src/framer/deleter.spec.ts +2 -2
- package/src/framer/deleter.ts +1 -1
- package/src/framer/frame.ts +1 -4
- package/src/framer/iterator.spec.ts +8 -8
- package/src/framer/iterator.ts +1 -1
- package/src/framer/streamProxy.ts +1 -1
- package/src/framer/streamer.spec.ts +185 -36
- package/src/framer/streamer.ts +28 -36
- package/src/framer/writer.spec.ts +7 -6
- package/src/framer/writer.ts +97 -111
- package/src/hardware/device/client.ts +45 -131
- package/src/hardware/device/device.spec.ts +163 -52
- package/src/hardware/device/payload.ts +10 -21
- package/src/hardware/rack/client.ts +87 -105
- package/src/hardware/rack/payload.ts +4 -13
- package/src/hardware/rack/rack.spec.ts +28 -35
- package/src/hardware/task/client.ts +335 -291
- package/src/hardware/task/payload.ts +86 -62
- package/src/hardware/task/task.spec.ts +208 -32
- package/src/index.ts +2 -1
- package/src/label/client.ts +100 -95
- package/src/label/external.ts +0 -2
- package/src/label/label.spec.ts +8 -6
- package/src/label/payload.ts +3 -4
- package/src/ontology/client.ts +41 -324
- package/src/ontology/group/group.spec.ts +2 -2
- package/src/ontology/group/group.ts +4 -5
- package/src/ontology/group/payload.ts +2 -25
- package/src/ontology/group/writer.ts +1 -1
- package/src/ontology/ontology.spec.ts +355 -41
- package/src/ontology/payload.ts +77 -112
- package/src/ontology/writer.ts +8 -17
- package/src/ranger/alias.ts +45 -37
- package/src/ranger/client.ts +144 -149
- package/src/ranger/external.ts +1 -1
- package/src/ranger/kv.ts +9 -27
- package/src/ranger/payload.ts +23 -37
- package/src/ranger/ranger.spec.ts +37 -56
- package/src/ranger/writer.ts +1 -1
- package/src/{signals/index.ts → testutil/client.ts} +11 -1
- package/src/user/client.ts +122 -47
- package/src/user/payload.ts +2 -5
- package/src/user/retriever.ts +1 -1
- package/src/user/user.spec.ts +31 -31
- package/src/user/writer.ts +1 -1
- package/src/util/decodeJSONString.ts +3 -3
- package/src/util/parseWithoutKeyConversion.ts +2 -2
- package/src/util/retrieve.ts +1 -1
- package/src/util/zod.ts +1 -1
- package/src/workspace/client.ts +20 -36
- package/src/workspace/lineplot/client.ts +5 -7
- package/src/workspace/lineplot/lineplot.spec.ts +2 -2
- package/src/workspace/lineplot/payload.ts +4 -7
- package/src/workspace/log/client.ts +5 -7
- package/src/workspace/log/log.spec.ts +2 -2
- package/src/workspace/log/payload.ts +4 -7
- package/src/workspace/payload.ts +4 -7
- package/src/workspace/schematic/client.ts +5 -7
- package/src/workspace/schematic/payload.ts +4 -7
- package/src/workspace/schematic/schematic.spec.ts +2 -2
- package/src/workspace/table/client.ts +5 -7
- package/src/workspace/table/payload.ts +4 -7
- package/src/workspace/table/table.spec.ts +2 -2
- package/src/workspace/workspace.spec.ts +2 -2
- package/dist/access/policy/ontology.d.ts +0 -5
- package/dist/access/policy/ontology.d.ts.map +0 -1
- package/dist/access/policy/retriever.d.ts +0 -40
- package/dist/access/policy/retriever.d.ts.map +0 -1
- package/dist/access/policy/writer.d.ts +0 -9
- package/dist/access/policy/writer.d.ts.map +0 -1
- package/dist/label/retriever.d.ts +0 -14
- package/dist/label/retriever.d.ts.map +0 -1
- package/dist/label/writer.d.ts +0 -54
- package/dist/label/writer.d.ts.map +0 -1
- package/dist/setupspecs.d.ts +0 -5
- package/dist/setupspecs.d.ts.map +0 -1
- package/dist/signals/external.d.ts +0 -2
- package/dist/signals/external.d.ts.map +0 -1
- package/dist/signals/index.d.ts +0 -2
- package/dist/signals/index.d.ts.map +0 -1
- package/dist/signals/observable.d.ts +0 -12
- package/dist/signals/observable.d.ts.map +0 -1
- package/src/access/policy/ontology.ts +0 -17
- package/src/access/policy/retriever.ts +0 -44
- package/src/access/policy/writer.ts +0 -65
- package/src/label/retriever.ts +0 -63
- package/src/label/writer.ts +0 -95
- package/src/setupspecs.ts +0 -27
- package/src/signals/external.ts +0 -10
- package/src/signals/observable.ts +0 -42
package/src/ontology/writer.ts
CHANGED
|
@@ -8,9 +8,9 @@
|
|
|
8
8
|
// included in the file licenses/APL.txt.
|
|
9
9
|
|
|
10
10
|
import { sendRequired, type UnaryClient } from "@synnaxlabs/freighter";
|
|
11
|
-
import { z } from "zod
|
|
11
|
+
import { z } from "zod";
|
|
12
12
|
|
|
13
|
-
import { type
|
|
13
|
+
import { type ID, idZ } from "@/ontology/payload";
|
|
14
14
|
|
|
15
15
|
const ADD_CHILDREN_ENDPOINT = "/ontology/add-children";
|
|
16
16
|
const REMOVE_CHILDREN_ENDPOINT = "/ontology/remove-children";
|
|
@@ -29,40 +29,31 @@ export class Writer {
|
|
|
29
29
|
this.client = client;
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
async addChildren(id:
|
|
32
|
+
async addChildren(id: ID, ...children: ID[]): Promise<void> {
|
|
33
33
|
await sendRequired<typeof addRemoveChildrenReqZ, typeof emptyResZ>(
|
|
34
34
|
this.client,
|
|
35
35
|
ADD_CHILDREN_ENDPOINT,
|
|
36
|
-
{ id
|
|
36
|
+
{ id, children },
|
|
37
37
|
addRemoveChildrenReqZ,
|
|
38
38
|
emptyResZ,
|
|
39
39
|
);
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
-
async removeChildren(id:
|
|
42
|
+
async removeChildren(id: ID, ...children: ID[]): Promise<void> {
|
|
43
43
|
await sendRequired<typeof addRemoveChildrenReqZ, typeof emptyResZ>(
|
|
44
44
|
this.client,
|
|
45
45
|
REMOVE_CHILDREN_ENDPOINT,
|
|
46
|
-
{ id
|
|
46
|
+
{ id, children },
|
|
47
47
|
addRemoveChildrenReqZ,
|
|
48
48
|
emptyResZ,
|
|
49
49
|
);
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
-
async moveChildren(
|
|
53
|
-
from: CrudeID,
|
|
54
|
-
to: CrudeID,
|
|
55
|
-
...children: CrudeID[]
|
|
56
|
-
): Promise<void> {
|
|
57
|
-
const req = {
|
|
58
|
-
from: new ID(from).payload,
|
|
59
|
-
to: new ID(to).payload,
|
|
60
|
-
children: children.map((c) => new ID(c).payload),
|
|
61
|
-
};
|
|
52
|
+
async moveChildren(from: ID, to: ID, ...children: ID[]): Promise<void> {
|
|
62
53
|
await sendRequired<typeof moveChildrenReqZ, typeof emptyResZ>(
|
|
63
54
|
this.client,
|
|
64
55
|
MOVE_CHILDREN_ENDPOINT,
|
|
65
|
-
|
|
56
|
+
{ from, to, children },
|
|
66
57
|
moveChildrenReqZ,
|
|
67
58
|
emptyResZ,
|
|
68
59
|
);
|
package/src/ranger/alias.ts
CHANGED
|
@@ -8,13 +8,13 @@
|
|
|
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
|
-
import { z } from "zod
|
|
13
|
+
import { z } from "zod";
|
|
13
14
|
|
|
14
15
|
import { channel } from "@/channel";
|
|
15
16
|
import { type framer } from "@/framer";
|
|
16
17
|
import { type Key, keyZ } from "@/ranger/payload";
|
|
17
|
-
import { signals } from "@/signals";
|
|
18
18
|
|
|
19
19
|
export const SET_ALIAS_CHANNEL_NAME = "sy_range_alias_set";
|
|
20
20
|
export const DELETE_ALIAS_CHANNEL_NAME = "sy_range_alias_delete";
|
|
@@ -38,10 +38,15 @@ const listReqZ = z.object({ range: keyZ });
|
|
|
38
38
|
|
|
39
39
|
const listResZ = z.object({ aliases: z.record(z.string(), z.string()) });
|
|
40
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
|
+
|
|
41
45
|
export class Aliaser {
|
|
42
46
|
private static readonly SET_ENDPOINT = "/range/alias/set";
|
|
43
47
|
private static readonly RESOLVE_ENDPOINT = "/range/alias/resolve";
|
|
44
48
|
private static readonly LIST_ENDPOINT = "/range/alias/list";
|
|
49
|
+
private static readonly RETRIEVE_ENDPOINT = "/range/alias/retrieve";
|
|
45
50
|
private static readonly DELETE_ENDPOINT = "/range/alias/delete";
|
|
46
51
|
private readonly frameClient: framer.Client;
|
|
47
52
|
private readonly cache = new Map<string, channel.Key>();
|
|
@@ -109,6 +114,23 @@ export class Aliaser {
|
|
|
109
114
|
).aliases;
|
|
110
115
|
}
|
|
111
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
|
+
|
|
112
134
|
async delete(aliases: channel.Key[]): Promise<void> {
|
|
113
135
|
await sendRequired<typeof deleteReqZ, typeof deleteResZ>(
|
|
114
136
|
this.client,
|
|
@@ -118,44 +140,30 @@ export class Aliaser {
|
|
|
118
140
|
deleteResZ,
|
|
119
141
|
);
|
|
120
142
|
}
|
|
121
|
-
|
|
122
|
-
async openChangeTracker(): Promise<signals.Observable<string, Alias>> {
|
|
123
|
-
return await signals.openObservable<string, Alias>(
|
|
124
|
-
this.frameClient,
|
|
125
|
-
SET_ALIAS_CHANNEL_NAME,
|
|
126
|
-
DELETE_ALIAS_CHANNEL_NAME,
|
|
127
|
-
decodeAliasChanges(this.rangeKey),
|
|
128
|
-
);
|
|
129
|
-
}
|
|
130
143
|
}
|
|
131
144
|
|
|
132
|
-
export
|
|
145
|
+
export const aliasZ = z.object({
|
|
146
|
+
alias: z.string().optional(),
|
|
147
|
+
channel: channel.keyZ,
|
|
148
|
+
range: keyZ,
|
|
149
|
+
});
|
|
150
|
+
export interface Alias extends z.infer<typeof aliasZ> {}
|
|
151
|
+
|
|
152
|
+
export type AliasChange = change.Change<string, Alias>;
|
|
153
|
+
|
|
154
|
+
const SEPARATOR = "---";
|
|
155
|
+
|
|
156
|
+
export const aliasKey = (alias: Pick<Alias, "range" | "channel">): string =>
|
|
157
|
+
`${alias.range}${SEPARATOR}${alias.channel}`;
|
|
158
|
+
|
|
159
|
+
export interface DecodedDeleteAliasChange {
|
|
133
160
|
range: Key;
|
|
134
161
|
channel: channel.Key;
|
|
135
|
-
alias: string;
|
|
136
162
|
}
|
|
137
163
|
|
|
138
|
-
export
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
const decodeAliasChanges =
|
|
145
|
-
(rangeKey: Key): signals.Decoder<string, Alias> =>
|
|
146
|
-
(variant, data) => {
|
|
147
|
-
if (variant === "delete")
|
|
148
|
-
return data
|
|
149
|
-
.toStrings()
|
|
150
|
-
.filter((k) => k.split(separator)[0] === rangeKey)
|
|
151
|
-
.map((alias) => ({
|
|
152
|
-
variant,
|
|
153
|
-
key: alias,
|
|
154
|
-
value: undefined,
|
|
155
|
-
}));
|
|
156
|
-
return data.parseJSON(aliasZ).map((alias) => ({
|
|
157
|
-
variant,
|
|
158
|
-
key: alias.alias,
|
|
159
|
-
value: alias,
|
|
160
|
-
}));
|
|
161
|
-
};
|
|
164
|
+
export const decodeDeleteAliasChange = (
|
|
165
|
+
deletedAlias: string,
|
|
166
|
+
): DecodedDeleteAliasChange => {
|
|
167
|
+
const [range, channel] = deletedAlias.split(SEPARATOR);
|
|
168
|
+
return { range, channel: Number(channel) };
|
|
169
|
+
};
|
package/src/ranger/client.ts
CHANGED
|
@@ -8,43 +8,53 @@
|
|
|
8
8
|
// included in the file licenses/APL.txt.
|
|
9
9
|
|
|
10
10
|
import { sendRequired, type UnaryClient } from "@synnaxlabs/freighter";
|
|
11
|
-
import { type CrudeTimeRange,
|
|
12
|
-
import { array } from "@synnaxlabs/x/array";
|
|
13
|
-
import { type AsyncTermSearcher } from "@synnaxlabs/x/search";
|
|
11
|
+
import { array, type CrudeTimeRange, TimeRange } from "@synnaxlabs/x";
|
|
14
12
|
import { type Series } from "@synnaxlabs/x/telem";
|
|
15
|
-
import { z } from "zod
|
|
13
|
+
import { z } from "zod";
|
|
16
14
|
|
|
17
15
|
import { type channel } from "@/channel";
|
|
18
|
-
import {
|
|
16
|
+
import { QueryError } from "@/errors";
|
|
19
17
|
import { type framer } from "@/framer";
|
|
20
18
|
import { label } from "@/label";
|
|
21
|
-
import { ontology } from "@/ontology";
|
|
22
|
-
import {
|
|
19
|
+
import { type ontology } from "@/ontology";
|
|
20
|
+
import { Aliaser } from "@/ranger/alias";
|
|
23
21
|
import { KV } from "@/ranger/kv";
|
|
24
22
|
import {
|
|
25
|
-
ALIAS_ONTOLOGY_TYPE,
|
|
26
|
-
analyzeParams,
|
|
27
23
|
type Key,
|
|
28
24
|
type Keys,
|
|
29
25
|
keyZ,
|
|
30
26
|
type Name,
|
|
31
27
|
type Names,
|
|
32
28
|
type New,
|
|
33
|
-
ONTOLOGY_TYPE,
|
|
34
29
|
type Params,
|
|
35
30
|
type Payload,
|
|
36
31
|
payloadZ,
|
|
37
32
|
} from "@/ranger/payload";
|
|
38
33
|
import { type CreateOptions, type Writer } from "@/ranger/writer";
|
|
39
|
-
import {
|
|
34
|
+
import { checkForMultipleOrNoResults } from "@/util/retrieve";
|
|
40
35
|
import { nullableArrayZ } from "@/util/zod";
|
|
41
36
|
|
|
37
|
+
export const SET_CHANNEL_NAME = "sy_range_set";
|
|
38
|
+
export const DELETE_CHANNEL_NAME = "sy_range_delete";
|
|
39
|
+
|
|
40
|
+
interface RangeConstructionOptions {
|
|
41
|
+
frameClient: framer.Client;
|
|
42
|
+
kv: KV;
|
|
43
|
+
aliaser: Aliaser;
|
|
44
|
+
channels: channel.Retriever;
|
|
45
|
+
labelClient: label.Client;
|
|
46
|
+
ontologyClient: ontology.Client;
|
|
47
|
+
rangeClient: Client;
|
|
48
|
+
}
|
|
49
|
+
|
|
42
50
|
export class Range {
|
|
43
51
|
key: string;
|
|
44
52
|
name: string;
|
|
45
53
|
readonly kv: KV;
|
|
46
54
|
readonly timeRange: TimeRange;
|
|
47
55
|
readonly color: string | undefined;
|
|
56
|
+
readonly parent: Payload | null;
|
|
57
|
+
readonly labels?: label.Label[];
|
|
48
58
|
readonly channels: channel.Retriever;
|
|
49
59
|
private readonly aliaser: Aliaser;
|
|
50
60
|
private readonly frameClient: framer.Client;
|
|
@@ -53,29 +63,30 @@ export class Range {
|
|
|
53
63
|
private readonly rangeClient: Client;
|
|
54
64
|
|
|
55
65
|
constructor(
|
|
56
|
-
name:
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
_rangeClient: Client,
|
|
66
|
+
{ name, timeRange = TimeRange.ZERO, key, color, parent, labels }: Payload,
|
|
67
|
+
{
|
|
68
|
+
frameClient,
|
|
69
|
+
kv,
|
|
70
|
+
aliaser,
|
|
71
|
+
channels,
|
|
72
|
+
labelClient,
|
|
73
|
+
ontologyClient,
|
|
74
|
+
rangeClient,
|
|
75
|
+
}: RangeConstructionOptions,
|
|
67
76
|
) {
|
|
68
77
|
this.key = key;
|
|
69
78
|
this.name = name;
|
|
70
79
|
this.timeRange = timeRange;
|
|
71
|
-
this.
|
|
80
|
+
this.parent = parent;
|
|
81
|
+
this.labels = labels;
|
|
82
|
+
this.frameClient = frameClient;
|
|
72
83
|
this.color = color;
|
|
73
|
-
this.kv =
|
|
74
|
-
this.aliaser =
|
|
75
|
-
this.channels =
|
|
76
|
-
this.labelClient =
|
|
77
|
-
this.ontologyClient =
|
|
78
|
-
this.rangeClient =
|
|
84
|
+
this.kv = kv;
|
|
85
|
+
this.aliaser = aliaser;
|
|
86
|
+
this.channels = channels;
|
|
87
|
+
this.labelClient = labelClient;
|
|
88
|
+
this.ontologyClient = ontologyClient;
|
|
89
|
+
this.rangeClient = rangeClient;
|
|
79
90
|
}
|
|
80
91
|
|
|
81
92
|
get ontologyID(): ontology.ID {
|
|
@@ -83,11 +94,17 @@ export class Range {
|
|
|
83
94
|
}
|
|
84
95
|
|
|
85
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;
|
|
86
101
|
return {
|
|
87
102
|
key: this.key,
|
|
88
103
|
name: this.name,
|
|
89
104
|
timeRange: this.timeRange,
|
|
90
105
|
color: this.color,
|
|
106
|
+
labels: this.labels,
|
|
107
|
+
parent,
|
|
91
108
|
};
|
|
92
109
|
}
|
|
93
110
|
|
|
@@ -109,10 +126,6 @@ export class Range {
|
|
|
109
126
|
return await this.aliaser.resolve(alias);
|
|
110
127
|
}
|
|
111
128
|
|
|
112
|
-
async openAliasTracker(): Promise<signals.Observable<string, Alias>> {
|
|
113
|
-
return await this.aliaser.openChangeTracker();
|
|
114
|
-
}
|
|
115
|
-
|
|
116
129
|
async retrieveParent(): Promise<Range | null> {
|
|
117
130
|
return this.rangeClient.retrieveParent(this.key);
|
|
118
131
|
}
|
|
@@ -133,8 +146,8 @@ export class Range {
|
|
|
133
146
|
return await this.frameClient.read(this.timeRange, channels);
|
|
134
147
|
}
|
|
135
148
|
|
|
136
|
-
async
|
|
137
|
-
return await this.labelClient.
|
|
149
|
+
async retrieveLabels(): Promise<label.Label[]> {
|
|
150
|
+
return await this.labelClient.retrieve({ for: ontologyID(this.key) });
|
|
138
151
|
}
|
|
139
152
|
|
|
140
153
|
async addLabel(...labels: label.Key[]): Promise<void> {
|
|
@@ -142,69 +155,47 @@ export class Range {
|
|
|
142
155
|
}
|
|
143
156
|
|
|
144
157
|
async removeLabel(...labels: label.Key[]): Promise<void> {
|
|
145
|
-
await this.labelClient.
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
async openChildRangeTracker(): Promise<observe.ObservableAsyncCloseable<Range[]>> {
|
|
149
|
-
const wrapper = new observe.Observer<Range[]>();
|
|
150
|
-
const initial: ontology.Resource[] = (await this.retrieveChildren()).map((r) => {
|
|
151
|
-
const id = ontologyID(r.key);
|
|
152
|
-
return { id, key: id.toString(), name: r.name, data: r.payload };
|
|
153
|
-
});
|
|
154
|
-
const base = await this.ontologyClient.openDependentTracker({
|
|
155
|
-
target: this.ontologyID,
|
|
156
|
-
dependents: initial,
|
|
157
|
-
resourceType: "range",
|
|
158
|
-
});
|
|
159
|
-
base.onChange((r: ontology.Resource[]) =>
|
|
160
|
-
wrapper.notify(this.rangeClient.resourcesToRanges(r)),
|
|
161
|
-
);
|
|
162
|
-
wrapper.setCloser(async () => await base.close());
|
|
163
|
-
return wrapper;
|
|
158
|
+
await this.labelClient.remove(ontologyID(this.key), labels);
|
|
164
159
|
}
|
|
165
160
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
const p = await this.retrieveParent();
|
|
169
|
-
if (p == null) return null;
|
|
170
|
-
const id = ontologyID(p.key);
|
|
171
|
-
const resourceP = { id, key: id.toString(), name: p.name, data: p.payload };
|
|
172
|
-
const base = await this.ontologyClient.openDependentTracker({
|
|
173
|
-
target: this.ontologyID,
|
|
174
|
-
dependents: [resourceP],
|
|
175
|
-
relationshipDirection: "to",
|
|
176
|
-
});
|
|
177
|
-
base.onChange((resources: ontology.Resource[]) => {
|
|
178
|
-
const ranges = this.rangeClient.resourcesToRanges(resources);
|
|
179
|
-
if (ranges.length === 0) return;
|
|
180
|
-
const p = ranges[0];
|
|
181
|
-
wrapper.notify(p);
|
|
182
|
-
});
|
|
183
|
-
wrapper.setCloser(async () => await base.close());
|
|
184
|
-
return wrapper;
|
|
161
|
+
static sort(a: Range, b: Range): number {
|
|
162
|
+
return TimeRange.sort(a.timeRange, b.timeRange);
|
|
185
163
|
}
|
|
186
164
|
}
|
|
187
165
|
|
|
188
|
-
|
|
189
|
-
sortTimeRange(a.timeRange, b.timeRange);
|
|
190
|
-
|
|
191
|
-
const retrieveReqZ = z.object({
|
|
166
|
+
const retrieveRequestZ = z.object({
|
|
192
167
|
keys: keyZ.array().optional(),
|
|
193
168
|
names: z.array(z.string()).optional(),
|
|
194
|
-
|
|
169
|
+
searchTerm: z.string().optional(),
|
|
195
170
|
overlapsWith: TimeRange.z.optional(),
|
|
196
171
|
limit: z.number().int().optional(),
|
|
197
172
|
offset: z.number().int().optional(),
|
|
198
173
|
hasLabels: label.keyZ.array().optional(),
|
|
174
|
+
includeLabels: z.boolean().optional(),
|
|
175
|
+
includeParent: z.boolean().optional(),
|
|
199
176
|
});
|
|
200
177
|
|
|
201
|
-
export
|
|
178
|
+
export type RetrieveRequest = z.infer<typeof retrieveRequestZ>;
|
|
179
|
+
|
|
180
|
+
const retrieveArgsZ = retrieveRequestZ
|
|
181
|
+
.or(keyZ.array().transform((keys) => ({ keys })))
|
|
182
|
+
.or(keyZ.transform((key) => ({ keys: [key] })))
|
|
183
|
+
.or(z.string().transform((name) => ({ names: [name] })))
|
|
184
|
+
.or(
|
|
185
|
+
z
|
|
186
|
+
.string()
|
|
187
|
+
.array()
|
|
188
|
+
.transform((names) => ({ names })),
|
|
189
|
+
)
|
|
190
|
+
.or(TimeRange.z.transform((timeRange) => ({ overlapsWith: timeRange })));
|
|
191
|
+
|
|
192
|
+
export type RetrieveArgs = z.input<typeof retrieveArgsZ>;
|
|
202
193
|
|
|
203
194
|
const RETRIEVE_ENDPOINT = "/range/retrieve";
|
|
204
195
|
|
|
205
196
|
const retrieveResZ = z.object({ ranges: nullableArrayZ(payloadZ) });
|
|
206
197
|
|
|
207
|
-
export class Client
|
|
198
|
+
export class Client {
|
|
208
199
|
readonly type: string = "range";
|
|
209
200
|
private readonly frameClient: framer.Client;
|
|
210
201
|
private readonly writer: Writer;
|
|
@@ -247,48 +238,28 @@ export class Client implements AsyncTermSearcher<string, Key, Range> {
|
|
|
247
238
|
await this.writer.delete(array.toArray(key));
|
|
248
239
|
}
|
|
249
240
|
|
|
250
|
-
async
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
async
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
async retrieve(range: CrudeTimeRange): Promise<Range[]>;
|
|
259
|
-
async retrieve(range: Key | Name): Promise<Range>;
|
|
260
|
-
async retrieve(range: Keys | Names): Promise<Range[]>;
|
|
261
|
-
async retrieve(ranges: Params | CrudeTimeRange): Promise<Range | Range[]> {
|
|
262
|
-
if (typeof ranges === "object" && "start" in ranges)
|
|
263
|
-
return await this.execRetrieve({ overlapsWith: new TimeRange(ranges) });
|
|
264
|
-
const { single, actual, variant, normalized, empty } = analyzeParams(ranges);
|
|
265
|
-
if (empty) return [];
|
|
266
|
-
const retrieved = await this.execRetrieve({ [variant]: normalized });
|
|
267
|
-
if (!single) return retrieved;
|
|
268
|
-
if (retrieved.length === 0)
|
|
269
|
-
throw new NotFoundError(`range matching ${actual as string} not found`);
|
|
270
|
-
if (retrieved.length > 1)
|
|
271
|
-
throw new MultipleFoundError(
|
|
272
|
-
`multiple ranges matching ${actual as string} found`,
|
|
273
|
-
);
|
|
274
|
-
return retrieved[0];
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
getKV(range: Key): KV {
|
|
278
|
-
return new KV(range, this.unaryClient, this.frameClient);
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
private async execRetrieve(req: RetrieveRequest): Promise<Range[]> {
|
|
282
|
-
const { ranges } = await sendRequired<typeof retrieveReqZ, typeof retrieveResZ>(
|
|
241
|
+
async retrieve(params: Key | Name): Promise<Range>;
|
|
242
|
+
async retrieve(params: Keys | Names): Promise<Range[]>;
|
|
243
|
+
async retrieve(params: TimeRange): Promise<Range[]>;
|
|
244
|
+
async retrieve(params: RetrieveRequest): Promise<Range[]>;
|
|
245
|
+
async retrieve(params: RetrieveArgs): Promise<Range | Range[]> {
|
|
246
|
+
const isSingle = typeof params === "string";
|
|
247
|
+
const { ranges } = await sendRequired(
|
|
283
248
|
this.unaryClient,
|
|
284
249
|
RETRIEVE_ENDPOINT,
|
|
285
|
-
|
|
286
|
-
|
|
250
|
+
params,
|
|
251
|
+
retrieveArgsZ,
|
|
287
252
|
retrieveResZ,
|
|
288
253
|
);
|
|
254
|
+
checkForMultipleOrNoResults("Range", params, ranges, isSingle);
|
|
255
|
+
if (isSingle) return this.sugarMany(ranges)[0];
|
|
289
256
|
return this.sugarMany(ranges);
|
|
290
257
|
}
|
|
291
258
|
|
|
259
|
+
getKV(range: Key): KV {
|
|
260
|
+
return new KV(range, this.unaryClient);
|
|
261
|
+
}
|
|
262
|
+
|
|
292
263
|
async retrieveParent(range: Key): Promise<Range | null> {
|
|
293
264
|
const res = await this.ontologyClient.retrieveParents(ontologyID(range));
|
|
294
265
|
if (res.length === 0) return null;
|
|
@@ -297,42 +268,47 @@ export class Client implements AsyncTermSearcher<string, Key, Range> {
|
|
|
297
268
|
return await this.retrieve(first.id.key);
|
|
298
269
|
}
|
|
299
270
|
|
|
300
|
-
|
|
301
|
-
return
|
|
302
|
-
payload.name,
|
|
303
|
-
payload.timeRange,
|
|
304
|
-
payload.key,
|
|
305
|
-
payload.color,
|
|
306
|
-
this.frameClient,
|
|
307
|
-
new KV(payload.key, this.unaryClient, this.frameClient),
|
|
308
|
-
new Aliaser(payload.key, this.frameClient, this.unaryClient),
|
|
309
|
-
this.channels,
|
|
310
|
-
this.labelClient,
|
|
311
|
-
this.ontologyClient,
|
|
312
|
-
this,
|
|
313
|
-
);
|
|
271
|
+
sugarOntologyResource(resource: ontology.Resource): Range {
|
|
272
|
+
return this.sugarOne(convertOntologyResourceToPayload(resource));
|
|
314
273
|
}
|
|
315
274
|
|
|
316
|
-
|
|
317
|
-
|
|
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);
|
|
318
278
|
}
|
|
319
279
|
|
|
320
|
-
async
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
if (variant === "delete")
|
|
327
|
-
return data.toUUIDs().map((k) => ({ variant, key: k, value: undefined }));
|
|
328
|
-
const sugared = this.sugarMany(data.parseJSON(payloadZ));
|
|
329
|
-
return sugared.map((r) => ({ variant, key: r.key, value: r }));
|
|
330
|
-
},
|
|
331
|
-
);
|
|
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);
|
|
332
286
|
}
|
|
333
287
|
|
|
334
|
-
|
|
335
|
-
|
|
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
|
+
|
|
298
|
+
sugarOne(payload: Payload): Range {
|
|
299
|
+
return new Range(payload, {
|
|
300
|
+
frameClient: this.frameClient,
|
|
301
|
+
kv: new KV(payload.key, this.unaryClient),
|
|
302
|
+
aliaser: new Aliaser(payload.key, this.frameClient, this.unaryClient),
|
|
303
|
+
channels: this.channels,
|
|
304
|
+
labelClient: this.labelClient,
|
|
305
|
+
ontologyClient: this.ontologyClient,
|
|
306
|
+
rangeClient: this,
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
sugarMany(payloads: Payload[]): Range[] {
|
|
311
|
+
return payloads.map((payload) => this.sugarOne(payload));
|
|
336
312
|
}
|
|
337
313
|
|
|
338
314
|
resourceToRange(resource: ontology.Resource): Range {
|
|
@@ -341,12 +317,31 @@ export class Client implements AsyncTermSearcher<string, Key, Range> {
|
|
|
341
317
|
name: resource.data?.name as string,
|
|
342
318
|
timeRange: new TimeRange(resource.data?.timeRange as CrudeTimeRange),
|
|
343
319
|
color: resource.data?.color as string,
|
|
320
|
+
labels: [],
|
|
321
|
+
parent: null,
|
|
344
322
|
});
|
|
345
323
|
}
|
|
346
324
|
}
|
|
347
325
|
|
|
348
|
-
export const ontologyID = (key: Key): ontology.ID =>
|
|
349
|
-
|
|
326
|
+
export const ontologyID = (key: Key): ontology.ID => ({ type: "range", key });
|
|
327
|
+
|
|
328
|
+
export const aliasOntologyID = (key: Key): ontology.ID => ({
|
|
329
|
+
type: "range-alias",
|
|
330
|
+
key,
|
|
331
|
+
});
|
|
350
332
|
|
|
351
|
-
export const
|
|
352
|
-
|
|
333
|
+
export const convertOntologyResourceToPayload = ({
|
|
334
|
+
data,
|
|
335
|
+
id: { key },
|
|
336
|
+
name,
|
|
337
|
+
}: ontology.Resource): Payload => {
|
|
338
|
+
const timeRange = TimeRange.z.parse(data?.timeRange);
|
|
339
|
+
return {
|
|
340
|
+
key,
|
|
341
|
+
name,
|
|
342
|
+
timeRange,
|
|
343
|
+
color: typeof data?.color === "string" ? data.color : undefined,
|
|
344
|
+
labels: [],
|
|
345
|
+
parent: null,
|
|
346
|
+
};
|
|
347
|
+
};
|
package/src/ranger/external.ts
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
// License, use of this software will be governed by the Apache License, Version 2.0,
|
|
8
8
|
// included in the file licenses/APL.txt.
|
|
9
9
|
|
|
10
|
-
export
|
|
10
|
+
export * from "@/ranger/alias";
|
|
11
11
|
export * from "@/ranger/client";
|
|
12
12
|
export * from "@/ranger/kv";
|
|
13
13
|
export * from "@/ranger/payload";
|
package/src/ranger/kv.ts
CHANGED
|
@@ -10,16 +10,20 @@
|
|
|
10
10
|
import { sendRequired, type UnaryClient } from "@synnaxlabs/freighter";
|
|
11
11
|
import { array } from "@synnaxlabs/x/array";
|
|
12
12
|
import { isObject } from "@synnaxlabs/x/identity";
|
|
13
|
-
import { z } from "zod
|
|
13
|
+
import { z } from "zod";
|
|
14
14
|
|
|
15
|
-
import { type framer } from "@/framer";
|
|
16
15
|
import { type Key, keyZ } from "@/ranger/payload";
|
|
17
|
-
import { signals } from "@/signals";
|
|
18
16
|
import { nullableArrayZ } from "@/util/zod";
|
|
19
17
|
|
|
20
|
-
const
|
|
18
|
+
export const KV_SET_CHANNEL = "sy_range_kv_set";
|
|
19
|
+
export const KV_DELETE_CHANNEL = "sy_range_kv_delete";
|
|
20
|
+
|
|
21
|
+
export const kvPairZ = z.object({ range: keyZ, key: z.string(), value: z.string() });
|
|
21
22
|
export interface KVPair extends z.infer<typeof kvPairZ> {}
|
|
22
23
|
|
|
24
|
+
export const kvPairKey = ({ range, key }: Omit<KVPair, "value">) =>
|
|
25
|
+
`${range}<--->${key}`;
|
|
26
|
+
|
|
23
27
|
const getReqZ = z.object({ range: keyZ, keys: z.string().array() });
|
|
24
28
|
export interface GetRequest extends z.infer<typeof getReqZ> {}
|
|
25
29
|
|
|
@@ -37,12 +41,10 @@ export class KV {
|
|
|
37
41
|
private static readonly DELETE_ENDPOINT = "/range/kv/delete";
|
|
38
42
|
private readonly rangeKey: Key;
|
|
39
43
|
private readonly client: UnaryClient;
|
|
40
|
-
private readonly frameClient: framer.Client;
|
|
41
44
|
|
|
42
|
-
constructor(rng: Key, client: UnaryClient
|
|
45
|
+
constructor(rng: Key, client: UnaryClient) {
|
|
43
46
|
this.rangeKey = rng;
|
|
44
47
|
this.client = client;
|
|
45
|
-
this.frameClient = frameClient;
|
|
46
48
|
}
|
|
47
49
|
|
|
48
50
|
async get(key: string): Promise<string>;
|
|
@@ -92,24 +94,4 @@ export class KV {
|
|
|
92
94
|
z.unknown(),
|
|
93
95
|
);
|
|
94
96
|
}
|
|
95
|
-
|
|
96
|
-
async openTracker(): Promise<signals.Observable<string, KVPair>> {
|
|
97
|
-
return await signals.openObservable<string, KVPair>(
|
|
98
|
-
this.frameClient,
|
|
99
|
-
"sy_range_kv_set",
|
|
100
|
-
"sy_range_kv_delete",
|
|
101
|
-
(variant, data) => {
|
|
102
|
-
if (variant === "delete")
|
|
103
|
-
return data.toStrings().map((combinedKey) => {
|
|
104
|
-
const [range, key] = combinedKey.split("<--->", 2);
|
|
105
|
-
return { variant, key: combinedKey, value: { range, key, value: "" } };
|
|
106
|
-
});
|
|
107
|
-
return data.parseJSON(kvPairZ).map((pair) => ({
|
|
108
|
-
variant,
|
|
109
|
-
key: `${pair.range}${pair.key}`,
|
|
110
|
-
value: pair,
|
|
111
|
-
}));
|
|
112
|
-
},
|
|
113
|
-
);
|
|
114
|
-
}
|
|
115
97
|
}
|