@mlagie/sql-connector 1.4.9 → 2.0.0

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.
@@ -0,0 +1,43 @@
1
+ ---
2
+ name: Bug report
3
+ about: Create a report to help us improve
4
+ title: ''
5
+ labels: ''
6
+ assignees: lagie-marin
7
+
8
+ ---
9
+
10
+ ## Bug Description
11
+
12
+ ## Steps to Reproduce
13
+
14
+ 1. Call the method `...`
15
+ 2. Pass the following options/parameters `...`
16
+ 3. Execute the code
17
+ 4. See the error
18
+
19
+ ## Code Snippet / Logs
20
+
21
+ **JavaScript Code:**
22
+
23
+ ```javascript
24
+ // Insert the JavaScript code that triggers the issue here
25
+ const result = await MyModel.find({ ... });
26
+ ```
27
+
28
+ ## Error Logs / Output
29
+
30
+ ```txt
31
+ TypeError: ... is not a function
32
+ at ...
33
+ ```
34
+
35
+ ## Expected Behavior
36
+
37
+ Example: The `find()` method should return a native JavaScript Array of `ModelInstance` objects so that `.length` or indexation like `[0]` works out of the box when multiple rows are returned.
38
+
39
+ ## Environment
40
+
41
+ - sql-connector version: vX.X.X
42
+ - Node.js version: vXX.XX.X
43
+ - Database (MySQL/MariaDB...): MySQL vX.X
@@ -0,0 +1,29 @@
1
+ ---
2
+ name: Feature request
3
+ about: Suggest an idea for this project
4
+ title: ''
5
+ labels: ''
6
+ assignees: lagie-marin
7
+
8
+ ---
9
+
10
+ ## Feature Type
11
+ - [ ] New method / API addition
12
+ - [ ] Performance improvement
13
+ - [ ] Architecture Refactoring (ORM / Data Mapping)
14
+ - [ ] Other:
15
+
16
+ ## Problem Statement
17
+ *Example: Currently, the ORM wraps the raw driver payload `[rows, fields]` inside a single global `ModelInstance`. This forces developers to leak internal database structure by accessing nested arrays like `data[0][0]` when handling records, destroying proper encapsulation.*
18
+
19
+ ## Proposed Solution
20
+ *Example: Refactor the `find()` method to map and split the query results. It should return a native Array where each row is mapped into its own independent `ModelInstance`. Implement dynamic data-mapping (via continuous references, Getters/Setters, or a Proxy) to allow direct property access (`job.status`) without cloning the memory footprint.*
21
+
22
+ ## Desired Developer Experience (DX / Example)
23
+ ```javascript
24
+ // Provide an example of how the ideal code should look after this feature:
25
+ const jobs = await InjectionJobs.find({ where: { status: "running" } });
26
+
27
+ console.log(jobs.length); // Native array length works
28
+ console.log(jobs[0].status); // Direct property access on the instance
29
+ await jobs[0].updateOne({ status: "success" }); // Instance methods remain fully bound
@@ -0,0 +1,47 @@
1
+ name: Publish Package
2
+
3
+ on:
4
+ release:
5
+ types: [published]
6
+ workflow_dispatch:
7
+
8
+ jobs:
9
+ check_security:
10
+ runs-on: ubuntu-latest
11
+ steps:
12
+ - name: Checkout GH repository
13
+ uses: actions/checkout@v6
14
+ - name: setup node
15
+ uses: actions/setup-node@v6
16
+ with:
17
+ node-version: 22
18
+ - name: Install deps
19
+ run: npm ci
20
+ - name: Audit deps
21
+ continue-on-error: false
22
+ run: |
23
+ npm audit
24
+ - name: Check security of module
25
+ continue-on-error: false
26
+ run: |
27
+ npm run security
28
+ publish:
29
+ needs: check_security
30
+ runs-on: ubuntu-latest
31
+ environment: sqlc
32
+ permissions:
33
+ contents: read
34
+ id-token: write
35
+
36
+ steps:
37
+ - uses: actions/checkout@v6
38
+ - uses: actions/setup-node@v6
39
+ with:
40
+ node-version: 22
41
+ registry-url: https://registry.npmjs.org
42
+ scope: "@mlagie"
43
+ - name: Update npm and Publish
44
+ run: |
45
+ npm install -g npm@latest
46
+ npm ci
47
+ npm publish --provenance --access public
package/README.md CHANGED
@@ -1,5 +1,8 @@
1
1
  # sql-connector documentation
2
2
 
3
+ ![GitHub package.json version](https://img.shields.io/github/package-json/v/lagie-marin/sql-connector?color=#008000) ![NPM Downloads](https://img.shields.io/npm/d18m/%40mlagie%2Fsql-connector?color=#008000) ![NPM Downloads](https://img.shields.io/npm/dw/%40mlagie%2Fsql-connector?color=#008000) ![GitHub followers](https://img.shields.io/github/followers/lagie-marin?style=plastic&color=color%3D%23008000) ![GitHub repo size](https://img.shields.io/github/repo-size/lagie-marin/sql-connector?color=%green)
4
+ ![GitHub last commit](https://img.shields.io/github/last-commit/lagie-marin/sql-connector)
5
+
3
6
  [Français](./docs/fr/README.md) | English
4
7
 
5
8
  sql-connector helps manage MySQL connections, define table schemas, sync tables automatically, and work with database models through a small API.
@@ -32,41 +35,91 @@ await connect(config);
32
35
  await logout();
33
36
  ```
