polfan-server-js-client 0.3.2 → 0.4.0

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.
Files changed (39) hide show
  1. package/.gitmodules +3 -3
  2. package/.idea/shelf/Uncommitted_changes_before_Checkout_at_05_08_2026_17_28_[Changes]/shelved.patch +447 -0
  3. package/.idea/shelf/Uncommitted_changes_before_Checkout_at_05_08_2026_17_28_[Changes]1/shelved.patch +0 -0
  4. package/.idea/shelf/Uncommitted_changes_before_Checkout_at_05_08_2026_17_28__Changes_.xml +4 -0
  5. package/.idea/workspace.xml +228 -49
  6. package/CODE_OF_CONDUCT.md +76 -76
  7. package/README.md +44 -44
  8. package/babel.config.js +5 -5
  9. package/build/index.cjs.js +181 -15
  10. package/build/index.cjs.js.map +1 -1
  11. package/build/index.umd.js +1 -1
  12. package/build/index.umd.js.map +1 -1
  13. package/build/types/index.d.ts +2 -1
  14. package/build/types/types/src/index.d.ts +3 -3
  15. package/build/types/types/src/schemes/SessionData.d.ts +6 -0
  16. package/build/types/types/src/schemes/User.d.ts +15 -0
  17. package/build/types/types/src/schemes/UserData.d.ts +5 -0
  18. package/build/types/types/src/schemes/commands/SetSessionData.d.ts +9 -1
  19. package/build/types/types/src/schemes/commands/SetUserData.d.ts +1 -0
  20. package/jest.config.ts +199 -199
  21. package/package.json +43 -43
  22. package/scripts/getPackageJson.js +24 -24
  23. package/src/FilesClient.ts +42 -42
  24. package/src/index.ts +31 -29
  25. package/src/state-tracker/ChatStateTracker.ts +71 -71
  26. package/src/state-tracker/UsersManager.ts +51 -51
  27. package/src/state-tracker/functions.ts +28 -28
  28. package/src/types/src/index.ts +313 -311
  29. package/src/types/src/schemes/SessionData.ts +8 -1
  30. package/src/types/src/schemes/User.ts +17 -1
  31. package/src/types/src/schemes/UserData.ts +5 -0
  32. package/src/types/src/schemes/commands/SetSessionData.ts +11 -2
  33. package/src/types/src/schemes/commands/SetUserData.ts +1 -0
  34. package/tests/async-utils.test.ts +29 -29
  35. package/tests/space-roles.test.ts +42 -42
  36. package/.idea/shelf/Uncommitted_changes_before_Checkout_at_26_07_2026_21_26_[Changes]/shelved.patch +0 -174
  37. package/.idea/shelf/Uncommitted_changes_before_Checkout_at_26_07_2026_21_26__Changes_.xml +0 -4
  38. package/.idea/shelf/Uncommitted_changes_before_rebase_[Changes]/shelved.patch +0 -18
  39. package/.idea/shelf/Uncommitted_changes_before_rebase__Changes_.xml +0 -4
@@ -1,25 +1,25 @@
1
- const fs = require('fs');
2
- const path = require('path');
3
-
4
- /**
5
- * A module to get package informations from package.json
6
- * @module getPackageJson
7
- * @param {...string} keys from package.json if no arguments passed it returns package.json content as object
8
- * @returns {object} with given keys or content of package.json as object
9
- */
10
-
11
- /**
12
- * Returns package info
13
- */
14
- const getPackageJson = function(...args) {
15
- const packageJSON = JSON.parse(fs.readFileSync(path.join(__dirname, '../package.json')));
16
- if (!args.length) {
17
- return packageJSON;
18
- }
19
- return args.reduce((out, key) => {
20
- out[key] = packageJSON[key];
21
- return out;
22
- }, {});
23
- };
24
-
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+
4
+ /**
5
+ * A module to get package informations from package.json
6
+ * @module getPackageJson
7
+ * @param {...string} keys from package.json if no arguments passed it returns package.json content as object
8
+ * @returns {object} with given keys or content of package.json as object
9
+ */
10
+
11
+ /**
12
+ * Returns package info
13
+ */
14
+ const getPackageJson = function(...args) {
15
+ const packageJSON = JSON.parse(fs.readFileSync(path.join(__dirname, '../package.json')));
16
+ if (!args.length) {
17
+ return packageJSON;
18
+ }
19
+ return args.reduce((out, key) => {
20
+ out[key] = packageJSON[key];
21
+ return out;
22
+ }, {});
23
+ };
24
+
25
25
  module.exports = getPackageJson;
