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.
Files changed (84) hide show
  1. package/README.md +104 -10
  2. package/dist/apis/connections-api.d.ts +6 -0
  3. package/dist/apis/connections-api.js +8 -0
  4. package/dist/auth/auth-profile.d.ts +45 -0
  5. package/dist/auth/auth-profile.js +72 -0
  6. package/dist/auth/auth-store.d.ts +14 -0
  7. package/dist/auth/auth-store.js +2 -0
  8. package/dist/auth/build-auth.d.ts +37 -0
  9. package/dist/auth/build-auth.js +75 -0
  10. package/dist/auth/cookie-auth.d.ts +17 -0
  11. package/dist/auth/cookie-auth.js +29 -0
  12. package/dist/auth/doover-auth.d.ts +41 -0
  13. package/dist/auth/doover-auth.js +24 -0
  14. package/dist/auth/doover-token-auth.d.ts +35 -0
  15. package/dist/auth/doover-token-auth.js +178 -0
  16. package/dist/auth/errors.d.ts +3 -0
  17. package/dist/auth/errors.js +10 -0
  18. package/dist/auth/jwt.d.ts +6 -0
  19. package/dist/auth/jwt.js +29 -0
  20. package/dist/client/doover-client.d.ts +15 -0
  21. package/dist/client/doover-client.js +45 -4
  22. package/dist/client/singleton.d.ts +26 -0
  23. package/dist/client/singleton.js +59 -0
  24. package/dist/client/stats.d.ts +55 -0
  25. package/dist/client/stats.js +97 -0
  26. package/dist/gateway/gateway-client.d.ts +34 -8
  27. package/dist/gateway/gateway-client.js +147 -52
  28. package/dist/gateway/types.d.ts +0 -1
  29. package/dist/http/rest-client.d.ts +21 -3
  30. package/dist/http/rest-client.js +33 -2
  31. package/dist/index.d.ts +12 -0
  32. package/dist/index.js +23 -5
  33. package/dist/node/config-manager.d.ts +34 -0
  34. package/dist/node/config-manager.js +177 -0
  35. package/dist/node/index.d.ts +1 -0
  36. package/dist/node/index.js +5 -0
  37. package/dist/react/context.d.ts +19 -0
  38. package/dist/react/context.js +28 -0
  39. package/dist/react/index.d.ts +24 -0
  40. package/dist/react/index.js +40 -0
  41. package/dist/react/sharedQueryClient.d.ts +21 -0
  42. package/dist/react/sharedQueryClient.js +39 -0
  43. package/dist/react/useAgentChannel.d.ts +7 -0
  44. package/dist/react/useAgentChannel.js +12 -0
  45. package/dist/react/useAgentConnections.d.ts +8 -0
  46. package/dist/react/useAgentConnections.js +24 -0
  47. package/dist/react/useChannelAggregate.d.ts +13 -0
  48. package/dist/react/useChannelAggregate.js +38 -0
  49. package/dist/react/useChannelMessages.d.ts +23 -0
  50. package/dist/react/useChannelMessages.js +56 -0
  51. package/dist/react/useChannelSubscription.d.ts +16 -0
  52. package/dist/react/useChannelSubscription.js +43 -0
  53. package/dist/react/useConnectionState.d.ts +21 -0
  54. package/dist/react/useConnectionState.js +43 -0
  55. package/dist/react/useMultiAgentAggregates.d.ts +22 -0
  56. package/dist/react/useMultiAgentAggregates.js +82 -0
  57. package/dist/react/useMultiAgentChannelMessages.d.ts +18 -0
  58. package/dist/react/useMultiAgentChannelMessages.js +78 -0
  59. package/dist/react/useSendMessage.d.ts +9 -0
  60. package/dist/react/useSendMessage.js +16 -0
  61. package/dist/react/useSendRpc.d.ts +61 -0
  62. package/dist/react/useSendRpc.js +158 -0
  63. package/dist/react/useTurnCredentials.d.ts +12 -0
  64. package/dist/react/useTurnCredentials.js +25 -0
  65. package/dist/react/useUpdateAggregate.d.ts +14 -0
  66. package/dist/react/useUpdateAggregate.js +25 -0
  67. package/dist/react/useUpdateMessage.d.ts +17 -0
  68. package/dist/react/useUpdateMessage.js +24 -0
  69. package/dist/test/apis.test.js +7 -1
  70. package/dist/test/auth.test.d.ts +1 -0
  71. package/dist/test/auth.test.js +502 -0
  72. package/dist/test/config-manager.test.d.ts +1 -0
  73. package/dist/test/config-manager.test.js +160 -0
  74. package/dist/test/doover-client.test.js +13 -0
  75. package/dist/test/doover-data-provider.test.js +155 -0
  76. package/dist/test/gateway-client.test.js +122 -8
  77. package/dist/test/react.test.d.ts +1 -0
  78. package/dist/test/react.test.js +353 -0
  79. package/dist/test/singleton.test.d.ts +1 -0
  80. package/dist/test/singleton.test.js +62 -0
  81. package/dist/types/common.d.ts +40 -0
  82. package/dist/viewer/doover-data-provider.d.ts +34 -5
  83. package/dist/viewer/doover-data-provider.js +96 -9
  84. package/package.json +33 -2
