vantiv.io 1.0.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.
- package/LICENSE +21 -0
- package/README.md +864 -0
- package/index.js +13 -0
- package/package.json +28 -0
- package/src/classes/Actions/Awaiter.js +202 -0
- package/src/classes/Actions/Channel.js +73 -0
- package/src/classes/Actions/Direct.js +263 -0
- package/src/classes/Actions/Inventory.js +156 -0
- package/src/classes/Actions/Music.js +278 -0
- package/src/classes/Actions/Player.js +377 -0
- package/src/classes/Actions/Public.js +66 -0
- package/src/classes/Actions/Room.js +333 -0
- package/src/classes/Actions/Utils.js +29 -0
- package/src/classes/Actions/lib/AudioStreaming.js +447 -0
- package/src/classes/Caches/MovementCache.js +357 -0
- package/src/classes/Handlers/AxiosErrorHandler.js +68 -0
- package/src/classes/Handlers/ErrorHandler.js +65 -0
- package/src/classes/Handlers/EventHandlers.js +259 -0
- package/src/classes/Handlers/WebSocketHandlers.js +54 -0
- package/src/classes/Managers/ChannelManager.js +303 -0
- package/src/classes/Managers/DanceFloorManagers.js +509 -0
- package/src/classes/Managers/Helpers/CleanupManager.js +130 -0
- package/src/classes/Managers/Helpers/LoggerManager.js +171 -0
- package/src/classes/Managers/Helpers/MetricsManager.js +83 -0
- package/src/classes/Managers/Networking/ConnectionManager.js +259 -0
- package/src/classes/Managers/Networking/CooldownManager.js +516 -0
- package/src/classes/Managers/Networking/EventsManager.js +64 -0
- package/src/classes/Managers/Networking/KeepAliveManager.js +109 -0
- package/src/classes/Managers/Networking/MessageHandler.js +110 -0
- package/src/classes/Managers/Networking/Request.js +329 -0
- package/src/classes/Managers/PermissionManager.js +288 -0
- package/src/classes/WebApi/Category/Grab.js +98 -0
- package/src/classes/WebApi/Category/Item.js +347 -0
- package/src/classes/WebApi/Category/Post.js +154 -0
- package/src/classes/WebApi/Category/Room.js +137 -0
- package/src/classes/WebApi/Category/User.js +88 -0
- package/src/classes/WebApi/webapi.js +52 -0
- package/src/constants/TypesConstants.js +89 -0
- package/src/constants/WebSocketConstants.js +80 -0
- package/src/core/Highrise.js +123 -0
- package/src/core/HighriseWebsocket.js +228 -0
- package/src/utils/ConvertSvgToPng.js +51 -0
- package/src/utils/ModelPool.js +160 -0
- package/src/utils/Models.js +128 -0
- package/src/utils/versionCheck.js +27 -0
- package/src/validators/ConfigValidator.js +205 -0
- package/src/validators/ConnectionValidator.js +65 -0
- package/typings/index.d.ts +3820 -0
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
const sharp = require('sharp');
|
|
2
|
+
const fs = require('fs').promises;
|
|
3
|
+
|
|
4
|
+
async function convertAvatarToPNG(encodedSVG, outputPath = 'avatar.png', options = {}) {
|
|
5
|
+
try {
|
|
6
|
+
const {
|
|
7
|
+
density = 1800,
|
|
8
|
+
quality = 100,
|
|
9
|
+
compressionLevel = 0
|
|
10
|
+
} = options;
|
|
11
|
+
|
|
12
|
+
function decodeAvatarSVG(encodedSVG) {
|
|
13
|
+
return encodedSVG
|
|
14
|
+
.replace(/\\u003C/g, '<')
|
|
15
|
+
.replace(/\\u003E/g, '>')
|
|
16
|
+
.replace(/\\u0026/g, '&')
|
|
17
|
+
.replace(/\\u0027/g, "'")
|
|
18
|
+
.replace(/\\u0022/g, '"');
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const decodedSVG = decodeAvatarSVG(encodedSVG);
|
|
22
|
+
|
|
23
|
+
const pngBuffer = await sharp(Buffer.from(decodedSVG), {
|
|
24
|
+
density: density
|
|
25
|
+
})
|
|
26
|
+
.png({
|
|
27
|
+
quality: quality,
|
|
28
|
+
compressionLevel: compressionLevel,
|
|
29
|
+
adaptiveFiltering: false,
|
|
30
|
+
force: true
|
|
31
|
+
})
|
|
32
|
+
.toBuffer();
|
|
33
|
+
|
|
34
|
+
await fs.writeFile(outputPath, pngBuffer);
|
|
35
|
+
|
|
36
|
+
const metadata = await sharp(pngBuffer).metadata();
|
|
37
|
+
console.log(`✅ Lossless PNG: ${metadata.width}x${metadata.height}px @ ${density}DPI`);
|
|
38
|
+
|
|
39
|
+
return {
|
|
40
|
+
success: true,
|
|
41
|
+
width: metadata.width,
|
|
42
|
+
height: metadata.height,
|
|
43
|
+
size: pngBuffer.length / 1024 / 1024
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
} catch (error) {
|
|
47
|
+
throw error;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
module.exports = convertAvatarToPNG;
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
class ModelPool {
|
|
2
|
+
constructor() {
|
|
3
|
+
this._pools = new Map();
|
|
4
|
+
this._parsers = new Map();
|
|
5
|
+
this._stats = {
|
|
6
|
+
created: 0,
|
|
7
|
+
reused: 0,
|
|
8
|
+
peakSize: 0,
|
|
9
|
+
batchReleased: 0
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
registerModel(type, parser) {
|
|
14
|
+
if (typeof parser !== 'function') {
|
|
15
|
+
throw new Error(`Parser for type '${type}' must be a function`);
|
|
16
|
+
}
|
|
17
|
+
this._parsers.set(type, parser);
|
|
18
|
+
if (!this._pools.has(type)) {
|
|
19
|
+
this._pools.set(type, []);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
get(type, ...args) {
|
|
24
|
+
if (!this._parsers.has(type)) {
|
|
25
|
+
throw new Error(`Model type '${type}' not registered. Call registerModel() first.`);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const pool = this._getPool(type);
|
|
29
|
+
const parser = this._parsers.get(type);
|
|
30
|
+
|
|
31
|
+
let model;
|
|
32
|
+
if (pool.length > 0) {
|
|
33
|
+
model = pool.pop();
|
|
34
|
+
this._stats.reused++;
|
|
35
|
+
Object.assign(model, parser(...args));
|
|
36
|
+
} else {
|
|
37
|
+
model = parser(...args);
|
|
38
|
+
this._stats.created++;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return model;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
release(type, model) {
|
|
45
|
+
if (!model) return;
|
|
46
|
+
|
|
47
|
+
const pool = this._getPool(type);
|
|
48
|
+
pool.push(model);
|
|
49
|
+
this._stats.peakSize = Math.max(this._stats.peakSize, pool.length);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
releaseBatch(type, models) {
|
|
53
|
+
if (!models || !Array.isArray(models) || models.length === 0) return;
|
|
54
|
+
|
|
55
|
+
const pool = this._getPool(type);
|
|
56
|
+
const validModels = models.filter(model => model != null);
|
|
57
|
+
|
|
58
|
+
if (validModels.length > 0) {
|
|
59
|
+
pool.push(...validModels);
|
|
60
|
+
this._stats.batchReleased += validModels.length;
|
|
61
|
+
this._stats.peakSize = Math.max(this._stats.peakSize, pool.length);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
releaseMultiple(modelMap) {
|
|
66
|
+
if (!modelMap || typeof modelMap !== 'object') return;
|
|
67
|
+
|
|
68
|
+
let totalReleased = 0;
|
|
69
|
+
|
|
70
|
+
for (const [type, models] of Object.entries(modelMap)) {
|
|
71
|
+
if (Array.isArray(models)) {
|
|
72
|
+
const pool = this._getPool(type);
|
|
73
|
+
const validModels = models.filter(model => model != null);
|
|
74
|
+
|
|
75
|
+
if (validModels.length > 0) {
|
|
76
|
+
pool.push(...validModels);
|
|
77
|
+
totalReleased += validModels.length;
|
|
78
|
+
this._stats.peakSize = Math.max(this._stats.peakSize, pool.length);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
this._stats.batchReleased += totalReleased;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
getBatch(type, count, ...args) {
|
|
87
|
+
if (count <= 0) return [];
|
|
88
|
+
|
|
89
|
+
const models = new Array(count);
|
|
90
|
+
for (let i = 0; i < count; i++) {
|
|
91
|
+
models[i] = this.get(type, ...args);
|
|
92
|
+
}
|
|
93
|
+
return models;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
hasType(type) {
|
|
97
|
+
return this._parsers.has(type);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
getRegisteredTypes() {
|
|
101
|
+
return Array.from(this._parsers.keys());
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
getStats() {
|
|
105
|
+
const poolSizes = {};
|
|
106
|
+
for (const [type, pool] of this._pools) {
|
|
107
|
+
poolSizes[type] = pool.length;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return {
|
|
111
|
+
...this._stats,
|
|
112
|
+
poolSizes,
|
|
113
|
+
registeredTypes: this.getRegisteredTypes(),
|
|
114
|
+
totalOperations: this._stats.created + this._stats.reused + this._stats.batchReleased
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
clear(type = null) {
|
|
119
|
+
if (type) {
|
|
120
|
+
this._pools.get(type)?.splice(0);
|
|
121
|
+
} else {
|
|
122
|
+
for (const pool of this._pools.values()) {
|
|
123
|
+
pool.splice(0);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
this._stats.created = 0;
|
|
127
|
+
this._stats.reused = 0;
|
|
128
|
+
this._stats.peakSize = 0;
|
|
129
|
+
this._stats.batchReleased = 0;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
getPoolSize(type) {
|
|
134
|
+
return this._pools.get(type)?.length || 0;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
preWarm(type, count) {
|
|
138
|
+
if (count <= 0) return;
|
|
139
|
+
|
|
140
|
+
const pool = this._getPool(type);
|
|
141
|
+
const parser = this._parsers.get(type);
|
|
142
|
+
|
|
143
|
+
for (let i = 0; i < count; i++) {
|
|
144
|
+
const model = parser();
|
|
145
|
+
pool.push(model);
|
|
146
|
+
this._stats.created++;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
this._stats.peakSize = Math.max(this._stats.peakSize, pool.length);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
_getPool(type) {
|
|
153
|
+
if (!this._pools.has(type)) {
|
|
154
|
+
this._pools.set(type, []);
|
|
155
|
+
}
|
|
156
|
+
return this._pools.get(type);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
module.exports = ModelPool;
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
class User {
|
|
2
|
+
constructor(data = {}) {
|
|
3
|
+
this.username = data.user.username || ''
|
|
4
|
+
this.id = data.user.id || ''
|
|
5
|
+
}
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
class Position {
|
|
9
|
+
constructor(data = {}) {
|
|
10
|
+
this.x = data.position.x || 0
|
|
11
|
+
this.y = data.position.y || 0
|
|
12
|
+
this.z = data.position.z || 0
|
|
13
|
+
this.facing = data.position.facing || ''
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
class Tip {
|
|
18
|
+
constructor(data = {}) {
|
|
19
|
+
this.sender = {
|
|
20
|
+
id: data.sender.id,
|
|
21
|
+
username: data.sender.username
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
this.receiver = {
|
|
25
|
+
id: data.receiver.id,
|
|
26
|
+
username: data.receiver.username
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
this.item = {
|
|
30
|
+
type: data.item.type || '',
|
|
31
|
+
amount: data.item.amount || 0
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
class AnchorPosition {
|
|
37
|
+
constructor(data = {}) {
|
|
38
|
+
this.entity_id = data.position.entity_id || ''
|
|
39
|
+
this.anchor_ix = data.position.anchor_ix || 0
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
class Message {
|
|
44
|
+
constructor(data = {}) {
|
|
45
|
+
this.user = {
|
|
46
|
+
id: data.user?.id || '',
|
|
47
|
+
username: data.user?.username || ''
|
|
48
|
+
};
|
|
49
|
+
this.message = data.message || ''
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
class Direct {
|
|
54
|
+
constructor(data = {}) {
|
|
55
|
+
this.user = {
|
|
56
|
+
id: data.user_id || '',
|
|
57
|
+
username: null
|
|
58
|
+
},
|
|
59
|
+
|
|
60
|
+
this.message = data.message
|
|
61
|
+
this.conversation = {
|
|
62
|
+
id: data.conversation_id || "",
|
|
63
|
+
is_new_conversation: data.is_new_conversation
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
class SessionMetadata {
|
|
69
|
+
constructor(data = {}) {
|
|
70
|
+
this.bot_id = data.user_id || ''
|
|
71
|
+
this.room = {
|
|
72
|
+
owner_id: data.room_info.owner_id || '',
|
|
73
|
+
room_name: data.room_info.room_name || ''
|
|
74
|
+
};
|
|
75
|
+
this.metadata = {
|
|
76
|
+
connection_id: data.connection_id || '',
|
|
77
|
+
rate_limits: data.rate_limits || {}
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
class RoomModerate {
|
|
83
|
+
constructor(data = {}) {
|
|
84
|
+
this.moderator = {
|
|
85
|
+
id: data.moderatorId || '',
|
|
86
|
+
username: data.moderator_username || null
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
this.target = {
|
|
90
|
+
id: data.targetUserId || '',
|
|
91
|
+
username: data.target_username || null
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
this.action = {
|
|
95
|
+
type: data.moderationType || '',
|
|
96
|
+
duration: data.duration || null
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
class HiddenChannel {
|
|
102
|
+
constructor(data = {}) {
|
|
103
|
+
this.sender_id = data.sender_id
|
|
104
|
+
this.message = data.msg
|
|
105
|
+
this.tags = data.tags
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
class Voice {
|
|
110
|
+
constructor(data = {}) {
|
|
111
|
+
this.users = data.users.map(([user, status]) => ({ user, status }));
|
|
112
|
+
this.seconds_left = data.seconds_left
|
|
113
|
+
this.ended = false
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
module.exports = {
|
|
118
|
+
Tip,
|
|
119
|
+
User,
|
|
120
|
+
Voice,
|
|
121
|
+
Direct,
|
|
122
|
+
Message,
|
|
123
|
+
Position,
|
|
124
|
+
RoomModerate,
|
|
125
|
+
HiddenChannel,
|
|
126
|
+
AnchorPosition,
|
|
127
|
+
SessionMetadata,
|
|
128
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
const colors = require('colors');
|
|
2
|
+
|
|
3
|
+
async function checkVersion(logger) {
|
|
4
|
+
try {
|
|
5
|
+
const packageJson = require('../../package.json');
|
|
6
|
+
const currentVersion = packageJson.version;
|
|
7
|
+
const packageName = packageJson.name;
|
|
8
|
+
|
|
9
|
+
const response = await fetch(`https://registry.npmjs.org/${packageName}/latest`);
|
|
10
|
+
const data = await response.json();
|
|
11
|
+
const latestVersion = data.version;
|
|
12
|
+
|
|
13
|
+
if (latestVersion === currentVersion) {
|
|
14
|
+
logger.success('versionCheck', `Using latest version: ${colors.green(`v${currentVersion}`)}`);
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
logger.warn('versionCheck',
|
|
19
|
+
`${colors.yellow('Update available:')} ${colors.red(`v${currentVersion}`)} → ${colors.green(`v${latestVersion}`)} | ${colors.cyan(`npm install ${packageName}@latest`)}`
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
} catch (error) {
|
|
23
|
+
logger.debug('versionCheck', 'Version check failed', { error: error.message });
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
module.exports = { checkVersion };
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
class ConfigValidator {
|
|
2
|
+
static validateHighriseOptions(options = {}) {
|
|
3
|
+
const errors = [];
|
|
4
|
+
const warnings = [];
|
|
5
|
+
const validated = { ...options };
|
|
6
|
+
|
|
7
|
+
if (validated.Events !== undefined) {
|
|
8
|
+
if (!Array.isArray(validated.Events)) {
|
|
9
|
+
errors.push('Events must be an array');
|
|
10
|
+
} else if (validated.Events.length === 0) {
|
|
11
|
+
errors.push('At least one event type must be specified');
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
if (validated.LoggerOptions !== undefined) {
|
|
16
|
+
if (typeof validated.LoggerOptions !== 'object' || validated.LoggerOptions === null) {
|
|
17
|
+
errors.push('LoggerOptions must be an object');
|
|
18
|
+
} else {
|
|
19
|
+
const loggerResult = this.validateLoggerOptions(validated.LoggerOptions);
|
|
20
|
+
if (!loggerResult.success) {
|
|
21
|
+
errors.push(...loggerResult.errors);
|
|
22
|
+
} else {
|
|
23
|
+
validated.LoggerOptions = loggerResult.validatedConfig;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (validated.autoReconnect !== undefined) {
|
|
29
|
+
if (typeof validated.autoReconnect !== 'boolean') {
|
|
30
|
+
errors.push('autoReconnect must be a boolean');
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (validated.reconnectDelay !== undefined) {
|
|
35
|
+
if (typeof validated.reconnectDelay !== 'number') {
|
|
36
|
+
errors.push('reconnectDelay must be a number');
|
|
37
|
+
} else if (validated.reconnectDelay < 1000) {
|
|
38
|
+
warnings.push('reconnectDelay below 1000ms may cause rate limiting');
|
|
39
|
+
validated.reconnectDelay = Math.max(500, validated.reconnectDelay);
|
|
40
|
+
} else if (validated.reconnectDelay > 300000) {
|
|
41
|
+
warnings.push('reconnectDelay above 300000ms (5 minutes) may impact user experience');
|
|
42
|
+
validated.reconnectDelay = Math.min(validated.reconnectDelay, 600000);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
validated.reconnectDelay = Math.round(validated.reconnectDelay);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return {
|
|
49
|
+
success: errors.length === 0,
|
|
50
|
+
errors,
|
|
51
|
+
warnings,
|
|
52
|
+
validatedConfig: validated
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
static validateLoggerOptions(loggerOptions = {}) {
|
|
57
|
+
const errors = [];
|
|
58
|
+
const warnings = [];
|
|
59
|
+
const validated = { ...loggerOptions };
|
|
60
|
+
|
|
61
|
+
const booleanFields = ['showTimestamp', 'showMethodName', 'colors'];
|
|
62
|
+
booleanFields.forEach(field => {
|
|
63
|
+
if (validated[field] !== undefined) {
|
|
64
|
+
if (typeof validated[field] !== 'boolean') {
|
|
65
|
+
// Try to convert string booleans
|
|
66
|
+
if (typeof validated[field] === 'string') {
|
|
67
|
+
if (validated[field].toLowerCase() === 'true') {
|
|
68
|
+
validated[field] = true;
|
|
69
|
+
warnings.push(`LoggerOptions.${field} was converted from string to boolean`);
|
|
70
|
+
} else if (validated[field].toLowerCase() === 'false') {
|
|
71
|
+
validated[field] = false;
|
|
72
|
+
warnings.push(`LoggerOptions.${field} was converted from string to boolean`);
|
|
73
|
+
} else {
|
|
74
|
+
errors.push(`LoggerOptions.${field} must be a boolean`);
|
|
75
|
+
}
|
|
76
|
+
} else {
|
|
77
|
+
errors.push(`LoggerOptions.${field} must be a boolean`);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
return {
|
|
84
|
+
success: errors.length === 0,
|
|
85
|
+
errors,
|
|
86
|
+
warnings,
|
|
87
|
+
validatedConfig: validated
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
static validateSenderConfig(config = {}) {
|
|
92
|
+
const errors = [];
|
|
93
|
+
const warnings = [];
|
|
94
|
+
const validated = { ...config };
|
|
95
|
+
|
|
96
|
+
if (validated.defaultTimeout !== undefined) {
|
|
97
|
+
const timeout = parseInt(validated.defaultTimeout);
|
|
98
|
+
if (isNaN(timeout)) {
|
|
99
|
+
errors.push('defaultTimeout must be a number');
|
|
100
|
+
} else if (timeout < 100) {
|
|
101
|
+
errors.push('defaultTimeout must be at least 100ms');
|
|
102
|
+
} else if (timeout > 60000) {
|
|
103
|
+
warnings.push('defaultTimeout above 60000ms may cause slow response detection');
|
|
104
|
+
validated.defaultTimeout = Math.min(timeout, 120000);
|
|
105
|
+
} else {
|
|
106
|
+
validated.defaultTimeout = timeout;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (validated.maxRetries !== undefined) {
|
|
111
|
+
const retries = parseInt(validated.maxRetries);
|
|
112
|
+
if (isNaN(retries)) {
|
|
113
|
+
errors.push('maxRetries must be a number');
|
|
114
|
+
} else if (retries < 0) {
|
|
115
|
+
errors.push('maxRetries cannot be negative');
|
|
116
|
+
} else if (retries > 10) {
|
|
117
|
+
warnings.push('maxRetries above 10 may cause excessive network traffic');
|
|
118
|
+
validated.maxRetries = Math.min(retries, 15);
|
|
119
|
+
} else {
|
|
120
|
+
validated.maxRetries = retries;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (validated.retryDelay !== undefined) {
|
|
125
|
+
const delay = parseInt(validated.retryDelay);
|
|
126
|
+
if (isNaN(delay)) {
|
|
127
|
+
errors.push('retryDelay must be a number');
|
|
128
|
+
} else if (delay < 10) {
|
|
129
|
+
warnings.push('retryDelay below 10ms may not allow proper error recovery');
|
|
130
|
+
validated.retryDelay = Math.max(delay, 5);
|
|
131
|
+
} else if (delay > 30000) {
|
|
132
|
+
warnings.push('retryDelay above 30000ms may impact user experience');
|
|
133
|
+
validated.retryDelay = Math.min(delay, 60000);
|
|
134
|
+
} else {
|
|
135
|
+
validated.retryDelay = delay;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return {
|
|
140
|
+
success: errors.length === 0,
|
|
141
|
+
errors,
|
|
142
|
+
warnings,
|
|
143
|
+
validatedConfig: validated
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
static validateEvents(events) {
|
|
148
|
+
const errors = [];
|
|
149
|
+
const validEvents = [
|
|
150
|
+
'SessionMetadata', 'ChatEvent', 'WhisperEvent', 'UserMovedEvent',
|
|
151
|
+
'UserJoinedEvent', 'UserLeftEvent', 'MessageEvent', 'TipReactionEvent',
|
|
152
|
+
'RoomModeratedEvent', 'ChannelEvent'
|
|
153
|
+
];
|
|
154
|
+
|
|
155
|
+
if (!Array.isArray(events)) {
|
|
156
|
+
errors.push('Events must be an array');
|
|
157
|
+
return { success: false, errors, validEvents };
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
if (events.length === 0) {
|
|
161
|
+
errors.push('At least one event type must be specified');
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const invalidEvents = events.filter(event => !validEvents.includes(event));
|
|
165
|
+
if (invalidEvents.length > 0) {
|
|
166
|
+
errors.push(`Invalid event types: ${invalidEvents.join(', ')}`);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const duplicateEvents = events.filter((event, index) => events.indexOf(event) !== index);
|
|
170
|
+
if (duplicateEvents.length > 0) {
|
|
171
|
+
warnings.push(`Duplicate event types: ${duplicateEvents.join(', ')}`);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
return {
|
|
175
|
+
success: errors.length === 0,
|
|
176
|
+
errors,
|
|
177
|
+
warnings: duplicateEvents.length > 0 ? [`Duplicate event types: ${duplicateEvents.join(', ')}`] : [],
|
|
178
|
+
validEvents,
|
|
179
|
+
validatedEvents: [...new Set(events)]
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
static validateCredentials(token, roomId) {
|
|
184
|
+
const errors = [];
|
|
185
|
+
|
|
186
|
+
if (!token || typeof token !== 'string') {
|
|
187
|
+
errors.push('Token must be a non-empty string');
|
|
188
|
+
} else if (token.length !== 64) {
|
|
189
|
+
errors.push('Token must be exactly 64 characters long');
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
if (!roomId || typeof roomId !== 'string') {
|
|
193
|
+
errors.push('Room ID must be a non-empty string');
|
|
194
|
+
} else if (roomId.length !== 24) {
|
|
195
|
+
errors.push('Room ID must be exactly 24 characters long');
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
return {
|
|
199
|
+
success: errors.length === 0,
|
|
200
|
+
errors
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
module.exports = { ConfigValidator };
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
class ConnectionValidator {
|
|
2
|
+
static validateCredentials(token, roomId) {
|
|
3
|
+
const errors = [];
|
|
4
|
+
|
|
5
|
+
if (!token || typeof token !== 'string') {
|
|
6
|
+
errors.push('Token must be a non-empty string');
|
|
7
|
+
} else if (token.length !== 64) {
|
|
8
|
+
errors.push(`Token must be exactly 64 characters (got ${token.length})`);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
if (!roomId || typeof roomId !== 'string') {
|
|
12
|
+
errors.push('Room ID must be a non-empty string');
|
|
13
|
+
} else if (roomId.length !== 24) {
|
|
14
|
+
errors.push(`Room ID must be exactly 24 characters (got ${roomId.length})`);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
if (errors.length > 0) {
|
|
18
|
+
throw new Error(`Connection validation failed: ${errors.join('; ')}`);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
static validateEvents(events) {
|
|
23
|
+
if (!Array.isArray(events)) {
|
|
24
|
+
throw new Error('Events must be an array');
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (events.length === 0) {
|
|
28
|
+
throw new Error('At least one event type must be specified');
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const validEvents = [
|
|
32
|
+
'SessionMetadata', 'ChatEvent', 'WhisperEvent',
|
|
33
|
+
'UserMovedEvent', 'UserJoinedEvent', 'UserLeftEvent',
|
|
34
|
+
'MessageEvent', 'TipReactionEvent', 'RoomModeratedEvent',
|
|
35
|
+
'ChannelEvent'
|
|
36
|
+
];
|
|
37
|
+
|
|
38
|
+
const invalidEvents = events.filter(event => !validEvents.includes(event));
|
|
39
|
+
if (invalidEvents.length > 0) {
|
|
40
|
+
throw new Error(`Invalid event types: ${invalidEvents.join(', ')}`);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return [...new Set(events)];
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
static validateOptions(options) {
|
|
47
|
+
const validated = { ...options };
|
|
48
|
+
|
|
49
|
+
if (validated.autoReconnect !== undefined && typeof validated.autoReconnect !== 'boolean') {
|
|
50
|
+
throw new TypeError('autoReconnect must be a boolean');
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (validated.reconnectDelay !== undefined) {
|
|
54
|
+
const delay = parseInt(validated.reconnectDelay);
|
|
55
|
+
if (isNaN(delay) || delay < 1000) {
|
|
56
|
+
throw new TypeError('reconnectDelay must be a number >= 1000');
|
|
57
|
+
}
|
|
58
|
+
validated.reconnectDelay = Math.min(delay, 300000);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return validated;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
module.exports = ConnectionValidator;
|