db-crud-wrapper 1.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.
package/CHANGELOG.md ADDED
@@ -0,0 +1,81 @@
1
+ # Changelog
2
+
3
+ ## v0.1.0 (2022-12-08)
4
+
5
+ Initial release
6
+ ## v0.1.1 (2023-01-01)
7
+
8
+ Bug fix:
9
+ - default server/database
10
+ ## v0.1.4 (2024-02-24)
11
+
12
+ Bug fix:
13
+ - update mssql to 9.3.2
14
+ - update uuid to 9.0.1
15
+ ## v0.1.5 (2024-03-03)
16
+
17
+ Bug fix:
18
+ - deleteById function crash
19
+ - new function return undefined
20
+ ## v0.1.5 (2024-03-03)
21
+ ## v0.1.6 (2024-03-03)
22
+
23
+ Bug fix:
24
+ - update function crash with date value
25
+ ## v0.1.9 (2024-03-12)
26
+
27
+ Added:
28
+ - execute store procedure
29
+ ## v0.2.0 (2024-04-02)
30
+
31
+ Changes:
32
+ - parameters position have been change for some api
33
+ Added:
34
+ - batch job that run multiple instructions
35
+ ## v0.2.1 (2024-12-05)
36
+
37
+ Refresh dependency
38
+ ## v0.3.0 (2024-12-28)
39
+
40
+ Changes:
41
+ - execute api definition
42
+ Added:
43
+ - support for mySQL
44
+ ## v0.3.1 (2025-01-01)
45
+
46
+ Bug fix:
47
+ - autoId flag not onored
48
+ ## v0.3.11 (2025-10-25)
49
+
50
+ Added:
51
+ - testConnection()
52
+ ## v0.3.13 (2025-11-04)
53
+
54
+ Bug fix:
55
+ - mssql
56
+ ## v0.3.14 (2025-11-08)
57
+
58
+ Bug fix:
59
+ - getByFilters can return an object instead of array
60
+ ## v0.3.15 (2025-11-10)
61
+
62
+ Added:
63
+ - config
64
+ - log via callback
65
+ ## v0.3.18 (2025-12-12)
66
+
67
+ Added:
68
+ - sessionStore
69
+ ## v0.3.24 (2025-12-21)
70
+
71
+ Added:
72
+ - appLog patameter
73
+ - delById(id, Ope)
74
+ ## v0.3.30 (2026-01-06)
75
+
76
+ Bug fix:
77
+ - use timezone: 'Z' in mysql connection
78
+ ## v1.0.1 (2026-05-17)
79
+
80
+ Changes:
81
+ - adopt fluent sintax: get('table1').byId('001').execute()
package/README.md ADDED
@@ -0,0 +1,96 @@
1
+ # db-crud-wrapper
2
+ CRUD api for database tables.
3
+
4
+ ## Installation
5
+ npm install db-crud-wrapper
6
+
7
+ # Usage
8
+
9
+ ## Fluent API
10
+ ```javascript
11
+ import dbw from "db-crud-wrapper";
12
+
13
+ // define server and default database
14
+ const myDB = dbw.server("server1")
15
+ .user("myUser")
16
+ .password("myUserPassword")
17
+ .type(my-sql) // my-sql, ms-sql, ...
18
+ .database("db1"); // .database() must be the last metod
19
+
20
+ // NOTE: the fluent API returns operation-specific builders.
21
+ // Example: .top() exists only on get queries, so calling it on put/patch/delete is not possible.
22
+ const myObj = dbw
23
+ .get("table1") // table name
24
+ .fields("Id", "OrderNumber") // fields
25
+ .top(10)
26
+ .filters("OrderType in ('P', 'A')", "and", "OrderDate > '2022-12-01'") // can be: single string, multiple params, or array
27
+ .orderby("OrderDate desc"); // can be: single string, multiple params, or array
28
+ const myResult = await myObj.execute();
29
+
30
+ const myResult = await dbw
31
+ .get("db2.table2")
32
+ .fields("Id", "OrderNumber")
33
+ .byId("xxxxx-xxxxx-xxxxxxxxxxxx-xxxxxx")
34
+ .orderby("OrderDate desc")
35
+ .execute();
36
+
37
+ // insert
38
+ const myResult = await dbw
39
+ .post("table2")
40
+ .values({ OrderId: "xxxxx", RowSeq: 1 })
41
+ .execute();
42
+
43
+ // update
44
+ const myResult = await dbw
45
+ .patch("table2")
46
+ .values({ ItemRef: "2024-TX-0001" })
47
+ .byId("xxxxx")
48
+ .execute();
49
+ const myResult = await dbw
50
+ .patch("db2.table2")
51
+ .values({ ItemRef: "2024-TX-0001" })
52
+ .filters("OrderId = 'xxxxx'")
53
+ .execute();
54
+
55
+ // upsert
56
+ const myResult = await dbw
57
+ .put("table2")
58
+ .values({ OrderId: "xxxxx", RowSeq: 1 })
59
+ .byId("xxxxx")
60
+ .execute();
61
+
62
+ // delete
63
+ const myReult = await dbw
64
+ .del("table2")
65
+ .byId("xxxxx")
66
+ .execute();
67
+ const myReult = await dbw
68
+ .del("db2.table2")
69
+ .filters("OrderId = 'xxxxx'")
70
+ .execute();
71
+ ```
72
+
73
+ License
74
+ -------
75
+
76
+ MIT License
77
+
78
+ Copyright (c) 2022
79
+
80
+ Permission is hereby granted, free of charge, to any person obtaining a copy
81
+ of this software and associated documentation files (the "Software"), to deal
82
+ in the Software without restriction, including without limitation the rights
83
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
84
+ copies of the Software, and to permit persons to whom the Software is
85
+ furnished to do so, subject to the following conditions:
86
+
87
+ The above copyright notice and this permission notice shall be included in all
88
+ copies or substantial portions of the Software.
89
+
90
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
91
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
92
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
93
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
94
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
95
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
96
+ SOFTWARE.
package/index.js ADDED
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+
3
+ import * as factory from "./lib/factory.js";
4
+
5
+ export default factory;
package/lib/builder.js ADDED
@@ -0,0 +1,404 @@
1
+ 'use strict';
2
+
3
+ import * as f from './f.js';
4
+ import * as dbOpe from './db-operations.js';
5
+ import { config } from './config.js';
6
+
7
+
8
+ function normalizeToArray(...values) {
9
+ const result = [];
10
+ values.forEach((value) => {
11
+ if (Array.isArray(value)) result.push(...value.filter((v) => v !== undefined && v !== null && v !== ''));
12
+ else if (value !== undefined && value !== null && value !== '') result.push(value);
13
+ });
14
+ return result;
15
+ }
16
+
17
+ function isQueryObject(obj) {
18
+ if (typeof obj !== 'object') throw new Error('Object not valid.');
19
+ if (!obj._operation || !obj._request[obj._operation]) throw new Error('Object not valid, missing operation get/post/put/patch/delete/...');
20
+ return true
21
+ }
22
+
23
+ export function parseObject(obj) {
24
+ if (isQueryObject(obj)) {
25
+ switch (obj._operation) {
26
+ case 'command':
27
+ return new CommandBuilder(obj)
28
+ case 'get':
29
+ return new GetBuilder(obj)
30
+ case 'post':
31
+ return new PostBuilder(obj)
32
+ case 'put':
33
+ return new PutBuilder(obj)
34
+ case 'patch':
35
+ return new PatchBuilder(obj)
36
+ case 'delete':
37
+ return new DeleteBuilder(obj)
38
+ case 'begin':
39
+ return new BeginBuilder(obj)
40
+ case 'commit':
41
+ return new CommitBuilder(obj)
42
+ case 'batch':
43
+ return new BatchBuilder(obj)
44
+ case 'passthrough':
45
+ return new PassthroughBuilder(obj)
46
+ default:
47
+ throw new Error('Operation not valid, must be one of get/post/put/patch/delete/...');
48
+ }
49
+ }
50
+ }
51
+
52
+ class BaseQueryBuilder {
53
+
54
+ constructor(tableOrCommand, operation) {
55
+
56
+ if (operation === undefined || operation === null || operation === '')
57
+ throw new Error('Missing operation get/post/put/patch/delete/...');
58
+ this._operation = operation;
59
+
60
+ this._request = {};
61
+ if (typeof tableOrCommand === 'object' && tableOrCommand._request && tableOrCommand._request[this._operation])
62
+ this._request[this._operation] = structuredClone(tableOrCommand._request[this._operation]);
63
+ else
64
+ this._request[this._operation] = {};
65
+
66
+ if (typeof tableOrCommand === 'object')
67
+ this._tableOrCommand = tableOrCommand._tableOrCommand
68
+ else
69
+ this._tableOrCommand = tableOrCommand
70
+ if (!['begin', 'commit', 'rollback', 'batch', 'passthrough'].includes(this._operation)) {
71
+ if (this._tableOrCommand === undefined || this._tableOrCommand === null || this._tableOrCommand === '')
72
+ throw new Error('Missing table or command.');
73
+ }
74
+
75
+ }
76
+
77
+ #getSchema(operation, tableOrCommand) {
78
+ switch (operation) {
79
+ case 'command':
80
+ return dbOpe.prepareSchema(tableOrCommand, dbOpe.objectType.procedure)
81
+ case 'get':
82
+ case 'post':
83
+ case 'put':
84
+ case 'patch':
85
+ case 'delete':
86
+ return dbOpe.prepareSchema(tableOrCommand, dbOpe.objectType.table)
87
+ default:
88
+ return dbOpe.prepareSchema(undefined, undefined);
89
+ }
90
+ }
91
+
92
+ params(params) {
93
+ if (!params) return this;
94
+ if (!this._operation || !this._request[this._operation]) throw new Error('Missing operation type get/post/put/patch/delete/...');
95
+ this._request[this._operation].params = params;
96
+ return this;
97
+ }
98
+
99
+ log(log) {
100
+ this._request.appLog = log;
101
+ return this;
102
+ }
103
+
104
+ toObject() {
105
+ return {
106
+ _operation: this._operation,
107
+ _tableOrCommand: this._tableOrCommand,
108
+ _request: structuredClone(this._request),
109
+ };
110
+ }
111
+
112
+ async execute(rawResult = false) {
113
+
114
+ // some check
115
+ if (!this._operation || !this._request[this._operation])
116
+ throw new Error('Missing operation get/post/put/patch/delete/...');
117
+ if (!['get', 'delete', 'patch', 'put', 'post', 'command', 'batch', 'passthrough'].includes(this._operation))
118
+ throw new Error(`Operation '${this._operation}' not supported.`);
119
+ if (!this._tableOrCommand && (['get', 'delete', 'patch', 'put', 'post', 'command'].includes(this._operation)))
120
+ throw new Error('Missing table or command.');
121
+ if ((this._operation === 'patch' || this._operation === 'put' || this._operation === 'post') && !this._request[this._operation]?.values) {
122
+ throw new Error('Missing values property. Use .values({...}) before execute().');
123
+ }
124
+
125
+ // prepare
126
+ let dbOpeReq = undefined; // can be an array or a string
127
+ if (this._operation === 'batch') {
128
+ dbOpeReq = this._request[this._operation].items.map((_item) => {
129
+ const _schema = this.#getSchema(_item._operation, _item._tableOrCommand)
130
+ const _connection = dbOpe.prepareConnection(_schema);
131
+ return dbOpe.prepareRun(_schema, _connection, { ..._item._request });
132
+ });
133
+ }
134
+ else if (this._operation === 'passthrough') {
135
+ const _schema = this.#getSchema(undefined, undefined)
136
+ const _connection = dbOpe.prepareConnection(_schema);
137
+ dbOpeReq = dbOpe.prepareRun(_schema, _connection, { ...this._request });
138
+ }
139
+ else {
140
+ const _schema = this.#getSchema(this._operation, this._tableOrCommand)
141
+ const _connection = dbOpe.prepareConnection(_schema);
142
+ dbOpeReq = dbOpe.prepareRun(_schema, _connection, { ...this._request });
143
+ }
144
+
145
+ // run (dbOpeReq should be an array or a string)
146
+ const _result = await dbOpe.runQuery(dbOpeReq, rawResult);
147
+
148
+ // if get.byId return a single object
149
+ if (!rawResult && this._operation === 'get' && this._request[this._operation].byId) {
150
+ return Array.isArray(_result) ? _result[0] : _result;
151
+ }
152
+
153
+ return _result;
154
+
155
+ }
156
+
157
+ }
158
+
159
+ export class GetBuilder extends BaseQueryBuilder {
160
+ constructor(nameOrObject) {
161
+ super(nameOrObject, 'get');
162
+ }
163
+
164
+ fields(...fields) {
165
+ const normalizedFields = normalizeToArray(...fields);
166
+ if (normalizedFields.length === 0) this._request[this._operation].fields = undefined
167
+ else this._request[this._operation].fields = normalizedFields;
168
+ return this;
169
+ }
170
+
171
+ top(value) {
172
+ if (value === undefined || value === null || value === '') return this;
173
+ this._request[this._operation].options = `top ${value}`;
174
+ return this;
175
+ }
176
+
177
+ filters(...filters) {
178
+ const normalizedFilters = normalizeToArray(...filters);
179
+ if (normalizedFilters.length === 0) this._request[this._operation].filters = undefined
180
+ else this._request[this._operation].filters = normalizedFilters;
181
+ return this;
182
+ }
183
+
184
+ byId(idValue) {
185
+ this._request[this._operation].byId = idValue;
186
+ return this;
187
+ }
188
+
189
+ groupBy(...fields) {
190
+ const normalizedFields = normalizeToArray(...fields);
191
+ if (normalizedFields.length === 0) this._request[this._operation].groupBy = undefined
192
+ else this._request[this._operation].groupBy = normalizedFields;
193
+ return this;
194
+ }
195
+
196
+ groupFilters(...fields) {
197
+ const normalizedFields = normalizeToArray(...fields);
198
+ if (normalizedFields.length === 0) this._request[this._operation].groupFilters = undefined
199
+ else this._request[this._operation].groupFilters = normalizedFields;
200
+ return this;
201
+ }
202
+
203
+ orderBy(...fields) {
204
+ const normalizedFields = normalizeToArray(...fields);
205
+ if (normalizedFields.length === 0) this._request[this._operation].orderBy = undefined
206
+ else this._request[this._operation].orderBy = normalizedFields;
207
+ return this;
208
+ }
209
+ }
210
+
211
+ // post add a new record
212
+ export class PostBuilder extends BaseQueryBuilder {
213
+ constructor(tablePath) {
214
+ super(tablePath, 'post');
215
+ }
216
+
217
+ values(values) {
218
+ this._request[this._operation].values = values;
219
+ return this;
220
+ }
221
+
222
+ byId(idValue) {
223
+ this._request[this._operation].byId = idValue;
224
+ return this;
225
+ }
226
+
227
+ }
228
+
229
+ // put try to update, if no records effected and byId is requested, then add a new record
230
+ export class PutBuilder extends BaseQueryBuilder {
231
+ constructor(tablePath) {
232
+ super(tablePath, 'put');
233
+ }
234
+
235
+ values(values) {
236
+ this._request[this._operation].values = values;
237
+ return this;
238
+ }
239
+
240
+ filters(...filters) {
241
+ const normalizedFilters = normalizeToArray(...filters);
242
+ if (normalizedFilters.length === 0) this._request[this._operation].filters = undefined
243
+ else this._request[this._operation].filters = normalizedFilters;
244
+ return this;
245
+ }
246
+
247
+ byId(idValue) {
248
+ this._request[this._operation].byId = idValue;
249
+ return this;
250
+ }
251
+
252
+ async execute(rawResult = false) {
253
+ // try to update
254
+ let _result = await super.execute(rawResult);
255
+
256
+ // if no records effected and byId is requested, then try to insert
257
+ if (_result === 0 && (this._request[this._operation].byId ?? '') !== '') {
258
+ _result = await new PostBuilder(this._tableOrCommand)
259
+ .values(this._request[this._operation].values)
260
+ .log(this._request.appLog)
261
+ .byId(this._request[this._operation].byId)
262
+ .execute(rawResult);
263
+ }
264
+
265
+ return _result;
266
+ }
267
+
268
+ }
269
+
270
+ // patch update the record/s
271
+ export class PatchBuilder extends BaseQueryBuilder {
272
+ constructor(tablePath) {
273
+ super(tablePath, 'patch');
274
+ }
275
+
276
+ values(values) {
277
+ this._request[this._operation].values = values;
278
+ return this;
279
+ }
280
+
281
+ filters(...filters) {
282
+ const normalizedFilters = normalizeToArray(...filters);
283
+ if (normalizedFilters.length === 0) this._request[this._operation].filters = undefined
284
+ else this._request[this._operation].filters = normalizedFilters;
285
+ return this;
286
+ }
287
+
288
+ byId(idValue) {
289
+ this._request[this._operation].byId = idValue;
290
+ return this;
291
+ }
292
+ }
293
+
294
+ // remove record/s
295
+ export class DeleteBuilder extends BaseQueryBuilder {
296
+ constructor(tablePath) {
297
+ super(tablePath, 'delete');
298
+ }
299
+
300
+ filters(...filters) {
301
+ const normalizedFilters = normalizeToArray(...filters);
302
+ if (normalizedFilters.length === 0) this._request[this._operation].filters = undefined
303
+ else this._request[this._operation].filters = normalizedFilters;
304
+ return this;
305
+ }
306
+
307
+ byId(idValue) {
308
+ this._request[this._operation].byId = idValue;
309
+ return this;
310
+ }
311
+ }
312
+
313
+ // exec/call a store procedure
314
+ export class CommandBuilder extends BaseQueryBuilder {
315
+
316
+ constructor(command) {
317
+ super(command, 'command');
318
+ }
319
+
320
+ arguments(...cmdArguments) {
321
+ const normalizedArguments = normalizeToArray(...cmdArguments);
322
+ if (normalizedArguments.length === 0) this._request[this._operation].arguments = undefined
323
+ else this._request[this._operation].arguments = normalizedArguments;
324
+ return this;
325
+ }
326
+
327
+ }
328
+
329
+ // begin transaction
330
+ class BeginBuilder extends BaseQueryBuilder {
331
+ constructor() {
332
+ super(undefined, 'begin');
333
+ }
334
+ }
335
+
336
+ // commit transaction
337
+ class CommitBuilder extends BaseQueryBuilder {
338
+ constructor() {
339
+ super(undefined, 'commit');
340
+ }
341
+ }
342
+
343
+ // rollback transaction
344
+ class RollbackBuilder extends BaseQueryBuilder {
345
+ constructor() {
346
+ super(undefined, 'rollback');
347
+ }
348
+ }
349
+
350
+ // passthrough, run a raw query
351
+ export class PassthroughBuilder extends BaseQueryBuilder {
352
+ constructor(command) {
353
+ // for passthrough don't set tableOrCommand but put command in the request.command property
354
+ if (typeof command === 'object')
355
+ super(command, 'passthrough')
356
+ else
357
+ super(undefined, 'passthrough')
358
+ this._request[this._operation].command = command;
359
+ }
360
+ }
361
+
362
+ // batch is an array of operations (get/put/post/...)
363
+ export class BatchBuilder extends BaseQueryBuilder {
364
+
365
+ constructor() {
366
+ super(undefined, 'batch');
367
+ this._request[this._operation].items = [];
368
+ this._request[this._operation].useTransaction = false;
369
+ }
370
+
371
+ useTransaction(useTransaction) {
372
+ if (typeof(useTransaction) === "boolean")
373
+ this._request[this._operation].useTransaction = useTransaction;
374
+ return this;
375
+ }
376
+
377
+ // run batch
378
+ async execute(rawResult = false) {
379
+
380
+ if (this._request[this._operation].items.length == 0)
381
+ throw new Error("There are not operations in this batch.");
382
+
383
+ // prepare transaction
384
+ if (this._request[this._operation].useTransaction) {
385
+ this._request[this._operation].items.unshift(new BeginBuilder().toObject());
386
+ this._request[this._operation].items.push(new CommitBuilder().toObject());
387
+ }
388
+
389
+ // run
390
+ // const _return = this._request[this._operation].items;
391
+ return await super.execute(rawResult);
392
+
393
+ }
394
+
395
+ // add operations
396
+ add(...obj) {
397
+ for (const _obj of obj) {
398
+ if (isQueryObject(_obj))
399
+ this._request[this._operation].items.push(structuredClone(_obj));
400
+ }
401
+ return this;
402
+ }
403
+
404
+ }