@synnaxlabs/client 0.20.0 → 0.22.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/.vscode/settings.json +3 -0
- package/README.md +13 -0
- package/dist/auth/auth.d.ts +7 -6
- package/dist/auth/auth.d.ts.map +1 -1
- package/dist/channel/client.d.ts +14 -11
- package/dist/channel/client.d.ts.map +1 -1
- package/dist/channel/creator.d.ts +10 -0
- package/dist/channel/creator.d.ts.map +1 -0
- package/dist/channel/payload.d.ts +6 -2
- package/dist/channel/payload.d.ts.map +1 -1
- package/dist/channel/retriever.d.ts +65 -35
- package/dist/channel/retriever.d.ts.map +1 -1
- package/dist/channel/writer.d.ts +4 -3
- package/dist/client.cjs +19 -22
- package/dist/client.cjs.map +1 -1
- package/dist/client.d.ts +12 -10
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +6781 -6675
- package/dist/client.js.map +1 -1
- package/dist/connection/checker.d.ts +4 -3
- package/dist/connection/checker.d.ts.map +1 -1
- package/dist/control/authority.d.ts +2 -1
- package/dist/control/state.d.ts +6 -4
- package/dist/control/state.d.ts.map +1 -1
- package/dist/errors.d.ts +24 -20
- package/dist/errors.d.ts.map +1 -1
- package/dist/framer/adapter.d.ts +5 -4
- package/dist/framer/adapter.d.ts.map +1 -1
- package/dist/framer/client.d.ts +12 -12
- package/dist/framer/client.d.ts.map +1 -1
- package/dist/framer/frame.d.ts +13 -12
- package/dist/framer/frame.d.ts.map +1 -1
- package/dist/framer/iterator.d.ts +5 -4
- package/dist/framer/iterator.d.ts.map +1 -1
- package/dist/framer/streamProxy.d.ts +3 -2
- package/dist/framer/streamProxy.d.ts.map +1 -1
- package/dist/framer/streamer.d.ts +13 -4
- package/dist/framer/streamer.d.ts.map +1 -1
- package/dist/framer/writer.d.ts +67 -33
- package/dist/framer/writer.d.ts.map +1 -1
- package/dist/hardware/client.d.ts +4 -3
- package/dist/hardware/device/client.d.ts +107 -14
- package/dist/hardware/device/client.d.ts.map +1 -1
- package/dist/hardware/device/external.d.ts +0 -2
- package/dist/hardware/device/external.d.ts.map +1 -1
- package/dist/hardware/device/index.d.ts +1 -1
- package/dist/hardware/device/index.d.ts.map +1 -1
- package/dist/hardware/rack/client.d.ts +43 -20
- package/dist/hardware/rack/client.d.ts.map +1 -1
- package/dist/hardware/rack/external.d.ts +0 -2
- package/dist/hardware/rack/external.d.ts.map +1 -1
- package/dist/hardware/task/client.d.ts +197 -14
- package/dist/hardware/task/client.d.ts.map +1 -1
- package/dist/hardware/task/index.d.ts +1 -1
- package/dist/hardware/task/index.d.ts.map +1 -1
- package/dist/index.d.ts +4 -3
- package/dist/index.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 +2 -1
- package/dist/label/retriever.d.ts +3 -2
- package/dist/label/writer.d.ts +4 -3
- package/dist/ontology/client.d.ts +82 -14
- package/dist/ontology/client.d.ts.map +1 -1
- package/dist/ontology/external.d.ts +0 -1
- package/dist/ontology/external.d.ts.map +1 -1
- package/dist/ontology/group/client.d.ts +3 -2
- package/dist/ontology/group/client.d.ts.map +1 -1
- package/dist/ontology/group/group.d.ts +1 -0
- package/dist/ontology/group/payload.d.ts +2 -1
- package/dist/ontology/group/writer.d.ts +4 -3
- package/dist/ontology/payload.d.ts +71 -60
- package/dist/ontology/payload.d.ts.map +1 -1
- package/dist/ontology/writer.d.ts +3 -2
- package/dist/ranger/active.d.ts +3 -2
- package/dist/ranger/alias.d.ts +7 -6
- package/dist/ranger/alias.d.ts.map +1 -1
- package/dist/ranger/client.d.ts +162 -11
- package/dist/ranger/client.d.ts.map +1 -1
- package/dist/ranger/external.d.ts +0 -1
- package/dist/ranger/external.d.ts.map +1 -1
- package/dist/ranger/kv.d.ts +5 -3
- package/dist/ranger/kv.d.ts.map +1 -1
- package/dist/ranger/payload.d.ts +57 -50
- package/dist/ranger/payload.d.ts.map +1 -1
- package/dist/ranger/range.d.ts +12 -10
- package/dist/ranger/range.d.ts.map +1 -1
- package/dist/ranger/writer.d.ts +3 -2
- package/dist/setupspecs.d.ts +2 -1
- package/dist/signals/observable.d.ts +8 -15
- package/dist/signals/observable.d.ts.map +1 -1
- package/dist/transport.d.ts +3 -2
- package/dist/transport.d.ts.map +1 -1
- package/dist/user/payload.d.ts +2 -1
- package/dist/util/retrieve.d.ts +24 -0
- package/dist/util/retrieve.d.ts.map +1 -0
- package/dist/util/retrieve.spec.d.ts +2 -0
- package/dist/util/retrieve.spec.d.ts.map +1 -0
- package/dist/util/telem.d.ts +2 -1
- package/dist/util/telem.d.ts.map +1 -1
- package/dist/util/zod.d.ts +4 -0
- package/dist/util/zod.d.ts.map +1 -0
- package/dist/workspace/client.d.ts +9 -6
- package/dist/workspace/client.d.ts.map +1 -1
- package/dist/workspace/lineplot/client.d.ts +6 -5
- package/dist/workspace/lineplot/client.d.ts.map +1 -1
- package/dist/workspace/lineplot/payload.d.ts +3 -2
- package/dist/workspace/lineplot/payload.d.ts.map +1 -1
- package/dist/workspace/lineplot/retriever.d.ts +3 -2
- package/dist/workspace/lineplot/retriever.d.ts.map +1 -1
- package/dist/workspace/lineplot/writer.d.ts +7 -6
- package/dist/workspace/lineplot/writer.d.ts.map +1 -1
- package/dist/workspace/payload.d.ts +3 -2
- package/dist/workspace/payload.d.ts.map +1 -1
- package/dist/workspace/retriever.d.ts +3 -2
- package/dist/workspace/schematic/client.d.ts +18 -0
- package/dist/workspace/schematic/client.d.ts.map +1 -0
- package/dist/workspace/schematic/external.d.ts.map +1 -0
- package/dist/workspace/schematic/index.d.ts +2 -0
- package/dist/workspace/schematic/index.d.ts.map +1 -0
- package/dist/workspace/{pid → schematic}/payload.d.ts +6 -5
- package/dist/workspace/schematic/payload.d.ts.map +1 -0
- package/dist/workspace/schematic/retriever.d.ts +10 -0
- package/dist/workspace/schematic/retriever.d.ts.map +1 -0
- package/dist/workspace/schematic/schematic.spec.d.ts +2 -0
- package/dist/workspace/schematic/schematic.spec.d.ts.map +1 -0
- package/dist/workspace/{pid → schematic}/writer.d.ts +11 -10
- package/dist/workspace/schematic/writer.d.ts.map +1 -0
- package/dist/workspace/writer.d.ts +5 -4
- package/dist/workspace/writer.d.ts.map +1 -1
- package/examples/node/package-lock.json +29 -12
- package/examples/node/package.json +2 -2
- package/examples/node/streamWrite.js +8 -11
- package/package.json +10 -9
- package/src/auth/auth.spec.ts +55 -15
- package/src/auth/auth.ts +41 -42
- package/src/channel/batchRetriever.spec.ts +37 -40
- package/src/channel/channel.spec.ts +4 -4
- package/src/channel/client.ts +42 -49
- package/src/channel/creator.ts +37 -0
- package/src/channel/payload.ts +2 -1
- package/src/channel/retriever.ts +55 -71
- package/src/client.ts +23 -20
- package/src/connection/checker.ts +1 -1
- package/src/connection/connection.spec.ts +1 -6
- package/src/control/state.ts +3 -1
- package/src/errors.ts +71 -67
- package/src/framer/adapter.spec.ts +33 -1
- package/src/framer/adapter.ts +10 -6
- package/src/framer/client.ts +13 -12
- package/src/framer/frame.spec.ts +1 -1
- package/src/framer/frame.ts +9 -6
- package/src/framer/iterator.spec.ts +1 -1
- package/src/framer/iterator.ts +1 -1
- package/src/framer/streamProxy.ts +12 -13
- package/src/framer/streamer.spec.ts +1 -1
- package/src/framer/streamer.ts +25 -1
- package/src/framer/writer.spec.ts +49 -2
- package/src/framer/writer.ts +27 -2
- package/src/hardware/device/client.ts +155 -28
- package/src/hardware/device/device.spec.ts +2 -2
- package/src/hardware/device/external.ts +0 -2
- package/src/hardware/device/index.ts +2 -2
- package/src/hardware/rack/client.ts +139 -56
- package/src/hardware/rack/external.ts +0 -2
- package/src/hardware/rack/rack.spec.ts +20 -1
- package/src/hardware/task/client.ts +324 -31
- package/src/hardware/task/index.ts +1 -1
- package/src/hardware/task/task.spec.ts +41 -0
- package/src/index.ts +3 -4
- package/src/label/client.ts +3 -2
- package/src/label/retriever.ts +1 -1
- package/src/label/writer.ts +1 -1
- package/src/ontology/client.ts +195 -41
- package/src/ontology/external.ts +0 -1
- package/src/ontology/group/client.ts +1 -2
- package/src/ontology/group/payload.ts +1 -1
- package/src/ontology/ontology.spec.ts +16 -0
- package/src/ontology/payload.ts +22 -13
- package/src/ranger/active.ts +5 -5
- package/src/ranger/alias.ts +2 -2
- package/src/ranger/client.ts +68 -17
- package/src/ranger/external.ts +0 -1
- package/src/ranger/kv.ts +6 -1
- package/src/ranger/payload.ts +6 -4
- package/src/ranger/range.ts +4 -1
- package/src/ranger/ranger.spec.ts +24 -2
- package/src/signals/observable.ts +24 -63
- package/src/transport.ts +2 -1
- package/src/util/retrieve.spec.ts +56 -0
- package/src/util/retrieve.ts +103 -0
- package/src/util/telem.ts +1 -1
- package/src/util/zod.ts +4 -0
- package/src/workspace/client.ts +6 -4
- package/src/workspace/lineplot/client.ts +3 -3
- package/src/workspace/lineplot/linePlot.spec.ts +11 -11
- package/src/workspace/lineplot/payload.ts +1 -1
- package/src/workspace/lineplot/retriever.ts +5 -13
- package/src/workspace/lineplot/writer.ts +8 -7
- package/src/workspace/payload.ts +6 -3
- package/src/workspace/retriever.ts +1 -1
- package/src/workspace/{pid → schematic}/client.ts +10 -10
- package/src/workspace/{pid → schematic}/external.ts +2 -2
- package/src/workspace/{pid → schematic}/index.ts +1 -1
- package/src/workspace/{pid → schematic}/payload.ts +4 -4
- package/src/workspace/{pid → schematic}/retriever.ts +10 -10
- package/src/workspace/{pid/pid.spec.ts → schematic/schematic.spec.ts} +35 -35
- package/src/workspace/{pid → schematic}/writer.ts +26 -25
- package/src/workspace/workspace.spec.ts +7 -7
- package/src/workspace/writer.ts +8 -2
- package/dist/hardware/device/payload.d.ts +0 -30
- package/dist/hardware/device/payload.d.ts.map +0 -1
- package/dist/hardware/device/retriever.d.ts +0 -10
- package/dist/hardware/device/retriever.d.ts.map +0 -1
- package/dist/hardware/device/writer.d.ts +0 -9
- package/dist/hardware/device/writer.d.ts.map +0 -1
- package/dist/hardware/rack/payload.d.ts +0 -26
- package/dist/hardware/rack/payload.d.ts.map +0 -1
- package/dist/hardware/rack/retriever.d.ts +0 -10
- package/dist/hardware/rack/retriever.d.ts.map +0 -1
- package/dist/hardware/rack/writer.d.ts +0 -9
- package/dist/hardware/rack/writer.d.ts.map +0 -1
- package/dist/hardware/task/external.d.ts +0 -4
- package/dist/hardware/task/external.d.ts.map +0 -1
- package/dist/hardware/task/payload.d.ts +0 -42
- package/dist/hardware/task/payload.d.ts.map +0 -1
- package/dist/hardware/task/retriever.d.ts +0 -29
- package/dist/hardware/task/retriever.d.ts.map +0 -1
- package/dist/hardware/task/writer.d.ts +0 -9
- package/dist/hardware/task/writer.d.ts.map +0 -1
- package/dist/ontology/retriever.d.ts +0 -13
- package/dist/ontology/retriever.d.ts.map +0 -1
- package/dist/ontology/signals.d.ts +0 -30
- package/dist/ontology/signals.d.ts.map +0 -1
- package/dist/ranger/retriever.d.ts +0 -11
- package/dist/ranger/retriever.d.ts.map +0 -1
- package/dist/workspace/pid/client.d.ts +0 -17
- package/dist/workspace/pid/client.d.ts.map +0 -1
- package/dist/workspace/pid/external.d.ts.map +0 -1
- package/dist/workspace/pid/index.d.ts +0 -2
- package/dist/workspace/pid/index.d.ts.map +0 -1
- package/dist/workspace/pid/payload.d.ts.map +0 -1
- package/dist/workspace/pid/pid.spec.d.ts +0 -2
- package/dist/workspace/pid/pid.spec.d.ts.map +0 -1
- package/dist/workspace/pid/retriever.d.ts +0 -9
- package/dist/workspace/pid/retriever.d.ts.map +0 -1
- package/dist/workspace/pid/writer.d.ts.map +0 -1
- package/src/hardware/device/payload.ts +0 -27
- package/src/hardware/device/retriever.ts +0 -60
- package/src/hardware/device/writer.ts +0 -59
- package/src/hardware/rack/payload.ts +0 -26
- package/src/hardware/rack/retriever.ts +0 -68
- package/src/hardware/rack/writer.ts +0 -59
- package/src/hardware/task/external.ts +0 -12
- package/src/hardware/task/payload.ts +0 -40
- package/src/hardware/task/retriever.ts +0 -70
- package/src/hardware/task/writer.ts +0 -65
- package/src/ontology/retriever.ts +0 -91
- package/src/ontology/signals.ts +0 -139
- package/src/ranger/retriever.ts +0 -50
- /package/dist/workspace/{pid → schematic}/external.d.ts +0 -0
package/src/auth/auth.ts
CHANGED
|
@@ -10,21 +10,9 @@
|
|
|
10
10
|
import type { Middleware, UnaryClient } from "@synnaxlabs/freighter";
|
|
11
11
|
import { z } from "zod";
|
|
12
12
|
|
|
13
|
-
import {
|
|
13
|
+
import { InvalidTokenError } from "@/errors";
|
|
14
14
|
import { user } from "@/user";
|
|
15
15
|
|
|
16
|
-
export const tokenMiddleware =
|
|
17
|
-
(token: () => Promise<string>): Middleware =>
|
|
18
|
-
async (md, next) => {
|
|
19
|
-
try {
|
|
20
|
-
const tk = await token();
|
|
21
|
-
md.params.Authorization = `Bearer ${tk}`;
|
|
22
|
-
} catch (err) {
|
|
23
|
-
return [md, err as Error];
|
|
24
|
-
}
|
|
25
|
-
return await next(md);
|
|
26
|
-
};
|
|
27
|
-
|
|
28
16
|
export const insecureCredentialsZ = z.object({
|
|
29
17
|
username: z.string(),
|
|
30
18
|
password: z.string(),
|
|
@@ -40,45 +28,56 @@ export type TokenResponse = z.infer<typeof tokenResponseZ>;
|
|
|
40
28
|
|
|
41
29
|
const LOGIN_ENDPOINT = "/auth/login";
|
|
42
30
|
|
|
31
|
+
const MAX_RETRIES = 3;
|
|
32
|
+
|
|
43
33
|
export class Client {
|
|
44
|
-
|
|
34
|
+
token: string | undefined;
|
|
45
35
|
private readonly client: UnaryClient;
|
|
46
|
-
|
|
36
|
+
private readonly credentials: InsecureCredentials;
|
|
37
|
+
private authenticating: Promise<Error | null> | undefined;
|
|
47
38
|
authenticated: boolean;
|
|
48
39
|
user: user.Payload | undefined;
|
|
40
|
+
private retryCount: number;
|
|
49
41
|
|
|
50
42
|
constructor(client: UnaryClient, credentials: InsecureCredentials) {
|
|
51
43
|
this.client = client;
|
|
52
44
|
this.authenticated = false;
|
|
53
|
-
this.
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
authenticate(credentials: InsecureCredentials): void {
|
|
57
|
-
this.authenticating = new Promise((resolve, reject) => {
|
|
58
|
-
this.client
|
|
59
|
-
.send<typeof insecureCredentialsZ, typeof tokenResponseZ>(
|
|
60
|
-
LOGIN_ENDPOINT,
|
|
61
|
-
credentials,
|
|
62
|
-
insecureCredentialsZ,
|
|
63
|
-
tokenResponseZ,
|
|
64
|
-
)
|
|
65
|
-
.then(([res, err]) => {
|
|
66
|
-
if (err != null) return reject(err);
|
|
67
|
-
this.token = res?.token;
|
|
68
|
-
this.user = res?.user;
|
|
69
|
-
this.authenticated = true;
|
|
70
|
-
resolve();
|
|
71
|
-
})
|
|
72
|
-
.catch(reject);
|
|
73
|
-
});
|
|
45
|
+
this.credentials = credentials;
|
|
46
|
+
this.retryCount = 0;
|
|
74
47
|
}
|
|
75
48
|
|
|
76
49
|
middleware(): Middleware {
|
|
77
|
-
|
|
78
|
-
if (!this.authenticated
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
50
|
+
const mw: Middleware = async (reqCtx, next) => {
|
|
51
|
+
if (!this.authenticated && !reqCtx.target.endsWith(LOGIN_ENDPOINT)) {
|
|
52
|
+
if (this.authenticating == null)
|
|
53
|
+
this.authenticating = new Promise(async (resolve) => {
|
|
54
|
+
const [res, err] = await this.client.send(
|
|
55
|
+
LOGIN_ENDPOINT,
|
|
56
|
+
|
|
57
|
+
this.credentials,
|
|
58
|
+
insecureCredentialsZ,
|
|
59
|
+
tokenResponseZ,
|
|
60
|
+
);
|
|
61
|
+
if (err != null) return resolve(err);
|
|
62
|
+
this.token = res?.token;
|
|
63
|
+
this.user = res?.user;
|
|
64
|
+
this.authenticated = true;
|
|
65
|
+
resolve(null);
|
|
66
|
+
});
|
|
67
|
+
const err = await this.authenticating;
|
|
68
|
+
if (err != null) return [reqCtx, err];
|
|
69
|
+
}
|
|
70
|
+
reqCtx.params.Authorization = `Bearer ${this.token}`;
|
|
71
|
+
const [resCtx, err] = await next(reqCtx);
|
|
72
|
+
if (err instanceof InvalidTokenError && this.retryCount < MAX_RETRIES) {
|
|
73
|
+
this.authenticated = false;
|
|
74
|
+
this.authenticating = undefined;
|
|
75
|
+
this.retryCount += 1;
|
|
76
|
+
return mw(reqCtx, next);
|
|
77
|
+
}
|
|
78
|
+
this.retryCount = 0;
|
|
79
|
+
return [resCtx, err];
|
|
80
|
+
};
|
|
81
|
+
return mw;
|
|
83
82
|
}
|
|
84
83
|
}
|
|
@@ -1,51 +1,51 @@
|
|
|
1
|
+
import { DataType, Rate } from "@synnaxlabs/x/telem";
|
|
1
2
|
import { describe, expect, it, vi } from "vitest";
|
|
2
|
-
import { newClient } from "@/setupspecs";
|
|
3
|
-
import { DebouncedBatchRetriever, Retriever, analyzeParams } from "@/channel/retriever";
|
|
4
|
-
import { Params, Payload } from "@/channel/payload";
|
|
5
|
-
import { DataType, Rate } from "@synnaxlabs/x";
|
|
6
|
-
|
|
7
3
|
|
|
4
|
+
import { type Params, type Payload } from "@/channel/payload";
|
|
5
|
+
import {
|
|
6
|
+
DebouncedBatchRetriever,
|
|
7
|
+
type Retriever,
|
|
8
|
+
type RetrieveOptions,
|
|
9
|
+
analyzeChannelParams,
|
|
10
|
+
} from "@/channel/retriever";
|
|
8
11
|
|
|
9
12
|
class MockRetriever implements Retriever {
|
|
10
|
-
func: (channels: Params,
|
|
13
|
+
func: (channels: Params, options?: RetrieveOptions) => Promise<Payload[]>;
|
|
11
14
|
|
|
12
|
-
constructor(
|
|
15
|
+
constructor(
|
|
16
|
+
func: (channels: Params, options?: RetrieveOptions) => Promise<Payload[]>,
|
|
17
|
+
) {
|
|
13
18
|
this.func = func;
|
|
14
19
|
}
|
|
15
20
|
|
|
16
|
-
async search(term: string
|
|
21
|
+
async search(term: string): Promise<Payload[]> {
|
|
17
22
|
throw new Error("Method not implemented.");
|
|
18
23
|
}
|
|
19
24
|
|
|
20
|
-
async page(offset: number, limit: number
|
|
25
|
+
async page(offset: number, limit: number): Promise<Payload[]> {
|
|
21
26
|
throw new Error("Method not implemented.");
|
|
22
27
|
}
|
|
23
28
|
|
|
24
|
-
async retrieve(channels: Params,
|
|
25
|
-
return this.func(channels,
|
|
29
|
+
async retrieve(channels: Params, options?: RetrieveOptions): Promise<Payload[]> {
|
|
30
|
+
return await this.func(channels, options);
|
|
26
31
|
}
|
|
27
|
-
|
|
28
32
|
}
|
|
29
33
|
|
|
30
|
-
|
|
31
34
|
describe("channelRetriever", () => {
|
|
32
35
|
it("should batch multiple retrieve requests", async () => {
|
|
33
36
|
const called = vi.fn();
|
|
34
37
|
const base = new MockRetriever(async (batch): Promise<Payload[]> => {
|
|
35
38
|
called(batch);
|
|
36
|
-
const {normalized} =
|
|
37
|
-
return normalized.map(
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
index:0
|
|
47
|
-
}),
|
|
48
|
-
);
|
|
39
|
+
const { normalized } = analyzeChannelParams(batch);
|
|
40
|
+
return normalized.map((key) => ({
|
|
41
|
+
key: key as number,
|
|
42
|
+
name: `channel-${key}`,
|
|
43
|
+
dataType: DataType.FLOAT32,
|
|
44
|
+
isIndex: false,
|
|
45
|
+
rate: Rate.hz(1),
|
|
46
|
+
leaseholder: 1,
|
|
47
|
+
index: 0,
|
|
48
|
+
}));
|
|
49
49
|
});
|
|
50
50
|
const retriever = new DebouncedBatchRetriever(base, 10);
|
|
51
51
|
const res = await Promise.all([
|
|
@@ -61,20 +61,17 @@ describe("channelRetriever", () => {
|
|
|
61
61
|
const called = vi.fn();
|
|
62
62
|
const base = new MockRetriever(async (batch): Promise<Payload[]> => {
|
|
63
63
|
called(batch);
|
|
64
|
-
const {normalized} =
|
|
65
|
-
return normalized.map(
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
}),
|
|
76
|
-
);
|
|
77
|
-
})
|
|
64
|
+
const { normalized } = analyzeChannelParams(batch);
|
|
65
|
+
return normalized.map((key) => ({
|
|
66
|
+
key: key as number,
|
|
67
|
+
name: `channel-${key}`,
|
|
68
|
+
dataType: DataType.FLOAT32,
|
|
69
|
+
isIndex: false,
|
|
70
|
+
rate: Rate.hz(1),
|
|
71
|
+
leaseholder: 1,
|
|
72
|
+
index: 0,
|
|
73
|
+
}));
|
|
74
|
+
});
|
|
78
75
|
const retriever = new DebouncedBatchRetriever(base, 10);
|
|
79
76
|
const res = await Promise.all([
|
|
80
77
|
retriever.retrieve([1]),
|
|
@@ -7,11 +7,11 @@
|
|
|
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 { DataType, Rate, TimeStamp } from "@synnaxlabs/x";
|
|
11
|
-
import { describe,
|
|
10
|
+
import { DataType, Rate, TimeStamp } from "@synnaxlabs/x/telem";
|
|
11
|
+
import { describe, expect, test, it } from "vitest";
|
|
12
12
|
|
|
13
13
|
import { Channel } from "@/channel/client";
|
|
14
|
-
import { QueryError } from "@/errors";
|
|
14
|
+
import { NotFoundError, QueryError } from "@/errors";
|
|
15
15
|
import { newClient } from "@/setupspecs";
|
|
16
16
|
|
|
17
17
|
const client = newClient();
|
|
@@ -185,7 +185,7 @@ describe("Channel", () => {
|
|
|
185
185
|
});
|
|
186
186
|
test("retrieve by key - not found", async () => {
|
|
187
187
|
await expect(async () => await client.channels.retrieve("1-1000")).rejects.toThrow(
|
|
188
|
-
|
|
188
|
+
NotFoundError,
|
|
189
189
|
);
|
|
190
190
|
});
|
|
191
191
|
test("retrieve by name", async () => {
|
package/src/channel/client.ts
CHANGED
|
@@ -8,17 +8,17 @@
|
|
|
8
8
|
// included in the file licenses/APL.txt.
|
|
9
9
|
|
|
10
10
|
import { type UnaryClient } from "@synnaxlabs/freighter";
|
|
11
|
+
import { type AsyncTermSearcher } from "@synnaxlabs/x/search";
|
|
11
12
|
import {
|
|
12
13
|
DataType,
|
|
13
14
|
Rate,
|
|
14
15
|
type TypedArray,
|
|
15
16
|
type CrudeDensity,
|
|
16
17
|
type TimeRange,
|
|
17
|
-
type AsyncTermSearcher,
|
|
18
|
-
toArray,
|
|
19
18
|
type CrudeTimeStamp,
|
|
20
19
|
type MultiSeries,
|
|
21
|
-
} from "@synnaxlabs/x";
|
|
20
|
+
} from "@synnaxlabs/x/telem";
|
|
21
|
+
import { toArray } from "@synnaxlabs/x/toArray";
|
|
22
22
|
|
|
23
23
|
import {
|
|
24
24
|
type Key,
|
|
@@ -29,15 +29,18 @@ import {
|
|
|
29
29
|
type NewPayload,
|
|
30
30
|
} from "@/channel/payload";
|
|
31
31
|
import {
|
|
32
|
-
|
|
32
|
+
analyzeChannelParams,
|
|
33
33
|
CacheRetriever,
|
|
34
34
|
ClusterRetriever,
|
|
35
35
|
DebouncedBatchRetriever,
|
|
36
36
|
type Retriever,
|
|
37
|
+
type PageOptions,
|
|
38
|
+
type RetrieveOptions,
|
|
37
39
|
} from "@/channel/retriever";
|
|
38
40
|
import { type Writer } from "@/channel/writer";
|
|
39
|
-
import { MultipleFoundError,
|
|
41
|
+
import { MultipleFoundError, ValidationError } from "@/errors";
|
|
40
42
|
import { type framer } from "@/framer";
|
|
43
|
+
import { checkForMultipleOrNoResults } from "@/util/retrieve";
|
|
41
44
|
|
|
42
45
|
interface CreateOptions {
|
|
43
46
|
retrieveIfNameExists?: boolean;
|
|
@@ -169,6 +172,7 @@ export class Channel {
|
|
|
169
172
|
* through the `channels` property of an {@link Synnax} client.
|
|
170
173
|
*/
|
|
171
174
|
export class Client implements AsyncTermSearcher<string, Key, Channel> {
|
|
175
|
+
readonly type = "channel";
|
|
172
176
|
private readonly frameClient: framer.Client;
|
|
173
177
|
private readonly client: UnaryClient;
|
|
174
178
|
readonly retriever: Retriever;
|
|
@@ -287,7 +291,7 @@ export class Client implements AsyncTermSearcher<string, Key, Channel> {
|
|
|
287
291
|
* const channel = await client.channels.retrieve(1);
|
|
288
292
|
* ```
|
|
289
293
|
*/
|
|
290
|
-
async retrieve(channel: KeyOrName,
|
|
294
|
+
async retrieve(channel: KeyOrName, options?: RetrieveOptions): Promise<Channel>;
|
|
291
295
|
|
|
292
296
|
/**
|
|
293
297
|
* Retrieves multiple channels from the database using the provided keys or the
|
|
@@ -301,7 +305,7 @@ export class Client implements AsyncTermSearcher<string, Key, Channel> {
|
|
|
301
305
|
* @param options.notDataTypes - Limits the query to only channels without the specified
|
|
302
306
|
*
|
|
303
307
|
*/
|
|
304
|
-
async retrieve(channels: Params,
|
|
308
|
+
async retrieve(channels: Params, options?: RetrieveOptions): Promise<Channel[]>;
|
|
305
309
|
|
|
306
310
|
/**
|
|
307
311
|
* Retrieves a channel from the database using the given parameters.
|
|
@@ -310,34 +314,45 @@ export class Client implements AsyncTermSearcher<string, Key, Channel> {
|
|
|
310
314
|
* @returns The retrieved channel.
|
|
311
315
|
* @raises {QueryError} If the channel does not exist or if multiple results are returned.
|
|
312
316
|
*/
|
|
313
|
-
async retrieve(
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
throw new MultipleFoundError(`multiple channels matching ${actual} found`);
|
|
322
|
-
return res[0];
|
|
317
|
+
async retrieve(
|
|
318
|
+
channels: Params,
|
|
319
|
+
options?: RetrieveOptions,
|
|
320
|
+
): Promise<Channel | Channel[]> {
|
|
321
|
+
const isSingle = !Array.isArray(channels);
|
|
322
|
+
const res = this.sugar(await this.retriever.retrieve(channels, options));
|
|
323
|
+
checkForMultipleOrNoResults("channel", channels, res, isSingle);
|
|
324
|
+
return isSingle ? res[0] : res;
|
|
323
325
|
}
|
|
324
326
|
|
|
325
|
-
async
|
|
326
|
-
|
|
327
|
-
if (variant === "keys") return await this.writer.delete({ keys: normalized });
|
|
328
|
-
return await this.writer.delete({ names: normalized });
|
|
327
|
+
async search(term: string, options?: RetrieveOptions): Promise<Channel[]> {
|
|
328
|
+
return this.sugar(await this.retriever.search(term, options));
|
|
329
329
|
}
|
|
330
330
|
|
|
331
|
-
async
|
|
332
|
-
|
|
331
|
+
async delete(channels: Params): Promise<void> {
|
|
332
|
+
const { normalized, variant } = analyzeChannelParams(channels);
|
|
333
|
+
if (variant === "keys")
|
|
334
|
+
return await this.writer.delete({ keys: normalized as Key[] });
|
|
335
|
+
return await this.writer.delete({ names: normalized as string[] });
|
|
333
336
|
}
|
|
334
337
|
|
|
335
|
-
|
|
336
|
-
|
|
338
|
+
newSearcherWithOptions(
|
|
339
|
+
options: RetrieveOptions,
|
|
340
|
+
): AsyncTermSearcher<string, Key, Channel> {
|
|
341
|
+
return {
|
|
342
|
+
type: this.type,
|
|
343
|
+
search: async (term: string) => await this.search(term, options),
|
|
344
|
+
retrieve: async (keys: Key[]) => await this.retrieve(keys, options),
|
|
345
|
+
page: async (offset: number, limit: number) =>
|
|
346
|
+
await this.page(offset, limit, options),
|
|
347
|
+
};
|
|
337
348
|
}
|
|
338
349
|
|
|
339
|
-
async page(
|
|
340
|
-
|
|
350
|
+
async page(
|
|
351
|
+
offset: number,
|
|
352
|
+
limit: number,
|
|
353
|
+
options?: Omit<RetrieveOptions, "limit" | "offset">,
|
|
354
|
+
): Promise<Channel[]> {
|
|
355
|
+
return this.sugar(await this.retriever.page(offset, limit, options));
|
|
341
356
|
}
|
|
342
357
|
|
|
343
358
|
createDebouncedBatchRetriever(deb: number = 10): Retriever {
|
|
@@ -351,25 +366,3 @@ export class Client implements AsyncTermSearcher<string, Key, Channel> {
|
|
|
351
366
|
return payloads.map((p) => new Channel({ ...p, frameClient }));
|
|
352
367
|
}
|
|
353
368
|
}
|
|
354
|
-
|
|
355
|
-
class SearcherUnderRange implements AsyncTermSearcher<string, Key, Channel> {
|
|
356
|
-
private readonly client: Client;
|
|
357
|
-
private readonly rangeKey?: string;
|
|
358
|
-
|
|
359
|
-
constructor(client: Client, rangeKey?: string) {
|
|
360
|
-
this.client = client;
|
|
361
|
-
this.rangeKey = rangeKey;
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
async search(term: string): Promise<Channel[]> {
|
|
365
|
-
return await this.client.search(term, this.rangeKey);
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
async page(offset: number, limit: number): Promise<Channel[]> {
|
|
369
|
-
return await this.client.page(offset, limit, this.rangeKey);
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
async retrieve(channels: Key[]): Promise<Channel[]> {
|
|
373
|
-
return await this.client.retrieve(channels, this.rangeKey);
|
|
374
|
-
}
|
|
375
|
-
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// Copyright 2023 Synnax Labs, Inc.
|
|
2
|
+
//
|
|
3
|
+
// Use of this software is governed by the Business Source License included in the file
|
|
4
|
+
// licenses/BSL.txt.
|
|
5
|
+
//
|
|
6
|
+
// As of the Change Date specified in that file, in accordance with the Business Source
|
|
7
|
+
// License, use of this software will be governed by the Apache License, Version 2.0,
|
|
8
|
+
// included in the file licenses/APL.txt.
|
|
9
|
+
|
|
10
|
+
import type { UnaryClient } from "@synnaxlabs/freighter";
|
|
11
|
+
import { z } from "zod";
|
|
12
|
+
|
|
13
|
+
import { type Payload, payload, newPayload, type NewPayload } from "@/channel/payload";
|
|
14
|
+
|
|
15
|
+
const reqZ = z.object({ channels: newPayload.array() });
|
|
16
|
+
|
|
17
|
+
const resZ = z.object({ channels: payload.array() });
|
|
18
|
+
|
|
19
|
+
export class Creator {
|
|
20
|
+
private static readonly ENDPOINT = "/channel/create";
|
|
21
|
+
private readonly client: UnaryClient;
|
|
22
|
+
|
|
23
|
+
constructor(client: UnaryClient) {
|
|
24
|
+
this.client = client;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
async create(channels: NewPayload[]): Promise<Payload[]> {
|
|
28
|
+
const [res, err] = await this.client.send<typeof reqZ, typeof resZ>(
|
|
29
|
+
Creator.ENDPOINT,
|
|
30
|
+
{ channels },
|
|
31
|
+
reqZ,
|
|
32
|
+
resZ,
|
|
33
|
+
);
|
|
34
|
+
if (err != null) throw err;
|
|
35
|
+
return res.channels;
|
|
36
|
+
}
|
|
37
|
+
}
|
package/src/channel/payload.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
|
-
import { DataType, Rate } from "@synnaxlabs/x";
|
|
10
|
+
import { DataType, Rate } from "@synnaxlabs/x/telem";
|
|
11
11
|
import { z } from "zod";
|
|
12
12
|
|
|
13
13
|
export const keyZ = z.number();
|
|
@@ -38,6 +38,7 @@ export const newPayload = payload.extend({
|
|
|
38
38
|
index: z.number().optional(),
|
|
39
39
|
rate: Rate.z.optional().default(0),
|
|
40
40
|
isIndex: z.boolean().optional(),
|
|
41
|
+
virtual: z.boolean().optional().default(false),
|
|
41
42
|
});
|
|
42
43
|
|
|
43
44
|
export type NewPayload = z.input<typeof newPayload>;
|
package/src/channel/retriever.ts
CHANGED
|
@@ -7,7 +7,8 @@
|
|
|
7
7
|
// License, use of this software will be governed by the Apache License, Version 2.0,
|
|
8
8
|
// included in the file licenses/APL.txt.
|
|
9
9
|
import type { UnaryClient } from "@synnaxlabs/freighter";
|
|
10
|
-
import { debounce
|
|
10
|
+
import { debounce } from "@synnaxlabs/x/debounce";
|
|
11
|
+
import { DataType } from "@synnaxlabs/x/telem";
|
|
11
12
|
import { Mutex } from "async-mutex";
|
|
12
13
|
import { z } from "zod";
|
|
13
14
|
|
|
@@ -16,13 +17,13 @@ import {
|
|
|
16
17
|
type KeyOrName,
|
|
17
18
|
type Keys,
|
|
18
19
|
type KeysOrNames,
|
|
19
|
-
type Name,
|
|
20
|
-
type Names,
|
|
21
20
|
type Params,
|
|
22
21
|
type Payload,
|
|
23
22
|
payload,
|
|
24
23
|
} from "@/channel/payload";
|
|
25
24
|
import { QueryError } from "@/errors";
|
|
25
|
+
import { type ParamAnalysisResult, analyzeParams } from "@/util/retrieve";
|
|
26
|
+
import { nullableArrayZ } from "@/util/zod";
|
|
26
27
|
|
|
27
28
|
const reqZ = z.object({
|
|
28
29
|
leaseholder: z.number().optional(),
|
|
@@ -32,18 +33,34 @@ const reqZ = z.object({
|
|
|
32
33
|
rangeKey: z.string().optional(),
|
|
33
34
|
limit: z.number().optional(),
|
|
34
35
|
offset: z.number().optional(),
|
|
36
|
+
dataTypes: DataType.z.array().optional(),
|
|
37
|
+
notDataTypes: DataType.z.array().optional(),
|
|
38
|
+
virtual: z.boolean().optional(),
|
|
39
|
+
isIndex: z.boolean().optional(),
|
|
40
|
+
internal: z.boolean().optional(),
|
|
35
41
|
});
|
|
36
42
|
|
|
37
43
|
type Request = z.input<typeof reqZ>;
|
|
38
44
|
|
|
45
|
+
export type RetrieveOptions = Omit<Request, "keys" | "names" | "search">;
|
|
46
|
+
export type PageOptions = Omit<RetrieveOptions, "offset" | "limit">;
|
|
47
|
+
|
|
39
48
|
const resZ = z.object({
|
|
40
|
-
channels: payload
|
|
49
|
+
channels: nullableArrayZ(payload),
|
|
41
50
|
});
|
|
42
51
|
|
|
52
|
+
export const analyzeChannelParams = (
|
|
53
|
+
channels: Params,
|
|
54
|
+
): ParamAnalysisResult<KeyOrName, { number: "keys"; string: "names" }> =>
|
|
55
|
+
analyzeParams(channels, {
|
|
56
|
+
number: "keys",
|
|
57
|
+
string: "names",
|
|
58
|
+
});
|
|
59
|
+
|
|
43
60
|
export interface Retriever {
|
|
44
|
-
retrieve: (channels: Params,
|
|
45
|
-
search: (term: string,
|
|
46
|
-
page: (offset: number, limit: number,
|
|
61
|
+
retrieve: (channels: Params, opts?: RetrieveOptions) => Promise<Payload[]>;
|
|
62
|
+
search: (term: string, opts?: RetrieveOptions) => Promise<Payload[]>;
|
|
63
|
+
page: (offset: number, limit: number, opts?: PageOptions) => Promise<Payload[]>;
|
|
47
64
|
}
|
|
48
65
|
|
|
49
66
|
export class ClusterRetriever implements Retriever {
|
|
@@ -54,17 +71,20 @@ export class ClusterRetriever implements Retriever {
|
|
|
54
71
|
this.client = client;
|
|
55
72
|
}
|
|
56
73
|
|
|
57
|
-
async search(term: string,
|
|
58
|
-
return await this.execute({ search: term,
|
|
74
|
+
async search(term: string, options?: RetrieveOptions): Promise<Payload[]> {
|
|
75
|
+
return await this.execute({ search: term, ...options });
|
|
59
76
|
}
|
|
60
77
|
|
|
61
|
-
async retrieve(channels: Params,
|
|
62
|
-
|
|
63
|
-
|
|
78
|
+
async retrieve(channels: Params, options?: RetrieveOptions): Promise<Payload[]> {
|
|
79
|
+
let { variant, normalized } = analyzeChannelParams(channels);
|
|
80
|
+
if (variant === "keys" && (normalized as Key[]).indexOf(0) !== -1)
|
|
81
|
+
normalized = (normalized as Key[]).filter((k) => k !== 0);
|
|
82
|
+
if (normalized.length === 0) return [];
|
|
83
|
+
return await this.execute({ [variant]: normalized, ...options });
|
|
64
84
|
}
|
|
65
85
|
|
|
66
|
-
async page(offset: number, limit: number,
|
|
67
|
-
return await this.execute({ offset, limit,
|
|
86
|
+
async page(offset: number, limit: number, options?: PageOptions): Promise<Payload[]> {
|
|
87
|
+
return await this.execute({ offset, limit, ...options });
|
|
68
88
|
}
|
|
69
89
|
|
|
70
90
|
private async execute(request: Request): Promise<Payload[]> {
|
|
@@ -90,16 +110,19 @@ export class CacheRetriever implements Retriever {
|
|
|
90
110
|
this.wrapped = wrapped;
|
|
91
111
|
}
|
|
92
112
|
|
|
93
|
-
async search(term: string,
|
|
94
|
-
return await this.wrapped.search(term,
|
|
113
|
+
async search(term: string, options?: RetrieveOptions): Promise<Payload[]> {
|
|
114
|
+
return await this.wrapped.search(term, options);
|
|
95
115
|
}
|
|
96
116
|
|
|
97
|
-
async page(offset: number, limit: number,
|
|
98
|
-
return await this.wrapped.page(offset, limit,
|
|
117
|
+
async page(offset: number, limit: number, options?: PageOptions): Promise<Payload[]> {
|
|
118
|
+
return await this.wrapped.page(offset, limit, options);
|
|
99
119
|
}
|
|
100
120
|
|
|
101
|
-
async retrieve(channels: Params): Promise<Payload[]> {
|
|
102
|
-
const { normalized } = analyzeParams(channels
|
|
121
|
+
async retrieve(channels: Params, options?: RetrieveOptions): Promise<Payload[]> {
|
|
122
|
+
const { normalized } = analyzeParams<string | number>(channels, {
|
|
123
|
+
string: "names",
|
|
124
|
+
number: "keys",
|
|
125
|
+
});
|
|
103
126
|
const results: Payload[] = [];
|
|
104
127
|
const toFetch: KeysOrNames = [];
|
|
105
128
|
normalized.forEach((keyOrName) => {
|
|
@@ -108,7 +131,7 @@ export class CacheRetriever implements Retriever {
|
|
|
108
131
|
else toFetch.push(keyOrName as never);
|
|
109
132
|
});
|
|
110
133
|
if (toFetch.length === 0) return results;
|
|
111
|
-
const fetched = await this.wrapped.retrieve(toFetch);
|
|
134
|
+
const fetched = await this.wrapped.retrieve(toFetch, options);
|
|
112
135
|
this.updateCache(fetched);
|
|
113
136
|
return results.concat(fetched);
|
|
114
137
|
}
|
|
@@ -127,49 +150,6 @@ export class CacheRetriever implements Retriever {
|
|
|
127
150
|
}
|
|
128
151
|
}
|
|
129
152
|
|
|
130
|
-
export type ParamAnalysisResult =
|
|
131
|
-
| {
|
|
132
|
-
single: true;
|
|
133
|
-
variant: "names";
|
|
134
|
-
normalized: Names;
|
|
135
|
-
actual: Name;
|
|
136
|
-
}
|
|
137
|
-
| {
|
|
138
|
-
single: true;
|
|
139
|
-
variant: "keys";
|
|
140
|
-
normalized: Keys;
|
|
141
|
-
actual: Key;
|
|
142
|
-
}
|
|
143
|
-
| {
|
|
144
|
-
single: false;
|
|
145
|
-
variant: "keys";
|
|
146
|
-
normalized: Keys;
|
|
147
|
-
actual: Keys;
|
|
148
|
-
}
|
|
149
|
-
| {
|
|
150
|
-
single: false;
|
|
151
|
-
variant: "names";
|
|
152
|
-
normalized: Names;
|
|
153
|
-
actual: Names;
|
|
154
|
-
};
|
|
155
|
-
|
|
156
|
-
export const analyzeParams = (channels: Params): ParamAnalysisResult => {
|
|
157
|
-
let normal = (toArray(channels) as KeysOrNames).filter((c) => c !== 0);
|
|
158
|
-
let variant: "names" | "keys" = "keys";
|
|
159
|
-
if (typeof normal[0] === "string") {
|
|
160
|
-
if (isNaN(parseInt(normal[0]))) variant = "names";
|
|
161
|
-
else {
|
|
162
|
-
normal = normal.map((v) => parseInt(v as string));
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
return {
|
|
166
|
-
single: !Array.isArray(channels),
|
|
167
|
-
variant,
|
|
168
|
-
normalized: normal,
|
|
169
|
-
actual: channels,
|
|
170
|
-
} as const as ParamAnalysisResult;
|
|
171
|
-
};
|
|
172
|
-
|
|
173
153
|
export interface PromiseFns<T> {
|
|
174
154
|
resolve: (value: T) => void;
|
|
175
155
|
reject: (reason?: any) => void;
|
|
@@ -189,22 +169,26 @@ export class DebouncedBatchRetriever implements Retriever {
|
|
|
189
169
|
}, deb);
|
|
190
170
|
}
|
|
191
171
|
|
|
192
|
-
async search(term: string,
|
|
193
|
-
return await this.wrapped.search(term,
|
|
172
|
+
async search(term: string, options?: RetrieveOptions): Promise<Payload[]> {
|
|
173
|
+
return await this.wrapped.search(term, options);
|
|
194
174
|
}
|
|
195
175
|
|
|
196
|
-
async page(
|
|
197
|
-
|
|
176
|
+
async page(
|
|
177
|
+
offset: number,
|
|
178
|
+
limit: number,
|
|
179
|
+
options?: RetrieveOptions,
|
|
180
|
+
): Promise<Payload[]> {
|
|
181
|
+
return await this.wrapped.page(offset, limit, options);
|
|
198
182
|
}
|
|
199
183
|
|
|
200
184
|
async retrieve(channels: Params): Promise<Payload[]> {
|
|
201
|
-
const { normalized, variant } =
|
|
185
|
+
const { normalized, variant } = analyzeChannelParams(channels);
|
|
202
186
|
// Bypass on name fetches for now.
|
|
203
187
|
if (variant === "names") return await this.wrapped.retrieve(normalized);
|
|
204
188
|
// eslint-disable-next-line @typescript-eslint/promise-function-async
|
|
205
189
|
const a = new Promise<Payload[]>((resolve, reject) => {
|
|
206
190
|
void this.mu.runExclusive(() => {
|
|
207
|
-
this.requests.set(normalized, { resolve, reject });
|
|
191
|
+
this.requests.set(normalized as Key[], { resolve, reject });
|
|
208
192
|
this.debouncedRun();
|
|
209
193
|
});
|
|
210
194
|
});
|
|
@@ -233,7 +217,7 @@ export const retrieveRequired = async (
|
|
|
233
217
|
r: Retriever,
|
|
234
218
|
params: Params,
|
|
235
219
|
): Promise<Payload[]> => {
|
|
236
|
-
const { normalized } =
|
|
220
|
+
const { normalized } = analyzeChannelParams(params);
|
|
237
221
|
const results = await r.retrieve(normalized);
|
|
238
222
|
const notFound: KeyOrName[] = [];
|
|
239
223
|
normalized.forEach((v) => {
|