@fabriktor/fx 0.0.38 → 0.0.39
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/src/chat/chat.d.ts +21 -3
- package/dist/src/chat/chat.js +284 -27
- package/package.json +2 -2
package/dist/src/chat/chat.d.ts
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import type { ChatOperator, WSCloseHandler, WSConnection, WSErrorHandler, WSMessageHandler } from "@fabriktor/client";
|
|
2
|
-
import type
|
|
2
|
+
import { type Chat, type ChatRoom, type ChatRoomMember } from "@fabriktor/schema";
|
|
3
|
+
import { type SearchPage } from "../core/search.js";
|
|
4
|
+
type ChatInsertOptions = Parameters<ChatOperator["insertOneChat"]>[0];
|
|
5
|
+
export type CreateChatOptions = ChatInsertOptions;
|
|
3
6
|
export type ReactChatOptions = Parameters<ChatOperator["reactChat"]>[0];
|
|
4
7
|
export type ChatFXOptions = {
|
|
5
8
|
chat: ChatOperator;
|
|
@@ -27,6 +30,13 @@ export type LatestChatsPage = {
|
|
|
27
30
|
has_more: boolean;
|
|
28
31
|
next_cursor: LatestChatsCursor;
|
|
29
32
|
};
|
|
33
|
+
export type SearchChatRoomMembersOptions = {
|
|
34
|
+
chat_room_id: string;
|
|
35
|
+
q?: string;
|
|
36
|
+
page?: number;
|
|
37
|
+
per_page?: number;
|
|
38
|
+
};
|
|
39
|
+
export type SearchChatRoomMembersPage = SearchPage<ChatRoomMember>;
|
|
30
40
|
export type ChatRoomMembersOptions = {
|
|
31
41
|
chat_room_id: string;
|
|
32
42
|
};
|
|
@@ -53,22 +63,24 @@ export type ReactUploadedChatOptions = {
|
|
|
53
63
|
reaction: ReactChatOptions;
|
|
54
64
|
};
|
|
55
65
|
export type ChatRoomSession = {
|
|
56
|
-
latest: LatestChatsPage;
|
|
57
66
|
chats_ws: WSConnection;
|
|
58
67
|
chat_room_ws: WSConnection;
|
|
68
|
+
latest: LatestChatsPage;
|
|
59
69
|
closeNormal(reason?: string): void;
|
|
60
70
|
closeLeavingPage(reason?: string): void;
|
|
61
71
|
};
|
|
62
72
|
export type GlobalChatRoomsSession = {
|
|
63
|
-
members: ChatRoomMember[];
|
|
64
73
|
chat_rooms_ws: WSConnection;
|
|
74
|
+
members: ChatRoomMember[];
|
|
65
75
|
closeNormal(reason?: string): void;
|
|
66
76
|
closeLeavingPage(reason?: string): void;
|
|
67
77
|
};
|
|
68
78
|
export interface ChatFXOperator {
|
|
79
|
+
createChat(opts: CreateChatOptions): Promise<Chat>;
|
|
69
80
|
newLatestChatsCursor(opts: LatestChatsOptions): LatestChatsCursor;
|
|
70
81
|
latestChats(opts: LatestChatsOptions): Promise<LatestChatsPage>;
|
|
71
82
|
nextLatestChats(opts: NextLatestChatsOptions): Promise<LatestChatsPage>;
|
|
83
|
+
searchChatRoomMembers(opts: SearchChatRoomMembersOptions): Promise<SearchChatRoomMembersPage>;
|
|
72
84
|
chatRoomMembers(opts: ChatRoomMembersOptions): Promise<ChatRoomMember[]>;
|
|
73
85
|
myChatRoomMembers(opts: MyChatRoomMembersOptions): Promise<ChatRoomMember[]>;
|
|
74
86
|
openChatRoom(opts: OpenChatRoomOptions): Promise<ChatRoomSession>;
|
|
@@ -78,13 +90,19 @@ export interface ChatFXOperator {
|
|
|
78
90
|
export declare class ChatFX implements ChatFXOperator {
|
|
79
91
|
private chat;
|
|
80
92
|
constructor(opts: ChatFXOptions);
|
|
93
|
+
createChat(opts: CreateChatOptions): Promise<Chat>;
|
|
81
94
|
newLatestChatsCursor(opts: LatestChatsOptions): LatestChatsCursor;
|
|
82
95
|
latestChats(opts: LatestChatsOptions): Promise<LatestChatsPage>;
|
|
83
96
|
nextLatestChats(opts: NextLatestChatsOptions): Promise<LatestChatsPage>;
|
|
97
|
+
searchChatRoomMembers(opts: SearchChatRoomMembersOptions): Promise<SearchChatRoomMembersPage>;
|
|
84
98
|
chatRoomMembers(opts: ChatRoomMembersOptions): Promise<ChatRoomMember[]>;
|
|
85
99
|
myChatRoomMembers(opts: MyChatRoomMembersOptions): Promise<ChatRoomMember[]>;
|
|
86
100
|
openChatRoom(opts: OpenChatRoomOptions): Promise<ChatRoomSession>;
|
|
87
101
|
openGlobalChatRooms(opts: OpenGlobalChatRoomsOptions): Promise<GlobalChatRoomsSession>;
|
|
88
102
|
reactUploadedChat(opts: ReactUploadedChatOptions): Promise<void>;
|
|
103
|
+
private openChatRoomConnections;
|
|
104
|
+
private openGlobalChatRoomsConnection;
|
|
105
|
+
private findChat;
|
|
89
106
|
}
|
|
90
107
|
export declare function newChatFX(opts: ChatFXOptions): ChatFX;
|
|
108
|
+
export {};
|
package/dist/src/chat/chat.js
CHANGED
|
@@ -3,16 +3,31 @@
|
|
|
3
3
|
// Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
4
4
|
// not use this file except in compliance with the License. You may obtain
|
|
5
5
|
// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
|
|
6
|
-
import {
|
|
6
|
+
import { ZeroID, } from "@fabriktor/schema";
|
|
7
|
+
import { errResolveFailed, errValidation } from "../core/err.js";
|
|
7
8
|
import { normalizeSearchPage, normalizeSearchPerPage, runSearch, } from "../core/search.js";
|
|
8
9
|
const closeCodeNormal = 1000;
|
|
9
10
|
const closeCodeLeavingPage = 1001;
|
|
11
|
+
const wsRefreshMilliseconds = 40 * 60 * 1000;
|
|
12
|
+
const wsRefreshRetryMilliseconds = 30 * 1000;
|
|
13
|
+
const resourceChatID = "chat_id";
|
|
14
|
+
const resourceChat = "chat";
|
|
15
|
+
const msgCreatedChatIDMissing = "Created chat response did not contain an ID.";
|
|
16
|
+
const msgChatMissing = "Chat could not be resolved after the mutation.";
|
|
10
17
|
const msgChatObjectsUploading = "Cannot react to a chat while objects are still uploading.";
|
|
11
18
|
export class ChatFX {
|
|
12
19
|
chat;
|
|
13
20
|
constructor(opts) {
|
|
14
21
|
this.chat = opts.chat;
|
|
15
22
|
}
|
|
23
|
+
async createChat(opts) {
|
|
24
|
+
const out = await this.chat.insertOneChat(opts);
|
|
25
|
+
const id = requiredOperationID(out, {
|
|
26
|
+
resource: resourceChatID,
|
|
27
|
+
msg: msgCreatedChatIDMissing,
|
|
28
|
+
});
|
|
29
|
+
return await this.findChat(id);
|
|
30
|
+
}
|
|
16
31
|
newLatestChatsCursor(opts) {
|
|
17
32
|
return {
|
|
18
33
|
chat_room_id: opts.chat_room_id,
|
|
@@ -45,6 +60,17 @@ export class ChatFX {
|
|
|
45
60
|
per_page: opts.cursor.per_page,
|
|
46
61
|
});
|
|
47
62
|
}
|
|
63
|
+
async searchChatRoomMembers(opts) {
|
|
64
|
+
return await runSearch({
|
|
65
|
+
search: (searchOpts) => this.chat.searchManyChatRoomMembers(searchOpts),
|
|
66
|
+
query: {
|
|
67
|
+
chat_room_id: opts.chat_room_id,
|
|
68
|
+
},
|
|
69
|
+
q: opts.q,
|
|
70
|
+
page: opts.page,
|
|
71
|
+
per_page: opts.per_page,
|
|
72
|
+
});
|
|
73
|
+
}
|
|
48
74
|
async chatRoomMembers(opts) {
|
|
49
75
|
const out = await this.chat.queryChatRoomMembers({
|
|
50
76
|
query: {
|
|
@@ -66,28 +92,66 @@ export class ChatFX {
|
|
|
66
92
|
chat_room_id: opts.chat_room_id,
|
|
67
93
|
per_page: opts.per_page,
|
|
68
94
|
});
|
|
69
|
-
const
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
95
|
+
const fx = this;
|
|
96
|
+
let current = await fx.openChatRoomConnections(opts);
|
|
97
|
+
let refresh_timer = null;
|
|
98
|
+
let closed = false;
|
|
99
|
+
current.activate();
|
|
100
|
+
scheduleRefresh();
|
|
101
|
+
async function refresh() {
|
|
102
|
+
refresh_timer = null;
|
|
103
|
+
if (closed) {
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
try {
|
|
107
|
+
const next = await fx.openChatRoomConnections(opts);
|
|
108
|
+
if (closed) {
|
|
109
|
+
next.close(closeCodeNormal);
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
const previous = current;
|
|
113
|
+
previous.deactivate();
|
|
114
|
+
current = next;
|
|
115
|
+
current.activate();
|
|
116
|
+
previous.close(closeCodeNormal);
|
|
117
|
+
scheduleRefresh();
|
|
118
|
+
}
|
|
119
|
+
catch {
|
|
120
|
+
scheduleRefresh(wsRefreshRetryMilliseconds);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
function scheduleRefresh(delay = wsRefreshMilliseconds) {
|
|
124
|
+
if (closed) {
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
refresh_timer = setTimeout(() => {
|
|
128
|
+
void refresh();
|
|
129
|
+
}, delay);
|
|
130
|
+
}
|
|
131
|
+
function closeSession(code, reason) {
|
|
132
|
+
if (closed) {
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
closed = true;
|
|
136
|
+
if (refresh_timer !== null) {
|
|
137
|
+
clearTimeout(refresh_timer);
|
|
138
|
+
refresh_timer = null;
|
|
139
|
+
}
|
|
140
|
+
current.close(code, reason);
|
|
141
|
+
}
|
|
82
142
|
return {
|
|
83
143
|
latest,
|
|
84
|
-
chats_ws
|
|
85
|
-
|
|
144
|
+
get chats_ws() {
|
|
145
|
+
return current.chats_ws;
|
|
146
|
+
},
|
|
147
|
+
get chat_room_ws() {
|
|
148
|
+
return current.chat_room_ws;
|
|
149
|
+
},
|
|
86
150
|
closeNormal(reason) {
|
|
87
|
-
|
|
151
|
+
closeSession(closeCodeNormal, reason);
|
|
88
152
|
},
|
|
89
153
|
closeLeavingPage(reason) {
|
|
90
|
-
|
|
154
|
+
closeSession(closeCodeLeavingPage, reason);
|
|
91
155
|
},
|
|
92
156
|
};
|
|
93
157
|
}
|
|
@@ -95,20 +159,63 @@ export class ChatFX {
|
|
|
95
159
|
const members = await this.myChatRoomMembers({
|
|
96
160
|
owner_id: opts.owner_id,
|
|
97
161
|
});
|
|
98
|
-
const
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
162
|
+
const fx = this;
|
|
163
|
+
let current = await fx.openGlobalChatRoomsConnection(opts);
|
|
164
|
+
let refresh_timer = null;
|
|
165
|
+
let closed = false;
|
|
166
|
+
current.activate();
|
|
167
|
+
scheduleRefresh();
|
|
168
|
+
async function refresh() {
|
|
169
|
+
refresh_timer = null;
|
|
170
|
+
if (closed) {
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
try {
|
|
174
|
+
const next = await fx.openGlobalChatRoomsConnection(opts);
|
|
175
|
+
if (closed) {
|
|
176
|
+
next.close(closeCodeNormal);
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
const previous = current;
|
|
180
|
+
previous.deactivate();
|
|
181
|
+
current = next;
|
|
182
|
+
current.activate();
|
|
183
|
+
previous.close(closeCodeNormal);
|
|
184
|
+
scheduleRefresh();
|
|
185
|
+
}
|
|
186
|
+
catch {
|
|
187
|
+
scheduleRefresh(wsRefreshRetryMilliseconds);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
function scheduleRefresh(delay = wsRefreshMilliseconds) {
|
|
191
|
+
if (closed) {
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
refresh_timer = setTimeout(() => {
|
|
195
|
+
void refresh();
|
|
196
|
+
}, delay);
|
|
197
|
+
}
|
|
198
|
+
function closeSession(code, reason) {
|
|
199
|
+
if (closed) {
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
closed = true;
|
|
203
|
+
if (refresh_timer !== null) {
|
|
204
|
+
clearTimeout(refresh_timer);
|
|
205
|
+
refresh_timer = null;
|
|
206
|
+
}
|
|
207
|
+
current.close(code, reason);
|
|
208
|
+
}
|
|
104
209
|
return {
|
|
105
210
|
members,
|
|
106
|
-
chat_rooms_ws
|
|
211
|
+
get chat_rooms_ws() {
|
|
212
|
+
return current.chat_rooms_ws;
|
|
213
|
+
},
|
|
107
214
|
closeNormal(reason) {
|
|
108
|
-
|
|
215
|
+
closeSession(closeCodeNormal, reason);
|
|
109
216
|
},
|
|
110
217
|
closeLeavingPage(reason) {
|
|
111
|
-
|
|
218
|
+
closeSession(closeCodeLeavingPage, reason);
|
|
112
219
|
},
|
|
113
220
|
};
|
|
114
221
|
}
|
|
@@ -121,6 +228,139 @@ export class ChatFX {
|
|
|
121
228
|
}
|
|
122
229
|
await this.chat.reactChat(opts.reaction);
|
|
123
230
|
}
|
|
231
|
+
async openChatRoomConnections(opts) {
|
|
232
|
+
let active = false;
|
|
233
|
+
const chats_ws = await this.chat.wsChats({
|
|
234
|
+
owner_id: opts.owner_id,
|
|
235
|
+
chat_room_id: opts.chat_room_id,
|
|
236
|
+
on_message(v, ev) {
|
|
237
|
+
if (active) {
|
|
238
|
+
opts.on_chat(v, ev);
|
|
239
|
+
}
|
|
240
|
+
},
|
|
241
|
+
on_error: opts.on_error === undefined
|
|
242
|
+
? undefined
|
|
243
|
+
: (ev) => {
|
|
244
|
+
if (active) {
|
|
245
|
+
opts.on_error?.(ev);
|
|
246
|
+
}
|
|
247
|
+
},
|
|
248
|
+
on_close: opts.on_close === undefined
|
|
249
|
+
? undefined
|
|
250
|
+
: (ev) => {
|
|
251
|
+
if (active) {
|
|
252
|
+
opts.on_close?.(ev);
|
|
253
|
+
}
|
|
254
|
+
},
|
|
255
|
+
});
|
|
256
|
+
let chat_room_ws;
|
|
257
|
+
try {
|
|
258
|
+
chat_room_ws = await this.chat.wsChatRoom({
|
|
259
|
+
id: opts.chat_room_id,
|
|
260
|
+
on_message(v, ev) {
|
|
261
|
+
if (active) {
|
|
262
|
+
opts.on_chat_room(v, ev);
|
|
263
|
+
}
|
|
264
|
+
},
|
|
265
|
+
on_error: opts.on_error === undefined
|
|
266
|
+
? undefined
|
|
267
|
+
: (ev) => {
|
|
268
|
+
if (active) {
|
|
269
|
+
opts.on_error?.(ev);
|
|
270
|
+
}
|
|
271
|
+
},
|
|
272
|
+
on_close: opts.on_close === undefined
|
|
273
|
+
? undefined
|
|
274
|
+
: (ev) => {
|
|
275
|
+
if (active) {
|
|
276
|
+
opts.on_close?.(ev);
|
|
277
|
+
}
|
|
278
|
+
},
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
catch (cause) {
|
|
282
|
+
chats_ws.close(closeCodeNormal);
|
|
283
|
+
await Promise.allSettled([chats_ws.opened]);
|
|
284
|
+
throw cause;
|
|
285
|
+
}
|
|
286
|
+
try {
|
|
287
|
+
await Promise.all([chats_ws.opened, chat_room_ws.opened]);
|
|
288
|
+
}
|
|
289
|
+
catch (cause) {
|
|
290
|
+
closeWS([chats_ws, chat_room_ws], closeCodeNormal);
|
|
291
|
+
throw cause;
|
|
292
|
+
}
|
|
293
|
+
return {
|
|
294
|
+
chats_ws,
|
|
295
|
+
chat_room_ws,
|
|
296
|
+
activate() {
|
|
297
|
+
active = true;
|
|
298
|
+
},
|
|
299
|
+
deactivate() {
|
|
300
|
+
active = false;
|
|
301
|
+
},
|
|
302
|
+
close(code, reason) {
|
|
303
|
+
closeWS([chats_ws, chat_room_ws], code, reason);
|
|
304
|
+
},
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
async openGlobalChatRoomsConnection(opts) {
|
|
308
|
+
let active = false;
|
|
309
|
+
const chat_rooms_ws = await this.chat.wsMineChatRooms({
|
|
310
|
+
owner_id: opts.owner_id,
|
|
311
|
+
on_message(v, ev) {
|
|
312
|
+
if (active) {
|
|
313
|
+
opts.on_chat_room(v, ev);
|
|
314
|
+
}
|
|
315
|
+
},
|
|
316
|
+
on_error: opts.on_error === undefined
|
|
317
|
+
? undefined
|
|
318
|
+
: (ev) => {
|
|
319
|
+
if (active) {
|
|
320
|
+
opts.on_error?.(ev);
|
|
321
|
+
}
|
|
322
|
+
},
|
|
323
|
+
on_close: opts.on_close === undefined
|
|
324
|
+
? undefined
|
|
325
|
+
: (ev) => {
|
|
326
|
+
if (active) {
|
|
327
|
+
opts.on_close?.(ev);
|
|
328
|
+
}
|
|
329
|
+
},
|
|
330
|
+
});
|
|
331
|
+
try {
|
|
332
|
+
await chat_rooms_ws.opened;
|
|
333
|
+
}
|
|
334
|
+
catch (cause) {
|
|
335
|
+
chat_rooms_ws.close(closeCodeNormal);
|
|
336
|
+
throw cause;
|
|
337
|
+
}
|
|
338
|
+
return {
|
|
339
|
+
chat_rooms_ws,
|
|
340
|
+
activate() {
|
|
341
|
+
active = true;
|
|
342
|
+
},
|
|
343
|
+
deactivate() {
|
|
344
|
+
active = false;
|
|
345
|
+
},
|
|
346
|
+
close(code, reason) {
|
|
347
|
+
chat_rooms_ws.close(code, reason);
|
|
348
|
+
},
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
async findChat(id) {
|
|
352
|
+
const out = await this.chat.findOneChat({
|
|
353
|
+
id,
|
|
354
|
+
});
|
|
355
|
+
const chat = out.value;
|
|
356
|
+
if (chat === undefined) {
|
|
357
|
+
throw errResolveFailed({
|
|
358
|
+
resource: resourceChat,
|
|
359
|
+
msg: msgChatMissing,
|
|
360
|
+
});
|
|
361
|
+
}
|
|
362
|
+
return chat;
|
|
363
|
+
}
|
|
124
364
|
}
|
|
125
365
|
export function newChatFX(opts) {
|
|
126
366
|
return new ChatFX(opts);
|
|
@@ -155,3 +395,20 @@ function closeWS(conns, code, reason) {
|
|
|
155
395
|
conn.close(code, reason);
|
|
156
396
|
}
|
|
157
397
|
}
|
|
398
|
+
function requiredOperationID(out, opts) {
|
|
399
|
+
const id = resolvedID(out.id);
|
|
400
|
+
if (id === undefined) {
|
|
401
|
+
throw errResolveFailed({
|
|
402
|
+
resource: opts.resource,
|
|
403
|
+
msg: opts.msg,
|
|
404
|
+
});
|
|
405
|
+
}
|
|
406
|
+
return id;
|
|
407
|
+
}
|
|
408
|
+
function resolvedID(value) {
|
|
409
|
+
const id = value?.trim();
|
|
410
|
+
if (id === undefined || id === "" || id === ZeroID) {
|
|
411
|
+
return undefined;
|
|
412
|
+
}
|
|
413
|
+
return id;
|
|
414
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fabriktor/fx",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.39",
|
|
4
4
|
"description": "",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"typescript": "^6.0.3"
|
|
18
18
|
},
|
|
19
19
|
"dependencies": {
|
|
20
|
-
"@fabriktor/client": "0.0.
|
|
20
|
+
"@fabriktor/client": "0.0.45",
|
|
21
21
|
"@fabriktor/schema": "0.0.32"
|
|
22
22
|
}
|
|
23
23
|
}
|