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.
@@ -0,0 +1,426 @@
1
+ 'use strict';
2
+
3
+ // import modules
4
+ import { v4 as uuidv4 } from 'uuid';
5
+ import { config } from './config.js';
6
+ import * as f from './f.js';
7
+ import * as mssql from './mssql.js';
8
+ import * as mysql from './mysql.js';
9
+
10
+ // Common
11
+ const defautlFields = ['*'];
12
+ export const serverType = Object.freeze({ mssql: 'ms-sql', mysql: 'my-sql', mongodb: 'mongo-db' });
13
+ export const objectType = Object.freeze({ table: 'T', procedure: 'P' });
14
+
15
+ // Exported functions
16
+ export function idField(tSchema) {
17
+ return tSchema.table?.idField ?? 'id';
18
+ }
19
+
20
+ export function autoId(tSchema) {
21
+ return tSchema.table?.autoId;
22
+ }
23
+
24
+ export function getProperty(obj, keyToFind) {
25
+ if (typeof obj !== 'object' || obj === null || !keyToFind) {
26
+ return undefined;
27
+ }
28
+
29
+ const lowerKeyToFind = keyToFind.toLowerCase();
30
+
31
+ for (const key in obj) {
32
+ // Usa hasOwnProperty per escludere le proprietà ereditate
33
+ if (Object.prototype.hasOwnProperty.call(obj, key)) {
34
+
35
+ if (key.toLowerCase() === lowerKeyToFind) {
36
+ return obj[key];
37
+ }
38
+ }
39
+ }
40
+
41
+ return undefined;
42
+ }
43
+
44
+ function normalizeReqOperations(reqOpe) {
45
+ if (Array.isArray(reqOpe)) return reqOpe;
46
+ if (reqOpe === undefined || reqOpe === null) return [];
47
+ return [reqOpe];
48
+ }
49
+
50
+ function denormalizePreparedOperations(prepared, wasArrayInput) {
51
+ if (wasArrayInput) return prepared;
52
+ return prepared.length > 0 ? prepared[0] : undefined;
53
+ }
54
+
55
+ function mergeByIdFilters(filters, idFilter) {
56
+ if (!filters || (Array.isArray(filters) && filters.length === 0)) return [idFilter];
57
+ if (Array.isArray(filters)) return [idFilter, 'and', ...filters];
58
+ return [idFilter, 'and', filters];
59
+ }
60
+
61
+ export async function runQuery(dbOpes, rawResult = false) {
62
+
63
+ // all connections must have same id
64
+ const _connection = Array.isArray(dbOpes) ? dbOpes[0].connection : dbOpes.connection;
65
+ if (Array.isArray(dbOpes)) {
66
+ for (let i = 1; i < dbOpes.length; i++) {
67
+ if (dbOpes[i].connection?.id !== _connection.id) throw new TypeError('runQuery: for multiple operations, connection must be the same');
68
+ }
69
+ }
70
+
71
+ // MSSQL
72
+ if (_connection.serverType === serverType.mssql) {
73
+ return await mssql.query(_connection, dbOpes, rawResult);
74
+ }
75
+
76
+ // MySQL
77
+ else if (_connection.serverType === serverType.mysql) {
78
+ return await mysql.query(_connection, dbOpes, rawResult);
79
+ }
80
+
81
+ // server type not supported
82
+ else throw new TypeError('server type not supported');
83
+
84
+ }
85
+
86
+ export function prepareConnection(schema) {
87
+ // MySQL
88
+ if (schema.server.type === serverType.mysql) {
89
+ const _return = mysql.prepareConnection(schema);
90
+ _return.serverType = serverType.mysql; // append 'serverType' to connection
91
+ return _return;
92
+ }
93
+ // MSSQL is also the default
94
+ if (!schema.server.type || schema.server.type === serverType.mssql) {
95
+ const _return = mssql.prepareConnection(schema);
96
+ _return.serverType = serverType.mssql; // append 'serverType' to connection
97
+ return _return;
98
+ }
99
+ throw new TypeError('server type not supported');
100
+ }
101
+
102
+ export async function closeConnection(connection) {
103
+ // MSSQL
104
+ if (connection.serverType === serverType.mssql) { return mssql.closeConnection(connection); }
105
+ // MySQL
106
+ if (connection.serverType === serverType.mysql) { return mysql.closeConnection(connection); }
107
+ }
108
+
109
+ export async function closeAllConnections() {
110
+ // MSSQL
111
+ await mssql.closeAllConnections();
112
+ // MySQL
113
+ await mysql.closeAllConnections();
114
+
115
+ return;
116
+ }
117
+
118
+ export function testConnection() {
119
+ const _connection = prepareConnection(prepareSchema("test", objectType.procedure));
120
+ // MSSQL
121
+ if (_connection.serverType === serverType.mssql) { return mssql.testConnection(_connection); }
122
+ // MySQL
123
+ if (_connection.serverType === serverType.mysql) { return mysql.testConnection(_connection); }
124
+ // server type not supported
125
+ throw new TypeError('server type not supported');
126
+ }
127
+
128
+ export function toStringValue(value) {
129
+ if (!value) return 'null';
130
+ if (typeof value === 'string' && value.trimStart().charAt(0) === '\'') return value;
131
+ return `\'${value}\'`;
132
+ }
133
+
134
+ export function prepareSchema(obj, objType) {
135
+ // objType = must be 'objectType.*' or undefined/null
136
+ // obj = 'systemName.dbName.objName' or 'dbName.objName' or 'objName' or 'systemNme..objName or undefined/null'
137
+
138
+ if (obj && typeof obj !== 'string') throw new TypeError('prepareSchema: obj must be a string');
139
+ if (objType && objType !== objectType.table && objType !== objectType.procedure) throw new TypeError(`prepareSchema: wrong objType, must be ${objectType.join(', ')}`);
140
+ const objPath = obj ? obj.trimStart().split(' ')[0] : undefined;
141
+
142
+ let serverName = undefined;
143
+ let dbName = undefined;
144
+ let objName = undefined;
145
+
146
+ // split objPath to serverName,dbName,objName
147
+ if (objPath) {
148
+ const objPathArray = objPath.split('.');
149
+ if (objPathArray.length > 2) {
150
+ serverName = objPathArray[0]
151
+ dbName = objPathArray[1]
152
+ objName = objPathArray[2]
153
+ }
154
+ else if (objPathArray.length > 1) {
155
+ dbName = objPathArray[0]
156
+ objName = objPathArray[1]
157
+ }
158
+ else if (objPathArray.length > 0) {
159
+ objName = objPathArray[0]
160
+ }
161
+ }
162
+
163
+ // server
164
+ if (!serverName || serverName.length == 0) {
165
+ if (!config.servers || !Object.keys(config.servers)[0]) throw new TypeError('missing default server.');
166
+ serverName = Object.keys(config.servers)[0];
167
+ }
168
+ else { // add server to schema
169
+ if (!config.servers) { config.servers = {} };
170
+ if (!config.servers[serverName]) { config.servers[serverName] = {} };
171
+ }
172
+ if (!config.servers[serverName].realName) { config.servers[serverName].realName = serverName } // add realName
173
+
174
+ // database
175
+ if (!dbName || dbName.length == 0) {
176
+ if (!config.servers[serverName].databases || !Object.keys(config.servers[serverName].databases)[0]) throw new TypeError('missing default database.');
177
+ dbName = Object.keys(config.servers[serverName].databases)[0];
178
+ }
179
+ else { // add db to schema
180
+ if (!config.servers[serverName].databases) { config.servers[serverName].databases = {} };
181
+ if (!config.servers[serverName].databases[dbName]) { config.servers[serverName].databases[dbName] = {} };
182
+ }
183
+ if (!config.servers[serverName].databases[dbName].realName) { config.servers[serverName].databases[dbName].realName = dbName } // add realName
184
+
185
+ // table
186
+ if (objType === objectType.table) {
187
+ if (!objName || objName.length == 0) {
188
+ if (!config.servers[serverName].databases[dbName] || !Object.keys(config.servers[serverName].databases[dbName].tables)[0]) throw new TypeError('missing default table in dbSchema');
189
+ objName = Object.keys(config.servers[serverName].databases[dbName].tables)[0];
190
+ }
191
+ else { // add table to schema
192
+ if (!config.servers[serverName].databases[dbName].tables) { config.servers[serverName].databases[dbName].tables = {} };
193
+ if (!config.servers[serverName].databases[dbName].tables[objName]) { config.servers[serverName].databases[dbName].tables[objName] = {} }
194
+ }
195
+ if (!config.servers[serverName].databases[dbName].tables[objName].realName) { config.servers[serverName].databases[dbName].tables[objName].realName = objName } // add realName
196
+ }
197
+
198
+ // store procedure
199
+ if (objType === objectType.procedure) {
200
+ if (!objName || objName.length == 0) {
201
+ throw new TypeError('missing procedure name.');
202
+ }
203
+ else { // add storeprocedure to schema
204
+ if (!config.servers[serverName].databases[dbName].procedures) { config.servers[serverName].databases[dbName].procedures = {} };
205
+ if (!config.servers[serverName].databases[dbName].procedures[objName]) { config.servers[serverName].databases[dbName].procedures[objName] = {command: obj} }
206
+ }
207
+ if (!config.servers[serverName].databases[dbName].procedures[objName].realName) { config.servers[serverName].databases[dbName].procedures[objName].realName = objName } // add realName
208
+ }
209
+
210
+ // return Schema
211
+ return {
212
+ table: (objType === objectType.table) ? config.servers[serverName].databases[dbName].tables[objName] : undefined,
213
+ procedure: (objType === objectType.procedure) ? config.servers[serverName].databases[dbName].procedures[objName] : undefined,
214
+ database: config.servers[serverName].databases[dbName],
215
+ server: config.servers[serverName]
216
+ }
217
+
218
+ }
219
+
220
+ export function prepareRun(tSchema, connection, reqOpe) {
221
+ const _isArrayInput = Array.isArray(reqOpe);
222
+ const _reqOpes = normalizeReqOperations(reqOpe);
223
+ const _result = _reqOpes.map((_ope) => {
224
+ if (_ope?.get) return prepareGet(tSchema, connection, _ope);
225
+ else if (_ope?.post) return preparePost(tSchema, connection, _ope);
226
+ else if (_ope?.patch) return preparePatch(tSchema, connection, _ope);
227
+ else if (_ope?.put) return preparePut(tSchema, connection, _ope);
228
+ else if (_ope?.delete) return prepareDelete(tSchema, connection, _ope);
229
+ else if (_ope?.command) return prepareCommand(tSchema, connection, _ope);
230
+ else if (_ope?.begin) return prepareBegin(connection, _ope);
231
+ else if (_ope?.commit) return prepareCommit(connection, _ope);
232
+ else if (_ope?.rollback) return prepareRollback(connection, _ope);
233
+ else if (_ope?.passthrough) return preparePassthrough(connection, _ope);
234
+ else throw new Error('Request sintax error, missing property get/post/patch/put/delete/...');
235
+ });
236
+ return denormalizePreparedOperations(_result, _isArrayInput);
237
+ }
238
+
239
+ export function prepareGet(tSchema, connection, reqOpe) {
240
+ const _isArrayInput = Array.isArray(reqOpe);
241
+ const _reqOpes = normalizeReqOperations(reqOpe);
242
+ const _result = _reqOpes.map((_ope) => {
243
+ const _get = _ope?.get ?? {};
244
+ const { byId, ...__get } = _get; // remove byId and create a copy
245
+ const mergedFilters = byId ? mergeByIdFilters(__get.filters, idField(tSchema) + ' = ' + toStringValue(byId)) : __get.filters;
246
+ return {
247
+ get: {
248
+ schema: tSchema,
249
+ options: __get.options,
250
+ fields: __get.fields ?? defautlFields,
251
+ filters: mergedFilters,
252
+ groupBy: __get.groupBy,
253
+ groupFilters: __get.groupFilters,
254
+ orderBy: __get.orderBy,
255
+ params: __get.params
256
+ },
257
+ connection: connection,
258
+ appLog: _ope?.hasOwnProperty('appLog') ? _ope.appLog : undefined
259
+ };
260
+ });
261
+ return denormalizePreparedOperations(_result, _isArrayInput);
262
+ };
263
+
264
+ export function preparePatch(tSchema, connection, reqOpe) {
265
+ const _isArrayInput = Array.isArray(reqOpe);
266
+ const _reqOpes = normalizeReqOperations(reqOpe);
267
+ const _result = _reqOpes.map((_ope) => {
268
+ const _patch = _ope?.patch;
269
+ if (_patch) {
270
+ if (!_patch.values) throw new Error('Request sintax error, missing "patch.values" property.')
271
+ const { byId, ...__patch } = _patch; // remove byId and create a copy
272
+ const mergedFilters = byId ? mergeByIdFilters(__patch.filters, idField(tSchema) + ' = ' + toStringValue(byId)) : __patch.filters;
273
+ return {
274
+ patch: {
275
+ schema: tSchema,
276
+ values: __patch.values,
277
+ filters: mergedFilters,
278
+ params: __patch.params
279
+ },
280
+ connection: connection,
281
+ appLog: _ope?.hasOwnProperty('appLog') ? _ope.appLog : undefined
282
+ };
283
+ }
284
+ else throw new Error('Request sintax error, missing "patch" property.');
285
+ });
286
+ return denormalizePreparedOperations(_result, _isArrayInput);
287
+ };
288
+
289
+ export function preparePost(tSchema, connection, reqOpe) {
290
+ const _isArrayInput = Array.isArray(reqOpe);
291
+ const _reqOpes = normalizeReqOperations(reqOpe);
292
+ const _result = _reqOpes.map((_ope) => {
293
+ const _post = _ope?.post;
294
+ if (_post) {
295
+ if (!_post.values) throw new Error('Request sintax error, missing "post.values" property.');
296
+ if (_post.byId) { // force Id value from byId
297
+ f.upsertProperty(_post.values, idField(tSchema), _post.byId);
298
+ }
299
+ if (autoId(tSchema)) // automatic Id
300
+ if (!Object.keys(_post.values).find(key => key.toUpperCase() === (idField(tSchema)).toUpperCase())) {
301
+ _post.values[idField(tSchema)] = uuidv4(); // automatic Id via uuidv4
302
+ }
303
+ return {
304
+ post: {
305
+ schema: tSchema,
306
+ values: _post.values,
307
+ params: _post.params
308
+ },
309
+ connection: connection,
310
+ appLog: _ope?.hasOwnProperty('appLog') ? _ope.appLog : undefined
311
+ };
312
+ }
313
+ else throw new Error('Request sintax error, missing "post" property.');
314
+ });
315
+ return denormalizePreparedOperations(_result, _isArrayInput);
316
+ };
317
+
318
+ export function preparePut(tSchema, connection, reqOpe) {
319
+ const _isArrayInput = Array.isArray(reqOpe);
320
+ const _reqOpes = normalizeReqOperations(reqOpe);
321
+ const _result = _reqOpes.map((_ope) => {
322
+ const _put = _ope?.put;
323
+ if (_put) {
324
+ if (!_put.values) throw new Error('Request sintax error, missing "put.values" property.');
325
+ const { byId, ...__put } = _put; // remove byId and create a copy
326
+ const mergedFilters = byId ? mergeByIdFilters(__put.filters, idField(tSchema) + ' = ' + toStringValue(byId)) : __put.filters;
327
+ return {
328
+ put: {
329
+ schema: tSchema,
330
+ values: _put.values,
331
+ filters: mergedFilters,
332
+ params: _put.params
333
+ },
334
+ connection: connection,
335
+ appLog: _ope?.hasOwnProperty('appLog') ? _ope.appLog : undefined
336
+ };
337
+ }
338
+ else throw new Error('Request sintax error, missing "put" property.');
339
+ });
340
+ return denormalizePreparedOperations(_result, _isArrayInput);
341
+ };
342
+
343
+ export function prepareDelete(tSchema, connection, reqOpe) {
344
+ const _isArrayInput = Array.isArray(reqOpe);
345
+ const _reqOpes = normalizeReqOperations(reqOpe);
346
+ const _result = _reqOpes.map((_ope) => {
347
+ const _delete = _ope?.delete;
348
+ if (_delete) {
349
+ const { byId, ...__delete } = _delete; // remove byId and create a copy
350
+ const mergedFilters = byId ? mergeByIdFilters(__delete.filters, idField(tSchema) + ' = ' + toStringValue(byId)) : __delete.filters;
351
+ return {
352
+ delete: {
353
+ schema: tSchema,
354
+ filters: mergedFilters,
355
+ params: __delete.params
356
+ },
357
+ connection: connection,
358
+ appLog: _ope?.hasOwnProperty('appLog') ? _ope.appLog : undefined
359
+ };
360
+ }
361
+ else throw new Error('Request sintax error, missing "delete" property.');
362
+ });
363
+ return denormalizePreparedOperations(_result, _isArrayInput);
364
+ };
365
+
366
+ export function prepareCommand(pSchema, connection, reqOpe) {
367
+ const _isArrayInput = Array.isArray(reqOpe);
368
+ const _reqOpes = normalizeReqOperations(reqOpe);
369
+ const _result = _reqOpes.map((_ope) => {
370
+ return {
371
+ command: {
372
+ schema: pSchema,
373
+ arguments: Array.isArray(_ope.command?.arguments) ? _ope.command?.arguments : [_ope.command?.arguments],
374
+ params: _ope.command?.params
375
+ },
376
+ connection: connection,
377
+ appLog: _ope?.hasOwnProperty('appLog') ? _ope.appLog : undefined
378
+ };
379
+ });
380
+ return denormalizePreparedOperations(_result, _isArrayInput);
381
+ };
382
+
383
+ export function preparePassthrough(connection, reqOpe) {
384
+ const _isArrayInput = Array.isArray(reqOpe);
385
+ const _reqOpes = normalizeReqOperations(reqOpe);
386
+ const _result = _reqOpes.map((_ope) => {
387
+ if (_ope?.passthrough) {
388
+ if (!_ope.passthrough.command) throw new Error('Request sintax error, missing "passthrough.command" property.')
389
+ return {
390
+ passthrough: {
391
+ command: _ope.passthrough.command,
392
+ params: _ope.passthrough.params
393
+ },
394
+ connection: connection,
395
+ appLog: _ope?.hasOwnProperty('appLog') ? _ope.appLog : undefined
396
+ };
397
+ }
398
+ else throw new Error('Request sintax error, missing "passthrough" property.');
399
+ });
400
+
401
+ return denormalizePreparedOperations(_result, _isArrayInput);
402
+ };
403
+
404
+ export function prepareBegin(connection, reqOpe) {
405
+ return {
406
+ begin: {},
407
+ connection: connection,
408
+ appLog: reqOpe?.hasOwnProperty('appLog') ? reqOpe.appLog : undefined
409
+ }
410
+ };
411
+
412
+ export function prepareCommit(connection, reqOpe) {
413
+ return {
414
+ commit: {},
415
+ connection: connection,
416
+ appLog: reqOpe?.hasOwnProperty('appLog') ? reqOpe.appLog : undefined
417
+ }
418
+ };
419
+
420
+ export function prepareRollback(connection, reqOpe) {
421
+ return {
422
+ rollback: {},
423
+ connection: connection,
424
+ appLog: reqOpe?.hasOwnProperty('appLog') ? reqOpe.appLog : undefined
425
+ }
426
+ };
package/lib/f.js ADDED
@@ -0,0 +1,45 @@
1
+ "use strict";
2
+
3
+ // set a deep object creating all intermediate structure
4
+ export function setDeep(obj, path, value) {
5
+ const keys = path.split('.');
6
+ let lastKey = keys.pop();
7
+ let pointer = obj;
8
+
9
+ for (let key of keys) {
10
+ if (!pointer[key] || typeof pointer[key] !== 'object') {
11
+ pointer[key] = {}; // Crea l'oggetto intermedio se non esiste
12
+ }
13
+ pointer = pointer[key];
14
+ }
15
+
16
+ pointer[lastKey] = value;
17
+ return obj;
18
+ }
19
+
20
+ // upsert property using unsensitive case name.
21
+ export function upsertProperty(obj, keyToUpdate, newValue) {
22
+ if (typeof obj !== 'object' || obj === null) {
23
+ // Non è un oggetto valido
24
+ return false;
25
+ }
26
+
27
+ const lowerKeyToUpdate = keyToUpdate.toLowerCase();
28
+ let propertyFound = false;
29
+
30
+ for (const key in obj) {
31
+ if (Object.prototype.hasOwnProperty.call(obj, key)) {
32
+ if (key.toLowerCase() === lowerKeyToUpdate) {
33
+ obj[key] = newValue;
34
+ propertyFound = true;
35
+ break;
36
+ }
37
+ }
38
+ }
39
+
40
+ if (!propertyFound) {
41
+ obj[keyToUpdate] = newValue;
42
+ }
43
+
44
+ return true;
45
+ }
package/lib/factory.js ADDED
@@ -0,0 +1,89 @@
1
+ "use strict";
2
+
3
+ import { config, Server, Database, Table, Field, Log } from './config.js';
4
+ import * as dbOpe from "./db-operations.js"
5
+ import newSessionStore from "./session-store.js"
6
+ import { DeleteBuilder, GetBuilder, PatchBuilder, PutBuilder, PostBuilder, CommandBuilder, BatchBuilder, parseObject, PassthroughBuilder } from "./builder.js"
7
+
8
+ export function command(command) {
9
+ return new CommandBuilder(command);
10
+ }
11
+
12
+ export function get(tablePath) {
13
+ return new GetBuilder(tablePath);
14
+ }
15
+
16
+ export function post(tablePath) {
17
+ return new PostBuilder(tablePath);
18
+ }
19
+
20
+ export function put(tablePath) {
21
+ return new PutBuilder(tablePath);
22
+ }
23
+
24
+ export function patch(tablePath) {
25
+ return new PatchBuilder(tablePath);
26
+ }
27
+
28
+ export function del(tablePath) {
29
+ return new DeleteBuilder(tablePath);
30
+ }
31
+
32
+ export function fromObject(obj) {
33
+ return parseObject(obj);
34
+ }
35
+
36
+ export function Batch() {
37
+ return new BatchBuilder();
38
+ }
39
+
40
+ export function Passthrough(command) {
41
+ return new PassthroughBuilder(command);
42
+ }
43
+
44
+ export function closeAllDbConnections() {
45
+ return dbOpe.closeAllConnections();
46
+ }
47
+
48
+ export function sessionStore(options) {
49
+ return new newSessionStore(options);
50
+ }
51
+
52
+ export function testDbConnection() {
53
+ return dbOpe.testConnection();
54
+ }
55
+
56
+ export function setLogLevel(level) {
57
+ if (typeof level === 'number') { config.log.level = level }
58
+ else if (typeof level === 'string' && Number.parseInt(level) != NaN) { config.log.level = Number.parseInt(level) }
59
+ }
60
+
61
+ export function setLogCallback(callback) {
62
+ if (typeof callback === 'function') { config.log.callback = callback }
63
+ }
64
+
65
+ export function setLogMaxAsyncInstances(maxAsyncInstances) {
66
+ if (typeof maxAsyncInstances === 'number') { config.log.maxAsyncInstance = maxAsyncInstances }
67
+ else if (typeof maxAsyncInstances === 'string' && Number.parseInt(maxAsyncInstances) != NaN) { config.log.maxAsyncInstance = Number.parseInt(maxAsyncInstances) }
68
+ }
69
+
70
+ export function log() {
71
+ return new Log();
72
+ }
73
+
74
+
75
+ export function server(server) {
76
+ return new Server(server);
77
+ }
78
+
79
+ export function database(database, serverName) {
80
+ return new Database(database, serverName);
81
+ }
82
+
83
+ export function table(table, dbName, serverName) {
84
+ return new Table(table, dbName, serverName);
85
+ }
86
+
87
+ export function field(field, tableName, dbName, serverName) {
88
+ return new Field(field, tableName, dbName, serverName);
89
+ }
package/lib/log.js ADDED
@@ -0,0 +1,21 @@
1
+ 'use strict';
2
+
3
+ import { config } from './config.js';
4
+ let numberOfAsyncInstances = 0;
5
+
6
+ export default function log(msg, level = 1) {
7
+ if (config.log.level >= level && config.log.callback) {
8
+ try {
9
+ const _param = {level: level, text: msg};
10
+ const _result = config.log.callback(_param);
11
+ if (_result && typeof _result.then === 'function') {
12
+ // È una Promise (o un oggetto thenable)
13
+ if (numberOfAsyncInstances <= config.log.maxAsyncInstance) {
14
+ numberOfAsyncInstances++;
15
+ _result.then((_param) => {}).catch(error => {}).finally(() => {numberOfAsyncInstances--;});
16
+ };
17
+ };
18
+ }
19
+ catch {}
20
+ }
21
+ };