doover-js 0.1.2 → 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/README.md +104 -10
- package/dist/apis/connections-api.d.ts +6 -0
- package/dist/apis/connections-api.js +8 -0
- package/dist/auth/auth-profile.d.ts +45 -0
- package/dist/auth/auth-profile.js +72 -0
- package/dist/auth/auth-store.d.ts +14 -0
- package/dist/auth/auth-store.js +2 -0
- package/dist/auth/build-auth.d.ts +37 -0
- package/dist/auth/build-auth.js +75 -0
- package/dist/auth/cookie-auth.d.ts +17 -0
- package/dist/auth/cookie-auth.js +29 -0
- package/dist/auth/doover-auth.d.ts +41 -0
- package/dist/auth/doover-auth.js +24 -0
- package/dist/auth/doover-token-auth.d.ts +35 -0
- package/dist/auth/doover-token-auth.js +178 -0
- package/dist/auth/errors.d.ts +3 -0
- package/dist/auth/errors.js +10 -0
- package/dist/auth/jwt.d.ts +6 -0
- package/dist/auth/jwt.js +29 -0
- package/dist/client/doover-client.d.ts +15 -0
- package/dist/client/doover-client.js +45 -4
- 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 +34 -8
- package/dist/gateway/gateway-client.js +147 -52
- package/dist/gateway/types.d.ts +0 -1
- package/dist/http/rest-client.d.ts +21 -3
- package/dist/http/rest-client.js +33 -2
- package/dist/index.d.ts +12 -0
- package/dist/index.js +23 -5
- package/dist/node/config-manager.d.ts +34 -0
- package/dist/node/config-manager.js +177 -0
- package/dist/node/index.d.ts +1 -0
- package/dist/node/index.js +5 -0
- 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/auth.test.d.ts +1 -0
- package/dist/test/auth.test.js +502 -0
- package/dist/test/config-manager.test.d.ts +1 -0
- package/dist/test/config-manager.test.js +160 -0
- 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 +34 -5
- package/dist/viewer/doover-data-provider.js +96 -9
- package/package.json +33 -2
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.ConfigManager = void 0;
|
|
37
|
+
const fs = __importStar(require("fs"));
|
|
38
|
+
const path = __importStar(require("path"));
|
|
39
|
+
const os = __importStar(require("os"));
|
|
40
|
+
const auth_profile_1 = require("../auth/auth-profile");
|
|
41
|
+
const errors_1 = require("../auth/errors");
|
|
42
|
+
/**
|
|
43
|
+
* File-backed profile store compatible with pydoover's config format.
|
|
44
|
+
*
|
|
45
|
+
* Reads `~/.doover/config` (or a custom path) synchronously on construction,
|
|
46
|
+
* matching Python's eager-read model.
|
|
47
|
+
*
|
|
48
|
+
* Behaviour:
|
|
49
|
+
* - Doover2 `[profile=...]` blocks are parsed into `AuthProfile` entries.
|
|
50
|
+
* - Legacy Doover1 `[...]` blocks and unrecognised raw blocks are preserved
|
|
51
|
+
* verbatim in their original order and written back unchanged.
|
|
52
|
+
* - `write()` creates the `~/.doover` directory if missing.
|
|
53
|
+
*/
|
|
54
|
+
class ConfigManager {
|
|
55
|
+
constructor(configPath) {
|
|
56
|
+
this.currentProfile = null;
|
|
57
|
+
this.current = null;
|
|
58
|
+
/** Parsed Doover2 profiles keyed by profile name. */
|
|
59
|
+
this.profiles = new Map();
|
|
60
|
+
/**
|
|
61
|
+
* Ordered list of *blocks* in the file as they were originally read. Each
|
|
62
|
+
* entry is either a `{ type: "managed"; name: string }` reference into
|
|
63
|
+
* `this.profiles`, or a `{ type: "raw"; text: string }` blob that is
|
|
64
|
+
* preserved verbatim.
|
|
65
|
+
*/
|
|
66
|
+
this.blocks = [];
|
|
67
|
+
this.configPath =
|
|
68
|
+
configPath ?? path.join(os.homedir(), ".doover", "config");
|
|
69
|
+
this.read();
|
|
70
|
+
}
|
|
71
|
+
// ------------------------------------------------------------------
|
|
72
|
+
// AuthProfileStore interface
|
|
73
|
+
// ------------------------------------------------------------------
|
|
74
|
+
get(profileName) {
|
|
75
|
+
return this.profiles.get(profileName);
|
|
76
|
+
}
|
|
77
|
+
create(entry) {
|
|
78
|
+
const existing = this.profiles.has(entry.profile);
|
|
79
|
+
this.profiles.set(entry.profile, entry);
|
|
80
|
+
if (!existing) {
|
|
81
|
+
this.blocks.push({ type: "managed", name: entry.profile });
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
delete(profileName) {
|
|
85
|
+
this.profiles.delete(profileName);
|
|
86
|
+
this.blocks = this.blocks.filter((b) => !(b.type === "managed" && b.name === profileName));
|
|
87
|
+
if (this.currentProfile === profileName) {
|
|
88
|
+
this.currentProfile = null;
|
|
89
|
+
this.current = null;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
write() {
|
|
93
|
+
const dir = path.dirname(this.configPath);
|
|
94
|
+
try {
|
|
95
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
96
|
+
}
|
|
97
|
+
catch (err) {
|
|
98
|
+
throw new errors_1.DooverAuthError(`Failed to create config directory '${dir}': ${err instanceof Error ? err.message : String(err)}`);
|
|
99
|
+
}
|
|
100
|
+
const sections = [];
|
|
101
|
+
for (const block of this.blocks) {
|
|
102
|
+
if (block.type === "raw") {
|
|
103
|
+
sections.push(block.text);
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
const profile = this.profiles.get(block.name);
|
|
107
|
+
if (profile) {
|
|
108
|
+
sections.push(profile.dumpBlock());
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
try {
|
|
113
|
+
fs.writeFileSync(this.configPath, sections.join("\n\n") + "\n", "utf-8");
|
|
114
|
+
}
|
|
115
|
+
catch (err) {
|
|
116
|
+
throw new errors_1.DooverAuthError(`Failed to write config file '${this.configPath}': ${err instanceof Error ? err.message : String(err)}`);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
// ------------------------------------------------------------------
|
|
120
|
+
// Internal parsing
|
|
121
|
+
// ------------------------------------------------------------------
|
|
122
|
+
read() {
|
|
123
|
+
let content;
|
|
124
|
+
try {
|
|
125
|
+
content = fs.readFileSync(this.configPath, "utf-8");
|
|
126
|
+
}
|
|
127
|
+
catch {
|
|
128
|
+
// File does not exist — start empty.
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
this.profiles.clear();
|
|
132
|
+
this.blocks = [];
|
|
133
|
+
const lines = content.split(/\r?\n/);
|
|
134
|
+
let i = 0;
|
|
135
|
+
while (i < lines.length) {
|
|
136
|
+
const line = lines[i];
|
|
137
|
+
const profileMatch = line.match(/^\[profile=(.+)\]$/);
|
|
138
|
+
if (profileMatch) {
|
|
139
|
+
// Doover2 managed block
|
|
140
|
+
const profileName = profileMatch[1];
|
|
141
|
+
const bodyLines = [];
|
|
142
|
+
i++;
|
|
143
|
+
while (i < lines.length && !lines[i].startsWith("[")) {
|
|
144
|
+
bodyLines.push(lines[i]);
|
|
145
|
+
i++;
|
|
146
|
+
}
|
|
147
|
+
const profile = auth_profile_1.AuthProfile.parse(profileName, bodyLines);
|
|
148
|
+
this.profiles.set(profileName, profile);
|
|
149
|
+
this.blocks.push({ type: "managed", name: profileName });
|
|
150
|
+
}
|
|
151
|
+
else if (line.match(/^\[.+\]$/) && !line.match(/^\[profile=/)) {
|
|
152
|
+
// Legacy Doover1 block — preserve verbatim.
|
|
153
|
+
const rawLines = [line];
|
|
154
|
+
i++;
|
|
155
|
+
while (i < lines.length && !lines[i].startsWith("[")) {
|
|
156
|
+
rawLines.push(lines[i]);
|
|
157
|
+
i++;
|
|
158
|
+
}
|
|
159
|
+
this.blocks.push({ type: "raw", text: rawLines.join("\n").trimEnd() });
|
|
160
|
+
}
|
|
161
|
+
else {
|
|
162
|
+
// Stray line outside any block — preserve as raw.
|
|
163
|
+
if (line.trim()) {
|
|
164
|
+
this.blocks.push({ type: "raw", text: line });
|
|
165
|
+
}
|
|
166
|
+
i++;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
// Set default current profile to the first managed profile.
|
|
170
|
+
if (this.profiles.size > 0) {
|
|
171
|
+
const firstName = this.profiles.keys().next().value;
|
|
172
|
+
this.currentProfile = firstName;
|
|
173
|
+
this.current = this.profiles.get(firstName);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
exports.ConfigManager = ConfigManager;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { ConfigManager } from "./config-manager";
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ConfigManager = void 0;
|
|
4
|
+
var config_manager_1 = require("./config-manager");
|
|
5
|
+
Object.defineProperty(exports, "ConfigManager", { enumerable: true, get: function () { return config_manager_1.ConfigManager; } });
|
|
@@ -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;
|