@@ -11,12 +11,19 @@ const errors_1 = require("../http/errors");
11
11
  const rest_client_1 = require("../http/rest-client");
12
12
  const snowflake_1 = require("../utils/snowflake");
13
13
  const path_parsing_1 = require("./path-parsing");
14
+ function isRpcMessageData(data) {
15
+ return (typeof data === "object" &&
16
+ data !== null &&
17
+ "status" in data &&
18
+ "method" in data &&
19
+ "request" in data);
20
+ }
14
21
  class DooverDataProvider {
15
- constructor(config) {
22
+ constructor(config, auth) {
16
23
  this.config = config;
17
24
  this.subscriptions = new Map();
18
- this.rest = new rest_client_1.RestClient(config);
19
- this.gateway = new gateway_client_1.GatewayClient(config);
25
+ this.rest = new rest_client_1.RestClient(config, auth);
26
+ this.gateway = new gateway_client_1.GatewayClient(config, auth);
20
27
  this.agentsApi = new agents_api_1.AgentsApi(this.rest);
21
28
  this.channelsApi = new channels_api_1.ChannelsApi(this.rest);
22
29
  this.messagesApi = new messages_api_1.MessagesApi(this.rest);
@@ -27,6 +34,11 @@ class DooverDataProvider {
27
34
  const identifier = this.toIdentifier(message.channel.agent_id, message.channel.name);
28
35
  this.subscriptions.get(key)?.forEach((entry) => entry.messageCallback(identifier, message));
29
36
  });
37
+ this.gateway.on("messageUpdate", (message) => {
38
+ const key = this.channelKey(message.channel.agent_id, message.channel.name);
39
+ const identifier = this.toIdentifier(message.channel.agent_id, message.channel.name);
40
+ this.subscriptions.get(key)?.forEach((entry) => entry.messageUpdateCallback?.(identifier, message));
41
+ });
30
42
  this.gateway.on("channelSync", (event) => {
31
43
  const key = this.channelKey(event.channel.agent_id, event.channel.name);
32
44
  const identifier = this.toIdentifier(event.channel.agent_id, event.channel.name);
@@ -78,13 +90,17 @@ class DooverDataProvider {
78
90
  }
79
91
  return this.channelsApi.getChannel(identifier.agentId, identifier.channelName);
80
92
  }
81
- async subscribeToChannel(identifier, callback, aggregateCallback) {
93
+ async subscribeToChannel(identifier, callback, aggregateCallback, messageUpdateCallback) {
82
94
  const validated = this.requireChannel(identifier);
83
95
  const key = this.channelKey(validated.agentId, validated.channelName);
84
96
  const entries = this.subscriptions.get(key) ?? [];
85
- entries.push({ messageCallback: callback, aggregateCallback });
97
+ entries.push({
98
+ messageCallback: callback,
99
+ aggregateCallback,
100
+ messageUpdateCallback,
101
+ });
86
102
  this.subscriptions.set(key, entries);
87
- this.gateway.connect();
103
+ await this.gateway.connect();
88
104
  this.gateway.subscribe({
89
105
  agent_id: validated.agentId,
90
106
  name: validated.channelName,
@@ -121,13 +137,27 @@ class DooverDataProvider {
121
137
  }
122
138
  return this.aggregatesApi.getAggregate(identifier.agentId, identifier.channelName);
123
139
  }
124
- async getMessages(identifier, beforeId) {
140
+ /**
141
+ * Fetch a page of messages for a channel.
142
+ *
143
+ * Accepts either a cursor string (legacy — shorthand for `{ before }`)
144
+ * or a `ListMessagesParams` options object.
145
+ *
146
+ * Default limit is 10 for backwards compatibility; pass `limit` explicitly
147
+ * for larger pages.
148
+ */
149
+ async getMessages(identifier, optionsOrBefore) {
125
150
  if (!identifier.agentId || !identifier.channelName) {
126
151
  return undefined;
127
152
  }
153
+ const options = typeof optionsOrBefore === "string"
154
+ ? { before: optionsOrBefore }
155
+ : (optionsOrBefore ?? {});
128
156
  const messages = await this.messagesApi.listMessages(identifier.agentId, identifier.channelName, {
129
- before: beforeId ?? (0, snowflake_1.generateSnowflakeIdAtTime)(new Date()),
130
- limit: 10,
157
+ before: options.before ?? (0, snowflake_1.generateSnowflakeIdAtTime)(new Date()),
158
+ limit: options.limit ?? 10,
159
+ ...(options.after !== undefined ? { after: options.after } : {}),
160
+ ...(options.field_name !== undefined ? { field_name: options.field_name } : {}),
131
161
  });
132
162
  return messages.reverse();
133
163
  }
@@ -141,6 +171,63 @@ class DooverDataProvider {
141
171
  data: message,
142
172
  });
143
173
  }
174
+ /**
175
+ * Send a message-based RPC to a channel and await a settled response.
176
+ *
177
+ * Wire protocol: POST a message with `{ type: "rpc", ...rpcRequest }`; the
178
+ * server echoes progress via `MessageUpdate` events keyed by the message id,
179
+ * carrying a `status` envelope. Resolves on `success` with the `response`
180
+ * payload, rejects on `error` with the status message.
181
+ *
182
+ * For low-latency ephemeral commands (camera PTZ etc.), use
183
+ * `gateway.sendOneShotMessage` directly instead — that path is not persisted
184
+ * to the database.
185
+ *
186
+ * `onStatus` fires on every intermediate status (`sent`, `acknowledged`,
187
+ * `deferred`, `pending`) and once more on terminal states.
188
+ */
189
+ sendRPC(identifier, rpcRequest, options) {
190
+ return new Promise((resolve, reject) => {
191
+ let rpcMessageId;
192
+ let settled = false;
193
+ const onUpdate = (_identifier, message) => {
194
+ if (settled)
195
+ return;
196
+ if (rpcMessageId === undefined || message.id !== rpcMessageId)
197
+ return;
198
+ if (!isRpcMessageData(message.data))
199
+ return;
200
+ const rpcMessage = message.data;
201
+ options?.onStatus?.(rpcMessage.status);
202
+ if (rpcMessage.status.code === "success") {
203
+ settled = true;
204
+ void this.unsubscribeFromChannel(identifier, noopMessageCallback);
205
+ resolve(rpcMessage.response);
206
+ }
207
+ else if (rpcMessage.status.code === "error") {
208
+ settled = true;
209
+ void this.unsubscribeFromChannel(identifier, noopMessageCallback);
210
+ reject(rpcMessage.status.message);
211
+ }
212
+ };
213
+ // noopMessageCallback is used as the identity handle for this
214
+ // subscription — subscribe/unsubscribe pair matches by reference.
215
+ const noopMessageCallback = () => { };
216
+ const noopAggregateCallback = () => { };
217
+ this.subscribeToChannel(identifier, noopMessageCallback, noopAggregateCallback, onUpdate)
218
+ .then(() => this.sendMessage(identifier, { type: "rpc", ...rpcRequest }))
219
+ .then((message) => {
220
+ rpcMessageId = message.id;
221
+ })
222
+ .catch((error) => {
223
+ if (settled)
224
+ return;
225
+ settled = true;
226
+ void this.unsubscribeFromChannel(identifier, noopMessageCallback);
227
+ reject(error);
228
+ });
229
+ });
230
+ }
144
231
  updateAggregate(identifier, message) {
145
232
  const validated = this.requireChannel(identifier);
146
233
  return this.aggregatesApi.patchAggregate(validated.agentId, validated.channelName, message);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "doover-js",
3
- "version": "0.1.2",
3
+ "version": "0.2.0",
4
4
  "description": "TypeScript client for Doover.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -8,6 +8,14 @@
8
8
  ".": {
9
9
  "types": "./dist/index.d.ts",
10
10
  "default": "./dist/index.js"
11
+ },
12
+ "./node": {
13
+ "types": "./dist/node/index.d.ts",
14
+ "default": "./dist/node/index.js"
15
+ },
16
+ "./react": {
17
+ "types": "./dist/react/index.d.ts",
18
+ "default": "./dist/react/index.js"
11
19
  }
12
20
  },
13
21
  "repository": {
@@ -25,20 +33,43 @@
25
33
  "build": "tsc",
26
34
  "clean": "rm -rf dist",
27
35
  "prepublishOnly": "npm run build",
28
- "test": "mocha --require tsx/cjs \"src/test/**/*.test.ts\""
36
+ "test:node": "mocha --exit --require tsx/cjs \"src/test/**/*.test.ts\"",
37
+ "test:react": "mocha --exit --require global-jsdom/register --require tsx/cjs \"src/test/**/*.test.tsx\"",
38
+ "test": "npm run test:node && npm run test:react"
29
39
  },
30
40
  "keywords": [
31
41
  "doover"
32
42
  ],
33
43
  "license": "Apache-2.0",
44
+ "peerDependencies": {
45
+ "@tanstack/react-query": "^5",
46
+ "react": "^18 || ^19"
47
+ },
48
+ "peerDependenciesMeta": {
49
+ "@tanstack/react-query": {
50
+ "optional": true
51
+ },
52
+ "react": {
53
+ "optional": true
54
+ }
55
+ },
34
56
  "devDependencies": {
57
+ "@tanstack/react-query": "^5.90.2",
58
+ "@testing-library/react": "^16.1.0",
35
59
  "@types/chai": "^5.2.2",
36
60
  "@types/chai-as-promised": "^7.1.8",
37
61
  "@types/mocha": "^10.0.10",
62
+ "@types/node": "^25.6.0",
63
+ "@types/react": "^18.3.17",
64
+ "@types/react-dom": "^18.3.5",
38
65
  "@types/sinon": "^17.0.4",
39
66
  "chai": "^5.2.1",
40
67
  "chai-as-promised": "^8.0.2",
68
+ "global-jsdom": "^25.0.0",
69
+ "jsdom": "^25.0.1",
41
70
  "mocha": "^11.1.0",
71
+ "react": "^18.3.1",
72
+ "react-dom": "^18.3.1",
42
73
  "sinon": "^21.0.0",
43
74
  "tsx": "^4.19.3",
44
75
  "typescript": "^5.8.3"