34
37
 
38
+ ### Example
39
+
40
+ ```js
41
+ const { connect: dbConnect, client, Model } = require("@mlagie/sql-connector");
42
+
43
+
44
+ await dbConnect({
45
+ host: 'localhost',
46
+ port: 3306,
47
+ user: 'root',
48
+ password: 'password',
49
+ database: 'mydatabase',
50
+ connectionLimit: 2,
51
+ multipleStatements: true,
52
+ idleTimeout: 10000,
53
+ typeCast: true,
54
+ }).then(() => { Logger.client("- connected to the database") }).catch(error => {
55
+ console.error(error);
56
+ process.exit();
57
+ });
58
+ ```
59
+
35
60
  ## Schema
36
61
 
37
62
  `Schema` describes the structure of a table. Each field can use the following properties.
38
63
 
39
- | Property | Type | Description |
40
- |---|---|---|
41
- | type | `SqlType` or `{ name: SqlType }` | SQL type for the field |
42
- | length | `number` | Maximum length |
43
- | required | `boolean` | Not null constraint |
44
- | default | `any` | Default value |
45
- | unique | `boolean` | Unique constraint |
46
- | auto_increment | `boolean` | Auto increment |
47
- | foreignKey | `string` | Foreign key reference |
48
- | enum | `string[]` | Allowed values |
49
- | primary_key | `boolean` | Primary key flag |
50
- | customize | `string` | Extra SQL options |
64
+ | Property | Type | Description |
65
+ |----------------|----------------------------------|------------------------|
66
+ | type | `SqlType` or `{ name: SqlType }` | SQL type for the field |
67
+ | length | `number` | Maximum length |
68
+ | required | `boolean` | Not null constraint |
69
+ | default | `any` | Default value |
70
+ | unique | `boolean` | Unique constraint |
71
+ | auto_increment | `boolean` | Auto increment |
72
+ | foreignKey | `string` | Foreign key reference |
73
+ | enum | `string[]` | Allowed values |
74
+ | primary_key | `boolean` | Primary key flag |
75
+ | customize | `string` | Extra SQL options |
76
+
77
+ ### Example Schema creation & Model Creation
51
78
 
52
79
  ```javascript
80
+ const { Schema, Model, sqlTypeMap } = require("@mlagie/sql-connector");
81
+
53
82
  const userSchema = new Schema({
54
- id: {
55
- type: Number,
56
- auto_increment: true,
57
- primary_key: true
58
- },
59
- email: {
60
- type: String,
61
- length: 255,
62
- unique: true,
63
- required: true
64
- },
65
- status: {
66
- type: String,
67
- enum: ['active', 'inactive', 'pending'],
68
- default: 'pending'
69
- }
83
+ id: {
84
+ type: Number,
85
+ auto_increment: true,
86
+ primary_key: true
87
+ },
88
+ group_uuid: {
89
+ type: String,
90
+ required: true,
91
+ primary_key: true,
92
+ length: 36
93
+ },
94
+ email: {
95
+ type: String,
96
+ length: 255,
97
+ unique: true,
98
+ required: true
99
+ },
100
+ status: {
101
+ type: String,
102
+ enum: ['active', 'inactive', 'pending'],
103
+ default: 'pending'
104
+ },
105
+ uuid: {
106
+ type: String,
107
+ required: true,
108
+ primary_key: true,
109
+ length: 36
110
+ },
111
+ my_uuid: {
112
+ type: String,
113
+ required: true,
114
+ length: 36
115
+ },
116
+ created_at: {
117
+ type: Date,
118
+ default: sqlTypeMap.CurrentTimestamp
119
+ }
120
+
121
+ module.exports = new Model("User", userSchema);
122
+
70
123
  });
71
124
  ```
72
125
 
