@synnaxlabs/client 0.26.7 → 0.28.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +7 -7
- package/README.md +36 -10
- package/api/client.api.md +3121 -0
- package/api-extractor.json +7 -0
- package/dist/access/client.d.ts +0 -1
- package/dist/access/payload.d.ts +0 -1
- package/dist/auth/auth.d.ts +0 -1
- package/dist/channel/client.d.ts +6 -2
- package/dist/channel/client.d.ts.map +1 -1
- package/dist/channel/creator.d.ts +0 -1
- package/dist/channel/payload.d.ts +4 -1
- package/dist/channel/payload.d.ts.map +1 -1
- package/dist/channel/retriever.d.ts +0 -1
- package/dist/channel/writer.d.ts +0 -1
- package/dist/client.cjs +23 -19
- package/dist/client.d.ts +10 -7
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +2326 -1904
- package/dist/connection/checker.d.ts +21 -2
- package/dist/connection/checker.d.ts.map +1 -1
- package/dist/control/client.d.ts +0 -1
- package/dist/control/client.d.ts.map +1 -1
- package/dist/control/state.d.ts +0 -1
- package/dist/errors.d.ts +0 -1
- package/dist/framer/adapter.d.ts +0 -1
- package/dist/framer/client.d.ts +0 -1
- package/dist/framer/client.d.ts.map +1 -1
- package/dist/framer/deleter.d.ts +0 -1
- package/dist/framer/frame.d.ts +11 -12
- package/dist/framer/iterator.d.ts +0 -1
- package/dist/framer/streamProxy.d.ts +0 -1
- package/dist/framer/streamer.d.ts +0 -1
- package/dist/framer/writer.d.ts +0 -1
- package/dist/framer/writer.d.ts.map +1 -1
- package/dist/hardware/client.d.ts +0 -1
- package/dist/hardware/device/client.d.ts +0 -1
- package/dist/hardware/device/payload.d.ts +0 -1
- package/dist/hardware/device/payload.d.ts.map +1 -1
- package/dist/hardware/rack/client.d.ts +0 -1
- package/dist/hardware/rack/payload.d.ts +0 -1
- package/dist/hardware/rack/payload.d.ts.map +1 -1
- package/dist/hardware/task/client.d.ts +12 -3
- package/dist/hardware/task/client.d.ts.map +1 -1
- package/dist/hardware/task/ni/types.d.ts +14495 -0
- package/dist/hardware/task/ni/types.d.ts.map +1 -0
- package/dist/hardware/task/payload.d.ts +6 -1
- package/dist/hardware/task/payload.d.ts.map +1 -1
- package/dist/label/client.d.ts +8 -6
- package/dist/label/client.d.ts.map +1 -1
- package/dist/label/payload.d.ts +0 -1
- package/dist/label/retriever.d.ts +0 -1
- package/dist/label/retriever.d.ts.map +1 -1
- package/dist/label/writer.d.ts +32 -2
- package/dist/label/writer.d.ts.map +1 -1
- package/dist/ontology/client.d.ts +136 -12
- package/dist/ontology/client.d.ts.map +1 -1
- package/dist/ontology/group/client.d.ts +0 -1
- package/dist/ontology/group/group.d.ts +0 -1
- package/dist/ontology/group/payload.d.ts +0 -1
- package/dist/ontology/group/writer.d.ts +0 -1
- package/dist/ontology/group/writer.d.ts.map +1 -1
- package/dist/ontology/payload.d.ts +6 -3
- package/dist/ontology/payload.d.ts.map +1 -1
- package/dist/ontology/writer.d.ts +4 -5
- package/dist/ontology/writer.d.ts.map +1 -1
- package/dist/ranger/alias.d.ts +0 -1
- package/dist/ranger/client.d.ts +57 -14
- package/dist/ranger/client.d.ts.map +1 -1
- package/dist/ranger/external.d.ts +1 -1
- package/dist/ranger/external.d.ts.map +1 -1
- package/dist/ranger/kv.d.ts +42 -5
- package/dist/ranger/kv.d.ts.map +1 -1
- package/dist/ranger/payload.d.ts +5 -2
- package/dist/ranger/payload.d.ts.map +1 -1
- package/dist/ranger/writer.d.ts +107 -2
- package/dist/ranger/writer.d.ts.map +1 -1
- package/dist/setupspecs.d.ts +0 -1
- package/dist/signals/observable.d.ts +0 -1
- package/dist/transport.d.ts +0 -1
- package/dist/user/client.d.ts +0 -1
- package/dist/user/payload.d.ts +0 -1
- package/dist/util/retrieve.d.ts +0 -1
- package/dist/util/telem.d.ts +0 -1
- package/dist/util/zod.d.ts +0 -1
- package/dist/workspace/client.d.ts +0 -1
- package/dist/workspace/lineplot/client.d.ts +0 -1
- package/dist/workspace/lineplot/payload.d.ts +0 -1
- package/dist/workspace/lineplot/retriever.d.ts +0 -1
- package/dist/workspace/lineplot/writer.d.ts +0 -1
- package/dist/workspace/payload.d.ts +0 -1
- package/dist/workspace/retriever.d.ts +0 -1
- package/dist/workspace/schematic/client.d.ts +0 -1
- package/dist/workspace/schematic/payload.d.ts +0 -1
- package/dist/workspace/schematic/retriever.d.ts +0 -1
- package/dist/workspace/schematic/writer.d.ts +0 -1
- package/dist/workspace/writer.d.ts +0 -1
- package/package.json +14 -12
- package/src/access/access.spec.ts +11 -11
- package/src/channel/batchRetriever.spec.ts +2 -0
- package/src/channel/channel.spec.ts +51 -31
- package/src/channel/client.ts +7 -0
- package/src/channel/payload.ts +1 -0
- package/src/client.ts +17 -8
- package/src/connection/checker.ts +58 -1
- package/src/connection/connection.spec.ts +43 -3
- package/src/control/client.ts +9 -0
- package/src/errors.spec.ts +9 -0
- package/src/framer/client.ts +0 -1
- package/src/framer/frame.spec.ts +2 -2
- package/src/framer/frame.ts +22 -22
- package/src/framer/writer.ts +2 -1
- package/src/hardware/device/payload.ts +9 -0
- package/src/hardware/rack/payload.ts +9 -0
- package/src/hardware/task/client.ts +82 -6
- package/src/hardware/task/ni/types.ts +1716 -0
- package/src/hardware/task/payload.ts +10 -0
- package/src/hardware/task/task.spec.ts +45 -30
- package/src/label/client.ts +49 -19
- package/src/label/label.spec.ts +9 -0
- package/src/label/retriever.ts +2 -1
- package/src/label/writer.ts +11 -3
- package/src/ontology/client.ts +227 -14
- package/src/ontology/group/writer.ts +10 -12
- package/src/ontology/ontology.spec.ts +3 -5
- package/src/ontology/payload.ts +5 -1
- package/src/ontology/writer.ts +26 -12
- package/src/ranger/client.ts +223 -41
- package/src/ranger/external.ts +1 -1
- package/src/ranger/kv.ts +50 -11
- package/src/ranger/payload.ts +9 -5
- package/src/ranger/ranger.spec.ts +114 -49
- package/src/ranger/writer.ts +7 -2
- package/src/vite-env.d.ts +1 -1
- package/vite.config.ts +6 -1
- package/dist/ranger/active.d.ts +0 -11
- package/dist/ranger/active.d.ts.map +0 -1
- package/dist/ranger/range.d.ts +0 -32
- package/dist/ranger/range.d.ts.map +0 -1
- package/src/ranger/active.ts +0 -74
- package/src/ranger/range.ts +0 -98
|
@@ -104,11 +104,9 @@ describe("Ontology", () => {
|
|
|
104
104
|
});
|
|
105
105
|
it("should correctly propagate resource changes to the ontology", async () => {
|
|
106
106
|
const change = await client.ontology.openChangeTracker();
|
|
107
|
-
const p = new Promise<ontology.ResourceChange[]>((resolve) =>
|
|
108
|
-
change.resources.onChange((changes) =>
|
|
109
|
-
|
|
110
|
-
});
|
|
111
|
-
});
|
|
107
|
+
const p = new Promise<ontology.ResourceChange[]>((resolve) =>
|
|
108
|
+
change.resources.onChange((changes) => resolve(changes)),
|
|
109
|
+
);
|
|
112
110
|
await client.ontology.groups.create(ontology.Root, randomName());
|
|
113
111
|
const c = await p;
|
|
114
112
|
expect(c.length).toBeGreaterThan(0);
|
package/src/ontology/payload.ts
CHANGED
|
@@ -17,7 +17,7 @@ export type RelationshipChange = change.Change<Relationship, undefined>;
|
|
|
17
17
|
export type RelationshipSet = change.Set<Relationship, undefined>;
|
|
18
18
|
export type RelationshipDelete = change.Delete<Relationship, undefined>;
|
|
19
19
|
|
|
20
|
-
const resourceTypeZ = z.union([
|
|
20
|
+
export const resourceTypeZ = z.union([
|
|
21
21
|
z.literal("label"),
|
|
22
22
|
z.literal("builtin"),
|
|
23
23
|
z.literal("cluster"),
|
|
@@ -53,6 +53,8 @@ export const stringIDZ = z.string().transform((v) => {
|
|
|
53
53
|
|
|
54
54
|
export const crudeIDZ = z.union([stringIDZ, idZ]);
|
|
55
55
|
|
|
56
|
+
export type CrudeID = { type: ResourceType; key: string } | string;
|
|
57
|
+
|
|
56
58
|
export class ID {
|
|
57
59
|
type: ResourceType;
|
|
58
60
|
key: string;
|
|
@@ -119,6 +121,8 @@ export type Resource<T extends UnknownRecord = UnknownRecord> = Omit<
|
|
|
119
121
|
"data"
|
|
120
122
|
> & { data?: T | null };
|
|
121
123
|
|
|
124
|
+
export type RelationshipDirection = "from" | "to";
|
|
125
|
+
|
|
122
126
|
export const relationshipSchemaZ = z.object({
|
|
123
127
|
from: ID.z,
|
|
124
128
|
type: z.string(),
|
package/src/ontology/writer.ts
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
import { sendRequired, type UnaryClient } from "@synnaxlabs/freighter";
|
|
11
11
|
import { z } from "zod";
|
|
12
12
|
|
|
13
|
-
import { type ID,idZ } from "@/ontology/payload";
|
|
13
|
+
import { type CrudeID, ID, idZ } from "@/ontology/payload";
|
|
14
14
|
|
|
15
15
|
const ENDPOINTS = {
|
|
16
16
|
ADD_CHILDREN: "/ontology/add-children",
|
|
@@ -36,28 +36,42 @@ export class Writer {
|
|
|
36
36
|
this.client = client;
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
-
async addChildren(id:
|
|
39
|
+
async addChildren(id: CrudeID, ...children: CrudeID[]): Promise<void> {
|
|
40
40
|
await sendRequired<typeof addRemoveChildrenReqZ, z.ZodTypeAny>(
|
|
41
|
-
this.client,
|
|
42
|
-
ENDPOINTS.ADD_CHILDREN,
|
|
43
|
-
{ id, children },
|
|
44
|
-
addRemoveChildrenReqZ,
|
|
45
|
-
z.object({})
|
|
41
|
+
this.client,
|
|
42
|
+
ENDPOINTS.ADD_CHILDREN,
|
|
43
|
+
{ id: new ID(id).payload, children: children.map((c) => new ID(c).payload) },
|
|
44
|
+
addRemoveChildrenReqZ,
|
|
45
|
+
z.object({}),
|
|
46
46
|
);
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
-
async removeChildren(id:
|
|
49
|
+
async removeChildren(id: CrudeID, ...children: CrudeID[]): Promise<void> {
|
|
50
50
|
await sendRequired<typeof addRemoveChildrenReqZ, z.ZodTypeAny>(
|
|
51
51
|
this.client,
|
|
52
52
|
ENDPOINTS.REMOVE_CHILDREN,
|
|
53
|
-
{ id, children },
|
|
53
|
+
{ id: new ID(id).payload, children: children.map((c) => new ID(c).payload) },
|
|
54
54
|
addRemoveChildrenReqZ,
|
|
55
55
|
z.object({}),
|
|
56
56
|
);
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
-
async moveChildren(
|
|
60
|
-
|
|
61
|
-
|
|
59
|
+
async moveChildren(
|
|
60
|
+
from: CrudeID,
|
|
61
|
+
to: CrudeID,
|
|
62
|
+
...children: CrudeID[]
|
|
63
|
+
): Promise<void> {
|
|
64
|
+
const req = {
|
|
65
|
+
from: new ID(from).payload,
|
|
66
|
+
to: new ID(to).payload,
|
|
67
|
+
children: children.map((c) => new ID(c).payload),
|
|
68
|
+
};
|
|
69
|
+
await sendRequired(
|
|
70
|
+
this.client,
|
|
71
|
+
ENDPOINTS.MOVE_CHILDREN,
|
|
72
|
+
req,
|
|
73
|
+
moveChildrenReqZ,
|
|
74
|
+
z.object({}),
|
|
75
|
+
);
|
|
62
76
|
}
|
|
63
77
|
}
|
package/src/ranger/client.ts
CHANGED
|
@@ -8,17 +8,22 @@
|
|
|
8
8
|
// included in the file licenses/APL.txt.
|
|
9
9
|
|
|
10
10
|
import { sendRequired, type UnaryClient } from "@synnaxlabs/freighter";
|
|
11
|
-
import { CrudeTimeRange, TimeRange } from "@synnaxlabs/x";
|
|
11
|
+
import { CrudeTimeRange, observe, TimeRange } from "@synnaxlabs/x";
|
|
12
12
|
import { type AsyncTermSearcher } from "@synnaxlabs/x/search";
|
|
13
|
+
import { type Series } from "@synnaxlabs/x/telem";
|
|
13
14
|
import { toArray } from "@synnaxlabs/x/toArray";
|
|
14
15
|
import { z } from "zod";
|
|
15
16
|
|
|
17
|
+
import { Key as ChannelKey } from "@/channel/payload";
|
|
16
18
|
import { type Retriever as ChannelRetriever } from "@/channel/retriever";
|
|
17
19
|
import { MultipleFoundError, NotFoundError } from "@/errors";
|
|
20
|
+
import { QueryError } from "@/errors";
|
|
18
21
|
import { type framer } from "@/framer";
|
|
19
22
|
import { type label } from "@/label";
|
|
20
|
-
import {
|
|
21
|
-
import {
|
|
23
|
+
import { type Label } from "@/label/payload";
|
|
24
|
+
import { ontology } from "@/ontology";
|
|
25
|
+
import { Resource } from "@/ontology/payload";
|
|
26
|
+
import { type Alias, Aliaser } from "@/ranger/alias";
|
|
22
27
|
import { KV } from "@/ranger/kv";
|
|
23
28
|
import {
|
|
24
29
|
analyzeParams,
|
|
@@ -32,16 +37,170 @@ import {
|
|
|
32
37
|
type Payload,
|
|
33
38
|
payloadZ,
|
|
34
39
|
} from "@/ranger/payload";
|
|
35
|
-
import {
|
|
36
|
-
import { type Writer } from "@/ranger/writer";
|
|
40
|
+
import { CreateOptions, type Writer } from "@/ranger/writer";
|
|
37
41
|
import { signals } from "@/signals";
|
|
38
42
|
import { nullableArrayZ } from "@/util/zod";
|
|
39
43
|
|
|
44
|
+
const ontologyID = (key: string): ontology.ID =>
|
|
45
|
+
new ontology.ID({ type: "range", key });
|
|
46
|
+
|
|
47
|
+
export class Range {
|
|
48
|
+
key: string;
|
|
49
|
+
name: string;
|
|
50
|
+
readonly kv: KV;
|
|
51
|
+
readonly timeRange: TimeRange;
|
|
52
|
+
readonly color: string | undefined;
|
|
53
|
+
readonly channels: ChannelRetriever;
|
|
54
|
+
private readonly aliaser: Aliaser;
|
|
55
|
+
private readonly frameClient: framer.Client;
|
|
56
|
+
private readonly labelClient: label.Client;
|
|
57
|
+
private readonly ontologyClient: ontology.Client;
|
|
58
|
+
private readonly rangeClient: Client;
|
|
59
|
+
|
|
60
|
+
constructor(
|
|
61
|
+
name: string,
|
|
62
|
+
timeRange: TimeRange = TimeRange.ZERO,
|
|
63
|
+
key: string,
|
|
64
|
+
color: string | undefined,
|
|
65
|
+
_frameClient: framer.Client,
|
|
66
|
+
_kv: KV,
|
|
67
|
+
_aliaser: Aliaser,
|
|
68
|
+
_channels: ChannelRetriever,
|
|
69
|
+
_labelClient: label.Client,
|
|
70
|
+
_ontologyClient: ontology.Client,
|
|
71
|
+
_rangeClient: Client,
|
|
72
|
+
) {
|
|
73
|
+
this.key = key;
|
|
74
|
+
this.name = name;
|
|
75
|
+
this.timeRange = timeRange;
|
|
76
|
+
this.frameClient = _frameClient;
|
|
77
|
+
this.color = color;
|
|
78
|
+
this.kv = _kv;
|
|
79
|
+
this.aliaser = _aliaser;
|
|
80
|
+
this.channels = _channels;
|
|
81
|
+
this.labelClient = _labelClient;
|
|
82
|
+
this.ontologyClient = _ontologyClient;
|
|
83
|
+
this.rangeClient = _rangeClient;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
get ontologyID(): ontology.ID {
|
|
87
|
+
return new ontology.ID({ key: this.key, type: "range" });
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
get payload(): Payload {
|
|
91
|
+
return {
|
|
92
|
+
key: this.key,
|
|
93
|
+
name: this.name,
|
|
94
|
+
timeRange: this.timeRange,
|
|
95
|
+
color: this.color,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
async setAlias(channel: ChannelKey | Name, alias: string): Promise<void> {
|
|
100
|
+
const ch = await this.channels.retrieve(channel);
|
|
101
|
+
if (ch.length === 0) {
|
|
102
|
+
throw new QueryError(`Channel ${channel} does not exist`);
|
|
103
|
+
}
|
|
104
|
+
await this.aliaser.set({ [ch[0].key]: alias });
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
async deleteAlias(...channels: ChannelKey[]): Promise<void> {
|
|
108
|
+
await this.aliaser.delete(channels);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
async listAliases(): Promise<Record<ChannelKey, string>> {
|
|
112
|
+
return await this.aliaser.list();
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
async resolveAlias(alias: string): Promise<ChannelKey> {
|
|
116
|
+
return await this.aliaser.resolve(alias);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
async openAliasTracker(): Promise<signals.Observable<string, Alias>> {
|
|
120
|
+
return await this.aliaser.openChangeTracker();
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
async retrieveParent(): Promise<Range | null> {
|
|
124
|
+
return this.rangeClient.retrieveParent(this.key);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
async retrieveChildren(): Promise<Range[]> {
|
|
128
|
+
const res = (
|
|
129
|
+
await this.ontologyClient.retrieveChildren(this.ontologyID, {
|
|
130
|
+
excludeFieldData: true,
|
|
131
|
+
types: ["range"],
|
|
132
|
+
})
|
|
133
|
+
).map((r) => r.id.key);
|
|
134
|
+
return await this.rangeClient.retrieve(res);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
async read(channel: Key | Name): Promise<Series>;
|
|
138
|
+
|
|
139
|
+
async read(channels: Params): Promise<framer.Frame>;
|
|
140
|
+
|
|
141
|
+
async read(channels: Params): Promise<Series | framer.Frame> {
|
|
142
|
+
return await this.frameClient.read(this.timeRange, channels);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
async labels(): Promise<Label[]> {
|
|
146
|
+
return await this.labelClient.retrieveFor(ontologyID(this.key));
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
async addLabel(...labels: label.Key[]): Promise<void> {
|
|
150
|
+
await this.labelClient.label(ontologyID(this.key), labels);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
async removeLabel(...labels: label.Key[]): Promise<void> {
|
|
154
|
+
await this.labelClient.removeLabels(ontologyID(this.key), labels);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
async openChildRangeTracker(): Promise<observe.ObservableAsyncCloseable<Range[]>> {
|
|
158
|
+
const wrapper = new observe.Observer<Range[]>();
|
|
159
|
+
const initial: ontology.Resource[] = (await this.retrieveChildren()).map((r) => {
|
|
160
|
+
const id = new ontology.ID({ key: r.key, type: "range" });
|
|
161
|
+
return { id, key: id.toString(), name: r.name, data: r.payload };
|
|
162
|
+
});
|
|
163
|
+
const base = await this.ontologyClient.openDependentTracker({
|
|
164
|
+
target: this.ontologyID,
|
|
165
|
+
dependents: initial,
|
|
166
|
+
resourceType: "range",
|
|
167
|
+
});
|
|
168
|
+
base.onChange((r: Resource[]) =>
|
|
169
|
+
wrapper.notify(this.rangeClient.resourcesToRanges(r)),
|
|
170
|
+
);
|
|
171
|
+
wrapper.setCloser(async () => await base.close());
|
|
172
|
+
return wrapper;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
async openParentRangeTracker(): Promise<observe.ObservableAsyncCloseable<Range> | null> {
|
|
176
|
+
const wrapper = new observe.Observer<Range>();
|
|
177
|
+
const p = await this.retrieveParent();
|
|
178
|
+
if (p == null) return null;
|
|
179
|
+
const id = new ontology.ID({ key: p.key, type: "range" });
|
|
180
|
+
const resourceP = { id, key: id.toString(), name: p.name, data: p.payload };
|
|
181
|
+
const base = await this.ontologyClient.openDependentTracker({
|
|
182
|
+
target: this.ontologyID,
|
|
183
|
+
dependents: [resourceP],
|
|
184
|
+
relationshipDirection: "to",
|
|
185
|
+
});
|
|
186
|
+
base.onChange((resources: Resource[]) => {
|
|
187
|
+
const ranges = this.rangeClient.resourcesToRanges(resources);
|
|
188
|
+
if (ranges.length === 0) return;
|
|
189
|
+
const p = ranges[0];
|
|
190
|
+
wrapper.notify(p);
|
|
191
|
+
});
|
|
192
|
+
wrapper.setCloser(async () => await base.close());
|
|
193
|
+
return wrapper;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
40
197
|
const retrieveReqZ = z.object({
|
|
41
198
|
keys: keyZ.array().optional(),
|
|
42
199
|
names: z.array(z.string()).optional(),
|
|
43
200
|
term: z.string().optional(),
|
|
44
201
|
overlapsWith: TimeRange.z.optional(),
|
|
202
|
+
limit: z.number().int().optional(),
|
|
203
|
+
offset: z.number().int().optional(),
|
|
45
204
|
});
|
|
46
205
|
|
|
47
206
|
export type RetrieveRequest = z.infer<typeof retrieveReqZ>;
|
|
@@ -58,8 +217,8 @@ export class Client implements AsyncTermSearcher<string, Key, Range> {
|
|
|
58
217
|
private readonly writer: Writer;
|
|
59
218
|
private readonly unaryClient: UnaryClient;
|
|
60
219
|
private readonly channels: ChannelRetriever;
|
|
61
|
-
private readonly active: Active;
|
|
62
220
|
private readonly labelClient: label.Client;
|
|
221
|
+
private readonly ontologyClient: ontology.Client;
|
|
63
222
|
|
|
64
223
|
constructor(
|
|
65
224
|
frameClient: framer.Client,
|
|
@@ -67,22 +226,26 @@ export class Client implements AsyncTermSearcher<string, Key, Range> {
|
|
|
67
226
|
unary: UnaryClient,
|
|
68
227
|
channels: ChannelRetriever,
|
|
69
228
|
labelClient: label.Client,
|
|
229
|
+
ontologyClient: ontology.Client,
|
|
70
230
|
) {
|
|
71
231
|
this.frameClient = frameClient;
|
|
72
232
|
this.writer = writer;
|
|
73
233
|
this.unaryClient = unary;
|
|
74
234
|
this.channels = channels;
|
|
75
|
-
this.active = new Active(unary);
|
|
76
235
|
this.labelClient = labelClient;
|
|
236
|
+
this.ontologyClient = ontologyClient;
|
|
77
237
|
}
|
|
78
238
|
|
|
79
|
-
async create(range: NewPayload): Promise<Range>;
|
|
239
|
+
async create(range: NewPayload, options?: CreateOptions): Promise<Range>;
|
|
80
240
|
|
|
81
|
-
async create(ranges: NewPayload[]): Promise<Range[]>;
|
|
241
|
+
async create(ranges: NewPayload[], options?: CreateOptions): Promise<Range[]>;
|
|
82
242
|
|
|
83
|
-
async create(
|
|
243
|
+
async create(
|
|
244
|
+
ranges: NewPayload | NewPayload[],
|
|
245
|
+
options?: CreateOptions,
|
|
246
|
+
): Promise<Range | Range[]> {
|
|
84
247
|
const single = !Array.isArray(ranges);
|
|
85
|
-
const res = this.
|
|
248
|
+
const res = this.sugarMany(await this.writer.create(toArray(ranges), options));
|
|
86
249
|
return single ? res[0] : res;
|
|
87
250
|
}
|
|
88
251
|
|
|
@@ -95,11 +258,11 @@ export class Client implements AsyncTermSearcher<string, Key, Range> {
|
|
|
95
258
|
}
|
|
96
259
|
|
|
97
260
|
async search(term: string): Promise<Range[]> {
|
|
98
|
-
return this.
|
|
261
|
+
return this.sugarMany(await this.execRetrieve({ term }));
|
|
99
262
|
}
|
|
100
263
|
|
|
101
|
-
async page(): Promise<Range[]> {
|
|
102
|
-
return
|
|
264
|
+
async page(offset: number, limit: number): Promise<Range[]> {
|
|
265
|
+
return this.sugarMany(await this.execRetrieve({ offset, limit }));
|
|
103
266
|
}
|
|
104
267
|
|
|
105
268
|
async retrieve(range: CrudeTimeRange): Promise<Range[]>;
|
|
@@ -111,7 +274,8 @@ export class Client implements AsyncTermSearcher<string, Key, Range> {
|
|
|
111
274
|
async retrieve(params: Params | CrudeTimeRange): Promise<Range | Range[]> {
|
|
112
275
|
if (typeof params === "object" && "start" in params)
|
|
113
276
|
return await this.execRetrieve({ overlapsWith: new TimeRange(params) });
|
|
114
|
-
const { single, actual, variant, normalized } = analyzeParams(params);
|
|
277
|
+
const { single, actual, variant, normalized, empty } = analyzeParams(params);
|
|
278
|
+
if (empty) return [];
|
|
115
279
|
const ranges = await this.execRetrieve({ [variant]: normalized });
|
|
116
280
|
if (!single) return ranges;
|
|
117
281
|
if (ranges.length === 0)
|
|
@@ -121,6 +285,10 @@ export class Client implements AsyncTermSearcher<string, Key, Range> {
|
|
|
121
285
|
return ranges[0];
|
|
122
286
|
}
|
|
123
287
|
|
|
288
|
+
getKV(range: Key): KV {
|
|
289
|
+
return new KV(range, this.unaryClient, this.frameClient);
|
|
290
|
+
}
|
|
291
|
+
|
|
124
292
|
private async execRetrieve(req: RetrieveRequest): Promise<Range[]> {
|
|
125
293
|
const { ranges } = await sendRequired<typeof retrieveReqZ, typeof retrieveResZ>(
|
|
126
294
|
this.unaryClient,
|
|
@@ -129,37 +297,38 @@ export class Client implements AsyncTermSearcher<string, Key, Range> {
|
|
|
129
297
|
retrieveReqZ,
|
|
130
298
|
retrieveResZ,
|
|
131
299
|
);
|
|
132
|
-
return this.
|
|
300
|
+
return this.sugarMany(ranges);
|
|
133
301
|
}
|
|
134
302
|
|
|
135
|
-
async
|
|
136
|
-
await this.
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
303
|
+
async retrieveParent(range: Key): Promise<Range | null> {
|
|
304
|
+
const res = await this.ontologyClient.retrieveParents({
|
|
305
|
+
key: range,
|
|
306
|
+
type: "range",
|
|
307
|
+
});
|
|
308
|
+
if (res.length === 0) return null;
|
|
309
|
+
const first = res[0];
|
|
310
|
+
if (first.id.type !== "range") return null;
|
|
311
|
+
return await this.retrieve(first.id.key);
|
|
143
312
|
}
|
|
144
313
|
|
|
145
|
-
|
|
146
|
-
|
|
314
|
+
sugarOne(payload: Payload): Range {
|
|
315
|
+
return new Range(
|
|
316
|
+
payload.name,
|
|
317
|
+
payload.timeRange,
|
|
318
|
+
payload.key,
|
|
319
|
+
payload.color,
|
|
320
|
+
this.frameClient,
|
|
321
|
+
new KV(payload.key, this.unaryClient, this.frameClient),
|
|
322
|
+
new Aliaser(payload.key, this.frameClient, this.unaryClient),
|
|
323
|
+
this.channels,
|
|
324
|
+
this.labelClient,
|
|
325
|
+
this.ontologyClient,
|
|
326
|
+
this,
|
|
327
|
+
);
|
|
147
328
|
}
|
|
148
329
|
|
|
149
|
-
|
|
150
|
-
return payloads.map((payload) =>
|
|
151
|
-
return new Range(
|
|
152
|
-
payload.name,
|
|
153
|
-
payload.timeRange,
|
|
154
|
-
payload.key,
|
|
155
|
-
payload.color,
|
|
156
|
-
this.frameClient,
|
|
157
|
-
new KV(payload.key, this.unaryClient),
|
|
158
|
-
new Aliaser(payload.key, this.frameClient, this.unaryClient),
|
|
159
|
-
this.channels,
|
|
160
|
-
this.labelClient,
|
|
161
|
-
);
|
|
162
|
-
});
|
|
330
|
+
sugarMany(payloads: Payload[]): Range[] {
|
|
331
|
+
return payloads.map((payload) => this.sugarOne(payload));
|
|
163
332
|
}
|
|
164
333
|
|
|
165
334
|
async openTracker(): Promise<signals.Observable<string, Range>> {
|
|
@@ -170,9 +339,22 @@ export class Client implements AsyncTermSearcher<string, Key, Range> {
|
|
|
170
339
|
(variant, data) => {
|
|
171
340
|
if (variant === "delete")
|
|
172
341
|
return data.toStrings().map((k) => ({ variant, key: k, value: undefined }));
|
|
173
|
-
const sugared = this.
|
|
342
|
+
const sugared = this.sugarMany(data.parseJSON(payloadZ));
|
|
174
343
|
return sugared.map((r) => ({ variant, key: r.key, value: r }));
|
|
175
344
|
},
|
|
176
345
|
);
|
|
177
346
|
}
|
|
347
|
+
|
|
348
|
+
resourcesToRanges(resources: Resource[]): Range[] {
|
|
349
|
+
return resources.map((r) => this.resourceToRange(r));
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
resourceToRange(resource: Resource): Range {
|
|
353
|
+
return this.sugarOne({
|
|
354
|
+
key: resource.id.key,
|
|
355
|
+
name: resource.data?.name as string,
|
|
356
|
+
timeRange: new TimeRange(resource.data?.timeRange as CrudeTimeRange),
|
|
357
|
+
color: resource.data?.color as string,
|
|
358
|
+
});
|
|
359
|
+
}
|
|
178
360
|
}
|
package/src/ranger/external.ts
CHANGED
package/src/ranger/kv.ts
CHANGED
|
@@ -7,27 +7,36 @@
|
|
|
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 { sendRequired,type UnaryClient } from "@synnaxlabs/freighter";
|
|
10
|
+
import { sendRequired, type UnaryClient } from "@synnaxlabs/freighter";
|
|
11
11
|
import { isObject } from "@synnaxlabs/x/identity";
|
|
12
12
|
import { toArray } from "@synnaxlabs/x/toArray";
|
|
13
13
|
import { z } from "zod";
|
|
14
14
|
|
|
15
|
+
import { framer } from "@/framer";
|
|
15
16
|
import { type Key, keyZ } from "@/ranger/payload";
|
|
17
|
+
import { signals } from "@/signals";
|
|
18
|
+
import { nullableArrayZ } from "@/util/zod";
|
|
16
19
|
|
|
17
20
|
const getReqZ = z.object({
|
|
18
21
|
range: keyZ,
|
|
19
22
|
keys: z.string().array(),
|
|
20
23
|
});
|
|
21
24
|
|
|
25
|
+
const kvPairZ = z.object({
|
|
26
|
+
range: keyZ,
|
|
27
|
+
key: z.string(),
|
|
28
|
+
value: z.string(),
|
|
29
|
+
});
|
|
30
|
+
|
|
22
31
|
const getResZ = z.object({
|
|
23
|
-
pairs:
|
|
32
|
+
pairs: nullableArrayZ(kvPairZ),
|
|
24
33
|
});
|
|
25
34
|
|
|
26
35
|
export type GetRequest = z.infer<typeof getReqZ>;
|
|
27
36
|
|
|
28
37
|
const setReqZ = z.object({
|
|
29
38
|
range: keyZ,
|
|
30
|
-
pairs:
|
|
39
|
+
pairs: kvPairZ.array(),
|
|
31
40
|
});
|
|
32
41
|
|
|
33
42
|
export type SetRequest = z.infer<typeof setReqZ>;
|
|
@@ -39,16 +48,20 @@ const deleteReqZ = z.object({
|
|
|
39
48
|
|
|
40
49
|
export type DeleteRequest = z.infer<typeof deleteReqZ>;
|
|
41
50
|
|
|
51
|
+
export type KVPair = z.infer<typeof kvPairZ>;
|
|
52
|
+
|
|
42
53
|
export class KV {
|
|
43
54
|
private static readonly GET_ENDPOINT = "/range/kv/get";
|
|
44
55
|
private static readonly SET_ENDPOINT = "/range/kv/set";
|
|
45
56
|
private static readonly DELETE_ENDPOINT = "/range/kv/delete";
|
|
46
57
|
private readonly rangeKey: Key;
|
|
47
58
|
private readonly client: UnaryClient;
|
|
59
|
+
private readonly frameClient: framer.Client;
|
|
48
60
|
|
|
49
|
-
constructor(rng: Key, client: UnaryClient) {
|
|
61
|
+
constructor(rng: Key, client: UnaryClient, frameClient: framer.Client) {
|
|
50
62
|
this.rangeKey = rng;
|
|
51
63
|
this.client = client;
|
|
64
|
+
this.frameClient = frameClient;
|
|
52
65
|
}
|
|
53
66
|
|
|
54
67
|
async get(key: string): Promise<string>;
|
|
@@ -56,14 +69,15 @@ export class KV {
|
|
|
56
69
|
async get(keys: string[]): Promise<Record<string, string>>;
|
|
57
70
|
|
|
58
71
|
async get(keys: string | string[]): Promise<string | Record<string, string>> {
|
|
59
|
-
const
|
|
72
|
+
const res = await sendRequired(
|
|
73
|
+
this.client,
|
|
60
74
|
KV.GET_ENDPOINT,
|
|
61
75
|
{ range: this.rangeKey, keys: toArray(keys) },
|
|
62
76
|
getReqZ,
|
|
63
77
|
getResZ,
|
|
64
78
|
);
|
|
65
|
-
if (
|
|
66
|
-
return
|
|
79
|
+
if (typeof keys === "string") return res.pairs[0].value;
|
|
80
|
+
return Object.fromEntries(res.pairs.map((pair) => [pair.key, pair.value]));
|
|
67
81
|
}
|
|
68
82
|
|
|
69
83
|
async list(): Promise<Record<string, string>> {
|
|
@@ -75,13 +89,18 @@ export class KV {
|
|
|
75
89
|
async set(kv: Record<string, string>): Promise<void>;
|
|
76
90
|
|
|
77
91
|
async set(key: string | Record<string, string>, value: string = ""): Promise<void> {
|
|
92
|
+
let pairs: KVPair[];
|
|
93
|
+
if (isObject(key))
|
|
94
|
+
pairs = Object.entries(key).map(([k, v]) => ({
|
|
95
|
+
range: this.rangeKey,
|
|
96
|
+
key: k,
|
|
97
|
+
value: v,
|
|
98
|
+
}));
|
|
99
|
+
else pairs = [{ range: this.rangeKey, key: key, value: value }];
|
|
78
100
|
await sendRequired(
|
|
79
101
|
this.client,
|
|
80
102
|
KV.SET_ENDPOINT,
|
|
81
|
-
{
|
|
82
|
-
range: this.rangeKey,
|
|
83
|
-
pairs: isObject(key) ? key : { [key]: value },
|
|
84
|
-
},
|
|
103
|
+
{ range: this.rangeKey, pairs },
|
|
85
104
|
setReqZ,
|
|
86
105
|
z.unknown(),
|
|
87
106
|
);
|
|
@@ -96,4 +115,24 @@ export class KV {
|
|
|
96
115
|
z.unknown(),
|
|
97
116
|
);
|
|
98
117
|
}
|
|
118
|
+
|
|
119
|
+
async openTracker(): Promise<signals.Observable<string, KVPair>> {
|
|
120
|
+
return await signals.openObservable<string, KVPair>(
|
|
121
|
+
this.frameClient,
|
|
122
|
+
"sy_range_kv_set",
|
|
123
|
+
"sy_range_kv_delete",
|
|
124
|
+
(variant, data) => {
|
|
125
|
+
if (variant === "delete")
|
|
126
|
+
return data.toStrings().map((combinedKey) => {
|
|
127
|
+
const [range, key] = combinedKey.split("<--->", 2);
|
|
128
|
+
return { variant, key: combinedKey, value: { range, key, value: "" } };
|
|
129
|
+
});
|
|
130
|
+
return data.parseJSON(kvPairZ).map((pair) => ({
|
|
131
|
+
variant,
|
|
132
|
+
key: `${pair.range}${pair.key}`,
|
|
133
|
+
value: pair,
|
|
134
|
+
}));
|
|
135
|
+
},
|
|
136
|
+
);
|
|
137
|
+
}
|
|
99
138
|
}
|
package/src/ranger/payload.ts
CHANGED
|
@@ -31,7 +31,7 @@ export type Payload = z.infer<typeof payloadZ>;
|
|
|
31
31
|
export const newPayloadZ = payloadZ.extend({
|
|
32
32
|
key: z.string().uuid().optional(),
|
|
33
33
|
});
|
|
34
|
-
export type NewPayload = z.
|
|
34
|
+
export type NewPayload = z.input<typeof newPayloadZ>;
|
|
35
35
|
|
|
36
36
|
export type ParamAnalysisResult =
|
|
37
37
|
| {
|
|
@@ -39,37 +39,41 @@ export type ParamAnalysisResult =
|
|
|
39
39
|
variant: "keys";
|
|
40
40
|
normalized: Keys;
|
|
41
41
|
actual: Key;
|
|
42
|
+
empty: never;
|
|
42
43
|
}
|
|
43
44
|
| {
|
|
44
45
|
single: true;
|
|
45
46
|
variant: "names";
|
|
46
47
|
normalized: Names;
|
|
47
48
|
actual: Name;
|
|
49
|
+
empty: never;
|
|
48
50
|
}
|
|
49
51
|
| {
|
|
50
52
|
single: false;
|
|
51
53
|
variant: "keys";
|
|
52
54
|
normalized: Keys;
|
|
53
55
|
actual: Keys;
|
|
56
|
+
empty: boolean;
|
|
54
57
|
}
|
|
55
58
|
| {
|
|
56
59
|
single: false;
|
|
57
60
|
variant: "names";
|
|
58
61
|
normalized: Names;
|
|
59
62
|
actual: Names;
|
|
63
|
+
empty: boolean;
|
|
60
64
|
};
|
|
61
65
|
|
|
62
66
|
export const analyzeParams = (params: Params): ParamAnalysisResult => {
|
|
63
67
|
const normal = toArray(params) as Keys | Names;
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
const isKey = keyZ.safeParse(normal[0]).success;
|
|
68
|
+
const empty = normal.length === 0;
|
|
69
|
+
let isKey = false;
|
|
70
|
+
if (!empty) isKey = keyZ.safeParse(normal[0]).success;
|
|
68
71
|
return {
|
|
69
72
|
single: !Array.isArray(params),
|
|
70
73
|
variant: isKey ? "keys" : "names",
|
|
71
74
|
normalized: normal,
|
|
72
75
|
actual: params,
|
|
76
|
+
empty,
|
|
73
77
|
} as const as ParamAnalysisResult;
|
|
74
78
|
};
|
|
75
79
|
|