doover-js 0.2.0-alpha.0 → 0.2.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/dist/apis/connections-api.d.ts +6 -0
- package/dist/apis/connections-api.js +8 -0
- package/dist/client/doover-client.d.ts +13 -0
- package/dist/client/doover-client.js +30 -2
- package/dist/client/singleton.d.ts +26 -0
- package/dist/client/singleton.js +59 -0
- package/dist/client/stats.d.ts +55 -0
- package/dist/client/stats.js +97 -0
- package/dist/gateway/gateway-client.d.ts +30 -6
- package/dist/gateway/gateway-client.js +125 -46
- package/dist/gateway/types.d.ts +0 -1
- package/dist/http/rest-client.d.ts +11 -0
- package/dist/http/rest-client.js +17 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +7 -1
- package/dist/react/context.d.ts +19 -0
- package/dist/react/context.js +28 -0
- package/dist/react/index.d.ts +24 -0
- package/dist/react/index.js +40 -0
- package/dist/react/sharedQueryClient.d.ts +21 -0
- package/dist/react/sharedQueryClient.js +39 -0
- package/dist/react/useAgentChannel.d.ts +7 -0
- package/dist/react/useAgentChannel.js +12 -0
- package/dist/react/useAgentConnections.d.ts +8 -0
- package/dist/react/useAgentConnections.js +24 -0
- package/dist/react/useChannelAggregate.d.ts +13 -0
- package/dist/react/useChannelAggregate.js +38 -0
- package/dist/react/useChannelMessages.d.ts +23 -0
- package/dist/react/useChannelMessages.js +56 -0
- package/dist/react/useChannelSubscription.d.ts +16 -0
- package/dist/react/useChannelSubscription.js +43 -0
- package/dist/react/useConnectionState.d.ts +21 -0
- package/dist/react/useConnectionState.js +43 -0
- package/dist/react/useMultiAgentAggregates.d.ts +22 -0
- package/dist/react/useMultiAgentAggregates.js +82 -0
- package/dist/react/useMultiAgentChannelMessages.d.ts +18 -0
- package/dist/react/useMultiAgentChannelMessages.js +78 -0
- package/dist/react/useSendMessage.d.ts +9 -0
- package/dist/react/useSendMessage.js +16 -0
- package/dist/react/useSendRpc.d.ts +61 -0
- package/dist/react/useSendRpc.js +158 -0
- package/dist/react/useTurnCredentials.d.ts +12 -0
- package/dist/react/useTurnCredentials.js +25 -0
- package/dist/react/useUpdateAggregate.d.ts +14 -0
- package/dist/react/useUpdateAggregate.js +25 -0
- package/dist/react/useUpdateMessage.d.ts +17 -0
- package/dist/react/useUpdateMessage.js +24 -0
- package/dist/test/apis.test.js +7 -1
- package/dist/test/doover-client.test.js +13 -0
- package/dist/test/doover-data-provider.test.js +155 -0
- package/dist/test/gateway-client.test.js +122 -8
- package/dist/test/react.test.d.ts +1 -0
- package/dist/test/react.test.js +353 -0
- package/dist/test/singleton.test.d.ts +1 -0
- package/dist/test/singleton.test.js +62 -0
- package/dist/types/common.d.ts +40 -0
- package/dist/viewer/doover-data-provider.d.ts +32 -4
- package/dist/viewer/doover-data-provider.js +92 -5
- package/package.json +28 -2
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { AuthConfig } from "../auth/build-auth";
|
|
2
2
|
import type { DooverAuth } from "../auth/doover-auth";
|
|
3
|
+
import type { DooverStatsCollector } from "../client/stats";
|
|
3
4
|
export interface DooverClientConfig extends AuthConfig {
|
|
4
5
|
dataRestUrl: string;
|
|
5
6
|
controlApiUrl: string;
|
|
@@ -13,6 +14,12 @@ export interface DooverClientConfig extends AuthConfig {
|
|
|
13
14
|
url: string;
|
|
14
15
|
headers?: Record<string, string>;
|
|
15
16
|
}) => WebSocket;
|
|
17
|
+
/**
|
|
18
|
+
* When true, disables the GatewayClient's browser lifecycle listeners
|
|
19
|
+
* (visibilitychange, online). Defaults to false — hooks are on by default
|
|
20
|
+
* in browsers and automatically skipped in non-browser environments.
|
|
21
|
+
*/
|
|
22
|
+
disableBrowserLifecycleHooks?: boolean;
|
|
16
23
|
}
|
|
17
24
|
export interface RequestOptions {
|
|
18
25
|
baseUrl?: string;
|
|
@@ -28,8 +35,12 @@ type QueryParams = Record<string, QueryValue> | object;
|
|
|
28
35
|
export declare class RestClient {
|
|
29
36
|
readonly config: Required<Pick<DooverClientConfig, "dataRestUrl" | "controlApiUrl" | "dataWssUrl" | "sharing" | "impersonateUserStorageKey">> & Pick<DooverClientConfig, "organisationId" | "fetchImpl" | "webSocketImpl" | "webSocketFactory">;
|
|
30
37
|
readonly auth: DooverAuth | null;
|
|
38
|
+
private stats;
|
|
31
39
|
constructor(config: DooverClientConfig, auth?: DooverAuth);
|
|
40
|
+
/** Attach a stats collector. Call with `null` to detach. */
|
|
41
|
+
setStats(stats: DooverStatsCollector | null): void;
|
|
32
42
|
request<T>(options: RequestOptions): Promise<T>;
|
|
43
|
+
private doRequest;
|
|
33
44
|
get<T>(path: string, query?: RequestOptions["query"], baseUrl?: string): Promise<T>;
|
|
34
45
|
post<T>(path: string, body?: RequestOptions["body"], query?: RequestOptions["query"], baseUrl?: string): Promise<T>;
|
|
35
46
|
put<T>(path: string, body?: RequestOptions["body"], query?: RequestOptions["query"], baseUrl?: string): Promise<T>;
|
package/dist/http/rest-client.js
CHANGED
|
@@ -4,6 +4,7 @@ exports.RestClient = void 0;
|
|
|
4
4
|
const errors_1 = require("./errors");
|
|
5
5
|
class RestClient {
|
|
6
6
|
constructor(config, auth) {
|
|
7
|
+
this.stats = null;
|
|
7
8
|
this.config = {
|
|
8
9
|
...config,
|
|
9
10
|
sharing: config.sharing ?? "internal",
|
|
@@ -12,7 +13,23 @@ class RestClient {
|
|
|
12
13
|
};
|
|
13
14
|
this.auth = auth ?? null;
|
|
14
15
|
}
|
|
16
|
+
/** Attach a stats collector. Call with `null` to detach. */
|
|
17
|
+
setStats(stats) {
|
|
18
|
+
this.stats = stats;
|
|
19
|
+
}
|
|
15
20
|
async request(options) {
|
|
21
|
+
const startedAt = this.stats?.recordRestStart() ?? null;
|
|
22
|
+
try {
|
|
23
|
+
const payload = await this.doRequest(options);
|
|
24
|
+
this.stats?.recordRestEnd(startedAt, true);
|
|
25
|
+
return payload;
|
|
26
|
+
}
|
|
27
|
+
catch (err) {
|
|
28
|
+
this.stats?.recordRestEnd(startedAt, false);
|
|
29
|
+
throw err;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
async doRequest(options) {
|
|
16
33
|
if (this.auth) {
|
|
17
34
|
await this.auth.ensureReady();
|
|
18
35
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
export { DooverClient } from "./client/doover-client";
|
|
2
|
+
export { getDooverClient, peekDooverClient, resetDooverClient, } from "./client/singleton";
|
|
3
|
+
export { DooverStatsCollector } from "./client/stats";
|
|
4
|
+
export type { DooverStatsSnapshot, RestStatsSnapshot, GatewayStatsSnapshot, } from "./client/stats";
|
|
2
5
|
export { AgentsApi } from "./apis/agents-api";
|
|
3
6
|
export { AggregatesApi } from "./apis/aggregates-api";
|
|
4
7
|
export { AlarmsApi } from "./apis/alarms-api";
|
package/dist/index.js
CHANGED
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.generateSnowflakeIdAtTime = exports.extractSnowflakeId = exports.addTimestampToMessage = exports.getIdentifierFromPath = exports.DooverDataProvider = exports.DooverValidationError = exports.DooverGatewayError = exports.DooverApiError = exports.RestClient = exports.GatewayClient = exports.buildAuth = exports.DooverAuthError = exports.AuthProfile = exports.DooverTokenAuth = exports.CookieAuth = exports.DooverAuth = exports.TurnApi = exports.ProcessorsApi = exports.PermissionsApi = exports.NotificationsApi = exports.MessagesApi = exports.ConnectionsApi = exports.ChannelsApi = exports.AlarmsApi = exports.AggregatesApi = exports.AgentsApi = exports.DooverClient = void 0;
|
|
3
|
+
exports.generateSnowflakeIdAtTime = exports.extractSnowflakeId = exports.addTimestampToMessage = exports.getIdentifierFromPath = exports.DooverDataProvider = exports.DooverValidationError = exports.DooverGatewayError = exports.DooverApiError = exports.RestClient = exports.GatewayClient = exports.buildAuth = exports.DooverAuthError = exports.AuthProfile = exports.DooverTokenAuth = exports.CookieAuth = exports.DooverAuth = exports.TurnApi = exports.ProcessorsApi = exports.PermissionsApi = exports.NotificationsApi = exports.MessagesApi = exports.ConnectionsApi = exports.ChannelsApi = exports.AlarmsApi = exports.AggregatesApi = exports.AgentsApi = exports.DooverStatsCollector = exports.resetDooverClient = exports.peekDooverClient = exports.getDooverClient = exports.DooverClient = void 0;
|
|
4
4
|
var doover_client_1 = require("./client/doover-client");
|
|
5
5
|
Object.defineProperty(exports, "DooverClient", { enumerable: true, get: function () { return doover_client_1.DooverClient; } });
|
|
6
|
+
var singleton_1 = require("./client/singleton");
|
|
7
|
+
Object.defineProperty(exports, "getDooverClient", { enumerable: true, get: function () { return singleton_1.getDooverClient; } });
|
|
8
|
+
Object.defineProperty(exports, "peekDooverClient", { enumerable: true, get: function () { return singleton_1.peekDooverClient; } });
|
|
9
|
+
Object.defineProperty(exports, "resetDooverClient", { enumerable: true, get: function () { return singleton_1.resetDooverClient; } });
|
|
10
|
+
var stats_1 = require("./client/stats");
|
|
11
|
+
Object.defineProperty(exports, "DooverStatsCollector", { enumerable: true, get: function () { return stats_1.DooverStatsCollector; } });
|
|
6
12
|
var agents_api_1 = require("./apis/agents-api");
|
|
7
13
|
Object.defineProperty(exports, "AgentsApi", { enumerable: true, get: function () { return agents_api_1.AgentsApi; } });
|
|
8
14
|
var aggregates_api_1 = require("./apis/aggregates-api");
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { type ReactNode } from "react";
|
|
2
|
+
import { DooverClient } from "../client/doover-client";
|
|
3
|
+
export interface DooverProviderProps {
|
|
4
|
+
client: DooverClient;
|
|
5
|
+
children: ReactNode;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Wraps a React subtree with a `DooverClient` instance so downstream hooks
|
|
9
|
+
* (`useDooverClient`, `useChannelAggregate`, etc.) can read it from context.
|
|
10
|
+
*
|
|
11
|
+
* Pair with a `@tanstack/react-query` `<QueryClientProvider>` in the same tree —
|
|
12
|
+
* the hooks call `useQueryClient()` internally and will throw without one.
|
|
13
|
+
*/
|
|
14
|
+
export declare function DooverProvider({ client, children }: DooverProviderProps): import("react/jsx-runtime").JSX.Element;
|
|
15
|
+
/**
|
|
16
|
+
* Read the `DooverClient` from context. Throws if called outside a
|
|
17
|
+
* `<DooverProvider>`.
|
|
18
|
+
*/
|
|
19
|
+
export declare function useDooverClient(): DooverClient;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DooverProvider = DooverProvider;
|
|
4
|
+
exports.useDooverClient = useDooverClient;
|
|
5
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
6
|
+
const react_1 = require("react");
|
|
7
|
+
const DooverClientContext = (0, react_1.createContext)(null);
|
|
8
|
+
/**
|
|
9
|
+
* Wraps a React subtree with a `DooverClient` instance so downstream hooks
|
|
10
|
+
* (`useDooverClient`, `useChannelAggregate`, etc.) can read it from context.
|
|
11
|
+
*
|
|
12
|
+
* Pair with a `@tanstack/react-query` `<QueryClientProvider>` in the same tree —
|
|
13
|
+
* the hooks call `useQueryClient()` internally and will throw without one.
|
|
14
|
+
*/
|
|
15
|
+
function DooverProvider({ client, children }) {
|
|
16
|
+
return ((0, jsx_runtime_1.jsx)(DooverClientContext.Provider, { value: client, children: children }));
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Read the `DooverClient` from context. Throws if called outside a
|
|
20
|
+
* `<DooverProvider>`.
|
|
21
|
+
*/
|
|
22
|
+
function useDooverClient() {
|
|
23
|
+
const client = (0, react_1.useContext)(DooverClientContext);
|
|
24
|
+
if (!client) {
|
|
25
|
+
throw new Error("useDooverClient must be called inside a <DooverProvider>.");
|
|
26
|
+
}
|
|
27
|
+
return client;
|
|
28
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export { DooverProvider, useDooverClient } from "./context";
|
|
2
|
+
export type { DooverProviderProps } from "./context";
|
|
3
|
+
export { useConnectionState } from "./useConnectionState";
|
|
4
|
+
export type { ConnectionState, ConnectionStatus } from "./useConnectionState";
|
|
5
|
+
export { useAgentConnections, agentConnectionsQueryKey, } from "./useAgentConnections";
|
|
6
|
+
export { useChannelSubscription, } from "./useChannelSubscription";
|
|
7
|
+
export type { ChannelSubscriptionHandlers } from "./useChannelSubscription";
|
|
8
|
+
export { useChannelAggregate, channelAggregateQueryKey, } from "./useChannelAggregate";
|
|
9
|
+
export { useAgentChannel } from "./useAgentChannel";
|
|
10
|
+
export { useSendMessage } from "./useSendMessage";
|
|
11
|
+
export { useUpdateAggregate } from "./useUpdateAggregate";
|
|
12
|
+
export type { UseUpdateAggregateOptions } from "./useUpdateAggregate";
|
|
13
|
+
export { useUpdateMessage } from "./useUpdateMessage";
|
|
14
|
+
export type { UpdateMessageVariables, UseUpdateMessageOptions, } from "./useUpdateMessage";
|
|
15
|
+
export { useChannelMessages, channelMessagesQueryKey, } from "./useChannelMessages";
|
|
16
|
+
export type { UseChannelMessagesOptions, UseChannelMessagesResult, } from "./useChannelMessages";
|
|
17
|
+
export { useSendRpc } from "./useSendRpc";
|
|
18
|
+
export type { RpcCommandId, RpcStatusEvent, SendRpcVariables, UseSendRpcOptions, UseSendRpcResult, } from "./useSendRpc";
|
|
19
|
+
export { useMultiAgentAggregates, multiAgentAggregatesQueryKey, } from "./useMultiAgentAggregates";
|
|
20
|
+
export type { UseMultiAgentAggregatesOptions, UseMultiAgentAggregatesResult, } from "./useMultiAgentAggregates";
|
|
21
|
+
export { useMultiAgentChannelMessages, multiAgentChannelMessagesQueryKey, } from "./useMultiAgentChannelMessages";
|
|
22
|
+
export type { UseMultiAgentChannelMessagesOptions, UseMultiAgentChannelMessagesResult, } from "./useMultiAgentChannelMessages";
|
|
23
|
+
export { useTurnCredentials } from "./useTurnCredentials";
|
|
24
|
+
export { getSharedQueryClient, resetSharedQueryClient, } from "./sharedQueryClient";
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.resetSharedQueryClient = exports.getSharedQueryClient = exports.useTurnCredentials = exports.multiAgentChannelMessagesQueryKey = exports.useMultiAgentChannelMessages = exports.multiAgentAggregatesQueryKey = exports.useMultiAgentAggregates = exports.useSendRpc = exports.channelMessagesQueryKey = exports.useChannelMessages = exports.useUpdateMessage = exports.useUpdateAggregate = exports.useSendMessage = exports.useAgentChannel = exports.channelAggregateQueryKey = exports.useChannelAggregate = exports.useChannelSubscription = exports.agentConnectionsQueryKey = exports.useAgentConnections = exports.useConnectionState = exports.useDooverClient = exports.DooverProvider = void 0;
|
|
4
|
+
var context_1 = require("./context");
|
|
5
|
+
Object.defineProperty(exports, "DooverProvider", { enumerable: true, get: function () { return context_1.DooverProvider; } });
|
|
6
|
+
Object.defineProperty(exports, "useDooverClient", { enumerable: true, get: function () { return context_1.useDooverClient; } });
|
|
7
|
+
var useConnectionState_1 = require("./useConnectionState");
|
|
8
|
+
Object.defineProperty(exports, "useConnectionState", { enumerable: true, get: function () { return useConnectionState_1.useConnectionState; } });
|
|
9
|
+
var useAgentConnections_1 = require("./useAgentConnections");
|
|
10
|
+
Object.defineProperty(exports, "useAgentConnections", { enumerable: true, get: function () { return useAgentConnections_1.useAgentConnections; } });
|
|
11
|
+
Object.defineProperty(exports, "agentConnectionsQueryKey", { enumerable: true, get: function () { return useAgentConnections_1.agentConnectionsQueryKey; } });
|
|
12
|
+
var useChannelSubscription_1 = require("./useChannelSubscription");
|
|
13
|
+
Object.defineProperty(exports, "useChannelSubscription", { enumerable: true, get: function () { return useChannelSubscription_1.useChannelSubscription; } });
|
|
14
|
+
var useChannelAggregate_1 = require("./useChannelAggregate");
|
|
15
|
+
Object.defineProperty(exports, "useChannelAggregate", { enumerable: true, get: function () { return useChannelAggregate_1.useChannelAggregate; } });
|
|
16
|
+
Object.defineProperty(exports, "channelAggregateQueryKey", { enumerable: true, get: function () { return useChannelAggregate_1.channelAggregateQueryKey; } });
|
|
17
|
+
var useAgentChannel_1 = require("./useAgentChannel");
|
|
18
|
+
Object.defineProperty(exports, "useAgentChannel", { enumerable: true, get: function () { return useAgentChannel_1.useAgentChannel; } });
|
|
19
|
+
var useSendMessage_1 = require("./useSendMessage");
|
|
20
|
+
Object.defineProperty(exports, "useSendMessage", { enumerable: true, get: function () { return useSendMessage_1.useSendMessage; } });
|
|
21
|
+
var useUpdateAggregate_1 = require("./useUpdateAggregate");
|
|
22
|
+
Object.defineProperty(exports, "useUpdateAggregate", { enumerable: true, get: function () { return useUpdateAggregate_1.useUpdateAggregate; } });
|
|
23
|
+
var useUpdateMessage_1 = require("./useUpdateMessage");
|
|
24
|
+
Object.defineProperty(exports, "useUpdateMessage", { enumerable: true, get: function () { return useUpdateMessage_1.useUpdateMessage; } });
|
|
25
|
+
var useChannelMessages_1 = require("./useChannelMessages");
|
|
26
|
+
Object.defineProperty(exports, "useChannelMessages", { enumerable: true, get: function () { return useChannelMessages_1.useChannelMessages; } });
|
|
27
|
+
Object.defineProperty(exports, "channelMessagesQueryKey", { enumerable: true, get: function () { return useChannelMessages_1.channelMessagesQueryKey; } });
|
|
28
|
+
var useSendRpc_1 = require("./useSendRpc");
|
|
29
|
+
Object.defineProperty(exports, "useSendRpc", { enumerable: true, get: function () { return useSendRpc_1.useSendRpc; } });
|
|
30
|
+
var useMultiAgentAggregates_1 = require("./useMultiAgentAggregates");
|
|
31
|
+
Object.defineProperty(exports, "useMultiAgentAggregates", { enumerable: true, get: function () { return useMultiAgentAggregates_1.useMultiAgentAggregates; } });
|
|
32
|
+
Object.defineProperty(exports, "multiAgentAggregatesQueryKey", { enumerable: true, get: function () { return useMultiAgentAggregates_1.multiAgentAggregatesQueryKey; } });
|
|
33
|
+
var useMultiAgentChannelMessages_1 = require("./useMultiAgentChannelMessages");
|
|
34
|
+
Object.defineProperty(exports, "useMultiAgentChannelMessages", { enumerable: true, get: function () { return useMultiAgentChannelMessages_1.useMultiAgentChannelMessages; } });
|
|
35
|
+
Object.defineProperty(exports, "multiAgentChannelMessagesQueryKey", { enumerable: true, get: function () { return useMultiAgentChannelMessages_1.multiAgentChannelMessagesQueryKey; } });
|
|
36
|
+
var useTurnCredentials_1 = require("./useTurnCredentials");
|
|
37
|
+
Object.defineProperty(exports, "useTurnCredentials", { enumerable: true, get: function () { return useTurnCredentials_1.useTurnCredentials; } });
|
|
38
|
+
var sharedQueryClient_1 = require("./sharedQueryClient");
|
|
39
|
+
Object.defineProperty(exports, "getSharedQueryClient", { enumerable: true, get: function () { return sharedQueryClient_1.getSharedQueryClient; } });
|
|
40
|
+
Object.defineProperty(exports, "resetSharedQueryClient", { enumerable: true, get: function () { return sharedQueryClient_1.resetSharedQueryClient; } });
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { QueryClient, type QueryClientConfig } from "@tanstack/react-query";
|
|
2
|
+
/**
|
|
3
|
+
* Get a process-wide `QueryClient` instance, creating it on first call.
|
|
4
|
+
*
|
|
5
|
+
* Uses `globalThis.__doover_js_query_client__` so that every app + remote
|
|
6
|
+
* in a module-federation setup ends up with the same `QueryClient` — and
|
|
7
|
+
* therefore the same cache — even if they each load their own copy of the
|
|
8
|
+
* react-query library. The first caller's `config` wins.
|
|
9
|
+
*
|
|
10
|
+
* Wrap your tree once at the app root:
|
|
11
|
+
*
|
|
12
|
+
* ```tsx
|
|
13
|
+
* import { QueryClientProvider } from "@tanstack/react-query";
|
|
14
|
+
* import { getSharedQueryClient } from "doover-js/react";
|
|
15
|
+
*
|
|
16
|
+
* <QueryClientProvider client={getSharedQueryClient()}>…</QueryClientProvider>
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
export declare function getSharedQueryClient(config?: QueryClientConfig): QueryClient;
|
|
20
|
+
/** Test-only: clear the shared `QueryClient`. */
|
|
21
|
+
export declare function resetSharedQueryClient(): void;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getSharedQueryClient = getSharedQueryClient;
|
|
4
|
+
exports.resetSharedQueryClient = resetSharedQueryClient;
|
|
5
|
+
const react_query_1 = require("@tanstack/react-query");
|
|
6
|
+
const GLOBAL_KEY = "__doover_js_query_client__";
|
|
7
|
+
function globalBag() {
|
|
8
|
+
return globalThis;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Get a process-wide `QueryClient` instance, creating it on first call.
|
|
12
|
+
*
|
|
13
|
+
* Uses `globalThis.__doover_js_query_client__` so that every app + remote
|
|
14
|
+
* in a module-federation setup ends up with the same `QueryClient` — and
|
|
15
|
+
* therefore the same cache — even if they each load their own copy of the
|
|
16
|
+
* react-query library. The first caller's `config` wins.
|
|
17
|
+
*
|
|
18
|
+
* Wrap your tree once at the app root:
|
|
19
|
+
*
|
|
20
|
+
* ```tsx
|
|
21
|
+
* import { QueryClientProvider } from "@tanstack/react-query";
|
|
22
|
+
* import { getSharedQueryClient } from "doover-js/react";
|
|
23
|
+
*
|
|
24
|
+
* <QueryClientProvider client={getSharedQueryClient()}>…</QueryClientProvider>
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
function getSharedQueryClient(config) {
|
|
28
|
+
const bag = globalBag();
|
|
29
|
+
const existing = bag[GLOBAL_KEY];
|
|
30
|
+
if (existing)
|
|
31
|
+
return existing;
|
|
32
|
+
const client = new react_query_1.QueryClient(config);
|
|
33
|
+
bag[GLOBAL_KEY] = client;
|
|
34
|
+
return client;
|
|
35
|
+
}
|
|
36
|
+
/** Test-only: clear the shared `QueryClient`. */
|
|
37
|
+
function resetSharedQueryClient() {
|
|
38
|
+
delete globalBag()[GLOBAL_KEY];
|
|
39
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Aggregate } from "../types/common";
|
|
2
|
+
/**
|
|
3
|
+
* Convenience wrapper around `useChannelAggregate` that accepts an agent id
|
|
4
|
+
* and channel name as separate arguments — the most common shape at call
|
|
5
|
+
* sites where the identifier isn't already a `ChannelIdentifier`.
|
|
6
|
+
*/
|
|
7
|
+
export declare function useAgentChannel<TData extends Aggregate["data"] = Aggregate["data"]>(agentId: string | undefined, channelName: string): import("@tanstack/react-query").UseQueryResult<Aggregate<TData> | undefined>;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.useAgentChannel = useAgentChannel;
|
|
4
|
+
const useChannelAggregate_1 = require("./useChannelAggregate");
|
|
5
|
+
/**
|
|
6
|
+
* Convenience wrapper around `useChannelAggregate` that accepts an agent id
|
|
7
|
+
* and channel name as separate arguments — the most common shape at call
|
|
8
|
+
* sites where the identifier isn't already a `ChannelIdentifier`.
|
|
9
|
+
*/
|
|
10
|
+
function useAgentChannel(agentId, channelName) {
|
|
11
|
+
return (0, useChannelAggregate_1.useChannelAggregate)({ agentId, channelName });
|
|
12
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type UseQueryResult } from "@tanstack/react-query";
|
|
2
|
+
import type { ConnectionDetails } from "../types/common";
|
|
3
|
+
export declare function agentConnectionsQueryKey(agentId: string | undefined): readonly ["doover", "agent", string | undefined, "connections"];
|
|
4
|
+
/**
|
|
5
|
+
* Fetch the list of active WSS connections for an agent. Query key:
|
|
6
|
+
* `["doover", "agent", agentId, "connections"]`.
|
|
7
|
+
*/
|
|
8
|
+
export declare function useAgentConnections(agentId: string | undefined): UseQueryResult<ConnectionDetails[]>;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.agentConnectionsQueryKey = agentConnectionsQueryKey;
|
|
4
|
+
exports.useAgentConnections = useAgentConnections;
|
|
5
|
+
const react_query_1 = require("@tanstack/react-query");
|
|
6
|
+
const context_1 = require("./context");
|
|
7
|
+
function agentConnectionsQueryKey(agentId) {
|
|
8
|
+
return ["doover", "agent", agentId, "connections"];
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Fetch the list of active WSS connections for an agent. Query key:
|
|
12
|
+
* `["doover", "agent", agentId, "connections"]`.
|
|
13
|
+
*/
|
|
14
|
+
function useAgentConnections(agentId) {
|
|
15
|
+
const client = (0, context_1.useDooverClient)();
|
|
16
|
+
return (0, react_query_1.useQuery)({
|
|
17
|
+
queryKey: agentConnectionsQueryKey(agentId),
|
|
18
|
+
enabled: !!agentId,
|
|
19
|
+
staleTime: 30000,
|
|
20
|
+
queryFn: () => agentId
|
|
21
|
+
? client.connections.getAgentConnections(agentId)
|
|
22
|
+
: Promise.resolve([]),
|
|
23
|
+
});
|
|
24
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type UseQueryResult } from "@tanstack/react-query";
|
|
2
|
+
import type { Aggregate } from "../types/common";
|
|
3
|
+
import type { ChannelIdentifier } from "../types/viewer";
|
|
4
|
+
export declare function channelAggregateQueryKey(agentId: string | undefined, channelName: string | undefined): readonly ["doover", "agent", string | undefined, "channel", string | undefined];
|
|
5
|
+
/**
|
|
6
|
+
* Fetch a channel's aggregate and keep it live via the gateway. Incoming
|
|
7
|
+
* `channelSync` and `aggregateUpdate` events patch the react-query cache
|
|
8
|
+
* under `channelAggregateQueryKey(agentId, channelName)`.
|
|
9
|
+
*
|
|
10
|
+
* Typed on `TData` — the shape of `aggregate.data`. Defaults to the generic
|
|
11
|
+
* `Record<string, JSONValue>` used by `Aggregate`.
|
|
12
|
+
*/
|
|
13
|
+
export declare function useChannelAggregate<TData extends Aggregate["data"] = Aggregate["data"]>(identifier: ChannelIdentifier): UseQueryResult<Aggregate<TData> | undefined>;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.channelAggregateQueryKey = channelAggregateQueryKey;
|
|
4
|
+
exports.useChannelAggregate = useChannelAggregate;
|
|
5
|
+
const react_1 = require("react");
|
|
6
|
+
const react_query_1 = require("@tanstack/react-query");
|
|
7
|
+
const context_1 = require("./context");
|
|
8
|
+
const useChannelSubscription_1 = require("./useChannelSubscription");
|
|
9
|
+
function channelAggregateQueryKey(agentId, channelName) {
|
|
10
|
+
return ["doover", "agent", agentId, "channel", channelName];
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Fetch a channel's aggregate and keep it live via the gateway. Incoming
|
|
14
|
+
* `channelSync` and `aggregateUpdate` events patch the react-query cache
|
|
15
|
+
* under `channelAggregateQueryKey(agentId, channelName)`.
|
|
16
|
+
*
|
|
17
|
+
* Typed on `TData` — the shape of `aggregate.data`. Defaults to the generic
|
|
18
|
+
* `Record<string, JSONValue>` used by `Aggregate`.
|
|
19
|
+
*/
|
|
20
|
+
function useChannelAggregate(identifier) {
|
|
21
|
+
const client = (0, context_1.useDooverClient)();
|
|
22
|
+
const queryClient = (0, react_query_1.useQueryClient)();
|
|
23
|
+
const { agentId, channelName } = identifier;
|
|
24
|
+
const key = channelAggregateQueryKey(agentId, channelName);
|
|
25
|
+
const onAggregate = (0, react_1.useCallback)((aggregate) => {
|
|
26
|
+
queryClient.setQueryData(key, aggregate);
|
|
27
|
+
},
|
|
28
|
+
// The key array is structurally stable per (agentId, channelName).
|
|
29
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
30
|
+
[queryClient, agentId, channelName]);
|
|
31
|
+
(0, useChannelSubscription_1.useChannelSubscription)(identifier, { onAggregate });
|
|
32
|
+
return (0, react_query_1.useQuery)({
|
|
33
|
+
queryKey: key,
|
|
34
|
+
enabled: !!agentId && !!channelName,
|
|
35
|
+
staleTime: Infinity,
|
|
36
|
+
queryFn: () => client.viewer.getAggregate(identifier),
|
|
37
|
+
});
|
|
38
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { type InfiniteData, type UseInfiniteQueryResult } from "@tanstack/react-query";
|
|
2
|
+
import type { MessageStructure } from "../types/common";
|
|
3
|
+
import type { ChannelIdentifier } from "../types/viewer";
|
|
4
|
+
export declare function channelMessagesQueryKey(agentId: string | undefined, channelName: string | undefined): readonly ["doover", "agent", string | undefined, "channel", string | undefined, "messages"];
|
|
5
|
+
export interface UseChannelMessagesOptions {
|
|
6
|
+
limit?: number;
|
|
7
|
+
/** If false, skip subscribing for live message-create updates. Defaults true. */
|
|
8
|
+
liveUpdates?: boolean;
|
|
9
|
+
}
|
|
10
|
+
type Page<TData> = MessageStructure<TData>[];
|
|
11
|
+
export interface UseChannelMessagesResult<TData> extends Omit<UseInfiniteQueryResult<InfiniteData<Page<TData>>>, "data"> {
|
|
12
|
+
/** Flat chronological list across all loaded pages. */
|
|
13
|
+
messages: MessageStructure<TData>[];
|
|
14
|
+
/** Raw react-query data (pages). */
|
|
15
|
+
data: InfiniteData<Page<TData>> | undefined;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Paginated infinite query over `DooverDataProvider.getMessages`, with live
|
|
19
|
+
* `messageCreate` pushes prepended/appended to the newest page. The "next"
|
|
20
|
+
* page fetches older messages (cursor = oldest-loaded message id).
|
|
21
|
+
*/
|
|
22
|
+
export declare function useChannelMessages<TData = unknown>(identifier: ChannelIdentifier, options?: UseChannelMessagesOptions): UseChannelMessagesResult<TData>;
|
|
23
|
+
export {};
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.channelMessagesQueryKey = channelMessagesQueryKey;
|
|
4
|
+
exports.useChannelMessages = useChannelMessages;
|
|
5
|
+
const react_1 = require("react");
|
|
6
|
+
const react_query_1 = require("@tanstack/react-query");
|
|
7
|
+
const context_1 = require("./context");
|
|
8
|
+
const useChannelSubscription_1 = require("./useChannelSubscription");
|
|
9
|
+
function channelMessagesQueryKey(agentId, channelName) {
|
|
10
|
+
return ["doover", "agent", agentId, "channel", channelName, "messages"];
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Paginated infinite query over `DooverDataProvider.getMessages`, with live
|
|
14
|
+
* `messageCreate` pushes prepended/appended to the newest page. The "next"
|
|
15
|
+
* page fetches older messages (cursor = oldest-loaded message id).
|
|
16
|
+
*/
|
|
17
|
+
function useChannelMessages(identifier, options) {
|
|
18
|
+
const client = (0, context_1.useDooverClient)();
|
|
19
|
+
const queryClient = (0, react_query_1.useQueryClient)();
|
|
20
|
+
const { agentId, channelName } = identifier;
|
|
21
|
+
const limit = options?.limit;
|
|
22
|
+
const liveUpdates = options?.liveUpdates ?? true;
|
|
23
|
+
const key = channelMessagesQueryKey(agentId, channelName);
|
|
24
|
+
const onMessage = (0, react_1.useCallback)((message) => {
|
|
25
|
+
queryClient.setQueryData(key, (current) => {
|
|
26
|
+
if (!current)
|
|
27
|
+
return current;
|
|
28
|
+
const typed = message;
|
|
29
|
+
// Newest page is index 0 — prepend there.
|
|
30
|
+
const [firstPage, ...rest] = current.pages;
|
|
31
|
+
return {
|
|
32
|
+
...current,
|
|
33
|
+
pages: [[...(firstPage ?? []), typed], ...rest],
|
|
34
|
+
};
|
|
35
|
+
});
|
|
36
|
+
},
|
|
37
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
38
|
+
[queryClient, agentId, channelName]);
|
|
39
|
+
(0, useChannelSubscription_1.useChannelSubscription)(liveUpdates ? identifier : undefined, { onMessage });
|
|
40
|
+
const query = (0, react_query_1.useInfiniteQuery)({
|
|
41
|
+
queryKey: key,
|
|
42
|
+
enabled: !!agentId && !!channelName,
|
|
43
|
+
staleTime: Infinity,
|
|
44
|
+
initialPageParam: undefined,
|
|
45
|
+
getNextPageParam: (lastPage) => lastPage && lastPage.length > 0 ? lastPage[0]?.id : undefined,
|
|
46
|
+
queryFn: async ({ pageParam }) => {
|
|
47
|
+
const page = await client.viewer.getMessages(identifier, {
|
|
48
|
+
...(typeof pageParam === "string" ? { before: pageParam } : {}),
|
|
49
|
+
...(limit !== undefined ? { limit } : {}),
|
|
50
|
+
});
|
|
51
|
+
return (page ?? []);
|
|
52
|
+
},
|
|
53
|
+
});
|
|
54
|
+
const messages = (0, react_1.useMemo)(() => (query.data?.pages ?? []).flat(), [query.data]);
|
|
55
|
+
return { ...query, messages };
|
|
56
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { Aggregate, MessageStructure } from "../types/common";
|
|
2
|
+
import type { ChannelIdentifier } from "../types/viewer";
|
|
3
|
+
export interface ChannelSubscriptionHandlers {
|
|
4
|
+
onMessage?: (message: MessageStructure) => void;
|
|
5
|
+
onMessageUpdate?: (message: MessageStructure) => void;
|
|
6
|
+
onAggregate?: (aggregate: Aggregate) => void;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Attach live-update handlers to a channel for the lifetime of the host
|
|
10
|
+
* component. Wraps `DooverDataProvider.subscribeToChannel` with ref-counted
|
|
11
|
+
* subscribe/unsubscribe and handler refs so changes to the handler closures
|
|
12
|
+
* don't re-trigger the underlying subscribe.
|
|
13
|
+
*
|
|
14
|
+
* Passing `undefined` (or an identifier without both fields) is a no-op.
|
|
15
|
+
*/
|
|
16
|
+
export declare function useChannelSubscription(identifier: ChannelIdentifier | undefined, handlers: ChannelSubscriptionHandlers): void;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.useChannelSubscription = useChannelSubscription;
|
|
4
|
+
const react_1 = require("react");
|
|
5
|
+
const context_1 = require("./context");
|
|
6
|
+
/**
|
|
7
|
+
* Attach live-update handlers to a channel for the lifetime of the host
|
|
8
|
+
* component. Wraps `DooverDataProvider.subscribeToChannel` with ref-counted
|
|
9
|
+
* subscribe/unsubscribe and handler refs so changes to the handler closures
|
|
10
|
+
* don't re-trigger the underlying subscribe.
|
|
11
|
+
*
|
|
12
|
+
* Passing `undefined` (or an identifier without both fields) is a no-op.
|
|
13
|
+
*/
|
|
14
|
+
function useChannelSubscription(identifier, handlers) {
|
|
15
|
+
const client = (0, context_1.useDooverClient)();
|
|
16
|
+
const handlersRef = (0, react_1.useRef)(handlers);
|
|
17
|
+
handlersRef.current = handlers;
|
|
18
|
+
(0, react_1.useEffect)(() => {
|
|
19
|
+
if (!identifier?.agentId || !identifier?.channelName)
|
|
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) => {
|
|
28
|
+
handlersRef.current.onMessageUpdate?.(message);
|
|
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
|
+
};
|
|
39
|
+
// Identifier fields are the real deps; we intentionally leave `handlers`
|
|
40
|
+
// out because it's read through handlersRef.
|
|
41
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
42
|
+
}, [client, identifier?.agentId, identifier?.channelName]);
|
|
43
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export type ConnectionStatus = "connecting" | "open";
|
|
2
|
+
export interface ConnectionState {
|
|
3
|
+
/**
|
|
4
|
+
* `"open"` once the gateway has received a `Ready` frame;
|
|
5
|
+
* `"connecting"` any time the socket is reconnecting or awaiting Ready.
|
|
6
|
+
*/
|
|
7
|
+
status: ConnectionStatus;
|
|
8
|
+
/** Timestamp of the most recent `Ready` frame, or `null` until the first one. */
|
|
9
|
+
lastOpenedAt: number | null;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Subscribes to the gateway's connection lifecycle and returns a snapshot
|
|
13
|
+
* suitable for a connection-indicator UI.
|
|
14
|
+
*
|
|
15
|
+
* The gateway doesn't measure application-level latency — liveness is
|
|
16
|
+
* handled at the WebSocket-protocol layer via ping/pong frames (opcodes
|
|
17
|
+
* 0x9 / 0xA) which browsers answer automatically but don't expose to JS.
|
|
18
|
+
* Dead connections surface as the `close` event, which we already handle
|
|
19
|
+
* via auto-reconnect.
|
|
20
|
+
*/
|
|
21
|
+
export declare function useConnectionState(): ConnectionState;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.useConnectionState = useConnectionState;
|
|
4
|
+
const react_1 = require("react");
|
|
5
|
+
const context_1 = require("./context");
|
|
6
|
+
const INITIAL = {
|
|
7
|
+
status: "connecting",
|
|
8
|
+
lastOpenedAt: null,
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* Subscribes to the gateway's connection lifecycle and returns a snapshot
|
|
12
|
+
* suitable for a connection-indicator UI.
|
|
13
|
+
*
|
|
14
|
+
* The gateway doesn't measure application-level latency — liveness is
|
|
15
|
+
* handled at the WebSocket-protocol layer via ping/pong frames (opcodes
|
|
16
|
+
* 0x9 / 0xA) which browsers answer automatically but don't expose to JS.
|
|
17
|
+
* Dead connections surface as the `close` event, which we already handle
|
|
18
|
+
* via auto-reconnect.
|
|
19
|
+
*/
|
|
20
|
+
function useConnectionState() {
|
|
21
|
+
const client = (0, context_1.useDooverClient)();
|
|
22
|
+
const [state, setState] = (0, react_1.useState)(() => client.gateway.isConnected() && client.gateway.getSession()
|
|
23
|
+
? {
|
|
24
|
+
status: "open",
|
|
25
|
+
lastOpenedAt: Date.now(),
|
|
26
|
+
}
|
|
27
|
+
: INITIAL);
|
|
28
|
+
(0, react_1.useEffect)(() => {
|
|
29
|
+
const onReady = () => setState((prev) => ({
|
|
30
|
+
...prev,
|
|
31
|
+
status: "open",
|
|
32
|
+
lastOpenedAt: Date.now(),
|
|
33
|
+
}));
|
|
34
|
+
const onClose = () => setState((prev) => ({ ...prev, status: "connecting" }));
|
|
35
|
+
client.gateway.on("ready", onReady);
|
|
36
|
+
client.gateway.on("close", onClose);
|
|
37
|
+
return () => {
|
|
38
|
+
client.gateway.off("ready", onReady);
|
|
39
|
+
client.gateway.off("close", onClose);
|
|
40
|
+
};
|
|
41
|
+
}, [client]);
|
|
42
|
+
return state;
|
|
43
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { type UseQueryResult } from "@tanstack/react-query";
|
|
2
|
+
import type { Aggregate } from "../types/common";
|
|
3
|
+
import type { AgentAggregate } from "../types/openapi";
|
|
4
|
+
export declare function multiAgentAggregatesQueryKey(channelName: string, agentIds: string[]): readonly ["doover", "channel", string, "aggregates", string];
|
|
5
|
+
export interface UseMultiAgentAggregatesOptions {
|
|
6
|
+
/** If false, skip per-agent live subscriptions. Defaults true. */
|
|
7
|
+
liveUpdates?: boolean;
|
|
8
|
+
}
|
|
9
|
+
export interface UseMultiAgentAggregatesResult {
|
|
10
|
+
/** Keyed by `agent_id` for O(1) lookup. */
|
|
11
|
+
aggregatesByAgent: Record<string, Aggregate>;
|
|
12
|
+
query: UseQueryResult<{
|
|
13
|
+
results: AgentAggregate[];
|
|
14
|
+
count: number;
|
|
15
|
+
}>;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Batch-fetch aggregates for a channel across many agents, then keep each
|
|
19
|
+
* agent's slot live via its own gateway subscription. Reconciles per-agent
|
|
20
|
+
* `channelSync`/`aggregateUpdate` events into the batched query cache.
|
|
21
|
+
*/
|
|
22
|
+
export declare function useMultiAgentAggregates(channelName: string, agentIds: string[], options?: UseMultiAgentAggregatesOptions): UseMultiAgentAggregatesResult;
|