fiberx-backend-toolkit 0.1.8 → 0.1.10

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.
@@ -7,3 +7,4 @@ export * from "./module_type";
7
7
  export * from "./express_decelaration";
8
8
  export * from "./rbac_type";
9
9
  export * from "./mailer_type";
10
+ export * from "./validator_type";
@@ -23,3 +23,4 @@ __exportStar(require("./module_type"), exports);
23
23
  __exportStar(require("./express_decelaration"), exports);
24
24
  __exportStar(require("./rbac_type"), exports);
25
25
  __exportStar(require("./mailer_type"), exports);
26
+ __exportStar(require("./validator_type"), exports);
@@ -0,0 +1,15 @@
1
+ export type QueryValueType = "string" | "number" | "boolean" | "enum" | "date" | "date-range" | "array" | "object";
2
+ export interface QueryFieldSchemaInterface<T = any> {
3
+ key: string;
4
+ type: QueryValueType;
5
+ required?: boolean;
6
+ default?: T;
7
+ enum_values?: readonly any[];
8
+ min?: number;
9
+ max?: number;
10
+ }
11
+ export interface QueryValidationResultInterface<T> {
12
+ v_state: boolean;
13
+ v_msg: string;
14
+ v_data?: T;
15
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -51,5 +51,6 @@ declare class InputValidatorUtil {
51
51
  static dirExists(directory: string, create_directory?: boolean, return_dir_path?: boolean): boolean | string;
52
52
  static isSafeHashCompare(a: string, b: string): boolean;
53
53
  static isDeepEqual(a: any, b: any): boolean;
54
+ static hasInputChanged(new_input: Record<string, any>, existing_data: Record<string, any>, keys_to_check: string[]): boolean;
54
55
  }
55
56
  export default InputValidatorUtil;
@@ -174,5 +174,40 @@ class InputValidatorUtil {
174
174
  }
175
175
  return false;
176
176
  }
177
+ static hasInputChanged(new_input, existing_data, keys_to_check) {
178
+ // Normalize values (booleans, "true"/"false", "1"/"0", null-like)
179
+ const normalize = (val) => {
180
+ if (val === "true")
181
+ return true;
182
+ if (val === "false")
183
+ return false;
184
+ if (val === "1")
185
+ return true;
186
+ if (val === "0")
187
+ return false;
188
+ return val;
189
+ };
190
+ // Utility to get nested value by dot notation
191
+ const getNestedValue = (obj, path) => {
192
+ return path.split('.').reduce((acc, part) => {
193
+ if (acc && typeof acc === 'object' && part in acc) {
194
+ return acc[part];
195
+ }
196
+ return undefined;
197
+ }, obj);
198
+ };
199
+ for (const key of keys_to_check) {
200
+ if (!(key in new_input)) {
201
+ continue;
202
+ }
203
+ const new_val = normalize(getNestedValue(new_input, key));
204
+ const old_val = normalize(getNestedValue(existing_data, key));
205
+ if (!InputValidatorUtil.isDeepEqual(new_val, old_val)) {
206
+ console.log("🔺 CHANGED:", key, { new_val, old_val });
207
+ return true;
208
+ }
209
+ }
210
+ return false;
211
+ }
177
212
  }
178
213
  exports.default = InputValidatorUtil;
