@fabriktor/fx 0.0.46 → 0.0.48

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.
@@ -2,12 +2,14 @@ export type AuthEmailOptions = {
2
2
  email: string;
3
3
  password: string;
4
4
  };
5
+ export type UpdateAuthEmailOptions = Pick<AuthEmailOptions, "email">;
5
6
  export type AuthSession = {
6
7
  token: string;
7
8
  };
8
9
  export interface AuthOperator {
9
10
  signUp(opts: AuthEmailOptions): Promise<AuthSession>;
10
11
  signIn(opts: AuthEmailOptions): Promise<AuthSession>;
12
+ updateEmail(opts: UpdateAuthEmailOptions): Promise<void>;
11
13
  getToken(): Promise<string>;
12
14
  refreshToken(): Promise<string>;
13
15
  emailVerified(): boolean;
@@ -1,5 +1,5 @@
1
- import type { ChatOperator, UsersOperator, WSCloseHandler, WSConnection, WSErrorHandler, WSMessageHandler } from "@fabriktor/client";
2
- import type { Chat, ChatRoom, ChatRoomMember, User } from "@fabriktor/schema";
1
+ import type { ChatOperator, WSCloseHandler, WSConnection, WSErrorHandler, WSMessageHandler } from "@fabriktor/client";
2
+ import type { Chat, ChatRoom, ChatRoomMember } from "@fabriktor/schema";
3
3
  import { type SearchPage } from "../core/search.js";
4
4
  type ChatInsertOptions = Parameters<ChatOperator["insertOneChat"]>[0];
5
5
  type ChatReplaceOptions = Parameters<ChatOperator["replaceOneChat"]>[0];
@@ -10,7 +10,6 @@ export type MarkChatObjectsUploadedOptions = {
10
10
  export type ReactChatOptions = Parameters<ChatOperator["reactChat"]>[0];
11
11
  export type ChatFXOptions = {
12
12
  chat: ChatOperator;
13
- users: UsersOperator;
14
13
  };
15
14
  type SearchChatsScope = {
16
15
  chat_room_id: string;
@@ -61,13 +60,6 @@ export type ChatRoomMembersOptions = {
61
60
  export type MyChatRoomMembersOptions = {
62
61
  owner_id: string;
63
62
  };
64
- export type ChatRoomParticipantsOptions = {
65
- chat_room_ids: string[];
66
- };
67
- export type ChatRoomParticipants = {
68
- members: ChatRoomMember[];
69
- users: User[];
70
- };
71
63
  export type OpenChatRoomOptions = {
72
64
  owner_id: string;
73
65
  chat_room_id: string;
@@ -110,14 +102,12 @@ export interface ChatFXOperator {
110
102
  searchChatRoomMembers(opts: SearchChatRoomMembersOptions): Promise<SearchChatRoomMembersPage>;
111
103
  chatRoomMembers(opts: ChatRoomMembersOptions): Promise<ChatRoomMember[]>;
112
104
  myChatRoomMembers(opts: MyChatRoomMembersOptions): Promise<ChatRoomMember[]>;
113
- chatRoomParticipants(opts: ChatRoomParticipantsOptions): Promise<ChatRoomParticipants>;
114
105
  openChatRoom(opts: OpenChatRoomOptions): Promise<ChatRoomSession>;
115
106
  openGlobalChatRooms(opts: OpenGlobalChatRoomsOptions): Promise<GlobalChatRoomsSession>;
116
107
  reactUploadedChat(opts: ReactUploadedChatOptions): Promise<void>;
117
108
  }
118
109
  export declare class ChatFX implements ChatFXOperator {
119
110
  private chat;
120
- private users;
121
111
  constructor(opts: ChatFXOptions);
122
112
  createChat(opts: CreateChatOptions): Promise<Chat>;
123
113
  markChatObjectsUploaded(opts: MarkChatObjectsUploadedOptions): Promise<Chat>;
@@ -128,7 +118,6 @@ export declare class ChatFX implements ChatFXOperator {
128
118
  searchChatRoomMembers(opts: SearchChatRoomMembersOptions): Promise<SearchChatRoomMembersPage>;
129
119
  chatRoomMembers(opts: ChatRoomMembersOptions): Promise<ChatRoomMember[]>;
130
120
  myChatRoomMembers(opts: MyChatRoomMembersOptions): Promise<ChatRoomMember[]>;
131
- chatRoomParticipants(opts: ChatRoomParticipantsOptions): Promise<ChatRoomParticipants>;
132
121
  openChatRoom(opts: OpenChatRoomOptions): Promise<ChatRoomSession>;
133
122
  openGlobalChatRooms(opts: OpenGlobalChatRoomsOptions): Promise<GlobalChatRoomsSession>;
134
123
  reactUploadedChat(opts: ReactUploadedChatOptions): Promise<void>;
@@ -16,10 +16,8 @@ const msgSearchQueryRequired = "Search query is required.";
16
16
  const msgChatObjectsUploading = "Cannot react to a chat while objects are still uploading.";
17
17
  export class ChatFX {
18
18
  chat;
19
- users;
20
19
  constructor(opts) {
21
20
  this.chat = opts.chat;
22
- this.users = opts.users;
23
21
  }
24
22
  async createChat(opts) {
25
23
  const out = await this.chat.insertOneChat(opts);
@@ -113,29 +111,6 @@ export class ChatFX {
113
111
  });
114
112
  return out.value ?? [];
115
113
  }
116
- async chatRoomParticipants(opts) {
117
- const chat_room_ids = normalizeIDs(opts.chat_room_ids);
118
- if (chat_room_ids.length === 0) {
119
- return { members: [], users: [] };
120
- }
121
- const memberGroups = await Promise.all(chat_room_ids.map((chat_room_id) => this.chatRoomMembers({
122
- chat_room_id,
123
- })));
124
- const members = uniqueChatRoomMembers(memberGroups.flat());
125
- const owner_ids = normalizeIDs(members.map((member) => member.owner_id));
126
- if (owner_ids.length === 0) {
127
- return { members, users: [] };
128
- }
129
- const out = await this.users.queryUsers({
130
- query: {
131
- id: owner_ids,
132
- },
133
- });
134
- return {
135
- members,
136
- users: out.value ?? [],
137
- };
138
- }
139
114
  async openChatRoom(opts) {
140
115
  const latest = await this.latestChats({
141
116
  chat_room_id: opts.chat_room_id,
@@ -414,12 +389,6 @@ export class ChatFX {
414
389
  export function newChatFX(opts) {
415
390
  return new ChatFX(opts);
416
391
  }
417
- function normalizeIDs(ids) {
418
- return [...new Set(ids.map((id) => id.trim()).filter((id) => id !== ""))];
419
- }
420
- function uniqueChatRoomMembers(members) {
421
- return [...new Map(members.map((member) => [member.id, member])).values()];
422
- }
423
392
  function latestChatsPage(opts) {
424
393
  return {
425
394
  chats: opts.raw.results,
package/dist/src/fx.js CHANGED
@@ -53,7 +53,6 @@ export class FX {
53
53
  });
54
54
  this.chat = newChatFX({
55
55
  chat: opts.chat,
56
- users: opts.users,
57
56
  });
58
57
  this.contacts = newContactsFX({
59
58
  contacts: opts.contacts,
@@ -4,6 +4,7 @@ import type { UsersFXOperator } from "../users/users.js";
4
4
  type ConnectionInsert = Parameters<ConnectionsOperator["insertOneConnection"]>[0]["in"];
5
5
  type BootstrapInput = Parameters<UsersOperator["bootstrap"]>[0]["in"];
6
6
  type BootstrapResult = Awaited<ReturnType<UsersOperator["bootstrap"]>>;
7
+ type UpdateEmailOptions = Parameters<AuthOperator["updateEmail"]>[0];
7
8
  export declare const SignInMode: {
8
9
  readonly Email: "email";
9
10
  readonly Username: "username";
@@ -31,6 +32,7 @@ export interface SessionFXOperator {
31
32
  signUpAndBootstrap(opts: SignUpAndBootstrapOptions): Promise<BootstrapResult>;
32
33
  signIn(opts: SignInOptions): Promise<void>;
33
34
  signInWithEmail(opts: SignInWithEmailOptions): Promise<void>;
35
+ updateEmail(opts: UpdateEmailOptions): Promise<void>;
34
36
  checkEmailVerification(): Promise<boolean>;
35
37
  }
36
38
  export declare class SessionFX implements SessionFXOperator {
@@ -42,6 +44,7 @@ export declare class SessionFX implements SessionFXOperator {
42
44
  signUpAndBootstrap(opts: SignUpAndBootstrapOptions): Promise<BootstrapResult>;
43
45
  signIn(opts: SignInOptions): Promise<void>;
44
46
  signInWithEmail(opts: SignInWithEmailOptions): Promise<void>;
47
+ updateEmail(opts: UpdateEmailOptions): Promise<void>;
45
48
  checkEmailVerification(): Promise<boolean>;
46
49
  }
47
50
  export declare function newSessionFX(opts: SessionFXOptions): SessionFX;
@@ -49,6 +49,11 @@ export class SessionFX {
49
49
  });
50
50
  await this.users.sync();
51
51
  }
52
+ async updateEmail(opts) {
53
+ await this.auth.updateEmail(opts);
54
+ await this.auth.refreshToken();
55
+ await this.users.sync();
56
+ }
52
57
  async checkEmailVerification() {
53
58
  await this.auth.reloadUser();
54
59
  if (!this.auth.emailVerified()) {
@@ -1,11 +1,21 @@
1
1
  import type { GetByUIDOptions, OperationResultOf, TmpPhonesOperator, TmpUsernamesOperator, UsernameVerification, UsersOperator } from "@fabriktor/client";
2
- import type { User } from "@fabriktor/schema";
2
+ import type { InAddress, InUser, User } from "@fabriktor/schema";
3
3
  import { type RunSearchOptions, type SearchPage } from "../core/search.js";
4
4
  export type ResolveUsernameOptions = Parameters<UsersOperator["resolveUsername"]>[0];
5
5
  export type VerifyUsernameOptions = Parameters<TmpUsernamesOperator["verify"]>[0];
6
6
  export type GenerateUsernamesOptions = VerifyUsernameOptions;
7
7
  export type SearchUsersOptions = GetByUIDOptions & Omit<RunSearchOptions<User>, "search">;
8
8
  export type SearchUsersPage = SearchPage<User>;
9
+ export type UserProfileInput = Pick<InUser, "first_name" | "last_name" | "phone" | "company_name" | "privacy"> & InAddress;
10
+ export type UpdateUserProfileOptions = GetByUIDOptions & {
11
+ id: string;
12
+ in: UserProfileInput;
13
+ };
14
+ export type UpdateUserPhoneOptions = GetByUIDOptions & {
15
+ id: string;
16
+ phone: string;
17
+ code: string;
18
+ };
9
19
  export type SendPhoneCodeOptions = Parameters<TmpPhonesOperator["send"]>[0];
10
20
  export type VerifyPhoneCodeOptions = Parameters<TmpPhonesOperator["verify"]>[0];
11
21
  export type UsersFXOptions = {
@@ -15,6 +25,8 @@ export type UsersFXOptions = {
15
25
  };
16
26
  export interface UsersFXOperator {
17
27
  searchUsers(opts: SearchUsersOptions): Promise<SearchUsersPage>;
28
+ updateUserProfile(opts: UpdateUserProfileOptions): Promise<User>;
29
+ updateUserPhone(opts: UpdateUserPhoneOptions): Promise<User>;
18
30
  resolveUsername(opts: ResolveUsernameOptions): Promise<string>;
19
31
  verifyUsername(opts: VerifyUsernameOptions): Promise<OperationResultOf<UsernameVerification>>;
20
32
  generateUsernames(opts: GenerateUsernamesOptions): Promise<OperationResultOf<UsernameVerification>>;
@@ -27,10 +39,13 @@ export declare class UsersFX implements UsersFXOperator {
27
39
  private tmp_phones;
28
40
  constructor(opts: UsersFXOptions);
29
41
  searchUsers(opts: SearchUsersOptions): Promise<SearchUsersPage>;
42
+ updateUserProfile(opts: UpdateUserProfileOptions): Promise<User>;
43
+ updateUserPhone(opts: UpdateUserPhoneOptions): Promise<User>;
30
44
  resolveUsername(opts: ResolveUsernameOptions): Promise<string>;
31
45
  verifyUsername(opts: VerifyUsernameOptions): Promise<OperationResultOf<UsernameVerification>>;
32
46
  generateUsernames(opts: GenerateUsernamesOptions): Promise<OperationResultOf<UsernameVerification>>;
33
47
  sendPhoneCode(opts: SendPhoneCodeOptions): Promise<void>;
34
48
  verifyPhoneCode(opts: VerifyPhoneCodeOptions): Promise<void>;
49
+ private updatedUser;
35
50
  }
36
51
  export declare function newUsersFX(opts: UsersFXOptions): UsersFX;
@@ -10,7 +10,12 @@ import { normalizeUsername, validateUsernameFormat } from "./username.js";
10
10
  const msgUnableToResolveUsername = "Unable to resolve username.";
11
11
  const msgPhoneRequired = "Phone number is required.";
12
12
  const msgPhoneCodeRequired = "Verification code is required.";
13
+ const msgFirstNameRequired = "First name is required.";
14
+ const msgLastNameRequired = "Last name is required.";
15
+ const msgAddressRequired = "A complete address is required.";
16
+ const msgUserMissingAfterUpdate = "User could not be resolved after the profile update.";
13
17
  const resourceUsername = "username";
18
+ const resourceUser = "user";
14
19
  export class UsersFX {
15
20
  users;
16
21
  tmp_usernames;
@@ -30,6 +35,31 @@ export class UsersFX {
30
35
  }),
31
36
  });
32
37
  }
38
+ async updateUserProfile(opts) {
39
+ const input = normalizeUserProfileInput(opts.in);
40
+ validateUserProfileInput(input);
41
+ await this.users.replaceOneUser({
42
+ id: opts.id,
43
+ in: input,
44
+ });
45
+ return await this.updatedUser(opts.uid);
46
+ }
47
+ async updateUserPhone(opts) {
48
+ const phone = normalizePhone(opts.phone);
49
+ const code = normalizePhoneCode(opts.code);
50
+ if (!validatePhone(phone)) {
51
+ throw errPhoneRequired("phone");
52
+ }
53
+ if (!validatePhoneCode(code)) {
54
+ throw errPhoneCodeRequired("code");
55
+ }
56
+ await this.tmp_phones.verify({ phone, code });
57
+ await this.users.replaceOneUser({
58
+ id: opts.id,
59
+ in: { phone },
60
+ });
61
+ return await this.updatedUser(opts.uid);
62
+ }
33
63
  async resolveUsername(opts) {
34
64
  const username = normalizeUsername(opts.username);
35
65
  const out = await this.users.resolveUsername({
@@ -81,10 +111,81 @@ export class UsersFX {
81
111
  code,
82
112
  });
83
113
  }
114
+ async updatedUser(uid) {
115
+ const out = await this.users.getByUID({ uid });
116
+ const user = out.value?.[0];
117
+ if (!user) {
118
+ throw errResolveFailed({
119
+ resource: resourceUser,
120
+ msg: msgUserMissingAfterUpdate,
121
+ });
122
+ }
123
+ return user;
124
+ }
84
125
  }
85
126
  export function newUsersFX(opts) {
86
127
  return new UsersFX(opts);
87
128
  }
129
+ function normalizeUserProfileInput(input) {
130
+ return {
131
+ first_name: input.first_name.trim(),
132
+ last_name: input.last_name.trim(),
133
+ phone: input.phone.trim(),
134
+ company_name: input.company_name.trim(),
135
+ privacy: {
136
+ is_searchable: input.privacy.is_searchable,
137
+ is_messageable: input.privacy.is_messageable,
138
+ },
139
+ lat: input.lat,
140
+ lon: input.lon,
141
+ street: input.street.trim(),
142
+ city: input.city.trim(),
143
+ state: input.state.trim(),
144
+ postal_code: input.postal_code.trim(),
145
+ country: input.country.trim(),
146
+ formatted_address: input.formatted_address.trim(),
147
+ place_name: input.place_name.trim(),
148
+ place_id: input.place_id.trim(),
149
+ };
150
+ }
151
+ function validateUserProfileInput(input) {
152
+ if (!input.first_name) {
153
+ throw errRequired({
154
+ field: "first_name",
155
+ msg: msgFirstNameRequired,
156
+ });
157
+ }
158
+ if (!input.last_name) {
159
+ throw errRequired({
160
+ field: "last_name",
161
+ msg: msgLastNameRequired,
162
+ });
163
+ }
164
+ if (input.phone && !validatePhone(input.phone)) {
165
+ throw errValidation({
166
+ field: "phone",
167
+ msg: msgPhoneRequired,
168
+ });
169
+ }
170
+ if (!hasCompleteAddress(input)) {
171
+ throw errValidation({
172
+ field: "formatted_address",
173
+ msg: msgAddressRequired,
174
+ });
175
+ }
176
+ }
177
+ function hasCompleteAddress(input) {
178
+ return (input.lat !== 0 &&
179
+ input.lon !== 0 &&
180
+ input.street.length > 0 &&
181
+ input.city.length > 0 &&
182
+ input.state.length > 0 &&
183
+ input.postal_code.length > 0 &&
184
+ input.country.length > 0 &&
185
+ input.formatted_address.length > 0 &&
186
+ input.place_name.length > 0 &&
187
+ input.place_id.length > 0);
188
+ }
88
189
  function errUsernameResolveFailed(field) {
89
190
  return errResolveFailed({
90
191
  resource: resourceUsername,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fabriktor/fx",
3
- "version": "0.0.46",
3
+ "version": "0.0.48",
4
4
  "description": "![Fabriktor Logo](./img/fabriktor-character.gif)",
5
5
  "type": "module",
6
6
  "exports": {