@fabriktor/fx 0.0.38 → 0.0.40
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/billable/billable.d.ts +164 -1
- package/dist/src/billable/billable.js +436 -0
- package/dist/src/calls/calls.d.ts +11 -0
- package/dist/src/calls/calls.js +26 -0
- package/dist/src/chat/chat.d.ts +41 -2
- package/dist/src/chat/chat.js +293 -28
- package/dist/src/contacts/contacts.d.ts +12 -2
- package/dist/src/contacts/contacts.js +33 -7
- package/dist/src/core/id.d.ts +3 -0
- package/dist/src/core/id.js +24 -0
- package/dist/src/feed/feed.d.ts +39 -3
- package/dist/src/feed/feed.js +85 -7
- package/dist/src/fulfillments/fulfillments.d.ts +32 -0
- package/dist/src/fulfillments/fulfillments.js +54 -0
- package/dist/src/fx.d.ts +5 -1
- package/dist/src/fx.js +11 -1
- package/dist/src/index.d.ts +2 -0
- package/dist/src/index.js +2 -0
- package/dist/src/jobs/jobs.d.ts +52 -4
- package/dist/src/jobs/jobs.js +120 -9
- package/dist/src/marketplace/marketplace.d.ts +29 -1
- package/dist/src/marketplace/marketplace.js +64 -1
- package/dist/src/memos/memos.js +4 -28
- package/dist/src/payments/payments.d.ts +112 -1
- package/dist/src/payments/payments.js +291 -15
- package/dist/src/payrolls/payrolls.d.ts +134 -1
- package/dist/src/payrolls/payrolls.js +364 -0
- package/dist/src/preferences/preferences.d.ts +83 -1
- package/dist/src/preferences/preferences.js +227 -0
- package/dist/src/privacy/privacy.d.ts +21 -2
- package/dist/src/privacy/privacy.js +56 -5
- package/dist/src/routes/routes.d.ts +63 -3
- package/dist/src/routes/routes.js +151 -1
- package/dist/src/timesheets/timesheets.d.ts +11 -1
- package/dist/src/timesheets/timesheets.js +39 -1
- package/package.json +2 -2
package/dist/src/calls/calls.js
CHANGED
|
@@ -4,7 +4,11 @@
|
|
|
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
6
|
import { errResolveFailed } from "../core/err.js";
|
|
7
|
+
import { requiredOperationID } from "../core/id.js";
|
|
8
|
+
const resourceEvent = "event";
|
|
7
9
|
const resourceVideoToken = "video_token";
|
|
10
|
+
const msgCreatedEventIDMissing = "Created event response did not contain an ID.";
|
|
11
|
+
const msgEventMissing = "Event could not be resolved after the mutation.";
|
|
8
12
|
const msgUnableToMintVideoToken = "Unable to mint video token.";
|
|
9
13
|
export class CallsFX {
|
|
10
14
|
calls;
|
|
@@ -13,6 +17,15 @@ export class CallsFX {
|
|
|
13
17
|
this.calls = opts.calls;
|
|
14
18
|
this.notifications = opts.notifications;
|
|
15
19
|
}
|
|
20
|
+
async createEvent(opts) {
|
|
21
|
+
const out = await this.calls.insertOneEvent(opts);
|
|
22
|
+
const id = requiredOperationID(out, msgCreatedEventIDMissing);
|
|
23
|
+
return await this.findEvent(id);
|
|
24
|
+
}
|
|
25
|
+
async replaceEvent(opts) {
|
|
26
|
+
await this.calls.replaceOneEvent(opts);
|
|
27
|
+
return await this.findEvent(opts.id);
|
|
28
|
+
}
|
|
16
29
|
async startCall(opts) {
|
|
17
30
|
const token = await this.requireVideoToken(opts.mint);
|
|
18
31
|
await this.notifications.relayNotification(opts.notification);
|
|
@@ -26,6 +39,19 @@ export class CallsFX {
|
|
|
26
39
|
async cancelCall(opts) {
|
|
27
40
|
await this.notifications.relayNotification(opts.notification);
|
|
28
41
|
}
|
|
42
|
+
async findEvent(id) {
|
|
43
|
+
const out = await this.calls.findOneEvent({
|
|
44
|
+
id,
|
|
45
|
+
});
|
|
46
|
+
const event = out.value;
|
|
47
|
+
if (event === undefined) {
|
|
48
|
+
throw errResolveFailed({
|
|
49
|
+
resource: resourceEvent,
|
|
50
|
+
msg: msgEventMissing,
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
return event;
|
|
54
|
+
}
|
|
29
55
|
async requireVideoToken(opts) {
|
|
30
56
|
const out = await this.calls.mintVideoToken(opts);
|
|
31
57
|
const token = out.value?.trim();
|
package/dist/src/chat/chat.d.ts
CHANGED
|
@@ -1,9 +1,29 @@
|
|
|
1
1
|
import type { ChatOperator, WSCloseHandler, WSConnection, WSErrorHandler, WSMessageHandler } from "@fabriktor/client";
|
|
2
2
|
import type { Chat, ChatRoom, ChatRoomMember } from "@fabriktor/schema";
|
|
3
|
+
import { type SearchPage } from "../core/search.js";
|
|
4
|
+
type ChatInsertOptions = Parameters<ChatOperator["insertOneChat"]>[0];
|
|
5
|
+
type ChatReplaceOptions = Parameters<ChatOperator["replaceOneChat"]>[0];
|
|
6
|
+
export type CreateChatOptions = ChatInsertOptions;
|
|
7
|
+
export type MarkChatObjectsUploadedOptions = {
|
|
8
|
+
id: ChatReplaceOptions["id"];
|
|
9
|
+
};
|
|
3
10
|
export type ReactChatOptions = Parameters<ChatOperator["reactChat"]>[0];
|
|
4
11
|
export type ChatFXOptions = {
|
|
5
12
|
chat: ChatOperator;
|
|
6
13
|
};
|
|
14
|
+
type SearchChatsScope = {
|
|
15
|
+
chat_room_id: string;
|
|
16
|
+
owner_id?: string;
|
|
17
|
+
} | {
|
|
18
|
+
chat_room_id?: string;
|
|
19
|
+
owner_id: string;
|
|
20
|
+
};
|
|
21
|
+
export type SearchChatsOptions = SearchChatsScope & {
|
|
22
|
+
q: string;
|
|
23
|
+
page?: number;
|
|
24
|
+
per_page?: number;
|
|
25
|
+
};
|
|
26
|
+
export type SearchChatsPage = SearchPage<Chat>;
|
|
7
27
|
export type LatestChatsOptions = {
|
|
8
28
|
chat_room_id: string;
|
|
9
29
|
page?: number;
|
|
@@ -27,6 +47,13 @@ export type LatestChatsPage = {
|
|
|
27
47
|
has_more: boolean;
|
|
28
48
|
next_cursor: LatestChatsCursor;
|
|
29
49
|
};
|
|
50
|
+
export type SearchChatRoomMembersOptions = {
|
|
51
|
+
chat_room_id: string;
|
|
52
|
+
q?: string;
|
|
53
|
+
page?: number;
|
|
54
|
+
per_page?: number;
|
|
55
|
+
};
|
|
56
|
+
export type SearchChatRoomMembersPage = SearchPage<ChatRoomMember>;
|
|
30
57
|
export type ChatRoomMembersOptions = {
|
|
31
58
|
chat_room_id: string;
|
|
32
59
|
};
|
|
@@ -53,22 +80,26 @@ export type ReactUploadedChatOptions = {
|
|
|
53
80
|
reaction: ReactChatOptions;
|
|
54
81
|
};
|
|
55
82
|
export type ChatRoomSession = {
|
|
56
|
-
latest: LatestChatsPage;
|
|
57
83
|
chats_ws: WSConnection;
|
|
58
84
|
chat_room_ws: WSConnection;
|
|
85
|
+
latest: LatestChatsPage;
|
|
59
86
|
closeNormal(reason?: string): void;
|
|
60
87
|
closeLeavingPage(reason?: string): void;
|
|
61
88
|
};
|
|
62
89
|
export type GlobalChatRoomsSession = {
|
|
63
|
-
members: ChatRoomMember[];
|
|
64
90
|
chat_rooms_ws: WSConnection;
|
|
91
|
+
members: ChatRoomMember[];
|
|
65
92
|
closeNormal(reason?: string): void;
|
|
66
93
|
closeLeavingPage(reason?: string): void;
|
|
67
94
|
};
|
|
68
95
|
export interface ChatFXOperator {
|
|
96
|
+
createChat(opts: CreateChatOptions): Promise<Chat>;
|
|
97
|
+
markChatObjectsUploaded(opts: MarkChatObjectsUploadedOptions): Promise<Chat>;
|
|
98
|
+
searchChats(opts: SearchChatsOptions): Promise<SearchChatsPage>;
|
|
69
99
|
newLatestChatsCursor(opts: LatestChatsOptions): LatestChatsCursor;
|
|
70
100
|
latestChats(opts: LatestChatsOptions): Promise<LatestChatsPage>;
|
|
71
101
|
nextLatestChats(opts: NextLatestChatsOptions): Promise<LatestChatsPage>;
|
|
102
|
+
searchChatRoomMembers(opts: SearchChatRoomMembersOptions): Promise<SearchChatRoomMembersPage>;
|
|
72
103
|
chatRoomMembers(opts: ChatRoomMembersOptions): Promise<ChatRoomMember[]>;
|
|
73
104
|
myChatRoomMembers(opts: MyChatRoomMembersOptions): Promise<ChatRoomMember[]>;
|
|
74
105
|
openChatRoom(opts: OpenChatRoomOptions): Promise<ChatRoomSession>;
|
|
@@ -78,13 +109,21 @@ export interface ChatFXOperator {
|
|
|
78
109
|
export declare class ChatFX implements ChatFXOperator {
|
|
79
110
|
private chat;
|
|
80
111
|
constructor(opts: ChatFXOptions);
|
|
112
|
+
createChat(opts: CreateChatOptions): Promise<Chat>;
|
|
113
|
+
markChatObjectsUploaded(opts: MarkChatObjectsUploadedOptions): Promise<Chat>;
|
|
114
|
+
searchChats(opts: SearchChatsOptions): Promise<SearchChatsPage>;
|
|
81
115
|
newLatestChatsCursor(opts: LatestChatsOptions): LatestChatsCursor;
|
|
82
116
|
latestChats(opts: LatestChatsOptions): Promise<LatestChatsPage>;
|
|
83
117
|
nextLatestChats(opts: NextLatestChatsOptions): Promise<LatestChatsPage>;
|
|
118
|
+
searchChatRoomMembers(opts: SearchChatRoomMembersOptions): Promise<SearchChatRoomMembersPage>;
|
|
84
119
|
chatRoomMembers(opts: ChatRoomMembersOptions): Promise<ChatRoomMember[]>;
|
|
85
120
|
myChatRoomMembers(opts: MyChatRoomMembersOptions): Promise<ChatRoomMember[]>;
|
|
86
121
|
openChatRoom(opts: OpenChatRoomOptions): Promise<ChatRoomSession>;
|
|
87
122
|
openGlobalChatRooms(opts: OpenGlobalChatRoomsOptions): Promise<GlobalChatRoomsSession>;
|
|
88
123
|
reactUploadedChat(opts: ReactUploadedChatOptions): Promise<void>;
|
|
124
|
+
private openChatRoomConnections;
|
|
125
|
+
private openGlobalChatRoomsConnection;
|
|
126
|
+
private findChat;
|
|
89
127
|
}
|
|
90
128
|
export declare function newChatFX(opts: ChatFXOptions): ChatFX;
|
|
129
|
+
export {};
|
package/dist/src/chat/chat.js
CHANGED
|
@@ -3,16 +3,56 @@
|
|
|
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 { errValidation } from "../core/err.js";
|
|
7
|
-
import {
|
|
6
|
+
import { errRequired, errResolveFailed, errValidation } from "../core/err.js";
|
|
7
|
+
import { requiredOperationID } from "../core/id.js";
|
|
8
|
+
import { normalizeSearchPage, normalizeSearchPerPage, normalizeSearchQuery, 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 resourceChat = "chat";
|
|
14
|
+
const msgCreatedChatIDMissing = "Created chat response did not contain an ID.";
|
|
15
|
+
const msgChatMissing = "Chat could not be resolved after the mutation.";
|
|
16
|
+
const msgSearchQueryRequired = "Search query is required.";
|
|
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, msgCreatedChatIDMissing);
|
|
26
|
+
return await this.findChat(id);
|
|
27
|
+
}
|
|
28
|
+
async markChatObjectsUploaded(opts) {
|
|
29
|
+
await this.chat.replaceOneChat({
|
|
30
|
+
id: opts.id,
|
|
31
|
+
in: {
|
|
32
|
+
objects_uploaded: true,
|
|
33
|
+
},
|
|
34
|
+
});
|
|
35
|
+
return await this.findChat(opts.id);
|
|
36
|
+
}
|
|
37
|
+
async searchChats(opts) {
|
|
38
|
+
const q = normalizeSearchQuery(opts.q);
|
|
39
|
+
if (q === undefined) {
|
|
40
|
+
throw errRequired({
|
|
41
|
+
field: "q",
|
|
42
|
+
msg: msgSearchQueryRequired,
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
return await runSearch({
|
|
46
|
+
search: (searchOpts) => this.chat.searchManyChats(searchOpts),
|
|
47
|
+
query: {
|
|
48
|
+
chat_room_id: opts.chat_room_id,
|
|
49
|
+
owner_id: opts.owner_id,
|
|
50
|
+
},
|
|
51
|
+
q,
|
|
52
|
+
page: opts.page,
|
|
53
|
+
per_page: opts.per_page,
|
|
54
|
+
});
|
|
55
|
+
}
|
|
16
56
|
newLatestChatsCursor(opts) {
|
|
17
57
|
return {
|
|
18
58
|
chat_room_id: opts.chat_room_id,
|
|
@@ -45,6 +85,17 @@ export class ChatFX {
|
|
|
45
85
|
per_page: opts.cursor.per_page,
|
|
46
86
|
});
|
|
47
87
|
}
|
|
88
|
+
async searchChatRoomMembers(opts) {
|
|
89
|
+
return await runSearch({
|
|
90
|
+
search: (searchOpts) => this.chat.searchManyChatRoomMembers(searchOpts),
|
|
91
|
+
query: {
|
|
92
|
+
chat_room_id: opts.chat_room_id,
|
|
93
|
+
},
|
|
94
|
+
q: opts.q,
|
|
95
|
+
page: opts.page,
|
|
96
|
+
per_page: opts.per_page,
|
|
97
|
+
});
|
|
98
|
+
}
|
|
48
99
|
async chatRoomMembers(opts) {
|
|
49
100
|
const out = await this.chat.queryChatRoomMembers({
|
|
50
101
|
query: {
|
|
@@ -66,28 +117,66 @@ export class ChatFX {
|
|
|
66
117
|
chat_room_id: opts.chat_room_id,
|
|
67
118
|
per_page: opts.per_page,
|
|
68
119
|
});
|
|
69
|
-
const
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
120
|
+
const fx = this;
|
|
121
|
+
let current = await fx.openChatRoomConnections(opts);
|
|
122
|
+
let refresh_timer = null;
|
|
123
|
+
let closed = false;
|
|
124
|
+
current.activate();
|
|
125
|
+
scheduleRefresh();
|
|
126
|
+
async function refresh() {
|
|
127
|
+
refresh_timer = null;
|
|
128
|
+
if (closed) {
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
try {
|
|
132
|
+
const next = await fx.openChatRoomConnections(opts);
|
|
133
|
+
if (closed) {
|
|
134
|
+
next.close(closeCodeNormal);
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
const previous = current;
|
|
138
|
+
previous.deactivate();
|
|
139
|
+
current = next;
|
|
140
|
+
current.activate();
|
|
141
|
+
previous.close(closeCodeNormal);
|
|
142
|
+
scheduleRefresh();
|
|
143
|
+
}
|
|
144
|
+
catch {
|
|
145
|
+
scheduleRefresh(wsRefreshRetryMilliseconds);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
function scheduleRefresh(delay = wsRefreshMilliseconds) {
|
|
149
|
+
if (closed) {
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
refresh_timer = setTimeout(() => {
|
|
153
|
+
void refresh();
|
|
154
|
+
}, delay);
|
|
155
|
+
}
|
|
156
|
+
function closeSession(code, reason) {
|
|
157
|
+
if (closed) {
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
closed = true;
|
|
161
|
+
if (refresh_timer !== null) {
|
|
162
|
+
clearTimeout(refresh_timer);
|
|
163
|
+
refresh_timer = null;
|
|
164
|
+
}
|
|
165
|
+
current.close(code, reason);
|
|
166
|
+
}
|
|
82
167
|
return {
|
|
83
168
|
latest,
|
|
84
|
-
chats_ws
|
|
85
|
-
|
|
169
|
+
get chats_ws() {
|
|
170
|
+
return current.chats_ws;
|
|
171
|
+
},
|
|
172
|
+
get chat_room_ws() {
|
|
173
|
+
return current.chat_room_ws;
|
|
174
|
+
},
|
|
86
175
|
closeNormal(reason) {
|
|
87
|
-
|
|
176
|
+
closeSession(closeCodeNormal, reason);
|
|
88
177
|
},
|
|
89
178
|
closeLeavingPage(reason) {
|
|
90
|
-
|
|
179
|
+
closeSession(closeCodeLeavingPage, reason);
|
|
91
180
|
},
|
|
92
181
|
};
|
|
93
182
|
}
|
|
@@ -95,20 +184,63 @@ export class ChatFX {
|
|
|
95
184
|
const members = await this.myChatRoomMembers({
|
|
96
185
|
owner_id: opts.owner_id,
|
|
97
186
|
});
|
|
98
|
-
const
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
187
|
+
const fx = this;
|
|
188
|
+
let current = await fx.openGlobalChatRoomsConnection(opts);
|
|
189
|
+
let refresh_timer = null;
|
|
190
|
+
let closed = false;
|
|
191
|
+
current.activate();
|
|
192
|
+
scheduleRefresh();
|
|
193
|
+
async function refresh() {
|
|
194
|
+
refresh_timer = null;
|
|
195
|
+
if (closed) {
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
try {
|
|
199
|
+
const next = await fx.openGlobalChatRoomsConnection(opts);
|
|
200
|
+
if (closed) {
|
|
201
|
+
next.close(closeCodeNormal);
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
const previous = current;
|
|
205
|
+
previous.deactivate();
|
|
206
|
+
current = next;
|
|
207
|
+
current.activate();
|
|
208
|
+
previous.close(closeCodeNormal);
|
|
209
|
+
scheduleRefresh();
|
|
210
|
+
}
|
|
211
|
+
catch {
|
|
212
|
+
scheduleRefresh(wsRefreshRetryMilliseconds);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
function scheduleRefresh(delay = wsRefreshMilliseconds) {
|
|
216
|
+
if (closed) {
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
refresh_timer = setTimeout(() => {
|
|
220
|
+
void refresh();
|
|
221
|
+
}, delay);
|
|
222
|
+
}
|
|
223
|
+
function closeSession(code, reason) {
|
|
224
|
+
if (closed) {
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
closed = true;
|
|
228
|
+
if (refresh_timer !== null) {
|
|
229
|
+
clearTimeout(refresh_timer);
|
|
230
|
+
refresh_timer = null;
|
|
231
|
+
}
|
|
232
|
+
current.close(code, reason);
|
|
233
|
+
}
|
|
104
234
|
return {
|
|
105
235
|
members,
|
|
106
|
-
chat_rooms_ws
|
|
236
|
+
get chat_rooms_ws() {
|
|
237
|
+
return current.chat_rooms_ws;
|
|
238
|
+
},
|
|
107
239
|
closeNormal(reason) {
|
|
108
|
-
|
|
240
|
+
closeSession(closeCodeNormal, reason);
|
|
109
241
|
},
|
|
110
242
|
closeLeavingPage(reason) {
|
|
111
|
-
|
|
243
|
+
closeSession(closeCodeLeavingPage, reason);
|
|
112
244
|
},
|
|
113
245
|
};
|
|
114
246
|
}
|
|
@@ -121,6 +253,139 @@ export class ChatFX {
|
|
|
121
253
|
}
|
|
122
254
|
await this.chat.reactChat(opts.reaction);
|
|
123
255
|
}
|
|
256
|
+
async openChatRoomConnections(opts) {
|
|
257
|
+
let active = false;
|
|
258
|
+
const chats_ws = await this.chat.wsChats({
|
|
259
|
+
owner_id: opts.owner_id,
|
|
260
|
+
chat_room_id: opts.chat_room_id,
|
|
261
|
+
on_message(v, ev) {
|
|
262
|
+
if (active) {
|
|
263
|
+
opts.on_chat(v, ev);
|
|
264
|
+
}
|
|
265
|
+
},
|
|
266
|
+
on_error: opts.on_error === undefined
|
|
267
|
+
? undefined
|
|
268
|
+
: (ev) => {
|
|
269
|
+
if (active) {
|
|
270
|
+
opts.on_error?.(ev);
|
|
271
|
+
}
|
|
272
|
+
},
|
|
273
|
+
on_close: opts.on_close === undefined
|
|
274
|
+
? undefined
|
|
275
|
+
: (ev) => {
|
|
276
|
+
if (active) {
|
|
277
|
+
opts.on_close?.(ev);
|
|
278
|
+
}
|
|
279
|
+
},
|
|
280
|
+
});
|
|
281
|
+
let chat_room_ws;
|
|
282
|
+
try {
|
|
283
|
+
chat_room_ws = await this.chat.wsChatRoom({
|
|
284
|
+
id: opts.chat_room_id,
|
|
285
|
+
on_message(v, ev) {
|
|
286
|
+
if (active) {
|
|
287
|
+
opts.on_chat_room(v, ev);
|
|
288
|
+
}
|
|
289
|
+
},
|
|
290
|
+
on_error: opts.on_error === undefined
|
|
291
|
+
? undefined
|
|
292
|
+
: (ev) => {
|
|
293
|
+
if (active) {
|
|
294
|
+
opts.on_error?.(ev);
|
|
295
|
+
}
|
|
296
|
+
},
|
|
297
|
+
on_close: opts.on_close === undefined
|
|
298
|
+
? undefined
|
|
299
|
+
: (ev) => {
|
|
300
|
+
if (active) {
|
|
301
|
+
opts.on_close?.(ev);
|
|
302
|
+
}
|
|
303
|
+
},
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
catch (cause) {
|
|
307
|
+
chats_ws.close(closeCodeNormal);
|
|
308
|
+
await Promise.allSettled([chats_ws.opened]);
|
|
309
|
+
throw cause;
|
|
310
|
+
}
|
|
311
|
+
try {
|
|
312
|
+
await Promise.all([chats_ws.opened, chat_room_ws.opened]);
|
|
313
|
+
}
|
|
314
|
+
catch (cause) {
|
|
315
|
+
closeWS([chats_ws, chat_room_ws], closeCodeNormal);
|
|
316
|
+
throw cause;
|
|
317
|
+
}
|
|
318
|
+
return {
|
|
319
|
+
chats_ws,
|
|
320
|
+
chat_room_ws,
|
|
321
|
+
activate() {
|
|
322
|
+
active = true;
|
|
323
|
+
},
|
|
324
|
+
deactivate() {
|
|
325
|
+
active = false;
|
|
326
|
+
},
|
|
327
|
+
close(code, reason) {
|
|
328
|
+
closeWS([chats_ws, chat_room_ws], code, reason);
|
|
329
|
+
},
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
async openGlobalChatRoomsConnection(opts) {
|
|
333
|
+
let active = false;
|
|
334
|
+
const chat_rooms_ws = await this.chat.wsMineChatRooms({
|
|
335
|
+
owner_id: opts.owner_id,
|
|
336
|
+
on_message(v, ev) {
|
|
337
|
+
if (active) {
|
|
338
|
+
opts.on_chat_room(v, ev);
|
|
339
|
+
}
|
|
340
|
+
},
|
|
341
|
+
on_error: opts.on_error === undefined
|
|
342
|
+
? undefined
|
|
343
|
+
: (ev) => {
|
|
344
|
+
if (active) {
|
|
345
|
+
opts.on_error?.(ev);
|
|
346
|
+
}
|
|
347
|
+
},
|
|
348
|
+
on_close: opts.on_close === undefined
|
|
349
|
+
? undefined
|
|
350
|
+
: (ev) => {
|
|
351
|
+
if (active) {
|
|
352
|
+
opts.on_close?.(ev);
|
|
353
|
+
}
|
|
354
|
+
},
|
|
355
|
+
});
|
|
356
|
+
try {
|
|
357
|
+
await chat_rooms_ws.opened;
|
|
358
|
+
}
|
|
359
|
+
catch (cause) {
|
|
360
|
+
chat_rooms_ws.close(closeCodeNormal);
|
|
361
|
+
throw cause;
|
|
362
|
+
}
|
|
363
|
+
return {
|
|
364
|
+
chat_rooms_ws,
|
|
365
|
+
activate() {
|
|
366
|
+
active = true;
|
|
367
|
+
},
|
|
368
|
+
deactivate() {
|
|
369
|
+
active = false;
|
|
370
|
+
},
|
|
371
|
+
close(code, reason) {
|
|
372
|
+
chat_rooms_ws.close(code, reason);
|
|
373
|
+
},
|
|
374
|
+
};
|
|
375
|
+
}
|
|
376
|
+
async findChat(id) {
|
|
377
|
+
const out = await this.chat.findOneChat({
|
|
378
|
+
id,
|
|
379
|
+
});
|
|
380
|
+
const chat = out.value;
|
|
381
|
+
if (chat === undefined) {
|
|
382
|
+
throw errResolveFailed({
|
|
383
|
+
resource: resourceChat,
|
|
384
|
+
msg: msgChatMissing,
|
|
385
|
+
});
|
|
386
|
+
}
|
|
387
|
+
return chat;
|
|
388
|
+
}
|
|
124
389
|
}
|
|
125
390
|
export function newChatFX(opts) {
|
|
126
391
|
return new ChatFX(opts);
|
|
@@ -1,5 +1,9 @@
|
|
|
1
|
-
import { Friend, Hired, Manager, Na, OldHire, type Contact, type ID, type OperationResult } from "@fabriktor/schema";
|
|
2
1
|
import type { ContactsOperator } from "@fabriktor/client";
|
|
2
|
+
import { Friend, Hired, Manager, Na, OldHire, type Contact, type ID, type OperationResult } from "@fabriktor/schema";
|
|
3
|
+
type ContactInsertOptions = Parameters<ContactsOperator["insertOneContact"]>[0];
|
|
4
|
+
type ContactReplaceOptions = Parameters<ContactsOperator["replaceOneContact"]>[0];
|
|
5
|
+
export type CreateContactOptions = ContactInsertOptions;
|
|
6
|
+
export type ReplaceContactOptions = ContactReplaceOptions;
|
|
3
7
|
export type ContactRelation = typeof Na | typeof Friend | typeof OldHire | typeof Hired | typeof Manager;
|
|
4
8
|
export type ActiveContactRelation = typeof Friend | typeof Hired | typeof Manager;
|
|
5
9
|
export type ContactsFXOptions = {
|
|
@@ -42,6 +46,8 @@ export type EndHireRelationOptions = {
|
|
|
42
46
|
contact: Contact;
|
|
43
47
|
};
|
|
44
48
|
export interface ContactsFXOperator {
|
|
49
|
+
createContact(opts: CreateContactOptions): Promise<Contact>;
|
|
50
|
+
replaceContact(opts: ReplaceContactOptions): Promise<Contact>;
|
|
45
51
|
loadContactIndex(opts: LoadContactIndexOptions): Promise<ContactIndex>;
|
|
46
52
|
startPresence(opts: StartPresenceOptions): Promise<PresenceSession>;
|
|
47
53
|
sendFriendRequest(opts: SendFriendRequestOptions): Promise<OperationResult>;
|
|
@@ -54,6 +60,8 @@ export interface ContactsFXOperator {
|
|
|
54
60
|
export declare class ContactsFX implements ContactsFXOperator {
|
|
55
61
|
private contacts;
|
|
56
62
|
constructor(opts: ContactsFXOptions);
|
|
63
|
+
createContact(opts: CreateContactOptions): Promise<Contact>;
|
|
64
|
+
replaceContact(opts: ReplaceContactOptions): Promise<Contact>;
|
|
57
65
|
loadContactIndex(opts: LoadContactIndexOptions): Promise<ContactIndex>;
|
|
58
66
|
startPresence(opts: StartPresenceOptions): Promise<PresenceSession>;
|
|
59
67
|
sendFriendRequest(opts: SendFriendRequestOptions): Promise<OperationResult>;
|
|
@@ -62,6 +70,8 @@ export declare class ContactsFX implements ContactsFXOperator {
|
|
|
62
70
|
acceptContactRequest(opts: AcceptContactRequestOptions): Promise<OperationResult>;
|
|
63
71
|
rejectContactRequest(opts: RejectContactRequestOptions): Promise<OperationResult>;
|
|
64
72
|
endHireRelation(opts: EndHireRelationOptions): Promise<OperationResult>;
|
|
65
|
-
private
|
|
73
|
+
private replaceContactResult;
|
|
74
|
+
private findContact;
|
|
66
75
|
}
|
|
67
76
|
export declare function newContactsFX(opts: ContactsFXOptions): ContactsFX;
|
|
77
|
+
export {};
|
|
@@ -4,8 +4,12 @@
|
|
|
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
6
|
import { Friend, Na, OldHire, } from "@fabriktor/schema";
|
|
7
|
-
import { errRequired, errValidation } from "../core/err.js";
|
|
7
|
+
import { errRequired, errResolveFailed, errValidation } from "../core/err.js";
|
|
8
|
+
import { requiredOperationID } from "../core/id.js";
|
|
8
9
|
const presenceIntervalMS = 120_000;
|
|
10
|
+
const resourceContact = "contact";
|
|
11
|
+
const msgCreatedContactIDMissing = "Created contact response did not contain an ID.";
|
|
12
|
+
const msgContactMissing = "Contact could not be resolved after the mutation.";
|
|
9
13
|
const msgOwnerIDRequired = "Owner ID is required.";
|
|
10
14
|
const msgContactIDRequired = "Contact ID is required.";
|
|
11
15
|
const msgRelationUpgradeInvalid = "Requested relation must be higher than the current relation.";
|
|
@@ -16,6 +20,15 @@ export class ContactsFX {
|
|
|
16
20
|
constructor(opts) {
|
|
17
21
|
this.contacts = opts.contacts;
|
|
18
22
|
}
|
|
23
|
+
async createContact(opts) {
|
|
24
|
+
const out = await this.contacts.insertOneContact(opts);
|
|
25
|
+
const id = requiredOperationID(out, msgCreatedContactIDMissing);
|
|
26
|
+
return await this.findContact(id);
|
|
27
|
+
}
|
|
28
|
+
async replaceContact(opts) {
|
|
29
|
+
await this.contacts.replaceOneContact(opts);
|
|
30
|
+
return await this.findContact(opts.id);
|
|
31
|
+
}
|
|
19
32
|
async loadContactIndex(opts) {
|
|
20
33
|
const owner_id = opts.owner_id.trim();
|
|
21
34
|
if (owner_id === "") {
|
|
@@ -101,7 +114,7 @@ export class ContactsFX {
|
|
|
101
114
|
msg: msgRelationUpgradeInvalid,
|
|
102
115
|
});
|
|
103
116
|
}
|
|
104
|
-
return await this.
|
|
117
|
+
return await this.replaceContactResult(opts.contact, {
|
|
105
118
|
is_pending: true,
|
|
106
119
|
relation: opts.contact.relation,
|
|
107
120
|
requested_relation: opts.requested_relation,
|
|
@@ -115,7 +128,7 @@ export class ContactsFX {
|
|
|
115
128
|
msg: msgRelationDowngradeInvalid,
|
|
116
129
|
});
|
|
117
130
|
}
|
|
118
|
-
return await this.
|
|
131
|
+
return await this.replaceContactResult(opts.contact, {
|
|
119
132
|
is_pending: false,
|
|
120
133
|
relation: opts.relation,
|
|
121
134
|
requested_relation: Na,
|
|
@@ -129,7 +142,7 @@ export class ContactsFX {
|
|
|
129
142
|
msg: msgPendingRelationRequired,
|
|
130
143
|
});
|
|
131
144
|
}
|
|
132
|
-
return await this.
|
|
145
|
+
return await this.replaceContactResult(opts.contact, {
|
|
133
146
|
is_pending: false,
|
|
134
147
|
relation: opts.contact.requested_relation,
|
|
135
148
|
requested_relation: Na,
|
|
@@ -137,19 +150,19 @@ export class ContactsFX {
|
|
|
137
150
|
});
|
|
138
151
|
}
|
|
139
152
|
async rejectContactRequest(opts) {
|
|
140
|
-
return await this.
|
|
153
|
+
return await this.replaceContactResult(opts.contact, {
|
|
141
154
|
is_archived: true,
|
|
142
155
|
});
|
|
143
156
|
}
|
|
144
157
|
async endHireRelation(opts) {
|
|
145
|
-
return await this.
|
|
158
|
+
return await this.replaceContactResult(opts.contact, {
|
|
146
159
|
is_pending: false,
|
|
147
160
|
relation: OldHire,
|
|
148
161
|
requested_relation: Na,
|
|
149
162
|
is_archived: false,
|
|
150
163
|
});
|
|
151
164
|
}
|
|
152
|
-
async
|
|
165
|
+
async replaceContactResult(contact, overrides) {
|
|
153
166
|
return await this.contacts.replaceOneContact({
|
|
154
167
|
id: contact.id,
|
|
155
168
|
in: {
|
|
@@ -162,6 +175,19 @@ export class ContactsFX {
|
|
|
162
175
|
},
|
|
163
176
|
});
|
|
164
177
|
}
|
|
178
|
+
async findContact(id) {
|
|
179
|
+
const out = await this.contacts.findOneContact({
|
|
180
|
+
id,
|
|
181
|
+
});
|
|
182
|
+
const contact = out.value;
|
|
183
|
+
if (contact === undefined) {
|
|
184
|
+
throw errResolveFailed({
|
|
185
|
+
resource: resourceContact,
|
|
186
|
+
msg: msgContactMissing,
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
return contact;
|
|
190
|
+
}
|
|
165
191
|
}
|
|
166
192
|
export function newContactsFX(opts) {
|
|
167
193
|
return new ContactsFX(opts);
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// Copyright (C) Fabriktor, Inc. 2025-present.
|
|
2
|
+
//
|
|
3
|
+
// Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
4
|
+
// not use this file except in compliance with the License. You may obtain
|
|
5
|
+
// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
|
|
6
|
+
import { IDKey, ZeroID } from "@fabriktor/schema";
|
|
7
|
+
import { errResolveFailed } from "./err.js";
|
|
8
|
+
export function requiredOperationID(out, msg) {
|
|
9
|
+
const id = resolvedID(out.id);
|
|
10
|
+
if (id === undefined) {
|
|
11
|
+
throw errResolveFailed({
|
|
12
|
+
resource: IDKey,
|
|
13
|
+
msg,
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
return id;
|
|
17
|
+
}
|
|
18
|
+
export function resolvedID(value) {
|
|
19
|
+
const id = value?.trim();
|
|
20
|
+
if (id === undefined || id === "" || id === ZeroID) {
|
|
21
|
+
return undefined;
|
|
22
|
+
}
|
|
23
|
+
return id;
|
|
24
|
+
}
|