@@ -0,0 +1,2 @@
1
+ import QueryValidatorUtil from "./query_validator_util";
2
+ export { QueryValidatorUtil };
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.QueryValidatorUtil = void 0;
7
+ const query_validator_util_1 = __importDefault(require("./query_validator_util"));
8
+ exports.QueryValidatorUtil = query_validator_util_1.default;
@@ -0,0 +1,6 @@
1
+ import { QueryFieldSchemaInterface, QueryValidationResultInterface } from "../types/validator_type";
2
+ declare class QueryValidatorUtil {
3
+ private constructor();
4
+ static validate<T extends Record<string, any>>(query: Record<string, any>, schema: QueryFieldSchemaInterface[]): QueryValidationResultInterface<T>;
5
+ }
6
+ export default QueryValidatorUtil;
@@ -0,0 +1,110 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const dayjs_1 = __importDefault(require("dayjs"));
7
+ class QueryValidatorUtil {
8
+ constructor() { }
9
+ static validate(query, schema) {
10
+ const result = {};
11
+ for (const field of schema) {
12
+ const raw_value = query[field.key];
13
+ // Handle missing value
14
+ if (raw_value === undefined || raw_value === null || raw_value === "") {
15
+ if (field.required) {
16
+ return {
17
+ v_state: false,
18
+ v_msg: `missing_required_query_${field.key}`
19
+ };
20
+ }
21
+ result[field.key] = field.default ?? null;
22
+ continue;
23
+ }
24
+ switch (field.type) {
25
+ case "string": {
26
+ result[field.key] = String(raw_value);
27
+ break;
28
+ }
29
+ case "number": {
30
+ const num = Number(raw_value);
31
+ if (isNaN(num)) {
32
+ return { v_state: false, v_msg: `invalid_query_${field.key}` };
33
+ }
34
+ if (field.min !== undefined && num < field.min) {
35
+ return { v_state: false, v_msg: `invalid_query_${field.key}` };
36
+ }
37
+ if (field.max !== undefined && num > field.max) {
38
+ return { v_state: false, v_msg: `invalid_query_${field.key}` };
39
+ }
40
+ result[field.key] = num;
41
+ break;
42
+ }
43
+ case "boolean": {
44
+ const val = String(raw_value).toLowerCase();
45
+ if (!["true", "false"].includes(val)) {
46
+ return { v_state: false, v_msg: `invalid_query_${field.key}` };
47
+ }
48
+ result[field.key] = val === "true";
49
+ break;
50
+ }
51
+ case "enum": {
52
+ if (!field.enum_values?.includes(raw_value)) {
53
+ return { v_state: false, v_msg: `invalid_query_${field.key}` };
54
+ }
55
+ result[field.key] = raw_value;
56
+ break;
57
+ }
58
+ case "date": {
59
+ const parsed = (0, dayjs_1.default)(raw_value);
60
+ if (!parsed.isValid()) {
61
+ return { v_state: false, v_msg: `invalid_query_${field.key}` };
62
+ }
63
+ // Return as native Date (recommended for DB usage)
64
+ result[field.key] = parsed.toDate();
65
+ break;
66
+ }
67
+ case "date-range": {
68
+ const parts = String(raw_value).split(",");
69
+ if (parts.length !== 2) {
70
+ return { v_state: false, v_msg: `invalid_query_${field.key}` };
71
+ }
72
+ const start = (0, dayjs_1.default)(parts[0]);
73
+ const end = (0, dayjs_1.default)(parts[1]);
74
+ if (!start.isValid() || !end.isValid()) {
75
+ return { v_state: false, v_msg: `invalid_query_${field.key}` };
76
+ }
77
+ // ✅ Ensure end is after start
78
+ if (!end.isAfter(start)) {
79
+ return { v_state: false, v_msg: `invalid_query_${field.key}_range` };
80
+ }
81
+ result[field.key] = {
82
+ start_date: start.toDate(),
83
+ end_date: end.toDate()
84
+ };
85
+ break;
86
+ }
87
+ case "array": {
88
+ if (!Array.isArray(raw_value)) {
89
+ return { v_state: false, v_msg: `invalid_query_${field.key}` };
90
+ }
91
+ result[field.key] = raw_value;
92
+ break;
93
+ }
94
+ case "object": {
95
+ if (typeof raw_value !== "object") {
96
+ return { v_state: false, v_msg: `invalid_query_${field.key}` };
97
+ }
98
+ result[field.key] = raw_value;
99
+ break;
100
+ }
101
+ }
102
+ }
103
+ return {
104
+ v_state: true,
105
+ v_msg: "valid_query_params",
106
+ v_data: result
107
+ };
108
+ }
109
+ }
110
+ exports.default = QueryValidatorUtil;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fiberx-backend-toolkit",
3
- "version": "0.1.8",
3
+ "version": "0.1.10",
4
4
  "description": "A TypeScript backend toolkit providing shared domain logic, infrastructure helpers, and utilities for FiberX server-side applications and services.",
5
5
  "type": "commonjs",
6
6
  "main": "./dist/index.js",