hola-server 1.0.11 → 2.0.1
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/README.md +196 -1
- package/core/array.js +79 -142
- package/core/bash.js +208 -259
- package/core/chart.js +26 -16
- package/core/cron.js +14 -3
- package/core/date.js +15 -44
- package/core/encrypt.js +19 -9
- package/core/file.js +42 -29
- package/core/lhs.js +32 -6
- package/core/meta.js +213 -289
- package/core/msg.js +20 -7
- package/core/number.js +105 -103
- package/core/obj.js +15 -12
- package/core/random.js +9 -6
- package/core/role.js +69 -77
- package/core/thread.js +12 -2
- package/core/type.js +300 -261
- package/core/url.js +20 -12
- package/core/validate.js +29 -26
- package/db/db.js +297 -227
- package/db/entity.js +631 -963
- package/db/gridfs.js +120 -166
- package/design/add_default_field_attr.md +56 -0
- package/http/context.js +22 -8
- package/http/cors.js +25 -8
- package/http/error.js +27 -9
- package/http/express.js +70 -41
- package/http/params.js +70 -42
- package/http/router.js +51 -40
- package/http/session.js +59 -36
- package/index.js +85 -9
- package/package.json +2 -2
- package/router/clone.js +28 -36
- package/router/create.js +21 -26
- package/router/delete.js +24 -28
- package/router/read.js +137 -123
- package/router/update.js +38 -56
- package/setting.js +22 -6
- package/skills/array.md +155 -0
- package/skills/bash.md +91 -0
- package/skills/chart.md +54 -0
- package/skills/code.md +422 -0
- package/skills/context.md +177 -0
- package/skills/date.md +58 -0
- package/skills/express.md +255 -0
- package/skills/file.md +60 -0
- package/skills/lhs.md +54 -0
- package/skills/meta.md +1023 -0
- package/skills/msg.md +30 -0
- package/skills/number.md +88 -0
- package/skills/obj.md +36 -0
- package/skills/params.md +206 -0
- package/skills/random.md +22 -0
- package/skills/role.md +59 -0
- package/skills/session.md +281 -0
- package/skills/storage.md +743 -0
- package/skills/thread.md +22 -0
- package/skills/type.md +547 -0
- package/skills/url.md +34 -0
- package/skills/validate.md +48 -0
- package/test/cleanup/close-db.js +5 -0
- package/test/core/array.js +226 -0
- package/test/core/chart.js +51 -0
- package/test/core/file.js +59 -0
- package/test/core/lhs.js +44 -0
- package/test/core/number.js +167 -12
- package/test/core/obj.js +47 -0
- package/test/core/random.js +24 -0
- package/test/core/thread.js +20 -0
- package/test/core/type.js +216 -0
- package/test/core/validate.js +67 -0
- package/test/db/db-ops.js +99 -0
- package/test/db/pipe_test.txt +0 -0
- package/test/db/test_case_design.md +528 -0
- package/test/db/test_db_class.js +613 -0
- package/test/db/test_entity_class.js +414 -0
- package/test/db/test_gridfs_class.js +234 -0
- package/test/entity/create.js +1 -1
- package/test/entity/delete-mixed.js +156 -0
- package/test/entity/ref-filter.js +63 -0
- package/tool/gen_i18n.js +55 -21
- package/test/crud/router.js +0 -99
- package/test/router/user.js +0 -17
package/core/type.js
CHANGED
|
@@ -1,329 +1,368 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Type conversion and validation system.
|
|
3
|
+
* @module core/type
|
|
4
|
+
*/
|
|
5
|
+
|
|
1
6
|
const { has_value, is_undefined } = require('./validate');
|
|
2
7
|
const { encrypt_pwd } = require('./encrypt');
|
|
8
|
+
|
|
3
9
|
const type_manager = {};
|
|
4
10
|
|
|
11
|
+
// ============================================================================
|
|
12
|
+
// Helper Functions
|
|
13
|
+
// ============================================================================
|
|
14
|
+
|
|
5
15
|
/**
|
|
6
|
-
*
|
|
7
|
-
* @param {
|
|
16
|
+
* Create success result.
|
|
17
|
+
* @param {*} value - Converted value.
|
|
18
|
+
* @returns {{value: *}} Success result.
|
|
8
19
|
*/
|
|
9
|
-
const
|
|
10
|
-
type_manager[type.name] = type;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
const convert_number = (value, type) => {
|
|
14
|
-
const num_value = Number(value);
|
|
15
|
-
if (isNaN(num_value)) {
|
|
16
|
-
return { err: 'convert error for value:' + value + ",and type:" + type };
|
|
17
|
-
} else {
|
|
18
|
-
return { value: num_value };
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
const convert_float = (value, type) => {
|
|
23
|
-
const float_value = parseFloat(value);
|
|
24
|
-
if (isNaN(float_value)) {
|
|
25
|
-
return { err: 'convert error for value:' + value + ",and type:" + type };
|
|
26
|
-
} else {
|
|
27
|
-
return { value: parseFloat(float_value.toFixed(2)) };
|
|
28
|
-
}
|
|
29
|
-
}
|
|
20
|
+
const ok = (value) => ({ value });
|
|
30
21
|
|
|
31
22
|
/**
|
|
32
|
-
*
|
|
33
|
-
* @param {type
|
|
34
|
-
* @
|
|
23
|
+
* Create error result.
|
|
24
|
+
* @param {string} type - Type name.
|
|
25
|
+
* @param {*} value - Original value.
|
|
26
|
+
* @returns {{err: string}} Error result.
|
|
35
27
|
*/
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
28
|
+
const err = (type, value) => ({ err: `invalid ${type}:${value}` });
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Check if value is an integer.
|
|
32
|
+
* @param {*} value - Value to check.
|
|
33
|
+
* @returns {boolean} True if integer.
|
|
34
|
+
*/
|
|
35
|
+
const is_int = (value) => parseInt(value) === parseFloat(value);
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Create a regex validation type.
|
|
39
|
+
* @param {string} name - Type name.
|
|
40
|
+
* @param {RegExp} pattern - Regex pattern.
|
|
41
|
+
* @returns {Object} Type definition.
|
|
42
|
+
*/
|
|
43
|
+
const regex_type = (name, pattern) => ({
|
|
44
|
+
name,
|
|
45
|
+
convert: (value) => pattern.test(value) ? ok(value) : err(name, value)
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Create an integer range validation type.
|
|
50
|
+
* @param {string} name - Type name.
|
|
51
|
+
* @param {number[]} valid_values - Array of valid values.
|
|
52
|
+
* @returns {Object} Type definition.
|
|
53
|
+
*/
|
|
54
|
+
const int_enum_type = (name, valid_values) => ({
|
|
55
|
+
name,
|
|
56
|
+
convert: (value) => {
|
|
57
|
+
if (!is_int(value)) return err(name, value);
|
|
58
|
+
const int_value = parseInt(value);
|
|
59
|
+
return valid_values.includes(int_value) ? ok(int_value) : err(name, value);
|
|
42
60
|
}
|
|
43
|
-
}
|
|
61
|
+
});
|
|
44
62
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
63
|
+
/**
|
|
64
|
+
* Create an integer range validation type.
|
|
65
|
+
* @param {string} name - Type name.
|
|
66
|
+
* @param {number} min - Minimum value.
|
|
67
|
+
* @param {number} max - Maximum value.
|
|
68
|
+
* @returns {Object} Type definition.
|
|
69
|
+
*/
|
|
70
|
+
const int_range_type = (name, min, max) => ({
|
|
71
|
+
name,
|
|
72
|
+
convert: (value) => {
|
|
73
|
+
if (!is_int(value)) return err(name, value);
|
|
74
|
+
const int_value = parseInt(value);
|
|
75
|
+
return (int_value >= min && int_value <= max) ? ok(int_value) : err(name, value);
|
|
49
76
|
}
|
|
50
|
-
}
|
|
77
|
+
});
|
|
51
78
|
|
|
52
|
-
|
|
79
|
+
/**
|
|
80
|
+
* Create a passthrough string type.
|
|
81
|
+
* @param {string} name - Type name.
|
|
82
|
+
* @returns {Object} Type definition.
|
|
83
|
+
*/
|
|
84
|
+
const string_type = (name) => ({ name, convert: (value) => ok(value + "") });
|
|
85
|
+
|
|
86
|
+
// ============================================================================
|
|
87
|
+
// Type Registration
|
|
88
|
+
// ============================================================================
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Register a custom type with conversion function.
|
|
92
|
+
* @param {Object} type - Type definition with name and convert function.
|
|
93
|
+
*/
|
|
94
|
+
const register_type = (type) => { type_manager[type.name] = type; };
|
|
53
95
|
|
|
54
|
-
|
|
96
|
+
/**
|
|
97
|
+
* Get registered type by name.
|
|
98
|
+
* @param {string} name - Type name.
|
|
99
|
+
* @returns {Object} Type definition.
|
|
100
|
+
* @throws {Error} If type not registered.
|
|
101
|
+
*/
|
|
102
|
+
const get_type = (name) => {
|
|
103
|
+
const type = type_manager[name];
|
|
104
|
+
if (!type) throw new Error(`no type registered for name [${name}]`);
|
|
105
|
+
return type;
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
// ============================================================================
|
|
109
|
+
// Built-in Types: Basic
|
|
110
|
+
// ============================================================================
|
|
111
|
+
|
|
112
|
+
register_type({ name: "obj", convert: ok });
|
|
113
|
+
register_type({ name: "string", convert: (value) => ok(value ? (value + "").trim() : "") });
|
|
114
|
+
register_type({ name: "password", convert: (value) => ok(encrypt_pwd(value)) });
|
|
115
|
+
register_type({ name: "file", convert: ok });
|
|
116
|
+
|
|
117
|
+
// Passthrough string types
|
|
118
|
+
register_type(string_type("lstr"));
|
|
119
|
+
register_type(string_type("text"));
|
|
120
|
+
register_type(string_type("date"));
|
|
121
|
+
register_type(string_type("enum"));
|
|
122
|
+
register_type(string_type("log_category"));
|
|
123
|
+
|
|
124
|
+
// ============================================================================
|
|
125
|
+
// Built-in Types: Boolean
|
|
126
|
+
// ============================================================================
|
|
127
|
+
|
|
128
|
+
register_type({
|
|
55
129
|
name: "boolean",
|
|
56
|
-
convert:
|
|
57
|
-
if (value === true || value === "true")
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
return { value: false };
|
|
61
|
-
} else {
|
|
62
|
-
return { err: 'boolean convert error for value:' + value };
|
|
63
|
-
}
|
|
130
|
+
convert: (value) => {
|
|
131
|
+
if (value === true || value === "true") return ok(true);
|
|
132
|
+
if (value === false || value === "false") return ok(false);
|
|
133
|
+
return err("boolean", value);
|
|
64
134
|
}
|
|
65
|
-
}
|
|
135
|
+
});
|
|
66
136
|
|
|
67
|
-
|
|
137
|
+
// ============================================================================
|
|
138
|
+
// Built-in Types: Numeric
|
|
139
|
+
// ============================================================================
|
|
68
140
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
const
|
|
73
|
-
|
|
74
|
-
convert: function (value) {
|
|
75
|
-
return is_int(value) ? { value: parseInt(value) } : { err: 'int convert error for value:' + value };
|
|
141
|
+
register_type({
|
|
142
|
+
name: "number",
|
|
143
|
+
convert: (value) => {
|
|
144
|
+
const num = Number(value);
|
|
145
|
+
return isNaN(num) ? err("number", value) : ok(num);
|
|
76
146
|
}
|
|
77
|
-
}
|
|
147
|
+
});
|
|
78
148
|
|
|
79
|
-
register_type(
|
|
149
|
+
register_type({
|
|
150
|
+
name: "int",
|
|
151
|
+
convert: (value) => is_int(value) ? ok(parseInt(value)) : err("int", value)
|
|
152
|
+
});
|
|
80
153
|
|
|
81
|
-
|
|
154
|
+
register_type({
|
|
82
155
|
name: "uint",
|
|
83
|
-
convert:
|
|
156
|
+
convert: (value) => {
|
|
84
157
|
const int_value = parseInt(value);
|
|
85
|
-
return is_int(value) && int_value >= 0 ?
|
|
158
|
+
return (is_int(value) && int_value >= 0) ? ok(int_value) : err("uint", value);
|
|
86
159
|
}
|
|
87
|
-
}
|
|
160
|
+
});
|
|
88
161
|
|
|
89
|
-
register_type(
|
|
90
|
-
|
|
91
|
-
const float_type = {
|
|
162
|
+
register_type({
|
|
92
163
|
name: "float",
|
|
93
|
-
convert:
|
|
94
|
-
|
|
164
|
+
convert: (value) => {
|
|
165
|
+
const num = parseFloat(value);
|
|
166
|
+
return isNaN(num) ? err("float", value) : ok(parseFloat(num.toFixed(2)));
|
|
95
167
|
}
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
register_type(float_type);
|
|
168
|
+
});
|
|
99
169
|
|
|
100
|
-
|
|
170
|
+
register_type({
|
|
101
171
|
name: "ufloat",
|
|
102
|
-
convert:
|
|
103
|
-
const
|
|
104
|
-
|
|
105
|
-
return { err: 'float convert error for value:' + value };
|
|
106
|
-
} else {
|
|
107
|
-
return { value: parseFloat(float_value.toFixed(2)) };
|
|
108
|
-
}
|
|
172
|
+
convert: (value) => {
|
|
173
|
+
const num = parseFloat(value);
|
|
174
|
+
return (isNaN(num) || num < 0) ? err("ufloat", value) : ok(parseFloat(num.toFixed(2)));
|
|
109
175
|
}
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
register_type(ufloat_type);
|
|
176
|
+
});
|
|
113
177
|
|
|
114
|
-
|
|
115
|
-
name: "
|
|
116
|
-
convert:
|
|
117
|
-
|
|
178
|
+
register_type({
|
|
179
|
+
name: "decimal",
|
|
180
|
+
convert: (value) => {
|
|
181
|
+
const num = parseFloat(value);
|
|
182
|
+
return isNaN(num) ? err("decimal", value) : ok(num);
|
|
118
183
|
}
|
|
119
|
-
}
|
|
184
|
+
});
|
|
120
185
|
|
|
121
|
-
register_type(
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
return { value: value ? (value + "").trim() : "" };
|
|
186
|
+
register_type({
|
|
187
|
+
name: "percentage",
|
|
188
|
+
convert: (value) => {
|
|
189
|
+
const num = parseFloat(value);
|
|
190
|
+
return isNaN(num) ? err("percentage", value) : ok(parseFloat(num.toFixed(2)));
|
|
127
191
|
}
|
|
128
|
-
}
|
|
192
|
+
});
|
|
129
193
|
|
|
130
|
-
register_type(
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
return { value: encrypt_pwd(value) };
|
|
194
|
+
register_type({
|
|
195
|
+
name: "currency",
|
|
196
|
+
convert: (value) => {
|
|
197
|
+
const num = Number(value);
|
|
198
|
+
return isNaN(num) ? err("currency", value) : ok(num);
|
|
136
199
|
}
|
|
137
|
-
}
|
|
200
|
+
});
|
|
138
201
|
|
|
139
|
-
|
|
202
|
+
// ============================================================================
|
|
203
|
+
// Built-in Types: Date/Time
|
|
204
|
+
// ============================================================================
|
|
140
205
|
|
|
141
|
-
|
|
142
|
-
name: "
|
|
143
|
-
convert:
|
|
144
|
-
|
|
206
|
+
register_type({
|
|
207
|
+
name: "datetime",
|
|
208
|
+
convert: (value) => {
|
|
209
|
+
const date = new Date(value);
|
|
210
|
+
return isNaN(date.getTime()) ? err("datetime", value) : ok(date.toISOString());
|
|
145
211
|
}
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
register_type(file_type);
|
|
212
|
+
});
|
|
149
213
|
|
|
150
|
-
|
|
151
|
-
name: "array",
|
|
152
|
-
convert: function (value) {
|
|
153
|
-
if (typeof value === "string") {
|
|
154
|
-
return { value: value.split(",") }
|
|
155
|
-
} else if (Array.isArray(value)) {
|
|
156
|
-
return { value: value }
|
|
157
|
-
} else {
|
|
158
|
-
return { err: "error array type" }
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
}
|
|
214
|
+
register_type(regex_type("time", /^([0-1]?[0-9]|2[0-3]):[0-5][0-9](:[0-5][0-9])?$/));
|
|
162
215
|
|
|
163
|
-
|
|
216
|
+
// ============================================================================
|
|
217
|
+
// Built-in Types: Validation
|
|
218
|
+
// ============================================================================
|
|
164
219
|
|
|
165
|
-
|
|
166
|
-
name: "
|
|
167
|
-
convert:
|
|
168
|
-
|
|
220
|
+
register_type({
|
|
221
|
+
name: "email",
|
|
222
|
+
convert: (value) => {
|
|
223
|
+
const pattern = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
|
|
224
|
+
return pattern.test(value) ? ok(value) : err("email", value);
|
|
169
225
|
}
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
register_type(lstr_type);
|
|
226
|
+
});
|
|
173
227
|
|
|
174
|
-
|
|
175
|
-
name: "
|
|
176
|
-
convert:
|
|
177
|
-
|
|
228
|
+
register_type({
|
|
229
|
+
name: "url",
|
|
230
|
+
convert: (value) => {
|
|
231
|
+
try { new URL(value); return ok(value); }
|
|
232
|
+
catch { return err("url", value); }
|
|
178
233
|
}
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
register_type(text_type);
|
|
234
|
+
});
|
|
182
235
|
|
|
183
|
-
|
|
184
|
-
name: "
|
|
185
|
-
convert:
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
register_type(date_type);
|
|
191
|
-
|
|
192
|
-
const age_type = {
|
|
193
|
-
name: "age",
|
|
194
|
-
convert: function (value) {
|
|
195
|
-
if (is_int(value)) {
|
|
196
|
-
const int_value = parseInt(value);
|
|
197
|
-
if (int_value < 0 || int_value > 200) {
|
|
198
|
-
return { err: "invalid age value:" + value };
|
|
199
|
-
} else {
|
|
200
|
-
return { value: int_value };
|
|
201
|
-
}
|
|
202
|
-
} else {
|
|
203
|
-
return { err: "age isn't int type:" + value };
|
|
204
|
-
}
|
|
236
|
+
register_type({
|
|
237
|
+
name: "phone",
|
|
238
|
+
convert: (value) => {
|
|
239
|
+
const cleaned = value.replace(/[\s\-\(\)]/g, '');
|
|
240
|
+
return /^\+?[1-9]\d{1,14}$/.test(cleaned) ? ok(cleaned) : err("phone", value);
|
|
205
241
|
}
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
register_type(age_type);
|
|
209
|
-
|
|
210
|
-
const gender_type = {
|
|
211
|
-
name: "gender",
|
|
212
|
-
convert: function (value) {
|
|
213
|
-
if (is_int(value)) {
|
|
214
|
-
const int_value = parseInt(value);
|
|
215
|
-
if (int_value == 0 || int_value == 1) {
|
|
216
|
-
return { value: int_value };
|
|
217
|
-
} else {
|
|
218
|
-
return { err: "invalid gender value:" + value }
|
|
219
|
-
}
|
|
220
|
-
} else {
|
|
221
|
-
return { err: "gender isn't int type:" + value };
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
register_type(gender_type);
|
|
227
|
-
|
|
228
|
-
const log_level_type = {
|
|
229
|
-
name: "log_level",
|
|
230
|
-
convert: function (value) {
|
|
231
|
-
if (is_int(value)) {
|
|
232
|
-
const int_value = parseInt(value);
|
|
233
|
-
if (int_value == 0 || int_value == 1 || int_value == 2 || int_value == 3) {
|
|
234
|
-
return { value: int_value };
|
|
235
|
-
} else {
|
|
236
|
-
return { err: "invalid level value:" + value }
|
|
237
|
-
}
|
|
238
|
-
} else {
|
|
239
|
-
return { err: "log level isn't int type:" + value };
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
}
|
|
242
|
+
});
|
|
243
243
|
|
|
244
|
-
register_type(
|
|
244
|
+
register_type(regex_type("uuid", /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i));
|
|
245
|
+
register_type(regex_type("color", /^#([0-9A-F]{3}){1,2}$/i));
|
|
245
246
|
|
|
246
|
-
|
|
247
|
-
name: "
|
|
248
|
-
convert:
|
|
249
|
-
|
|
247
|
+
register_type({
|
|
248
|
+
name: "ip_address",
|
|
249
|
+
convert: (value) => {
|
|
250
|
+
if (!/^(\d{1,3}\.){3}\d{1,3}$/.test(value)) return err("ip_address", value);
|
|
251
|
+
const valid = value.split('.').every(part => parseInt(part) <= 255);
|
|
252
|
+
return valid ? ok(value) : err("ip_address", value);
|
|
250
253
|
}
|
|
251
|
-
}
|
|
254
|
+
});
|
|
252
255
|
|
|
253
|
-
|
|
256
|
+
// ============================================================================
|
|
257
|
+
// Built-in Types: Data Structures
|
|
258
|
+
// ============================================================================
|
|
254
259
|
|
|
255
|
-
|
|
256
|
-
name: "
|
|
257
|
-
convert:
|
|
258
|
-
|
|
260
|
+
register_type({
|
|
261
|
+
name: "array",
|
|
262
|
+
convert: (value) => {
|
|
263
|
+
if (typeof value === "string") return ok(value.split(","));
|
|
264
|
+
if (Array.isArray(value)) return ok(value);
|
|
265
|
+
return err("array", value);
|
|
259
266
|
}
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
register_type(
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
return
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
register_type({
|
|
270
|
+
name: "json",
|
|
271
|
+
convert: (value) => {
|
|
272
|
+
if (typeof value === 'object') return ok(value);
|
|
273
|
+
try { return ok(JSON.parse(value)); }
|
|
274
|
+
catch { return err("json", value); }
|
|
268
275
|
}
|
|
269
|
-
}
|
|
276
|
+
});
|
|
270
277
|
|
|
271
|
-
|
|
278
|
+
// ============================================================================
|
|
279
|
+
// Built-in Types: Transformations
|
|
280
|
+
// ============================================================================
|
|
272
281
|
|
|
273
|
-
|
|
274
|
-
name: "
|
|
275
|
-
convert:
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
return { err: 'err email for value:' + value };
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
|
-
}
|
|
282
|
+
register_type({
|
|
283
|
+
name: "slug",
|
|
284
|
+
convert: (value) => ok(value.toString().toLowerCase().trim()
|
|
285
|
+
.replace(/\s+/g, '-')
|
|
286
|
+
.replace(/[^\w\-]+/g, '')
|
|
287
|
+
.replace(/\-\-+/g, '-'))
|
|
288
|
+
});
|
|
284
289
|
|
|
285
|
-
|
|
290
|
+
// ============================================================================
|
|
291
|
+
// Built-in Types: Domain-Specific
|
|
292
|
+
// ============================================================================
|
|
286
293
|
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
294
|
+
register_type(int_range_type("age", 0, 200));
|
|
295
|
+
register_type(int_enum_type("gender", [0, 1]));
|
|
296
|
+
register_type(int_enum_type("log_level", [0, 1, 2, 3]));
|
|
290
297
|
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
}
|
|
298
|
+
// ============================================================================
|
|
299
|
+
// Conversion Functions
|
|
300
|
+
// ============================================================================
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* Convert a single field value.
|
|
304
|
+
* @param {*} field_value - Value to convert.
|
|
305
|
+
* @param {string} type_name - Type name.
|
|
306
|
+
* @returns {{value?: *, err?: string}} Conversion result.
|
|
307
|
+
*/
|
|
308
|
+
const convert_field = (field_value, type_name) => {
|
|
309
|
+
const type = get_type(type_name || "string");
|
|
310
|
+
return type.convert(field_value);
|
|
311
|
+
};
|
|
306
312
|
|
|
307
|
-
|
|
313
|
+
/**
|
|
314
|
+
* Convert object fields to their defined types.
|
|
315
|
+
* @param {Object} obj - Object with values to convert.
|
|
316
|
+
* @param {Array<{name: string, type?: string}>} fields - Field definitions.
|
|
317
|
+
* @param {boolean} preserve_empty - Whether to preserve empty values as "".
|
|
318
|
+
* @returns {{obj: Object, error_field_names: string[]}} Converted object and error fields.
|
|
319
|
+
*/
|
|
320
|
+
const convert_fields = (obj, fields, preserve_empty = false) => {
|
|
308
321
|
const result = {};
|
|
309
322
|
const error_field_names = [];
|
|
310
323
|
|
|
311
|
-
|
|
324
|
+
for (const field of fields) {
|
|
312
325
|
const field_value = obj[field.name];
|
|
326
|
+
|
|
313
327
|
if (has_value(field_value)) {
|
|
314
|
-
const
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
error_field_names.push(field.name);
|
|
319
|
-
} else {
|
|
320
|
-
result[field.name] = value;
|
|
321
|
-
}
|
|
322
|
-
} else if (!is_undefined(field_value)) {
|
|
328
|
+
const { value, err } = convert_field(field_value, field.type);
|
|
329
|
+
if (err) error_field_names.push(field.name);
|
|
330
|
+
else result[field.name] = value;
|
|
331
|
+
} else if (preserve_empty && !is_undefined(field_value)) {
|
|
323
332
|
result[field.name] = "";
|
|
324
333
|
}
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
}
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
return { obj: result, error_field_names };
|
|
337
|
+
};
|
|
328
338
|
|
|
329
|
-
|
|
339
|
+
/**
|
|
340
|
+
* Convert object fields to their defined types.
|
|
341
|
+
* @param {Object} obj - Object with values to convert.
|
|
342
|
+
* @param {Array<{name: string, type?: string}>} fields - Field definitions.
|
|
343
|
+
* @returns {{obj: Object, error_field_names: string[]}} Converted object and error fields.
|
|
344
|
+
*/
|
|
345
|
+
const convert_type = (obj, fields) => convert_fields(obj, fields, false);
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* Convert object fields for update operation (preserves empty values).
|
|
349
|
+
* @param {Object} obj - Object with values to convert.
|
|
350
|
+
* @param {Array<{name: string, type?: string}>} fields - Field definitions.
|
|
351
|
+
* @returns {{obj: Object, error_field_names: string[]}} Converted object and error fields.
|
|
352
|
+
*/
|
|
353
|
+
const convert_update_type = (obj, fields) => convert_fields(obj, fields, true);
|
|
354
|
+
|
|
355
|
+
module.exports = {
|
|
356
|
+
register_type,
|
|
357
|
+
convert_type,
|
|
358
|
+
convert_update_type,
|
|
359
|
+
get_type,
|
|
360
|
+
// Helper functions for custom type creation
|
|
361
|
+
ok,
|
|
362
|
+
err,
|
|
363
|
+
is_int,
|
|
364
|
+
regex_type,
|
|
365
|
+
int_enum_type,
|
|
366
|
+
int_range_type,
|
|
367
|
+
string_type
|
|
368
|
+
};
|
package/core/url.js
CHANGED
|
@@ -1,22 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview URL and HTTP request utility functions.
|
|
3
|
+
* @module core/url
|
|
4
|
+
*/
|
|
5
|
+
|
|
1
6
|
const axios = require('axios');
|
|
2
7
|
const { get_settings } = require("../setting");
|
|
3
8
|
|
|
4
|
-
|
|
9
|
+
/**
|
|
10
|
+
* Create HTTP request function with preset URL and method.
|
|
11
|
+
* @param {string} url - Request URL.
|
|
12
|
+
* @param {string} method - HTTP method (GET, POST, etc.).
|
|
13
|
+
* @returns {Function} Function that accepts config and returns axios promise.
|
|
14
|
+
*/
|
|
15
|
+
const url = (url, method) => {
|
|
5
16
|
const settings = get_settings();
|
|
6
17
|
const axios_config = settings.axios;
|
|
7
18
|
|
|
8
|
-
return
|
|
9
|
-
|
|
10
|
-
url
|
|
11
|
-
method
|
|
12
|
-
validateStatus: false
|
|
19
|
+
return (config) => {
|
|
20
|
+
const params = {
|
|
21
|
+
url,
|
|
22
|
+
method,
|
|
23
|
+
validateStatus: false,
|
|
24
|
+
...(axios_config.proxy && { proxy: axios_config.proxy })
|
|
13
25
|
};
|
|
14
|
-
if (axios_config.proxy) {
|
|
15
|
-
params.proxy = axios_config.proxy;
|
|
16
|
-
}
|
|
17
26
|
return axios.request({ ...params, ...config });
|
|
18
|
-
}
|
|
27
|
+
};
|
|
19
28
|
};
|
|
20
29
|
|
|
21
|
-
|
|
22
|
-
module.exports = { url }
|
|
30
|
+
module.exports = { url };
|