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.
Files changed (83) hide show
  1. package/README.md +196 -1
  2. package/core/array.js +79 -142
  3. package/core/bash.js +208 -259
  4. package/core/chart.js +26 -16
  5. package/core/cron.js +14 -3
  6. package/core/date.js +15 -44
  7. package/core/encrypt.js +19 -9
  8. package/core/file.js +42 -29
  9. package/core/lhs.js +32 -6
  10. package/core/meta.js +213 -289
  11. package/core/msg.js +20 -7
  12. package/core/number.js +105 -103
  13. package/core/obj.js +15 -12
  14. package/core/random.js +9 -6
  15. package/core/role.js +69 -77
  16. package/core/thread.js +12 -2
  17. package/core/type.js +300 -261
  18. package/core/url.js +20 -12
  19. package/core/validate.js +29 -26
  20. package/db/db.js +297 -227
  21. package/db/entity.js +631 -963
  22. package/db/gridfs.js +120 -166
  23. package/design/add_default_field_attr.md +56 -0
  24. package/http/context.js +22 -8
  25. package/http/cors.js +25 -8
  26. package/http/error.js +27 -9
  27. package/http/express.js +70 -41
  28. package/http/params.js +70 -42
  29. package/http/router.js +51 -40
  30. package/http/session.js +59 -36
  31. package/index.js +85 -9
  32. package/package.json +2 -2
  33. package/router/clone.js +28 -36
  34. package/router/create.js +21 -26
  35. package/router/delete.js +24 -28
  36. package/router/read.js +137 -123
  37. package/router/update.js +38 -56
  38. package/setting.js +22 -6
  39. package/skills/array.md +155 -0
  40. package/skills/bash.md +91 -0
  41. package/skills/chart.md +54 -0
  42. package/skills/code.md +422 -0
  43. package/skills/context.md +177 -0
  44. package/skills/date.md +58 -0
  45. package/skills/express.md +255 -0
  46. package/skills/file.md +60 -0
  47. package/skills/lhs.md +54 -0
  48. package/skills/meta.md +1023 -0
  49. package/skills/msg.md +30 -0
  50. package/skills/number.md +88 -0
  51. package/skills/obj.md +36 -0
  52. package/skills/params.md +206 -0
  53. package/skills/random.md +22 -0
  54. package/skills/role.md +59 -0
  55. package/skills/session.md +281 -0
  56. package/skills/storage.md +743 -0
  57. package/skills/thread.md +22 -0
  58. package/skills/type.md +547 -0
  59. package/skills/url.md +34 -0
  60. package/skills/validate.md +48 -0
  61. package/test/cleanup/close-db.js +5 -0
  62. package/test/core/array.js +226 -0
  63. package/test/core/chart.js +51 -0
  64. package/test/core/file.js +59 -0
  65. package/test/core/lhs.js +44 -0
  66. package/test/core/number.js +167 -12
  67. package/test/core/obj.js +47 -0
  68. package/test/core/random.js +24 -0
  69. package/test/core/thread.js +20 -0
  70. package/test/core/type.js +216 -0
  71. package/test/core/validate.js +67 -0
  72. package/test/db/db-ops.js +99 -0
  73. package/test/db/pipe_test.txt +0 -0
  74. package/test/db/test_case_design.md +528 -0
  75. package/test/db/test_db_class.js +613 -0
  76. package/test/db/test_entity_class.js +414 -0
  77. package/test/db/test_gridfs_class.js +234 -0
  78. package/test/entity/create.js +1 -1
  79. package/test/entity/delete-mixed.js +156 -0
  80. package/test/entity/ref-filter.js +63 -0
  81. package/tool/gen_i18n.js +55 -21
  82. package/test/crud/router.js +0 -99
  83. package/test/router/user.js +0 -17
package/core/number.js CHANGED
@@ -1,179 +1,181 @@
1
+ /**
2
+ * @fileoverview Number manipulation and mathematical utility functions.
3
+ * @module core/number
4
+ */
5
+
1
6
  const { has_value } = require('./validate');
2
7
  const { is_object } = require('./obj');
3
8
 
