vimp-engine 0.14.3 → 0.15.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/core/Cargo.toml +1 -1
- package/package.json +1 -1
- package/src/client/components/model/Auth.js +7 -14
- package/src/client/components/view/Auth.js +29 -19
- package/src/client/lib/authParams.js +41 -0
- package/src/client/lib/formBuilder.js +362 -26
- package/src/client/main.js +28 -22
- package/src/client/style.css +10 -12
- package/src/client/views/gameShell.js +1 -1
- package/src/client/views/includes/auth.pug +1 -1
- package/src/client/views/includes/lobby.pug +6 -5
- package/src/devtools/contract/index.js +28 -5
- package/src/devtools/contract/result.js +8 -2
- package/src/devtools/contract/rules/b5-room-form.js +17 -1
- package/src/devtools/contract/rules/c10-auth-schema.js +26 -3
- package/src/devtools/contract/rules/c4-component-dependencies.js +42 -11
- package/src/devtools/contract/rules/c6-stat-columns.js +91 -16
- package/src/host/PortMachine.js +17 -1
- package/src/lib/formOptions.js +16 -0
- package/src/lib/formPattern.js +16 -0
- package/src/lib/validators.js +112 -7
package/src/lib/validators.js
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { anchorPattern } from './formPattern.js';
|
|
2
|
+
import { normalizeOptions } from './formOptions.js';
|
|
3
|
+
|
|
1
4
|
const NAME_REGEXP = new RegExp('^[a-zA-Z]([\\w\\s#]{0,13})[\\w]{1}$');
|
|
2
5
|
|
|
3
6
|
/**
|
|
@@ -8,12 +11,71 @@ const NAME_REGEXP = new RegExp('^[a-zA-Z]([\\w\\s#]{0,13})[\\w]{1}$');
|
|
|
8
11
|
export const isValidName = name =>
|
|
9
12
|
typeof name === 'string' && NAME_REGEXP.test(name);
|
|
10
13
|
|
|
14
|
+
// потолок длины поля формы, когда maxlength в дескрипторе не объявлен.
|
|
15
|
+
// regExp игры исполняется на хосте против строки, которую прислал клиент, и
|
|
16
|
+
// катастрофический паттерн вроде "(a+)+b" превращает три десятка символов в
|
|
17
|
+
// минуты заблокированного event loop — а хост это либо Worker с авторитетным
|
|
18
|
+
// матчем (замирает комната целиком), либо процесс dedicated. 256 — с запасом
|
|
19
|
+
// на любое поле auth-формы (ник, имя модели): длиннее не бывает у формы и не
|
|
20
|
+
// нужно тому, кто её обошёл
|
|
21
|
+
const MAX_FIELD_LENGTH = 256;
|
|
22
|
+
|
|
23
|
+
// контролы со списком вариантов: браузер другого значения и не отдаст —
|
|
24
|
+
// <option>.value и <input type=radio>.value это ровно объявленный список
|
|
25
|
+
const OPTION_CONTROLS = ['select', 'radio'];
|
|
26
|
+
|
|
27
|
+
// maxlength/regExp форма применяет только к text-полю (formBuilder.
|
|
28
|
+
// validateField), и хост обязан к тому же: отбить значение варианта, который
|
|
29
|
+
// сам же предложил список, значит завести игрока в тупик. Контрол по
|
|
30
|
+
// умолчанию — text (тот же дефолт, что у билдера формы)
|
|
31
|
+
const isTextControl = control => control === 'text' || control === undefined;
|
|
32
|
+
|
|
33
|
+
// source-варианты хост не резолвит (их и форма в auth не резолвит: она
|
|
34
|
+
// строится с пустым ctx) — сверяем только объявленный inline-список.
|
|
35
|
+
// String(): и <option>.value, и <input type=radio>.value — DOM-свойства,
|
|
36
|
+
// они всегда строки, так что options: [1, 2] форма отдаёт как '1'/'2'.
|
|
37
|
+
// Списка нет вовсе (или он не массив — дефект схемы) — валидного значения у
|
|
38
|
+
// поля не существует, и форма это говорит прямо ('no options available'):
|
|
39
|
+
// пропустить здесь что угодно значило бы дать обошедшему форму клиенту
|
|
40
|
+
// больше прав, чем игроку, который войти не может вовсе
|
|
41
|
+
const isDeclaredOption = (list, value) =>
|
|
42
|
+
Array.isArray(list) &&
|
|
43
|
+
normalizeOptions(list).some(opt => String(opt.value) === value);
|
|
44
|
+
|
|
45
|
+
// regExp дескриптора якорится тем же anchorPattern, что и на клиенте
|
|
46
|
+
// (formBuilder.fieldPattern). Некомпилируемый паттерн — дефект схемы игры,
|
|
47
|
+
// а не ограничение: поле проходит, иначе игра с битым regExp не пускала бы
|
|
48
|
+
// в комнату вообще никого
|
|
49
|
+
const matchesPattern = (regExp, value) => {
|
|
50
|
+
try {
|
|
51
|
+
return anchorPattern(regExp).test(value);
|
|
52
|
+
} catch {
|
|
53
|
+
return true;
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
|
|
11
57
|
// движковые правила валидации; игровые (например isValidModel)
|
|
12
58
|
// инжектируются третьим аргументом validateAuth (authSchema игры)
|
|
13
59
|
const validationRules = {
|
|
14
60
|
isValidName,
|
|
15
61
|
};
|
|
16
62
|
|
|
63
|
+
/**
|
|
64
|
+
* Резолвит имя валидатора из дескриптора: движковые правила, перекрытые
|
|
65
|
+
* игровыми. Одно определение на движок — им пользуется и validateAuth
|
|
66
|
+
* (чтобы звать), и правило C10 с PortMachine (чтобы проверять): две копии
|
|
67
|
+
* этого правила уже разъезжались на типе значения, и не-функция в
|
|
68
|
+
* validators игры уходила TypeError'ом прямо в обработчик сообщения.
|
|
69
|
+
* @param {string} name - Имя из options.validator.
|
|
70
|
+
* @param {Object} [validators] - authSchema.validators игры.
|
|
71
|
+
* @returns {Function|undefined} Функция либо undefined, если имя не резолвится.
|
|
72
|
+
*/
|
|
73
|
+
export const resolveValidator = (name, validators = {}) => {
|
|
74
|
+
const fn = { ...validationRules, ...validators }[name];
|
|
75
|
+
|
|
76
|
+
return typeof fn === 'function' ? fn : undefined;
|
|
77
|
+
};
|
|
78
|
+
|
|
17
79
|
/**
|
|
18
80
|
* Валидирует объект с данными для авторизации.
|
|
19
81
|
* @param {object} data - Объект с данными для проверки.
|
|
@@ -23,7 +85,6 @@ const validationRules = {
|
|
|
23
85
|
* @returns {Array|undefined} - Массив ошибок или undefined.
|
|
24
86
|
*/
|
|
25
87
|
export const validateAuth = (data, authParams, validators = {}) => {
|
|
26
|
-
const rules = { ...validationRules, ...validators };
|
|
27
88
|
const errors = [];
|
|
28
89
|
|
|
29
90
|
for (const { name, options } of authParams) {
|
|
@@ -37,14 +98,58 @@ export const validateAuth = (data, authParams, validators = {}) => {
|
|
|
37
98
|
return [{ name, error: `Property must be a string` }];
|
|
38
99
|
}
|
|
39
100
|
|
|
101
|
+
// Те же декларативные правила, по которым отказывает форма
|
|
102
|
+
// (client/lib/formBuilder.js → validateField): клиент, обошедший форму,
|
|
103
|
+
// не должен получать больше прав, чем клиент, её заполнивший. Пустое
|
|
104
|
+
// значение пропускается ровно как на клиенте (required здесь не
|
|
105
|
+
// проверяется: solo-путь boot.autoAuth отвечает дефолтами схемы, среди
|
|
106
|
+
// которых бывает '') — пустота остаётся делом игрового валидатора.
|
|
107
|
+
// min/max формы здесь не применяются и не нужны: числовое поле отдаёт из
|
|
108
|
+
// формы число, а нестроковое значение отбито выше, то есть числовых
|
|
109
|
+
// полей в authSchema не бывает вовсе
|
|
110
|
+
|
|
111
|
+
// длина — первой и безусловно: потолок ограничивает не столько ввод,
|
|
112
|
+
// сколько работу паттерна ниже (см. MAX_FIELD_LENGTH)
|
|
113
|
+
const limit =
|
|
114
|
+
isTextControl(options?.control) && options?.maxlength !== undefined
|
|
115
|
+
? options.maxlength
|
|
116
|
+
: MAX_FIELD_LENGTH;
|
|
117
|
+
|
|
118
|
+
if (value.length > limit) {
|
|
119
|
+
errors.push({ name, error: 'too long' });
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// членство в списке вариантов — самое жёсткое ограничение формы: без
|
|
124
|
+
// этой проверки поле-select без игрового валидатора принимает от
|
|
125
|
+
// обошедшего форму клиента любую строку
|
|
126
|
+
if (
|
|
127
|
+
OPTION_CONTROLS.includes(options?.control) &&
|
|
128
|
+
!options.source &&
|
|
129
|
+
!isDeclaredOption(options.options, value)
|
|
130
|
+
) {
|
|
131
|
+
errors.push({ name, error: 'not an option' });
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (
|
|
136
|
+
value !== '' &&
|
|
137
|
+
isTextControl(options?.control) &&
|
|
138
|
+
options?.regExp &&
|
|
139
|
+
!matchesPattern(options.regExp, value)
|
|
140
|
+
) {
|
|
141
|
+
errors.push({ name, error: 'invalid format' });
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
144
|
+
|
|
40
145
|
if (options?.validator) {
|
|
41
|
-
const validatorFn =
|
|
146
|
+
const validatorFn = resolveValidator(options.validator, validators);
|
|
42
147
|
|
|
43
|
-
//
|
|
44
|
-
//
|
|
45
|
-
//
|
|
46
|
-
//
|
|
47
|
-
//
|
|
148
|
+
// нерезолвнутое имя (опечатка) и не-функция ведут себя одинаково —
|
|
149
|
+
// поле проходит. Для клиента это норма (игровые валидаторы к нему не
|
|
150
|
+
// едут, авторитет проверки на хосте), для хоста — дефект схемы, о
|
|
151
|
+
// котором говорят C10 и console.error в PortMachine: звать что попало
|
|
152
|
+
// нельзя, TypeError отсюда уходит прямо в обработчик сообщения
|
|
48
153
|
if (validatorFn && !validatorFn(value)) {
|
|
49
154
|
errors.push({ name, error: 'not valid' });
|
|
50
155
|
}
|