@@ -75,13 +128,9 @@ const userSchema = new Schema({
75
128
  `Model.syncAllTables()` compares JS schemas with the database and applies only meaningful differences.
76
129
 
77
130
  - New columns are added automatically.
78
- - Removed columns are only dropped with `dangerousSync: true`.
79
- - Column renames are supported through `oldName`.
80
- - Orphan tables are backed up to a `backup_*.sql` file before deletion.
81
131
 
82
132
  ```javascript
83
133
  await Model.syncAllTables();
84
- await Model.syncAllTables({ dangerousSync: true });
85
134
  ```
86
135
 
87
136
  Important: do not set both `primary_key: true` and `unique: true` on the same field. A primary key is already unique and not null.
@@ -92,25 +141,188 @@ Important: do not set both `primary_key: true` and `unique: true` on the same fi
92
141
 
93
142
  Main methods:
94
143
 
95
- - `save(data)` inserts a row
96
- - `findOne(filter, fields)` fetches a single row
97
- - `find(filter, fields)` fetches multiple rows
98
- - `findAll(options)` supports advanced queries
99
- - `count(filter)` counts rows
100
- - `customRequest(custom)` runs a custom SQL query
101
- - `delete(filter)` deletes a row
102
- - `dropTable()` drops the table
103
- - `generate_uuid()` generates a unique UUID
104
- - `Model.createAllTables()` creates tables in dependency order
144
+ - `save(data)` Inserts a row
145
+ - `find(options)` Retrieves entries from the table.
146
+ - `count(filter)` Counts rows
147
+ - `customRequest(custom)` Runs a custom SQL query
148
+ - `delete(filter)` Deletes a row
149
+ - `dropTable()` Drops the table
150
+ - `generate_uuid()` Generates a unique UUID
105
151
 
106
152
  ```javascript
107
153
  const userModel = new Model('users', userSchema);
108
154
 
109
- await Model.createAllTables();
155
+ await Model.syncAllTables();
110
156
  await userModel.save({ email: 'user@example.com', status: 'active' });
111
157
 
112
- const user = await userModel.findOne({ email: 'user@example.com' });
113
- await userModel.delete({ email: 'user@example.com' });
158
+ const user = await userModel.find({ where: { email: 'user@example.com' }});
159
+ await user[0].deleteOne();
160
+ ```
161
+
162
+ ## save function
163
+
164
+ Saves data to the database table.
165
+
166
+ - **Parameters** `data` *(Object)* - The data to insert into the table.
167
+ - **Returns** `Promise<Object>` - A promise that resolves with the result of the insertion.
168
+ - **Throws** `Error` - Throws an error if the insert fails.
169
+
170
+ ```js
171
+ const User = require("user");
172
+
173
+ async function createUser(email, stat) {
174
+ if (!email || !stat) {
175
+ console.error("Email & stat is required");
176
+ return;
177
+ }
178
+ await User.save({ email: email, status: stat });
179
+
180
+ }
181
+ ```
182
+
183
+ ## find function
184
+
185
+ Retrieves entries from the table.
186
+
187
+ - **Parameters** `options` *(Object)* - Query options
188
+ - **Parameters** `options.select` *(string[])* - Fields to be returned.
189
+ - **Parameters** `options.where` *(Object)* - Filters (key/value).
190
+ - **Parameters** `options.order` *(Array)* - Ex: [['points', 'DESC']]
191
+ - **Parameters** `options.limit` *(number)* - Limit of results.
192
+ - **Returns** `Promise<Array<ModelInstance>>`
193
+
194
+ ### find Options
195
+
196
+ | Option | Type | Description | Example |
197
+ |------------|-----------------|----------------------------------------------------------|----------------------------------------------|
198
+ | `select` | Array | Fields or transformations to retrieve | `['date_day']` |
199
+ | `where` | Object / String | Filtering conditions | `{ project_id: 1 }` |
200
+ | `groupBy` | Array | Fields used to group results | `['period']` |
201
+ | `orderBy` | Array | Sorting rules | `[{ field: 'date_day', direction: 'DESC' }]` |
202
+ | `having` | String | HAVING clause for aggregated queries | `'SUM(total_runs) > 100'` |
203
+ | `limit` | Number | Limits the number of results | `100` |
204
+
205
+ ## Example find
206
+
207
+ ```js
208
+ const User = require("user");
209
+
210
+ User.find({
211
+ select: [
212
+ { dateFormat: ['date_day', '%Y-%m'], as: 'period' },
213
+ { sum: 'error' },
214
+ { sum: 'reload' },
215
+ ],
216
+ groupBy: ['period'],
217
+ orderBy: [{ field: 'period', direction: 'ASC' }],
218
+ limit: 10
219
+ });
220
+ ```
221
+
222
+ ```js
223
+ const User = require("user");
224
+
225
+ User.find({
226
+ select: [
227
+ "email"
228
+ ],
229
+ where: {
230
+ id: 1
231
+ }
232
+ })
233
+ ```
234
+
235
+ ## count function
236
+
237
+ Counts the number of records matching the given filter.
238
+
239
+ - **Parameters** `filter` *(Object)* The filter criteria for the query. Should be an object where keys are column names and values are the values to filter by.
240
+ - **Returns** `Promise<ModelInstance|number>` - A promise that resolves to a `ModelInstance` if a record is found, or `0` if no records match the filter.
241
+
242
+ ## Example count
243
+
244
+ ```js
245
+ const User = require("user");
246
+
247
+ User.count({
248
+ id: id
249
+ })
250
+ ```
251
+
252
+ ## customRequest function
253
+
254
+ The customRequest function allows you to execute SQL queries that are not supported by sql-connector; this could be in queries where the keywords are not yet implemented.
255
+
256
+ - **Parameters** `custom` *(string)* The custom SQL_request query to execute.
257
+ - **Returns** `Promise<void>` A promise that resolves when the query is executed.
258
+ - **Throws** `Error` Throws an error if query execution fails.
259
+
260
+ ## Example customRequest
261
+
262
+ ```js
263
+ const User = require("user");
264
+
265
+ User.customRequest("SELECT id, email, status
266
+ FROM users
267
+ WHERE status IN ('active', 'pending')
268
+ AND email LIKE '%gmail.com';")
269
+ ```
270
+
271
+ ## delete function
272
+
273
+ Deletes an entry from the SQL table that matches the provided filter.
274
+
275
+ - **Parameters** `filter` *(Object)* An object representing the filter conditions for deletion.
276
+ - **Returns** `Promise<number>` A promise that resolves to 0 if no rows were deleted, * or to a ModelInstance representing the deleted row.
277
+ - **Throws** `Error` Throws an error if the SQL query fails.
278
+
279
+ ## Example delete
280
+
281
+ ```js
282
+ const User = require("user");
283
+
284
+ User.delete({
285
+ email: my@gmail.com
286
+ })
287
+ ```
288
+
289
+ ## dropTable function
290
+
291
+ Asynchronously drops a table if it exists in the database.
292
+
293
+ This function constructs a SQL_request query to drop a table with the name specified by the `this.name` property. It then executes the query using a promise-based approach.
294
+ If the query is successful, the result is logged to the console.
295
+ If an error occurs during the execution of the query, an error message is logged.
296
+
297
+ - **Returns** `Promise<void>` A promise that resolves when the query execution is complete.
298
+
299
+ ## Example dropTable
300
+
301
+ ```js
302
+ const User = require("user");
303
+
304
+ User.dropTable();
305
+ ```
306
+
307
+ ## generate_uuid function
308
+
309
+ Generates a unique UUID for the current model.
310
+ This function generates a UUID using the SQL_request `UUID()` function and checks if the generated UUID already exists in the database for the current model. If the UUID is unique, it is returned.
311
+ Otherwise, the function resolves to `null`.
312
+
313
+ - **Parameters** `string` var_uuid By default, it is set to uuid.
314
+ - **Returns** `Promise<string|null>` A promise that resolves to a unique UUID string if successful, or `null` if an error occurs or the UUID is not unique.
315
+ - **Throws** `Error` If there is an error executing the SQL_request query.
316
+
317
+ ## Example generate_uuid
318
+
319
+ ```js
320
+ const User = require("user");
321
+
322
+ const uuid = await User.generate_uuid();
323
+ const my_uuid = await User.generate_uuid("my_uuid");
324
+
325
+ await User.save{ email: "user@example.com", status: "active", uuid: uuid, my_uuid: my_uuid }
114
326
  ```
115
327
 
116
328
  ## Model instances
@@ -122,8 +334,8 @@ await userModel.delete({ email: 'user@example.com' });
122
334
  - `deleteOne()` deletes the instance row
123
335
  - `customRequest(custom)` runs a custom query
124
336
 
125
- ```javascript
126
- const userInstance = new ModelInstance('users', { email: 'user@example.com' });
337
+ ```js
338
+ const userInstance = await find({ select: "users", where: { email: 'user@example.com' }})[0];
127
339
 
128
340
  await userInstance.updateOne({ status: 'inactive' });
129
341
  await userInstance.deleteOne();
@@ -155,4 +367,4 @@ module.exports = client => {
155
367
 
156
368
  ## Summary
157
369
 
158
- sql-connector provides a small layer to connect to MySQL, describe schemas, synchronize tables, and manipulate data with typed models.
370
+ sql-connector provides a small layer to connect to MySQL, describe schemas, synchronize tables, and manipulate data with typed models.