4
9
  /**
5
- * Parse String value to number
6
- * @param {String value to parse to number} str
7
- * @returns
10
+ * Parse string value to number, returns 0 if invalid.
11
+ * @param {string|number} str - Value to parse.
12
+ * @returns {number} Parsed number or 0.
8
13
  */
9
- const parse_num = function (str) {
14
+ const parse_num = (str) => {
10
15
  const value = parseFloat(str);
11
16
  return value ? value : 0;
12
17
  };
13
18
 
14
19
  /**
15
- * Parse String value to fixed2 number
16
- * @param {String value to parse to number} str
17
- * @returns
20
+ * Parse string value to number with 2 decimal places.
21
+ * @param {string|number} str - Value to parse.
22
+ * @returns {number} Parsed number rounded to 2 decimal places.
18
23
  */
19
- const to_fixed2 = function (str) {
24
+ const to_fixed2 = (str) => {
20
25
  const value = parse_num(str);
21
26
  return value ? parseFloat(value.toFixed(2)) : 0;
22
27
  };
23
28
 
24
29
  /**
25
- * Round the number to fixed 2
26
- * @param {number to be rounded to fixed2} num
27
- * @returns
30
+ * Round number to 2 decimal places.
31
+ * @param {number} num - Number to round.
32
+ * @returns {number} Rounded number.
28
33
  */
29
- const round_to_fixed2 = function (num) {
30
- return Math.round(num * 100) / 100;
31
- };
34
+ const round_to_fixed2 = (num) => Math.round(num * 100) / 100;
32
35
 
33
36
  /**
34
- * Generate range array, for example: range(3) = [0,1,2]
35
- * @param {start index} start
36
- * @param {end index} end
37
- * @param {stepping} step
38
- * @returns
37
+ * Generate range array. Example: range(3) = [0,1,2], range(1,5) = [1,2,3,4,5]
38
+ * @param {number} start - Start value or length if end not provided.
39
+ * @param {number} [end] - End value (inclusive).
40
+ * @param {number} [step=1] - Step increment.
41
+ * @returns {number[]} Array of numbers in the range.
39
42
  */
40
- const range = (start, end, step = 1) => end ? [...Array(Math.floor((end - start) / step) + 1)].map((_, i) => start + i * step) : Array.from({ length: start }, (_, key) => key);
43
+ const range = (start, end, step = 1) => {
44
+ if (!end) return Array.from({ length: start }, (_, key) => key);
45
+ return [...Array(Math.floor((end - start) / step) + 1)].map((_, i) => start + i * step);
46
+ };
41
47
 
42
48
  /**
43
- * Generate scale array, for example: scale(2,10) = [2,4,8]
44
- * @param {start index} start
45
- * @param {end index} end
46
- * @param {scale ratio} scale
47
- * @returns
49
+ * Generate scale array with exponential growth. Example: scale(2,10) = [2,4,8]
50
+ * @param {number} start - Start value.
51
+ * @param {number} end - End value.
52
+ * @param {number} [ratio=2] - Scale multiplier.
53
+ * @returns {number[]} Array of scaled numbers.
48
54
  */
49
- const scale = (start, end, scale = 2) => [...Array(Math.floor(Math.log(end / start) / Math.log(scale)) + 1)].map((_, i) => start * Math.pow(scale, i));
55
+ const scale = (start, end, ratio = 2) => {
56
+ const length = Math.floor(Math.log(end / start) / Math.log(ratio)) + 1;
57
+ return [...Array(length)].map((_, i) => start * Math.pow(ratio, i));
58
+ };
50
59
 
51
60
  /**
52
- * Create a space
53
- * @param {min value} min
54
- * @param {max value} max
55
- * @returns
61
+ * Create a space object representing a min-max range.
62
+ * @param {number} min - Minimum value.
63
+ * @param {number} max - Maximum value.
64
+ * @returns {{min: number, max: number}} Space object.
65
+ * @throws {Error} If min or max not provided.
56
66
  */
57
67
  const space = (min, max) => {
58
- if (has_value(min) && has_value(max)) {
59
- return { ["min"]: min, ["max"]: max };
60
- }
61
-
62
- throw new Error("min and max not provide for space");
63
- }
68
+ if (!has_value(min) || !has_value(max)) throw new Error("min and max not provided for space");
69
+ return { min, max };
70
+ };
64
71
 
65
72
  /**
66
- * Check this value is space object
67
- * @param {value to be checked} value
68
- * @returns true if it is space otherwise return false
73
+ * Check if value is a space object (has min and max properties).
74
+ * @param {*} value - Value to check.
75
+ * @returns {boolean} True if value is a space object.
69
76
  */
70
- const is_space = (value) => {
71
- return is_object(value) && has_value(value.min) && has_value(value.max);
72
- }
77
+ const is_space = (value) => is_object(value) && has_value(value.min) && has_value(value.max);
73
78
 
74
79
  /**
75
- * check the object is integer or not
76
- * @param {string need to check} str
77
- * @returns
80
+ * Check if value is an integer.
81
+ * @param {*} value - Value to check.
82
+ * @returns {boolean} True if value is an integer.
78
83
  */
79
- const is_integer = (value) => {
80
- const regex_pattern = /^-?[0-9]+$/;
81
- return regex_pattern.test(value);
82
- }
84
+ const is_integer = (value) => /^-?[0-9]+$/.test(value);
83
85
 
84
86
  /**
85
- * Check the object attr contains space object or not
86
- * @param {object need to check} obj
87
- * @returns true if contains space
87
+ * Check if object contains any space objects as property values.
88
+ * @param {Object} obj - Object to check.
89
+ * @returns {boolean} True if any property is a space object.
88
90
  */
89
91
  const contains_space = (obj) => {
90
92
  for (const key in obj) {
91
- const value = obj[key];
92
- if (is_space(value)) {
93
- return true;
94
- }
93
+ if (is_space(obj[key])) return true;
95
94
  }
96
95
  return false;
97
- }
96
+ };
98
97
 
