@mlagie/sql-connector 3.0.3 → 3.1.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.
- package/README.md +985 -316
- package/docs/API.md +240 -0
- package/docs/DIALECTS.md +56 -0
- package/docs/MIGRATIONS.md +277 -0
- package/docs/QUERY.md +281 -0
- package/docs/README.md +20 -0
- package/docs/SCHEMA.md +167 -0
- package/docs/fr/README_FR.md +549 -305
- package/index.d.ts +2002 -137
- package/index.js +39 -9
- package/package.json +7 -6
- package/src/db/dialects.js +1 -1
- package/src/migration/BackupManager.js +42 -0
- package/src/migration/Migration.js +163 -0
- package/src/migration/MigrationPlan.js +62 -0
- package/src/migration/MigrationRunner.js +194 -0
- package/src/migration/MigrationStore.js +137 -0
- package/src/migration/SchemaDiffer.js +261 -0
- package/src/migration/SchemaInspector.js +44 -0
- package/src/migration/SchemaSnapshot.js +53 -0
- package/src/migration/crypto.js +79 -0
- package/src/migration/dialects/Dialect.js +112 -0
- package/src/migration/dialects/MySQLDialect.js +205 -0
- package/src/migration/dialects/PostgreSQLDialect.js +233 -0
- package/src/migration/dialects/index.js +21 -0
- package/src/migration/errors.js +28 -0
- package/src/migration/index.js +18 -0
- package/src/migration/utils.js +89 -0
- package/src/models/Model.js +8 -2
- package/src/models/Schema.js +20 -20
- package/src/utils/buildQuery/buildQuery.js +51 -11
- package/src/utils/sqlTypeMap.js +8 -0
package/docs/fr/README_FR.md
CHANGED
|
@@ -1,483 +1,727 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @mlagie/sql-connector — Documentation française
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-

