@mlagie/sql-connector 2.0.0 → 2.0.2
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/eslint.config.mjs +26 -1
- package/package.json +2 -2
- package/releases/2.0.1.md +23 -0
- package/releases/2.0.2.md +20 -0
- package/src/models/Model.js +22 -54
- package/src/models/ModelInstance.js +26 -35
- package/src/utils/formatObject.js +1 -1
package/eslint.config.mjs
CHANGED
|
@@ -1,12 +1,37 @@
|
|
|
1
|
+
import js from "@eslint/js";
|
|
1
2
|
import security from "eslint-plugin-security";
|
|
2
3
|
|
|
3
4
|
export default [
|
|
4
5
|
{
|
|
6
|
+
ignores: ["eslint.config.mjs"]
|
|
7
|
+
},
|
|
8
|
+
js.configs.recommended,
|
|
9
|
+
{
|
|
10
|
+
files: ["**/*.js"],
|
|
11
|
+
languageOptions: {
|
|
12
|
+
sourceType: "commonjs",
|
|
13
|
+
globals: {
|
|
14
|
+
console: "readonly",
|
|
15
|
+
exports: "readonly",
|
|
16
|
+
module: "readonly",
|
|
17
|
+
process: "readonly",
|
|
18
|
+
require: "readonly",
|
|
19
|
+
__dirname: "readonly",
|
|
20
|
+
__filename: "readonly"
|
|
21
|
+
}
|
|
22
|
+
},
|
|
5
23
|
plugins: {
|
|
6
24
|
security
|
|
7
25
|
},
|
|
8
26
|
rules: {
|
|
9
|
-
...security.configs.recommended.rules
|
|
27
|
+
...security.configs.recommended.rules,
|
|
28
|
+
"no-unused-vars": [
|
|
29
|
+
"error",
|
|
30
|
+
{
|
|
31
|
+
argsIgnorePattern: "^(?:_|resolve|reject)$",
|
|
32
|
+
varsIgnorePattern: "^_$"
|
|
33
|
+
}
|
|
34
|
+
]
|
|
10
35
|
}
|
|
11
36
|
}
|
|
12
37
|
];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mlagie/sql-connector",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.2",
|
|
4
4
|
"description": "Le module sql-connector permet de gérer les connexions à une base de données MySQL, de définir des schémas de tables, et d'interagir avec les données de manière simple et efficace.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -36,10 +36,10 @@
|
|
|
36
36
|
"private": false,
|
|
37
37
|
"dependencies": {
|
|
38
38
|
"@mlagie/logger": "1.0.2",
|
|
39
|
-
"glob": "^13.0.6",
|
|
40
39
|
"mysql2": "3.22.5"
|
|
41
40
|
},
|
|
42
41
|
"devDependencies": {
|
|
42
|
+
"@eslint/js": "^10.0.1",
|
|
43
43
|
"eslint": "^10.6.0",
|
|
44
44
|
"eslint-plugin-security": "^4.0.1"
|
|
45
45
|
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Release v2.0.1 — Hotfix: Application Launch Crash (Variable Reference Error)
|
|
2
|
+
|
|
3
|
+
This patch release resolves a critical regression introduced in `v2.0.0` that completely blocked applications from starting when using `@mlagie/sql-connector`.
|
|
4
|
+
|
|
5
|
+
## Problem
|
|
6
|
+
|
|
7
|
+
Upon initializing the package or starting the parent application, a runtime crash occurred due to an undefined or incorrectly scoped variable. This prevented the ORM from establishing connections and loading models properly, causing the host application to crash immediately on boot.
|
|
8
|
+
|
|
9
|
+
## What Was Fixed
|
|
10
|
+
|
|
11
|
+
* **Variable Scope Resolution:** Fixed the broken variable reference causing the startup crash during initialization.
|
|
12
|
+
* **Boot Reliability:** Restored seamless application launches when importing and configuring the SQL connector.
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## 📦 Component Impact
|
|
17
|
+
|
|
18
|
+
| Impacted Area | Description | Status |
|
|
19
|
+
| :--- | :--- | :--- |
|
|
20
|
+
| **Core Initialization** | Uncaught reference/variable error blocking `npm start` | **Fixed** |
|
|
21
|
+
| **Stability** | Application crash on package import | **Resolved** |
|
|
22
|
+
|
|
23
|
+
*If you encountered initialization errors with v2.0.0, please upgrade your dependency to v2.0.1 immediately.* `npm update @mlagie/sql-connector`
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# Release v2.0.2 — Performance Optimization & Code Cleanup
|
|
2
|
+
|
|
3
|
+
This patch release focuses on optimizing the internal runtime footprint and improving overall execution performance by removing legacy, unused code paths and dependencies.
|
|
4
|
+
|
|
5
|
+
## What Was Improved
|
|
6
|
+
|
|
7
|
+
* **Dead Code Elimination:** Removed unused functions, legacy variables, and obsolete logic that were lingering from previous architecture overhauls (such as the old `serveur` logger reference).
|
|
8
|
+
* **Performance Boost:** Cleaning up these elements reduces CPU overhead and memory usage during runtime model instantiation and schema synchronization.
|
|
9
|
+
* **Code Maintenance:** Streamlined internal loops and conditional structures to ensure the ORM remains fast, lightweight, and maintainable.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## Component Impact
|
|
14
|
+
|
|
15
|
+
| Impacted Area | Description | Status |
|
|
16
|
+
| :--- | :--- | :--- |
|
|
17
|
+
| **ModelInstance & Models** | Removal of unused references and obsolete properties | **Optimized** |
|
|
18
|
+
| **Runtime Footprint** | Decreased execution overhead during database mappings | **Improved** |
|
|
19
|
+
|
|
20
|
+
*This is a fully backward-compatible patch. Upgrading is highly recommended to benefit from the performance improvements.* `npm update @mlagie/sql-connector`
|
package/src/models/Model.js
CHANGED
|
@@ -54,35 +54,6 @@ function formatDefaultSql(defaultValue, fieldType) {
|
|
|
54
54
|
return `DEFAULT "${String(defaultValue).replace(/"/g, '\\"')}"`;
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
-
function normalizeDefaultValue(defaultValue, fieldType) {
|
|
58
|
-
if (defaultValue === undefined) return null;
|
|
59
|
-
if (defaultValue === null) return "null";
|
|
60
|
-
|
|
61
|
-
if (typeof defaultValue === "function") {
|
|
62
|
-
if (isDateLikeType(fieldType)) return "current_timestamp";
|
|
63
|
-
return normalizeDefaultValue(defaultValue(), fieldType);
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
if (defaultValue instanceof Date) {
|
|
67
|
-
return defaultValue.toISOString().slice(0, 19).replace("T", " ");
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
if (isSqlTemporalDefault(defaultValue)) return "current_timestamp";
|
|
71
|
-
if (typeof defaultValue === "boolean") return defaultValue ? "1" : "0";
|
|
72
|
-
if (typeof defaultValue === "object") return JSON.stringify(defaultValue);
|
|
73
|
-
|
|
74
|
-
return String(defaultValue);
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
function normalizeDbDefaultValue(defaultValue) {
|
|
78
|
-
if (defaultValue === null || defaultValue === undefined) return null;
|
|
79
|
-
|
|
80
|
-
const normalizedValue = String(defaultValue).trim();
|
|
81
|
-
if (/^current_timestamp(\(\))?$/i.test(normalizedValue)) return "current_timestamp";
|
|
82
|
-
if (/^now\(\)$/i.test(normalizedValue)) return "current_timestamp";
|
|
83
|
-
return normalizedValue;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
57
|
function generateValueSQL(value) {
|
|
87
58
|
return value.map(item => {
|
|
88
59
|
if (item === null) return 'NULL';
|
|
@@ -121,7 +92,7 @@ function getColumnDefinition(fieldName, field) {
|
|
|
121
92
|
}
|
|
122
93
|
|
|
123
94
|
const fieldType = getFieldType(field);
|
|
124
|
-
let colDef
|
|
95
|
+
let colDef;
|
|
125
96
|
|
|
126
97
|
if (Array.isArray(field.enum) && field.enum.length > 0) {
|
|
127
98
|
const enumValues = field.enum.map(v => `'${v.replace(/'/g, "''")}'`).join(", ");
|
|
@@ -166,9 +137,9 @@ class Model {
|
|
|
166
137
|
* @returns {Promise<void>}
|
|
167
138
|
*/
|
|
168
139
|
static async syncAllTables() {
|
|
169
|
-
// Dépendances : {table: [tables dont elle dépend]}
|
|
170
140
|
const dependencies = {};
|
|
171
141
|
const modelMap = {};
|
|
142
|
+
|
|
172
143
|
for (const model of Model.pendingModels) {
|
|
173
144
|
modelMap[model.name] = model;
|
|
174
145
|
dependencies[model.name] = [];
|
|
@@ -181,8 +152,6 @@ class Model {
|
|
|
181
152
|
}
|
|
182
153
|
|
|
183
154
|
const conn = getConnexion();
|
|
184
|
-
const [dbTablesRows] = await conn.promise().query("SHOW TABLES");
|
|
185
|
-
const dbTables = dbTablesRows.map(row => Object.values(row)[0]);
|
|
186
155
|
|
|
187
156
|
const sorted = [];
|
|
188
157
|
const visited = {};
|
|
@@ -224,7 +193,7 @@ class Model {
|
|
|
224
193
|
generateCreateTableStatement(schema) {
|
|
225
194
|
let foreignKey = [];
|
|
226
195
|
const columns = Object.keys(schema).map(fieldName => {
|
|
227
|
-
|
|
196
|
+
const field = getSafe(schema, fieldName);
|
|
228
197
|
let lengthDefault = 255;
|
|
229
198
|
|
|
230
199
|
if (!field.type && typeof field == "object" && !(Array.isArray(field.enum) && field.enum.length > 0)) throw new Error(`Field ${fieldName} has no type defined.`);
|
|
@@ -342,18 +311,17 @@ class Model {
|
|
|
342
311
|
|
|
343
312
|
const query = `SELECT ${buildSelect(select)} FROM ${this.name}${joinClause} ${buildQueryParts(options)}`;
|
|
344
313
|
|
|
345
|
-
|
|
346
|
-
await getConnexion().promise().query(query)
|
|
347
|
-
|
|
348
|
-
|
|
314
|
+
try {
|
|
315
|
+
const result = await getConnexion().promise().query(query);
|
|
316
|
+
const rows = result && Array.isArray(result) ? result[0] : result;
|
|
317
|
+
|
|
318
|
+
if (!rows || rows.length === 0) return [];
|
|
349
319
|
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
});
|
|
356
|
-
});
|
|
320
|
+
return rows.map(row => new ModelInstance(this.name, row, this.schema));
|
|
321
|
+
} catch (err) {
|
|
322
|
+
error(`Error executing auto-prefixed find: ${err}`);
|
|
323
|
+
throw err;
|
|
324
|
+
}
|
|
357
325
|
}
|
|
358
326
|
|
|
359
327
|
/**
|
|
@@ -372,16 +340,16 @@ class Model {
|
|
|
372
340
|
* @throws {Error} Throws an error if query execution fails.
|
|
373
341
|
*/
|
|
374
342
|
async customRequest(custom, custom_err_name = "") {
|
|
375
|
-
|
|
376
|
-
await getConnexion().promise().query(custom)
|
|
377
|
-
if (rows.length == 0) return resolve(0);
|
|
343
|
+
try {
|
|
344
|
+
const rows = await getConnexion().promise().query(custom);
|
|
378
345
|
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
});
|
|
384
|
-
|
|
346
|
+
if (rows.length == 0) return 0;
|
|
347
|
+
|
|
348
|
+
return new ModelInstance(this.name, rows, this.schema);
|
|
349
|
+
} catch (err) {
|
|
350
|
+
error(`Error executing query ${custom_err_name}: ${err}`);
|
|
351
|
+
throw err;
|
|
352
|
+
}
|
|
385
353
|
}
|
|
386
354
|
|
|
387
355
|
/**
|
|
@@ -3,7 +3,6 @@ const { getConnexion } = require("../db/connexion");
|
|
|
3
3
|
const formatObject = require("../utils/formatObject");
|
|
4
4
|
const generateCondition = require("../utils/generateCondition");
|
|
5
5
|
const util = require("util");
|
|
6
|
-
const { serveur } = require("@mlagie/logger");
|
|
7
6
|
const { getSafe, setSafe } = require("../utils/security/safe");
|
|
8
7
|
|
|
9
8
|
/**
|
|
@@ -45,7 +44,7 @@ class ModelInstance {
|
|
|
45
44
|
get: () => {
|
|
46
45
|
const val = getSafe(row, key);
|
|
47
46
|
if (typeof val === 'string' && val.trim().startsWith('{') && val.trim().endsWith('}')) {
|
|
48
|
-
try { return JSON.parse(val); } catch
|
|
47
|
+
try { return JSON.parse(val); } catch { return val; }
|
|
49
48
|
}
|
|
50
49
|
return val;
|
|
51
50
|
},
|
|
@@ -99,13 +98,15 @@ class ModelInstance {
|
|
|
99
98
|
if (typeof rawRec === 'string') {
|
|
100
99
|
try {
|
|
101
100
|
rawRec = JSON.parse(rawRec);
|
|
102
|
-
} catch
|
|
101
|
+
} catch {
|
|
102
|
+
rawRec = recordsArray;
|
|
103
|
+
}
|
|
103
104
|
}
|
|
104
105
|
const rec = rawRec;
|
|
105
106
|
|
|
106
107
|
const schemaDict = this.schema && this.schema.schemaDict ? this.schema.schemaDict : null;
|
|
107
108
|
if (schemaDict) {
|
|
108
|
-
const pkKeys = Object.entries(schemaDict).filter(([
|
|
109
|
+
const pkKeys = Object.entries(schemaDict).filter(([, v]) => v && v.primary_key === true).map(([k]) => k);
|
|
109
110
|
if (pkKeys.length > 0) {
|
|
110
111
|
const pkObj = {};
|
|
111
112
|
for (const k of pkKeys) {
|
|
@@ -115,10 +116,11 @@ class ModelInstance {
|
|
|
115
116
|
}
|
|
116
117
|
}
|
|
117
118
|
if (!whereClause) whereClause = generateCondition(formatObject(rec), false, this.schema);
|
|
118
|
-
} catch
|
|
119
|
-
|
|
119
|
+
} catch {
|
|
120
|
+
const originalFallbackRec = this.getRecordData();
|
|
121
|
+
let fallbackRec = originalFallbackRec;
|
|
120
122
|
if (Array.isArray(fallbackRec)) fallbackRec = fallbackRec[0];
|
|
121
|
-
if (typeof fallbackRec === 'string') { try { fallbackRec = JSON.parse(fallbackRec); } catch
|
|
123
|
+
if (typeof fallbackRec === 'string') { try { fallbackRec = JSON.parse(fallbackRec); } catch { fallbackRec = originalFallbackRec; } }
|
|
122
124
|
whereClause = generateCondition(formatObject(fallbackRec), false, this.schema);
|
|
123
125
|
}
|
|
124
126
|
|
|
@@ -152,17 +154,12 @@ class ModelInstance {
|
|
|
152
154
|
async delete(filter) {
|
|
153
155
|
const sql_request = `DELETE FROM ${this.name} WHERE ${generateCondition(formatObject(filter))}`;
|
|
154
156
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
if (rows[1] != undefined) return resolve(0);
|
|
159
|
-
|
|
160
|
-
resolve(1);
|
|
161
|
-
}).catch((err) => {
|
|
162
|
-
error(`Error executing query delete: ${err}`);
|
|
163
|
-
return 0;
|
|
164
|
-
});
|
|
157
|
+
const rows = await getConnexion().promise().query(sql_request).catch((err) => {
|
|
158
|
+
error(`Error executing query delete: ${err}`);
|
|
159
|
+
throw err;
|
|
165
160
|
});
|
|
161
|
+
|
|
162
|
+
return rows[1] != undefined ? 0 : 1;
|
|
166
163
|
}
|
|
167
164
|
|
|
168
165
|
/**
|
|
@@ -173,16 +170,12 @@ class ModelInstance {
|
|
|
173
170
|
async deleteOne() {
|
|
174
171
|
const sql_request = `DELETE FROM ${this.name} WHERE ${generateCondition(formatObject(this.getRecordData()))}`;
|
|
175
172
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
resolve(1);
|
|
181
|
-
}).catch((err) => {
|
|
182
|
-
error(`Error executing query deleteOne: ${err}`);
|
|
183
|
-
return 0;
|
|
184
|
-
});
|
|
173
|
+
const rows = await getConnexion().promise().query(sql_request).catch((err) => {
|
|
174
|
+
error(`Error executing query deleteOne: ${err}`);
|
|
175
|
+
throw err;
|
|
185
176
|
});
|
|
177
|
+
|
|
178
|
+
return rows[1] != undefined ? 0 : 1;
|
|
186
179
|
}
|
|
187
180
|
|
|
188
181
|
/**
|
|
@@ -192,16 +185,14 @@ class ModelInstance {
|
|
|
192
185
|
* @throws {Error} Throws an error if query execution fails.
|
|
193
186
|
*/
|
|
194
187
|
async customRequest(custom) {
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
resolve(new ModelInstance(this.name, rows, this.schema)).data;
|
|
200
|
-
}).catch((err) => {
|
|
201
|
-
error(`Error executing query: ${err}`);
|
|
202
|
-
return;
|
|
203
|
-
});
|
|
188
|
+
const rows = await getConnexion().promise().query(custom).catch((err) => {
|
|
189
|
+
error(`Error executing query: ${err}`);
|
|
190
|
+
throw err;
|
|
204
191
|
});
|
|
192
|
+
|
|
193
|
+
if (rows.length == 0) return 0;
|
|
194
|
+
|
|
195
|
+
return new ModelInstance(this.name, rows, this.schema).data;
|
|
205
196
|
}
|
|
206
197
|
}
|
|
207
198
|
|
|
@@ -2,7 +2,7 @@ const { getSafe, setSafe } = require("./security/safe");
|
|
|
2
2
|
|
|
3
3
|
module.exports = function (obj) {
|
|
4
4
|
for (const key in obj) {
|
|
5
|
-
if (
|
|
5
|
+
if (Object.prototype.hasOwnProperty.call(obj, key)) {
|
|
6
6
|
const value = getSafe(obj, key);
|
|
7
7
|
if (value instanceof Date) {
|
|
8
8
|
// convert Date to MySQL DATETIME (no timezone)
|