99
98
  /**
100
- * Generate the random number
101
- * @param {min value} min
102
- * @param {max value} max
103
- * @returns
99
+ * Generate random number between min and max.
100
+ * @param {number} min - Minimum value.
101
+ * @param {number} max - Maximum value.
102
+ * @returns {number} Random number (integer if both bounds are integers).
104
103
  */
105
104
  const random_number = (min, max) => {
106
105
  const random = Math.random() * (max - min) + min;
107
- return is_integer(min) && is_integer(max) ? Math.floor(random) : to_fixed(random);
108
- }
106
+ return is_integer(min) && is_integer(max) ? Math.floor(random) : to_fixed2(random);
107
+ };
109
108
 
110
109
  /**
111
- * Use LHS method to generate sample ranges
112
- * @param {min value} min
113
- * @param {max value} max
114
- * @param {sample number} n
115
- * @returns
110
+ * Generate Latin Hypercube Sampling ranges for a given range.
111
+ * @param {number} min - Minimum value.
112
+ * @param {number} max - Maximum value.
113
+ * @param {number} n - Number of samples.
114
+ * @returns {{min: number, max: number}[]} Array of sample ranges.
116
115
  */
117
116
  const lhs_samples = (min, max, n) => {
118
117
  const all_int = is_integer(min + "") && is_integer(max + "");
119
118
  const interval = (max - min) / n;
120
119
  const ranges = [];
120
+
121
121
  for (let i = 0; i < n; i++) {
122
122
  const start = i * interval + min;
123
- let end = start + interval;
124
- if (i == n - 1) {
125
- end = max;
126
- }
127
-
128
- const min_value = start < end ? start : end;
129
- const max_value = start > end ? start : end;
130
-
131
- let obj = {};
132
- if (all_int) {
133
- obj = { min: Math.floor(min_value), max: Math.floor(max_value) };
134
- } else {
135
- obj = { min: to_fixed2(min_value), max: to_fixed2(max_value) };
136
- }
137
- ranges.push(obj);
123
+ const end = (i === n - 1) ? max : start + interval;
124
+ const min_val = Math.min(start, end);
125
+ const max_val = Math.max(start, end);
126
+
127
+ ranges.push(all_int
128
+ ? { min: Math.floor(min_val), max: Math.floor(max_val) }
129
+ : { min: to_fixed2(min_val), max: to_fixed2(max_val) }
130
+ );
138
131
  }
139
132
  return ranges;
140
- }
133
+ };
141
134
 