|
|
3
|
+
[](https://www.npmjs.com/package/@mlagie/sql-connector)
|
|
5
4
|
|
|
6
|
-
|
|
5
|
+
Couche SQL légère et orientée dialecte pour **MySQL** et **PostgreSQL**.
|
|
7
6
|
|
|
8
|
-
|
|
7
|
+
`@mlagie/sql-connector` fournit :
|
|
9
8
|
|
|
10
|
-
|
|
9
|
+
- gestion des connexions ;
|
|
10
|
+
- schémas déclaratifs ;
|
|
11
|
+
- types SQL et alias de types ;
|
|
12
|
+
- modèles CRUD ;
|
|
13
|
+
- conditions structurées `WHERE` / `HAVING` ;
|
|
14
|
+
- `IN`, `BETWEEN`, `LIKE`, `ILIKE`, `NULL`, `AND`, `OR`, `NOT` ;
|
|
15
|
+
- agrégations, groupements, tris et jointures ;
|
|
16
|
+
- gestion des dépendances de clés étrangères ;
|
|
17
|
+
- synchronisation des schémas ;
|
|
18
|
+
- planification et exécution des migrations ;
|
|
19
|
+
- checksum, fichiers de migration chiffrés et sauvegardes chiffrées optionnelles ;
|
|
20
|
+
- déclarations TypeScript.
|
|
11
21
|
|
|
12
|
-
|
|
13
|
-
const { Schema, connect, logout, Model, ModelInstance, client, sqlTypeMap } = require('sql-connector');
|
|
14
|
-
```
|
|
15
|
-
|
|
16
|
-
## Connexion à la base
|
|
17
|
-
|
|
18
|
-
`connect(config, dialect)` ouvre une connexion à une base de données. MySQL est utilisé par défaut ; utilisez `"postgres"` pour PostgreSQL.
|
|
19
|
-
|
|
20
|
-
```javascript
|
|
21
|
-
const config = {
|
|
22
|
-
host: 'localhost',
|
|
23
|
-
port: 3306,
|
|
24
|
-
user: 'root',
|
|
25
|
-
password: 'password',
|
|
26
|
-
database: 'mydatabase'
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
await connect(config);
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
### PostgreSQL
|
|
22
|
+
> **SGBD supportés :** MySQL et PostgreSQL.
|
|
33
23
|
|
|
34
|
-
|
|
24
|
+
**Documentation :** [English](../../README.md) · Français
|
|
35
25
|
|
|
36
|
-
|
|
37
|
-
const postgresConfig = {
|
|
38
|
-
host: 'localhost',
|
|
39
|
-
port: 5432,
|
|
40
|
-
user: 'postgres',
|
|
41
|
-
password: 'password',
|
|
42
|
-
database: 'mydatabase'
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
await connect(postgresConfig, 'postgres');
|
|
46
|
-
```
|
|
26
|
+
---
|
|
47
27
|
|
|
48
|
-
|
|
28
|
+
## Sommaire
|
|
29
|
+
|
|
30
|
+
1. [Installation](#installation)
|
|
31
|
+
2. [Démarrage rapide](#démarrage-rapide)
|
|
32
|
+
3. [Connexion](#connexion)
|
|
33
|
+
4. [Schémas](#schémas)
|
|
34
|
+
- [Propriétés d'un champ](#propriétés-dun-champ)
|
|
35
|
+
- [Types](#types)
|
|
36
|
+
- [Valeurs par défaut](#valeurs-par-défaut)
|
|
37
|
+
- [Contraintes](#contraintes)
|
|
38
|
+
- [Clés étrangères](#clés-étrangères)
|
|
39
|
+
5. [Modèles](#modèles)
|
|
40
|
+
6. [Conditions](#conditions)
|
|
41
|
+
7. [Select, agrégations, groupement et tri](#select-agrégations-groupement-et-tri)
|
|
42
|
+
8. [Jointures](#jointures)
|
|
43
|
+
9. [Synchronisation](#synchronisation)
|
|
44
|
+
10. [Instances de modèle](#instances-de-modèle)
|
|
45
|
+
11. [Migrations](#migrations)
|
|
46
|
+
- [Migration workflow](#migration-workflow)
|
|
47
|
+
- [Migration options](#migration-options)
|
|
48
|
+
- [Migration opérations](#migration-opérations)
|
|
49
|
+
- [Migration renommage](#migration-renommage)
|
|
50
|
+
- [Changement destructifs](#changements-destructifs)
|
|
51
|
+
- [Checksum et historique](#checksum-et-historique)
|
|
52
|
+
- [Chriffrement](#chiffrement)
|
|
53
|
+
- [Sauvegardes](#sauvegardes)
|
|
54
|
+
- [Statut et nettoyage](#statut-et-nettoyage)
|
|
55
|
+
12. [TypeScript](#typescript)
|
|
56
|
+
13. [Sécurité SQL](#sécurité-sql)
|
|
57
|
+
14. [API exportée](#api-exportée)
|
|
58
|
+
15. [Tests](#tests)
|
|
59
|
+
16. [Documentation détaillée](#documentation-détaillée)
|
|
60
|
+
17. [Licence](#licence)
|
|
49
61
|
|
|
50
|
-
|
|
51
|
-
|----------|-----------------------------|--------------|-------------------------------|
|
|
52
|
-
| MySQL | Accolades inverses | `?` | `1` / `0` |
|
|
53
|
-
| PostgreSQL | Guillemets doubles | `$1`, `$2`, ... | `TRUE` / `FALSE` |
|
|
62
|
+
---
|
|
54
63
|
|
|
55
|
-
|
|
64
|
+
## Installation
|
|
56
65
|
|
|
57
|
-
```
|
|
58
|
-
|
|
66
|
+
```bash
|
|
67
|
+
npm install @mlagie/sql-connector
|
|
59
68
|
```
|
|
60
69
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
`Schema` décrit la structure d'une table. Chaque champ peut utiliser les propriétés suivantes.
|
|
70
|
+
---
|
|
64
71
|
|
|
65
|
-
|
|
66
|
-
|----------------|----------------------------------|-----------------------------|
|
|
67
|
-
| type | `SqlType` ou `{ name: SqlType }` | Type SQL du champ |
|
|
68
|
-
| length | `number` | Longueur maximale |
|
|
69
|
-
| required | `boolean` | Champ obligatoire |
|
|
70
|
-
| default | `any` | Valeur par défaut |
|
|
71
|
-
| unique | `boolean` | Valeur unique |
|
|
72
|
-
| auto_increment | `boolean` | Auto-incrément |
|
|
73
|
-
| foreignKey | `string` | Référence de clé étrangère |
|
|
74
|
-
| enum | `string[]` | Liste de valeurs autorisées |
|
|
75
|
-
| primary_key | `boolean` | Clé primaire |
|
|
76
|
-
| customize | `string` | Options SQL additionnelles |
|
|
72
|
+
## Démarrage rapide
|
|
77
73
|
|
|
78
|
-
```
|
|
79
|
-
const {
|
|
74
|
+
```js
|
|
75
|
+
const {
|
|
76
|
+
Schema,
|
|
77
|
+
Model,
|
|
78
|
+
connect,
|
|
79
|
+
logout,
|
|
80
|
+
} = require('@mlagie/sql-connector');
|
|
81
|
+
|
|
82
|
+
await connect({
|
|
83
|
+
host: 'localhost',
|
|
84
|
+
port: 3306,
|
|
85
|
+
user: 'root',
|
|
86
|
+
password: 'password',
|
|
87
|
+
database: 'mydatabase',
|
|
88
|
+
});
|
|
80
89
|
|
|
81
90
|
const userSchema = new Schema({
|
|
82
91
|
id: {
|
|
83
92
|
type: Number,
|
|
84
93
|
auto_increment: true,
|
|
85
|
-
primary_key: true
|
|
94
|
+
primary_key: true,
|
|
86
95
|
},
|
|
87
96
|
email: {
|
|
88
97
|
type: String,
|
|
89
98
|
length: 255,
|
|
99
|
+
required: true,
|
|
90
100
|
unique: true,
|
|
91
|
-
required: true
|
|
92
101
|
},
|
|
93
102
|
status: {
|
|
94
103
|
type: String,
|
|
95
104
|
enum: ['active', 'inactive', 'pending'],
|
|
96
|
-
default: 'pending'
|
|
105
|
+
default: 'pending',
|
|
97
106
|
},
|
|
98
|
-
uuid: {
|
|
99
|
-
type: String,
|
|
100
|
-
required: true,
|
|
101
|
-
primary_key: true,
|
|
102
|
-
length: 36
|
|
103
|
-
},
|
|
104
|
-
my_uuid: {
|
|
105
|
-
type: String,
|
|
106
|
-
required: true,
|
|
107
|
-
length: 36
|
|
108
|
-
},
|
|
109
|
-
created_at: {
|
|
110
|
-
type: Date,
|
|
111
|
-
default: sqlTypeMap.CurrentTimestamp
|
|
112
|
-
}
|
|
113
107
|
});
|
|
114
108
|
|
|
115
|
-
|
|
116
|
-
```
|
|
109
|
+
const User = new Model('users', userSchema);
|
|
117
110
|
|
|
118
|
-
|
|
111
|
+
await Model.syncAllTables();
|
|
112
|
+
await User.save({ email: 'user@example.com', status: 'active' });
|
|
119
113
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
114
|
+
const users = await User.find({
|
|
115
|
+
where: {
|
|
116
|
+
status: { IN: ['active', 'pending'] },
|
|
117
|
+
},
|
|
124
118
|
});
|
|
119
|
+
|
|
120
|
+
await logout();
|
|
125
121
|
```
|
|
126
122
|
|
|
127
|
-
|
|
123
|
+
---
|
|
128
124
|
|
|
129
|
-
|
|
125
|
+
## Connexion
|
|
130
126
|
|
|
131
|
-
|
|
132
|
-
- Les clés étrangères utilisent le format `table(colonne)` et peuvent être déclarées sur plusieurs champs.
|
|
127
|
+
### MySQL
|
|
133
128
|
|
|
134
|
-
|
|
135
|
-
|
|
129
|
+
MySQL est le dialecte par défaut.
|
|
130
|
+
|
|
131
|
+
```js
|
|
132
|
+
await connect({
|
|
133
|
+
host: 'localhost',
|
|
134
|
+
port: 3306,
|
|
135
|
+
user: 'root',
|
|
136
|
+
password: 'password',
|
|
137
|
+
database: 'mydatabase',
|
|
138
|
+
});
|
|
136
139
|
```
|
|
137
140
|
|
|
138
|
-
|
|
141
|
+
### PostgreSQL
|
|
139
142
|
|
|
140
|
-
|
|
143
|
+
```js
|
|
144
|
+
await connect({
|
|
145
|
+
host: 'localhost',
|
|
146
|
+
port: 5432,
|
|
147
|
+
user: 'postgres',
|
|
148
|
+
password: 'password',
|
|
149
|
+
database: 'mydatabase',
|
|
150
|
+
}, 'postgres');
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### Fermer la connexion
|
|
154
|
+
|
|
155
|
+
```js
|
|
156
|
+
await logout();
|
|
157
|
+
```
|
|
141
158
|
|
|
142
|
-
|
|
159
|
+
---
|
|
143
160
|
|
|
144
|
-
|
|
161
|
+
## Schémas
|
|
145
162
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
163
|
+
```js
|
|
164
|
+
const schema = new Schema({
|
|
165
|
+
name: { type: String, length: 120, required: true },
|
|
166
|
+
age: { type: Number },
|
|
167
|
+
active: { type: Boolean, default: true },
|
|
168
|
+
});
|
|
169
|
+
```
|
|
153
170
|
|
|
154
|
-
|
|
155
|
-
|
|
171
|
+
### Propriétés d'un champ
|
|
172
|
+
|
|
173
|
+
| Propriété | Type | Description |
|
|
174
|
+
|---|---|---|
|
|
175
|
+
| `type` | constructeur / chaîne / `{ name }` | Type du champ. |
|
|
176
|
+
| `length` | `number` | Longueur du type. |
|
|
177
|
+
| `precision` | `number` | Précision décimale. |
|
|
178
|
+
| `scale` | `number` | Échelle décimale. |
|
|
179
|
+
| `required` | `boolean` | Ajoute `NOT NULL`. |
|
|
180
|
+
| `default` | quelconque | Valeur SQL par défaut. |
|
|
181
|
+
| `unique` | `boolean` | Ajoute `UNIQUE`. |
|
|
182
|
+
| `primary_key` | `boolean` | Ajoute `PRIMARY KEY`. |
|
|
183
|
+
| `auto_increment` | `boolean` | Auto-incrément / identity selon le dialecte. |
|
|
184
|
+
| `enum` | `string[]` | Valeurs autorisées. |
|
|
185
|
+
| `foreignKey` | `string` | Référence comme `users(id)` ou `users.id`. |
|
|
186
|
+
| `oldname` | `string` | Ancien nom utilisé explicitement par les migrations. |
|
|
187
|
+
| `customize` | `string` | SQL additionnel de définition. |
|
|
188
|
+
|
|
189
|
+
### Types
|
|
190
|
+
|
|
191
|
+
| Alias | SQL |
|
|
192
|
+
|---|---|
|
|
193
|
+
| `String` | `VARCHAR` |
|
|
194
|
+
| `Char` | `CHAR` |
|
|
195
|
+
| `Number` | `INT` |
|
|
196
|
+
| `SmallInt` | `SMALLINT` |
|
|
197
|
+
| `BigInt` | `BIGINT` |
|
|
198
|
+
| `Decimal` | `DECIMAL` |
|
|
199
|
+
| `Boolean` | `BOOLEAN` |
|
|
200
|
+
| `Date` | `DATETIME` |
|
|
201
|
+
| `DateTime` | `DATETIME` |
|
|
202
|
+
| `Timestamp` | `TIMESTAMP` |
|
|
203
|
+
| `Float` | `FLOAT` |
|
|
204
|
+
| `Double` | `DOUBLE` |
|
|
205
|
+
| `Text` | `TEXT` |
|
|
206
|
+
| `Blob` | `BLOB` |
|
|
207
|
+
| `Binary` | `VARBINARY` |
|
|
208
|
+
| `Uuid` | `CHAR` |
|
|
209
|
+
| `Object` | `JSON` |
|
|
210
|
+
| `Array` | `VARCHAR` |
|
|
211
|
+
| `Now` | `NOW()` |
|
|
212
|
+
| `CurrentTimestamp` | `CURRENT_TIMESTAMP` |
|
|
213
|
+
|
|
214
|
+
Les constructeurs JavaScript `String`, `Number`, `Boolean`, `Date`, `Object` et `Array` sont également acceptés. `Now` et `CurrentTimestamp` représentent des expressions/constantes SQL temporelles et sont surtout destinés aux valeurs par défaut, pas à des types de stockage portables.
|
|
215
|
+
|
|
216
|
+
### Valeurs par défaut
|
|
156
217
|
|
|
157
|
-
|
|
158
|
-
|
|
218
|
+
```js
|
|
219
|
+
const schema = new Schema({
|
|
220
|
+
active: { type: Boolean, default: true },
|
|
221
|
+
retries: { type: Number, default: 0 },
|
|
222
|
+
note: { type: String, default: 'pending' },
|
|
223
|
+
created_at: { type: Date, default: 'CURRENT_TIMESTAMP' },
|
|
224
|
+
metadata: { type: Object, default: { source: 'api' } },
|
|
225
|
+
});
|
|
226
|
+
```
|
|
159
227
|
|
|
160
|
-
|
|
161
|
-
|
|
228
|
+
Les valeurs par défaut peuvent être `null`, chaînes, nombres, booléens, `Date`, objets, expressions temporelles SQL ou fonctions retournant une valeur supportée.
|
|
229
|
+
|
|
230
|
+
```js
|
|
231
|
+
created_at: {
|
|
232
|
+
type: Date,
|
|
233
|
+
default: sqlTypeMap.CurrentTimestamp,
|
|
234
|
+
}
|
|
162
235
|
```
|
|
163
236
|
|
|
164
|
-
|
|
237
|
+
### Contraintes
|
|
165
238
|
|
|
166
|
-
|
|
239
|
+
Le module prend en charge `NOT NULL`, `UNIQUE`, `PRIMARY KEY`, `FOREIGN KEY`, `DEFAULT`, `ENUM`, l'auto-incrément et `customize`.
|
|
167
240
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
241
|
+
Ne combinez pas `primary_key: true` et `unique: true`.
|
|
242
|
+
|
|
243
|
+
### Clés étrangères
|
|
171
244
|
|
|
172
245
|
```js
|
|
173
|
-
const
|
|
246
|
+
const orderSchema = new Schema({
|
|
247
|
+
user_id: { type: Number, foreignKey: 'users(id)' },
|
|
248
|
+
product_id: { type: Number, foreignKey: 'products.id' },
|
|
249
|
+
});
|
|
250
|
+
```
|
|
174
251
|
|
|
175
|
-
|
|
176
|
-
if (!email || !stat) {
|
|
177
|
-
console.error("Email & stat is required");
|
|
178
|
-
return;
|
|
179
|
-
}
|
|
180
|
-
await User.save({ email: email, status: stat });
|
|
252
|
+
`Model.syncAllTables()` respecte l'ordre des dépendances et rejette les cycles.
|
|
181
253
|
|
|
182
|
-
|
|
254
|
+
---
|
|
255
|
+
|
|
256
|
+
## Modèles
|
|
257
|
+
|
|
258
|
+
```js
|
|
259
|
+
const User = new Model('users', userSchema);
|
|
183
260
|
```
|
|
184
261
|
|
|
185
|
-
|
|
262
|
+
Méthodes principales :
|
|
186
263
|
|
|
187
|
-
|
|
264
|
+
- `save(data)` — insertion ;
|
|
265
|
+
- `find(options)` — recherche structurée ;
|
|
266
|
+
- `count(filter)` — comptage ;
|
|
267
|
+
- `delete(filter)` — suppression ;
|
|
268
|
+
- `dropTable()` — suppression de table ;
|
|
269
|
+
- `generate_uuid(column?)` — génération d'UUID ;
|
|
270
|
+
- `customRequest(sql)` — SQL brut de confiance.
|
|
188
271
|
|
|
189
|
-
|
|
190
|
-
- **Paramètres** `options.select` *(Array<string|SelectAggregation>)* – Champs, agrégations ou transformations à retourner.
|
|
191
|
-
- **Paramètres** `options.where` *(Object / string)* – Conditions de filtrage (objet clé/valeur ou clause brute sous forme de chaîne).
|
|
192
|
-
- **Paramètres** `options.groupBy` *(Array<string|GroupAggregation>)* – Champs, colonnes ou expressions utilisés pour regrouper les résultats.
|
|
193
|
-
- **Paramètres** `options.orderBy` *(Array<string|OrderByOption>)* – Règles de tri.
|
|
194
|
-
- **Paramètres** `options.join` *(JoinOption / JoinOption[])* – Structures de configuration pour les jointures de tables.
|
|
195
|
-
- **Paramètres** `options.limit` *(number)* – Nombre maximal de résultats à retourner.
|
|
196
|
-
- **Retourne** `Promise<Array<ModelInstance>>`
|
|
272
|
+
Exemple :
|
|
197
273
|
|
|
198
|
-
|
|
274
|
+
```js
|
|
275
|
+
const users = await User.find({
|
|
276
|
+
select: ['id', 'email'],
|
|
277
|
+
where: {
|
|
278
|
+
status: { IN: ['active', 'pending'] },
|
|
279
|
+
},
|
|
280
|
+
orderBy: [
|
|
281
|
+
{ field: 'email', direction: 'ASC' },
|
|
282
|
+
],
|
|
283
|
+
limit: 50,
|
|
284
|
+
offset: 0,
|
|
285
|
+
});
|
|
286
|
+
```
|
|
199
287
|
|
|
200
|
-
|
|
288
|
+
---
|
|
201
289
|
|
|
202
|
-
|
|
203
|
-
|---------------------------------|-------------------|-----------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------|
|
|
204
|
-
| `col` | `string` | Sélectionne une colonne de table simple, sans agrégation. | `{ col: 'email' }` $\rightarrow$ \`email\` |
|
|
205
|
-
| `sum` | `string` | Calcule la somme de toutes les valeurs numériques d'une colonne. | `{ sum: 'error' }` $\rightarrow$ `SUM(`\`error\``)` |
|
|
206
|
-
| `distinct` | `string` | Applique une contrainte DISTINCT sur la colonne spécifiée. | `{ distinct: 'status' }` $\rightarrow$ `DISTINCT `\`status\` |
|
|
207
|
-
| `dateFormat` | `[string, string]`| Formate une colonne de type Date en utilisant le formatage standard MySQL (`[colonne, chaine_de_format]`).| `{ dateFormat: ['created_at', '%Y-%m'] }` $\rightarrow$ `DATE_FORMAT(`\`created_at\``, '%Y-%m')` |
|
|
208
|
-
| `count` | `string` | Comptage de lignes standard (ignore les valeurs `NULL`). | `{ count: 'id' }` $\rightarrow$ `COUNT(`\`id\``)` |
|
|
209
|
-
| `count` (Array) | `string[]` | Calcule le nombre de combinaisons uniques sur plusieurs colonnes (COUNT DISTINCT). | `{ count: ['team', 'source'] }` $\rightarrow$ `COUNT( DISTINCT `\`team\``, `\`source\`` )` |
|
|
210
|
-
| `count` (Object) | `Object` | Agrégation conditionnelle automatisée (`CASE WHEN`). Idéal pour les indicateurs clés (KPIs) et statuts. | `{ count: { deletedAt: null } }` $\rightarrow$ `COUNT(CASE WHEN `\`deletedAt\`` = NULL THEN 1 END)` |
|
|
211
|
-
| `as` | `string` | Définit un identifiant de sortie personnalisé ou un alias d'agrégation (SQL `AS`). | `{ count: 'id', as: 'total' }` $\rightarrow$ `COUNT(`\`id\``) AS `\`total\` |
|
|
290
|
+
## Conditions
|
|
212
291
|
|
|
213
|
-
|
|
292
|
+
Les `where` et `having` utilisent des objets structurés. Les chaînes SQL brutes sont refusées par le builder.
|
|
214
293
|
|
|
215
|
-
|
|
294
|
+
### Valeur simple
|
|
216
295
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
| `dateFormat` | `[string, string]`| Regroupe de manière sécurisée par une colonne Date formatée à l'aide d'entrées paramétrées (Format : `[colonne, format]`). | `{ dateFormat: ['created_at', '%Y-%m'] }` → `DATE_FORMAT(` \`created_at\``, '%Y-%m')` |
|
|
221
|
-
| `as` | `string` | Cible directement l'alias défini dans le select pour respecter les contraintes strictes de syntaxe MySQL. | `{ col: 'role', as: 'user_role' }` → \`user_role\` |
|
|
296
|
+
```js
|
|
297
|
+
{ status: 'active' }
|
|
298
|
+
```
|
|
222
299
|
|
|
223
|
-
|
|
300
|
+
### Opérateurs
|
|
301
|
+
|
|
302
|
+
Les opérateurs supportés sont :
|
|
303
|
+
|
|
304
|
+
```text
|
|
305
|
+
=
|
|
306
|
+
!=
|
|
307
|
+
>
|
|
308
|
+
<
|
|
309
|
+
>=
|
|
310
|
+
<=
|
|
311
|
+
LIKE
|
|
312
|
+
NOT LIKE
|
|
313
|
+
ILIKE
|
|
314
|
+
NOT ILIKE
|
|
315
|
+
IN
|
|
316
|
+
NOT IN
|
|
317
|
+
BETWEEN
|
|
318
|
+
NOT BETWEEN
|
|
319
|
+
```
|
|
224
320
|
|
|
225
|
-
|
|
321
|
+
Exemples :
|
|
226
322
|
|
|
227
|
-
|
|
323
|
+
```js
|
|
324
|
+
{ age: { '>=': 18 } }
|
|
325
|
+
{ email: { LIKE: '%@example.com' } }
|
|
326
|
+
{ status: { IN: ['active', 'pending'] } }
|
|
327
|
+
{ age: { BETWEEN: [18, 65] } }
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
### NULL
|
|
331
|
+
|
|
332
|
+
```js
|
|
333
|
+
{ deleted_at: null }
|
|
334
|
+
{ deleted_at: { '=': null } }
|
|
335
|
+
{ deleted_at: { '!=': null } }
|
|
336
|
+
```
|
|
228
337
|
|
|
229
|
-
|
|
338
|
+
Ces formes deviennent `IS NULL` / `IS NOT NULL`.
|
|
230
339
|
|
|
231
|
-
|
|
232
|
-
- **direction** `('ASC'\|'DESC')` : Le sens du tri (Par défaut : `'ASC'`).
|
|
340
|
+
### AND / OR / NOT
|
|
233
341
|
|
|
234
|
-
|
|
342
|
+
```js
|
|
343
|
+
{
|
|
344
|
+
AND: [
|
|
345
|
+
{ active: true },
|
|
346
|
+
{
|
|
347
|
+
OR: [
|
|
348
|
+
{ role: 'admin' },
|
|
349
|
+
{ role: 'moderator' },
|
|
350
|
+
],
|
|
351
|
+
},
|
|
352
|
+
],
|
|
353
|
+
}
|
|
354
|
+
```
|
|
235
355
|
|
|
236
|
-
|
|
356
|
+
```js
|
|
357
|
+
{
|
|
358
|
+
NOT: {
|
|
359
|
+
status: { IN: ['deleted', 'blocked'] },
|
|
360
|
+
},
|
|
361
|
+
}
|
|
362
|
+
```
|
|
237
363
|
|
|
238
|
-
|
|
239
|
-
- **on** `(string)` : Chaîne de caractères représentant la condition de jointure (ex: `"MyTable.project_id = Projects.id"`).
|
|
240
|
-
- **alias** `(string)` *(Optionnel)* : Un alias SQL alternatif pour la table jointe.
|
|
241
|
-
- **type** `('INNER'\|'LEFT'\|'RIGHT')` *(Optionnel)* : Type de jointure SQL (Par défaut : `'INNER'`).
|
|
364
|
+
Les tableaux `AND` / `OR` vides et les valeurs invalides de `NOT` sont refusés.
|
|
242
365
|
|
|
243
366
|
---
|
|
244
367
|
|
|
245
|
-
##
|
|
368
|
+
## Select, agrégations, groupement et tri
|
|
246
369
|
|
|
247
|
-
|
|
248
|
-
const User = require("user");
|
|
370
|
+
Le `select` accepte des chaînes ou des objets structurés :
|
|
249
371
|
|
|
250
|
-
|
|
372
|
+
```js
|
|
373
|
+
const stats = await User.find({
|
|
251
374
|
select: [
|
|
252
|
-
|
|
253
|
-
{
|
|
254
|
-
{ sum: '
|
|
375
|
+
'status',
|
|
376
|
+
{ count: 'id', as: 'total' },
|
|
377
|
+
{ sum: 'amount', as: 'revenue' },
|
|
255
378
|
],
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
379
|
+
where: {
|
|
380
|
+
active: true,
|
|
381
|
+
},
|
|
259
382
|
});
|
|
260
383
|
```
|
|
261
384
|
|
|
262
|
-
|
|
263
|
-
|
|
385
|
+
### Groupement avec `GROUP BY`
|
|
386
|
+
|
|
387
|
+
`groupBy` est une option de `find()` et permet de regrouper les lignes avant l'application de `having`.
|
|
264
388
|
|
|
265
|
-
|
|
389
|
+
```js
|
|
390
|
+
const stats = await User.find({
|
|
266
391
|
select: [
|
|
267
|
-
|
|
392
|
+
'status',
|
|
393
|
+
{ count: 'id', as: 'total' },
|
|
268
394
|
],
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
}
|
|
272
|
-
})
|
|
395
|
+
groupBy: ['status'],
|
|
396
|
+
});
|
|
273
397
|
```
|
|
274
398
|
|
|
275
|
-
|
|
399
|
+
Les éléments de `groupBy` peuvent être des colonnes ou des expressions structurées comme `dateFormat`.
|
|
276
400
|
|
|
277
|
-
|
|
401
|
+
### Filtrer les groupes avec `HAVING`
|
|
278
402
|
|
|
279
|
-
|
|
403
|
+
`having` filtre le résultat **après** le `GROUP BY` et utilise le même format de conditions structurées que `where` :
|
|
280
404
|
|
|
281
405
|
```js
|
|
282
|
-
const
|
|
283
|
-
|
|
284
|
-
const stats = await MyTable.find({
|
|
406
|
+
const stats = await User.find({
|
|
285
407
|
select: [
|
|
286
|
-
|
|
287
|
-
{ count:
|
|
408
|
+
'status',
|
|
409
|
+
{ count: 'id', as: 'total' },
|
|
288
410
|
],
|
|
289
|
-
|
|
411
|
+
groupBy: ['status'],
|
|
412
|
+
having: {
|
|
413
|
+
total: { '>': 10 },
|
|
414
|
+
},
|
|
290
415
|
});
|
|
291
416
|
```
|
|
292
417
|
|
|
293
|
-
|
|
418
|
+
Les chaînes SQL brutes dans `having` sont refusées.
|
|
294
419
|
|
|
295
|
-
|
|
420
|
+
### Tri avec `ORDER BY`
|
|
296
421
|
|
|
297
|
-
|
|
298
|
-
const { ProjectPipeline } = require("./models");
|
|
422
|
+
`orderBy` permet de trier le résultat final. Il accepte des colonnes ou des alias de sélection, avec une direction `ASC` ou `DESC` :
|
|
299
423
|
|
|
300
|
-
|
|
424
|
+
```js
|
|
425
|
+
const stats = await User.find({
|
|
301
426
|
select: [
|
|
302
|
-
|
|
303
|
-
{ count:
|
|
427
|
+
'status',
|
|
428
|
+
{ count: 'id', as: 'total' },
|
|
429
|
+
],
|
|
430
|
+
groupBy: ['status'],
|
|
431
|
+
having: {
|
|
432
|
+
total: { '>': 10 },
|
|
433
|
+
},
|
|
434
|
+
orderBy: [
|
|
435
|
+
{ field: 'total', direction: 'DESC' },
|
|
436
|
+
{ field: 'status', direction: 'ASC' },
|
|
304
437
|
],
|
|
305
|
-
where: {
|
|
306
|
-
source: 'jenkins',
|
|
307
|
-
team: 'GROUP-1'
|
|
308
|
-
}
|
|
309
438
|
});
|
|
310
|
-
|
|
439
|
+
```
|
|
311
440
|
|
|
312
|
-
|
|
441
|
+
### Exemple complet
|
|
313
442
|
|
|
314
|
-
|
|
443
|
+
Toutes les clauses peuvent être combinées dans une seule requête :
|
|
315
444
|
|
|
316
445
|
```js
|
|
317
|
-
const
|
|
318
|
-
|
|
319
|
-
const history = await ProjectPipeline.find({
|
|
446
|
+
const stats = await User.find({
|
|
320
447
|
select: [
|
|
321
|
-
|
|
322
|
-
{
|
|
323
|
-
{
|
|
448
|
+
'status',
|
|
449
|
+
{ count: 'id', as: 'total' },
|
|
450
|
+
{ sum: 'amount', as: 'revenue' },
|
|
324
451
|
],
|
|
325
452
|
where: {
|
|
326
|
-
|
|
453
|
+
active: true,
|
|
327
454
|
},
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
type: 'LEFT'
|
|
455
|
+
groupBy: ['status'],
|
|
456
|
+
having: {
|
|
457
|
+
total: { '>': 10 },
|
|
332
458
|
},
|
|
333
|
-
groupBy: [
|
|
334
|
-
'project_name',
|
|
335
|
-
{ dateFormat: ['ProjectPipelines.created_at', '%Y-%m'] }
|
|
336
|
-
],
|
|
337
459
|
orderBy: [
|
|
338
|
-
{ field: '
|
|
339
|
-
{ field: 'project_name', direction: 'ASC' }
|
|
460
|
+
{ field: 'total', direction: 'DESC' },
|
|
340
461
|
],
|
|
341
|
-
limit: 50
|
|
342
462
|
});
|
|
343
463
|
```
|
|
344
464
|
|
|
345
|
-
|
|
465
|
+
Ce flux correspond à `SELECT` → `WHERE` → `GROUP BY` → `HAVING` → `ORDER BY`. Les valeurs de `where` et `having` sont paramétrées par le Connector.
|
|
346
466
|
|
|
347
|
-
|
|
467
|
+
---
|
|
348
468
|
|
|
349
|
-
|
|
350
|
-
- **Returns** `Promise<ModelInstance|number>` - Une promesse qui se résout en une instance `ModelInstance` si un enregistrement est trouvé, ou en `0` si aucun enregistrement ne correspond au filtre.
|
|
469
|
+
## Jointures
|
|
351
470
|
|
|
352
|
-
|
|
471
|
+
La couche `Model.find()` génère actuellement une `INNER JOIN` à partir de `table` et `on` :
|
|
353
472
|
|
|
354
473
|
```js
|
|
355
|
-
|
|
474
|
+
join: {
|
|
475
|
+
table: 'profiles',
|
|
476
|
+
on: 'users.id = profiles.user_id',
|
|
477
|
+
}
|
|
478
|
+
```
|
|
479
|
+
|
|
480
|
+
L'expression `on` doit être considérée comme du SQL de confiance.
|
|
481
|
+
|
|
482
|
+
---
|
|
483
|
+
|
|
484
|
+
## Synchronisation
|
|
356
485
|
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
})
|
|
486
|
+
```js
|
|
487
|
+
await Model.syncAllTables();
|
|
360
488
|
```
|
|
361
489
|
|
|
362
|
-
|
|
490
|
+
La synchronisation crée les tables dans l'ordre des dépendances de clés étrangères et détecte les cycles.
|
|
363
491
|
|
|
364
|
-
|
|
492
|
+
Pour les évolutions contrôlées du schéma, utilisez les migrations.
|
|
365
493
|
|
|
366
|
-
|
|
367
|
-
- **Returns** `Promise<void>` Valeur retournée
|
|
368
|
-
- **Throws** `Error` Lève une erreur si l'exécution de la requête échoue.
|
|
494
|
+
---
|
|
369
495
|
|
|
370
|
-
##
|
|
496
|
+
## Instances de modèle
|
|
371
497
|
|
|
372
498
|
```js
|
|
373
|
-
const
|
|
499
|
+
const [user] = await User.find({
|
|
500
|
+
where: { email: 'user@example.com' },
|
|
501
|
+
});
|
|
374
502
|
|
|
375
|
-
await
|
|
376
|
-
|
|
377
|
-
WHERE status IN ('active', 'pending')
|
|
378
|
-
AND email LIKE '%gmail.com';")
|
|
503
|
+
await user.updateOne({ status: 'inactive' });
|
|
504
|
+
await user.delete();
|
|
379
505
|
```
|
|
380
506
|
|
|
381
|
-
|
|
507
|
+
Méthodes : `updateOne`, `delete`, `customRequest`, `getRecordData`, `toJSON`.
|
|
382
508
|
|
|
383
|
-
|
|
509
|
+
---
|
|
384
510
|
|
|
385
|
-
|
|
386
|
-
- **Returns** `Promise<number>` Une promesse qui se résout à 0 si aucune ligne n'a été supprimée, ou à une instance de modèle représentant la ligne supprimée.
|
|
387
|
-
- **Throws** `Error` Une promesse qui se résout à 0 si aucune ligne n'a été supprimée, ou à une instance de modèle représentant la ligne supprimée.
|
|
511
|
+
## Migrations
|
|
388
512
|
|
|
389
|
-
|
|
513
|
+
Le système de migration compare les modèles souhaités au schéma réellement présent dans la base, produit un plan puis génère le SQL adapté au dialecte.
|
|
390
514
|
|
|
391
515
|
```js
|
|
392
|
-
const
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
516
|
+
const {
|
|
517
|
+
Migration,
|
|
518
|
+
MigrationPlan,
|
|
519
|
+
MigrationStore,
|
|
520
|
+
MigrationRunner,
|
|
521
|
+
SchemaDiffer,
|
|
522
|
+
SchemaInspector,
|
|
523
|
+
} = require('@mlagie/sql-connector');
|
|
397
524
|
```
|
|
398
525
|
|
|
399
|
-
|
|
526
|
+
### Migration Workflow
|
|
400
527
|
|
|
401
|
-
|
|
528
|
+
```js
|
|
529
|
+
const migration = new Migration({
|
|
530
|
+
encryptionKey: process.env.SQL_CONNECTOR_MIGRATION_KEY,
|
|
531
|
+
});
|
|
402
532
|
|
|
403
|
-
|
|
404
|
-
|
|
533
|
+
const plan = await migration.plan({
|
|
534
|
+
name: 'users-schema',
|
|
535
|
+
});
|
|
405
536
|
|
|
406
|
-
|
|
537
|
+
console.log(plan.toJSON());
|
|
407
538
|
|
|
408
|
-
|
|
539
|
+
if (plan.hasChanges()) {
|
|
540
|
+
plan.approve();
|
|
541
|
+
await plan.execute();
|
|
542
|
+
}
|
|
543
|
+
```
|
|
409
544
|
|
|
410
|
-
|
|
545
|
+
Ou directement :
|
|
411
546
|
|
|
412
547
|
```js
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
548
|
+
await Migration.migrate({
|
|
549
|
+
name: 'users-schema',
|
|
550
|
+
encryptionKey: process.env.SQL_CONNECTOR_MIGRATION_KEY,
|
|
551
|
+
});
|
|
416
552
|
```
|
|
417
553
|
|
|
418
|
-
|
|
554
|
+
### Migration Options
|
|
555
|
+
|
|
556
|
+
| Option | Description |
|
|
557
|
+
|---|---|
|
|
558
|
+
| `connection` | Connexion existante. |
|
|
559
|
+
| `dialect` | `mysql`, `postgres` ou instance de dialecte. |
|
|
560
|
+
| `models` | Modèles à comparer. |
|
|
561
|
+
| `directory` | Dossier des migrations. |
|
|
562
|
+
| `backupDirectory` | Dossier des sauvegardes. |
|
|
563
|
+
| `encryptionKey` | Clé de chiffrement. |
|
|
564
|
+
| `retention` | Durée de conservation (`ms`, `s`, `m`, `h`, `d`, `w`). |
|
|
565
|
+
| `allowDestructiveChanges` | Autorise les suppressions de colonnes. |
|
|
566
|
+
| `allowDropTables` | Autorise les suppressions de tables. |
|
|
419
567
|
|
|
420
|
-
|
|
568
|
+
### Migration Opérations
|
|
421
569
|
|
|
422
|
-
|
|
570
|
+
Le diff peut générer :
|
|
423
571
|
|
|
424
|
-
|
|
572
|
+
- `createTable` ;
|
|
573
|
+
- `addColumn` ;
|
|
574
|
+
- `renameColumn` ;
|
|
575
|
+
- `alterColumn` ;
|
|
576
|
+
- `dropColumn` — destructif ;
|
|
577
|
+
- `dropTable` — destructif.
|
|
425
578
|
|
|
426
|
-
|
|
427
|
-
- **Returns** `Promise<string|null>` Une promesse qui se résout en une chaîne UUID unique en cas de succès, ou en `null` si une erreur survient ou si l'UUID n'est pas unique.
|
|
428
|
-
- **Throws** `Error` S'il y a une erreur lors de l'exécution de la requête SQL_request.
|
|
579
|
+
### Migration Renommage
|
|
429
580
|
|
|
430
|
-
|
|
581
|
+
Le renommage doit être explicite avec `oldname` :
|
|
431
582
|
|
|
432
583
|
```js
|
|
433
|
-
|
|
584
|
+
email_address: {
|
|
585
|
+
type: String,
|
|
586
|
+
length: 320,
|
|
587
|
+
oldname: 'email',
|
|
588
|
+
}
|
|
589
|
+
```
|
|
590
|
+
|
|
591
|
+
### Changements destructifs
|
|
592
|
+
|
|
593
|
+
Par défaut, la suppression d'une colonne ou d'une table est bloquée.
|
|
434
594
|
|
|
435
|
-
|
|
436
|
-
const
|
|
595
|
+
```js
|
|
596
|
+
const plan = await migration.plan({
|
|
597
|
+
allowDestructiveChanges: true,
|
|
598
|
+
allowDropTables: true,
|
|
599
|
+
});
|
|
437
600
|
|
|
438
|
-
|
|
601
|
+
plan.approve();
|
|
602
|
+
await plan.execute({
|
|
603
|
+
allowDestructiveChanges: true,
|
|
604
|
+
});
|
|
439
605
|
```
|
|
440
606
|
|
|
441
|
-
|
|
607
|
+
L'approbation et l'autorisation destructive sont deux protections distinctes.
|
|
608
|
+
|
|
609
|
+
### Checksum et historique
|
|
610
|
+
|
|
611
|
+
Le runner crée `sql_connector_migrations` et enregistre l'identifiant, le checksum SHA-256 et les dates d'application/création.
|
|
442
612
|
|
|
443
|
-
|
|
613
|
+
Un identifiant déjà appliqué avec un checksum différent provoque une erreur.
|
|
444
614
|
|
|
445
|
-
|
|
446
|
-
- `delete(model)` supprime la ligne avec un filtre
|
|
447
|
-
- `delete()` supprime la ligne de l'instance
|
|
448
|
-
- `customRequest(custom)` exécute une requête personnalisée
|
|
615
|
+
### Chiffrement
|
|
449
616
|
|
|
450
|
-
|
|
451
|
-
const userInstance = new ModelInstance('users', { email: 'user@example.com' });
|
|
617
|
+
Les migrations sont écrites en `.sql.enc`.
|
|
452
618
|
|
|
453
|
-
|
|
454
|
-
|
|
619
|
+
```bash
|
|
620
|
+
export SQL_CONNECTOR_MIGRATION_KEY='un-secret-long-et-aleatoire'
|
|
455
621
|
```
|
|
456
622
|
|
|
457
|
-
|
|
623
|
+
Le format utilise AES-256-GCM et une clé dérivée.
|
|
458
624
|
|
|
459
|
-
|
|
625
|
+
### Sauvegardes
|
|
460
626
|
|
|
461
|
-
|
|
462
|
-
|
|
627
|
+
Avec `retention`, les tables affectées sont sauvegardées avant la migration dans des fichiers JSON chiffrés.
|
|
628
|
+
|
|
629
|
+
```js
|
|
630
|
+
new Migration({
|
|
631
|
+
encryptionKey: process.env.SQL_CONNECTOR_MIGRATION_KEY,
|
|
632
|
+
retention: '30d',
|
|
633
|
+
});
|
|
463
634
|
```
|
|
464
635
|
|
|
465
|
-
|
|
636
|
+
La sauvegarde est un snapshot de données et ne constitue pas un rollback automatique.
|
|
466
637
|
|
|
467
|
-
|
|
638
|
+
### Statut et nettoyage
|
|
468
639
|
|
|
469
|
-
```
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
return 1;
|
|
474
|
-
}
|
|
640
|
+
```js
|
|
641
|
+
const migration = new Migration();
|
|
642
|
+
const status = await migration.status();
|
|
643
|
+
console.table(status);
|
|
475
644
|
|
|
476
|
-
|
|
477
|
-
};
|
|
478
|
-
};
|
|
645
|
+
await migration.cleanup();
|
|
479
646
|
```
|
|
480
647
|
|
|
481
|
-
|
|
648
|
+
---
|
|
649
|
+
|
|
650
|
+
## TypeScript
|
|
651
|
+
|
|
652
|
+
Le package fournit `index.d.ts` avec les types de schéma, conditions, requêtes, modèles et migrations.
|
|
653
|
+
|
|
654
|
+
```ts
|
|
655
|
+
import {
|
|
656
|
+
Schema,
|
|
657
|
+
Model,
|
|
658
|
+
Migration,
|
|
659
|
+
InferSchema,
|
|
660
|
+
} from '@mlagie/sql-connector';
|
|
661
|
+
```
|
|
662
|
+
|
|
663
|
+
---
|
|
664
|
+
|
|
665
|
+
## Sécurité SQL
|
|
666
|
+
|
|
667
|
+
Le builder structuré sépare les identifiants SQL des valeurs :
|
|
668
|
+
|
|
669
|
+
- valeurs `WHERE` paramétrées ;
|
|
670
|
+
- valeurs `HAVING` paramétrées ;
|
|
671
|
+
- éléments de `IN` / `NOT IN` paramétrés ;
|
|
672
|
+
- bornes de `BETWEEN` paramétrées ;
|
|
673
|
+
- placeholders PostgreSQL numérotés ;
|
|
674
|
+
- échappement des identifiants ;
|
|
675
|
+
- refus des `WHERE` / `HAVING` sous forme de chaînes brutes.
|
|
676
|
+
|
|
677
|
+
`customRequest()` reste volontairement une porte d'accès au SQL brut et doit recevoir uniquement du SQL de confiance.
|
|
678
|
+
|
|
679
|
+
---
|
|
680
|
+
|
|
681
|
+
## API exportée
|
|
682
|
+
|
|
683
|
+
```text
|
|
684
|
+
client
|
|
685
|
+
connect
|
|
686
|
+
logout
|
|
687
|
+
Schema
|
|
688
|
+
Model
|
|
689
|
+
ModelInstance
|
|
690
|
+
sqlTypeMap
|
|
691
|
+
Migration
|
|
692
|
+
MigrationPlan
|
|
693
|
+
MigrationStore
|
|
694
|
+
MigrationRunner
|
|
695
|
+
SchemaDiffer
|
|
696
|
+
SchemaInspector
|
|
697
|
+
MySQLDialect
|
|
698
|
+
PostgreSQLDialect
|
|
699
|
+
```
|
|
700
|
+
|
|
701
|
+
---
|
|
702
|
+
|
|
703
|
+
## Tests
|
|
704
|
+
|
|
705
|
+
```bash
|
|
706
|
+
npm install
|
|
707
|
+
npm test
|
|
708
|
+
npm run test:coverage
|
|
709
|
+
npm run security
|
|
710
|
+
```
|
|
711
|
+
|
|
712
|
+
---
|
|
713
|
+
|
|
714
|
+
## Documentation détaillée
|
|
715
|
+
|
|
716
|
+
- [Index de documentation](../README.md)
|
|
717
|
+
- [Schéma](../SCHEMA.md)
|
|
718
|
+
- [Conditions et requêtes](../QUERY.md)
|
|
719
|
+
- [Migrations](../MIGRATIONS.md)
|
|
720
|
+
- [API](../API.md)
|
|
721
|
+
- [Dialectes](../DIALECTS.md)
|
|
722
|
+
- [Contribution](../../CONTRIBUTING.md)
|
|
723
|
+
- [Sécurité](../../SECURITY.md)
|
|
724
|
+
|
|
725
|
+
## Licence
|
|
482
726
|
|
|
483
|
-
|
|
727
|
+
MIT — voir [LICENSE](../../LICENSE).
|