@glade-chat/glade.js 0.1.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 (38) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +52 -0
  3. package/package.json +57 -0
  4. package/src/client/Client.js +355 -0
  5. package/src/errors/index.js +59 -0
  6. package/src/gateway/Gateway.js +172 -0
  7. package/src/gateway/handleDispatch.js +259 -0
  8. package/src/index.js +66 -0
  9. package/src/managers/CachedManager.js +95 -0
  10. package/src/managers/ChannelManager.js +40 -0
  11. package/src/managers/DMManager.js +43 -0
  12. package/src/managers/FriendManager.js +89 -0
  13. package/src/managers/HouseManager.js +44 -0
  14. package/src/managers/InviteManager.js +46 -0
  15. package/src/managers/MemberManager.js +41 -0
  16. package/src/managers/MessageManager.js +69 -0
  17. package/src/managers/RoleManager.js +65 -0
  18. package/src/managers/RoomManager.js +80 -0
  19. package/src/managers/UserManager.js +41 -0
  20. package/src/rest/REST.js +250 -0
  21. package/src/rest/Routes.js +85 -0
  22. package/src/structures/Base.js +70 -0
  23. package/src/structures/ClientUser.js +142 -0
  24. package/src/structures/DMChannel.js +85 -0
  25. package/src/structures/House.js +171 -0
  26. package/src/structures/Invite.js +83 -0
  27. package/src/structures/Member.js +148 -0
  28. package/src/structures/Message.js +176 -0
  29. package/src/structures/ReactionGroup.js +42 -0
  30. package/src/structures/Role.js +107 -0
  31. package/src/structures/Room.js +264 -0
  32. package/src/structures/User.js +113 -0
  33. package/src/structures/VoiceState.js +42 -0
  34. package/src/util/Collection.js +167 -0
  35. package/src/util/Constants.js +183 -0
  36. package/src/util/Permissions.js +148 -0
  37. package/src/util/Util.js +62 -0
  38. package/types/index.d.ts +784 -0