142
135
  /**
143
- * Create an random sample object
144
- * @param {object } sample configuration
145
- * @returns a sample object
136
+ * Create random sample object from configuration with arrays or space objects.
137
+ * @param {Object} obj - Configuration object with arrays or space values.
138
+ * @returns {Object} Sample object with randomly selected values.
146
139
  */
147
140
  const random_sample = (obj) => {
148
141
  const sample_obj = {};
149
142
  for (const key in obj) {
150
143
  const value = obj[key];
151
144
  if (Array.isArray(value)) {
152
- const random = Math.floor(random_number(0, value.length));
153
- sample_obj[key] = value[random];
145
+ sample_obj[key] = value[Math.floor(random_number(0, value.length))];
154
146
  } else if (is_space(value)) {
155
- const random = random_number(value.min, value.max);
156
- sample_obj[key] = random;
147
+ sample_obj[key] = random_number(value.min, value.max);
157
148
  } else {
158
149
  sample_obj[key] = value;
159
150
  }
160
151
  }
161
152
  return sample_obj;
162
- }
153
+ };
163
154
 
164
155
  /**
165
- * Extract number from string
166
- * @param {contain number value} value
167
- * @returns
156
+ * Extract numeric value from string containing numbers.
157
+ * @param {string} value - String to extract number from.
158
+ * @returns {number} Extracted number or 0 if not found.
168
159
  */
169
160
  const extract_number = (value) => {
170
161
  const numbers = value.match(/([+\-0-9\\.]+)/g);
171
- if (!numbers) {
172
- return 0;
173
- }
174
-
175
- const values = numbers.map(Number).filter(v => has_value(v));
176
- return values.length == 1 ? values[0] : 0;
177
- }
162
+ if (!numbers) return 0;
163
+ const values = numbers.map(Number).filter((v) => has_value(v));
164
+ return values.length === 1 ? values[0] : 0;
165
+ };
178
166
 
179
- module.exports = { parse_num, extract_number, to_fixed2, round_to_fixed2, range, scale, space, is_space, contains_space, is_integer, random_number, random_sample, lhs_samples }
167
+ module.exports = {
168
+ parse_num,
169
+ extract_number,
170
+ to_fixed2,
171
+ round_to_fixed2,
172
+ range,
173
+ scale,
174
+ space,
175
+ is_space,
176
+ contains_space,
177
+ is_integer,
178
+ random_number,
179
+ random_sample,
180
+ lhs_samples
181
+ };
package/core/obj.js CHANGED
@@ -1,22 +1,25 @@
1
1
  /**
2
- * Create a new object based on the attrs values of the object
3
- * @param {Object to be copied} obj
4
- * @param {attributes to be copied} attrs
5
- * @returns
2
+ * @fileoverview Object manipulation utility functions.
3
+ * @module core/obj
4
+ */
5
+
6
+ /**
7
+ * Create a new object by copying specified attributes from source object.
8
+ * @param {Object} obj - Source object to copy from.
9
+ * @param {string[]} attrs - Array of attribute names to copy.
10
+ * @returns {Object} New object containing only the specified attributes.
6
11
  */
7
12
  const copy_obj = (obj, attrs) => {
8
13
  const copied = {};
9
- attrs.forEach(attr => copied[attr] = obj[attr]);
14
+ attrs.forEach((attr) => { copied[attr] = obj[attr]; });
10
15
  return copied;
11
16
  };
12
17
 
13
18
  /**
14
- * Check this is object or not
15
- * @param {Object to be checked} obj
16
- * @returns
19
+ * Check if a value is a plain object (not null, not array).
20
+ * @param {*} obj - Value to check.
21
+ * @returns {boolean} True if value is a plain object, false otherwise.
17
22
  */
18
- const is_object = (obj) => {
19
- return typeof obj === 'object' && obj !== null && !Array.isArray(obj);
20
- }
23
+ const is_object = (obj) => typeof obj === 'object' && obj !== null && !Array.isArray(obj);
21
24
 
