doover-js 0.1.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/LICENSE +201 -0
- package/README.md +35 -0
- package/dist/apis/agents-api.d.ts +22 -0
- package/dist/apis/agents-api.js +20 -0
- package/dist/apis/aggregates-api.d.ts +15 -0
- package/dist/apis/aggregates-api.js +21 -0
- package/dist/apis/alarms-api.d.ts +12 -0
- package/dist/apis/alarms-api.js +27 -0
- package/dist/apis/channels-api.d.ts +27 -0
- package/dist/apis/channels-api.js +24 -0
- package/dist/apis/connections-api.d.ts +23 -0
- package/dist/apis/connections-api.js +24 -0
- package/dist/apis/messages-api.d.ts +32 -0
- package/dist/apis/messages-api.js +45 -0
- package/dist/apis/notifications-api.d.ts +23 -0
- package/dist/apis/notifications-api.js +54 -0
- package/dist/apis/permissions-api.d.ts +9 -0
- package/dist/apis/permissions-api.js +18 -0
- package/dist/apis/processors-api.d.ts +21 -0
- package/dist/apis/processors-api.js +45 -0
- package/dist/apis/turn-api.d.ts +7 -0
- package/dist/apis/turn-api.js +12 -0
- package/dist/client/doover-client.d.ts +29 -0
- package/dist/client/doover-client.js +34 -0
- package/dist/gateway/gateway-client.d.ts +37 -0
- package/dist/gateway/gateway-client.js +258 -0
- package/dist/gateway/types.d.ts +135 -0
- package/dist/gateway/types.js +2 -0
- package/dist/http/errors.d.ts +19 -0
- package/dist/http/errors.js +28 -0
- package/dist/http/rest-client.d.ts +35 -0
- package/dist/http/rest-client.js +129 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.js +41 -0
- package/dist/test/apis.test.d.ts +1 -0
- package/dist/test/apis.test.js +248 -0
- package/dist/test/doover-client.test.d.ts +1 -0
- package/dist/test/doover-client.test.js +29 -0
- package/dist/test/doover-data-provider.test.d.ts +1 -0
- package/dist/test/doover-data-provider.test.js +228 -0
- package/dist/test/gateway-client.test.d.ts +1 -0
- package/dist/test/gateway-client.test.js +235 -0
- package/dist/test/helpers.d.ts +32 -0
- package/dist/test/helpers.js +115 -0
- package/dist/test/rest-client.test.d.ts +1 -0
- package/dist/test/rest-client.test.js +115 -0
- package/dist/test/utils.test.d.ts +1 -0
- package/dist/test/utils.test.js +54 -0
- package/dist/types/common.d.ts +130 -0
- package/dist/types/common.js +2 -0
- package/dist/types/openapi.d.ts +144 -0
- package/dist/types/openapi.js +2 -0
- package/dist/types/viewer.d.ts +70 -0
- package/dist/types/viewer.js +2 -0
- package/dist/utils/snowflake.d.ts +13 -0
- package/dist/utils/snowflake.js +26 -0
- package/dist/viewer/doover-data-provider.d.ts +55 -0
- package/dist/viewer/doover-data-provider.js +191 -0
- package/dist/viewer/path-parsing.d.ts +5 -0
- package/dist/viewer/path-parsing.js +38 -0
- package/package.json +46 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { RestClient } from "../http/rest-client";
|
|
2
|
+
import type { CreateIngestionRequest, PutScheduleRequest, PutSubscriptionRequest, ScheduleInfo, SubscriptionInfo } from "../types/openapi";
|
|
3
|
+
export declare class ProcessorsApi {
|
|
4
|
+
private readonly rest;
|
|
5
|
+
constructor(rest: RestClient);
|
|
6
|
+
createProcessorSchedule(agentId: string, scheduleId: string, body: PutScheduleRequest): Promise<unknown>;
|
|
7
|
+
deleteProcessorSchedule(agentId: string, scheduleId: string): Promise<unknown>;
|
|
8
|
+
regenerateScheduleToken(agentId: string, scheduleId: string): Promise<unknown>;
|
|
9
|
+
getScheduleInfo(scheduleId: string): Promise<ScheduleInfo>;
|
|
10
|
+
getScheduleInfoAlias(scheduleId: string): Promise<ScheduleInfo>;
|
|
11
|
+
createProcessorSubscription(agentId: string, subscriptionId: string, body: PutSubscriptionRequest): Promise<unknown>;
|
|
12
|
+
deleteProcessorSubscription(agentId: string, subscriptionId: string): Promise<unknown>;
|
|
13
|
+
getProcessorSubscriptionInfo(subscriptionName: string): Promise<SubscriptionInfo>;
|
|
14
|
+
getProcessorSubscriptionInfoAlias(subscriptionArn: string): Promise<SubscriptionInfo>;
|
|
15
|
+
createIngestionEndpoint(agentId: string, ingestionId: string, body: CreateIngestionRequest): Promise<unknown>;
|
|
16
|
+
deleteIngestionEndpoint(agentId: string, ingestionId: string): Promise<unknown>;
|
|
17
|
+
invokeIngestionEndpoint(agentId: string, ingestionId: string, body: unknown, params?: {
|
|
18
|
+
wait?: boolean;
|
|
19
|
+
json_response?: boolean;
|
|
20
|
+
}): Promise<unknown>;
|
|
21
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ProcessorsApi = void 0;
|
|
4
|
+
class ProcessorsApi {
|
|
5
|
+
constructor(rest) {
|
|
6
|
+
this.rest = rest;
|
|
7
|
+
}
|
|
8
|
+
createProcessorSchedule(agentId, scheduleId, body) {
|
|
9
|
+
return this.rest.put(`/agents/${agentId}/processors/schedules/${scheduleId}`, body);
|
|
10
|
+
}
|
|
11
|
+
deleteProcessorSchedule(agentId, scheduleId) {
|
|
12
|
+
return this.rest.delete(`/agents/${agentId}/processors/schedules/${scheduleId}`);
|
|
13
|
+
}
|
|
14
|
+
regenerateScheduleToken(agentId, scheduleId) {
|
|
15
|
+
return this.rest.post(`/agents/${agentId}/processors/schedules/${scheduleId}/token`, {});
|
|
16
|
+
}
|
|
17
|
+
getScheduleInfo(scheduleId) {
|
|
18
|
+
return this.rest.get(`/processors/schedules/${scheduleId}`);
|
|
19
|
+
}
|
|
20
|
+
getScheduleInfoAlias(scheduleId) {
|
|
21
|
+
return this.rest.get(`/processors/schedules/${scheduleId}/info`);
|
|
22
|
+
}
|
|
23
|
+
createProcessorSubscription(agentId, subscriptionId, body) {
|
|
24
|
+
return this.rest.put(`/agents/${agentId}/processors/subscriptions/${subscriptionId}`, body);
|
|
25
|
+
}
|
|
26
|
+
deleteProcessorSubscription(agentId, subscriptionId) {
|
|
27
|
+
return this.rest.delete(`/agents/${agentId}/processors/subscriptions/${subscriptionId}`);
|
|
28
|
+
}
|
|
29
|
+
getProcessorSubscriptionInfo(subscriptionName) {
|
|
30
|
+
return this.rest.get(`/processors/subscriptions/${subscriptionName}`);
|
|
31
|
+
}
|
|
32
|
+
getProcessorSubscriptionInfoAlias(subscriptionArn) {
|
|
33
|
+
return this.rest.get(`/processors/subscriptions/${subscriptionArn}/info`);
|
|
34
|
+
}
|
|
35
|
+
createIngestionEndpoint(agentId, ingestionId, body) {
|
|
36
|
+
return this.rest.put(`/agents/${agentId}/processors/ingestions/${ingestionId}`, body);
|
|
37
|
+
}
|
|
38
|
+
deleteIngestionEndpoint(agentId, ingestionId) {
|
|
39
|
+
return this.rest.delete(`/agents/${agentId}/processors/ingestions/${ingestionId}`);
|
|
40
|
+
}
|
|
41
|
+
invokeIngestionEndpoint(agentId, ingestionId, body, params) {
|
|
42
|
+
return this.rest.post(`/agents/${agentId}/processors/ingestions/${ingestionId}/invoke`, body, params);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
exports.ProcessorsApi = ProcessorsApi;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { RestClient } from "../http/rest-client";
|
|
2
|
+
import type { TurnCredential, TurnTokenRequest } from "../types/openapi";
|
|
3
|
+
export declare class TurnApi {
|
|
4
|
+
private readonly rest;
|
|
5
|
+
constructor(rest: RestClient);
|
|
6
|
+
createTurnToken(body: TurnTokenRequest): Promise<TurnCredential>;
|
|
7
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.TurnApi = void 0;
|
|
4
|
+
class TurnApi {
|
|
5
|
+
constructor(rest) {
|
|
6
|
+
this.rest = rest;
|
|
7
|
+
}
|
|
8
|
+
createTurnToken(body) {
|
|
9
|
+
return this.rest.post("/turn/token", body);
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
exports.TurnApi = TurnApi;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { AgentsApi } from "../apis/agents-api";
|
|
2
|
+
import { AggregatesApi } from "../apis/aggregates-api";
|
|
3
|
+
import { AlarmsApi } from "../apis/alarms-api";
|
|
4
|
+
import { ChannelsApi } from "../apis/channels-api";
|
|
5
|
+
import { ConnectionsApi } from "../apis/connections-api";
|
|
6
|
+
import { MessagesApi } from "../apis/messages-api";
|
|
7
|
+
import { NotificationsApi } from "../apis/notifications-api";
|
|
8
|
+
import { PermissionsApi } from "../apis/permissions-api";
|
|
9
|
+
import { ProcessorsApi } from "../apis/processors-api";
|
|
10
|
+
import { TurnApi } from "../apis/turn-api";
|
|
11
|
+
import { GatewayClient } from "../gateway/gateway-client";
|
|
12
|
+
import { RestClient, type DooverClientConfig } from "../http/rest-client";
|
|
13
|
+
import { DooverDataProvider } from "../viewer/doover-data-provider";
|
|
14
|
+
export declare class DooverClient {
|
|
15
|
+
readonly rest: RestClient;
|
|
16
|
+
readonly viewer: DooverDataProvider;
|
|
17
|
+
readonly channels: ChannelsApi;
|
|
18
|
+
readonly messages: MessagesApi;
|
|
19
|
+
readonly aggregates: AggregatesApi;
|
|
20
|
+
readonly alarms: AlarmsApi;
|
|
21
|
+
readonly connections: ConnectionsApi;
|
|
22
|
+
readonly notifications: NotificationsApi;
|
|
23
|
+
readonly permissions: PermissionsApi;
|
|
24
|
+
readonly processors: ProcessorsApi;
|
|
25
|
+
readonly turn: TurnApi;
|
|
26
|
+
readonly agents: AgentsApi;
|
|
27
|
+
readonly gateway: GatewayClient;
|
|
28
|
+
constructor(config: DooverClientConfig);
|
|
29
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DooverClient = void 0;
|
|
4
|
+
const agents_api_1 = require("../apis/agents-api");
|
|
5
|
+
const aggregates_api_1 = require("../apis/aggregates-api");
|
|
6
|
+
const alarms_api_1 = require("../apis/alarms-api");
|
|
7
|
+
const channels_api_1 = require("../apis/channels-api");
|
|
8
|
+
const connections_api_1 = require("../apis/connections-api");
|
|
9
|
+
const messages_api_1 = require("../apis/messages-api");
|
|
10
|
+
const notifications_api_1 = require("../apis/notifications-api");
|
|
11
|
+
const permissions_api_1 = require("../apis/permissions-api");
|
|
12
|
+
const processors_api_1 = require("../apis/processors-api");
|
|
13
|
+
const turn_api_1 = require("../apis/turn-api");
|
|
14
|
+
const gateway_client_1 = require("../gateway/gateway-client");
|
|
15
|
+
const rest_client_1 = require("../http/rest-client");
|
|
16
|
+
const doover_data_provider_1 = require("../viewer/doover-data-provider");
|
|
17
|
+
class DooverClient {
|
|
18
|
+
constructor(config) {
|
|
19
|
+
this.rest = new rest_client_1.RestClient(config);
|
|
20
|
+
this.viewer = new doover_data_provider_1.DooverDataProvider(config);
|
|
21
|
+
this.channels = new channels_api_1.ChannelsApi(this.rest);
|
|
22
|
+
this.messages = new messages_api_1.MessagesApi(this.rest);
|
|
23
|
+
this.aggregates = new aggregates_api_1.AggregatesApi(this.rest);
|
|
24
|
+
this.alarms = new alarms_api_1.AlarmsApi(this.rest);
|
|
25
|
+
this.connections = new connections_api_1.ConnectionsApi(this.rest);
|
|
26
|
+
this.notifications = new notifications_api_1.NotificationsApi(this.rest);
|
|
27
|
+
this.permissions = new permissions_api_1.PermissionsApi(this.rest);
|
|
28
|
+
this.processors = new processors_api_1.ProcessorsApi(this.rest);
|
|
29
|
+
this.turn = new turn_api_1.TurnApi(this.rest);
|
|
30
|
+
this.agents = new agents_api_1.AgentsApi(this.rest);
|
|
31
|
+
this.gateway = new gateway_client_1.GatewayClient(config);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
exports.DooverClient = DooverClient;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { DooverClientConfig } from "../http/rest-client";
|
|
2
|
+
import type { GatewayListenerMap, WebSocketSession } from "./types";
|
|
3
|
+
import type { ChannelRef, JSONValue } from "../types/common";
|
|
4
|
+
export declare class GatewayClient {
|
|
5
|
+
private readonly config;
|
|
6
|
+
private socket;
|
|
7
|
+
private session;
|
|
8
|
+
private heartbeatTimer;
|
|
9
|
+
private lastHeartbeatAt;
|
|
10
|
+
private missedHeartbeats;
|
|
11
|
+
private reconnectTimer;
|
|
12
|
+
private listeners;
|
|
13
|
+
private subscriptions;
|
|
14
|
+
constructor(config: DooverClientConfig);
|
|
15
|
+
connect(): void;
|
|
16
|
+
disconnect(code?: number, reason?: string): void;
|
|
17
|
+
on<K extends keyof GatewayListenerMap>(eventName: K, listener: GatewayListenerMap[K]): void;
|
|
18
|
+
off<K extends keyof GatewayListenerMap>(eventName: K, listener: GatewayListenerMap[K]): void;
|
|
19
|
+
subscribe(channel: ChannelRef, options?: {
|
|
20
|
+
diff_only?: boolean;
|
|
21
|
+
}): void;
|
|
22
|
+
unsubscribe(channel: ChannelRef): void;
|
|
23
|
+
syncChannel(channel: ChannelRef): void;
|
|
24
|
+
sendOneShotMessage(channel: ChannelRef, data: JSONValue): void;
|
|
25
|
+
getSession(): WebSocketSession | null;
|
|
26
|
+
getLatency(): number | null;
|
|
27
|
+
isConnected(): boolean;
|
|
28
|
+
private handleMessage;
|
|
29
|
+
private identifyOrResume;
|
|
30
|
+
private resubscribeAll;
|
|
31
|
+
private startHeartbeat;
|
|
32
|
+
private stopHeartbeat;
|
|
33
|
+
private send;
|
|
34
|
+
private emit;
|
|
35
|
+
private scheduleReconnect;
|
|
36
|
+
private channelKey;
|
|
37
|
+
}
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.GatewayClient = void 0;
|
|
4
|
+
const errors_1 = require("../http/errors");
|
|
5
|
+
const snowflake_1 = require("../utils/snowflake");
|
|
6
|
+
class GatewayClient {
|
|
7
|
+
constructor(config) {
|
|
8
|
+
this.config = config;
|
|
9
|
+
this.socket = null;
|
|
10
|
+
this.session = null;
|
|
11
|
+
this.heartbeatTimer = null;
|
|
12
|
+
this.lastHeartbeatAt = null;
|
|
13
|
+
this.missedHeartbeats = 0;
|
|
14
|
+
this.reconnectTimer = null;
|
|
15
|
+
this.listeners = {
|
|
16
|
+
ready: new Set(),
|
|
17
|
+
channelSync: new Set(),
|
|
18
|
+
messageCreate: new Set(),
|
|
19
|
+
messageUpdate: new Set(),
|
|
20
|
+
aggregateUpdate: new Set(),
|
|
21
|
+
alarmTrigger: new Set(),
|
|
22
|
+
oneShotMessage: new Set(),
|
|
23
|
+
channelSubscription: new Set(),
|
|
24
|
+
channelUnsubscription: new Set(),
|
|
25
|
+
wssError: new Set(),
|
|
26
|
+
sessionCancelled: new Set(),
|
|
27
|
+
open: new Set(),
|
|
28
|
+
close: new Set(),
|
|
29
|
+
heartbeatAck: new Set(),
|
|
30
|
+
};
|
|
31
|
+
this.subscriptions = new Map();
|
|
32
|
+
}
|
|
33
|
+
connect() {
|
|
34
|
+
if (this.socket && this.socket.readyState <= WebSocket.OPEN) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
const WebSocketImpl = this.config.webSocketImpl ?? WebSocket;
|
|
38
|
+
this.socket = new WebSocketImpl(this.config.dataWssUrl);
|
|
39
|
+
this.socket.onopen = () => this.emit("open");
|
|
40
|
+
this.socket.onclose = (event) => {
|
|
41
|
+
this.stopHeartbeat();
|
|
42
|
+
this.emit("close", event);
|
|
43
|
+
this.scheduleReconnect();
|
|
44
|
+
};
|
|
45
|
+
this.socket.onerror = () => undefined;
|
|
46
|
+
this.socket.onmessage = (event) => this.handleMessage(event.data);
|
|
47
|
+
}
|
|
48
|
+
disconnect(code, reason) {
|
|
49
|
+
this.stopHeartbeat();
|
|
50
|
+
if (this.reconnectTimer) {
|
|
51
|
+
clearTimeout(this.reconnectTimer);
|
|
52
|
+
this.reconnectTimer = null;
|
|
53
|
+
}
|
|
54
|
+
this.socket?.close(code, reason);
|
|
55
|
+
this.socket = null;
|
|
56
|
+
}
|
|
57
|
+
on(eventName, listener) {
|
|
58
|
+
this.listeners[eventName].add(listener);
|
|
59
|
+
}
|
|
60
|
+
off(eventName, listener) {
|
|
61
|
+
this.listeners[eventName].delete(listener);
|
|
62
|
+
}
|
|
63
|
+
subscribe(channel, options) {
|
|
64
|
+
this.subscriptions.set(this.channelKey(channel), {
|
|
65
|
+
channel,
|
|
66
|
+
diff_only: options?.diff_only ?? false,
|
|
67
|
+
});
|
|
68
|
+
if (!this.isConnected()) {
|
|
69
|
+
this.connect();
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
this.send({
|
|
73
|
+
op: 12,
|
|
74
|
+
d: {
|
|
75
|
+
channel,
|
|
76
|
+
organisation_id: this.config.organisationId ?? null,
|
|
77
|
+
diff_only: options?.diff_only ?? false,
|
|
78
|
+
},
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
unsubscribe(channel) {
|
|
82
|
+
this.subscriptions.delete(this.channelKey(channel));
|
|
83
|
+
if (!this.isConnected()) {
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
this.send({
|
|
87
|
+
op: 13,
|
|
88
|
+
d: {
|
|
89
|
+
channel,
|
|
90
|
+
organisation_id: this.config.organisationId ?? null,
|
|
91
|
+
},
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
syncChannel(channel) {
|
|
95
|
+
if (!this.isConnected()) {
|
|
96
|
+
this.connect();
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
this.send({
|
|
100
|
+
op: 14,
|
|
101
|
+
d: {
|
|
102
|
+
channel,
|
|
103
|
+
organisation_id: this.config.organisationId ?? null,
|
|
104
|
+
},
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
sendOneShotMessage(channel, data) {
|
|
108
|
+
if (!this.isConnected()) {
|
|
109
|
+
this.connect();
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
this.send({
|
|
113
|
+
op: 15,
|
|
114
|
+
d: {
|
|
115
|
+
channel,
|
|
116
|
+
data,
|
|
117
|
+
},
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
getSession() {
|
|
121
|
+
return this.session;
|
|
122
|
+
}
|
|
123
|
+
getLatency() {
|
|
124
|
+
return this.lastHeartbeatAt === null ? null : Date.now() - this.lastHeartbeatAt;
|
|
125
|
+
}
|
|
126
|
+
isConnected() {
|
|
127
|
+
return this.socket?.readyState === WebSocket.OPEN;
|
|
128
|
+
}
|
|
129
|
+
handleMessage(raw) {
|
|
130
|
+
const message = JSON.parse(raw);
|
|
131
|
+
if (message.op === 2) {
|
|
132
|
+
this.missedHeartbeats = 0;
|
|
133
|
+
const latency = this.lastHeartbeatAt === null ? null : Date.now() - this.lastHeartbeatAt;
|
|
134
|
+
this.emit("heartbeatAck", latency);
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
if (message.op === 3) {
|
|
138
|
+
this.session = null;
|
|
139
|
+
this.emit("sessionCancelled");
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
if (message.op !== 0 || !message.t) {
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
switch (message.t) {
|
|
146
|
+
case "Hello":
|
|
147
|
+
this.identifyOrResume();
|
|
148
|
+
this.startHeartbeat();
|
|
149
|
+
break;
|
|
150
|
+
case "Ready":
|
|
151
|
+
this.session = message.d;
|
|
152
|
+
this.emit("ready", message.d);
|
|
153
|
+
this.resubscribeAll();
|
|
154
|
+
break;
|
|
155
|
+
case "ChannelSync":
|
|
156
|
+
this.emit("channelSync", message.d);
|
|
157
|
+
break;
|
|
158
|
+
case "MessageCreate":
|
|
159
|
+
this.emit("messageCreate", (0, snowflake_1.addTimestampToMessage)(message.d));
|
|
160
|
+
break;
|
|
161
|
+
case "MessageUpdate":
|
|
162
|
+
this.emit("messageUpdate", (0, snowflake_1.addTimestampToMessage)(message.d));
|
|
163
|
+
break;
|
|
164
|
+
case "AggregateUpdate":
|
|
165
|
+
this.emit("aggregateUpdate", message.d);
|
|
166
|
+
break;
|
|
167
|
+
case "AlarmTrigger":
|
|
168
|
+
this.emit("alarmTrigger", message.d);
|
|
169
|
+
break;
|
|
170
|
+
case "OneShotMessage":
|
|
171
|
+
this.emit("oneShotMessage", message.d);
|
|
172
|
+
break;
|
|
173
|
+
case "ChannelSubscription":
|
|
174
|
+
this.emit("channelSubscription", message.d);
|
|
175
|
+
break;
|
|
176
|
+
case "ChannelUnsubscription":
|
|
177
|
+
this.emit("channelUnsubscription", message.d);
|
|
178
|
+
break;
|
|
179
|
+
case "WSSErrorEvent":
|
|
180
|
+
this.emit("wssError", message.d);
|
|
181
|
+
break;
|
|
182
|
+
default:
|
|
183
|
+
break;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
identifyOrResume() {
|
|
187
|
+
if (this.session) {
|
|
188
|
+
this.send({
|
|
189
|
+
op: 11,
|
|
190
|
+
d: {
|
|
191
|
+
session_id: this.session.session_id,
|
|
192
|
+
session_token: this.session.session_token,
|
|
193
|
+
default_connection: false,
|
|
194
|
+
},
|
|
195
|
+
});
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
this.send({
|
|
199
|
+
op: 10,
|
|
200
|
+
d: {
|
|
201
|
+
default_connection: false,
|
|
202
|
+
organisation_id: this.config.organisationId ?? null,
|
|
203
|
+
sharing_mode: this.config.sharing ?? "internal",
|
|
204
|
+
},
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
resubscribeAll() {
|
|
208
|
+
for (const entry of this.subscriptions.values()) {
|
|
209
|
+
this.subscribe(entry.channel, { diff_only: entry.diff_only });
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
startHeartbeat() {
|
|
213
|
+
this.stopHeartbeat();
|
|
214
|
+
this.heartbeatTimer = setInterval(() => {
|
|
215
|
+
if (!this.socket || this.socket.readyState !== WebSocket.OPEN) {
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
this.lastHeartbeatAt = Date.now();
|
|
219
|
+
this.missedHeartbeats += 1;
|
|
220
|
+
if (this.missedHeartbeats > 3) {
|
|
221
|
+
this.socket.close(4000, "Missed heartbeats");
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
this.send({ op: 1, d: {} });
|
|
225
|
+
}, 20000);
|
|
226
|
+
}
|
|
227
|
+
stopHeartbeat() {
|
|
228
|
+
if (this.heartbeatTimer) {
|
|
229
|
+
clearInterval(this.heartbeatTimer);
|
|
230
|
+
this.heartbeatTimer = null;
|
|
231
|
+
}
|
|
232
|
+
this.missedHeartbeats = 0;
|
|
233
|
+
}
|
|
234
|
+
send(payload) {
|
|
235
|
+
if (!this.socket || this.socket.readyState !== WebSocket.OPEN) {
|
|
236
|
+
throw new errors_1.DooverGatewayError("WebSocket is not connected");
|
|
237
|
+
}
|
|
238
|
+
this.socket.send(JSON.stringify(payload));
|
|
239
|
+
}
|
|
240
|
+
emit(eventName, ...args) {
|
|
241
|
+
this.listeners[eventName].forEach((listener) => {
|
|
242
|
+
listener(...args);
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
scheduleReconnect() {
|
|
246
|
+
if (this.reconnectTimer) {
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
this.reconnectTimer = setTimeout(() => {
|
|
250
|
+
this.reconnectTimer = null;
|
|
251
|
+
this.connect();
|
|
252
|
+
}, 1000);
|
|
253
|
+
}
|
|
254
|
+
channelKey(channel) {
|
|
255
|
+
return `${channel.agent_id}/${channel.name}`;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
exports.GatewayClient = GatewayClient;
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import type { Aggregate, Alarm, ChannelRef, JSONValue, MessageStructure } from "../types/common";
|
|
2
|
+
export interface WebSocketSubscription {
|
|
3
|
+
channel: ChannelRef;
|
|
4
|
+
organisation_id?: string | null;
|
|
5
|
+
diff_only: boolean;
|
|
6
|
+
}
|
|
7
|
+
export interface WebSocketSession {
|
|
8
|
+
session_id: string;
|
|
9
|
+
session_token: string;
|
|
10
|
+
subscriptions: WebSocketSubscription[];
|
|
11
|
+
}
|
|
12
|
+
export interface GatewayMessageBase {
|
|
13
|
+
op: number;
|
|
14
|
+
d: unknown;
|
|
15
|
+
s?: number | null;
|
|
16
|
+
t?: string;
|
|
17
|
+
}
|
|
18
|
+
export interface GatewayHello extends GatewayMessageBase {
|
|
19
|
+
op: 0;
|
|
20
|
+
t: "Hello";
|
|
21
|
+
d: Record<string, never>;
|
|
22
|
+
}
|
|
23
|
+
export interface GatewayReady extends GatewayMessageBase {
|
|
24
|
+
op: 0;
|
|
25
|
+
t: "Ready";
|
|
26
|
+
d: WebSocketSession;
|
|
27
|
+
}
|
|
28
|
+
export interface GatewayChannelSync extends GatewayMessageBase {
|
|
29
|
+
op: 0;
|
|
30
|
+
t: "ChannelSync";
|
|
31
|
+
d: {
|
|
32
|
+
channel: ChannelRef;
|
|
33
|
+
aggregate: Aggregate;
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
export interface GatewayMessageCreate extends GatewayMessageBase {
|
|
37
|
+
op: 0;
|
|
38
|
+
t: "MessageCreate";
|
|
39
|
+
d: Omit<MessageStructure, "timestamp">;
|
|
40
|
+
}
|
|
41
|
+
export interface GatewayMessageUpdate extends GatewayMessageBase {
|
|
42
|
+
op: 0;
|
|
43
|
+
t: "MessageUpdate";
|
|
44
|
+
d: Omit<MessageStructure, "timestamp"> & {
|
|
45
|
+
request_data: JSONValue;
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
export interface GatewayAggregateUpdate extends GatewayMessageBase {
|
|
49
|
+
op: 0;
|
|
50
|
+
t: "AggregateUpdate";
|
|
51
|
+
d: {
|
|
52
|
+
author_id: string;
|
|
53
|
+
channel: ChannelRef;
|
|
54
|
+
aggregate: Aggregate;
|
|
55
|
+
request_data: Aggregate;
|
|
56
|
+
organisation_id: string;
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
export interface GatewayAlarmTrigger extends GatewayMessageBase {
|
|
60
|
+
op: 0;
|
|
61
|
+
t: "AlarmTrigger";
|
|
62
|
+
d: {
|
|
63
|
+
channel: ChannelRef;
|
|
64
|
+
alarm: Alarm;
|
|
65
|
+
old_state: Alarm["state"];
|
|
66
|
+
new_state: Alarm["state"];
|
|
67
|
+
aggregate: Aggregate;
|
|
68
|
+
request_data: Aggregate;
|
|
69
|
+
organisation_id: string;
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
export interface GatewayOneShotMessage extends GatewayMessageBase {
|
|
73
|
+
op: 0;
|
|
74
|
+
t: "OneShotMessage";
|
|
75
|
+
d: {
|
|
76
|
+
id: string | null;
|
|
77
|
+
author_id: string;
|
|
78
|
+
channel: ChannelRef;
|
|
79
|
+
data: JSONValue;
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
export interface GatewayChannelSubscription extends GatewayMessageBase {
|
|
83
|
+
op: 0;
|
|
84
|
+
t: "ChannelSubscription";
|
|
85
|
+
d: {
|
|
86
|
+
agent_id: string;
|
|
87
|
+
channel: ChannelRef;
|
|
88
|
+
session_id: string;
|
|
89
|
+
default_session: boolean;
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
export interface GatewayChannelUnsubscription extends GatewayMessageBase {
|
|
93
|
+
op: 0;
|
|
94
|
+
t: "ChannelUnsubscription";
|
|
95
|
+
d: {
|
|
96
|
+
agent_id: string;
|
|
97
|
+
channel: ChannelRef;
|
|
98
|
+
session_id: string;
|
|
99
|
+
default_session: boolean;
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
export interface GatewayWSSErrorEvent extends GatewayMessageBase {
|
|
103
|
+
op: 0;
|
|
104
|
+
t: "WSSErrorEvent";
|
|
105
|
+
d: {
|
|
106
|
+
message: string;
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
export interface GatewayHeartbeatAck extends GatewayMessageBase {
|
|
110
|
+
op: 2;
|
|
111
|
+
d: Record<string, never>;
|
|
112
|
+
}
|
|
113
|
+
export interface GatewaySessionCancelled extends GatewayMessageBase {
|
|
114
|
+
op: 3;
|
|
115
|
+
d: Record<string, never>;
|
|
116
|
+
}
|
|
117
|
+
export type GatewayInboundMessage = GatewayHello | GatewayReady | GatewayChannelSync | GatewayMessageCreate | GatewayMessageUpdate | GatewayAggregateUpdate | GatewayAlarmTrigger | GatewayOneShotMessage | GatewayChannelSubscription | GatewayChannelUnsubscription | GatewayWSSErrorEvent | GatewayHeartbeatAck | GatewaySessionCancelled;
|
|
118
|
+
export interface GatewayListenerMap {
|
|
119
|
+
ready: (event: GatewayReady["d"]) => void;
|
|
120
|
+
channelSync: (event: GatewayChannelSync["d"]) => void;
|
|
121
|
+
messageCreate: (event: MessageStructure) => void;
|
|
122
|
+
messageUpdate: (event: GatewayMessageUpdate["d"] & {
|
|
123
|
+
timestamp: number;
|
|
124
|
+
}) => void;
|
|
125
|
+
aggregateUpdate: (event: GatewayAggregateUpdate["d"]) => void;
|
|
126
|
+
alarmTrigger: (event: GatewayAlarmTrigger["d"]) => void;
|
|
127
|
+
oneShotMessage: (event: GatewayOneShotMessage["d"]) => void;
|
|
128
|
+
channelSubscription: (event: GatewayChannelSubscription["d"]) => void;
|
|
129
|
+
channelUnsubscription: (event: GatewayChannelUnsubscription["d"]) => void;
|
|
130
|
+
wssError: (event: GatewayWSSErrorEvent["d"]) => void;
|
|
131
|
+
sessionCancelled: () => void;
|
|
132
|
+
open: () => void;
|
|
133
|
+
close: (event: CloseEvent) => void;
|
|
134
|
+
heartbeatAck: (latencyMs: number | null) => void;
|
|
135
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export declare class DooverApiError extends Error {
|
|
2
|
+
status: number;
|
|
3
|
+
body: unknown;
|
|
4
|
+
url: string;
|
|
5
|
+
method: string;
|
|
6
|
+
constructor(params: {
|
|
7
|
+
status: number;
|
|
8
|
+
body: unknown;
|
|
9
|
+
url: string;
|
|
10
|
+
method: string;
|
|
11
|
+
message?: string;
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
export declare class DooverValidationError extends Error {
|
|
15
|
+
constructor(message: string);
|
|
16
|
+
}
|
|
17
|
+
export declare class DooverGatewayError extends Error {
|
|
18
|
+
constructor(message: string);
|
|
19
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DooverGatewayError = exports.DooverValidationError = exports.DooverApiError = void 0;
|
|
4
|
+
class DooverApiError extends Error {
|
|
5
|
+
constructor(params) {
|
|
6
|
+
super(params.message ?? `Request failed with status ${params.status}`);
|
|
7
|
+
this.name = "DooverApiError";
|
|
8
|
+
this.status = params.status;
|
|
9
|
+
this.body = params.body;
|
|
10
|
+
this.url = params.url;
|
|
11
|
+
this.method = params.method;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
exports.DooverApiError = DooverApiError;
|
|
15
|
+
class DooverValidationError extends Error {
|
|
16
|
+
constructor(message) {
|
|
17
|
+
super(message);
|
|
18
|
+
this.name = "DooverValidationError";
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
exports.DooverValidationError = DooverValidationError;
|
|
22
|
+
class DooverGatewayError extends Error {
|
|
23
|
+
constructor(message) {
|
|
24
|
+
super(message);
|
|
25
|
+
this.name = "DooverGatewayError";
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
exports.DooverGatewayError = DooverGatewayError;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
export interface DooverClientConfig {
|
|
2
|
+
dataRestUrl: string;
|
|
3
|
+
controlApiUrl: string;
|
|
4
|
+
dataWssUrl: string;
|
|
5
|
+
organisationId?: string | null;
|
|
6
|
+
sharing?: "internal" | "external" | "none";
|
|
7
|
+
impersonateUserStorageKey?: string;
|
|
8
|
+
fetchImpl?: typeof fetch;
|
|
9
|
+
webSocketImpl?: typeof WebSocket;
|
|
10
|
+
}
|
|
11
|
+
export interface RequestOptions {
|
|
12
|
+
baseUrl?: string;
|
|
13
|
+
path: string;
|
|
14
|
+
method?: string;
|
|
15
|
+
query?: QueryParams;
|
|
16
|
+
body?: BodyInit | object | null;
|
|
17
|
+
headers?: HeadersInit;
|
|
18
|
+
}
|
|
19
|
+
type QueryValue = string | number | boolean | null | undefined | Array<string | number | boolean>;
|
|
20
|
+
type QueryParams = Record<string, QueryValue> | object;
|
|
21
|
+
export declare class RestClient {
|
|
22
|
+
readonly config: Required<Pick<DooverClientConfig, "dataRestUrl" | "controlApiUrl" | "dataWssUrl" | "sharing" | "impersonateUserStorageKey">> & Pick<DooverClientConfig, "organisationId" | "fetchImpl" | "webSocketImpl">;
|
|
23
|
+
constructor(config: DooverClientConfig);
|
|
24
|
+
request<T>(options: RequestOptions): Promise<T>;
|
|
25
|
+
get<T>(path: string, query?: RequestOptions["query"], baseUrl?: string): Promise<T>;
|
|
26
|
+
post<T>(path: string, body?: RequestOptions["body"], query?: RequestOptions["query"], baseUrl?: string): Promise<T>;
|
|
27
|
+
put<T>(path: string, body?: RequestOptions["body"], query?: RequestOptions["query"], baseUrl?: string): Promise<T>;
|
|
28
|
+
patch<T>(path: string, body?: RequestOptions["body"], query?: RequestOptions["query"], baseUrl?: string): Promise<T>;
|
|
29
|
+
delete<T>(path: string, query?: RequestOptions["query"], baseUrl?: string): Promise<T>;
|
|
30
|
+
private buildUrl;
|
|
31
|
+
private normalizeBody;
|
|
32
|
+
private parseResponse;
|
|
33
|
+
private getImpersonatedUserId;
|
|
34
|
+
}
|
|
35
|
+
export {};
|