doover-js 0.5.4 → 0.6.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 (155) hide show
  1. package/README.md +146 -0
  2. package/dist/apis/_args.d.ts +16 -0
  3. package/dist/apis/_args.js +26 -0
  4. package/dist/apis/agents-api.d.ts +4 -1
  5. package/dist/apis/agents-api.js +139 -1
  6. package/dist/apis/aggregates-api.d.ts +23 -3
  7. package/dist/apis/aggregates-api.js +33 -4
  8. package/dist/apis/alarms-api.d.ts +30 -0
  9. package/dist/apis/alarms-api.js +51 -6
  10. package/dist/apis/channels-api.d.ts +40 -3
  11. package/dist/apis/channels-api.js +50 -5
  12. package/dist/apis/connections-api.d.ts +21 -5
  13. package/dist/apis/connections-api.js +26 -10
  14. package/dist/apis/messages-api.d.ts +54 -9
  15. package/dist/apis/messages-api.js +94 -12
  16. package/dist/apis/notifications-api.d.ts +52 -0
  17. package/dist/apis/notifications-api.js +99 -13
  18. package/dist/apis/permissions-api.d.ts +8 -0
  19. package/dist/apis/permissions-api.js +11 -2
  20. package/dist/apis/processors-api.d.ts +35 -0
  21. package/dist/apis/processors-api.js +64 -8
  22. package/dist/apis/users-api.d.ts +8 -0
  23. package/dist/apis/users-api.js +13 -0
  24. package/dist/client/capabilities.d.ts +10 -0
  25. package/dist/client/capabilities.js +43 -0
  26. package/dist/client/data-client.d.ts +104 -0
  27. package/dist/client/data-client.js +2 -0
  28. package/dist/client/doover-client.d.ts +31 -31
  29. package/dist/client/doover-client.js +58 -37
  30. package/dist/client/errors.d.ts +23 -0
  31. package/dist/client/errors.js +50 -0
  32. package/dist/client/local-agent-client.d.ts +65 -0
  33. package/dist/client/local-agent-client.js +222 -0
  34. package/dist/client/multiplex-client.d.ts +151 -0
  35. package/dist/client/multiplex-client.js +744 -0
  36. package/dist/client/multiplex-gateway.d.ts +49 -0
  37. package/dist/client/multiplex-gateway.js +114 -0
  38. package/dist/client/multiplex-merge.d.ts +12 -0
  39. package/dist/client/multiplex-merge.js +42 -0
  40. package/dist/client/provenance.d.ts +60 -0
  41. package/dist/client/provenance.js +113 -0
  42. package/dist/client/stats.d.ts +24 -0
  43. package/dist/client/stats.js +62 -0
  44. package/dist/client/status-tracker.d.ts +24 -0
  45. package/dist/client/status-tracker.js +90 -0
  46. package/dist/gateway/gateway-client.d.ts +23 -1
  47. package/dist/gateway/gateway-client.js +75 -6
  48. package/dist/http/rest-client.d.ts +5 -0
  49. package/dist/index.d.ts +15 -0
  50. package/dist/index.js +22 -7
  51. package/dist/react/context.d.ts +9 -5
  52. package/dist/react/context.js +6 -2
  53. package/dist/react/index.d.ts +3 -0
  54. package/dist/react/index.js +3 -1
  55. package/dist/react/useChannelAggregate.d.ts +8 -1
  56. package/dist/react/useChannelAggregate.js +24 -6
  57. package/dist/react/useChannelMessage.d.ts +8 -1
  58. package/dist/react/useChannelMessage.js +17 -6
  59. package/dist/react/useChannelMessages.d.ts +8 -1
  60. package/dist/react/useChannelMessages.js +21 -7
  61. package/dist/react/useChannelSubscription.d.ts +6 -0
  62. package/dist/react/useChannelSubscription.js +8 -18
  63. package/dist/react/useClientStatus.d.ts +10 -0
  64. package/dist/react/useClientStatus.js +22 -0
  65. package/dist/react/useConnectionState.d.ts +5 -0
  66. package/dist/react/useConnectionState.js +5 -0
  67. package/dist/react/useMultiAgentAggregates.d.ts +8 -1
  68. package/dist/react/useMultiAgentAggregates.js +25 -21
  69. package/dist/react/useMultiAgentChannelMessages.d.ts +8 -1
  70. package/dist/react/useMultiAgentChannelMessages.js +23 -18
  71. package/dist/react/useSendMessage.d.ts +8 -1
  72. package/dist/react/useSendMessage.js +15 -2
  73. package/dist/react/useSendRpc.js +4 -1
  74. package/dist/react/useUpdateAggregate.d.ts +6 -0
  75. package/dist/react/useUpdateAggregate.js +20 -4
  76. package/dist/react/useUpdateMessage.d.ts +5 -0
  77. package/dist/react/useUpdateMessage.js +11 -2
  78. package/dist/rpc/errors.d.ts +10 -0
  79. package/dist/rpc/errors.js +23 -0
  80. package/dist/rpc/rpc-dispatcher.d.ts +28 -0
  81. package/dist/rpc/rpc-dispatcher.js +129 -0
  82. package/dist/test/api-args.test.d.ts +1 -0
  83. package/dist/test/api-args.test.js +48 -0
  84. package/dist/test/api-overloads.test.d.ts +9 -0
  85. package/dist/test/api-overloads.test.js +276 -0
  86. package/dist/test/capabilities.test.d.ts +1 -0
  87. package/dist/test/capabilities.test.js +27 -0
  88. package/dist/test/data-client-errors.test.d.ts +1 -0
  89. package/dist/test/data-client-errors.test.js +24 -0
  90. package/dist/test/data-client-shape.test.d.ts +1 -0
  91. package/dist/test/data-client-shape.test.js +28 -0
  92. package/dist/test/doover-client-dataclient.test.d.ts +1 -0
  93. package/dist/test/doover-client-dataclient.test.js +58 -0
  94. package/dist/test/doover-client-provenance.test.d.ts +1 -0
  95. package/dist/test/doover-client-provenance.test.js +32 -0
  96. package/dist/test/exports.test.d.ts +1 -0
  97. package/dist/test/exports.test.js +76 -0
  98. package/dist/test/gateway-provenance.test.d.ts +1 -0
  99. package/dist/test/gateway-provenance.test.js +29 -0
  100. package/dist/test/gateway-subscriptions.test.d.ts +1 -0
  101. package/dist/test/gateway-subscriptions.test.js +95 -0
  102. package/dist/test/local-agent-client.test.d.ts +1 -0
  103. package/dist/test/local-agent-client.test.js +202 -0
  104. package/dist/test/multiplex-capabilities.test.d.ts +1 -0
  105. package/dist/test/multiplex-capabilities.test.js +37 -0
  106. package/dist/test/multiplex-conflicts.test.d.ts +1 -0
  107. package/dist/test/multiplex-conflicts.test.js +35 -0
  108. package/dist/test/multiplex-gateway.test.d.ts +1 -0
  109. package/dist/test/multiplex-gateway.test.js +103 -0
  110. package/dist/test/multiplex-merge.test.d.ts +1 -0
  111. package/dist/test/multiplex-merge.test.js +19 -0
  112. package/dist/test/multiplex-noncore.test.d.ts +1 -0
  113. package/dist/test/multiplex-noncore.test.js +82 -0
  114. package/dist/test/multiplex-passthrough.test.d.ts +1 -0
  115. package/dist/test/multiplex-passthrough.test.js +35 -0
  116. package/dist/test/multiplex-reads.test.d.ts +1 -0
  117. package/dist/test/multiplex-reads.test.js +87 -0
  118. package/dist/test/multiplex-registry.test.d.ts +1 -0
  119. package/dist/test/multiplex-registry.test.js +81 -0
  120. package/dist/test/multiplex-routing.test.d.ts +1 -0
  121. package/dist/test/multiplex-routing.test.js +45 -0
  122. package/dist/test/multiplex-status.test.d.ts +1 -0
  123. package/dist/test/multiplex-status.test.js +63 -0
  124. package/dist/test/multiplex-writes.test.d.ts +1 -0
  125. package/dist/test/multiplex-writes.test.js +93 -0
  126. package/dist/test/provenance-regression.test.d.ts +1 -0
  127. package/dist/test/provenance-regression.test.js +45 -0
  128. package/dist/test/provenance-stamper.test.d.ts +1 -0
  129. package/dist/test/provenance-stamper.test.js +41 -0
  130. package/dist/test/provenance-type.test.d.ts +1 -0
  131. package/dist/test/provenance-type.test.js +15 -0
  132. package/dist/test/react-client-status.test.d.ts +1 -0
  133. package/dist/test/react-client-status.test.js +35 -0
  134. package/dist/test/react-dataclient.test.d.ts +1 -0
  135. package/dist/test/react-dataclient.test.js +28 -0
  136. package/dist/test/react-exports.test.d.ts +1 -0
  137. package/dist/test/react-exports.test.js +45 -0
  138. package/dist/test/react-sources-option.test.d.ts +1 -0
  139. package/dist/test/react-sources-option.test.js +41 -0
  140. package/dist/test/react.test.js +2 -2
  141. package/dist/test/rpc-dispatcher.test.d.ts +1 -0
  142. package/dist/test/rpc-dispatcher.test.js +190 -0
  143. package/dist/test/stats-rpc.test.d.ts +1 -0
  144. package/dist/test/stats-rpc.test.js +69 -0
  145. package/dist/test/status-tracker.test.d.ts +1 -0
  146. package/dist/test/status-tracker.test.js +85 -0
  147. package/dist/test/users-api.test.d.ts +1 -0
  148. package/dist/test/users-api.test.js +19 -0
  149. package/dist/types/common.d.ts +16 -0
  150. package/dist/types/provenance.d.ts +45 -0
  151. package/dist/types/provenance.js +2 -0
  152. package/dist/types/viewer.d.ts +3 -0
  153. package/dist/viewer/doover-data-provider.d.ts +30 -34
  154. package/dist/viewer/doover-data-provider.js +82 -316
  155. package/package.json +1 -1