22
- module.exports = { copy_obj, is_object }
25
+ module.exports = { copy_obj, is_object };
package/core/random.js CHANGED
@@ -1,9 +1,12 @@
1
1
  /**
2
- *
3
- * @returns Random code generated based on Math
2
+ * @fileoverview Random generation utility functions.
3
+ * @module core/random
4
4
  */
5
- const random_code = function () {
6
- return Math.floor(Math.random() * 1000000);
7
- };
8
5
 
9
- module.exports = { random_code }
6
+ /**
7
+ * Generate random 6-digit code.
8
+ * @returns {number} Random number between 0 and 999999.
9
+ */
10
+ const random_code = () => Math.floor(Math.random() * 1000000);
11
+
12
+ module.exports = { random_code };
package/core/role.js CHANGED
@@ -1,116 +1,108 @@
1
+ /**
2
+ * @fileoverview Role-based access control utility functions.
3
+ * @module core/role
4
+ */
5
+
1
6
  const { get_settings } = require("../setting");
2
7
 
3
8
  /**
4
- * Validate the role defination in meta config
5
- * @param {*} role_name
6
- * @returns
9
+ * Find a role by name from settings.
10
+ * @param {string} role_name - Role name to find.
11
+ * @returns {Object|undefined} Role object or undefined if not found.
7
12
  */
8
- const validate_meta_role = (role_name) => {
13
+ const find_role = (role_name) => {
9
14
  const settings = get_settings();
10
- //there is role defined in meta but no roles config in settings
11
- if (!settings.roles) {
12
- return false;
13
- }
14
- return is_valid_role(role_name);
15
- }
15
+ return settings.roles ? settings.roles.find((role) => role.name === role_name) : undefined;
16
+ };
16
17
 
17
- const is_valid_role = (role_name) => {
18
+ /**
19
+ * Validate role name exists in settings configuration.
20
+ * @param {string} role_name - Role name to validate.
21
+ * @returns {boolean} True if role is valid and configured.
22
+ */
23
+ const validate_meta_role = (role_name) => {
18
24
  const settings = get_settings();
19
- const roles = settings.roles.filter(role => role.name == role_name);
20
- return roles.length == 1;
21
- }
25
+ if (!settings.roles) return false;
26
+ return find_role(role_name) !== undefined;
27
+ };
22
28
 
23
- const is_root_role = (role_name) => {
24
- const settings = get_settings();
25
- //no role defined, then there is no role limition, so treat each user as root
26
- if (!settings.roles) {
27
- return true;
28
- }
29
+ /**
30
+ * Check if role name exists in settings.
31
+ * @param {string} role_name - Role name to check.
32
+ * @returns {boolean} True if role exists.
33
+ */
34
+ const is_valid_role = (role_name) => find_role(role_name) !== undefined;
29
35
 
30
- if (is_valid_role(role_name)) {
31
- return settings.roles.filter(role => role.name == role_name)[0].root == true;
32
- } else {
33
- return false;
34
- }
35
- }
36
+ /**
37
+ * Check if role has root/admin privileges.
38
+ * @param {string} role_name - Role name to check.
39
+ * @returns {boolean} True if role is root.
40
+ */
41
+ const is_root_role = (role_name) => {
42
+ const role = find_role(role_name);
43
+ return role ? role.root === true : true;
44
+ };
36
45
 
37
46
  /**
38
- * get user's role from user session
39
- * @param {request} req
40
- * @returns
47
+ * Get user role from session.
48
+ * @param {Object} req - HTTP request object.
49
+ * @returns {string|null} User's role or null.
41
50
  */
42
51
  const get_session_user_role = (req) => {
43
52
  const user = req && req.session ? req.session.user : null;
44
53
  return user ? user.role : null;
45
- }
54
+ };
46
55
 
47
56
  /**
48
- * get user from user session
49
- * @param {*} req
50
- * @returns
57
+ * Get user object from session.
58
+ * @param {Object} req - HTTP request object.
59
+ * @returns {Object|null} User object or null.
51
60
  */
52
- const get_session_user = (req) => {
53
- return req && req.session ? req.session.user : null;
54
- }
61
+ const get_session_user = (req) => req && req.session ? req.session.user : null;
55
62
 
