skapi-js 1.0.17 → 1.0.18
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/skapi.js +1 -1
- package/dist/skapi.js.map +1 -1
- package/dist/skapi.module.js +1 -1
- package/dist/skapi.module.js.map +1 -1
- package/js/main/skapi.d.ts +24 -2
- package/js/main/skapi.js +27 -4
- package/js/methods/realtime.d.ts +27 -0
- package/js/methods/realtime.js +159 -0
- package/js/methods/request.js +1 -0
- package/package.json +1 -1
package/js/main/skapi.d.ts
CHANGED
|
@@ -28,15 +28,37 @@ export default class Skapi {
|
|
|
28
28
|
};
|
|
29
29
|
private __connection;
|
|
30
30
|
private __authConnection;
|
|
31
|
+
private __socket;
|
|
32
|
+
private __socket_group;
|
|
31
33
|
constructor(service: string, owner: string, options?: {
|
|
32
34
|
autoLogin: boolean;
|
|
33
35
|
});
|
|
34
36
|
updateConnection(): Promise<Connection>;
|
|
35
37
|
private checkAdmin;
|
|
36
38
|
private request;
|
|
37
|
-
private getSubscribedTo;
|
|
38
|
-
private getSubscribers;
|
|
39
39
|
normalizeRecord: any;
|
|
40
|
+
connectRealtime(cb: (rt: {
|
|
41
|
+
status: 'message' | 'error' | 'success' | 'close' | 'notice';
|
|
42
|
+
message: any;
|
|
43
|
+
}) => Promise<void>): any;
|
|
44
|
+
closeRealtime(): Promise<void>;
|
|
45
|
+
getRealtimeUsers(params: {
|
|
46
|
+
group: string;
|
|
47
|
+
user_id?: string;
|
|
48
|
+
}, fetchOptions?: FetchOptions): Promise<DatabaseResponse<string[]>>;
|
|
49
|
+
postRealtime(message: any, recipient: string): Promise<{
|
|
50
|
+
status: 'success';
|
|
51
|
+
message: 'Message sent.';
|
|
52
|
+
} | {
|
|
53
|
+
status: 'error';
|
|
54
|
+
message: 'Realtime connection is not open.';
|
|
55
|
+
}>;
|
|
56
|
+
joinRealtime(params: {
|
|
57
|
+
group: string | null;
|
|
58
|
+
}): Promise<{
|
|
59
|
+
status: 'success';
|
|
60
|
+
message: string;
|
|
61
|
+
}>;
|
|
40
62
|
getConnection(): Promise<Connection>;
|
|
41
63
|
getProfile(options?: {
|
|
42
64
|
refreshToken: boolean;
|
package/js/main/skapi.js
CHANGED
|
@@ -7,8 +7,9 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
7
7
|
import SkapiError from './error';
|
|
8
8
|
import validator from '../utils/validator';
|
|
9
9
|
import { getRecords, postRecord, deleteRecords, getTables, getIndexes, getTags, uploadFiles, getFile, grantPrivateRecordAccess, removePrivateRecordAccess, listPrivateRecordAccess, requestPrivateRecordAccessKey, deleteFiles, normalizeRecord } from '../methods/database';
|
|
10
|
+
import { connectRealtime, joinRealtime, postRealtime, closeRealtime, getRealtimeUsers } from '../methods/realtime';
|
|
10
11
|
import { request, secureRequest, mock, getFormResponse, formHandler, getConnection } from '../methods/request';
|
|
11
|
-
import { subscribe, unsubscribe, blockSubscriber, unblockSubscriber,
|
|
12
|
+
import { subscribe, unsubscribe, blockSubscriber, unblockSubscriber, getSubscriptions, subscribeNewsletter, getNewsletters, unsubscribeNewsletter, getNewsletterSubscription } from '../methods/subscription';
|
|
12
13
|
import { checkAdmin, getProfile, logout, recoverAccount, resendSignupConfirmation, authentication, login, signup, disableAccount, resetPassword, verifyEmail, verifyPhoneNumber, forgotPassword, changePassword, updateProfile, getUsers, setUserPool, userPool, lastVerifiedEmail, requestUsernameChange } from '../methods/user';
|
|
13
14
|
export default class Skapi {
|
|
14
15
|
get user() {
|
|
@@ -22,7 +23,7 @@ export default class Skapi {
|
|
|
22
23
|
set user(value) {
|
|
23
24
|
}
|
|
24
25
|
constructor(service, owner, options) {
|
|
25
|
-
this.version = '1.0.
|
|
26
|
+
this.version = '1.0.18';
|
|
26
27
|
this.session = null;
|
|
27
28
|
this.connection = null;
|
|
28
29
|
this.host = 'skapi';
|
|
@@ -84,8 +85,6 @@ export default class Skapi {
|
|
|
84
85
|
};
|
|
85
86
|
this.checkAdmin = checkAdmin.bind(this);
|
|
86
87
|
this.request = request.bind(this);
|
|
87
|
-
this.getSubscribedTo = getSubscribedTo.bind(this);
|
|
88
|
-
this.getSubscribers = getSubscribers.bind(this);
|
|
89
88
|
this.normalizeRecord = normalizeRecord.bind(this);
|
|
90
89
|
if (typeof service !== 'string' || typeof owner !== 'string') {
|
|
91
90
|
throw new SkapiError('"service" and "owner" should be type <string>.', { code: 'INVALID_PARAMETER' });
|
|
@@ -192,6 +191,21 @@ export default class Skapi {
|
|
|
192
191
|
}, { bypassAwaitConnection: true, method: 'get' });
|
|
193
192
|
return this.connection;
|
|
194
193
|
}
|
|
194
|
+
connectRealtime(cb) {
|
|
195
|
+
return connectRealtime.bind(this)(cb);
|
|
196
|
+
}
|
|
197
|
+
closeRealtime() {
|
|
198
|
+
return closeRealtime.bind(this)();
|
|
199
|
+
}
|
|
200
|
+
getRealtimeUsers(params, fetchOptions) {
|
|
201
|
+
return getRealtimeUsers.bind(this)(params, fetchOptions);
|
|
202
|
+
}
|
|
203
|
+
postRealtime(message, recipient) {
|
|
204
|
+
return postRealtime.bind(this)(message, recipient);
|
|
205
|
+
}
|
|
206
|
+
joinRealtime(params) {
|
|
207
|
+
return joinRealtime.bind(this)(params);
|
|
208
|
+
}
|
|
195
209
|
getConnection() {
|
|
196
210
|
return getConnection.bind(this)();
|
|
197
211
|
}
|
|
@@ -291,6 +305,15 @@ export default class Skapi {
|
|
|
291
305
|
return subscribeNewsletter.bind(this)(form);
|
|
292
306
|
}
|
|
293
307
|
}
|
|
308
|
+
__decorate([
|
|
309
|
+
formHandler()
|
|
310
|
+
], Skapi.prototype, "getRealtimeUsers", null);
|
|
311
|
+
__decorate([
|
|
312
|
+
formHandler()
|
|
313
|
+
], Skapi.prototype, "postRealtime", null);
|
|
314
|
+
__decorate([
|
|
315
|
+
formHandler()
|
|
316
|
+
], Skapi.prototype, "joinRealtime", null);
|
|
294
317
|
__decorate([
|
|
295
318
|
formHandler()
|
|
296
319
|
], Skapi.prototype, "getConnection", null);
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { DatabaseResponse, FetchOptions } from '../Types';
|
|
2
|
+
type RealTimeCallback = (rt: {
|
|
3
|
+
status: 'message' | 'error' | 'success' | 'close' | 'notice';
|
|
4
|
+
message: string | {
|
|
5
|
+
[key: string]: any;
|
|
6
|
+
};
|
|
7
|
+
}) => void;
|
|
8
|
+
export declare function closeRealtime(): Promise<void>;
|
|
9
|
+
export declare function connectRealtime(cb: RealTimeCallback, delay?: number): Promise<void>;
|
|
10
|
+
export declare function postRealtime(message: any, recipient: string): Promise<{
|
|
11
|
+
status: 'success';
|
|
12
|
+
message: 'Message sent.';
|
|
13
|
+
} | {
|
|
14
|
+
status: 'error';
|
|
15
|
+
message: 'Realtime connection is not open.';
|
|
16
|
+
}>;
|
|
17
|
+
export declare function joinRealtime(params: {
|
|
18
|
+
group?: string | null;
|
|
19
|
+
}): Promise<{
|
|
20
|
+
status: 'success';
|
|
21
|
+
message: string;
|
|
22
|
+
}>;
|
|
23
|
+
export declare function getRealtimeUsers(params: {
|
|
24
|
+
group: string;
|
|
25
|
+
user_id?: string;
|
|
26
|
+
}, fetchOptions?: FetchOptions): Promise<DatabaseResponse<string[]>>;
|
|
27
|
+
export {};
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import SkapiError from '../main/error';
|
|
2
|
+
import validator from '../utils/validator';
|
|
3
|
+
import { extractFormMeta } from '../utils/utils';
|
|
4
|
+
import { request } from './request';
|
|
5
|
+
async function prepareWebsocket() {
|
|
6
|
+
await this.getProfile();
|
|
7
|
+
if (!this.session) {
|
|
8
|
+
throw new SkapiError(`No access.`, { code: 'INVALID_REQUEST' });
|
|
9
|
+
}
|
|
10
|
+
let r = await this.record_endpoint;
|
|
11
|
+
return new WebSocket(r.websocket_private + '?token=' + this.session.accessToken.jwtToken);
|
|
12
|
+
}
|
|
13
|
+
let reconnectAttempts = 0;
|
|
14
|
+
export async function closeRealtime() {
|
|
15
|
+
let socket = this.__socket ? await this.__socket : this.__socket;
|
|
16
|
+
if (socket) {
|
|
17
|
+
socket.close();
|
|
18
|
+
}
|
|
19
|
+
this.__socket = null;
|
|
20
|
+
this.__socket_group = null;
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
export function connectRealtime(cb, delay = 0) {
|
|
24
|
+
if (typeof cb !== 'function') {
|
|
25
|
+
throw new SkapiError(`Callback must be a function.`, { code: 'INVALID_REQUEST' });
|
|
26
|
+
}
|
|
27
|
+
if (reconnectAttempts || !(this.__socket instanceof Promise)) {
|
|
28
|
+
this.__socket = new Promise(resolve => {
|
|
29
|
+
setTimeout(async () => {
|
|
30
|
+
await this.__connection;
|
|
31
|
+
let socket = await prepareWebsocket.bind(this)();
|
|
32
|
+
socket.onopen = () => {
|
|
33
|
+
reconnectAttempts = 0;
|
|
34
|
+
cb({ status: 'success', message: 'Connected to WebSocket server.' });
|
|
35
|
+
if (this.__socket_group) {
|
|
36
|
+
socket.send(JSON.stringify({
|
|
37
|
+
action: 'joinGroup',
|
|
38
|
+
rid: this.__socket_group,
|
|
39
|
+
token: this.session.accessToken.jwtToken
|
|
40
|
+
}));
|
|
41
|
+
}
|
|
42
|
+
resolve(socket);
|
|
43
|
+
};
|
|
44
|
+
socket.onmessage = event => {
|
|
45
|
+
let data = JSON.parse(decodeURI(event.data));
|
|
46
|
+
if (data?.['#notice']) {
|
|
47
|
+
cb({ status: 'notice', message: data['#notice'] });
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
cb({ status: 'message', message: data });
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
socket.onclose = event => {
|
|
54
|
+
if (event.wasClean) {
|
|
55
|
+
cb({ status: 'close', message: 'WebSocket connection closed.' });
|
|
56
|
+
this.__socket = null;
|
|
57
|
+
this.__socket_group = null;
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
const maxAttempts = 10;
|
|
61
|
+
reconnectAttempts++;
|
|
62
|
+
if (reconnectAttempts < maxAttempts) {
|
|
63
|
+
let delay = Math.min(1000 * (2 ** reconnectAttempts), 30000);
|
|
64
|
+
cb({ status: 'error', message: `Skapi: WebSocket connection error. Reconnecting in ${delay / 1000} seconds...` });
|
|
65
|
+
connectRealtime.bind(this)(cb, delay);
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
cb({ status: 'error', message: 'Skapi: WebSocket connection error. Max reconnection attempts reached.' });
|
|
69
|
+
this.__socket = null;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
socket.onerror = () => {
|
|
74
|
+
cb({ status: 'error', message: 'Skapi: WebSocket connection error.' });
|
|
75
|
+
throw new SkapiError(`Skapi: WebSocket connection error.`, { code: 'ERROR' });
|
|
76
|
+
};
|
|
77
|
+
}, delay);
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
return this.__socket;
|
|
81
|
+
}
|
|
82
|
+
export async function postRealtime(message, recipient) {
|
|
83
|
+
let socket = this.__socket ? await this.__socket : this.__socket;
|
|
84
|
+
if (!socket) {
|
|
85
|
+
throw new SkapiError(`No realtime connection. Execute connectRealtime() before this method.`, { code: 'INVALID_REQUEST' });
|
|
86
|
+
}
|
|
87
|
+
if (!recipient) {
|
|
88
|
+
throw new SkapiError(`No recipient.`, { code: 'INVALID_REQUEST' });
|
|
89
|
+
}
|
|
90
|
+
if (message instanceof FormData || message instanceof SubmitEvent || message instanceof HTMLFormElement) {
|
|
91
|
+
message = extractFormMeta(message).meta;
|
|
92
|
+
}
|
|
93
|
+
if (socket.readyState === 1) {
|
|
94
|
+
try {
|
|
95
|
+
validator.UserId(recipient);
|
|
96
|
+
socket.send(JSON.stringify({
|
|
97
|
+
action: 'sendMessage',
|
|
98
|
+
uid: recipient,
|
|
99
|
+
content: message,
|
|
100
|
+
token: this.session.accessToken.jwtToken
|
|
101
|
+
}));
|
|
102
|
+
}
|
|
103
|
+
catch (err) {
|
|
104
|
+
if (this.__socket_group !== recipient) {
|
|
105
|
+
throw new SkapiError(`User has not joined to the recipient group. Run joinRealtime("${recipient}")`, { code: 'INVALID_REQUEST' });
|
|
106
|
+
}
|
|
107
|
+
socket.send(JSON.stringify({
|
|
108
|
+
action: 'broadcast',
|
|
109
|
+
rid: recipient,
|
|
110
|
+
content: message,
|
|
111
|
+
token: this.session.accessToken.jwtToken
|
|
112
|
+
}));
|
|
113
|
+
}
|
|
114
|
+
return { status: 'success', message: 'Message sent.' };
|
|
115
|
+
}
|
|
116
|
+
return { status: 'error', message: 'Realtime connection is not open.' };
|
|
117
|
+
}
|
|
118
|
+
export async function joinRealtime(params) {
|
|
119
|
+
let socket = this.__socket ? await this.__socket : this.__socket;
|
|
120
|
+
if (!socket) {
|
|
121
|
+
throw new SkapiError(`No realtime connection. Execute connectRealtime() before this method.`, { code: 'INVALID_REQUEST' });
|
|
122
|
+
}
|
|
123
|
+
if (params instanceof FormData || params instanceof SubmitEvent || params instanceof HTMLFormElement) {
|
|
124
|
+
params = extractFormMeta(params).meta;
|
|
125
|
+
}
|
|
126
|
+
let { group = null } = params;
|
|
127
|
+
if (!group && !this.__socket_group) {
|
|
128
|
+
return { status: 'success', message: 'Left realtime message group.' };
|
|
129
|
+
}
|
|
130
|
+
if (group !== null && typeof group !== 'string') {
|
|
131
|
+
throw new SkapiError(`"group" must be a string | null.`, { code: 'INVALID_PARAMETER' });
|
|
132
|
+
}
|
|
133
|
+
socket.send(JSON.stringify({
|
|
134
|
+
action: 'joinGroup',
|
|
135
|
+
rid: group,
|
|
136
|
+
token: this.session.accessToken.jwtToken
|
|
137
|
+
}));
|
|
138
|
+
this.__socket_group = group;
|
|
139
|
+
return { status: 'success', message: group ? `Joined realtime message group: "${group}".` : 'Left realtime message group.' };
|
|
140
|
+
}
|
|
141
|
+
export async function getRealtimeUsers(params, fetchOptions) {
|
|
142
|
+
await this.__connection;
|
|
143
|
+
params = validator.Params(params, {
|
|
144
|
+
user_id: (v) => validator.UserId(v, 'User ID in "user_id"'),
|
|
145
|
+
group: 'string'
|
|
146
|
+
}, ['group']);
|
|
147
|
+
if (!params.group) {
|
|
148
|
+
throw new SkapiError(`"group" is required.`, { code: 'INVALID_PARAMETER' });
|
|
149
|
+
}
|
|
150
|
+
let res = request.bind(this)('get-ws-group', params, {
|
|
151
|
+
fetchOptions,
|
|
152
|
+
auth: true,
|
|
153
|
+
method: 'post'
|
|
154
|
+
});
|
|
155
|
+
for (let i = 0; i < res.list.length; i++) {
|
|
156
|
+
res[i] = res[i].uid.split('#')[1];
|
|
157
|
+
}
|
|
158
|
+
return res;
|
|
159
|
+
}
|
package/js/methods/request.js
CHANGED
package/package.json
CHANGED