@@ -0,0 +1,129 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.RpcDispatcher = void 0;
4
+ const errors_1 = require("./errors");
5
+ function isRpcMessageData(data) {
6
+ return (typeof data === "object" &&
7
+ data !== null &&
8
+ "status" in data &&
9
+ "method" in data &&
10
+ "request" in data);
11
+ }
12
+ class RpcDispatcher {
13
+ constructor(gateway, messages) {
14
+ this.gateway = gateway;
15
+ this.messages = messages;
16
+ this.pending = new Map();
17
+ this.channelRefs = new Map();
18
+ this.stats = null;
19
+ }
20
+ setStats(collector) {
21
+ this.stats = collector;
22
+ }
23
+ send(channel, request, options) {
24
+ return new Promise((resolve, reject) => {
25
+ const channelRef = {
26
+ agent_id: channel.agentId,
27
+ name: channel.channelName,
28
+ };
29
+ const channelKey = `${channelRef.agent_id}/${channelRef.name}`;
30
+ this.acquireChannel(channelKey, channelRef);
31
+ const startedAt = this.stats?.recordRpcStart() ?? null;
32
+ this.messages
33
+ .postMessage(channel.agentId, channel.channelName, {
34
+ type: "rpc",
35
+ ...request,
36
+ })
37
+ .then((message) => {
38
+ const pending = {
39
+ resolve: resolve,
40
+ reject,
41
+ onStatus: options?.onStatus,
42
+ request: request,
43
+ channelKey,
44
+ channel: channelRef,
45
+ startedAt,
46
+ };
47
+ if (options?.signal) {
48
+ const onAbort = () => this.settle(message.id, "abort", undefined, options.signal?.reason);
49
+ if (options.signal.aborted) {
50
+ this.releaseChannel(channelKey);
51
+ this.stats?.recordRpcEnd(startedAt, "abort");
52
+ reject(options.signal.reason ?? new Error("Aborted"));
53
+ return;
54
+ }
55
+ options.signal.addEventListener("abort", onAbort);
56
+ pending.signal = options.signal;
57
+ pending.abortListener = onAbort;
58
+ }
59
+ if (options?.timeoutMs !== undefined) {
60
+ pending.timer = setTimeout(() => {
61
+ this.settle(message.id, "timeout", undefined, new Error("RPC timed out"));
62
+ }, options.timeoutMs);
63
+ }
64
+ this.pending.set(message.id, pending);
65
+ })
66
+ .catch((err) => {
67
+ // postMessage failed before we registered pending — release and reject.
68
+ this.releaseChannel(channelKey);
69
+ this.stats?.recordRpcEnd(startedAt, "error");
70
+ reject(err);
71
+ });
72
+ });
73
+ }
74
+ acquireChannel(channelKey, channelRef) {
75
+ const existing = this.channelRefs.get(channelKey);
76
+ if (existing) {
77
+ existing.refCount += 1;
78
+ return;
79
+ }
80
+ const handlers = {
81
+ onMessageUpdate: (msg, requestData) => this.route(msg, requestData),
82
+ };
83
+ const unsubscribe = this.gateway.subscribeToChannel(channelRef, handlers);
84
+ this.channelRefs.set(channelKey, { unsubscribe, refCount: 1 });
85
+ }
86
+ releaseChannel(channelKey) {
87
+ const entry = this.channelRefs.get(channelKey);
88
+ if (!entry)
89
+ return;
90
+ entry.refCount -= 1;
91
+ if (entry.refCount <= 0) {
92
+ entry.unsubscribe();
93
+ this.channelRefs.delete(channelKey);
94
+ }
95
+ }
96
+ route(msg, _requestData) {
97
+ const pending = this.pending.get(msg.id);
98
+ if (!pending)
99
+ return;
100
+ if (!isRpcMessageData(msg.data))
101
+ return;
102
+ const status = msg.data.status;
103
+ pending.onStatus?.(status);
104
+ if (status.code === "success") {
105
+ this.settle(msg.id, "success", msg.data.response);
106
+ }
107
+ else if (status.code === "error") {
108
+ this.settle(msg.id, "error", undefined, new errors_1.DooverRpcError(status, pending.request));
109
+ }
110
+ }
111
+ settle(messageId, outcome, response, rejection) {
112
+ const pending = this.pending.get(messageId);
113
+ if (!pending)
114
+ return;
115
+ this.pending.delete(messageId);
116
+ if (pending.timer)
117
+ clearTimeout(pending.timer);
118
+ if (pending.signal && pending.abortListener) {
119
+ pending.signal.removeEventListener("abort", pending.abortListener);
120
+ }
121
+ this.releaseChannel(pending.channelKey);
122
+ this.stats?.recordRpcEnd(pending.startedAt, outcome);
123
+ if (outcome === "success")
124
+ pending.resolve(response);
125
+ else
126
+ pending.reject(rejection);
127
+ }
128
+ }
129
+ exports.RpcDispatcher = RpcDispatcher;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,48 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const chai_1 = require("chai");
4
+ const _args_1 = require("../apis/_args");
5
+ describe("resolveChannelArgs", () => {
6
+ it("parses positional (agentId, channelName)", () => {
7
+ const result = (0, _args_1.resolveChannelArgs)(["a1", "c1"]);
8
+ (0, chai_1.expect)(result).to.deep.equal({ agentId: "a1", channelName: "c1", options: undefined });
9
+ });
10
+ it("parses positional with options", () => {
11
+ const result = (0, _args_1.resolveChannelArgs)(["a1", "c1", { x: 7 }]);
12
+ (0, chai_1.expect)(result).to.deep.equal({ agentId: "a1", channelName: "c1", options: { x: 7 } });
13
+ });
14
+ it("parses identifier object", () => {
15
+ const result = (0, _args_1.resolveChannelArgs)([
16
+ { agentId: "a1", channelName: "c1" },
17
+ ]);
18
+ (0, chai_1.expect)(result).to.deep.equal({ agentId: "a1", channelName: "c1", options: undefined });
19
+ });
20
+ it("parses identifier object with options", () => {
21
+ const result = (0, _args_1.resolveChannelArgs)([
22
+ { agentId: "a1", channelName: "c1" },
23
+ { x: 7 },
24
+ ]);
25
+ (0, chai_1.expect)(result).to.deep.equal({ agentId: "a1", channelName: "c1", options: { x: 7 } });
26
+ });
27
+ });
28
+ describe("resolveAgentArgs", () => {
29
+ it("parses positional agentId", () => {
30
+ const result = (0, _args_1.resolveAgentArgs)(["a1"]);
31
+ (0, chai_1.expect)(result).to.deep.equal({ agentId: "a1", options: undefined });
32
+ });
33
+ it("parses positional with options", () => {
34
+ const result = (0, _args_1.resolveAgentArgs)(["a1", { archived: true }]);
35
+ (0, chai_1.expect)(result).to.deep.equal({ agentId: "a1", options: { archived: true } });
36
+ });
37
+ it("parses identifier object", () => {
38
+ const result = (0, _args_1.resolveAgentArgs)([{ agentId: "a1" }]);
39
+ (0, chai_1.expect)(result).to.deep.equal({ agentId: "a1", options: undefined });
40
+ });
41
+ it("parses identifier object with options", () => {
42
+ const result = (0, _args_1.resolveAgentArgs)([
43
+ { agentId: "a1" },
44
+ { archived: true },
45
+ ]);
46
+ (0, chai_1.expect)(result).to.deep.equal({ agentId: "a1", options: { archived: true } });
47
+ });
48
+ });
@@ -0,0 +1,9 @@
1
+ import type { RestClient } from "../http/rest-client";
2
+ interface RecordedCall {
3
+ method: "get" | "post" | "put" | "patch" | "delete" | "request";
4
+ args: unknown[];
5
+ }
6
+ export declare function makeRestStub(): RestClient & {
7
+ calls: RecordedCall[];
8
+ };
9
+ export {};
@@ -0,0 +1,276 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.makeRestStub = makeRestStub;
4
+ const chai_1 = require("chai");
5
+ const channels_api_1 = require("../apis/channels-api");
6
+ const messages_api_1 = require("../apis/messages-api");
7
+ function makeRestStub() {
8
+ const calls = [];
9
+ const stub = {
10
+ calls,
11
+ get: (...args) => {
12
+ calls.push({ method: "get", args });
13
+ return Promise.resolve([]);
14
+ },
15
+ post: (...args) => {
16
+ calls.push({ method: "post", args });
17
+ return Promise.resolve(undefined);
18
+ },
19
+ put: (...args) => {
20
+ calls.push({ method: "put", args });
21
+ return Promise.resolve(undefined);
22
+ },
23
+ patch: (...args) => {
24
+ calls.push({ method: "patch", args });
25
+ return Promise.resolve(undefined);
26
+ },
27
+ delete: (...args) => {
28
+ calls.push({ method: "delete", args });
29
+ return Promise.resolve(undefined);
30
+ },
31
+ request: (...args) => {
32
+ calls.push({ method: "request", args });
33
+ return Promise.resolve(undefined);
34
+ },
35
+ };
36
+ return stub;
37
+ }
38
+ describe("ChannelsApi overloads", () => {
39
+ it("getChannel positional and identifier-object produce identical requests", async () => {
40
+ const restA = makeRestStub();
41
+ const restB = makeRestStub();
42
+ const apiA = new channels_api_1.ChannelsApi(restA);
43
+ const apiB = new channels_api_1.ChannelsApi(restB);
44
+ await apiA.getChannel("a1", "c1", { include_aggregate: true });
45
+ await apiB.getChannel({ agentId: "a1", channelName: "c1" }, { include_aggregate: true });
46
+ (0, chai_1.expect)(restA.calls).to.deep.equal(restB.calls);
47
+ });
48
+ it("listChannels positional and identifier-object produce identical requests", async () => {
49
+ const restA = makeRestStub();
50
+ const restB = makeRestStub();
51
+ const apiA = new channels_api_1.ChannelsApi(restA);
52
+ const apiB = new channels_api_1.ChannelsApi(restB);
53
+ await apiA.listChannels("a1", { include_archived: true });
54
+ await apiB.listChannels({ agentId: "a1" }, { include_archived: true });
55
+ (0, chai_1.expect)(restA.calls).to.deep.equal(restB.calls);
56
+ });
57
+ it("archiveChannel posts to /archive", async () => {
58
+ const rest = makeRestStub();
59
+ const api = new channels_api_1.ChannelsApi(rest);
60
+ await api.archiveChannel("a1", "c1");
61
+ (0, chai_1.expect)(rest.calls).to.deep.equal([
62
+ { method: "post", args: ["/agents/a1/channels/c1/archive", {}] },
63
+ ]);
64
+ });
65
+ it("unarchiveChannel posts to /unarchive", async () => {
66
+ const rest = makeRestStub();
67
+ const api = new channels_api_1.ChannelsApi(rest);
68
+ await api.unarchiveChannel({ agentId: "a1", channelName: "c1" });
69
+ (0, chai_1.expect)(rest.calls).to.deep.equal([
70
+ { method: "post", args: ["/agents/a1/channels/c1/unarchive", {}] },
71
+ ]);
72
+ });
73
+ });
74
+ describe("MessagesApi overloads + defaults", () => {
75
+ it("listMessages positional and identifier-object produce identical requests", async () => {
76
+ const restA = makeRestStub();
77
+ const restB = makeRestStub();
78
+ const apiA = new messages_api_1.MessagesApi(restA);
79
+ const apiB = new messages_api_1.MessagesApi(restB);
80
+ await apiA.listMessages("a1", "c1", { limit: 5, order: "desc" });
81
+ await apiB.listMessages({ agentId: "a1", channelName: "c1" }, { limit: 5, order: "desc" });
82
+ (0, chai_1.expect)(restA.calls).to.deep.equal(restB.calls);
83
+ });
84
+ it("listMessages defaults limit to 10 when omitted", async () => {
85
+ const rest = makeRestStub();
86
+ const api = new messages_api_1.MessagesApi(rest);
87
+ await api.listMessages("a1", "c1");
88
+ (0, chai_1.expect)(rest.calls).to.have.lengthOf(1);
89
+ const params = rest.calls[0].args[1];
90
+ (0, chai_1.expect)(params.limit).to.equal(10);
91
+ (0, chai_1.expect)(params.before, "before should be set to a snowflake id").to.be.a("string");
92
+ });
93
+ it("listMessages with order='asc' reverses the response array", async () => {
94
+ const rest = makeRestStub();
95
+ rest.get = ((..._args) => Promise.resolve([
96
+ { id: "3", data: {}, attachments: [], author_id: "a", channel: { agent_id: "a1", name: "c1" } },
97
+ { id: "2", data: {}, attachments: [], author_id: "a", channel: { agent_id: "a1", name: "c1" } },
98
+ { id: "1", data: {}, attachments: [], author_id: "a", channel: { agent_id: "a1", name: "c1" } },
99
+ ]));
100
+ const api = new messages_api_1.MessagesApi(rest);
101
+ const result = await api.listMessages("a1", "c1", { order: "asc", limit: 3 });
102
+ (0, chai_1.expect)(result.map((m) => m.id)).to.deep.equal(["1", "2", "3"]);
103
+ });
104
+ it("listMessages with order='desc' (default) preserves server order", async () => {
105
+ const rest = makeRestStub();
106
+ rest.get = ((..._args) => Promise.resolve([
107
+ { id: "3", data: {}, attachments: [], author_id: "a", channel: { agent_id: "a1", name: "c1" } },
108
+ { id: "2", data: {}, attachments: [], author_id: "a", channel: { agent_id: "a1", name: "c1" } },
109
+ ]));
110
+ const api = new messages_api_1.MessagesApi(rest);
111
+ const result = await api.listMessages("a1", "c1", { limit: 2 });
112
+ (0, chai_1.expect)(result.map((m) => m.id)).to.deep.equal(["3", "2"]);
113
+ });
114
+ it("getTimeseries strips order before sending to server", async () => {
115
+ const rest = makeRestStub();
116
+ const api = new messages_api_1.MessagesApi(rest);
117
+ await api.getTimeseries("a1", "c1", { limit: 5, order: "asc" });
118
+ (0, chai_1.expect)(rest.calls).to.have.lengthOf(1);
119
+ const wireParams = rest.calls[0].args[1];
120
+ (0, chai_1.expect)(wireParams.order).to.equal(undefined);
121
+ (0, chai_1.expect)(wireParams.limit).to.equal(5);
122
+ });
123
+ });
124
+ const aggregates_api_1 = require("../apis/aggregates-api");
125
+ describe("AggregatesApi overloads", () => {
126
+ it("getAggregate positional and identifier-object produce identical requests", async () => {
127
+ const restA = makeRestStub();
128
+ const restB = makeRestStub();
129
+ const apiA = new aggregates_api_1.AggregatesApi(restA);
130
+ const apiB = new aggregates_api_1.AggregatesApi(restB);
131
+ await apiA.getAggregate("a1", "c1");
132
+ await apiB.getAggregate({ agentId: "a1", channelName: "c1" });
133
+ (0, chai_1.expect)(restA.calls).to.deep.equal(restB.calls);
134
+ });
135
+ it("patchAggregate identifier form passes through body and params", async () => {
136
+ const rest = makeRestStub();
137
+ const api = new aggregates_api_1.AggregatesApi(rest);
138
+ await api.patchAggregate({ agentId: "a1", channelName: "c1" }, { x: 1 }, { suppress_response: true });
139
+ (0, chai_1.expect)(rest.calls).to.deep.equal([
140
+ {
141
+ method: "patch",
142
+ args: ["/agents/a1/channels/c1/aggregate", { x: 1 }, { suppress_response: true }],
143
+ },
144
+ ]);
145
+ });
146
+ });
147
+ const alarms_api_1 = require("../apis/alarms-api");
148
+ describe("AlarmsApi overloads", () => {
149
+ it("listAlarms positional and identifier-object produce identical requests", async () => {
150
+ const restA = makeRestStub();
151
+ const restB = makeRestStub();
152
+ const apiA = new alarms_api_1.AlarmsApi(restA);
153
+ const apiB = new alarms_api_1.AlarmsApi(restB);
154
+ await apiA.listAlarms("a1", "c1");
155
+ await apiB.listAlarms({ agentId: "a1", channelName: "c1" });
156
+ (0, chai_1.expect)(restA.calls).to.deep.equal(restB.calls);
157
+ });
158
+ it("getAlarm via identifier hits expected path", async () => {
159
+ const rest = makeRestStub();
160
+ const api = new alarms_api_1.AlarmsApi(rest);
161
+ await api.getAlarm({ agentId: "a1", channelName: "c1" }, "alm1");
162
+ (0, chai_1.expect)(rest.calls).to.deep.equal([
163
+ { method: "get", args: ["/agents/a1/channels/c1/alarms/alm1"] },
164
+ ]);
165
+ });
166
+ });
167
+ const connections_api_1 = require("../apis/connections-api");
168
+ describe("ConnectionsApi overloads", () => {
169
+ it("getAgentConnections positional and identifier-object produce identical requests", async () => {
170
+ const restA = makeRestStub();
171
+ const restB = makeRestStub();
172
+ const apiA = new connections_api_1.ConnectionsApi(restA);
173
+ const apiB = new connections_api_1.ConnectionsApi(restB);
174
+ await apiA.getAgentConnections("a1");
175
+ await apiB.getAgentConnections({ agentId: "a1" });
176
+ (0, chai_1.expect)(restA.calls).to.deep.equal(restB.calls);
177
+ });
178
+ it("getChannelSubscriptions identifier form hits expected path", async () => {
179
+ const rest = makeRestStub();
180
+ const api = new connections_api_1.ConnectionsApi(rest);
181
+ await api.getChannelSubscriptions({ agentId: "a1", channelName: "c1" });
182
+ (0, chai_1.expect)(rest.calls).to.deep.equal([
183
+ { method: "get", args: ["/agents/a1/channels/c1/subscriptions"] },
184
+ ]);
185
+ });
186
+ });
187
+ const notifications_api_1 = require("../apis/notifications-api");
188
+ describe("NotificationsApi overloads", () => {
189
+ it("getAgentNotifications positional and identifier-object produce identical requests", async () => {
190
+ const restA = makeRestStub();
191
+ const restB = makeRestStub();
192
+ const apiA = new notifications_api_1.NotificationsApi(restA);
193
+ const apiB = new notifications_api_1.NotificationsApi(restB);
194
+ await apiA.getAgentNotifications("a1");
195
+ await apiB.getAgentNotifications({ agentId: "a1" });
196
+ (0, chai_1.expect)(restA.calls).to.deep.equal(restB.calls);
197
+ });
198
+ it("updateNotificationEndpoint identifier form preserves endpointId and body", async () => {
199
+ const rest = makeRestStub();
200
+ const api = new notifications_api_1.NotificationsApi(rest);
201
+ await api.updateNotificationEndpoint({ agentId: "a1" }, "ep1", { name: "x" });
202
+ (0, chai_1.expect)(rest.calls).to.deep.equal([
203
+ {
204
+ method: "patch",
205
+ args: ["/agents/a1/notifications/endpoints/ep1", { name: "x" }],
206
+ },
207
+ ]);
208
+ });
209
+ });
210
+ const permissions_api_1 = require("../apis/permissions-api");
211
+ describe("PermissionsApi overloads", () => {
212
+ it("getAgentPermission positional and identifier-object produce identical requests", async () => {
213
+ const restA = makeRestStub();
214
+ const restB = makeRestStub();
215
+ const apiA = new permissions_api_1.PermissionsApi(restA);
216
+ const apiB = new permissions_api_1.PermissionsApi(restB);
217
+ await apiA.getAgentPermission("a1");
218
+ await apiB.getAgentPermission({ agentId: "a1" });
219
+ (0, chai_1.expect)(restA.calls).to.deep.equal(restB.calls);
220
+ });
221
+ });
222
+ const processors_api_1 = require("../apis/processors-api");
223
+ describe("ProcessorsApi overloads", () => {
224
+ it("createProcessorSchedule identifier form hits expected path", async () => {
225
+ const rest = makeRestStub();
226
+ const api = new processors_api_1.ProcessorsApi(rest);
227
+ await api.createProcessorSchedule({ agentId: "a1" }, "sch1", { foo: "bar" });
228
+ (0, chai_1.expect)(rest.calls).to.deep.equal([
229
+ {
230
+ method: "put",
231
+ args: ["/agents/a1/processors/schedules/sch1", { foo: "bar" }],
232
+ },
233
+ ]);
234
+ });
235
+ });
236
+ const agents_api_1 = require("../apis/agents-api");
237
+ describe("AgentsApi listAgents", () => {
238
+ it("calls /agents/ on controlApiUrl with omitSharingHeader", async () => {
239
+ const rest = makeRestStub();
240
+ const api = new agents_api_1.AgentsApi(rest, "https://control.example.com");
241
+ rest.request = ((...args) => {
242
+ rest.calls.push({ method: "request", args });
243
+ return Promise.resolve({ agents: [] });
244
+ });
245
+ await api.listAgents({ includeArchived: true });
246
+ (0, chai_1.expect)(rest.calls).to.have.lengthOf(1);
247
+ (0, chai_1.expect)(rest.calls[0].method).to.equal("request");
248
+ const req = rest.calls[0].args[0];
249
+ (0, chai_1.expect)(req.path).to.equal("/agents/");
250
+ (0, chai_1.expect)(req.baseUrl).to.equal("https://control.example.com");
251
+ (0, chai_1.expect)(req.omitSharingHeader).to.equal(true);
252
+ (0, chai_1.expect)(req.query).to.deep.equal({ "include-archived": true });
253
+ });
254
+ it("normalises agent entries and merges organisations/users when requested", async () => {
255
+ const rest = makeRestStub();
256
+ rest.request = ((..._args) => Promise.resolve({
257
+ agents: [
258
+ { id: "a1", name: "Agent One", type: "device" },
259
+ ],
260
+ organisations: [{ id: "o1", name: "Org One" }],
261
+ users: [{ id: "u1", name: "User One" }],
262
+ }));
263
+ const api = new agents_api_1.AgentsApi(rest);
264
+ const result = await api.listAgents({
265
+ includeOrganisations: true,
266
+ includeUsers: true,
267
+ mergeIncludedAsAgents: true,
268
+ });
269
+ (0, chai_1.expect)(result.results).to.have.lengthOf(3);
270
+ (0, chai_1.expect)(result.count).to.equal(3);
271
+ const ids = (result.results ?? []).map((a) => a.id).sort();
272
+ (0, chai_1.expect)(ids).to.deep.equal(["a1", "o1", "u1"]);
273
+ const types = (result.results ?? []).map((a) => a.type).sort();
274
+ (0, chai_1.expect)(types).to.deep.equal(["device", "organisation", "user"]);
275
+ });
276
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const chai_1 = require("chai");
4
+ const mocha_1 = require("mocha");
5
+ const capabilities_1 = require("../client/capabilities");
6
+ (0, mocha_1.describe)("Capability", () => {
7
+ (0, mocha_1.it)("ALL_CAPABILITIES has no duplicates and includes the core set", () => {
8
+ (0, chai_1.expect)(new Set(capabilities_1.ALL_CAPABILITIES).size).to.equal(capabilities_1.ALL_CAPABILITIES.length);
9
+ for (const cap of [
10
+ "agents.list",
11
+ "channels.list",
12
+ "channels.get",
13
+ "channels.create",
14
+ "channels.archive",
15
+ "aggregates.get",
16
+ "aggregates.put",
17
+ "aggregates.patch",
18
+ "messages.list",
19
+ "messages.listHistorical",
20
+ "messages.post",
21
+ "gateway.subscribe",
22
+ "gateway.realtime",
23
+ ]) {
24
+ (0, chai_1.expect)(capabilities_1.ALL_CAPABILITIES).to.include(cap);
25
+ }
26
+ });
27
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const chai_1 = require("chai");
4
+ const mocha_1 = require("mocha");
5
+ const errors_1 = require("../client/errors");
6
+ const errors_2 = require("../http/errors");
7
+ (0, mocha_1.describe)("DataClient errors", () => {
8
+ (0, mocha_1.it)("UnsupportedCapabilityError carries the capability and clientId", () => {
9
+ const err = new errors_1.UnsupportedCapabilityError("messages.listHistorical", "local:1");
10
+ (0, chai_1.expect)(err).to.be.instanceOf(errors_2.DooverApiError);
11
+ (0, chai_1.expect)(err.capability).to.equal("messages.listHistorical");
12
+ (0, chai_1.expect)(err.clientId).to.equal("local:1");
13
+ (0, chai_1.expect)(err.message).to.include("messages.listHistorical");
14
+ (0, chai_1.expect)(err.name).to.equal("UnsupportedCapabilityError");
15
+ });
16
+ (0, mocha_1.it)("AmbiguousWriteError lists candidate source ids", () => {
17
+ const err = new errors_1.AmbiguousWriteError("messages.post", ["cloud", "local:1"]);
18
+ (0, chai_1.expect)(err).to.be.instanceOf(errors_2.DooverApiError);
19
+ (0, chai_1.expect)(err.capability).to.equal("messages.post");
20
+ (0, chai_1.expect)(err.candidateSourceIds).to.deep.equal(["cloud", "local:1"]);
21
+ (0, chai_1.expect)(err.message).to.include("cloud");
22
+ (0, chai_1.expect)(err.message).to.include("local:1");
23
+ });
24
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const chai_1 = require("chai");
4
+ const mocha_1 = require("mocha");
5
+ const doover_client_1 = require("../client/doover-client");
6
+ const helpers_1 = require("./helpers");
7
+ (0, mocha_1.describe)("DataClient shape", () => {
8
+ (0, mocha_1.it)("DooverClient is assignable to DataClient (compile-time) and has the contract methods", () => {
9
+ const client = new doover_client_1.DooverClient({
10
+ dataRestUrl: "https://api.example.com",
11
+ controlApiUrl: "https://control.example.com",
12
+ dataWssUrl: "wss://ws.example.com",
13
+ fetchImpl: (0, helpers_1.createFetchMock)(),
14
+ webSocketImpl: helpers_1.MockWebSocket,
15
+ disableBrowserLifecycleHooks: true,
16
+ });
17
+ (0, chai_1.expect)(client.agents).to.exist;
18
+ (0, chai_1.expect)(client.gateway).to.exist;
19
+ (0, chai_1.expect)(client.rpc).to.exist;
20
+ (0, chai_1.expect)(typeof client.getCapabilities).to.equal("function");
21
+ (0, chai_1.expect)(typeof client.supports).to.equal("function");
22
+ (0, chai_1.expect)(typeof client.isConnected).to.equal("function");
23
+ (0, chai_1.expect)(typeof client.getStatus).to.equal("function");
24
+ (0, chai_1.expect)(typeof client.onStatusChange).to.equal("function");
25
+ (0, chai_1.expect)(typeof client.getAgentScope).to.equal("function");
26
+ (0, chai_1.expect)(typeof client.getKnownAgentScope).to.equal("function");
27
+ });
28
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,58 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const chai_1 = require("chai");
4
+ const mocha_1 = require("mocha");
5
+ const doover_client_1 = require("../client/doover-client");
6
+ const capabilities_1 = require("../client/capabilities");
7
+ const helpers_1 = require("./helpers");
8
+ function makeClient(extra = {}) {
9
+ return new doover_client_1.DooverClient({
10
+ dataRestUrl: "https://api.example.com",
11
+ controlApiUrl: "https://control.example.com",
12
+ dataWssUrl: "wss://ws.example.com",
13
+ fetchImpl: (0, helpers_1.createFetchMock)(),
14
+ webSocketImpl: helpers_1.MockWebSocket,
15
+ disableBrowserLifecycleHooks: true,
16
+ ...extra,
17
+ });
18
+ }
19
+ (0, mocha_1.describe)("DooverClient as DataClient", () => {
20
+ (0, mocha_1.it)("advertises the full capability set", () => {
21
+ const caps = makeClient().getCapabilities();
22
+ for (const c of capabilities_1.ALL_CAPABILITIES)
23
+ (0, chai_1.expect)(caps.has(c)).to.equal(true);
24
+ (0, chai_1.expect)(makeClient().supports("messages.listHistorical")).to.equal(true);
25
+ });
26
+ (0, mocha_1.it)("getAgentScope resolves to { mode: 'all' } with no network call", async () => {
27
+ const fetchMock = (0, helpers_1.createFetchMock)();
28
+ const client = makeClient({ fetchImpl: fetchMock });
29
+ (0, chai_1.expect)(await client.getAgentScope()).to.deep.equal({ mode: "all" });
30
+ (0, chai_1.expect)(client.getKnownAgentScope()).to.deep.equal({ mode: "all" });
31
+ (0, chai_1.expect)(fetchMock.called).to.equal(false);
32
+ });
33
+ (0, mocha_1.it)("isConnected mirrors the gateway; getStatus reflects it; clientId defaults to 'cloud'", () => {
34
+ const client = makeClient();
35
+ (0, chai_1.expect)(client.isConnected()).to.equal(false);
36
+ const status = client.getStatus();
37
+ (0, chai_1.expect)(status.clientId).to.equal("cloud");
38
+ (0, chai_1.expect)(status.connected).to.equal(false);
39
+ (0, chai_1.expect)(status.agentScope).to.deep.equal({ mode: "all" });
40
+ });
41
+ (0, mocha_1.it)("honours a custom sourceId", () => {
42
+ (0, chai_1.expect)(makeClient({ sourceId: "cloud-eu" }).getStatus().clientId).to.equal("cloud-eu");
43
+ });
44
+ (0, mocha_1.it)("onStatusChange fires on gateway lifecycle and unsubscribes", async () => {
45
+ const client = makeClient();
46
+ const seen = [];
47
+ const off = client.onStatusChange(() => seen.push(1));
48
+ // Connect gateway and drive a lifecycle event
49
+ await client.gateway.connect();
50
+ const ws = helpers_1.MockWebSocket.instances[helpers_1.MockWebSocket.instances.length - 1];
51
+ ws?.open?.();
52
+ (0, chai_1.expect)(seen.length).to.be.greaterThan(0);
53
+ off();
54
+ const len = seen.length;
55
+ ws?.close?.();
56
+ (0, chai_1.expect)(seen.length).to.equal(len);
57
+ });
58
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,32 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const chai_1 = require("chai");
4
+ const mocha_1 = require("mocha");
5
+ const doover_client_1 = require("../client/doover-client");
6
+ const helpers_1 = require("./helpers");
7
+ (0, mocha_1.describe)("DooverClient REST provenance", () => {
8
+ (0, mocha_1.it)("stamps __source on REST results", async () => {
9
+ const fetchMock = (0, helpers_1.createFetchMock)((url) => {
10
+ if (url.includes("/channels/c1"))
11
+ return (0, helpers_1.createJsonResponse)({ name: "c1", is_private: false, owner_id: "o" });
12
+ return (0, helpers_1.createJsonResponse)({});
13
+ });
14
+ const client = new doover_client_1.DooverClient({
15
+ dataRestUrl: "https://api.example.com",
16
+ controlApiUrl: "https://control.example.com",
17
+ dataWssUrl: "wss://ws.example.com",
18
+ fetchImpl: fetchMock,
19
+ webSocketImpl: helpers_1.MockWebSocket,
20
+ disableBrowserLifecycleHooks: true,
21
+ sourceId: "cloud",
22
+ });
23
+ const channel = await client.channels.getChannel("a1", "c1");
24
+ (0, chai_1.expect)(channel.name).to.equal("c1");
25
+ (0, chai_1.expect)(channel.__source?.client).to.deep.include({ id: "cloud", kind: "cloud" });
26
+ (0, chai_1.expect)(channel.__source?.via).to.include({ transport: "rest", method: "channels.getChannel" });
27
+ if (channel.__source?.via.transport === "rest") {
28
+ (0, chai_1.expect)(channel.__source.via.durationMs).to.be.a("number");
29
+ (0, chai_1.expect)(channel.__source.via.request).to.have.property("args");
30
+ }
31
+ });
32
+ });
@@ -0,0 +1 @@
1
+ export {};