56
63
  /**
57
- *
58
- * @param {*} req
64
+ * Check if current user has root privileges.
65
+ * @param {Object} req - HTTP request object.
66
+ * @returns {boolean} True if user is root.
59
67
  */
60
- const is_root_user = (req) => {
61
- return is_root_role(get_session_user_role(req));
62
- }
68
+ const is_root_user = (req) => is_root_role(get_session_user_role(req));
63
69
 
64
70
  /**
65
- * Get the meta mode based on user's role
66
- * @param {request} req
67
- * @param {meta} meta
68
- * @returns
71
+ * Get user's role permissions for a meta entity.
72
+ * @param {Object} req - HTTP request object.
73
+ * @param {Object} meta - Meta entity definition.
74
+ * @returns {[string, string]} Array of [mode, view] permissions.
69
75
  */
70
76
  const get_user_role_right = (req, meta) => {
71
77
  const settings = get_settings();
72
- //no role defined in settings or no roles defined in meta, use meta mode
73
- if (!settings.roles || !meta.roles) {
74
- return [meta.mode, "*"];
75
- }
78
+ if (!settings.roles || !meta.roles) return [meta.mode, "*"];
76
79
 
77
80
  const user_role = get_session_user_role(req);
78
- if (!user_role) {
79
- return ["", ""];
80
- }
81
+ if (!user_role) return ["", ""];
82
+ if (!is_valid_role(user_role)) return ["", ""];
81
83
 
82
- if (is_valid_role(user_role)) {
83
- const roles = meta.roles;
84
- for (let i = 0; i < roles.length; i++) {
85
- const role = roles[i];
86
- const role_settings = role.split(":");
87
- const role_name = role_settings[0];
88
- const role_mode = role_settings[1];
89
- const role_view = role_settings.length == 3 ? role_settings[2] : "*";
90
- if (user_role == role_name) {
91
- // * stands to get the mode from meta definition
92
- if (role_mode == "*") {
93
- return [meta.mode, role_view];
94
- } else {
95
- return [role_mode, role_view];
96
- }
97
- }
84
+ for (const role of meta.roles) {
85
+ const role_settings = role.split(":");
86
+ const [role_name, role_mode] = role_settings;
87
+ const role_view = role_settings.length === 3 ? role_settings[2] : "*";
88
+ if (user_role === role_name) {
89
+ return role_mode === "*" ? [meta.mode, role_view] : [role_mode, role_view];
98
90
  }
99
91
  }
100
92
  return ["", ""];
101
- }
93
+ };
102
94
 
103
95
  /**
104
- * Check whether the user has the mode right on the meta or not
105
- * @param {http request} req
106
- * @param {meta defination} meta
107
- * @param {meta mode} mode
108
- * @param {view} view
109
- * @returns
96
+ * Check if user has required mode permission on meta.
97
+ * @param {Object} req - HTTP request object.
98
+ * @param {Object} meta - Meta entity definition.
99
+ * @param {string} mode - Required mode (c/r/u/d).
100
+ * @param {string} view - Required view.
101
+ * @returns {boolean} True if user has permission.
110
102
  */
111
103
  const check_user_role = (req, meta, mode, view) => {
112
104
  const [role_mode, role_view] = get_user_role_right(req, meta);
113
- return role_mode.includes(mode) && (role_view == "*" || role_view.includes(view));
114
- }
105
+ return role_mode.includes(mode) && (role_view === "*" || role_view.includes(view));
106
+ };
115
107
 
116
108
  module.exports = { is_root_role, is_root_user, validate_meta_role, check_user_role, get_user_role_right, get_session_user };
package/core/thread.js CHANGED
@@ -1,3 +1,13 @@
1
- const snooze = ms => new Promise(resolve => setTimeout(resolve, ms));
1
+ /**
2
+ * @fileoverview Threading and async utility functions.
3
+ * @module core/thread
4
+ */
2
5
 
3
- module.exports = { snooze }
6
+ /**
7
+ * Sleep for specified milliseconds.
8
+ * @param {number} ms - Milliseconds to sleep.
9
+ * @returns {Promise<void>} Promise that resolves after delay.
10
+ */
11
+ const snooze = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
12
+
13
+ module.exports = { snooze };