doover-js 0.5.4 → 0.6.1
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/README.md +146 -0
- package/dist/apis/_args.d.ts +16 -0
- package/dist/apis/_args.js +26 -0
- package/dist/apis/agents-api.d.ts +4 -1
- package/dist/apis/agents-api.js +139 -1
- package/dist/apis/aggregates-api.d.ts +23 -3
- package/dist/apis/aggregates-api.js +33 -4
- package/dist/apis/alarms-api.d.ts +30 -0
- package/dist/apis/alarms-api.js +51 -6
- package/dist/apis/channels-api.d.ts +40 -3
- package/dist/apis/channels-api.js +50 -5
- package/dist/apis/connections-api.d.ts +21 -5
- package/dist/apis/connections-api.js +26 -10
- package/dist/apis/messages-api.d.ts +54 -9
- package/dist/apis/messages-api.js +94 -12
- package/dist/apis/notifications-api.d.ts +52 -0
- package/dist/apis/notifications-api.js +99 -13
- package/dist/apis/permissions-api.d.ts +8 -0
- package/dist/apis/permissions-api.js +11 -2
- package/dist/apis/processors-api.d.ts +35 -0
- package/dist/apis/processors-api.js +64 -8
- package/dist/apis/users-api.d.ts +8 -0
- package/dist/apis/users-api.js +13 -0
- package/dist/client/capabilities.d.ts +10 -0
- package/dist/client/capabilities.js +43 -0
- package/dist/client/data-client.d.ts +104 -0
- package/dist/client/data-client.js +2 -0
- package/dist/client/doover-client.d.ts +31 -31
- package/dist/client/doover-client.js +58 -37
- package/dist/client/errors.d.ts +23 -0
- package/dist/client/errors.js +50 -0
- package/dist/client/local-agent-client.d.ts +65 -0
- package/dist/client/local-agent-client.js +222 -0
- package/dist/client/multiplex-client.d.ts +151 -0
- package/dist/client/multiplex-client.js +744 -0
- package/dist/client/multiplex-gateway.d.ts +49 -0
- package/dist/client/multiplex-gateway.js +114 -0
- package/dist/client/multiplex-merge.d.ts +12 -0
- package/dist/client/multiplex-merge.js +42 -0
- package/dist/client/provenance.d.ts +60 -0
- package/dist/client/provenance.js +113 -0
- package/dist/client/stats.d.ts +24 -0
- package/dist/client/stats.js +62 -0
- package/dist/client/status-tracker.d.ts +24 -0
- package/dist/client/status-tracker.js +90 -0
- package/dist/gateway/gateway-client.d.ts +23 -1
- package/dist/gateway/gateway-client.js +75 -6
- package/dist/http/rest-client.d.ts +5 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.js +24 -7
- package/dist/react/context.d.ts +9 -5
- package/dist/react/context.js +6 -2
- package/dist/react/index.d.ts +3 -0
- package/dist/react/index.js +3 -1
- package/dist/react/useChannelAggregate.d.ts +8 -1
- package/dist/react/useChannelAggregate.js +24 -6
- package/dist/react/useChannelMessage.d.ts +8 -1
- package/dist/react/useChannelMessage.js +17 -6
- package/dist/react/useChannelMessages.d.ts +8 -1
- package/dist/react/useChannelMessages.js +21 -7
- package/dist/react/useChannelSubscription.d.ts +6 -0
- package/dist/react/useChannelSubscription.js +8 -18
- package/dist/react/useClientStatus.d.ts +10 -0
- package/dist/react/useClientStatus.js +22 -0
- package/dist/react/useConnectionState.d.ts +5 -0
- package/dist/react/useConnectionState.js +5 -0
- package/dist/react/useMultiAgentAggregates.d.ts +8 -1
- package/dist/react/useMultiAgentAggregates.js +25 -21
- package/dist/react/useMultiAgentChannelMessages.d.ts +19 -1
- package/dist/react/useMultiAgentChannelMessages.js +42 -19
- package/dist/react/useSendMessage.d.ts +8 -1
- package/dist/react/useSendMessage.js +15 -2
- package/dist/react/useSendRpc.js +4 -1
- package/dist/react/useUpdateAggregate.d.ts +6 -0
- package/dist/react/useUpdateAggregate.js +20 -4
- package/dist/react/useUpdateMessage.d.ts +5 -0
- package/dist/react/useUpdateMessage.js +11 -2
- package/dist/rpc/errors.d.ts +10 -0
- package/dist/rpc/errors.js +23 -0
- package/dist/rpc/rpc-dispatcher.d.ts +28 -0
- package/dist/rpc/rpc-dispatcher.js +129 -0
- package/dist/test/api-args.test.d.ts +1 -0
- package/dist/test/api-args.test.js +48 -0
- package/dist/test/api-overloads.test.d.ts +9 -0
- package/dist/test/api-overloads.test.js +276 -0
- package/dist/test/capabilities.test.d.ts +1 -0
- package/dist/test/capabilities.test.js +27 -0
- package/dist/test/data-client-errors.test.d.ts +1 -0
- package/dist/test/data-client-errors.test.js +24 -0
- package/dist/test/data-client-shape.test.d.ts +1 -0
- package/dist/test/data-client-shape.test.js +28 -0
- package/dist/test/doover-client-dataclient.test.d.ts +1 -0
- package/dist/test/doover-client-dataclient.test.js +58 -0
- package/dist/test/doover-client-provenance.test.d.ts +1 -0
- package/dist/test/doover-client-provenance.test.js +32 -0
- package/dist/test/exports.test.d.ts +1 -0
- package/dist/test/exports.test.js +76 -0
- package/dist/test/gateway-provenance.test.d.ts +1 -0
- package/dist/test/gateway-provenance.test.js +29 -0
- package/dist/test/gateway-subscriptions.test.d.ts +1 -0
- package/dist/test/gateway-subscriptions.test.js +95 -0
- package/dist/test/local-agent-client.test.d.ts +1 -0
- package/dist/test/local-agent-client.test.js +202 -0
- package/dist/test/multiplex-capabilities.test.d.ts +1 -0
- package/dist/test/multiplex-capabilities.test.js +37 -0
- package/dist/test/multiplex-conflicts.test.d.ts +1 -0
- package/dist/test/multiplex-conflicts.test.js +35 -0
- package/dist/test/multiplex-gateway.test.d.ts +1 -0
- package/dist/test/multiplex-gateway.test.js +103 -0
- package/dist/test/multiplex-merge.test.d.ts +1 -0
- package/dist/test/multiplex-merge.test.js +19 -0
- package/dist/test/multiplex-noncore.test.d.ts +1 -0
- package/dist/test/multiplex-noncore.test.js +82 -0
- package/dist/test/multiplex-passthrough.test.d.ts +1 -0
- package/dist/test/multiplex-passthrough.test.js +35 -0
- package/dist/test/multiplex-reads.test.d.ts +1 -0
- package/dist/test/multiplex-reads.test.js +87 -0
- package/dist/test/multiplex-registry.test.d.ts +1 -0
- package/dist/test/multiplex-registry.test.js +81 -0
- package/dist/test/multiplex-routing.test.d.ts +1 -0
- package/dist/test/multiplex-routing.test.js +45 -0
- package/dist/test/multiplex-status.test.d.ts +1 -0
- package/dist/test/multiplex-status.test.js +63 -0
- package/dist/test/multiplex-writes.test.d.ts +1 -0
- package/dist/test/multiplex-writes.test.js +93 -0
- package/dist/test/provenance-regression.test.d.ts +1 -0
- package/dist/test/provenance-regression.test.js +45 -0
- package/dist/test/provenance-stamper.test.d.ts +1 -0
- package/dist/test/provenance-stamper.test.js +41 -0
- package/dist/test/provenance-type.test.d.ts +1 -0
- package/dist/test/provenance-type.test.js +15 -0
- package/dist/test/react-client-status.test.d.ts +1 -0
- package/dist/test/react-client-status.test.js +35 -0
- package/dist/test/react-dataclient.test.d.ts +1 -0
- package/dist/test/react-dataclient.test.js +28 -0
- package/dist/test/react-exports.test.d.ts +1 -0
- package/dist/test/react-exports.test.js +45 -0
- package/dist/test/react-sources-option.test.d.ts +1 -0
- package/dist/test/react-sources-option.test.js +41 -0
- package/dist/test/react.test.js +2 -2
- package/dist/test/rpc-dispatcher.test.d.ts +1 -0
- package/dist/test/rpc-dispatcher.test.js +190 -0
- package/dist/test/stats-rpc.test.d.ts +1 -0
- package/dist/test/stats-rpc.test.js +69 -0
- package/dist/test/status-tracker.test.d.ts +1 -0
- package/dist/test/status-tracker.test.js +85 -0
- package/dist/test/users-api.test.d.ts +1 -0
- package/dist/test/users-api.test.js +19 -0
- package/dist/types/audit.d.ts +53 -0
- package/dist/types/audit.js +14 -0
- package/dist/types/common.d.ts +16 -0
- package/dist/types/provenance.d.ts +45 -0
- package/dist/types/provenance.js +2 -0
- package/dist/types/viewer.d.ts +3 -0
- package/dist/viewer/doover-data-provider.d.ts +30 -34
- package/dist/viewer/doover-data-provider.js +82 -316
- package/package.json +1 -1
|
@@ -18,24 +18,14 @@ function useChannelSubscription(identifier, handlers) {
|
|
|
18
18
|
(0, react_1.useEffect)(() => {
|
|
19
19
|
if (!identifier?.agentId || !identifier?.channelName)
|
|
20
20
|
return;
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
handlersRef.current.onAggregate?.(
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
};
|
|
30
|
-
void client.viewer.subscribeToChannel(identifier, messageCallback, aggregateCallback, messageUpdateCallback);
|
|
31
|
-
return () => {
|
|
32
|
-
client.viewer
|
|
33
|
-
.unsubscribeFromChannel(identifier, messageCallback)
|
|
34
|
-
.catch(() => {
|
|
35
|
-
// Already unsubscribed — race between strict-mode double-effect
|
|
36
|
-
// and async subscribe is harmless.
|
|
37
|
-
});
|
|
38
|
-
};
|
|
21
|
+
const channel = { agent_id: identifier.agentId, name: identifier.channelName };
|
|
22
|
+
const off = client.gateway.subscribeToChannel(channel, {
|
|
23
|
+
onMessage: (msg) => handlersRef.current.onMessage?.(msg),
|
|
24
|
+
onMessageUpdate: (msg, rd) => handlersRef.current.onMessageUpdate?.(msg, rd),
|
|
25
|
+
onAggregate: (agg) => handlersRef.current.onAggregate?.(agg),
|
|
26
|
+
});
|
|
27
|
+
void client.gateway.connect();
|
|
28
|
+
return () => off();
|
|
39
29
|
// Identifier fields are the real deps; we intentionally leave `handlers`
|
|
40
30
|
// out because it's read through handlersRef.
|
|
41
31
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { DataClientStatus } from "../client/data-client";
|
|
2
|
+
/**
|
|
3
|
+
* Returns the live `DataClientStatus` for the client in context. Seeds with
|
|
4
|
+
* `client.getStatus()`, subscribes via `client.onStatusChange(...)` for the
|
|
5
|
+
* component's lifetime. Works for a `DooverClient` (single-source) or a
|
|
6
|
+
* `MultiplexClient` (rolled-up status with a `members` breakdown). Prefer this
|
|
7
|
+
* over `useConnectionState` for new code — it carries session, latency, last
|
|
8
|
+
* error and per-source state.
|
|
9
|
+
*/
|
|
10
|
+
export declare function useClientStatus(): DataClientStatus;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.useClientStatus = useClientStatus;
|
|
4
|
+
const react_1 = require("react");
|
|
5
|
+
const context_1 = require("./context");
|
|
6
|
+
/**
|
|
7
|
+
* Returns the live `DataClientStatus` for the client in context. Seeds with
|
|
8
|
+
* `client.getStatus()`, subscribes via `client.onStatusChange(...)` for the
|
|
9
|
+
* component's lifetime. Works for a `DooverClient` (single-source) or a
|
|
10
|
+
* `MultiplexClient` (rolled-up status with a `members` breakdown). Prefer this
|
|
11
|
+
* over `useConnectionState` for new code — it carries session, latency, last
|
|
12
|
+
* error and per-source state.
|
|
13
|
+
*/
|
|
14
|
+
function useClientStatus() {
|
|
15
|
+
const client = (0, context_1.useDooverClient)();
|
|
16
|
+
const [status, setStatus] = (0, react_1.useState)(() => client.getStatus());
|
|
17
|
+
(0, react_1.useEffect)(() => {
|
|
18
|
+
setStatus(client.getStatus());
|
|
19
|
+
return client.onStatusChange(setStatus);
|
|
20
|
+
}, [client]);
|
|
21
|
+
return status;
|
|
22
|
+
}
|
|
@@ -9,6 +9,11 @@ export interface ConnectionState {
|
|
|
9
9
|
lastOpenedAt: number | null;
|
|
10
10
|
}
|
|
11
11
|
/**
|
|
12
|
+
* @deprecated Prefer `useClientStatus()` — it works for both `DooverClient` and
|
|
13
|
+
* `MultiplexClient`, and carries session, latency, last error and per-source
|
|
14
|
+
* state. `useConnectionState` is gateway-flavoured and single-source only;
|
|
15
|
+
* it will remain but new code should not use it.
|
|
16
|
+
*
|
|
12
17
|
* Subscribes to the gateway's connection lifecycle and returns a snapshot
|
|
13
18
|
* suitable for a connection-indicator UI.
|
|
14
19
|
*
|
|
@@ -8,6 +8,11 @@ const INITIAL = {
|
|
|
8
8
|
lastOpenedAt: null,
|
|
9
9
|
};
|
|
10
10
|
/**
|
|
11
|
+
* @deprecated Prefer `useClientStatus()` — it works for both `DooverClient` and
|
|
12
|
+
* `MultiplexClient`, and carries session, latency, last error and per-source
|
|
13
|
+
* state. `useConnectionState` is gateway-flavoured and single-source only;
|
|
14
|
+
* it will remain but new code should not use it.
|
|
15
|
+
*
|
|
11
16
|
* Subscribes to the gateway's connection lifecycle and returns a snapshot
|
|
12
17
|
* suitable for a connection-indicator UI.
|
|
13
18
|
*
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type UseQueryResult } from "@tanstack/react-query";
|
|
2
2
|
import type { Aggregate, JSONValue, AgentAggregate } from "../types/common";
|
|
3
|
-
export declare function multiAgentAggregatesQueryKey(channelName: string, agentIds: string[], fields?: readonly string[]): readonly ["doover", "channel", string, "aggregates", string] | readonly ["doover", "channel", string, "aggregates", string, {
|
|
3
|
+
export declare function multiAgentAggregatesQueryKey(channelName: string, agentIds: string[], fields?: readonly string[], sources?: string[]): readonly ["doover", "channel", string, "aggregates", string, "src", string] | readonly ["doover", "channel", string, "aggregates", string, "src", string, {
|
|
4
4
|
readonly fields: string[];
|
|
5
5
|
}];
|
|
6
6
|
export interface UseMultiAgentAggregatesOptions {
|
|
@@ -14,6 +14,13 @@ export interface UseMultiAgentAggregatesOptions {
|
|
|
14
14
|
* pushes whatever it has, so consumers should still tolerate the full shape.
|
|
15
15
|
*/
|
|
16
16
|
fields?: string[];
|
|
17
|
+
/**
|
|
18
|
+
* Restrict to these source ids on a `MultiplexClient`. Ignored for a plain
|
|
19
|
+
* `DooverClient` or `LocalAgentClient`. When set, the query key is
|
|
20
|
+
* source-dimensioned so data from different source subsets is cached
|
|
21
|
+
* independently.
|
|
22
|
+
*/
|
|
23
|
+
sources?: string[];
|
|
17
24
|
}
|
|
18
25
|
export interface UseMultiAgentAggregatesResult<TData> {
|
|
19
26
|
/** Keyed by `agent_id` for O(1) lookup. */
|
|
@@ -6,13 +6,16 @@ const react_1 = require("react");
|
|
|
6
6
|
const react_query_1 = require("@tanstack/react-query");
|
|
7
7
|
const context_1 = require("./context");
|
|
8
8
|
const useChannelAggregate_1 = require("./useChannelAggregate");
|
|
9
|
-
function multiAgentAggregatesQueryKey(channelName, agentIds, fields) {
|
|
9
|
+
function multiAgentAggregatesQueryKey(channelName, agentIds, fields, sources) {
|
|
10
|
+
const sourceDim = sources && sources.length ? [...sources].sort().join(",") : "*";
|
|
10
11
|
const base = [
|
|
11
12
|
"doover",
|
|
12
13
|
"channel",
|
|
13
14
|
channelName,
|
|
14
15
|
"aggregates",
|
|
15
16
|
[...agentIds].sort().join(","),
|
|
17
|
+
"src",
|
|
18
|
+
sourceDim,
|
|
16
19
|
];
|
|
17
20
|
return fields && fields.length > 0
|
|
18
21
|
? [...base, { fields: [...fields].sort() }]
|
|
@@ -27,24 +30,31 @@ function useMultiAgentAggregates(channelName, agentIds, options) {
|
|
|
27
30
|
const client = (0, context_1.useDooverClient)();
|
|
28
31
|
const queryClient = (0, react_query_1.useQueryClient)();
|
|
29
32
|
const liveUpdates = options?.liveUpdates ?? true;
|
|
33
|
+
const sources = options?.sources;
|
|
30
34
|
const fields = options?.fields;
|
|
31
|
-
const key = multiAgentAggregatesQueryKey(channelName, agentIds, fields);
|
|
35
|
+
const key = multiAgentAggregatesQueryKey(channelName, agentIds, fields, sources);
|
|
32
36
|
const query = (0, react_query_1.useQuery)({
|
|
33
37
|
queryKey: key,
|
|
34
38
|
enabled: agentIds.length > 0,
|
|
35
39
|
staleTime: Infinity,
|
|
36
40
|
queryFn: async () => {
|
|
37
|
-
const
|
|
41
|
+
const params = {
|
|
38
42
|
agent_id: agentIds,
|
|
39
43
|
...(fields && fields.length > 0 ? { field_name: fields } : {}),
|
|
40
|
-
}
|
|
44
|
+
};
|
|
45
|
+
// Pass `{ sources }` as a trailing bag only when set — cast through
|
|
46
|
+
// `never` since the TypeScript overloads don't declare it.
|
|
47
|
+
const sourcesArg = sources ? { sources } : undefined;
|
|
48
|
+
const response = await (sourcesArg
|
|
49
|
+
? client.agents.getMultiAgentAggregates(channelName, params, sourcesArg)
|
|
50
|
+
: client.agents.getMultiAgentAggregates(channelName, params));
|
|
41
51
|
// Seed the per-agent `channelAggregateQueryKey` cache so that
|
|
42
52
|
// sibling `useChannelAggregate(id, channelName)` calls for any
|
|
43
53
|
// of these agents get an instant cache hit rather than issuing
|
|
44
54
|
// a second fetch for data we already have.
|
|
45
55
|
for (const result of response.results) {
|
|
46
56
|
const { agent_id, ...aggregate } = result;
|
|
47
|
-
queryClient.setQueryData((0, useChannelAggregate_1.channelAggregateQueryKey)(agent_id, channelName), aggregate);
|
|
57
|
+
queryClient.setQueryData((0, useChannelAggregate_1.channelAggregateQueryKey)(agent_id, channelName, sources), aggregate);
|
|
48
58
|
}
|
|
49
59
|
return response;
|
|
50
60
|
},
|
|
@@ -65,29 +75,23 @@ function useMultiAgentAggregates(channelName, agentIds, options) {
|
|
|
65
75
|
});
|
|
66
76
|
// Mirror live updates into the per-agent cache so sibling
|
|
67
77
|
// `useChannelAggregate` consumers see them too.
|
|
68
|
-
queryClient.setQueryData((0, useChannelAggregate_1.channelAggregateQueryKey)(agentId, channelName), aggregate);
|
|
78
|
+
queryClient.setQueryData((0, useChannelAggregate_1.channelAggregateQueryKey)(agentId, channelName, sources), aggregate);
|
|
69
79
|
},
|
|
70
80
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
71
|
-
[queryClient, channelName, agentIds.join(",")]);
|
|
81
|
+
[queryClient, channelName, agentIds.join(","), sources?.join(",")]);
|
|
72
82
|
(0, react_1.useEffect)(() => {
|
|
73
83
|
if (!liveUpdates || agentIds.length === 0)
|
|
74
84
|
return;
|
|
75
|
-
const
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
patchAgentAggregate(agentId, aggregate);
|
|
81
|
-
};
|
|
82
|
-
void client.viewer.subscribeToChannel(identifier, messageCallback, aggregateCallback);
|
|
83
|
-
return { identifier, messageCallback };
|
|
85
|
+
const offs = agentIds.map((agentId) => {
|
|
86
|
+
const channel = { agent_id: agentId, name: channelName };
|
|
87
|
+
return client.gateway.subscribeToChannel(channel, {
|
|
88
|
+
onAggregate: (agg) => patchAgentAggregate(agentId, agg),
|
|
89
|
+
});
|
|
84
90
|
});
|
|
91
|
+
void client.gateway.connect();
|
|
85
92
|
return () => {
|
|
86
|
-
for (const
|
|
87
|
-
|
|
88
|
-
.unsubscribeFromChannel(identifier, messageCallback)
|
|
89
|
-
.catch(() => { });
|
|
90
|
-
}
|
|
93
|
+
for (const off of offs)
|
|
94
|
+
off();
|
|
91
95
|
};
|
|
92
96
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
93
97
|
}, [client, channelName, liveUpdates, agentIds.join(",")]);
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { type InfiniteData, type UseInfiniteQueryResult } from "@tanstack/react-query";
|
|
2
2
|
import type { MessageStructure } from "../types/common";
|
|
3
|
-
export declare function multiAgentChannelMessagesQueryKey(channelName: string, agentIds: string[]
|
|
3
|
+
export declare function multiAgentChannelMessagesQueryKey(channelName: string, agentIds: string[], sources?: string[], scope?: {
|
|
4
|
+
after?: string;
|
|
5
|
+
fields?: readonly string[];
|
|
6
|
+
}): readonly ["doover", "channel", string, "messages", string, "src", string] | readonly ["doover", "channel", string, "messages", string, "src", string, Record<string, unknown>];
|
|
4
7
|
export interface UseMultiAgentChannelMessagesOptions {
|
|
5
8
|
limit?: number;
|
|
6
9
|
/** If false, skip live subscriptions per agent. Defaults true. */
|
|
@@ -12,6 +15,21 @@ export interface UseMultiAgentChannelMessagesOptions {
|
|
|
12
15
|
fields?: string[];
|
|
13
16
|
/** Optional first-page `before` cursor (snowflake id). */
|
|
14
17
|
initialBefore?: string;
|
|
18
|
+
/**
|
|
19
|
+
* Optional lower-bound snowflake id. Forwarded server-side so each
|
|
20
|
+
* agent's pagination stops on its own once it walks past the bound —
|
|
21
|
+
* mirrors `useChannelMessages`'s `after`. Use this to fetch a bounded
|
|
22
|
+
* time window (e.g. last 24h) across many agents without filtering
|
|
23
|
+
* on the client.
|
|
24
|
+
*/
|
|
25
|
+
after?: string;
|
|
26
|
+
/**
|
|
27
|
+
* Restrict to these source ids on a `MultiplexClient`. Ignored for a plain
|
|
28
|
+
* `DooverClient` or `LocalAgentClient`. When set, the query key is
|
|
29
|
+
* source-dimensioned so data from different source subsets is cached
|
|
30
|
+
* independently.
|
|
31
|
+
*/
|
|
32
|
+
sources?: string[];
|
|
15
33
|
}
|
|
16
34
|
interface Page<TData> {
|
|
17
35
|
results: MessageStructure<TData>[];
|
|
@@ -5,14 +5,30 @@ exports.useMultiAgentChannelMessages = useMultiAgentChannelMessages;
|
|
|
5
5
|
const react_1 = require("react");
|
|
6
6
|
const react_query_1 = require("@tanstack/react-query");
|
|
7
7
|
const context_1 = require("./context");
|
|
8
|
-
function multiAgentChannelMessagesQueryKey(channelName, agentIds) {
|
|
9
|
-
|
|
8
|
+
function multiAgentChannelMessagesQueryKey(channelName, agentIds, sources, scope) {
|
|
9
|
+
const sourceDim = sources && sources.length ? [...sources].sort().join(",") : "*";
|
|
10
|
+
const base = [
|
|
10
11
|
"doover",
|
|
11
12
|
"channel",
|
|
12
13
|
channelName,
|
|
13
14
|
"messages",
|
|
14
15
|
[...agentIds].sort().join(","),
|
|
16
|
+
"src",
|
|
17
|
+
sourceDim,
|
|
15
18
|
];
|
|
19
|
+
// Pagination cursors come from the first page's response and are scoped
|
|
20
|
+
// to *that* response's `after`/`fields`. Mixing them under one cache
|
|
21
|
+
// entry would cause `getNextPageParam` to anchor on an unrelated page,
|
|
22
|
+
// so a differently-bounded request must live under its own key.
|
|
23
|
+
const scopeKey = {};
|
|
24
|
+
if (scope?.after)
|
|
25
|
+
scopeKey.after = scope.after;
|
|
26
|
+
if (scope?.fields && scope.fields.length > 0) {
|
|
27
|
+
scopeKey.fields = [...scope.fields].sort();
|
|
28
|
+
}
|
|
29
|
+
return Object.keys(scopeKey).length > 0
|
|
30
|
+
? [...base, scopeKey]
|
|
31
|
+
: base;
|
|
16
32
|
}
|
|
17
33
|
function useMultiAgentChannelMessages(channelName, agentIds, options) {
|
|
18
34
|
const client = (0, context_1.useDooverClient)();
|
|
@@ -21,7 +37,12 @@ function useMultiAgentChannelMessages(channelName, agentIds, options) {
|
|
|
21
37
|
const liveUpdates = options?.liveUpdates ?? true;
|
|
22
38
|
const fields = options?.fields;
|
|
23
39
|
const initialBefore = options?.initialBefore;
|
|
24
|
-
const
|
|
40
|
+
const sources = options?.sources;
|
|
41
|
+
const after = options?.after;
|
|
42
|
+
const key = multiAgentChannelMessagesQueryKey(channelName, agentIds, sources, {
|
|
43
|
+
after,
|
|
44
|
+
fields,
|
|
45
|
+
});
|
|
25
46
|
const prependMessage = (0, react_1.useCallback)((message) => {
|
|
26
47
|
queryClient.setQueryData(key, (current) => {
|
|
27
48
|
if (!current)
|
|
@@ -39,25 +60,20 @@ function useMultiAgentChannelMessages(channelName, agentIds, options) {
|
|
|
39
60
|
});
|
|
40
61
|
},
|
|
41
62
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
42
|
-
[queryClient, channelName, agentIds.join(",")]);
|
|
63
|
+
[queryClient, channelName, agentIds.join(","), sources?.join(",")]);
|
|
43
64
|
(0, react_1.useEffect)(() => {
|
|
44
65
|
if (!liveUpdates || agentIds.length === 0)
|
|
45
66
|
return;
|
|
46
|
-
const
|
|
47
|
-
const
|
|
48
|
-
|
|
49
|
-
prependMessage(
|
|
50
|
-
};
|
|
51
|
-
const aggregateCallback = () => { };
|
|
52
|
-
void client.viewer.subscribeToChannel(identifier, messageCallback, aggregateCallback);
|
|
53
|
-
return { identifier, messageCallback };
|
|
67
|
+
const offs = agentIds.map((agentId) => {
|
|
68
|
+
const channel = { agent_id: agentId, name: channelName };
|
|
69
|
+
return client.gateway.subscribeToChannel(channel, {
|
|
70
|
+
onMessage: (msg) => prependMessage(msg),
|
|
71
|
+
});
|
|
54
72
|
});
|
|
73
|
+
void client.gateway.connect();
|
|
55
74
|
return () => {
|
|
56
|
-
for (const
|
|
57
|
-
|
|
58
|
-
.unsubscribeFromChannel(identifier, messageCallback)
|
|
59
|
-
.catch(() => { });
|
|
60
|
-
}
|
|
75
|
+
for (const off of offs)
|
|
76
|
+
off();
|
|
61
77
|
};
|
|
62
78
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
63
79
|
}, [client, channelName, liveUpdates, agentIds.join(",")]);
|
|
@@ -68,12 +84,19 @@ function useMultiAgentChannelMessages(channelName, agentIds, options) {
|
|
|
68
84
|
initialPageParam: initialBefore,
|
|
69
85
|
getNextPageParam: (lastPage) => lastPage?.next,
|
|
70
86
|
queryFn: async ({ pageParam }) => {
|
|
71
|
-
const
|
|
87
|
+
const params = {
|
|
72
88
|
agent_id: agentIds,
|
|
73
89
|
...(typeof pageParam === "string" ? { before: pageParam } : {}),
|
|
74
90
|
...(limit !== undefined ? { limit } : {}),
|
|
75
91
|
...(fields && fields.length > 0 ? { field_name: fields } : {}),
|
|
76
|
-
|
|
92
|
+
...(after !== undefined ? { after } : {}),
|
|
93
|
+
};
|
|
94
|
+
// Pass `{ sources }` as a trailing bag only when set — cast through
|
|
95
|
+
// `never` since the TypeScript overloads don't declare it.
|
|
96
|
+
const sourcesArg = sources ? { sources } : undefined;
|
|
97
|
+
const page = await (sourcesArg
|
|
98
|
+
? client.agents.getMultiAgentMessages(channelName, params, sourcesArg)
|
|
99
|
+
: client.agents.getMultiAgentMessages(channelName, params));
|
|
77
100
|
return page;
|
|
78
101
|
},
|
|
79
102
|
});
|
|
@@ -1,9 +1,16 @@
|
|
|
1
1
|
import { type UseMutationResult } from "@tanstack/react-query";
|
|
2
2
|
import type { MessageStructure } from "../types/common";
|
|
3
3
|
import type { ChannelIdentifier } from "../types/viewer";
|
|
4
|
+
export interface UseSendMessageOptions {
|
|
5
|
+
/**
|
|
6
|
+
* Restrict the write to these source ids on a `MultiplexClient`. Ignored
|
|
7
|
+
* for a plain `DooverClient` or `LocalAgentClient`.
|
|
8
|
+
*/
|
|
9
|
+
sources?: string[];
|
|
10
|
+
}
|
|
4
11
|
/**
|
|
5
12
|
* Mutation wrapping `DooverDataProvider.sendMessage`. Posts a JSON message
|
|
6
13
|
* to the channel and resolves with the created `MessageStructure` (including
|
|
7
14
|
* its server-assigned `id` and `timestamp`).
|
|
8
15
|
*/
|
|
9
|
-
export declare function useSendMessage<TData extends object = object>(identifier: ChannelIdentifier): UseMutationResult<MessageStructure<TData>, Error, TData>;
|
|
16
|
+
export declare function useSendMessage<TData extends object = object>(identifier: ChannelIdentifier, options?: UseSendMessageOptions): UseMutationResult<MessageStructure<TData>, Error, TData>;
|
|
@@ -8,9 +8,22 @@ const context_1 = require("./context");
|
|
|
8
8
|
* to the channel and resolves with the created `MessageStructure` (including
|
|
9
9
|
* its server-assigned `id` and `timestamp`).
|
|
10
10
|
*/
|
|
11
|
-
function useSendMessage(identifier) {
|
|
11
|
+
function useSendMessage(identifier, options) {
|
|
12
12
|
const client = (0, context_1.useDooverClient)();
|
|
13
|
+
const sources = options?.sources;
|
|
13
14
|
return (0, react_query_1.useMutation)({
|
|
14
|
-
mutationFn: (data) =>
|
|
15
|
+
mutationFn: (data) => {
|
|
16
|
+
if (!identifier.agentId || !identifier.channelName) {
|
|
17
|
+
return Promise.reject(new Error("Identifier must include agentId and channelName"));
|
|
18
|
+
}
|
|
19
|
+
const id = { agentId: identifier.agentId, channelName: identifier.channelName };
|
|
20
|
+
const body = { data: data };
|
|
21
|
+
// Pass `{ sources }` as a trailing bag only when set — cast through
|
|
22
|
+
// `never` since the TypeScript overloads don't declare it.
|
|
23
|
+
const sourcesArg = sources ? { sources } : undefined;
|
|
24
|
+
return (sourcesArg
|
|
25
|
+
? client.messages.postMessage(id, body, sourcesArg)
|
|
26
|
+
: client.messages.postMessage(id, body));
|
|
27
|
+
},
|
|
15
28
|
});
|
|
16
29
|
}
|
package/dist/react/useSendRpc.js
CHANGED
|
@@ -115,7 +115,10 @@ function useSendRpc(identifier, options) {
|
|
|
115
115
|
? { app_key: options.app_key }
|
|
116
116
|
: {}),
|
|
117
117
|
};
|
|
118
|
-
|
|
118
|
+
if (!identifier.agentId || !identifier.channelName) {
|
|
119
|
+
throw new Error("Identifier must include agentId and channelName");
|
|
120
|
+
}
|
|
121
|
+
return client.rpc.send({ agentId: identifier.agentId, channelName: identifier.channelName }, rpcRequest, {
|
|
119
122
|
onStatus: (status) => appendStatus(commandId, status),
|
|
120
123
|
});
|
|
121
124
|
},
|
|
@@ -11,6 +11,12 @@ export interface UseUpdateAggregateOptions {
|
|
|
11
11
|
* `suppress_response`. See `AggregateMutationParams`.
|
|
12
12
|
*/
|
|
13
13
|
params?: AggregateMutationParams;
|
|
14
|
+
/**
|
|
15
|
+
* Restrict the write to these source ids on a `MultiplexClient`. Ignored
|
|
16
|
+
* for a plain `DooverClient` or `LocalAgentClient`. Also scopes the
|
|
17
|
+
* react-query cache invalidation to the matching source-dimensioned key.
|
|
18
|
+
*/
|
|
19
|
+
sources?: string[];
|
|
14
20
|
}
|
|
15
21
|
/**
|
|
16
22
|
* Mutation wrapping `DooverDataProvider.updateAggregate` (PATCH) or
|
|
@@ -15,12 +15,28 @@ function useUpdateAggregate(identifier, options) {
|
|
|
15
15
|
const queryClient = (0, react_query_1.useQueryClient)();
|
|
16
16
|
const replace = options?.replace ?? false;
|
|
17
17
|
const params = options?.params;
|
|
18
|
+
const sources = options?.sources;
|
|
18
19
|
return (0, react_query_1.useMutation)({
|
|
19
|
-
mutationFn: (data) =>
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
mutationFn: (data) => {
|
|
21
|
+
if (!identifier.agentId || !identifier.channelName) {
|
|
22
|
+
return Promise.reject(new Error("Identifier must include agentId and channelName"));
|
|
23
|
+
}
|
|
24
|
+
const id = { agentId: identifier.agentId, channelName: identifier.channelName };
|
|
25
|
+
const body = data;
|
|
26
|
+
// Pass `{ sources }` as a trailing bag only when set — cast through
|
|
27
|
+
// `never` since the TypeScript overloads don't declare it.
|
|
28
|
+
const sourcesArg = sources ? { sources } : undefined;
|
|
29
|
+
if (replace) {
|
|
30
|
+
return (sourcesArg
|
|
31
|
+
? client.aggregates.putAggregate(id, body, params, sourcesArg)
|
|
32
|
+
: client.aggregates.putAggregate(id, body, params));
|
|
33
|
+
}
|
|
34
|
+
return (sourcesArg
|
|
35
|
+
? client.aggregates.patchAggregate(id, body, params, sourcesArg)
|
|
36
|
+
: client.aggregates.patchAggregate(id, body, params));
|
|
37
|
+
},
|
|
22
38
|
onSuccess: (aggregate) => {
|
|
23
|
-
queryClient.setQueryData((0, useChannelAggregate_1.channelAggregateQueryKey)(identifier.agentId, identifier.channelName), aggregate);
|
|
39
|
+
queryClient.setQueryData((0, useChannelAggregate_1.channelAggregateQueryKey)(identifier.agentId, identifier.channelName, sources), aggregate);
|
|
24
40
|
},
|
|
25
41
|
});
|
|
26
42
|
}
|
|
@@ -14,6 +14,11 @@ export interface UpdateMessageCallOptions {
|
|
|
14
14
|
params?: MessageMutationParams;
|
|
15
15
|
}
|
|
16
16
|
export interface UseUpdateMessageOptions extends UpdateMessageCallOptions {
|
|
17
|
+
/**
|
|
18
|
+
* Restrict the write to these source ids on a `MultiplexClient`. Ignored
|
|
19
|
+
* for a plain `DooverClient` or `LocalAgentClient`.
|
|
20
|
+
*/
|
|
21
|
+
sources?: string[];
|
|
17
22
|
}
|
|
18
23
|
export interface UpdateMessageVariables<TBody extends MessageBody = MessageBody> {
|
|
19
24
|
messageId: string;
|
|
@@ -13,6 +13,7 @@ function useUpdateMessage(identifier, options) {
|
|
|
13
13
|
const client = (0, context_1.useDooverClient)();
|
|
14
14
|
const defaultReplace = options?.replace ?? false;
|
|
15
15
|
const defaultParams = options?.params;
|
|
16
|
+
const sources = options?.sources;
|
|
16
17
|
return (0, react_query_1.useMutation)({
|
|
17
18
|
mutationFn: ({ messageId, body, options: call, }) => {
|
|
18
19
|
if (!identifier.agentId || !identifier.channelName) {
|
|
@@ -22,8 +23,16 @@ function useUpdateMessage(identifier, options) {
|
|
|
22
23
|
const params = call?.params || defaultParams
|
|
23
24
|
? { ...defaultParams, ...call?.params }
|
|
24
25
|
: undefined;
|
|
25
|
-
|
|
26
|
-
|
|
26
|
+
// Pass `{ sources }` as a trailing bag only when set — cast through
|
|
27
|
+
// `never` since the TypeScript overloads don't declare it.
|
|
28
|
+
const sourcesArg = sources ? { sources } : undefined;
|
|
29
|
+
if (replace) {
|
|
30
|
+
return sourcesArg
|
|
31
|
+
? client.messages.putMessage(identifier.agentId, identifier.channelName, messageId, body, params, sourcesArg)
|
|
32
|
+
: client.messages.putMessage(identifier.agentId, identifier.channelName, messageId, body, params);
|
|
33
|
+
}
|
|
34
|
+
return sourcesArg
|
|
35
|
+
? client.messages.patchMessage(identifier.agentId, identifier.channelName, messageId, body, params, sourcesArg)
|
|
27
36
|
: client.messages.patchMessage(identifier.agentId, identifier.channelName, messageId, body, params);
|
|
28
37
|
},
|
|
29
38
|
});
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { RpcRequest, RpcStatus } from "../types/common";
|
|
2
|
+
export declare class DooverRpcError extends Error {
|
|
3
|
+
readonly status: RpcStatus<unknown> & {
|
|
4
|
+
code: "error";
|
|
5
|
+
};
|
|
6
|
+
readonly request: RpcRequest;
|
|
7
|
+
constructor(status: RpcStatus<unknown> & {
|
|
8
|
+
code: "error";
|
|
9
|
+
}, request: RpcRequest);
|
|
10
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DooverRpcError = void 0;
|
|
4
|
+
class DooverRpcError extends Error {
|
|
5
|
+
constructor(status, request) {
|
|
6
|
+
const text = typeof status.message === "string"
|
|
7
|
+
? status.message
|
|
8
|
+
: (() => {
|
|
9
|
+
try {
|
|
10
|
+
return JSON.stringify(status.message);
|
|
11
|
+
}
|
|
12
|
+
catch {
|
|
13
|
+
return "Unknown RPC error";
|
|
14
|
+
}
|
|
15
|
+
})();
|
|
16
|
+
super(text);
|
|
17
|
+
this.name = "DooverRpcError";
|
|
18
|
+
this.status = status;
|
|
19
|
+
this.request = request;
|
|
20
|
+
Object.setPrototypeOf(this, DooverRpcError.prototype);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
exports.DooverRpcError = DooverRpcError;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { MessagesApi } from "../apis/messages-api";
|
|
2
|
+
import type { DooverStatsCollector } from "../client/stats";
|
|
3
|
+
import type { GatewayClient } from "../gateway/gateway-client";
|
|
4
|
+
import type { RpcRequest, RpcStatus } from "../types/common";
|
|
5
|
+
export interface SendRpcOptions<TPending = undefined> {
|
|
6
|
+
onStatus?: (status: RpcStatus<TPending>) => void;
|
|
7
|
+
signal?: AbortSignal;
|
|
8
|
+
timeoutMs?: number;
|
|
9
|
+
}
|
|
10
|
+
interface ChannelIdentifierLike {
|
|
11
|
+
agentId: string;
|
|
12
|
+
channelName: string;
|
|
13
|
+
}
|
|
14
|
+
export declare class RpcDispatcher {
|
|
15
|
+
private readonly gateway;
|
|
16
|
+
private readonly messages;
|
|
17
|
+
private pending;
|
|
18
|
+
private channelRefs;
|
|
19
|
+
private stats;
|
|
20
|
+
constructor(gateway: GatewayClient, messages: MessagesApi);
|
|
21
|
+
setStats(collector: DooverStatsCollector | null): void;
|
|
22
|
+
send<TReq = object, TRes = object, TPending = undefined>(channel: ChannelIdentifierLike, request: RpcRequest<TReq>, options?: SendRpcOptions<TPending>): Promise<TRes>;
|
|
23
|
+
private acquireChannel;
|
|
24
|
+
private releaseChannel;
|
|
25
|
+
private route;
|
|
26
|
+
private settle;
|
|
27
|
+
}
|
|
28
|
+
export {};
|