@@ -0,0 +1,183 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Default {@link Client} options. The backend URLs default to the public Glade
5
+ * service; override `rest`/`gateway` to target a self-hosted deployment.
6
+ */
7
+ export const DefaultOptions = {
8
+ /** Base origin of the REST API, without the version segment. */
9
+ rest: 'https://api.glade.chat',
10
+ /** Origin of the Socket.IO gateway. */
11
+ gateway: 'https://ws.glade.chat',
12
+ /** API version path segment. */
13
+ version: 'v1',
14
+ /** Whether the REST client should auto-refresh the access token on a 401. */
15
+ autoRefresh: true,
16
+ /**
17
+ * How many ms before the access token's expiry to proactively refresh it.
18
+ * Keeps a long-running gateway connection authenticated.
19
+ */
20
+ refreshSkewMs: 60_000,
21
+ /** Whether to cache structures received from REST and the gateway. */
22
+ cache: true,
23
+ /**
24
+ * Auto-subscribe to a House room's realtime events by emitting `room:join`.
25
+ * The server only auto-joins user/house/DM channels on connect, so without this
26
+ * the client receives no text-room message/typing/reaction events.
27
+ */
28
+ autoSubscribeRooms: true,
29
+ /** Socket.IO client options merged into the gateway connection. */
30
+ ws: {
31
+ transports: ['websocket', 'polling'],
32
+ },
33
+ /** When true, the client emits verbose `debug` events. */
34
+ debug: false,
35
+ };
36
+
37
+ /**
38
+ * Client-facing event names (camelCase), emitted by {@link Client}.
39
+ * @enum {string}
40
+ */
41
+ export const Events = {
42
+ Ready: 'ready',
43
+ Debug: 'debug',
44
+ Error: 'error',
45
+ Warn: 'warn',
46
+ Raw: 'raw',
47
+
48
+ Connecting: 'connecting',
49
+ Reconnecting: 'reconnecting',
50
+ Disconnect: 'disconnect',
51
+
52
+ MessageCreate: 'messageCreate',
53
+ MessageUpdate: 'messageUpdate',
54
+ MessageDelete: 'messageDelete',
55
+ MessagePinUpdate: 'messagePinUpdate',
56
+ MessageReactionUpdate: 'messageReactionUpdate',
57
+
58
+ TypingStart: 'typingStart',
59
+ PresenceUpdate: 'presenceUpdate',
60
+ Mention: 'mention',
61
+
62
+ RoomCreate: 'roomCreate',
63
+ RoomUpdate: 'roomUpdate',
64
+ RoomDelete: 'roomDelete',
65
+ RoomsReorder: 'roomsReorder',
66
+
67
+ HouseUpdate: 'houseUpdate',
68
+ HouseDelete: 'houseDelete',
69
+ MemberJoin: 'memberJoin',
70
+ MembersUpdate: 'membersUpdate',
71
+ RolesUpdate: 'rolesUpdate',
72
+
73
+ FriendRequest: 'friendRequest',
74
+ FriendAccepted: 'friendAccepted',
75
+ FriendRemoved: 'friendRemoved',
76
+
77
+ VoicePeerJoin: 'voicePeerJoin',
78
+ VoicePeerLeave: 'voicePeerLeave',
79
+ VoicePeerUpdate: 'voicePeerUpdate',
80
+ VoiceRoomState: 'voiceRoomState',
81
+ VoiceSignal: 'voiceSignal',
82
+
83
+ SessionRevoked: 'sessionRevoked',
84
+ };
85
+
86
+ /**
87
+ * Raw Socket.IO event names the server dispatches to the client (server → client).
88
+ * @enum {string}
89
+ */
90
+ export const GatewayDispatch = {
91
+ Ready: 'ready',
92
+ MessageNew: 'message:new',
93
+ MessageUpdated: 'message:updated',
94
+ MessageDeleted: 'message:deleted',
95
+ MessagePinned: 'message:pinned',
96
+ ReactionUpdated: 'reaction:updated',
97
+ Mention: 'mention',
98
+ DmIncoming: 'dm:incoming',
99
+ Typing: 'typing',
100
+ PresenceUpdate: 'presence:update',
101
+ RoomCreated: 'room:created',
102
+ RoomUpdated: 'room:updated',
103
+ RoomDeleted: 'room:deleted',
104
+ RoomReordered: 'room:reordered',
105
+ HouseUpdated: 'house:updated',
106
+ HouseDeleted: 'house:deleted',
107
+ RolesUpdated: 'roles:updated',
108
+ MembersUpdated: 'members:updated',
109
+ MemberJoined: 'member:joined',
110
+ FriendRequest: 'friend:request',
111
+ FriendAccepted: 'friend:accepted',
112
+ FriendRemoved: 'friend:removed',
113
+ SessionRevoked: 'session:revoked',
114
+ VoicePeerJoined: 'voice:peer-joined',
115
+ VoicePeerLeft: 'voice:peer-left',
116
+ VoicePeerState: 'voice:peer-state',
117
+ VoiceRoomState: 'voice:room-state',
118
+ VoiceSignal: 'voice:signal',
119
+ };
120
+
121
+ /**
122
+ * Raw Socket.IO event names the client emits to the server (client → server).
123
+ * @enum {string}
124
+ */
125
+ export const GatewayCommand = {
126
+ RoomJoin: 'room:join',
127
+ RoomLeave: 'room:leave',
128
+ MessageSend: 'message:send',
129
+ MessageEdit: 'message:edit',
130
+ MessageDelete: 'message:delete',
131
+ ReactionToggle: 'reaction:toggle',
132
+ TypingStart: 'typing:start',
133
+ TypingStop: 'typing:stop',
134
+ PresenceSet: 'presence:set',
135
+ VoiceJoin: 'voice:join',
136
+ VoiceLeave: 'voice:leave',
137
+ VoiceSync: 'voice:sync',
138
+ VoiceSignal: 'voice:signal',
139
+ VoiceState: 'voice:state',
140
+ };
141
+
142
+ /**
143
+ * Room (channel) types.
144
+ * @enum {string}
145
+ */
146
+ export const RoomTypes = {
147
+ Text: 'text',
148
+ Voice: 'voice',
149
+ Portal: 'portal',
150
+ };
151
+
152
+ /**
153
+ * Presence statuses a user can hold.
154
+ * @enum {string}
155
+ */
156
+ export const PresenceStatus = {
157
+ Online: 'online',
158
+ Idle: 'idle',
159
+ DnD: 'dnd',
160
+ Offline: 'offline',
161
+ };
162
+
163
+ /** Statuses a user may manually set (offline is implicit). */
164
+ export const SettableStatus = [
165
+ PresenceStatus.Online,
166
+ PresenceStatus.Idle,
167
+ PresenceStatus.DnD,
168
+ ];
169
+
170
+ /**
171
+ * Friendship statuses returned by the friends API.
172
+ * @enum {string}
173
+ */
174
+ export const FriendStatus = {
175
+ Self: 'self',
176
+ None: 'none',
177
+ Friends: 'friends',
178
+ Incoming: 'incoming',
179
+ Outgoing: 'outgoing',
180
+ };
181
+
182
+ /** Name of the httpOnly refresh-token cookie the backend sets. */
183
+ export const REFRESH_COOKIE = 'glade_rt';
@@ -0,0 +1,148 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Glade House permission flags. These mirror the bitfield stored in
5
+ * `Role.permissions` and the allow/deny masks on per-room overrides server-side.
6
+ * @enum {number}
7
+ */
8
+ export const PermissionFlags = {
9
+ /** Grants every permission and bypasses channel overrides. */
10
+ Administrator: 1 << 0,
11
+ /** Edit the House name / icon. */
12
+ ManageHouse: 1 << 1,
13
+ /** Create, edit, delete and assign roles. */
14
+ ManageRoles: 1 << 2,
15
+ /** Create, edit, delete and clone rooms, and set channel overrides. */
16
+ ManageChannels: 1 << 3,
17
+ /** Remove members from the House. */
18
+ KickMembers: 1 << 4,
19
+ /** Pin and delete other members' messages. */
20
+ ManageMessages: 1 << 5,
21
+ /** View rooms. */
22
+ ViewChannels: 1 << 6,
23
+ /** Send messages in rooms. */
24
+ SendMessages: 1 << 7,
25
+ /** Connect to voice rooms. */
26
+ Connect: 1 << 8,
27
+ };
28
+
29
+ /** Every permission OR'd together. */
30
+ export const ALL_PERMISSIONS = Object.values(PermissionFlags).reduce((a, b) => a | b, 0);
31
+
32
+ /** Permissions granted to the default `@everyone` role by the backend. */
33
+ export const DEFAULT_EVERYONE =
34
+ PermissionFlags.ViewChannels | PermissionFlags.SendMessages | PermissionFlags.Connect;
35
+
36
+ /** Permissions a per-room override is allowed to toggle. */
37
+ export const CHANNEL_OVERRIDABLE =
38
+ PermissionFlags.ViewChannels |
39
+ PermissionFlags.SendMessages |
40
+ PermissionFlags.Connect |
41
+ PermissionFlags.ManageMessages;
42
+
43
+ /**
44
+ * A wrapper around a Glade permission bitfield, offering ergonomic checks and edits.
45
+ * Accepts flag names (keys of {@link PermissionFlags}), raw numbers, or other
46
+ * `PermissionsBitField` instances anywhere a resolvable is expected.
47
+ */
48
+ export class PermissionsBitField {
49
+ /**
50
+ * @param {PermissionResolvable} [bits=0]
51
+ */
52
+ constructor(bits = 0) {
53
+ /** @type {number} The raw bitfield. */
54
+ this.bitfield = PermissionsBitField.resolve(bits);
55
+ }
56
+
57
+ /**
58
+ * Resolves a permission-like value into a raw number.
59
+ * @param {PermissionResolvable} bits
60
+ * @returns {number}
61
+ */
62
+ static resolve(bits = 0) {
63
+ if (typeof bits === 'number') return bits;
64
+ if (bits instanceof PermissionsBitField) return bits.bitfield;
65
+ if (typeof bits === 'string') {
66
+ const flag = PermissionFlags[bits];
67
+ if (flag === undefined) throw new RangeError(`Unknown permission flag: ${bits}`);
68
+ return flag;
69
+ }
70
+ if (Array.isArray(bits)) {
71
+ return bits.map((b) => PermissionsBitField.resolve(b)).reduce((a, b) => a | b, 0);
72
+ }
73
+ throw new TypeError(`Cannot resolve permissions from: ${typeof bits}`);
74
+ }
75
+
76
+ /**
77
+ * Whether the bitfield holds all of the given permissions. The `Administrator`
78
+ * flag short-circuits to `true`, matching the server's `has()` semantics.
79
+ * @param {PermissionResolvable} permission
80
+ * @param {boolean} [checkAdmin=true]
81
+ * @returns {boolean}
82
+ */
83
+ has(permission, checkAdmin = true) {
84
+ if (checkAdmin && (this.bitfield & PermissionFlags.Administrator) !== 0) return true;
85
+ const bit = PermissionsBitField.resolve(permission);
86
+ return (this.bitfield & bit) === bit;
87
+ }
88
+
89
+ /**
90
+ * Returns whether any of the given permission bits are present.
91
+ * @param {PermissionResolvable} permission
92
+ * @returns {boolean}
93
+ */
94
+ any(permission) {
95
+ const bit = PermissionsBitField.resolve(permission);
96
+ return (this.bitfield & bit) !== 0;
97
+ }
98
+
99
+ /**
100
+ * Adds permissions, returning this for chaining.
101
+ * @param {...PermissionResolvable} bits
102
+ * @returns {this}
103
+ */
104
+ add(...bits) {
105
+ for (const b of bits) this.bitfield |= PermissionsBitField.resolve(b);
106
+ return this;
107
+ }
108
+
109
+ /**
110
+ * Removes permissions, returning this for chaining.
111
+ * @param {...PermissionResolvable} bits
112
+ * @returns {this}
113
+ */
114
+ remove(...bits) {
115
+ for (const b of bits) this.bitfield &= ~PermissionsBitField.resolve(b);
116
+ return this;
117
+ }
118
+
119
+ /**
120
+ * The flag names currently set in this bitfield.
121
+ * @returns {string[]}
122
+ */
123
+ toArray() {
124
+ return Object.keys(PermissionFlags).filter(
125
+ (name) => (this.bitfield & PermissionFlags[name]) === PermissionFlags[name],
126
+ );
127
+ }
128
+
129
+ /** @returns {number} */
130
+ valueOf() {
131
+ return this.bitfield;
132
+ }
133
+
134
+ /** @returns {string[]} */
135
+ toJSON() {
136
+ return this.toArray();
137
+ }
138
+ }
139
+
140
+ PermissionsBitField.Flags = PermissionFlags;
141
+ PermissionsBitField.All = ALL_PERMISSIONS;
142
+ PermissionsBitField.DefaultEveryone = DEFAULT_EVERYONE;
143
+
144
+ /**
145
+ * @typedef {keyof typeof PermissionFlags | number | PermissionsBitField | Array<keyof typeof PermissionFlags | number | PermissionsBitField>} PermissionResolvable
146
+ */
147
+
148
+ export default PermissionsBitField;
@@ -0,0 +1,62 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Generates a short, unlikely-to-collide nonce used to correlate an optimistically
5
+ * sent message with its `message:new` echo from the gateway.
6
+ * @returns {string}
7
+ */
8
+ export function generateNonce() {
9
+ return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
10
+ }
11
+
12
+ /**
13
+ * Decodes the payload of a JWT without verifying its signature. Used only to read
14
+ * the access token's `exp` so the client can refresh proactively.
15
+ * @param {string} token
16
+ * @returns {Record<string, any> | null}
17
+ */
18
+ export function decodeJwt(token) {
19
+ try {
20
+ const part = token.split('.')[1];
21
+ if (!part) return null;
22
+ const json = Buffer.from(part.replace(/-/g, '+').replace(/_/g, '/'), 'base64').toString('utf8');
23
+ return JSON.parse(json);
24
+ } catch {
25
+ return null;
26
+ }
27
+ }
28
+
29
+ /**
30
+ * Builds a query string (with leading `?`) from an object, skipping
31
+ * `undefined`/`null` values. Returns `''` when there is nothing to encode.
32
+ * @param {Record<string, any>} [query]
33
+ * @returns {string}
34
+ */
35
+ export function makeQuery(query) {
36
+ if (!query) return '';
37
+ const params = new URLSearchParams();
38
+ for (const [key, value] of Object.entries(query)) {
39
+ if (value === undefined || value === null) continue;
40
+ params.append(key, String(value));
41
+ }
42
+ const str = params.toString();
43
+ return str ? `?${str}` : '';
44
+ }
45
+
46
+ /**
47
+ * Trims any trailing slashes from a URL/origin.
48
+ * @param {string} url
49
+ * @returns {string}
50
+ */
51
+ export function trimTrailingSlash(url) {
52
+ return String(url).replace(/\/+$/, '');
53
+ }
54
+
55
+ /**
56
+ * Resolves something to its id: accepts a raw id string or any object with an `id`.
57
+ * @param {string | { id: string }} value
58
+ * @returns {string}
59
+ */
60
+ export function resolveId(value) {
61
+ return typeof value === 'string' ? value : value?.id;
62
+ }