@synnaxlabs/client 0.38.1 → 0.39.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/dist/access/payload.d.ts +6 -6
- package/dist/access/payload.d.ts.map +1 -1
- package/dist/access/policy/client.d.ts +5 -3
- package/dist/access/policy/client.d.ts.map +1 -1
- package/dist/access/policy/external.d.ts +1 -0
- package/dist/access/policy/external.d.ts.map +1 -1
- package/dist/access/policy/ontology.d.ts +5 -0
- package/dist/access/policy/ontology.d.ts.map +1 -0
- package/dist/access/policy/payload.d.ts +86 -89
- package/dist/access/policy/payload.d.ts.map +1 -1
- package/dist/access/policy/retriever.d.ts +7 -6
- package/dist/access/policy/retriever.d.ts.map +1 -1
- package/dist/access/policy/writer.d.ts +2 -2
- package/dist/access/policy/writer.d.ts.map +1 -1
- package/dist/auth/auth.d.ts +2 -1
- package/dist/auth/auth.d.ts.map +1 -1
- package/dist/channel/client.d.ts +6 -5
- package/dist/channel/client.d.ts.map +1 -1
- package/dist/channel/payload.d.ts +13 -11
- package/dist/channel/payload.d.ts.map +1 -1
- package/dist/channel/retriever.d.ts +9 -6
- package/dist/channel/retriever.d.ts.map +1 -1
- package/dist/channel/writer.d.ts +6 -4
- package/dist/channel/writer.d.ts.map +1 -1
- package/dist/client.cjs +30 -30
- package/dist/client.d.ts +4 -2
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +3376 -3423
- package/dist/connection/checker.d.ts +5 -4
- package/dist/connection/checker.d.ts.map +1 -1
- package/dist/control/state.d.ts +10 -8
- package/dist/control/state.d.ts.map +1 -1
- package/dist/errors.d.ts +5 -0
- package/dist/errors.d.ts.map +1 -1
- package/dist/framer/adapter.d.ts +14 -15
- package/dist/framer/adapter.d.ts.map +1 -1
- package/dist/framer/client.d.ts +13 -15
- package/dist/framer/client.d.ts.map +1 -1
- package/dist/framer/deleter.d.ts +3 -2
- package/dist/framer/deleter.d.ts.map +1 -1
- package/dist/framer/frame.d.ts +31 -27
- package/dist/framer/frame.d.ts.map +1 -1
- package/dist/framer/iterator.d.ts +4 -5
- package/dist/framer/iterator.d.ts.map +1 -1
- package/dist/framer/streamer.d.ts +5 -6
- package/dist/framer/streamer.d.ts.map +1 -1
- package/dist/framer/writer.d.ts +42 -39
- package/dist/framer/writer.d.ts.map +1 -1
- package/dist/hardware/device/client.d.ts +17 -12
- package/dist/hardware/device/client.d.ts.map +1 -1
- package/dist/hardware/device/payload.d.ts +19 -16
- package/dist/hardware/device/payload.d.ts.map +1 -1
- package/dist/hardware/rack/client.d.ts +15 -15
- package/dist/hardware/rack/client.d.ts.map +1 -1
- package/dist/hardware/rack/payload.d.ts +9 -8
- package/dist/hardware/rack/payload.d.ts.map +1 -1
- package/dist/hardware/task/client.d.ts +38 -29
- package/dist/hardware/task/client.d.ts.map +1 -1
- package/dist/hardware/task/payload.d.ts +58 -53
- package/dist/hardware/task/payload.d.ts.map +1 -1
- package/dist/label/client.d.ts +4 -3
- package/dist/label/client.d.ts.map +1 -1
- package/dist/label/payload.d.ts +4 -4
- package/dist/label/payload.d.ts.map +1 -1
- package/dist/label/retriever.d.ts.map +1 -1
- package/dist/label/writer.d.ts +13 -10
- package/dist/label/writer.d.ts.map +1 -1
- package/dist/ontology/client.d.ts +12 -10
- package/dist/ontology/client.d.ts.map +1 -1
- package/dist/ontology/group/client.d.ts +5 -4
- package/dist/ontology/group/client.d.ts.map +1 -1
- package/dist/ontology/group/group.d.ts +7 -5
- package/dist/ontology/group/group.d.ts.map +1 -1
- package/dist/ontology/group/payload.d.ts +6 -5
- package/dist/ontology/group/payload.d.ts.map +1 -1
- package/dist/ontology/group/writer.d.ts +8 -8
- package/dist/ontology/group/writer.d.ts.map +1 -1
- package/dist/ontology/payload.d.ts +72 -62
- package/dist/ontology/payload.d.ts.map +1 -1
- package/dist/ontology/writer.d.ts.map +1 -1
- package/dist/ranger/alias.d.ts +9 -10
- package/dist/ranger/alias.d.ts.map +1 -1
- package/dist/ranger/client.d.ts +18 -18
- 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 +18 -14
- package/dist/ranger/kv.d.ts.map +1 -1
- package/dist/ranger/payload.d.ts +13 -13
- package/dist/ranger/payload.d.ts.map +1 -1
- package/dist/ranger/writer.d.ts +14 -14
- package/dist/ranger/writer.d.ts.map +1 -1
- package/dist/setupspecs.d.ts.map +1 -1
- package/dist/signals/observable.d.ts +3 -1
- package/dist/signals/observable.d.ts.map +1 -1
- package/dist/user/client.d.ts +5 -3
- package/dist/user/client.d.ts.map +1 -1
- package/dist/user/payload.d.ts +7 -6
- package/dist/user/payload.d.ts.map +1 -1
- package/dist/user/retriever.d.ts +2 -1
- package/dist/user/retriever.d.ts.map +1 -1
- package/dist/user/writer.d.ts +2 -2
- package/dist/user/writer.d.ts.map +1 -1
- package/dist/util/decodeJSONString.d.ts +3 -0
- package/dist/util/decodeJSONString.d.ts.map +1 -0
- package/dist/util/parseWithoutKeyConversion.d.ts +3 -0
- package/dist/util/parseWithoutKeyConversion.d.ts.map +1 -0
- package/dist/util/retrieve.d.ts +1 -1
- package/dist/util/retrieve.d.ts.map +1 -1
- package/dist/util/telem.d.ts.map +1 -1
- package/dist/util/zod.d.ts.map +1 -1
- package/dist/workspace/client.d.ts +6 -60
- package/dist/workspace/client.d.ts.map +1 -1
- package/dist/workspace/external.d.ts +3 -0
- package/dist/workspace/external.d.ts.map +1 -0
- package/dist/workspace/index.d.ts +1 -1
- package/dist/workspace/index.d.ts.map +1 -1
- package/dist/workspace/lineplot/client.d.ts +5 -44
- package/dist/workspace/lineplot/client.d.ts.map +1 -1
- package/dist/workspace/lineplot/external.d.ts +3 -0
- package/dist/workspace/lineplot/external.d.ts.map +1 -0
- package/dist/workspace/lineplot/index.d.ts +1 -1
- package/dist/workspace/lineplot/index.d.ts.map +1 -1
- package/dist/workspace/lineplot/payload.d.ts +45 -0
- package/dist/workspace/lineplot/payload.d.ts.map +1 -0
- package/dist/workspace/log/client.d.ts +5 -44
- package/dist/workspace/log/client.d.ts.map +1 -1
- package/dist/workspace/log/external.d.ts +3 -0
- package/dist/workspace/log/external.d.ts.map +1 -0
- package/dist/workspace/log/index.d.ts +1 -1
- package/dist/workspace/log/index.d.ts.map +1 -1
- package/dist/workspace/log/payload.d.ts +45 -0
- package/dist/workspace/log/payload.d.ts.map +1 -0
- package/dist/workspace/payload.d.ts +60 -0
- package/dist/workspace/payload.d.ts.map +1 -0
- package/dist/workspace/schematic/client.d.ts +5 -68
- package/dist/workspace/schematic/client.d.ts.map +1 -1
- package/dist/workspace/schematic/external.d.ts +3 -0
- package/dist/workspace/schematic/external.d.ts.map +1 -0
- package/dist/workspace/schematic/index.d.ts +1 -1
- package/dist/workspace/schematic/index.d.ts.map +1 -1
- package/dist/workspace/schematic/payload.d.ts +71 -0
- package/dist/workspace/schematic/payload.d.ts.map +1 -0
- package/dist/workspace/table/client.d.ts +5 -57
- package/dist/workspace/table/client.d.ts.map +1 -1
- package/dist/workspace/table/external.d.ts +3 -0
- package/dist/workspace/table/external.d.ts.map +1 -0
- package/dist/workspace/table/index.d.ts +1 -1
- package/dist/workspace/table/index.d.ts.map +1 -1
- package/dist/workspace/table/payload.d.ts +60 -0
- package/dist/workspace/table/payload.d.ts.map +1 -0
- package/examples/node/basicReadWrite.js +26 -26
- package/examples/node/liveStream.js +15 -15
- package/examples/node/seriesAndFrames.js +38 -38
- package/examples/node/streamWrite.js +47 -45
- package/package.json +15 -13
- package/src/access/payload.ts +12 -12
- package/src/access/policy/client.ts +13 -12
- package/src/access/policy/external.ts +1 -0
- package/src/access/policy/ontology.ts +17 -0
- package/src/access/policy/payload.ts +7 -19
- package/src/access/policy/policy.spec.ts +16 -16
- package/src/access/policy/retriever.ts +2 -1
- package/src/access/policy/writer.ts +4 -4
- package/src/auth/auth.spec.ts +27 -23
- package/src/auth/auth.ts +7 -11
- package/src/channel/batchRetriever.spec.ts +25 -22
- package/src/channel/client.ts +19 -21
- package/src/channel/payload.ts +16 -20
- package/src/channel/retriever.ts +20 -21
- package/src/channel/writer.ts +11 -13
- package/src/client.ts +6 -16
- package/src/connection/checker.ts +9 -11
- package/src/connection/connection.spec.ts +17 -5
- package/src/control/state.ts +8 -9
- package/src/errors.spec.ts +1 -1
- package/src/errors.ts +8 -0
- package/src/framer/adapter.spec.ts +28 -23
- package/src/framer/adapter.ts +37 -41
- package/src/framer/client.spec.ts +5 -11
- package/src/framer/client.ts +34 -38
- package/src/framer/deleter.ts +5 -6
- package/src/framer/frame.ts +62 -50
- package/src/framer/iterator.ts +11 -16
- package/src/framer/streamer.spec.ts +2 -10
- package/src/framer/streamer.ts +15 -19
- package/src/framer/writer.spec.ts +48 -7
- package/src/framer/writer.ts +39 -31
- package/src/hardware/device/client.ts +64 -39
- package/src/hardware/device/device.spec.ts +49 -33
- package/src/hardware/device/payload.ts +29 -29
- package/src/hardware/rack/client.ts +52 -65
- package/src/hardware/rack/payload.ts +9 -18
- package/src/hardware/rack/rack.spec.ts +12 -0
- package/src/hardware/task/client.ts +160 -131
- package/src/hardware/task/payload.ts +49 -68
- package/src/hardware/task/task.spec.ts +98 -81
- package/src/label/client.ts +12 -15
- package/src/label/payload.ts +3 -9
- package/src/label/retriever.ts +3 -7
- package/src/label/writer.ts +8 -15
- package/src/ontology/client.ts +17 -22
- package/src/ontology/group/client.ts +5 -5
- package/src/ontology/group/group.spec.ts +4 -4
- package/src/ontology/group/group.ts +10 -7
- package/src/ontology/group/payload.ts +11 -35
- package/src/ontology/group/writer.ts +22 -26
- package/src/ontology/ontology.spec.ts +15 -15
- package/src/ontology/payload.ts +67 -43
- package/src/ontology/writer.ts +16 -23
- package/src/ranger/alias.ts +25 -42
- package/src/ranger/client.ts +29 -38
- package/src/ranger/external.ts +1 -1
- package/src/ranger/kv.ts +9 -32
- package/src/ranger/payload.ts +14 -36
- package/src/ranger/ranger.spec.ts +1 -2
- package/src/ranger/writer.ts +8 -26
- package/src/signals/observable.ts +3 -4
- package/src/user/client.ts +8 -4
- package/src/user/payload.ts +5 -9
- package/src/user/retriever.ts +1 -1
- package/src/user/user.spec.ts +17 -15
- package/src/user/writer.ts +3 -10
- package/src/util/decodeJSONString.ts +13 -0
- package/src/util/parseWithoutKeyConversion.ts +19 -0
- package/src/util/retrieve.spec.ts +3 -13
- package/src/util/retrieve.ts +2 -12
- package/src/util/telem.ts +1 -1
- package/src/vite-env.d.ts +1 -0
- package/src/workspace/client.ts +30 -57
- package/src/workspace/external.ts +11 -0
- package/src/workspace/index.ts +1 -1
- package/src/workspace/lineplot/client.ts +22 -36
- package/src/workspace/lineplot/external.ts +11 -0
- package/src/workspace/lineplot/index.ts +1 -1
- package/src/workspace/lineplot/linePlot.spec.ts +1 -2
- package/src/workspace/lineplot/payload.ts +32 -0
- package/src/workspace/log/client.ts +25 -39
- package/src/workspace/log/external.ts +11 -0
- package/src/workspace/log/index.ts +1 -1
- package/src/workspace/log/log.spec.ts +5 -18
- package/src/workspace/log/payload.ts +32 -0
- package/src/workspace/payload.ts +36 -0
- package/src/workspace/schematic/client.ts +30 -56
- package/src/workspace/schematic/external.ts +11 -0
- package/src/workspace/schematic/index.ts +1 -1
- package/src/workspace/schematic/payload.ts +37 -0
- package/src/workspace/schematic/schematic.spec.ts +15 -6
- package/src/workspace/table/client.ts +27 -50
- package/src/workspace/table/external.ts +11 -0
- package/src/workspace/table/index.ts +1 -1
- package/src/workspace/table/payload.ts +36 -0
- package/src/workspace/workspace.spec.ts +1 -2
- package/dist/channel/creator.d.ts +0 -9
- package/dist/channel/creator.d.ts.map +0 -1
- package/src/channel/creator.ts +0 -37
|
@@ -94,7 +94,7 @@ describe("Policy", () => {
|
|
|
94
94
|
});
|
|
95
95
|
describe("many", () => {
|
|
96
96
|
test("with keys", async () => {
|
|
97
|
-
const policiesToCreate: policy.
|
|
97
|
+
const policiesToCreate: policy.New[] = [
|
|
98
98
|
{
|
|
99
99
|
subjects: [{ type: user.ONTOLOGY_TYPE, key: "10" }],
|
|
100
100
|
objects: [
|
|
@@ -185,8 +185,8 @@ describe("Policy", () => {
|
|
|
185
185
|
await client.access.policy.delete([policies[0].key, policies[1].key]);
|
|
186
186
|
});
|
|
187
187
|
test("by subject", async () => {
|
|
188
|
-
const key1 = id.
|
|
189
|
-
const key2 = id.
|
|
188
|
+
const key1 = id.create();
|
|
189
|
+
const key2 = id.create();
|
|
190
190
|
const created = await client.access.policy.create([
|
|
191
191
|
{
|
|
192
192
|
subjects: [
|
|
@@ -216,10 +216,10 @@ describe("Policy", () => {
|
|
|
216
216
|
});
|
|
217
217
|
describe("delete", async () => {
|
|
218
218
|
test("one", async () => {
|
|
219
|
-
const id1 = id.
|
|
220
|
-
const id2 = id.
|
|
221
|
-
const id3 = id.
|
|
222
|
-
const policies: policy.
|
|
219
|
+
const id1 = id.create();
|
|
220
|
+
const id2 = id.create();
|
|
221
|
+
const id3 = id.create();
|
|
222
|
+
const policies: policy.New[] = [
|
|
223
223
|
{
|
|
224
224
|
subjects: [
|
|
225
225
|
{ type: user.ONTOLOGY_TYPE, key: id1 },
|
|
@@ -250,10 +250,10 @@ describe("Policy", () => {
|
|
|
250
250
|
await client.access.policy.delete(created[1].key);
|
|
251
251
|
});
|
|
252
252
|
test("many", async () => {
|
|
253
|
-
const id1 = id.
|
|
254
|
-
const id2 = id.
|
|
255
|
-
const id3 = id.
|
|
256
|
-
const policies: policy.
|
|
253
|
+
const id1 = id.create();
|
|
254
|
+
const id2 = id.create();
|
|
255
|
+
const id3 = id.create();
|
|
256
|
+
const policies: policy.New[] = [
|
|
257
257
|
{
|
|
258
258
|
subjects: [
|
|
259
259
|
{ type: user.ONTOLOGY_TYPE, key: id1 },
|
|
@@ -288,7 +288,7 @@ describe("Policy", () => {
|
|
|
288
288
|
|
|
289
289
|
describe("privilege", async () => {
|
|
290
290
|
test("new user", async () => {
|
|
291
|
-
const username = id.
|
|
291
|
+
const username = id.create();
|
|
292
292
|
const user2 = await client.user.create({ username, password: "pwd1" });
|
|
293
293
|
expect(user2).toBeDefined();
|
|
294
294
|
const client2 = new Synnax({
|
|
@@ -298,7 +298,7 @@ describe("privilege", async () => {
|
|
|
298
298
|
password: "pwd1",
|
|
299
299
|
});
|
|
300
300
|
await expect(
|
|
301
|
-
client2.user.create({ username: id.
|
|
301
|
+
client2.user.create({ username: id.create(), password: id.create() }),
|
|
302
302
|
).rejects.toThrow(AuthError);
|
|
303
303
|
|
|
304
304
|
const policy = await client.access.policy.create({
|
|
@@ -307,11 +307,11 @@ describe("privilege", async () => {
|
|
|
307
307
|
actions: ["create"],
|
|
308
308
|
});
|
|
309
309
|
|
|
310
|
-
const newUsername = id.
|
|
310
|
+
const newUsername = id.create();
|
|
311
311
|
|
|
312
312
|
const newUser = await client2.user.create({
|
|
313
313
|
username: newUsername,
|
|
314
|
-
password: id.
|
|
314
|
+
password: id.create(),
|
|
315
315
|
});
|
|
316
316
|
|
|
317
317
|
expect(newUser.username).toEqual(newUsername);
|
|
@@ -320,7 +320,7 @@ describe("privilege", async () => {
|
|
|
320
320
|
await client.access.policy.delete(policy.key);
|
|
321
321
|
|
|
322
322
|
await expect(
|
|
323
|
-
client2.user.create({ username: id.
|
|
323
|
+
client2.user.create({ username: id.create(), password: id.create() }),
|
|
324
324
|
).rejects.toThrow(AuthError);
|
|
325
325
|
});
|
|
326
326
|
});
|
|
@@ -18,7 +18,8 @@ const reqZ = z.object({
|
|
|
18
18
|
keys: keyZ.array().optional(),
|
|
19
19
|
subjects: ontology.idZ.array().optional(),
|
|
20
20
|
});
|
|
21
|
-
|
|
21
|
+
interface Request extends z.infer<typeof reqZ> {}
|
|
22
|
+
|
|
22
23
|
const resZ = z.object({ policies: nullableArrayZ(policyZ) });
|
|
23
24
|
|
|
24
25
|
const ENDPOINT = "/access/policy/retrieve";
|
|
@@ -14,8 +14,8 @@ import { z } from "zod";
|
|
|
14
14
|
import {
|
|
15
15
|
type Key,
|
|
16
16
|
keyZ,
|
|
17
|
-
type
|
|
18
|
-
|
|
17
|
+
type New,
|
|
18
|
+
newZ,
|
|
19
19
|
type Policy,
|
|
20
20
|
policyZ,
|
|
21
21
|
} from "@/access/policy/payload";
|
|
@@ -36,8 +36,8 @@ export class Writer {
|
|
|
36
36
|
this.client = client;
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
-
async create(policies:
|
|
40
|
-
const parsedPolicies =
|
|
39
|
+
async create(policies: New | New[]): Promise<Policy[]> {
|
|
40
|
+
const parsedPolicies = newZ.array().parse(toArray(policies));
|
|
41
41
|
const req = parsedPolicies.map((policy) => ({
|
|
42
42
|
objects: toArray(policy.objects),
|
|
43
43
|
actions: toArray(policy.actions),
|
package/src/auth/auth.spec.ts
CHANGED
|
@@ -12,7 +12,7 @@ import { URL } from "@synnaxlabs/x/url";
|
|
|
12
12
|
import { describe, expect, it, test } from "vitest";
|
|
13
13
|
|
|
14
14
|
import { auth } from "@/auth";
|
|
15
|
-
import { AuthError, InvalidTokenError } from "@/errors";
|
|
15
|
+
import { AuthError, ExpiredTokenError, InvalidTokenError } from "@/errors";
|
|
16
16
|
import { HOST, PORT } from "@/setupspecs";
|
|
17
17
|
import { Transport } from "@/transport";
|
|
18
18
|
|
|
@@ -46,30 +46,34 @@ describe("auth", () => {
|
|
|
46
46
|
expect(err).toBeInstanceOf(AuthError);
|
|
47
47
|
});
|
|
48
48
|
|
|
49
|
-
describe("
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
49
|
+
describe("token retry", () => {
|
|
50
|
+
const ERROR_TYPES = [InvalidTokenError, ExpiredTokenError];
|
|
51
|
+
ERROR_TYPES.forEach((ErrorType) => {
|
|
52
|
+
it(`should re-authenticate and retry the request for ${ErrorType.name}`, async () => {
|
|
53
|
+
const transport = new Transport(new URL({ host: HOST, port: PORT }));
|
|
54
|
+
const client = new auth.Client(transport.unary, {
|
|
55
|
+
username: "synnax",
|
|
56
|
+
password: "seldon",
|
|
57
|
+
});
|
|
58
|
+
const mw = client.middleware();
|
|
59
|
+
let isFirst = true;
|
|
60
|
+
let tkOne: string | undefined;
|
|
61
|
+
let tkTwo: string | undefined;
|
|
62
|
+
const [, err] = await mw(DUMMY_CTX, async () => {
|
|
63
|
+
if (isFirst) {
|
|
64
|
+
isFirst = false;
|
|
65
|
+
tkOne = client.token;
|
|
66
|
+
return [DUMMY_CTX, new ErrorType()];
|
|
67
|
+
}
|
|
68
|
+
tkTwo = client.token;
|
|
69
|
+
return [DUMMY_CTX, null];
|
|
70
|
+
});
|
|
71
|
+
expect(err).toBeNull();
|
|
72
|
+
expect(tkOne).toBeDefined();
|
|
73
|
+
expect(tkTwo).toBeDefined();
|
|
55
74
|
});
|
|
56
|
-
const mw = client.middleware();
|
|
57
|
-
let isFirst = true;
|
|
58
|
-
let tkOne: string | undefined;
|
|
59
|
-
let tkTwo: string | undefined;
|
|
60
|
-
const [, err] = await mw(DUMMY_CTX, async () => {
|
|
61
|
-
if (isFirst) {
|
|
62
|
-
isFirst = false;
|
|
63
|
-
tkOne = client.token;
|
|
64
|
-
return [DUMMY_CTX, new InvalidTokenError()];
|
|
65
|
-
}
|
|
66
|
-
tkTwo = client.token;
|
|
67
|
-
return [DUMMY_CTX, null];
|
|
68
|
-
});
|
|
69
|
-
expect(err).toBeNull();
|
|
70
|
-
expect(tkOne).toBeDefined();
|
|
71
|
-
expect(tkTwo).toBeDefined();
|
|
72
75
|
});
|
|
76
|
+
|
|
73
77
|
it("should fail after MAX_RETRIES", async () => {
|
|
74
78
|
const transport = new Transport(new URL({ host: HOST, port: PORT }));
|
|
75
79
|
const client = new auth.Client(transport.unary, {
|
package/src/auth/auth.ts
CHANGED
|
@@ -10,19 +10,13 @@
|
|
|
10
10
|
import { type Middleware, sendRequired, type UnaryClient } from "@synnaxlabs/freighter";
|
|
11
11
|
import { z } from "zod";
|
|
12
12
|
|
|
13
|
-
import { InvalidTokenError } from "@/errors";
|
|
13
|
+
import { ExpiredTokenError, InvalidTokenError } from "@/errors";
|
|
14
14
|
import { user } from "@/user";
|
|
15
15
|
|
|
16
|
-
const insecureCredentialsZ = z.object({
|
|
17
|
-
|
|
18
|
-
password: z.string(),
|
|
19
|
-
});
|
|
20
|
-
type InsecureCredentials = z.infer<typeof insecureCredentialsZ>;
|
|
16
|
+
const insecureCredentialsZ = z.object({ username: z.string(), password: z.string() });
|
|
17
|
+
interface InsecureCredentials extends z.infer<typeof insecureCredentialsZ> {}
|
|
21
18
|
|
|
22
|
-
const tokenResponseZ = z.object({
|
|
23
|
-
token: z.string(),
|
|
24
|
-
user: user.userZ,
|
|
25
|
-
});
|
|
19
|
+
const tokenResponseZ = z.object({ token: z.string(), user: user.userZ });
|
|
26
20
|
|
|
27
21
|
const LOGIN_ENDPOINT = "/auth/login";
|
|
28
22
|
|
|
@@ -37,6 +31,8 @@ const changePasswordReqZ = z.object({
|
|
|
37
31
|
});
|
|
38
32
|
const changePasswordResZ = z.object({});
|
|
39
33
|
|
|
34
|
+
const RETRY_ON = [InvalidTokenError, ExpiredTokenError] as const;
|
|
35
|
+
|
|
40
36
|
export class Client {
|
|
41
37
|
token: string | undefined;
|
|
42
38
|
private readonly client: UnaryClient;
|
|
@@ -94,7 +90,7 @@ export class Client {
|
|
|
94
90
|
}
|
|
95
91
|
reqCtx.params.Authorization = `Bearer ${this.token}`;
|
|
96
92
|
const [resCtx, err] = await next(reqCtx);
|
|
97
|
-
if (
|
|
93
|
+
if (RETRY_ON.some((e) => e.matches(err)) && this.retryCount < MAX_RETRIES) {
|
|
98
94
|
this.authenticated = false;
|
|
99
95
|
this.authenticating = undefined;
|
|
100
96
|
this.retryCount += 1;
|
|
@@ -10,42 +10,45 @@
|
|
|
10
10
|
import { DataType, Rate } from "@synnaxlabs/x/telem";
|
|
11
11
|
import { describe, expect, it, vi } from "vitest";
|
|
12
12
|
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
analyzeChannelParams,
|
|
16
|
-
DebouncedBatchRetriever,
|
|
17
|
-
type RetrieveOptions,
|
|
18
|
-
type Retriever,
|
|
19
|
-
} from "@/channel/retriever";
|
|
13
|
+
import { channel } from "@/channel";
|
|
20
14
|
|
|
21
|
-
class MockRetriever implements Retriever {
|
|
22
|
-
func: (
|
|
15
|
+
class MockRetriever implements channel.Retriever {
|
|
16
|
+
func: (
|
|
17
|
+
channels: channel.Params,
|
|
18
|
+
options?: channel.RetrieveOptions,
|
|
19
|
+
) => Promise<channel.Payload[]>;
|
|
23
20
|
|
|
24
21
|
constructor(
|
|
25
|
-
func: (
|
|
22
|
+
func: (
|
|
23
|
+
channels: channel.Params,
|
|
24
|
+
options?: channel.RetrieveOptions,
|
|
25
|
+
) => Promise<channel.Payload[]>,
|
|
26
26
|
) {
|
|
27
27
|
this.func = func;
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
async search(): Promise<Payload[]> {
|
|
30
|
+
async search(): Promise<channel.Payload[]> {
|
|
31
31
|
throw new Error("Method not implemented.");
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
async page(): Promise<Payload[]> {
|
|
34
|
+
async page(): Promise<channel.Payload[]> {
|
|
35
35
|
throw new Error("Method not implemented.");
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
async retrieve(
|
|
38
|
+
async retrieve(
|
|
39
|
+
channels: channel.Params,
|
|
40
|
+
options?: channel.RetrieveOptions,
|
|
41
|
+
): Promise<channel.Payload[]> {
|
|
39
42
|
return await this.func(channels, options);
|
|
40
43
|
}
|
|
41
44
|
}
|
|
42
45
|
|
|
43
|
-
describe("
|
|
46
|
+
describe("channelchannel.Retriever", () => {
|
|
44
47
|
it("should batch multiple retrieve requests", async () => {
|
|
45
48
|
const called = vi.fn();
|
|
46
|
-
const base = new MockRetriever(async (batch): Promise<Payload[]> => {
|
|
49
|
+
const base = new MockRetriever(async (batch): Promise<channel.Payload[]> => {
|
|
47
50
|
called(batch);
|
|
48
|
-
const { normalized } =
|
|
51
|
+
const { normalized } = channel.analyzeParams(batch);
|
|
49
52
|
return normalized.map((key) => ({
|
|
50
53
|
key: key as number,
|
|
51
54
|
name: `channel-${key}`,
|
|
@@ -60,7 +63,7 @@ describe("channelRetriever", () => {
|
|
|
60
63
|
requires: [],
|
|
61
64
|
}));
|
|
62
65
|
});
|
|
63
|
-
const retriever = new DebouncedBatchRetriever(base, 10);
|
|
66
|
+
const retriever = new channel.DebouncedBatchRetriever(base, 10);
|
|
64
67
|
const res = await Promise.all([
|
|
65
68
|
retriever.retrieve([1]),
|
|
66
69
|
retriever.retrieve([2]),
|
|
@@ -72,9 +75,9 @@ describe("channelRetriever", () => {
|
|
|
72
75
|
});
|
|
73
76
|
it("should only fetch duplicate keys once", async () => {
|
|
74
77
|
const called = vi.fn();
|
|
75
|
-
const base = new MockRetriever(async (batch): Promise<Payload[]> => {
|
|
78
|
+
const base = new MockRetriever(async (batch): Promise<channel.Payload[]> => {
|
|
76
79
|
called(batch);
|
|
77
|
-
const { normalized } =
|
|
80
|
+
const { normalized } = channel.analyzeParams(batch);
|
|
78
81
|
return normalized.map((key) => ({
|
|
79
82
|
key: key as number,
|
|
80
83
|
name: `channel-${key}`,
|
|
@@ -89,7 +92,7 @@ describe("channelRetriever", () => {
|
|
|
89
92
|
requires: [],
|
|
90
93
|
}));
|
|
91
94
|
});
|
|
92
|
-
const retriever = new DebouncedBatchRetriever(base, 10);
|
|
95
|
+
const retriever = new channel.DebouncedBatchRetriever(base, 10);
|
|
93
96
|
const res = await Promise.all([
|
|
94
97
|
retriever.retrieve([1]),
|
|
95
98
|
retriever.retrieve([2]),
|
|
@@ -100,10 +103,10 @@ describe("channelRetriever", () => {
|
|
|
100
103
|
expect(res.map((r) => r.map((c) => c.key))).toEqual([[1], [2], [1, 2]]);
|
|
101
104
|
});
|
|
102
105
|
it("should throw an error if the fetch fails", async () => {
|
|
103
|
-
const base = new MockRetriever(async (): Promise<Payload[]> => {
|
|
106
|
+
const base = new MockRetriever(async (): Promise<channel.Payload[]> => {
|
|
104
107
|
throw new Error("failed to fetch");
|
|
105
108
|
});
|
|
106
|
-
const retriever = new DebouncedBatchRetriever(base, 10);
|
|
109
|
+
const retriever = new channel.DebouncedBatchRetriever(base, 10);
|
|
107
110
|
await expect(retriever.retrieve([1])).rejects.toThrow("failed to fetch");
|
|
108
111
|
});
|
|
109
112
|
});
|
package/src/channel/client.ts
CHANGED
|
@@ -22,16 +22,17 @@ import { toArray } from "@synnaxlabs/x/toArray";
|
|
|
22
22
|
import { z } from "zod";
|
|
23
23
|
|
|
24
24
|
import {
|
|
25
|
+
channelZ,
|
|
25
26
|
type Key,
|
|
26
27
|
type KeyOrName,
|
|
27
|
-
type
|
|
28
|
-
|
|
28
|
+
type Name,
|
|
29
|
+
type New,
|
|
30
|
+
ONTOLOGY_TYPE,
|
|
29
31
|
type Params,
|
|
30
32
|
type Payload,
|
|
31
|
-
payload,
|
|
32
33
|
} from "@/channel/payload";
|
|
33
34
|
import {
|
|
34
|
-
|
|
35
|
+
analyzeParams,
|
|
35
36
|
CacheRetriever,
|
|
36
37
|
ClusterRetriever,
|
|
37
38
|
DebouncedBatchRetriever,
|
|
@@ -41,7 +42,7 @@ import {
|
|
|
41
42
|
import { type Writer } from "@/channel/writer";
|
|
42
43
|
import { ValidationError } from "@/errors";
|
|
43
44
|
import { type framer } from "@/framer";
|
|
44
|
-
import {
|
|
45
|
+
import { ontology } from "@/ontology";
|
|
45
46
|
import { group } from "@/ontology/group";
|
|
46
47
|
import { checkForMultipleOrNoResults } from "@/util/retrieve";
|
|
47
48
|
|
|
@@ -69,7 +70,7 @@ export class Channel {
|
|
|
69
70
|
* A human-readable name for the channel. This name is not guaranteed to be
|
|
70
71
|
* unique.
|
|
71
72
|
*/
|
|
72
|
-
readonly name:
|
|
73
|
+
readonly name: Name;
|
|
73
74
|
/**
|
|
74
75
|
* The rate at which the channel samples telemetry. This only applies to fixed rate
|
|
75
76
|
* channels, and will be 0 if the channel is indexed.
|
|
@@ -132,10 +133,7 @@ export class Channel {
|
|
|
132
133
|
alias,
|
|
133
134
|
expression = "",
|
|
134
135
|
requires = [],
|
|
135
|
-
}:
|
|
136
|
-
frameClient?: framer.Client;
|
|
137
|
-
density?: CrudeDensity;
|
|
138
|
-
}) {
|
|
136
|
+
}: New & { frameClient?: framer.Client; density?: CrudeDensity }) {
|
|
139
137
|
this.key = key;
|
|
140
138
|
this.name = name;
|
|
141
139
|
this.rate = new Rate(rate ?? 0);
|
|
@@ -163,7 +161,7 @@ export class Channel {
|
|
|
163
161
|
* network transportation, but also provided to you as a convenience.
|
|
164
162
|
*/
|
|
165
163
|
get payload(): Payload {
|
|
166
|
-
return
|
|
164
|
+
return channelZ.parse({
|
|
167
165
|
key: this.key,
|
|
168
166
|
name: this.name,
|
|
169
167
|
rate: this.rate.valueOf(),
|
|
@@ -186,7 +184,7 @@ export class Channel {
|
|
|
186
184
|
* @returns the ontology ID of the channel
|
|
187
185
|
*/
|
|
188
186
|
get ontologyID(): ontology.ID {
|
|
189
|
-
return
|
|
187
|
+
return ontologyID(this.key);
|
|
190
188
|
}
|
|
191
189
|
|
|
192
190
|
/**
|
|
@@ -215,9 +213,7 @@ const RETRIEVE_GROUP_ENDPOINT = "/channel/retrieve-group";
|
|
|
215
213
|
|
|
216
214
|
const retrieveGroupReqZ = z.object({});
|
|
217
215
|
|
|
218
|
-
const retrieveGroupResZ = z.object({
|
|
219
|
-
group: group.groupZ,
|
|
220
|
-
});
|
|
216
|
+
const retrieveGroupResZ = z.object({ group: group.groupZ });
|
|
221
217
|
|
|
222
218
|
/**
|
|
223
219
|
* The core client class for executing channel operations against a Synnax
|
|
@@ -225,7 +221,7 @@ const retrieveGroupResZ = z.object({
|
|
|
225
221
|
* through the `channels` property of an {@link Synnax} client.
|
|
226
222
|
*/
|
|
227
223
|
export class Client implements AsyncTermSearcher<string, Key, Channel> {
|
|
228
|
-
readonly type =
|
|
224
|
+
readonly type = ONTOLOGY_TYPE;
|
|
229
225
|
private readonly frameClient: framer.Client;
|
|
230
226
|
private readonly client: UnaryClient;
|
|
231
227
|
readonly retriever: Retriever;
|
|
@@ -278,7 +274,7 @@ export class Client implements AsyncTermSearcher<string, Key, Channel> {
|
|
|
278
274
|
* });
|
|
279
275
|
* ```
|
|
280
276
|
*/
|
|
281
|
-
async create(channel:
|
|
277
|
+
async create(channel: New, options?: CreateOptions): Promise<Channel>;
|
|
282
278
|
|
|
283
279
|
/**
|
|
284
280
|
* Creates multiple channels with the given properties. The order of the channels
|
|
@@ -303,10 +299,10 @@ export class Client implements AsyncTermSearcher<string, Key, Channel> {
|
|
|
303
299
|
*
|
|
304
300
|
* @param channels
|
|
305
301
|
*/
|
|
306
|
-
async create(channels:
|
|
302
|
+
async create(channels: New[], options?: CreateOptions): Promise<Channel[]>;
|
|
307
303
|
|
|
308
304
|
async create(
|
|
309
|
-
channels:
|
|
305
|
+
channels: New | New[],
|
|
310
306
|
options: CreateOptions = {},
|
|
311
307
|
): Promise<Channel | Channel[]> {
|
|
312
308
|
const { retrieveIfNameExists = false } = options;
|
|
@@ -387,7 +383,7 @@ export class Client implements AsyncTermSearcher<string, Key, Channel> {
|
|
|
387
383
|
* @param channels - The keys or names of the channels to delete.
|
|
388
384
|
*/
|
|
389
385
|
async delete(channels: Params): Promise<void> {
|
|
390
|
-
const { normalized, variant } =
|
|
386
|
+
const { normalized, variant } = analyzeParams(channels);
|
|
391
387
|
if (variant === "keys")
|
|
392
388
|
return await this.writer.delete({ keys: normalized as Key[] });
|
|
393
389
|
return await this.writer.delete({ names: normalized as string[] });
|
|
@@ -395,7 +391,6 @@ export class Client implements AsyncTermSearcher<string, Key, Channel> {
|
|
|
395
391
|
|
|
396
392
|
async rename(key: Key, name: string): Promise<void>;
|
|
397
393
|
async rename(keys: Key[], names: string[]): Promise<void>;
|
|
398
|
-
|
|
399
394
|
async rename(keys: Key | Key[], names: string | string[]): Promise<void> {
|
|
400
395
|
return await this.writer.rename(toArray(keys), toArray(names));
|
|
401
396
|
}
|
|
@@ -466,3 +461,6 @@ export const resolveCalculatedIndex = async (
|
|
|
466
461
|
}
|
|
467
462
|
return null;
|
|
468
463
|
};
|
|
464
|
+
|
|
465
|
+
export const ontologyID = (key: Key): ontology.ID =>
|
|
466
|
+
new ontology.ID({ type: ONTOLOGY_TYPE, key: key.toString() });
|
package/src/channel/payload.ts
CHANGED
|
@@ -10,25 +10,25 @@
|
|
|
10
10
|
import { DataType, Rate } from "@synnaxlabs/x/telem";
|
|
11
11
|
import { z } from "zod";
|
|
12
12
|
|
|
13
|
-
import { ontology } from "@/ontology";
|
|
14
13
|
import { nullableArrayZ } from "@/util/zod";
|
|
15
14
|
|
|
16
15
|
export const keyZ = z.number();
|
|
17
|
-
export type Key =
|
|
18
|
-
export type Keys =
|
|
19
|
-
export
|
|
20
|
-
export type
|
|
16
|
+
export type Key = z.infer<typeof keyZ>;
|
|
17
|
+
export type Keys = Key[];
|
|
18
|
+
export const nameZ = z.string();
|
|
19
|
+
export type Name = z.infer<typeof nameZ>;
|
|
20
|
+
export type Names = Name[];
|
|
21
21
|
export type KeyOrName = Key | Name;
|
|
22
22
|
export type KeysOrNames = Keys | Names;
|
|
23
23
|
export type Params = Key | Name | Keys | Names;
|
|
24
24
|
|
|
25
|
-
export const
|
|
26
|
-
name:
|
|
27
|
-
key:
|
|
25
|
+
export const channelZ = z.object({
|
|
26
|
+
name: nameZ,
|
|
27
|
+
key: keyZ,
|
|
28
28
|
rate: Rate.z,
|
|
29
29
|
dataType: DataType.z,
|
|
30
30
|
leaseholder: z.number(),
|
|
31
|
-
index:
|
|
31
|
+
index: keyZ,
|
|
32
32
|
isIndex: z.boolean(),
|
|
33
33
|
internal: z.boolean(),
|
|
34
34
|
virtual: z.boolean(),
|
|
@@ -36,13 +36,12 @@ export const payload = z.object({
|
|
|
36
36
|
expression: z.string().default(""),
|
|
37
37
|
requires: nullableArrayZ(keyZ),
|
|
38
38
|
});
|
|
39
|
+
export interface Payload extends z.infer<typeof channelZ> {}
|
|
39
40
|
|
|
40
|
-
export
|
|
41
|
-
|
|
42
|
-
export const newPayload = payload.extend({
|
|
43
|
-
key: z.number().optional(),
|
|
41
|
+
export const newZ = channelZ.extend({
|
|
42
|
+
key: keyZ.optional(),
|
|
44
43
|
leaseholder: z.number().optional(),
|
|
45
|
-
index:
|
|
44
|
+
index: keyZ.optional(),
|
|
46
45
|
rate: Rate.z.optional().default(0),
|
|
47
46
|
isIndex: z.boolean().optional(),
|
|
48
47
|
internal: z.boolean().optional().default(false),
|
|
@@ -50,10 +49,7 @@ export const newPayload = payload.extend({
|
|
|
50
49
|
expression: z.string().optional().default(""),
|
|
51
50
|
requires: nullableArrayZ(keyZ).optional().default([]),
|
|
52
51
|
});
|
|
52
|
+
export interface New extends z.input<typeof newZ> {}
|
|
53
53
|
|
|
54
|
-
export
|
|
55
|
-
|
|
56
|
-
export const ONTOLOGY_TYPE: ontology.ResourceType = "channel";
|
|
57
|
-
|
|
58
|
-
export const ontologyID = (key: Key): ontology.ID =>
|
|
59
|
-
new ontology.ID({ type: ONTOLOGY_TYPE, key: key.toString() });
|
|
54
|
+
export const ONTOLOGY_TYPE = "channel";
|
|
55
|
+
export type OntologyType = typeof ONTOLOGY_TYPE;
|
package/src/channel/retriever.ts
CHANGED
|
@@ -6,28 +6,33 @@
|
|
|
6
6
|
// As of the Change Date specified in that file, in accordance with the Business Source
|
|
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 { type UnaryClient } from "@synnaxlabs/freighter";
|
|
10
11
|
import { debounce } from "@synnaxlabs/x/debounce";
|
|
11
12
|
import { DataType } from "@synnaxlabs/x/telem";
|
|
12
13
|
import { Mutex } from "async-mutex";
|
|
13
14
|
import { z } from "zod";
|
|
14
15
|
|
|
15
16
|
import {
|
|
17
|
+
channelZ,
|
|
16
18
|
type Key,
|
|
17
19
|
type KeyOrName,
|
|
18
20
|
type Keys,
|
|
19
21
|
type KeysOrNames,
|
|
22
|
+
keyZ,
|
|
20
23
|
type Params,
|
|
21
24
|
type Payload,
|
|
22
|
-
payload,
|
|
23
25
|
} from "@/channel/payload";
|
|
24
26
|
import { QueryError } from "@/errors";
|
|
25
|
-
import {
|
|
27
|
+
import {
|
|
28
|
+
analyzeParams as analyzeParameters,
|
|
29
|
+
type ParamAnalysisResult,
|
|
30
|
+
} from "@/util/retrieve";
|
|
26
31
|
import { nullableArrayZ } from "@/util/zod";
|
|
27
32
|
|
|
28
33
|
const reqZ = z.object({
|
|
29
34
|
leaseholder: z.number().optional(),
|
|
30
|
-
keys:
|
|
35
|
+
keys: keyZ.array().optional(),
|
|
31
36
|
names: z.string().array().optional(),
|
|
32
37
|
search: z.string().optional(),
|
|
33
38
|
rangeKey: z.string().optional(),
|
|
@@ -39,23 +44,17 @@ const reqZ = z.object({
|
|
|
39
44
|
isIndex: z.boolean().optional(),
|
|
40
45
|
internal: z.boolean().optional(),
|
|
41
46
|
});
|
|
47
|
+
interface Request extends z.input<typeof reqZ> {}
|
|
42
48
|
|
|
43
|
-
|
|
49
|
+
export interface RetrieveOptions extends Omit<Request, "keys" | "names" | "search"> {}
|
|
50
|
+
export interface PageOptions extends Omit<RetrieveOptions, "offset" | "limit"> {}
|
|
44
51
|
|
|
45
|
-
|
|
46
|
-
export type PageOptions = Omit<RetrieveOptions, "offset" | "limit">;
|
|
52
|
+
const resZ = z.object({ channels: nullableArrayZ(channelZ) });
|
|
47
53
|
|
|
48
|
-
const
|
|
49
|
-
channels: nullableArrayZ(payload),
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
export const analyzeChannelParams = (
|
|
54
|
+
export const analyzeParams = (
|
|
53
55
|
channels: Params,
|
|
54
56
|
): ParamAnalysisResult<KeyOrName, { number: "keys"; string: "names" }> =>
|
|
55
|
-
|
|
56
|
-
number: "keys",
|
|
57
|
-
string: "names",
|
|
58
|
-
});
|
|
57
|
+
analyzeParameters(channels, { number: "keys", string: "names" });
|
|
59
58
|
|
|
60
59
|
export interface Retriever {
|
|
61
60
|
retrieve: (channels: Params, opts?: RetrieveOptions) => Promise<Payload[]>;
|
|
@@ -76,7 +75,7 @@ export class ClusterRetriever implements Retriever {
|
|
|
76
75
|
}
|
|
77
76
|
|
|
78
77
|
async retrieve(channels: Params, options?: RetrieveOptions): Promise<Payload[]> {
|
|
79
|
-
const res =
|
|
78
|
+
const res = analyzeParams(channels);
|
|
80
79
|
const { variant } = res;
|
|
81
80
|
let { normalized } = res;
|
|
82
81
|
if (variant === "keys" && (normalized as Key[]).indexOf(0) !== -1)
|
|
@@ -121,7 +120,7 @@ export class CacheRetriever implements Retriever {
|
|
|
121
120
|
}
|
|
122
121
|
|
|
123
122
|
async retrieve(channels: Params, options?: RetrieveOptions): Promise<Payload[]> {
|
|
124
|
-
const { normalized } =
|
|
123
|
+
const { normalized } = analyzeParameters<string | number>(channels, {
|
|
125
124
|
string: "names",
|
|
126
125
|
number: "keys",
|
|
127
126
|
});
|
|
@@ -139,7 +138,7 @@ export class CacheRetriever implements Retriever {
|
|
|
139
138
|
}
|
|
140
139
|
|
|
141
140
|
delete(channels: Params): void {
|
|
142
|
-
const { variant, normalized } =
|
|
141
|
+
const { variant, normalized } = analyzeParams(channels);
|
|
143
142
|
if (variant === "names")
|
|
144
143
|
(normalized as string[]).forEach((name) => {
|
|
145
144
|
const keys = this.namesToKeys.get(name);
|
|
@@ -234,7 +233,7 @@ export class DebouncedBatchRetriever implements Retriever {
|
|
|
234
233
|
}
|
|
235
234
|
|
|
236
235
|
async retrieve(channels: Params): Promise<Payload[]> {
|
|
237
|
-
const { normalized, variant } =
|
|
236
|
+
const { normalized, variant } = analyzeParams(channels);
|
|
238
237
|
// Bypass on name fetches for now.
|
|
239
238
|
if (variant === "names") return await this.wrapped.retrieve(normalized);
|
|
240
239
|
|
|
@@ -269,7 +268,7 @@ export const retrieveRequired = async (
|
|
|
269
268
|
r: Retriever,
|
|
270
269
|
channels: Params,
|
|
271
270
|
): Promise<Payload[]> => {
|
|
272
|
-
const { normalized } =
|
|
271
|
+
const { normalized } = analyzeParams(channels);
|
|
273
272
|
const results = await r.retrieve(normalized);
|
|
274
273
|
const notFound: KeyOrName[] = [];
|
|
275
274
|
normalized.forEach((v) => {
|