hola-server 1.0.10 → 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
@@ -0,0 +1,22 @@
1
+ # Thread Utilities Skill
2
+
3
+ ## Overview
4
+
5
+ The `hola-server/core/thread.js` module provides utilities for controlling execution flow (threading/timing).
6
+
7
+ ## Importing
8
+
9
+ ```javascript
10
+ const { snooze } = require("hola-server/core/thread");
11
+ ```
12
+
13
+ ## API Reference
14
+
15
+ ### `snooze(ms)`
16
+ Pauses execution for a specified duration (Non-blocking sleep).
17
+ - **param**: `ms` (number) - Milliseconds to wait.
18
+ - **returns**: `Promise<void>`
19
+
20
+ ```javascript
21
+ await snooze(1000); // Waits for 1 second
22
+ ```
package/skills/type.md ADDED
@@ -0,0 +1,547 @@
1
+ # Type System in Hola Meta-Programming Framework
2
+
3
+ ## Overview
4
+
5
+ The Hola framework uses a robust type system to ensure data validation, conversion, and consistency across the entire stack. This document explains how to use built-in types and how to define customized types for your entities.
6
+
7
+ ## Core Principles
8
+
9
+ ### 1. Field Attributes Restrictions
10
+
11
+ When defining entity fields, **only the following attributes are allowed**:
12
+
13
+ **Standard Field Attributes (from `FIELD_ATTRS`):**
14
+ - `name` - Field name (required)
15
+ - `type` - Data type (default: "string")
16
+ - `required` - Whether field is required
17
+ - `default` - Default value for the field (validated against field type)
18
+ - `ref` - Reference to another entity collection
19
+ - `link` - Link to another field (auto-populated from ref)
20
+ - `delete` - Deletion behavior for ref fields ("keep" or "cascade")
21
+ - `create` - Show in create form
22
+ - `list` - Show in table list
23
+ - `search` - Show in search form
24
+ - `update` - Allow update
25
+ - `clone` - Include in clone
26
+ - `sys` - System field (server-side only)
27
+ - `secure` - Hidden from client entirely
28
+ - `group` - User group sharing control
29
+ - `view` - Form view identifier
30
+
31
+ **Link Field Attributes (from `LINK_FIELD_ATTRS`):**
32
+ - `name` - Field name
33
+ - `link` - Field to link to
34
+ - `list` - Show in list
35
+
36
+ > **IMPORTANT**: Any attributes outside these lists will cause validation errors. For example, `enum_values`, `max_length`, `accept` are **NOT** valid field attributes in the meta definition.
37
+
38
+ ### 2. Type Attribute Rules
39
+
40
+ The `type` attribute should **only** use types defined in the core type system:
41
+
42
+ **Server-Side Types** (from `hola-server/core/type.js`):
43
+ - Basic: `obj`, `string`, `lstr`, `text`, `password`, `file`, `date`, `enum`, `log_category`
44
+ - Boolean: `boolean`
45
+ - Numeric: `number`, `int`, `uint`, `float`, `ufloat`, `decimal`, `percentage`, `currency`
46
+ - Date/Time: `datetime`, `time`
47
+ - Validation: `email`, `url`, `phone`, `uuid`, `color`, `ip_address`
48
+ - Data Structures: `array`, `json`
49
+ - Transformations: `slug`
50
+ - Domain-Specific: `age`, `gender`, `log_level`
51
+
52
+ Any type name not in this list is considered a **customized type** and must be registered before use.
53
+
54
+ ### 3. Default Values
55
+
56
+ The `default` attribute allows you to specify default values that will be automatically populated in create forms when the field is empty:
57
+
58
+ ```javascript
59
+ fields: [
60
+ { name: "quantity", type: "int", default: 0 },
61
+ { name: "active", type: "boolean", default: true },
62
+ { name: "price", type: "float", default: 9.99 },
63
+ { name: "category", type: "product_category", default: 0 } // First enum value
64
+ ]
65
+ ```
66
+
67
+ **Validation Rules:**
68
+ - The default value **must be valid** for the field's type
69
+ - Validation happens during meta definition loading using `type.convert()`
70
+ - If the default value doesn't pass type validation, an error is thrown
71
+
72
+ **Client-Side Behavior:**
73
+ - Default values are applied automatically in `BasicForm.vue` during create operations
74
+ - Defaults are only applied when the field value is `undefined`, `null`, or empty string
75
+ - User can still override default values by entering different values
76
+
77
+ ### 4. Enumeration Pattern (g18n-Compatible)
78
+
79
+ The Hola framework follows the **g18n** (global internationalization) pattern for enumerations:
80
+
81
+ - **Store integer values in database** (e.g., 0, 1, 2)
82
+ - **Display translated labels on client side** (e.g., "Male", "Female")
83
+ - **Define enums as customized int enum types**, NOT as string enums
84
+
85
+ **❌ INCORRECT - String Enum:**
86
+ ```javascript
87
+ fields: [
88
+ {
89
+ name: "category",
90
+ type: "enum",
91
+ enum_values: ["Electronics", "Clothing", "Food"] // ❌ NO enum_values attribute
92
+ }
93
+ ]
94
+ ```
95
+
96
+ **✅ CORRECT - Int Enum Type:**
97
+ ```javascript
98
+ // 1. Register customized type using built-in helper
99
+ const { register_type, int_enum_type } = require("hola-server/core/type");
100
+
101
+ register_type(int_enum_type("product_category", [0, 1, 2]));
102
+ // 0=Electronics, 1=Clothing, 2=Food
103
+
104
+ // 2. Use in field definition
105
+ fields: [
106
+ {
107
+ name: "category",
108
+ type: "product_category" // ✅ Custom type
109
+ }
110
+ ]
111
+ ```
112
+
113
+ **Client-Side Labels** (in `hola-web/src/core/type.js`):
114
+ ```javascript
115
+ register_type({
116
+ name: "product_category",
117
+ input_type: "autocomplete",
118
+ items: (vue) => [
119
+ { value: 0, text: vue.$t("product_category.electronics") },
120
+ { value: 1, text: vue.$t("product_category.clothing") },
121
+ { value: 2, text: vue.$t("product_category.food") }
122
+ ],
123
+ format: (value, vue) => {
124
+ const labels = ["electronics", "clothing", "food"];
125
+ return labels[value] ? vue.$t(`product_category.${labels[value]}`) : "";
126
+ }
127
+ });
128
+ ```
129
+
130
+ **Localized Labels** (in `hola-web/src/locales/en.json`):
131
+ ```json
132
+ {
133
+ "product_category": {
134
+ "electronics": "Electronics",
135
+ "clothing": "Clothing",
136
+ "food": "Food"
137
+ }
138
+ }
139
+ ```
140
+
141
+ ## Built-in Types Reference
142
+
143
+ ### Basic Types
144
+
145
+ | Type | Server Conversion | Client Input | Use Case |
146
+ |------|------------------|--------------|----------|
147
+ | `string` | Trim whitespace, default "" | `v-text-field` | Short text (≤255 chars) |
148
+ | `lstr` | Passthrough string | `v-textarea` | Long string |
149
+ | `text` | Passthrough string | Rich editor | Long formatted text |
150
+ | `password` | Encrypt with hash | Password input | Secure credentials |
151
+ | `file` | Passthrough | File upload | File attachments |
152
+ | `date` | Passthrough string | Date picker | Date only |
153
+ | `enum` | Passthrough string | Autocomplete | String options |
154
+
155
+ ### Numeric Types
156
+
157
+ | Type | Server Conversion | Validation | Client Input |
158
+ |------|------------------|------------|--------------|
159
+ | `number` | Parse to number | Any number | Number input |
160
+ | `int` | Parse to integer | Integer only | Number input |
161
+ | `uint` | Parse to unsigned int | Integer ≥ 0 | Number input |
162
+ | `float` | Parse to 2 decimals | Float with 2 decimals | Number input |
163
+ | `ufloat` | Parse to unsigned 2 decimals | Float ≥ 0 with 2 decimals | Number input |
164
+ | `decimal` | Parse to decimal | Any float | Number input |
165
+ | `percentage` | Parse to 2 decimals | Float | Number input with % |
166
+ | `currency` | Parse to number | Number | Number input with $ |
167
+
168
+ ### Validation Types
169
+
170
+ | Type | Pattern/Rule | Example |
171
+ |------|-------------|---------|
172
+ | `email` | Email regex pattern | `user@example.com` |
173
+ | `url` | Valid URL structure | `https://example.com` |
174
+ | `phone` | International format | `+1234567890` |
175
+ | `uuid` | UUID v1-v5 | `550e8400-e29b-41d4-a716-446655440000` |
176
+ | `color` | Hex color | `#FF5733` or `#F57` |
177
+ | `ip_address` | IPv4 format | `192.168.1.1` |
178
+
179
+ ### Domain-Specific Types
180
+
181
+ | Type | Valid Values | Description |
182
+ |------|-------------|-------------|
183
+ | `age` | 0-200 (int) | Person age |
184
+ | `gender` | 0=Male, 1=Female | Gender enum |
185
+ | `log_level` | 0=Debug, 1=Info, 2=Warn, 3=Error | Log severity |
186
+
187
+ ## Creating Customized Types
188
+
189
+ ### Step 1: Server-Side Type Registration
190
+
191
+ Create a type definition in your entity file or a shared types file:
192
+
193
+ ```javascript
194
+ const { register_type, ok, err, is_int, int_enum_type, int_range_type, regex_type } = require("hola-server/core/type");
195
+
196
+ // Example 1: Int Enum Type
197
+ // Use the built-in helper function
198
+ register_type(int_enum_type("order_status", [0, 1, 2, 3]));
199
+ // 0=Pending, 1=Processing, 2=Shipped, 3=Delivered
200
+
201
+ // Example 2: Int Range Type
202
+ // Use the built-in helper function
203
+ register_type(int_range_type("priority", 1, 5));
204
+
205
+ // Example 3: Min/Max Value Type (like age type)
206
+ // Server-side: Use int_range_type helper for integer ranges
207
+ register_type(int_range_type("employee_age", 18, 65));
208
+ // This creates validation: 18 <= value <= 65
209
+
210
+ // Example 4: Regex Validation Type
211
+ // Use the built-in helper function
212
+ register_type(regex_type("sku_code", /^[A-Z]{3}-\d{6}$/));
213
+
214
+ // Example 5: Custom Business Logic Type
215
+ // Use ok() and err() helpers for return values
216
+ register_type({
217
+ name: "discount_rate",
218
+ convert: (value) => {
219
+ const num = parseFloat(value);
220
+ if (isNaN(num)) return err("discount_rate", value);
221
+ if (num < 0 || num > 100) return err("discount_rate", value);
222
+ return ok(parseFloat(num.toFixed(2)));
223
+ }
224
+ });
225
+
226
+ // Example 6: Custom Int Enum with Business Logic
227
+ // Use is_int() helper for validation
228
+ register_type({
229
+ name: "approval_status",
230
+ convert: (value) => {
231
+ if (!is_int(value)) return err("approval_status", value);
232
+ const int_value = parseInt(value);
233
+ const valid = [0, 1, 2]; // 0=Pending, 1=Approved, 2=Rejected
234
+ return valid.includes(int_value) ? ok(int_value) : err("approval_status", value);
235
+ }
236
+ });
237
+ ```
238
+
239
+ **Available Helper Functions:**
240
+
241
+ | Helper | Purpose | Example |
242
+ |--------|---------|---------|
243
+ | `ok(value)` | Return success result | `ok(42)` → `{value: 42}` |
244
+ | `err(type, value)` | Return error result | `err("int", "abc")` → `{err: "invalid int:abc"}` |
245
+ | `is_int(value)` | Check if value is integer | `is_int(42)` → `true` |
246
+ | `int_enum_type(name, values)` | Create int enum type | `int_enum_type("status", [0,1,2])` |
247
+ | `int_range_type(name, min, max)` | Create int range type | `int_range_type("age", 0, 200)` |
248
+ | `regex_type(name, pattern)` | Create regex validation type | `regex_type("email", /.../)` |
249
+ | `string_type(name)` | Create passthrough string type | `string_type("code")` |
250
+
251
+
252
+ ### Step 2: Client-Side Type Registration
253
+
254
+ Register the corresponding client-side type in your Vue app:
255
+
256
+ ```javascript
257
+ // hola-web/src/core/type.js or custom types file
258
+ import { register_type } from "@/core/type";
259
+
260
+ // Example 1: Int Enum with i18n
261
+ register_type({
262
+ name: "order_status",
263
+ input_type: "autocomplete",
264
+ items: (vue) => [
265
+ { value: 0, text: vue.$t("order_status.pending") },
266
+ { value: 1, text: vue.$t("order_status.processing") },
267
+ { value: 2, text: vue.$t("order_status.shipped") },
268
+ { value: 3, text: vue.$t("order_status.delivered") }
269
+ ],
270
+ format: (value, vue) => {
271
+ const statuses = ["pending", "processing", "shipped", "delivered"];
272
+ return statuses[value] ? vue.$t(`order_status.${statuses[value]}`) : "";
273
+ }
274
+ });
275
+
276
+ // Example 2: Range Input
277
+ register_type({
278
+ name: "priority",
279
+ input_type: "slider",
280
+ min: 1,
281
+ max: 5,
282
+ step: 1,
283
+ rule: (vue, field_name) => {
284
+ const err = vue.$t("type.priority", { field: field_name });
285
+ return (value) => {
286
+ const num = parseInt(value);
287
+ return (num >= 1 && num <= 5) || err;
288
+ };
289
+ },
290
+ format: (value) => `Priority ${value}`
291
+ });
292
+
293
+ // Example 3: Min/Max Value with Suffix (like age type)
294
+ // Client-side: Number input with validation and localized suffix
295
+ register_type({
296
+ name: "employee_age",
297
+ input_type: "number",
298
+ search_input_type: "text",
299
+ suffix: (vue) => vue.$t("type.age_unit"), // e.g., "years old"
300
+ rule: (vue, field_name) => {
301
+ const err = vue.$t("type.employee_age", { field: field_name });
302
+ return (value) => {
303
+ if (!value) return true;
304
+ const num = parseInt(value);
305
+ return (num >= 18 && num <= 65) || err;
306
+ };
307
+ },
308
+ format: (value, vue) => value ? `${value} ${vue.$t("type.age_unit")}` : ""
309
+ });
310
+
311
+ // Example 4: Custom Validation
312
+ register_type({
313
+ name: "sku_code",
314
+ input_type: "text",
315
+ rule: (vue, field_name) => {
316
+ const err = vue.$t("type.sku_code", { field: field_name });
317
+ const pattern = /^[A-Z]{3}-\d{6}$/;
318
+ return (value) => !value || pattern.test(value) || err;
319
+ }
320
+ });
321
+
322
+ // Example 4: Formatted Number
323
+ register_type({
324
+ name: "discount_rate",
325
+ input_type: "number",
326
+ suffix: "%",
327
+ rule: (vue, field_name) => {
328
+ const err = vue.$t("type.discount_rate", { field: field_name });
329
+ return (value) => {
330
+ if (!value) return true;
331
+ const num = parseFloat(value);
332
+ return (!isNaN(num) && num >= 0 && num <= 100) || err;
333
+ };
334
+ },
335
+ format: (value) => value ? `${value.toFixed(2)}%` : ""
336
+ });
337
+ ```
338
+
339
+ ### Step 3: Add i18n Translations
340
+
341
+ Add translations for your custom types:
342
+
343
+ ```json
344
+ // hola-web/src/locales/en.json
345
+ {
346
+ "order_status": {
347
+ "pending": "Pending",
348
+ "processing": "Processing",
349
+ "shipped": "Shipped",
350
+ "delivered": "Delivered"
351
+ },
352
+ "type": {
353
+ "priority": "Priority must be between 1 and 5",
354
+ "employee_age": "Age must be between 18 and 65",
355
+ "age_unit": "years old",
356
+ "sku_code": "SKU code must be in format XXX-123456",
357
+ "discount_rate": "Discount rate must be between 0 and 100"
358
+ }
359
+ }
360
+ ```
361
+
362
+ ### Step 4: Use in Entity Definition
363
+
364
+ ```javascript
365
+ const { init_router } = require("hola-server");
366
+
367
+ module.exports = init_router({
368
+ collection: "product",
369
+ creatable: true,
370
+ readable: true,
371
+ updatable: true,
372
+ deleteable: true,
373
+
374
+ primary_keys: ["sku"],
375
+ ref_label: "name",
376
+
377
+ fields: [
378
+ { name: "sku", type: "sku_code", required: true },
379
+ { name: "name", type: "string", required: true },
380
+ { name: "status", type: "order_status", required: true },
381
+ { name: "priority", type: "priority" },
382
+ { name: "age", type: "employee_age" },
383
+ { name: "discount", type: "discount_rate" }
384
+ ]
385
+ });
386
+ ```
387
+
388
+ ## Complete Example: Product Category Type
389
+
390
+ Here's a complete example showing all steps:
391
+
392
+ **1. Server Type (`hola-server/router/product.js`):**
393
+ ```javascript
394
+ const { init_router } = require("hola-server");
395
+ const { register_type, int_enum_type } = require("hola-server/core/type");
396
+
397
+ // Define custom type using built-in helper
398
+ register_type(int_enum_type("product_category", [0, 1, 2]));
399
+ // 0=Electronics, 1=Clothing, 2=Food
400
+
401
+ // Use in entity
402
+ module.exports = init_router({
403
+ collection: "product",
404
+ creatable: true,
405
+ readable: true,
406
+ updatable: true,
407
+ deleteable: true,
408
+
409
+ primary_keys: ["name"],
410
+ ref_label: "name",
411
+
412
+ fields: [
413
+ { name: "name", type: "string", required: true },
414
+ { name: "price", type: "decimal", required: true },
415
+ { name: "category", type: "product_category", required: true }
416
+ ]
417
+ });
418
+ ```
419
+
420
+ **2. Client Type (`hola-web/src/types/product.js`):**
421
+ ```javascript
422
+ import { register_type } from "@/core/type";
423
+
424
+ register_type({
425
+ name: "product_category",
426
+ input_type: "autocomplete",
427
+ items: (vue) => [
428
+ { value: 0, text: vue.$t("product_category.electronics") },
429
+ { value: 1, text: vue.$t("product_category.clothing") },
430
+ { value: 2, text: vue.$t("product_category.food") }
431
+ ],
432
+ format: (value, vue) => {
433
+ const categories = ["electronics", "clothing", "food"];
434
+ return categories[value] ? vue.$t(`product_category.${categories[value]}`) : "";
435
+ }
436
+ });
437
+ ```
438
+
439
+ **3. Translations (`hola-web/src/locales/en.json`):**
440
+ ```json
441
+ {
442
+ "product_category": {
443
+ "electronics": "Electronics",
444
+ "clothing": "Clothing",
445
+ "food": "Food"
446
+ }
447
+ }
448
+ ```
449
+
450
+ ## Best Practices
451
+
452
+ ### 1. Use Int Enums for All Enumerations
453
+ - Store integers in the database for efficiency and language-independence
454
+ - Use i18n labels on the client side for display
455
+ - Never use string enums with hardcoded values
456
+
457
+ ### 2. Keep Types DRY
458
+ - Use the built-in helper functions exported from `hola-server/core/type` (`int_enum_type`, `int_range_type`, `regex_type`, etc.)
459
+ - Share type definitions across multiple entities if appropriate
460
+ - Only define custom helpers when you need specialized business logic not covered by built-ins
461
+
462
+ ### 3. Validation Consistency
463
+ - Ensure server-side and client-side validation rules match
464
+ - Server validation is authoritative; client validation improves UX
465
+
466
+ ### 4. Type Naming Conventions
467
+ - Use descriptive names: `order_status`, `product_category`, `priority_level`
468
+ - Avoid generic names: `status`, `type`, `category` (too vague)
469
+ - Use snake_case for consistency with other Hola conventions
470
+
471
+ ### 5. Error Messages
472
+ - Provide clear, actionable error messages
473
+ - Use i18n for all user-facing messages
474
+ - Include field context in validation errors
475
+
476
+ ### 6. Register Before Use
477
+ - Always register custom types before defining entities that use them
478
+ - Register in entity file or in a shared types initialization module
479
+ - Verify type exists using `get_type(name)` if needed
480
+
481
+ ## Common Mistakes to Avoid
482
+
483
+ ### ❌ Don't: Add Custom Attributes to Fields
484
+ ```javascript
485
+ // ❌ WRONG
486
+ fields: [
487
+ {
488
+ name: "category",
489
+ type: "enum",
490
+ enum_values: ["A", "B"], // ❌ Not a valid field attribute
491
+ max_length: 100 // ❌ Not a valid field attribute
492
+ }
493
+ ]
494
+ ```
495
+
496
+ ### ✅ Do: Use Customized Types
497
+ ```javascript
498
+ // ✅ CORRECT
499
+ register_type({
500
+ name: "my_category",
501
+ convert: (value) => {
502
+ const valid = [0, 1];
503
+ const int_val = parseInt(value);
504
+ return valid.includes(int_val) ? { value: int_val } : { err: "invalid" };
505
+ }
506
+ });
507
+
508
+ fields: [
509
+ { name: "category", type: "my_category", required: true, default: 0 } // ✅ Valid with default
510
+ ]
511
+ ```
512
+
513
+ ### ❌ Don't: Use String Enums
514
+ ```javascript
515
+ // ❌ WRONG - String values
516
+ register_type({
517
+ name: "status",
518
+ convert: (value) => {
519
+ const valid = ["active", "inactive"];
520
+ return valid.includes(value) ? { value } : { err: "invalid" };
521
+ }
522
+ });
523
+ ```
524
+
525
+ ### ✅ Do: Use Int Enums
526
+ ```javascript
527
+ // ✅ CORRECT - Int values
528
+ register_type({
529
+ name: "status",
530
+ convert: (value) => {
531
+ const int_val = parseInt(value);
532
+ const valid = [0, 1]; // 0=inactive, 1=active
533
+ return valid.includes(int_val) ? { value: int_val } : { err: "invalid" };
534
+ }
535
+ });
536
+ ```
537
+
538
+ ## Summary
539
+
540
+ The Hola type system provides:
541
+ - **Strict field attribute validation** - only predefined attributes allowed
542
+ - **Comprehensive built-in types** - covering common data validation needs
543
+ - **Customized type support** - extend with your own business logic
544
+ - **g18n-compatible enums** - int values in DB, i18n labels in UI
545
+ - **Server-client consistency** - matching validation on both sides
546
+
547
+ Follow these guidelines to create robust, maintainable, and internationalization-ready entities in your Hola applications.
package/skills/url.md ADDED
@@ -0,0 +1,34 @@
1
+ # URL & HTTP Utilities Skill
2
+
3
+ ## Overview
4
+
5
+ The `hola-server/core/url.js` module provides a factory for creating pre-configured Axios request functions using system settings (like proxy configuration).
6
+
7
+ ## Importing
8
+
9
+ ```javascript
10
+ const { url } = require("hola-server/core/url");
11
+ ```
12
+
13
+ ## API Reference
14
+
15
+ ### `url(target_url, method)`
16
+ Creates a function that performs an HTTP request to the specified target.
17
+ - **param**: `target_url` (string) - The URL endpoint.
18
+ - **param**: `method` (string) - HTTP method ('GET', 'POST', etc.).
19
+ - **returns**: `Function` - `(config) => Promise`
20
+
21
+ #### Usage Example
22
+
23
+ ```javascript
24
+ // 1. Create a reusable request function
25
+ const getGithubProfile = url("https://api.github.com/users/octocat", "GET");
26
+
27
+ // 2. Execute it (optionally passing axios config overrides/headers)
28
+ try {
29
+ const response = await getGithubProfile({ headers: { 'User-Agent': 'node' } });
30
+ console.log(response.data);
31
+ } catch (err) {
32
+ console.error(err);
33
+ }
34
+ ```
@@ -0,0 +1,48 @@
1
+ # Validation Utilities Skill
2
+
3
+ ## Overview
4
+
5
+ The `hola-server/core/validate.js` module provides basic validation helpers to check for undefined, empty, or missing values.
6
+
7
+ ## Importing
8
+
9
+ ```javascript
10
+ const {
11
+ is_undefined, has_value, validate_required_fields
12
+ } = require("hola-server/core/validate");
13
+ ```
14
+
15
+ ## API Reference
16
+
17
+ ### `is_undefined(value)`
18
+ Checks if a value is strictly `undefined`.
19
+ - **param**: `value` (*)
20
+ - **returns**: `boolean`
21
+
22
+ ### `has_value(value)`
23
+ Checks if a value is "meaningful". Returns `false` for:
24
+ - `null`
25
+ - `undefined`
26
+ - `NaN`
27
+ - Empty strings `""` or whitespace-only strings ` " " `
28
+ - **returns**: `boolean`
29
+
30
+ ```javascript
31
+ has_value(0); // true
32
+ has_value(false); // true
33
+ has_value(""); // false
34
+ has_value(" "); // false
35
+ has_value(null); // false
36
+ ```
37
+
38
+ ### `validate_required_fields(obj, field_names)`
39
+ Checks an object for missing required properties (using `has_value`).
40
+ - **param**: `obj` (Object) - Object to validate.
41
+ - **param**: `field_names` (string[]) - List of keys that must be present.
42
+ - **returns**: `string[]` - Array of field names that failed validation.
43
+
44
+ ```javascript
45
+ const data = { name: "Alice", age: null };
46
+ const missing = validate_required_fields(data, ["name", "age", "email"]);
47
+ // missing is ["age", "email"]
48
+ ```
@@ -0,0 +1,5 @@
1
+ const { close_db } = require('../../db/db');
2
+
3
+ after(async () => {
4
+ await close_db();
5
+ });