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.
Files changed (157) hide show
  1. package/README.md +146 -0
  2. package/dist/apis/_args.d.ts +16 -0
  3. package/dist/apis/_args.js +26 -0
  4. package/dist/apis/agents-api.d.ts +4 -1
  5. package/dist/apis/agents-api.js +139 -1
  6. package/dist/apis/aggregates-api.d.ts +23 -3
  7. package/dist/apis/aggregates-api.js +33 -4
  8. package/dist/apis/alarms-api.d.ts +30 -0
  9. package/dist/apis/alarms-api.js +51 -6
  10. package/dist/apis/channels-api.d.ts +40 -3
  11. package/dist/apis/channels-api.js +50 -5
  12. package/dist/apis/connections-api.d.ts +21 -5
  13. package/dist/apis/connections-api.js +26 -10
  14. package/dist/apis/messages-api.d.ts +54 -9
  15. package/dist/apis/messages-api.js +94 -12
  16. package/dist/apis/notifications-api.d.ts +52 -0
  17. package/dist/apis/notifications-api.js +99 -13
  18. package/dist/apis/permissions-api.d.ts +8 -0
  19. package/dist/apis/permissions-api.js +11 -2
  20. package/dist/apis/processors-api.d.ts +35 -0
  21. package/dist/apis/processors-api.js +64 -8
  22. package/dist/apis/users-api.d.ts +8 -0
  23. package/dist/apis/users-api.js +13 -0
  24. package/dist/client/capabilities.d.ts +10 -0
  25. package/dist/client/capabilities.js +43 -0
  26. package/dist/client/data-client.d.ts +104 -0
  27. package/dist/client/data-client.js +2 -0
  28. package/dist/client/doover-client.d.ts +31 -31
  29. package/dist/client/doover-client.js +58 -37
  30. package/dist/client/errors.d.ts +23 -0
  31. package/dist/client/errors.js +50 -0
  32. package/dist/client/local-agent-client.d.ts +65 -0
  33. package/dist/client/local-agent-client.js +222 -0
  34. package/dist/client/multiplex-client.d.ts +151 -0
  35. package/dist/client/multiplex-client.js +744 -0
  36. package/dist/client/multiplex-gateway.d.ts +49 -0
  37. package/dist/client/multiplex-gateway.js +114 -0
  38. package/dist/client/multiplex-merge.d.ts +12 -0
  39. package/dist/client/multiplex-merge.js +42 -0
  40. package/dist/client/provenance.d.ts +60 -0
  41. package/dist/client/provenance.js +113 -0
  42. package/dist/client/stats.d.ts +24 -0
  43. package/dist/client/stats.js +62 -0
  44. package/dist/client/status-tracker.d.ts +24 -0
  45. package/dist/client/status-tracker.js +90 -0
  46. package/dist/gateway/gateway-client.d.ts +23 -1
  47. package/dist/gateway/gateway-client.js +75 -6
  48. package/dist/http/rest-client.d.ts +5 -0
  49. package/dist/index.d.ts +17 -0
  50. package/dist/index.js +24 -7
  51. package/dist/react/context.d.ts +9 -5
  52. package/dist/react/context.js +6 -2
  53. package/dist/react/index.d.ts +3 -0
  54. package/dist/react/index.js +3 -1
  55. package/dist/react/useChannelAggregate.d.ts +8 -1
  56. package/dist/react/useChannelAggregate.js +24 -6
  57. package/dist/react/useChannelMessage.d.ts +8 -1
  58. package/dist/react/useChannelMessage.js +17 -6
  59. package/dist/react/useChannelMessages.d.ts +8 -1
  60. package/dist/react/useChannelMessages.js +21 -7
  61. package/dist/react/useChannelSubscription.d.ts +6 -0
  62. package/dist/react/useChannelSubscription.js +8 -18
  63. package/dist/react/useClientStatus.d.ts +10 -0
  64. package/dist/react/useClientStatus.js +22 -0
  65. package/dist/react/useConnectionState.d.ts +5 -0
  66. package/dist/react/useConnectionState.js +5 -0
  67. package/dist/react/useMultiAgentAggregates.d.ts +8 -1
  68. package/dist/react/useMultiAgentAggregates.js +25 -21
  69. package/dist/react/useMultiAgentChannelMessages.d.ts +19 -1
  70. package/dist/react/useMultiAgentChannelMessages.js +42 -19
  71. package/dist/react/useSendMessage.d.ts +8 -1
  72. package/dist/react/useSendMessage.js +15 -2
  73. package/dist/react/useSendRpc.js +4 -1
  74. package/dist/react/useUpdateAggregate.d.ts +6 -0
  75. package/dist/react/useUpdateAggregate.js +20 -4
  76. package/dist/react/useUpdateMessage.d.ts +5 -0
  77. package/dist/react/useUpdateMessage.js +11 -2
  78. package/dist/rpc/errors.d.ts +10 -0
  79. package/dist/rpc/errors.js +23 -0
  80. package/dist/rpc/rpc-dispatcher.d.ts +28 -0
  81. package/dist/rpc/rpc-dispatcher.js +129 -0
  82. package/dist/test/api-args.test.d.ts +1 -0
  83. package/dist/test/api-args.test.js +48 -0
  84. package/dist/test/api-overloads.test.d.ts +9 -0
  85. package/dist/test/api-overloads.test.js +276 -0
  86. package/dist/test/capabilities.test.d.ts +1 -0
  87. package/dist/test/capabilities.test.js +27 -0
  88. package/dist/test/data-client-errors.test.d.ts +1 -0
  89. package/dist/test/data-client-errors.test.js +24 -0
  90. package/dist/test/data-client-shape.test.d.ts +1 -0
  91. package/dist/test/data-client-shape.test.js +28 -0
  92. package/dist/test/doover-client-dataclient.test.d.ts +1 -0
  93. package/dist/test/doover-client-dataclient.test.js +58 -0
  94. package/dist/test/doover-client-provenance.test.d.ts +1 -0
  95. package/dist/test/doover-client-provenance.test.js +32 -0
  96. package/dist/test/exports.test.d.ts +1 -0
  97. package/dist/test/exports.test.js +76 -0
  98. package/dist/test/gateway-provenance.test.d.ts +1 -0
  99. package/dist/test/gateway-provenance.test.js +29 -0
  100. package/dist/test/gateway-subscriptions.test.d.ts +1 -0
  101. package/dist/test/gateway-subscriptions.test.js +95 -0
  102. package/dist/test/local-agent-client.test.d.ts +1 -0
  103. package/dist/test/local-agent-client.test.js +202 -0
  104. package/dist/test/multiplex-capabilities.test.d.ts +1 -0
  105. package/dist/test/multiplex-capabilities.test.js +37 -0
  106. package/dist/test/multiplex-conflicts.test.d.ts +1 -0
  107. package/dist/test/multiplex-conflicts.test.js +35 -0
  108. package/dist/test/multiplex-gateway.test.d.ts +1 -0
  109. package/dist/test/multiplex-gateway.test.js +103 -0
  110. package/dist/test/multiplex-merge.test.d.ts +1 -0
  111. package/dist/test/multiplex-merge.test.js +19 -0
  112. package/dist/test/multiplex-noncore.test.d.ts +1 -0
  113. package/dist/test/multiplex-noncore.test.js +82 -0
  114. package/dist/test/multiplex-passthrough.test.d.ts +1 -0
  115. package/dist/test/multiplex-passthrough.test.js +35 -0
  116. package/dist/test/multiplex-reads.test.d.ts +1 -0
  117. package/dist/test/multiplex-reads.test.js +87 -0
  118. package/dist/test/multiplex-registry.test.d.ts +1 -0
  119. package/dist/test/multiplex-registry.test.js +81 -0
  120. package/dist/test/multiplex-routing.test.d.ts +1 -0
  121. package/dist/test/multiplex-routing.test.js +45 -0
  122. package/dist/test/multiplex-status.test.d.ts +1 -0
  123. package/dist/test/multiplex-status.test.js +63 -0
  124. package/dist/test/multiplex-writes.test.d.ts +1 -0
  125. package/dist/test/multiplex-writes.test.js +93 -0
  126. package/dist/test/provenance-regression.test.d.ts +1 -0
  127. package/dist/test/provenance-regression.test.js +45 -0
  128. package/dist/test/provenance-stamper.test.d.ts +1 -0
  129. package/dist/test/provenance-stamper.test.js +41 -0
  130. package/dist/test/provenance-type.test.d.ts +1 -0
  131. package/dist/test/provenance-type.test.js +15 -0
  132. package/dist/test/react-client-status.test.d.ts +1 -0
  133. package/dist/test/react-client-status.test.js +35 -0
  134. package/dist/test/react-dataclient.test.d.ts +1 -0
  135. package/dist/test/react-dataclient.test.js +28 -0
  136. package/dist/test/react-exports.test.d.ts +1 -0
  137. package/dist/test/react-exports.test.js +45 -0
  138. package/dist/test/react-sources-option.test.d.ts +1 -0
  139. package/dist/test/react-sources-option.test.js +41 -0
  140. package/dist/test/react.test.js +2 -2
  141. package/dist/test/rpc-dispatcher.test.d.ts +1 -0
  142. package/dist/test/rpc-dispatcher.test.js +190 -0
  143. package/dist/test/stats-rpc.test.d.ts +1 -0
  144. package/dist/test/stats-rpc.test.js +69 -0
  145. package/dist/test/status-tracker.test.d.ts +1 -0
  146. package/dist/test/status-tracker.test.js +85 -0
  147. package/dist/test/users-api.test.d.ts +1 -0
  148. package/dist/test/users-api.test.js +19 -0
  149. package/dist/types/audit.d.ts +53 -0
  150. package/dist/types/audit.js +14 -0
  151. package/dist/types/common.d.ts +16 -0
  152. package/dist/types/provenance.d.ts +45 -0
  153. package/dist/types/provenance.js +2 -0
  154. package/dist/types/viewer.d.ts +3 -0
  155. package/dist/viewer/doover-data-provider.d.ts +30 -34
  156. package/dist/viewer/doover-data-provider.js +82 -316
  157. 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 messageCallback = (_id, message) => {
22
- handlersRef.current.onMessage?.(message);
23
- };
24
- const aggregateCallback = (_id, aggregate) => {
25
- handlersRef.current.onAggregate?.(aggregate);
26
- };
27
- const messageUpdateCallback = (_id, message, request_data) => {
28
- handlersRef.current.onMessageUpdate?.(message, request_data);
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 response = await client.agents.getMultiAgentAggregates(channelName, {
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 noopMessage = () => { };
76
- const subscriptions = agentIds.map((agentId) => {
77
- const identifier = { agentId, channelName };
78
- const messageCallback = noopMessage;
79
- const aggregateCallback = (_id, aggregate) => {
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 { identifier, messageCallback } of subscriptions) {
87
- client.viewer
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[]): readonly ["doover", "channel", string, "messages", 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
- return [
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 key = multiAgentChannelMessagesQueryKey(channelName, agentIds);
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 subscriptions = agentIds.map((agentId) => {
47
- const identifier = { agentId, channelName };
48
- const messageCallback = (_id, message) => {
49
- prependMessage(message);
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 { identifier, messageCallback } of subscriptions) {
57
- client.viewer
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 page = await client.agents.getMultiAgentMessages(channelName, {
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) => client.viewer.sendMessage(identifier, 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
  }
@@ -115,7 +115,10 @@ function useSendRpc(identifier, options) {
115
115
  ? { app_key: options.app_key }
116
116
  : {}),
117
117
  };
118
- return client.viewer.sendRPC(identifier, rpcRequest, {
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) => (replace
20
- ? client.viewer.putAggregate(identifier, data, params)
21
- : client.viewer.updateAggregate(identifier, data, params)),
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
- return replace
26
- ? client.messages.putMessage(identifier.agentId, identifier.channelName, messageId, body, params)
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 {};