airdcpp-apisocket 3.0.0-beta.2 → 3.0.0-beta.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ApiConstants.d.ts +6 -0
- package/dist/ApiConstants.js +6 -0
- package/dist/ApiConstants.js.map +1 -0
- package/dist/NodeSocket.d.ts +4 -0
- package/dist/NodeSocket.js +5 -0
- package/dist/NodeSocket.js.map +1 -0
- package/dist/Promise.d.ts +8 -0
- package/dist/Promise.js +25 -0
- package/dist/Promise.js.map +1 -0
- package/dist/PublicHelpers.d.ts +2 -0
- package/dist/PublicHelpers.js +108 -0
- package/dist/PublicHelpers.js.map +1 -0
- package/dist/SocketBase.d.ts +4 -0
- package/dist/SocketBase.js +347 -0
- package/dist/SocketBase.js.map +1 -0
- package/dist/SocketLogger.d.ts +9 -0
- package/dist/SocketLogger.js +69 -0
- package/dist/SocketLogger.js.map +1 -0
- package/dist/SocketRequestHandler.d.ts +14 -0
- package/dist/SocketRequestHandler.js +164 -0
- package/dist/SocketRequestHandler.js.map +1 -0
- package/dist/SocketSubscriptionHandler.d.ts +11 -0
- package/dist/SocketSubscriptionHandler.js +157 -0
- package/dist/SocketSubscriptionHandler.js.map +1 -0
- package/dist/tests/helpers.d.ts +57 -0
- package/dist/tests/helpers.js +127 -0
- package/dist/tests/helpers.js.map +1 -0
- package/dist/types/api.d.ts +30 -0
- package/dist/types/api.js +3 -0
- package/dist/types/api.js.map +1 -0
- package/dist/types/api_internal.d.ts +24 -0
- package/dist/types/api_internal.js +2 -0
- package/dist/types/api_internal.js.map +1 -0
- package/dist/types/index.d.ts +7 -0
- package/dist/types/index.js +8 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/logger.d.ts +6 -0
- package/dist/types/logger.js +2 -0
- package/dist/types/logger.js.map +1 -0
- package/dist/types/options.d.ts +31 -0
- package/dist/types/options.js +3 -0
- package/dist/types/options.js.map +1 -0
- package/dist/types/public_helpers.d.ts +28 -0
- package/dist/types/public_helpers.js +2 -0
- package/dist/types/public_helpers.js.map +1 -0
- package/dist/types/public_helpers_internal.d.ts +27 -0
- package/dist/types/public_helpers_internal.js +2 -0
- package/dist/types/public_helpers_internal.js.map +1 -0
- package/dist/types/requests.d.ts +14 -0
- package/dist/types/requests.js +2 -0
- package/dist/types/requests.js.map +1 -0
- package/dist/types/socket.d.ts +23 -0
- package/dist/types/socket.js +2 -0
- package/dist/types/socket.js.map +1 -0
- package/dist/types/subscriptions.d.ts +20 -0
- package/dist/types/subscriptions.js +2 -0
- package/dist/types/subscriptions.js.map +1 -0
- package/dist/utils.d.ts +2 -0
- package/dist/utils.js +12 -0
- package/dist/utils.js.map +1 -0
- package/dist-es/tests/helpers.d.ts +74 -0
- package/dist-es/tests/helpers.js +134 -0
- package/dist-es/tests/helpers.js.map +1 -0
- package/dist-es/types/public_helpers.d.ts +1 -1
- package/dist-es/types/public_helpers_internal.d.ts +2 -2
- package/package.json +6 -3
- package/src/tests/Socket.test.ts +14 -12
- package/src/tests/helpers.ts +28 -13
- package/src/types/public_helpers.ts +1 -1
- package/src/types/public_helpers_internal.ts +2 -2
- package/tsconfig.json +1 -1
@@ -0,0 +1,14 @@
|
|
1
|
+
import * as API from './types/api.js';
|
2
|
+
import * as APIInternal from './types/api_internal.js';
|
3
|
+
import * as Options from './types/options.js';
|
4
|
+
import * as Socket from './types/socket.js';
|
5
|
+
import { Logger } from './types/logger.js';
|
6
|
+
import { SocketRequestMethods } from './types/requests.js';
|
7
|
+
declare const SocketRequestHandler: (socket: () => Socket.APISocket, logger: Logger, { requestTimeout, ignoredRequestPaths }: Options.SocketRequestOptions) => {
|
8
|
+
socket: SocketRequestMethods;
|
9
|
+
onSocketConnected(): void;
|
10
|
+
onSocketDisconnected(): void;
|
11
|
+
handleMessage(messageObj: APIInternal.RequestSuccessResponse | APIInternal.RequestErrorResponse): void;
|
12
|
+
postAuthenticate(path: string, data: API.TokenAuthenticationData | API.CredentialsAuthenticationData | API.RefreshTokenAuthenticationData): Promise<any>;
|
13
|
+
};
|
14
|
+
export default SocketRequestHandler;
|
@@ -0,0 +1,164 @@
|
|
1
|
+
import chalk from 'chalk';
|
2
|
+
import invariant from 'invariant';
|
3
|
+
import Promise from './Promise.js';
|
4
|
+
import { eventIgnored } from './utils.js';
|
5
|
+
const SocketRequestHandler = (socket, logger, { requestTimeout = 30, ignoredRequestPaths }) => {
|
6
|
+
let callbacks = {};
|
7
|
+
let currentCallbackId = 0;
|
8
|
+
let timeoutReportInterval;
|
9
|
+
// Internal
|
10
|
+
// This creates a new callback ID for a request
|
11
|
+
const getCallbackId = () => {
|
12
|
+
if (currentCallbackId > 100000) {
|
13
|
+
currentCallbackId = 0;
|
14
|
+
}
|
15
|
+
currentCallbackId += 1;
|
16
|
+
return currentCallbackId;
|
17
|
+
};
|
18
|
+
const filterPassword = (data) => {
|
19
|
+
if (!data || !data.hasOwnProperty('password')) {
|
20
|
+
return data;
|
21
|
+
}
|
22
|
+
return {
|
23
|
+
...data,
|
24
|
+
password: '(hidden)',
|
25
|
+
};
|
26
|
+
};
|
27
|
+
const sendRequest = (method, path, data, authenticating = false) => {
|
28
|
+
// Pre-checks
|
29
|
+
if (!authenticating && !socket().isConnected()) {
|
30
|
+
logger.warn(`Attempting to send request on a non-authenticated socket: ${path}`);
|
31
|
+
return Promise.reject('Not authorized');
|
32
|
+
}
|
33
|
+
if (!socket().nativeSocket) {
|
34
|
+
logger.warn(`Attempting to send request without a socket: ${path}`);
|
35
|
+
return Promise.reject('No socket');
|
36
|
+
}
|
37
|
+
const callbackId = getCallbackId();
|
38
|
+
// Reporting
|
39
|
+
invariant(path, 'Attempting socket request without a path');
|
40
|
+
const ignored = eventIgnored(path, ignoredRequestPaths);
|
41
|
+
if (!ignored) {
|
42
|
+
logger.verbose(chalk.white.bold(callbackId.toString()), method, path, data ? filterPassword(data) : '(no data)');
|
43
|
+
}
|
44
|
+
// Callback
|
45
|
+
const resolver = Promise.pending();
|
46
|
+
callbacks[callbackId.toString()] = {
|
47
|
+
time: Date.now(),
|
48
|
+
resolver,
|
49
|
+
ignored,
|
50
|
+
};
|
51
|
+
// Actual request
|
52
|
+
const request = {
|
53
|
+
path,
|
54
|
+
method,
|
55
|
+
data,
|
56
|
+
callback_id: callbackId,
|
57
|
+
};
|
58
|
+
socket().nativeSocket.send(JSON.stringify(request));
|
59
|
+
return resolver.promise;
|
60
|
+
};
|
61
|
+
// Report timed out requests
|
62
|
+
// This is more about spotting backend issues, such as frozen threads and dropped responses
|
63
|
+
// The socket itself should handle actual connection issues
|
64
|
+
const reportTimeouts = () => {
|
65
|
+
const now = Date.now();
|
66
|
+
Object.keys(callbacks).forEach(callbackId => {
|
67
|
+
const request = callbacks[callbackId];
|
68
|
+
if (request.time + (requestTimeout * 1000) < now) {
|
69
|
+
logger.warn(`Request ${callbackId} timed out`);
|
70
|
+
}
|
71
|
+
});
|
72
|
+
};
|
73
|
+
const cancelPendingRequests = (message = 'Request cancelled') => {
|
74
|
+
Object.keys(callbacks)
|
75
|
+
.forEach(id => {
|
76
|
+
logger.verbose(`Canceling a pending request ${id} (${message})`);
|
77
|
+
const cb = callbacks[id];
|
78
|
+
cb.resolver.reject(message);
|
79
|
+
});
|
80
|
+
callbacks = {};
|
81
|
+
};
|
82
|
+
// Public
|
83
|
+
const RequestsPublic = {
|
84
|
+
put: (path, data) => {
|
85
|
+
return sendRequest('PUT', path, data);
|
86
|
+
},
|
87
|
+
patch: (path, data) => {
|
88
|
+
return sendRequest('PATCH', path, data);
|
89
|
+
},
|
90
|
+
post: (path, data) => {
|
91
|
+
return sendRequest('POST', path, data);
|
92
|
+
},
|
93
|
+
delete: (path) => {
|
94
|
+
//invariant(!data, 'No data is allowed for delete command');
|
95
|
+
return sendRequest('DELETE', path);
|
96
|
+
},
|
97
|
+
get: (path) => {
|
98
|
+
//invariant(!data, 'No data is allowed for get command');
|
99
|
+
return sendRequest('GET', path);
|
100
|
+
},
|
101
|
+
getPendingRequestCount: () => {
|
102
|
+
return Object.keys(callbacks).length;
|
103
|
+
},
|
104
|
+
};
|
105
|
+
Object.assign(RequestsPublic, {
|
106
|
+
reportRequestTimeouts: reportTimeouts, // internal method for testing
|
107
|
+
});
|
108
|
+
const formatFieldError = (error) => {
|
109
|
+
return error.field && error.code ? `${error.field} (${error.code})` : '';
|
110
|
+
};
|
111
|
+
// Shared for the socket
|
112
|
+
const RequestsInternal = {
|
113
|
+
onSocketConnected() {
|
114
|
+
timeoutReportInterval = setInterval(reportTimeouts, 30000);
|
115
|
+
},
|
116
|
+
onSocketDisconnected() {
|
117
|
+
// Clear callbacks
|
118
|
+
cancelPendingRequests('Socket disconnected');
|
119
|
+
clearTimeout(timeoutReportInterval);
|
120
|
+
},
|
121
|
+
handleMessage(messageObj) {
|
122
|
+
const id = messageObj.callback_id;
|
123
|
+
if (!callbacks.hasOwnProperty(id)) {
|
124
|
+
logger.warn('No pending request for an API response', id, messageObj);
|
125
|
+
return;
|
126
|
+
}
|
127
|
+
if (messageObj.code >= 200 && messageObj.code <= 204) {
|
128
|
+
const { data } = messageObj;
|
129
|
+
if (!callbacks[id].ignored) {
|
130
|
+
logger.verbose(chalk.green(id.toString()), 'SUCCEEDED', data ? data : '(no data)');
|
131
|
+
}
|
132
|
+
callbacks[id].resolver.resolve(data);
|
133
|
+
}
|
134
|
+
else {
|
135
|
+
const errorMessageObj = messageObj;
|
136
|
+
if (!errorMessageObj.error) {
|
137
|
+
// API should always return an error message but this isn't always the case
|
138
|
+
// (e.g. https://github.com/airdcpp/airdcpp-windows/commit/596b31a9c8c4e72f6c9279972a40ea30f10798c4)
|
139
|
+
logger.warn('Error message missing from the response (this is an API bug that should be reported)', id, messageObj);
|
140
|
+
}
|
141
|
+
const { code } = errorMessageObj;
|
142
|
+
const error = errorMessageObj.error || {
|
143
|
+
message: '(no error description)'
|
144
|
+
};
|
145
|
+
logger.warn(id, code, error.message, formatFieldError(error));
|
146
|
+
callbacks[id].resolver.reject({
|
147
|
+
message: error.message,
|
148
|
+
code,
|
149
|
+
json: error
|
150
|
+
});
|
151
|
+
}
|
152
|
+
delete callbacks[id];
|
153
|
+
},
|
154
|
+
postAuthenticate(path, data) {
|
155
|
+
return sendRequest('POST', path, data, true);
|
156
|
+
},
|
157
|
+
};
|
158
|
+
return {
|
159
|
+
...RequestsInternal,
|
160
|
+
socket: RequestsPublic,
|
161
|
+
};
|
162
|
+
};
|
163
|
+
export default SocketRequestHandler;
|
164
|
+
//# sourceMappingURL=SocketRequestHandler.js.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"SocketRequestHandler.js","sourceRoot":"","sources":["../src/SocketRequestHandler.ts"],"names":[],"mappings":"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"}
|
@@ -0,0 +1,11 @@
|
|
1
|
+
import { Logger } from './types/logger.js';
|
2
|
+
import * as APIInternal from './types/api_internal.js';
|
3
|
+
import * as Options from './types/options.js';
|
4
|
+
import * as Socket from './types/socket.js';
|
5
|
+
import * as Subscriptions from './types/subscriptions.js';
|
6
|
+
declare const SocketSubscriptionHandler: (socket: () => Socket.APISocket, logger: Logger, { ignoredListenerEvents }: Options.SocketSubscriptionOptions) => {
|
7
|
+
socket: Subscriptions.SocketSubscriptions;
|
8
|
+
onSocketDisconnected(): void;
|
9
|
+
handleMessage(message: APIInternal.IncomingSubscriptionEvent): void;
|
10
|
+
};
|
11
|
+
export default SocketSubscriptionHandler;
|
@@ -0,0 +1,157 @@
|
|
1
|
+
import invariant from 'invariant';
|
2
|
+
import { EventEmitter } from 'events';
|
3
|
+
import { eventIgnored } from './utils.js';
|
4
|
+
import Promise from './Promise.js';
|
5
|
+
const SocketSubscriptionHandler = (socket, logger, { ignoredListenerEvents = [] }) => {
|
6
|
+
// Internal
|
7
|
+
const getEmitId = (event, id) => {
|
8
|
+
invariant(id !== 0, 'Entity ID "0" is not allowed');
|
9
|
+
return id ? (event + id) : event;
|
10
|
+
};
|
11
|
+
const getSubscriptionUrl = (moduleUrl, id, event) => {
|
12
|
+
if (id) {
|
13
|
+
return `${moduleUrl}/${id}/listeners/${event}`;
|
14
|
+
}
|
15
|
+
return `${moduleUrl}/listeners/${event}`;
|
16
|
+
};
|
17
|
+
let subscriptions = {};
|
18
|
+
const emitter = new EventEmitter();
|
19
|
+
// Subscriptions pending to be added in the API
|
20
|
+
const pendingSubscriptions = {};
|
21
|
+
const removeSocketListener = (subscriptionUrl, subscriptionId, callback, sendApi) => {
|
22
|
+
if (!socket().isConnected()) {
|
23
|
+
return;
|
24
|
+
}
|
25
|
+
subscriptions[subscriptionId]--;
|
26
|
+
emitter.removeListener(subscriptionId, callback);
|
27
|
+
if (subscriptions[subscriptionId] === 0) {
|
28
|
+
if (sendApi && socket().isConnected()) {
|
29
|
+
socket().delete(subscriptionUrl)
|
30
|
+
.catch((error) => {
|
31
|
+
logger.error('Failed to remove socket listener', subscriptionUrl, error);
|
32
|
+
});
|
33
|
+
}
|
34
|
+
delete subscriptions[subscriptionId];
|
35
|
+
}
|
36
|
+
};
|
37
|
+
const removeLocalListener = (subscriptionId, callback) => {
|
38
|
+
emitter.removeListener(subscriptionId, callback);
|
39
|
+
};
|
40
|
+
const handleHookAction = (subscriptionUrl, callback, data, completionId) => {
|
41
|
+
callback(data, completionData => {
|
42
|
+
socket().post(`${subscriptionUrl}/${completionId}/resolve`, completionData)
|
43
|
+
.catch((error) => logger.error('Failed to complete hook action', subscriptionUrl, error));
|
44
|
+
}, (rejectId, rejectMessage) => {
|
45
|
+
socket().post(`${subscriptionUrl}/${completionId}/reject`, {
|
46
|
+
reject_id: rejectId,
|
47
|
+
message: rejectMessage,
|
48
|
+
}).catch(error => logger.error('Failed to complete failed hook action', subscriptionUrl, error));
|
49
|
+
});
|
50
|
+
};
|
51
|
+
const onSubscriptionAddSucceeded = (subscriptionId) => {
|
52
|
+
const pending = pendingSubscriptions[subscriptionId];
|
53
|
+
pending.forEach(pendingItem => pendingItem.resolver.resolve(pendingItem.removeHandler));
|
54
|
+
subscriptions[subscriptionId] = pending.length;
|
55
|
+
delete pendingSubscriptions[subscriptionId];
|
56
|
+
};
|
57
|
+
const onSubscriptionAddFailed = (subscriptionId, error) => {
|
58
|
+
const pending = pendingSubscriptions[subscriptionId];
|
59
|
+
pending.forEach(pendingItem => pendingItem.resolver.reject(error));
|
60
|
+
delete pendingSubscriptions[subscriptionId];
|
61
|
+
};
|
62
|
+
const addPendingEntry = (subscriptionUrl, subscriptionId, callback, data) => {
|
63
|
+
const removeHandler = (sendApi = true) => removeSocketListener(subscriptionUrl, subscriptionId, callback, sendApi);
|
64
|
+
if (!subscriptions[subscriptionId]) {
|
65
|
+
if (!pendingSubscriptions[subscriptionId]) {
|
66
|
+
pendingSubscriptions[subscriptionId] = [];
|
67
|
+
socket()
|
68
|
+
.post(subscriptionUrl, data)
|
69
|
+
.then(onSubscriptionAddSucceeded.bind(SocketSubscriptionHandler, subscriptionId), onSubscriptionAddFailed.bind(SocketSubscriptionHandler, subscriptionId));
|
70
|
+
}
|
71
|
+
const resolver = Promise.pending();
|
72
|
+
pendingSubscriptions[subscriptionId].push({
|
73
|
+
resolver,
|
74
|
+
removeHandler
|
75
|
+
});
|
76
|
+
return resolver.promise;
|
77
|
+
}
|
78
|
+
subscriptions[subscriptionId]++;
|
79
|
+
return Promise.resolve(removeHandler);
|
80
|
+
};
|
81
|
+
const getTotalEmitterSubscriptionCount = () => {
|
82
|
+
return Object.keys(subscriptions)
|
83
|
+
.reduce((reduced, name) => emitter.listenerCount(name) + reduced, 0);
|
84
|
+
};
|
85
|
+
// Public
|
86
|
+
// Listen to a specific event without sending subscription to the server
|
87
|
+
const SocketSubscriptionsPublic = {
|
88
|
+
addViewUpdateListener: (viewName, callback, entityId) => {
|
89
|
+
const subscriptionId = getEmitId(`${viewName}_updated`, entityId);
|
90
|
+
emitter.on(subscriptionId, callback);
|
91
|
+
return () => removeLocalListener(subscriptionId, callback);
|
92
|
+
},
|
93
|
+
// Listen to a specific event and manage the API subscription automatically
|
94
|
+
addListener: (apiModule, event, callback, entityId) => {
|
95
|
+
if (!socket().isConnected()) {
|
96
|
+
throw 'Listeners can be added only for a connected socket';
|
97
|
+
}
|
98
|
+
invariant(apiModule.indexOf('/') === -1, 'The first argument should only contain the API section without any path tokens (entity ID should be supplied separately)');
|
99
|
+
const subscriptionId = getEmitId(event, entityId);
|
100
|
+
const subscriptionUrl = getSubscriptionUrl(apiModule, entityId, event);
|
101
|
+
emitter.on(subscriptionId, callback);
|
102
|
+
return addPendingEntry(subscriptionUrl, subscriptionId, callback);
|
103
|
+
},
|
104
|
+
hasListeners: () => {
|
105
|
+
return Object.keys(subscriptions).length > 0 || getTotalEmitterSubscriptionCount() > 0;
|
106
|
+
},
|
107
|
+
addHook: (apiModule, event, callback, subscriberInfo) => {
|
108
|
+
if (!socket().isConnected()) {
|
109
|
+
throw 'Hooks can be added only for a connected socket';
|
110
|
+
}
|
111
|
+
invariant(apiModule.indexOf('/') === -1, 'The first argument should only contain the API section without any path tokens');
|
112
|
+
const subscriptionId = event;
|
113
|
+
if (subscriptions[subscriptionId] || pendingSubscriptions[subscriptionId]) {
|
114
|
+
throw 'Hook exists';
|
115
|
+
}
|
116
|
+
const subscriptionUrl = `${apiModule}/hooks/${event}`;
|
117
|
+
callback = handleHookAction.bind(SocketSubscriptionHandler, subscriptionUrl, callback);
|
118
|
+
emitter.on(subscriptionId, callback);
|
119
|
+
return addPendingEntry(subscriptionUrl, subscriptionId, callback, subscriberInfo);
|
120
|
+
},
|
121
|
+
getPendingSubscriptionCount: () => {
|
122
|
+
return Object.keys(pendingSubscriptions).length;
|
123
|
+
},
|
124
|
+
};
|
125
|
+
// For the socket
|
126
|
+
const SocketSubscriptionsInternal = {
|
127
|
+
onSocketDisconnected() {
|
128
|
+
emitter.removeAllListeners();
|
129
|
+
subscriptions = {};
|
130
|
+
},
|
131
|
+
handleMessage(message) {
|
132
|
+
const ignored = eventIgnored(message.event, ignoredListenerEvents);
|
133
|
+
if (message.completion_id) {
|
134
|
+
if (!ignored) {
|
135
|
+
logger.verbose(message.event, `(completion id ${message.completion_id})`, message.data);
|
136
|
+
}
|
137
|
+
emitter.emit(message.event, message.data, message.completion_id);
|
138
|
+
}
|
139
|
+
else {
|
140
|
+
if (!ignored) {
|
141
|
+
logger.verbose(message.event, message.id ? `(entity ${message.id})` : '(no entity)', message.data);
|
142
|
+
}
|
143
|
+
if (message.id) {
|
144
|
+
// There can be subscribers for a single entity or for all events of this type... emit for both
|
145
|
+
emitter.emit(getEmitId(message.event, message.id), message.data, message.id);
|
146
|
+
}
|
147
|
+
emitter.emit(message.event, message.data, message.id);
|
148
|
+
}
|
149
|
+
},
|
150
|
+
};
|
151
|
+
return {
|
152
|
+
...SocketSubscriptionsInternal,
|
153
|
+
socket: SocketSubscriptionsPublic,
|
154
|
+
};
|
155
|
+
};
|
156
|
+
export default SocketSubscriptionHandler;
|
157
|
+
//# sourceMappingURL=SocketSubscriptionHandler.js.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"SocketSubscriptionHandler.js","sourceRoot":"","sources":["../src/SocketSubscriptionHandler.ts"],"names":[],"mappings":"AAAA,OAAO,SAAS,MAAM,WAAW,CAAC;AAClC,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAEtC,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC1C,OAAO,OAA0B,MAAM,cAAc,CAAC;AAgBtD,MAAM,yBAAyB,GAAG,CAChC,MAA8B,EAC9B,MAAc,EACd,EAAE,qBAAqB,GAAG,EAAE,EAAqC,EACjE,EAAE;IACF,WAAW;IACX,MAAM,SAAS,GAAG,CAAC,KAAa,EAAE,EAAiB,EAAE,EAAE;QACrD,SAAS,CAAC,EAAE,KAAK,CAAC,EAAE,8BAA8B,CAAC,CAAC;QACpD,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC,KAAK,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;IACnC,CAAC,CAAC;IAEF,MAAM,kBAAkB,GAAG,CAAC,SAAiB,EAAE,EAA4B,EAAE,KAAa,EAAE,EAAE;QAC5F,IAAI,EAAE,EAAE,CAAC;YACP,OAAO,GAAG,SAAS,IAAI,EAAE,cAAc,KAAK,EAAE,CAAC;QACjD,CAAC;QAED,OAAO,GAAG,SAAS,cAAc,KAAK,EAAE,CAAC;IAC3C,CAAC,CAAC;IAEF,IAAI,aAAa,GAA8B,EAAE,CAAC;IAClD,MAAM,OAAO,GAAG,IAAI,YAAY,EAAE,CAAC;IAEnC,+CAA+C;IAC/C,MAAM,oBAAoB,GAA6C,EAAE,CAAC;IAE1E,MAAM,oBAAoB,GAAG,CAC3B,eAAuB,EACvB,cAAsB,EACtB,QAAwF,EACxF,OAAgB,EAChB,EAAE;QACF,IAAI,CAAC,MAAM,EAAE,CAAC,WAAW,EAAE,EAAE,CAAC;YAC5B,OAAO;QACT,CAAC;QAED,aAAa,CAAC,cAAc,CAAC,EAAE,CAAC;QAChC,OAAO,CAAC,cAAc,CAAC,cAAc,EAAE,QAAQ,CAAC,CAAC;QAEjD,IAAI,aAAa,CAAC,cAAc,CAAC,KAAK,CAAC,EAAE,CAAC;YACxC,IAAI,OAAO,IAAI,MAAM,EAAE,CAAC,WAAW,EAAE,EAAE,CAAC;gBACtC,MAAM,EAAE,CAAC,MAAM,CAAC,eAAe,CAAC;qBAC7B,KAAK,CACJ,CAAC,KAA6B,EAAE,EAAE;oBAChC,MAAM,CAAC,KAAK,CAAC,kCAAkC,EAAE,eAAe,EAAE,KAAK,CAAC,CAAC;gBAC3E,CAAC,CAAC,CAAC;YACT,CAAC;YAED,OAAO,aAAa,CAAC,cAAc,CAAC,CAAC;QACvC,CAAC;IACH,CAAC,CAAC;IAEF,MAAM,mBAAmB,GAAG,CAC1B,cAAsB,EACtB,QAAwF,EACxF,EAAE;QACF,OAAO,CAAC,cAAc,CAAC,cAAc,EAAE,QAAQ,CAAC,CAAC;IACnD,CAAC,CAAC;IAEF,MAAM,gBAAgB,GAAG,CACvB,eAAuB,EACvB,QAA4D,EAC5D,IAAW,EACX,YAA0C,EAC1C,EAAE;QACF,QAAQ,CACN,IAAI,EACJ,cAAc,CAAC,EAAE;YACf,MAAM,EAAE,CAAC,IAAI,CAAC,GAAG,eAAe,IAAI,YAAY,UAAU,EAAE,cAAc,CAAC;iBACxE,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,gCAAgC,EAAE,eAAe,EAAE,KAAK,CAAC,CAAC,CAAC;QAC9F,CAAC,EACD,CAAC,QAAQ,EAAE,aAAa,EAAE,EAAE;YAC1B,MAAM,EAAE,CAAC,IAAI,CAAC,GAAG,eAAe,IAAI,YAAY,SAAS,EAAE;gBACzD,SAAS,EAAE,QAAQ;gBACnB,OAAO,EAAE,aAAa;aACvB,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,uCAAuC,EAAE,eAAe,EAAE,KAAK,CAAC,CAAC,CAAC;QACnG,CAAC,CACF,CAAC;IACJ,CAAC,CAAC;IAEF,MAAM,0BAA0B,GAAG,CAAC,cAAsB,EAAE,EAAE;QAC5D,MAAM,OAAO,GAAG,oBAAoB,CAAC,cAAc,CAAC,CAAC;QACrD,OAAO,CAAC,OAAO,CAAC,WAAW,CAAC,EAAE,CAAC,WAAW,CAAC,QAAQ,CAAC,OAAO,CAAC,WAAW,CAAC,aAAa,CAAC,CAAC,CAAC;QAExF,aAAa,CAAC,cAAc,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC;QAC/C,OAAO,oBAAoB,CAAC,cAAc,CAAC,CAAC;IAC9C,CAAC,CAAC;IAEF,MAAM,uBAAuB,GAAG,CAAC,cAAsB,EAAE,KAA6B,EAAE,EAAE;QACxF,MAAM,OAAO,GAAG,oBAAoB,CAAC,cAAc,CAAC,CAAC;QACrD,OAAO,CAAC,OAAO,CAAC,WAAW,CAAC,EAAE,CAAC,WAAW,CAAC,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;QAEnE,OAAO,oBAAoB,CAAC,cAAc,CAAC,CAAC;IAC9C,CAAC,CAAC;IAEF,MAAM,eAAe,GAAG,CACtB,eAAuB,EACvB,cAAsB,EACtB,QAAwF,EACxF,IAAY,EACsC,EAAE;QACpD,MAAM,aAAa,GAAG,CAAC,OAAO,GAAG,IAAI,EAAE,EAAE,CAAC,oBAAoB,CAAC,eAAe,EAAE,cAAc,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;QAEnH,IAAI,CAAC,aAAa,CAAC,cAAc,CAAC,EAAE,CAAC;YACnC,IAAI,CAAC,oBAAoB,CAAC,cAAc,CAAC,EAAE,CAAC;gBAC1C,oBAAoB,CAAC,cAAc,CAAC,GAAG,EAAE,CAAC;gBAC1C,MAAM,EAAE;qBACL,IAAI,CAAC,eAAe,EAAE,IAAI,CAAC;qBAC3B,IAAI,CACH,0BAA0B,CAAC,IAAI,CAAC,yBAAyB,EAAE,cAAc,CAAC,EAC1E,uBAAuB,CAAC,IAAI,CAAC,yBAAyB,EAAE,cAAc,CAAC,CACxE,CAAC;YACN,CAAC;YAED,MAAM,QAAQ,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC;YACnC,oBAAoB,CAAC,cAAc,CAAC,CAAC,IAAI,CAAC;gBACxC,QAAQ;gBACR,aAAa;aACd,CAAC,CAAC;YAEH,OAAO,QAAQ,CAAC,OAAO,CAAC;QAC1B,CAAC;QAED,aAAa,CAAC,cAAc,CAAC,EAAE,CAAC;QAChC,OAAO,OAAO,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC;IACxC,CAAC,CAAC;IAEF,MAAM,gCAAgC,GAAG,GAAW,EAAE;QACpD,OAAO,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC;aAC9B,MAAM,CAAC,CAAC,OAAO,EAAE,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,aAAa,CAAC,IAAI,CAAC,GAAG,OAAO,EAAE,CAAC,CAAC,CAAC;IACzE,CAAC,CAAC;IAEF,SAAS;IAET,wEAAwE;IACxE,MAAM,yBAAyB,GAAsC;QACnE,qBAAqB,EAAE,CAAC,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,EAAE;YACtD,MAAM,cAAc,GAAG,SAAS,CAAC,GAAG,QAAQ,UAAU,EAAE,QAAQ,CAAC,CAAC;YAClE,OAAO,CAAC,EAAE,CAAC,cAAc,EAAE,QAAQ,CAAC,CAAC;YACrC,OAAO,GAAG,EAAE,CAAC,mBAAmB,CAAC,cAAc,EAAE,QAAQ,CAAC,CAAC;QAC7D,CAAC;QAED,2EAA2E;QAC3E,WAAW,EAAE,CAAC,SAAS,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,EAAE;YACpD,IAAI,CAAC,MAAM,EAAE,CAAC,WAAW,EAAE,EAAE,CAAC;gBAC5B,MAAM,oDAAoD,CAAC;YAC7D,CAAC;YAED,SAAS,CACP,SAAS,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,EAC7B,0HAA0H,CAC3H,CAAC;YAEF,MAAM,cAAc,GAAG,SAAS,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;YAClD,MAAM,eAAe,GAAG,kBAAkB,CAAC,SAAS,EAAE,QAAQ,EAAE,KAAK,CAAC,CAAC;YAEvE,OAAO,CAAC,EAAE,CAAC,cAAc,EAAE,QAAQ,CAAC,CAAC;YACrC,OAAO,eAAe,CAAC,eAAe,EAAE,cAAc,EAAE,QAAQ,CAAC,CAAC;QACpE,CAAC;QAED,YAAY,EAAE,GAAG,EAAE;YACjB,OAAO,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,MAAM,GAAG,CAAC,IAAI,gCAAgC,EAAE,GAAG,CAAC,CAAC;QACzF,CAAC;QAED,OAAO,EAAE,CACP,SAAiB,EACjB,KAAa,EACb,QAA4D,EAC5D,cAAgD,EAChD,EAAE;YACF,IAAI,CAAC,MAAM,EAAE,CAAC,WAAW,EAAE,EAAE,CAAC;gBAC5B,MAAM,gDAAgD,CAAC;YACzD,CAAC;YAED,SAAS,CACP,SAAS,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,EAC7B,gFAAgF,CACjF,CAAC;YAEF,MAAM,cAAc,GAAG,KAAK,CAAC;YAC7B,IAAI,aAAa,CAAC,cAAc,CAAC,IAAI,oBAAoB,CAAC,cAAc,CAAC,EAAE,CAAC;gBAC1E,MAAM,aAAa,CAAC;YACtB,CAAC;YAED,MAAM,eAAe,GAAG,GAAG,SAAS,UAAU,KAAK,EAAE,CAAC;YAEtD,QAAQ,GAAG,gBAAgB,CAAC,IAAI,CAAC,yBAAyB,EAAE,eAAe,EAAE,QAAQ,CAAC,CAAC;YACvF,OAAO,CAAC,EAAE,CAAC,cAAc,EAAE,QAAQ,CAAC,CAAC;YAErC,OAAO,eAAe,CAAC,eAAe,EAAE,cAAc,EAAE,QAAQ,EAAE,cAAc,CAAC,CAAC;QACpF,CAAC;QAED,2BAA2B,EAAE,GAAG,EAAE;YAChC,OAAO,MAAM,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC,MAAM,CAAC;QAClD,CAAC;KACF,CAAC;IAEF,iBAAiB;IACjB,MAAM,2BAA2B,GAAG;QAClC,oBAAoB;YAClB,OAAO,CAAC,kBAAkB,EAAE,CAAC;YAC7B,aAAa,GAAG,EAAE,CAAC;QACrB,CAAC;QAED,aAAa,CAAC,OAA8C;YAC1D,MAAM,OAAO,GAAG,YAAY,CAAC,OAAO,CAAC,KAAK,EAAE,qBAAqB,CAAC,CAAC;YACnE,IAAI,OAAO,CAAC,aAAa,EAAE,CAAC;gBAC1B,IAAI,CAAC,OAAO,EAAE,CAAC;oBACb,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,EAAE,kBAAkB,OAAO,CAAC,aAAa,GAAG,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;gBAC1F,CAAC;gBAED,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,aAAa,CAAC,CAAC;YACnE,CAAC;iBAAM,CAAC;gBACN,IAAI,CAAC,OAAO,EAAE,CAAC;oBACb,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,EAAE,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,WAAW,OAAO,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,aAAa,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;gBACrG,CAAC;gBAED,IAAI,OAAO,CAAC,EAAE,EAAE,CAAC;oBACf,+FAA+F;oBAC/F,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,KAAK,EAAE,OAAO,CAAC,EAAE,CAAC,EAAE,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,EAAE,CAAC,CAAC;gBAC/E,CAAC;gBAED,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,EAAE,CAAC,CAAC;YACxD,CAAC;QACH,CAAC;KACF,CAAC;IAEF,OAAO;QACL,GAAG,2BAA2B;QAC9B,MAAM,EAAE,yBAAyB;KAClC,CAAC;AACJ,CAAC,CAAC;AAEF,eAAe,yBAAyB,CAAC"}
|
@@ -0,0 +1,57 @@
|
|
1
|
+
import * as Options from '../types/options.js';
|
2
|
+
export declare const waitForExpect: (func: () => void | Promise<void>) => any;
|
3
|
+
declare const CONNECT_PARAMS: {
|
4
|
+
username: string;
|
5
|
+
password: string;
|
6
|
+
url: string;
|
7
|
+
};
|
8
|
+
declare const AUTH_RESPONSE: {
|
9
|
+
auth_token: string;
|
10
|
+
refresh_token: string;
|
11
|
+
user: {
|
12
|
+
permissions: string[];
|
13
|
+
username: string;
|
14
|
+
active_sessions: number;
|
15
|
+
last_login: number;
|
16
|
+
};
|
17
|
+
system: {
|
18
|
+
cid: string;
|
19
|
+
hostname: string;
|
20
|
+
network_type: string;
|
21
|
+
path_separator: string;
|
22
|
+
platform: string;
|
23
|
+
language: string;
|
24
|
+
};
|
25
|
+
wizard_pending: boolean;
|
26
|
+
};
|
27
|
+
export type MockSocketOptions = Omit<Options.APISocketOptions, 'username' | 'password' | 'url'> & {
|
28
|
+
username?: string;
|
29
|
+
password?: string;
|
30
|
+
url?: string;
|
31
|
+
};
|
32
|
+
declare const getSocket: (options?: MockSocketOptions) => {
|
33
|
+
socket: import("../NodeSocket.js").APISocket;
|
34
|
+
mockConsole: {
|
35
|
+
log: import("jest-mock").Mock<(a1: any, a2: any, a3: any, a4: any) => void>;
|
36
|
+
info: import("jest-mock").Mock<(a1: any, a2: any, a3: any, a4: any) => void>;
|
37
|
+
warn: import("jest-mock").Mock<(a1: any, a2: any, a3: any, a4: any) => void>;
|
38
|
+
error: import("jest-mock").Mock<(a1: any, a2: any, a3: any, a4: any) => void>;
|
39
|
+
};
|
40
|
+
};
|
41
|
+
type Callback = (requestData: object) => void;
|
42
|
+
declare const getConnectedSocket: (server: ReturnType<typeof getMockServer>, options?: MockSocketOptions, authCallback?: Callback) => Promise<{
|
43
|
+
socket: import("../NodeSocket.js").APISocket;
|
44
|
+
mockConsole: {
|
45
|
+
log: import("jest-mock").Mock<(a1: any, a2: any, a3: any, a4: any) => void>;
|
46
|
+
info: import("jest-mock").Mock<(a1: any, a2: any, a3: any, a4: any) => void>;
|
47
|
+
warn: import("jest-mock").Mock<(a1: any, a2: any, a3: any, a4: any) => void>;
|
48
|
+
error: import("jest-mock").Mock<(a1: any, a2: any, a3: any, a4: any) => void>;
|
49
|
+
};
|
50
|
+
}>;
|
51
|
+
declare const getMockServer: () => {
|
52
|
+
addDataHandler: <DataT extends object | undefined>(method: string, path: string, data?: DataT, subscriptionCallback?: Callback) => void;
|
53
|
+
addErrorHandler: (method: string, path: string, errorStr: string | null, errorCode: number, subscriptionCallback?: Callback) => void;
|
54
|
+
stop: () => void;
|
55
|
+
send: (data: object) => void;
|
56
|
+
};
|
57
|
+
export { getMockServer, getSocket, getConnectedSocket, CONNECT_PARAMS, AUTH_RESPONSE };
|
@@ -0,0 +1,127 @@
|
|
1
|
+
import { Socket } from '../NodeSocket.js';
|
2
|
+
import { Server, WebSocket } from 'mock-socket';
|
3
|
+
import { jest } from '@jest/globals';
|
4
|
+
import ApiConstants from '../ApiConstants.js';
|
5
|
+
import { EventEmitter } from 'events';
|
6
|
+
import waitForExpectOriginal from 'wait-for-expect';
|
7
|
+
const EXCEPT_TIMEOUT = 1000;
|
8
|
+
//@ts-ignore
|
9
|
+
export const waitForExpect = (func) => waitForExpectOriginal.default(func, EXCEPT_TIMEOUT);
|
10
|
+
const VERBOSE = false;
|
11
|
+
const getMockConsole = () => ({
|
12
|
+
log: jest.fn((a1, a2, a3, a4) => {
|
13
|
+
if (VERBOSE) {
|
14
|
+
console.log(a1, a2, a3, a4);
|
15
|
+
}
|
16
|
+
}),
|
17
|
+
info: jest.fn((a1, a2, a3, a4) => {
|
18
|
+
if (VERBOSE) {
|
19
|
+
console.info(a1, a2, a3, a4);
|
20
|
+
}
|
21
|
+
}),
|
22
|
+
warn: jest.fn((a1, a2, a3, a4) => {
|
23
|
+
console.warn(a1, a2, a3, a4);
|
24
|
+
}),
|
25
|
+
error: jest.fn((a1, a2, a3, a4) => {
|
26
|
+
console.error(a1, a2, a3, a4);
|
27
|
+
}),
|
28
|
+
});
|
29
|
+
const CONNECT_PARAMS = {
|
30
|
+
username: 'test',
|
31
|
+
password: 'test',
|
32
|
+
url: 'ws://localhost:7171/api/v1/',
|
33
|
+
};
|
34
|
+
const getDefaultSocketOptions = (mockConsole) => ({
|
35
|
+
...CONNECT_PARAMS,
|
36
|
+
logOutput: mockConsole,
|
37
|
+
logLevel: VERBOSE ? 'verbose' : 'warn',
|
38
|
+
});
|
39
|
+
const AUTH_RESPONSE = {
|
40
|
+
auth_token: 'b823187f-4aab-4b71-9764-e63e88401a26',
|
41
|
+
refresh_token: '5124faasf-4aab-4b71-9764-e63e88401a26',
|
42
|
+
user: {
|
43
|
+
permissions: ['admin'],
|
44
|
+
username: 'test',
|
45
|
+
active_sessions: 1,
|
46
|
+
last_login: 0,
|
47
|
+
},
|
48
|
+
system: {
|
49
|
+
cid: 'AHLUODI2YZ2U7FDWMHFNJU65ERGKUN4MH7GW5LY',
|
50
|
+
hostname: 'ubuntu-htpc',
|
51
|
+
network_type: 'private',
|
52
|
+
path_separator: '/',
|
53
|
+
platform: 'other',
|
54
|
+
language: 'fi',
|
55
|
+
},
|
56
|
+
wizard_pending: false,
|
57
|
+
};
|
58
|
+
const getSocket = (options = {}) => {
|
59
|
+
const mockConsole = getMockConsole();
|
60
|
+
const socket = Socket({
|
61
|
+
...getDefaultSocketOptions(mockConsole),
|
62
|
+
...options,
|
63
|
+
}, WebSocket);
|
64
|
+
return { socket, mockConsole };
|
65
|
+
};
|
66
|
+
const getConnectedSocket = async (server, options, authCallback) => {
|
67
|
+
server.addDataHandler('POST', ApiConstants.LOGIN_URL, AUTH_RESPONSE, authCallback);
|
68
|
+
const { socket, mockConsole } = getSocket(options);
|
69
|
+
await socket.connect();
|
70
|
+
return { socket, mockConsole };
|
71
|
+
};
|
72
|
+
const toEmitId = (path, method) => {
|
73
|
+
return `${path}_${method}`;
|
74
|
+
};
|
75
|
+
const getMockServer = () => {
|
76
|
+
const mockServer = new Server(CONNECT_PARAMS.url);
|
77
|
+
let socket;
|
78
|
+
const emitter = new EventEmitter();
|
79
|
+
const addServerHandler = (method, path, responseData, subscriptionCallback) => {
|
80
|
+
emitter.addListener(toEmitId(path, method), (request, s) => {
|
81
|
+
if (subscriptionCallback) {
|
82
|
+
subscriptionCallback(request);
|
83
|
+
}
|
84
|
+
const response = {
|
85
|
+
callback_id: request.callback_id,
|
86
|
+
...responseData,
|
87
|
+
};
|
88
|
+
s.send(JSON.stringify(response));
|
89
|
+
});
|
90
|
+
};
|
91
|
+
mockServer.on('connection', s => {
|
92
|
+
socket = s;
|
93
|
+
socket.on('message', (messageObj) => {
|
94
|
+
const request = JSON.parse(messageObj);
|
95
|
+
emitter.emit(toEmitId(request.path, request.method), request, s);
|
96
|
+
});
|
97
|
+
});
|
98
|
+
mockServer.on('close', () => {
|
99
|
+
emitter.removeAllListeners();
|
100
|
+
});
|
101
|
+
return {
|
102
|
+
addDataHandler: (method, path, data, subscriptionCallback) => {
|
103
|
+
addServerHandler(method, path, {
|
104
|
+
data,
|
105
|
+
code: 200,
|
106
|
+
}, subscriptionCallback);
|
107
|
+
},
|
108
|
+
addErrorHandler: (method, path, errorStr, errorCode, subscriptionCallback) => {
|
109
|
+
addServerHandler(method, path, {
|
110
|
+
error: !errorStr ? null : {
|
111
|
+
message: errorStr,
|
112
|
+
},
|
113
|
+
code: errorCode,
|
114
|
+
}, subscriptionCallback);
|
115
|
+
},
|
116
|
+
stop: () => {
|
117
|
+
mockServer.stop(() => {
|
118
|
+
// ...
|
119
|
+
});
|
120
|
+
},
|
121
|
+
send: (data) => {
|
122
|
+
socket.send(JSON.stringify(data));
|
123
|
+
},
|
124
|
+
};
|
125
|
+
};
|
126
|
+
export { getMockServer, getSocket, getConnectedSocket, CONNECT_PARAMS, AUTH_RESPONSE };
|
127
|
+
//# sourceMappingURL=helpers.js.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"helpers.js","sourceRoot":"","sources":["../../src/tests/helpers.ts"],"names":[],"mappings":"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"}
|
@@ -0,0 +1,30 @@
|
|
1
|
+
export type AuthTokenType = string;
|
2
|
+
export interface AuthenticationResponse {
|
3
|
+
auth_token: AuthTokenType;
|
4
|
+
}
|
5
|
+
export interface LogoutResponse {
|
6
|
+
}
|
7
|
+
export interface CredentialsAuthenticationData {
|
8
|
+
username: string;
|
9
|
+
password: string;
|
10
|
+
grant_type: 'password';
|
11
|
+
max_inactivity?: number;
|
12
|
+
}
|
13
|
+
export interface RefreshTokenAuthenticationData {
|
14
|
+
refresh_token: string;
|
15
|
+
grant_type: 'refresh_token';
|
16
|
+
max_inactivity?: number;
|
17
|
+
}
|
18
|
+
export interface TokenAuthenticationData {
|
19
|
+
auth_token: string;
|
20
|
+
}
|
21
|
+
export type FieldErrorCode = 'missing_field' | 'invalid' | 'already_exists';
|
22
|
+
export interface FieldError extends ErrorBase {
|
23
|
+
message: string;
|
24
|
+
field: string;
|
25
|
+
code: FieldErrorCode;
|
26
|
+
}
|
27
|
+
export interface ErrorBase {
|
28
|
+
message: string;
|
29
|
+
}
|
30
|
+
export type EntityId = string | number;
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"api.js","sourceRoot":"","sources":["../../src/types/api.ts"],"names":[],"mappings":"AAAA,iBAAiB"}
|
@@ -0,0 +1,24 @@
|
|
1
|
+
export type CompletionIdType = number;
|
2
|
+
import { ErrorBase, FieldError, EntityId } from './api.js';
|
3
|
+
export interface RequestResponseBase {
|
4
|
+
code: number;
|
5
|
+
callback_id: number;
|
6
|
+
}
|
7
|
+
export interface RequestSuccessResponse<DataT extends object | undefined = object | undefined> extends RequestResponseBase {
|
8
|
+
data?: DataT;
|
9
|
+
}
|
10
|
+
export interface RequestErrorResponse extends RequestResponseBase {
|
11
|
+
error: ErrorBase | FieldError;
|
12
|
+
}
|
13
|
+
export interface OutgoingRequest<DataT extends object | undefined = object | undefined> {
|
14
|
+
path: string;
|
15
|
+
method: string;
|
16
|
+
data: DataT;
|
17
|
+
callback_id: number;
|
18
|
+
}
|
19
|
+
export interface IncomingSubscriptionEvent<DataT extends object | undefined = object | undefined> {
|
20
|
+
event: string;
|
21
|
+
data: DataT;
|
22
|
+
completion_id?: CompletionIdType;
|
23
|
+
id?: EntityId;
|
24
|
+
}
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"api_internal.js","sourceRoot":"","sources":["../../src/types/api_internal.ts"],"names":[],"mappings":""}
|