@@ -1,43 +1,43 @@
1
- import {AbstractRestClient, RestClientResponse} from "./AbstractRestClient";
2
-
3
- export interface File {
4
- id: string;
5
- url: string;
6
- name: string;
7
- mime: string;
8
- size: number;
9
- width?: number;
10
- height?: number;
11
- }
12
-
13
- export class FilesClient extends AbstractRestClient {
14
- protected defaultUrl: string = 'https://files.devana.pl';
15
-
16
- public async uploadFile(file: globalThis.File | Blob): Promise<RestClientResponse<File>> {
17
- const name = encodeURIComponent((file as globalThis.File).name ?? '');
18
- let headers = {
19
- ...this.getAuthHeaders(),
20
- Accept: 'application/json',
21
- 'Content-Disposition': `attachment; filename="${name}"`,
22
- 'Content-Length': file.size
23
- };
24
-
25
- const response = await fetch(this.getUrl('files'), {
26
- method: 'POST',
27
- body: file,
28
- headers
29
- });
30
-
31
- return this.convertFetchResponse<File>(response);
32
- }
33
-
34
- public async getFileMeta(id: string): Promise<RestClientResponse<File>> {
35
- return this.send('GET', 'files/' + id);
36
- }
37
-
38
- public async getFileMetaBulk(ids: string[]): Promise<RestClientResponse<File[]>> {
39
- const searchParams = new URLSearchParams();
40
- ids.forEach(id => searchParams.append('id[]', id));
41
- return this.send('GET', 'files?' + searchParams);
42
- }
1
+ import {AbstractRestClient, RestClientResponse} from "./AbstractRestClient";
2
+
3
+ export interface File {
4
+ id: string;
5
+ url: string;
6
+ name: string;
7
+ mime: string;
8
+ size: number;
9
+ width?: number;
10
+ height?: number;
11
+ }
12
+
13
+ export class FilesClient extends AbstractRestClient {
14
+ protected defaultUrl: string = 'https://files.devana.pl';
15
+
16
+ public async uploadFile(file: globalThis.File | Blob): Promise<RestClientResponse<File>> {
17
+ const name = encodeURIComponent((file as globalThis.File).name ?? '');
18
+ let headers = {
19
+ ...this.getAuthHeaders(),
20
+ Accept: 'application/json',
21
+ 'Content-Disposition': `attachment; filename="${name}"`,
22
+ 'Content-Length': file.size
23
+ };
24
+
25
+ const response = await fetch(this.getUrl('files'), {
26
+ method: 'POST',
27
+ body: file,
28
+ headers
29
+ });
30
+
31
+ return this.convertFetchResponse<File>(response);
32
+ }
33
+
34
+ public async getFileMeta(id: string): Promise<RestClientResponse<File>> {
35
+ return this.send('GET', 'files/' + id);
36
+ }
37
+
38
+ public async getFileMetaBulk(ids: string[]): Promise<RestClientResponse<File[]>> {
39
+ const searchParams = new URLSearchParams();
40
+ ids.forEach(id => searchParams.append('id[]', id));
41
+ return this.send('GET', 'files?' + searchParams);
42
+ }
43
43
  }
package/src/index.ts CHANGED
@@ -1,30 +1,32 @@
1
- import {WebSocketChatClient} from "./WebSocketChatClient";
2
- import {WebApiChatClient} from "./WebApiChatClient";
3
- import {
4
- IndexedCollection,
5
- IndexedObjectCollection,
6
- ObservableIndexedCollection,
7
- ObservableIndexedObjectCollection
8
- } from "./IndexedObjectCollection";
9
- import {FilesClient, File} from "./FilesClient";
10
- import { Permissions, PermissionDefinition, Layer } from "./Permissions";
11
- import * as ChatTypes from './types/src';
12
- import {extractUserFromMember} from "./state-tracker/functions";
13
- import {AbstractRestClient} from "./AbstractRestClient";
14
- import {UnreadSummary} from "./state-tracker/FollowedTopicsManager";
15
-
16
- export {
17
- IndexedCollection, ObservableIndexedCollection,
18
- IndexedObjectCollection, ObservableIndexedObjectCollection,
19
- Permissions, PermissionDefinition, Layer,
20
- WebSocketChatClient, WebApiChatClient,
21
- FilesClient,
22
- AbstractRestClient,
23
- extractUserFromMember,
24
- };
25
-
26
- export type {
27
- ChatTypes,
28
- File,
29
- UnreadSummary,
1
+ import {WebSocketChatClient} from "./WebSocketChatClient";
2
+ import {WebApiChatClient} from "./WebApiChatClient";
3
+ import {
4
+ IndexedCollection,
5
+ IndexedObjectCollection,
6
+ ObservableIndexedCollection,
7
+ ObservableIndexedObjectCollection
8
+ } from "./IndexedObjectCollection";
9
+ import {FilesClient, File} from "./FilesClient";
10
+ import { Permissions, PermissionDefinition, Layer } from "./Permissions";
11
+ import * as ChatTypes from './types/src';
12
+ import {UserStatus} from './types/src';
13
+ import {extractUserFromMember} from "./state-tracker/functions";
14
+ import {AbstractRestClient} from "./AbstractRestClient";
15
+ import {UnreadSummary} from "./state-tracker/FollowedTopicsManager";
16
+
17
+ export {
18
+ IndexedCollection, ObservableIndexedCollection,
19
+ IndexedObjectCollection, ObservableIndexedObjectCollection,
20
+ Permissions, PermissionDefinition, Layer,
21
+ WebSocketChatClient, WebApiChatClient,
22
+ FilesClient,
23
+ AbstractRestClient,
24
+ extractUserFromMember,
25
+ UserStatus,
26
+ };
27
+
28
+ export type {
29
+ ChatTypes,
30
+ File,
31
+ UnreadSummary,
30
32
  };
@@ -1,72 +1,72 @@
1
- import {WebSocketChatClient} from "../WebSocketChatClient";
2
- import {Session, User} from "../types/src";
3
- import {RoomsManager} from "./RoomsManager";
4
- import {SpacesManager} from "./SpacesManager";
5
- import {PermissionsManager} from "./PermissionsManager";
6
- import {DeferredTask} from "./AsyncUtils";
7
- import {EmoticonsManager} from "./EmoticonsManager";
8
- import {UsersManager} from "./UsersManager";
9
- import {RelationshipsManager} from "./RelationshipsManager";
10
-
11
- export class ChatStateTracker {
12
- public readonly client: WebSocketChatClient;
13
-
14
- /**
15
- * State of your permissions.
16
- */
17
- public readonly permissions: PermissionsManager;
18
-
19
- /**
20
- * State of the rooms you are in.
21
- */
22
- public readonly rooms: RoomsManager;
23
-
24
- /**
25
- * State of the spaces you are in.
26
- */
27
- public readonly spaces: SpacesManager;
28
-
29
- /**
30
- * State of the emoticons (global and space-related).
31
- */
32
- public readonly emoticons: EmoticonsManager;
33
-
34
- /**
35
- * Users related state.
36
- */
37
- public readonly users: UsersManager;
38
-
39
- /**
40
- * State of relationships with other users.
41
- */
42
- public readonly relationships: RelationshipsManager;
43
-
44
- private _me: User = null;
45
- private readonly deferredSession = new DeferredTask();
46
-
47
- public constructor(client: WebSocketChatClient) {
48
- this.client = client;
49
- this.client.on('Session', ev => this.handleSession(ev));
50
-
51
- this.permissions = new PermissionsManager(this);
52
- this.rooms = new RoomsManager(this);
53
- this.spaces = new SpacesManager(this);
54
- this.emoticons = new EmoticonsManager(this);
55
- this.users = new UsersManager(this);
56
- this.relationships = new RelationshipsManager(this);
57
- }
58
-
59
- public get me(): User | null {
60
- return this._me;
61
- }
62
-
63
- public async getMe(): Promise<User> {
64
- await this.deferredSession.promise;
65
- return this._me;
66
- }
67
-
68
- private handleSession(ev: Session): void {
69
- this._me = ev.user;
70
- this.deferredSession.resolve();
71
- }
1
+ import {WebSocketChatClient} from "../WebSocketChatClient";
2
+ import {Session, User} from "../types/src";
3
+ import {RoomsManager} from "./RoomsManager";
4
+ import {SpacesManager} from "./SpacesManager";
5
+ import {PermissionsManager} from "./PermissionsManager";
6
+ import {DeferredTask} from "./AsyncUtils";
7
+ import {EmoticonsManager} from "./EmoticonsManager";
8
+ import {UsersManager} from "./UsersManager";
9
+ import {RelationshipsManager} from "./RelationshipsManager";
10
+
11
+ export class ChatStateTracker {
12
+ public readonly client: WebSocketChatClient;
13
+
14
+ /**
15
+ * State of your permissions.
16
+ */
17
+ public readonly permissions: PermissionsManager;
18
+
19
+ /**
20
+ * State of the rooms you are in.
21
+ */
22
+ public readonly rooms: RoomsManager;
23
+
24
+ /**
25
+ * State of the spaces you are in.
26
+ */
27
+ public readonly spaces: SpacesManager;
28
+
29
+ /**
30
+ * State of the emoticons (global and space-related).
31
+ */
32
+ public readonly emoticons: EmoticonsManager;
33
+
34
+ /**
35
+ * Users related state.
36
+ */
37
+ public readonly users: UsersManager;
38
+
39
+ /**
40
+ * State of relationships with other users.
41
+ */
42
+ public readonly relationships: RelationshipsManager;
43
+
44
+ private _me: User = null;
45
+ private readonly deferredSession = new DeferredTask();
46
+
47
+ public constructor(client: WebSocketChatClient) {
48
+ this.client = client;
49
+ this.client.on('Session', ev => this.handleSession(ev));
50
+
51
+ this.permissions = new PermissionsManager(this);
52
+ this.rooms = new RoomsManager(this);
53
+ this.spaces = new SpacesManager(this);
54
+ this.emoticons = new EmoticonsManager(this);
55
+ this.users = new UsersManager(this);
56
+ this.relationships = new RelationshipsManager(this);
57
+ }
58
+
59
+ public get me(): User | null {
60
+ return this._me;
61
+ }
62
+
63
+ public async getMe(): Promise<User> {
64
+ await this.deferredSession.promise;
65
+ return this._me;
66
+ }
67
+
68
+ private handleSession(ev: Session): void {
69
+ this._me = ev.user;
70
+ this.deferredSession.resolve();
71
+ }
72
72
  }
@@ -1,52 +1,52 @@
1
- import {ChatStateTracker} from "./ChatStateTracker";
2
- import {ObservableIndexedObjectCollection} from "../IndexedObjectCollection";
3
- import {RoomMember, Session, SpaceMember, User} from "../types/src";
4
- import {extractUserFromMember} from "./functions";
5
- import {EventTarget} from "../EventTarget";
6
-
7
- export class UsersManager {
8
- public readonly onlineStatus = new EventTarget<{ change: User }>();
9
-
10
- private readonly users: ObservableIndexedObjectCollection<User> = new ObservableIndexedObjectCollection('id');
11
-
12
- public constructor(private tracker: ChatStateTracker) {
13
- // RoomMemberUpdated & SpaceMemberUpdated events are not contains user object
14
- tracker.client.on('UserUpdated', event => this.handleUsers([event.user]));
15
- tracker.client.on('RoomMembersJoined', event => this.handleMembers(event.members));
16
- tracker.client.on('SpaceMemberJoined', event => this.handleMembers([event.member]));
17
- tracker.client.on('SpaceMembers', event => this.handleMembers(event.members));
18
- tracker.client.on('RoomMembers', event => this.handleMembers(event.members));
19
- tracker.client.on('Messages', event => this.handleUsers(event.messages.map(message => message.author.user)));
20
- tracker.client.on('NewMessage', event => this.handleUsers([event.message.author.user]));
21
- tracker.client.on('Session', event => this.handleSession(event));
22
- }
23
-
24
- /**
25
- * Get all available (cached) user objects at once.
26
- */
27
- public async getAvailable(): Promise<ObservableIndexedObjectCollection<User>> {
28
- return this.users;
29
- }
30
-
31
- private handleMembers(members: (RoomMember | SpaceMember)[]): void {
32
- this.handleUsers(members.map(extractUserFromMember));
33
- }
34
-
35
- private handleSession(session: Session): void {
36
- // Keep the "seen users" cache across reconnects so bound user lists do
37
- // not blank out; just ensure our own user is present/updated. Stale
38
- // entries are refreshed as member/message collections refetch.
39
- this.handleUsers([session.user]);
40
- }
41
-
42
- private handleUsers(users: User[]): void {
43
- users.forEach(newUser => {
44
- const oldUser = this.users.get(newUser.id);
45
- if (oldUser && oldUser.online !== newUser.online) {
46
- this.onlineStatus.emit('change', newUser);
47
- }
48
- });
49
-
50
- this.users.set(...users);
51
- }
1
+ import {ChatStateTracker} from "./ChatStateTracker";
2
+ import {ObservableIndexedObjectCollection} from "../IndexedObjectCollection";
3
+ import {RoomMember, Session, SpaceMember, User} from "../types/src";
4
+ import {extractUserFromMember} from "./functions";
5
+ import {EventTarget} from "../EventTarget";
6
+
7
+ export class UsersManager {
8
+ public readonly onlineStatus = new EventTarget<{ change: User }>();
9
+
10
+ private readonly users: ObservableIndexedObjectCollection<User> = new ObservableIndexedObjectCollection('id');
11
+
12
+ public constructor(private tracker: ChatStateTracker) {
13
+ // RoomMemberUpdated & SpaceMemberUpdated events are not contains user object
14
+ tracker.client.on('UserUpdated', event => this.handleUsers([event.user]));
15
+ tracker.client.on('RoomMembersJoined', event => this.handleMembers(event.members));
16
+ tracker.client.on('SpaceMemberJoined', event => this.handleMembers([event.member]));
17
+ tracker.client.on('SpaceMembers', event => this.handleMembers(event.members));
18
+ tracker.client.on('RoomMembers', event => this.handleMembers(event.members));
19
+ tracker.client.on('Messages', event => this.handleUsers(event.messages.map(message => message.author.user)));
20
+ tracker.client.on('NewMessage', event => this.handleUsers([event.message.author.user]));
21
+ tracker.client.on('Session', event => this.handleSession(event));
22
+ }
23
+
24
+ /**
25
+ * Get all available (cached) user objects at once.
26
+ */
27
+ public async getAvailable(): Promise<ObservableIndexedObjectCollection<User>> {
28
+ return this.users;
29
+ }
30
+
31
+ private handleMembers(members: (RoomMember | SpaceMember)[]): void {
32
+ this.handleUsers(members.map(extractUserFromMember));
33
+ }
34
+
35
+ private handleSession(session: Session): void {
36
+ // Keep the "seen users" cache across reconnects so bound user lists do
37
+ // not blank out; just ensure our own user is present/updated. Stale
38
+ // entries are refreshed as member/message collections refetch.
39
+ this.handleUsers([session.user]);
40
+ }
41
+
42
+ private handleUsers(users: User[]): void {
43
+ users.forEach(newUser => {
44
+ const oldUser = this.users.get(newUser.id);
45
+ if (oldUser && oldUser.status !== newUser.status) {
46
+ this.onlineStatus.emit('change', newUser);
47
+ }
48
+ });
49
+
50
+ this.users.set(...users);
51
+ }
52
52
  }
@@ -1,29 +1,29 @@
1
- import {Role, RoomMember, SpaceMember, User} from "../types/src";
2
-
3
- export function reorderRolesOnPriorityUpdate(allRoles: Role[], oldRole: Role, updatedRole: Role): Role[] {
4
- // If the priority has changed, adjust the rest of roles
5
- const increased = (updatedRole.priority - oldRole.priority) > 0;
6
- const decreased = ! increased;
7
- const changedRoles: Role[] = [];
8
-
9
- allRoles.forEach(role => {
10
- if (role.id === updatedRole.id) {
11
- // Skip the updated role
12
- return;
13
- }
14
- if (increased && oldRole.priority <= role.priority) {
15
- role.priority--;
16
- changedRoles.push(role);
17
- }
18
- if (decreased && updatedRole.priority <= role.priority) {
19
- role.priority++;
20
- changedRoles.push(role);
21
- }
22
- });
23
-
24
- return changedRoles;
25
- }
26
-
27
- export function extractUserFromMember(member: RoomMember | SpaceMember): User | null {
28
- return member.user ?? (member as RoomMember).spaceMember?.user;
1
+ import {Role, RoomMember, SpaceMember, User} from "../types/src";
2
+
3
+ export function reorderRolesOnPriorityUpdate(allRoles: Role[], oldRole: Role, updatedRole: Role): Role[] {
4
+ // If the priority has changed, adjust the rest of roles
5
+ const increased = (updatedRole.priority - oldRole.priority) > 0;
6
+ const decreased = ! increased;
7
+ const changedRoles: Role[] = [];
8
+
9
+ allRoles.forEach(role => {
10
+ if (role.id === updatedRole.id) {
11
+ // Skip the updated role
12
+ return;
13
+ }
14
+ if (increased && oldRole.priority <= role.priority) {
15
+ role.priority--;
16
+ changedRoles.push(role);
17
+ }
18
+ if (decreased && updatedRole.priority <= role.priority) {
19
+ role.priority++;
20
+ changedRoles.push(role);
21
+ }
22
+ });
23
+
24
+ return changedRoles;
25
+ }
26
+
27
+ export function extractUserFromMember(member: RoomMember | SpaceMember): User | null {
28
+ return member.user ?? (member as RoomMember).spaceMember?.user;
29
29
  }