node-firebird 2.0.2 → 2.3.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.
@@ -135,102 +135,110 @@ class ServiceManager extends Events.EventEmitter {
135
135
  }
136
136
 
137
137
  _processquery(buffer, callback) {
138
- //console.log(buffer);
139
- var br = new BlrReader(buffer);
140
- var tinfo = br.readByteCode();
141
- var res = {};
142
- res.result = 0;
143
- for (; tinfo !== Const.isc_info_end; tinfo = br.readByteCode()) {
144
- switch (tinfo) {
145
- case Const.isc_info_svc_server_version:
146
- case Const.isc_info_svc_implementation:
147
- case Const.isc_info_svc_user_dbpath:
148
- case Const.isc_info_svc_get_env:
149
- case Const.isc_info_svc_get_env_lock:
150
- case Const.isc_info_svc_get_env_msg:
151
- res[this._infosmapping[tinfo]] = br.readString();
152
- break;
153
- case Const.isc_info_svc_version:
154
- res[this._infosmapping[tinfo]] = br.readInt32();
155
- break;
156
- case Const.isc_info_svc_svr_db_info:
157
- this._processdbinfo(br, res);
158
- break;
159
- case Const.isc_info_svc_limbo_trans:
160
- // not implemented
161
- for (; tinfo !== isc_info_flag_end; tinfo = br.readByteCode())
138
+ if (!Buffer.isBuffer(buffer)) {
139
+ doError(new Error('Malformed service-manager response: missing BLR buffer'), callback);
140
+ return;
141
+ }
142
+
143
+ try {
144
+ var br = new BlrReader(buffer);
145
+ var tinfo = br.readByteCode();
146
+ var res = {};
147
+ res.result = 0;
148
+ for (; tinfo !== Const.isc_info_end; tinfo = br.readByteCode()) {
149
+ switch (tinfo) {
150
+ case Const.isc_info_svc_server_version:
151
+ case Const.isc_info_svc_implementation:
152
+ case Const.isc_info_svc_user_dbpath:
153
+ case Const.isc_info_svc_get_env:
154
+ case Const.isc_info_svc_get_env_lock:
155
+ case Const.isc_info_svc_get_env_msg:
156
+ res[this._infosmapping[tinfo]] = br.readString();
162
157
  break;
163
- case Const.isc_info_svc_get_users:
164
- br.pos += 2
165
- res[this._infosmapping[tinfo]] = [];
166
- break;
167
- case Const.isc_spb_sec_username:
168
- var tuser = res[this._infosmapping[68]];
169
- tuser.push({});
170
- tuser[tuser.length - 1].username = br.readString();
171
- break;
172
- case Const.isc_spb_sec_firstname:
173
- var tuser = res[this._infosmapping[68]];
174
- var user = tuser[tuser.length-1];
175
- user.firstname = br.readString();
176
- break;
177
- case Const.isc_spb_sec_middlename:
178
- var tuser = res[this._infosmapping[68]];
179
- var user = tuser[tuser.length-1];
180
- user.middlename = br.readString();
181
- break;
182
- case Const.isc_spb_sec_lastname:
183
- var tuser = res[this._infosmapping[68]];
184
- var user = tuser[tuser.length-1];
185
- user.lastname = br.readString();
186
- break;
187
- case Const.isc_spb_sec_groupid:
188
- var tuser = res[this._infosmapping[68]];
189
- var user = tuser[tuser.length-1];
190
- user.groupid = br.readInt32();
191
- break;
192
- case Const.isc_spb_sec_userid:
193
- var tuser = res[this._infosmapping[68]];
194
- var user = tuser[tuser.length-1];
195
- user.userid = br.readInt32();
196
-
197
- break;
198
- case Const.isc_spb_sec_admin:
199
- var tuser = res[this._infosmapping[68]];
200
- var user = tuser[tuser.length-1];
201
- user.admin = br.readInt32();
202
- break;
203
-
204
- case Const.isc_info_svc_line:
205
- res.line = br.readString();
206
- break;
207
-
208
- case Const.isc_info_svc_to_eof:
209
- res.line = br.readString();
210
- break;
211
-
212
- case Const.isc_info_truncated:
213
- res.result = 1; // too much data for the result buffer increase size of it (buffersize parameter))
214
- break;
215
-
216
- case Const.isc_info_data_not_ready:
217
- res.result = 2;
218
- break;
219
-
220
- case Const.isc_info_svc_timeout:
221
- res.result = 3;
222
- break;
223
-
224
- case Const.isc_info_svc_stdin:
225
-
226
- break;
227
-
228
- case Const.isc_info_svc_capabilities:
229
- this._processcapabilities(br, res);
230
- break;
158
+ case Const.isc_info_svc_version:
159
+ res[this._infosmapping[tinfo]] = br.readInt32();
160
+ break;
161
+ case Const.isc_info_svc_svr_db_info:
162
+ this._processdbinfo(br, res);
163
+ break;
164
+ case Const.isc_info_svc_limbo_trans:
165
+ // not implemented
166
+ for (; tinfo !== isc_info_flag_end; tinfo = br.readByteCode())
167
+ break;
168
+ case Const.isc_info_svc_get_users:
169
+ br.pos += 2
170
+ res[this._infosmapping[tinfo]] = [];
171
+ break;
172
+ case Const.isc_spb_sec_username:
173
+ var tuser = res[this._infosmapping[68]];
174
+ tuser.push({});
175
+ tuser[tuser.length - 1].username = br.readString();
176
+ break;
177
+ case Const.isc_spb_sec_firstname:
178
+ var tuser = res[this._infosmapping[68]];
179
+ var user = tuser[tuser.length-1];
180
+ user.firstname = br.readString();
181
+ break;
182
+ case Const.isc_spb_sec_middlename:
183
+ var tuser = res[this._infosmapping[68]];
184
+ var user = tuser[tuser.length-1];
185
+ user.middlename = br.readString();
186
+ break;
187
+ case Const.isc_spb_sec_lastname:
188
+ var tuser = res[this._infosmapping[68]];
189
+ var user = tuser[tuser.length-1];
190
+ user.lastname = br.readString();
191
+ break;
192
+ case Const.isc_spb_sec_groupid:
193
+ var tuser = res[this._infosmapping[68]];
194
+ var user = tuser[tuser.length-1];
195
+ user.groupid = br.readInt32();
196
+ break;
197
+ case Const.isc_spb_sec_userid:
198
+ var tuser = res[this._infosmapping[68]];
199
+ var user = tuser[tuser.length-1];
200
+ user.userid = br.readInt32();
201
+
202
+ break;
203
+ case Const.isc_spb_sec_admin:
204
+ var tuser = res[this._infosmapping[68]];
205
+ var user = tuser[tuser.length-1];
206
+ user.admin = br.readInt32();
207
+ break;
208
+
209
+ case Const.isc_info_svc_line:
210
+ res.line = br.readString();
211
+ break;
212
+
213
+ case Const.isc_info_svc_to_eof:
214
+ res.line = br.readString();
215
+ break;
216
+
217
+ case Const.isc_info_truncated:
218
+ res.result = 1; // too much data for the result buffer increase size of it (buffersize parameter))
219
+ break;
220
+
221
+ case Const.isc_info_data_not_ready:
222
+ res.result = 2;
223
+ break;
224
+
225
+ case Const.isc_info_svc_timeout:
226
+ res.result = 3;
227
+ break;
228
+
229
+ case Const.isc_info_svc_stdin:
230
+
231
+ break;
232
+
233
+ case Const.isc_info_svc_capabilities:
234
+ this._processcapabilities(br, res);
235
+ break;
236
+ }
231
237
  }
238
+ callback(null, res);
239
+ } catch (err) {
240
+ doError(new Error('Malformed service-manager response: ' + err.message), callback);
232
241
  }
233
- callback(null, res);
234
242
  }
235
243
 
236
244
  detach(callback, force) {
@@ -1047,4 +1055,4 @@ class ServiceManager extends Events.EventEmitter {
1047
1055
  ServiceManager.ShutdownMode = ShutdownMode;
1048
1056
  ServiceManager.ShutdownKind = ShutdownKind;
1049
1057
 
1050
- module.exports = ServiceManager;
1058
+ module.exports = ServiceManager;
@@ -25,15 +25,15 @@ class Statement {
25
25
  this.connection.dropStatement(this, callback);
26
26
  }
27
27
 
28
- execute(transaction, params, callback, custom) {
28
+ execute(transaction, params, callback, options) {
29
29
  if (params instanceof Function) {
30
- custom = callback;
30
+ options = callback;
31
31
  callback = params;
32
32
  params = undefined;
33
33
  }
34
34
 
35
- this.custom = custom;
36
- this.connection.executeStatement(transaction, this, params, callback, custom);
35
+ this.options = options;
36
+ this.connection.executeStatement(transaction, this, params, callback, options);
37
37
  }
38
38
 
39
39
  fetch(transaction, count, callback) {
@@ -25,9 +25,9 @@ class Transaction {
25
25
  }
26
26
  }
27
27
 
28
- execute(query, params, callback, custom) {
28
+ execute(query, params, callback, options) {
29
29
  if (params instanceof Function) {
30
- custom = callback;
30
+ options = callback;
31
31
  callback = params;
32
32
  params = undefined;
33
33
  }
@@ -100,13 +100,13 @@ class Transaction {
100
100
  break;
101
101
  }
102
102
 
103
- }, custom);
103
+ }, options);
104
104
  });
105
105
  }
106
106
 
107
- sequentially(query, params, on, callback, asArray) {
107
+ sequentially(query, params, on, callback, options = {}) {
108
108
  if (params instanceof Function) {
109
- asArray = callback;
109
+ options = callback;
110
110
  callback = on;
111
111
  on = params;
112
112
  params = undefined;
@@ -117,7 +117,7 @@ class Transaction {
117
117
  }
118
118
 
119
119
  if (callback instanceof Boolean) {
120
- asArray = callback;
120
+ options = callback;
121
121
  callback = undefined;
122
122
  }
123
123
 
@@ -152,11 +152,23 @@ class Transaction {
152
152
  }
153
153
  };
154
154
 
155
- self.execute(query, params, callback, { asObject: !asArray, asStream: true, on: _on });
155
+ // back compatibility - options parameter is a boolean
156
+ if (typeof options === 'boolean') {
157
+ options = { asObject: !options, asStream: true, on: _on };
158
+ } else {
159
+ options = {
160
+ asStream: true,
161
+ asObject: true,
162
+ on: _on,
163
+ ...options,
164
+ };
165
+ }
166
+
167
+ self.execute(query, params, callback, options);
156
168
  return self;
157
169
  }
158
170
 
159
- query(query, params, callback) {
171
+ query(query, params, callback, options = {}) {
160
172
  if (params instanceof Function) {
161
173
  callback = params;
162
174
  params = undefined;
@@ -165,7 +177,13 @@ class Transaction {
165
177
  if (callback === undefined)
166
178
  callback = noop;
167
179
 
168
- this.execute(query, params, callback, { asObject: true, asStream: callback === undefined || callback === null });
180
+ options = {
181
+ asObject: true,
182
+ asStream: callback === undefined || callback === null,
183
+ ...options,
184
+ };
185
+
186
+ this.execute(query, params, callback, options);
169
187
  }
170
188
 
171
189
  commit(callback) {
@@ -199,6 +199,44 @@ class SQLVarInt128 {
199
199
 
200
200
  //------------------------------------------------------
201
201
 
202
+ class SQLVarDecFloat16 {
203
+ decode(data, lowerV13) {
204
+ var ret = data.readDecFloat16();
205
+
206
+ if (!lowerV13 || !data.readInt()) {
207
+ return ret;
208
+ }
209
+
210
+ return null;
211
+ }
212
+
213
+ calcBlr(blr) {
214
+ blr.addByte(Const.blr_dec64);
215
+ blr.addShort(0);
216
+ }
217
+ }
218
+
219
+ //------------------------------------------------------
220
+
221
+ class SQLVarDecFloat34 {
222
+ decode(data, lowerV13) {
223
+ var ret = data.readDecFloat34();
224
+
225
+ if (!lowerV13 || !data.readInt()) {
226
+ return ret;
227
+ }
228
+
229
+ return null;
230
+ }
231
+
232
+ calcBlr(blr) {
233
+ blr.addByte(Const.blr_dec128);
234
+ blr.addShort(0);
235
+ }
236
+ }
237
+
238
+ //------------------------------------------------------
239
+
202
240
  class SQLVarFloat {
203
241
  decode(data, lowerV13) {
204
242
  var ret = data.readFloat();
@@ -295,6 +333,92 @@ class SQLVarTimeStamp {
295
333
 
296
334
  //------------------------------------------------------
297
335
 
336
+ class SQLVarTimeTz {
337
+ decode(data, lowerV13) {
338
+ var time = data.readUInt();
339
+ data.readInt(); // skip timezone info
340
+
341
+ if (!lowerV13 || !data.readInt()) {
342
+ var d = new Date(0);
343
+ d.setMilliseconds(Math.floor(time / 10) + d.getTimezoneOffset() * MsPerMinute);
344
+ return d;
345
+ }
346
+ return null;
347
+ }
348
+
349
+ calcBlr(blr) {
350
+ blr.addByte(Const.blr_sql_time_tz);
351
+ }
352
+ }
353
+
354
+ //------------------------------------------------------
355
+
356
+ class SQLVarTimeTzEx extends SQLVarTimeTz {
357
+ decode(data, lowerV13) {
358
+ var time = data.readUInt();
359
+ data.readInt(); // skip timezone info
360
+ data.readInt(); // skip ext_offset
361
+
362
+ if (!lowerV13 || !data.readInt()) {
363
+ var d = new Date(0);
364
+ d.setMilliseconds(Math.floor(time / 10) + d.getTimezoneOffset() * MsPerMinute);
365
+ return d;
366
+ }
367
+ return null;
368
+ }
369
+
370
+ calcBlr(blr) {
371
+ blr.addByte(Const.blr_ex_time_tz);
372
+ }
373
+ }
374
+
375
+ //------------------------------------------------------
376
+
377
+ class SQLVarTimeStampTz {
378
+ decode(data, lowerV13) {
379
+ var date = data.readInt();
380
+ var time = data.readUInt();
381
+ data.readInt(); // skip timezone info
382
+
383
+ if (!lowerV13 || !data.readInt()) {
384
+ var d = new Date(0);
385
+ d.setMilliseconds((date - DateOffset) * TimeCoeff + Math.floor(time / 10) + d.getTimezoneOffset() * MsPerMinute);
386
+ return d;
387
+ }
388
+
389
+ return null;
390
+ }
391
+
392
+ calcBlr(blr) {
393
+ blr.addByte(Const.blr_timestamp_tz);
394
+ }
395
+ }
396
+
397
+ //------------------------------------------------------
398
+
399
+ class SQLVarTimeStampTzEx extends SQLVarTimeStampTz {
400
+ decode(data, lowerV13) {
401
+ var date = data.readInt();
402
+ var time = data.readUInt();
403
+ data.readInt(); // skip timezone info
404
+ data.readInt(); // skip ext_offset
405
+
406
+ if (!lowerV13 || !data.readInt()) {
407
+ var d = new Date(0);
408
+ d.setMilliseconds((date - DateOffset) * TimeCoeff + Math.floor(time / 10) + d.getTimezoneOffset() * MsPerMinute);
409
+ return d;
410
+ }
411
+
412
+ return null;
413
+ }
414
+
415
+ calcBlr(blr) {
416
+ blr.addByte(Const.blr_ex_timestamp_tz);
417
+ }
418
+ }
419
+
420
+ //------------------------------------------------------
421
+
298
422
  class SQLVarBoolean {
299
423
  decode(data, lowerV13) {
300
424
  var ret = data.readInt();
@@ -378,6 +502,50 @@ class SQLParamInt128 {
378
502
 
379
503
  //------------------------------------------------------
380
504
 
505
+ class SQLParamDecFloat16 {
506
+ constructor(value) {
507
+ this.value = value;
508
+ }
509
+
510
+ calcBlr(blr) {
511
+ blr.addByte(Const.blr_dec64);
512
+ blr.addShort(0);
513
+ }
514
+
515
+ encode(data) {
516
+ if (this.value != null) {
517
+ data.addDecFloat16(this.value);
518
+ } else {
519
+ data.addDecFloat16(0);
520
+ data.addInt(1);
521
+ }
522
+ }
523
+ }
524
+
525
+ //------------------------------------------------------
526
+
527
+ class SQLParamDecFloat34 {
528
+ constructor(value) {
529
+ this.value = value;
530
+ }
531
+
532
+ calcBlr(blr) {
533
+ blr.addByte(Const.blr_dec128);
534
+ blr.addShort(0);
535
+ }
536
+
537
+ encode(data) {
538
+ if (this.value != null) {
539
+ data.addDecFloat34(this.value);
540
+ } else {
541
+ data.addDecFloat34(0);
542
+ data.addInt(1);
543
+ }
544
+ }
545
+ }
546
+
547
+ //------------------------------------------------------
548
+
381
549
  class SQLParamDouble {
382
550
  constructor(value) {
383
551
  this.value = value;
@@ -509,6 +677,8 @@ module.exports = {
509
677
  SQLVarInt,
510
678
  SQLVarInt64,
511
679
  SQLVarInt128,
680
+ SQLVarDecFloat16,
681
+ SQLVarDecFloat34,
512
682
  SQLVarFloat,
513
683
  SQLVarNull,
514
684
  SQLVarShort,
@@ -516,12 +686,18 @@ module.exports = {
516
686
  SQLVarText,
517
687
  SQLVarTime,
518
688
  SQLVarTimeStamp,
689
+ SQLVarTimeTz,
690
+ SQLVarTimeTzEx,
691
+ SQLVarTimeStampTz,
692
+ SQLVarTimeStampTzEx,
519
693
  SQLParamBool,
520
694
  SQLParamDate,
521
695
  SQLParamDouble,
522
696
  SQLParamInt,
523
697
  SQLParamInt64,
524
698
  SQLParamInt128,
699
+ SQLParamDecFloat16,
700
+ SQLParamDecFloat34,
525
701
  SQLParamQuad,
526
702
  SQLParamString,
527
703
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-firebird",
3
- "version": "2.0.2",
3
+ "version": "2.3.0",
4
4
  "description": "Pure JavaScript and Asynchronous Firebird client for Node.js.",
5
5
  "keywords": [
6
6
  "firebird",
@@ -25,15 +25,15 @@
25
25
  "types": "./lib/index.d.ts",
26
26
  "license": "MPL-2.0",
27
27
  "scripts": {
28
- "test": "vitest run"
28
+ "test": "vitest run",
29
+ "lint": "oxlint"
29
30
  },
30
31
  "dependencies": {
31
- "big-integer": "^1.6.51",
32
32
  "long": "^5.2.3"
33
33
  },
34
34
  "devDependencies": {
35
- "coveralls-next": "^4.2.0",
36
- "nyc": "^15.0.1",
35
+ "coveralls-next": "^6.0.1",
36
+ "oxlint": "^0.15.11",
37
37
  "vitest": "^4.0.18"
38
38
  }
39
39
  }
package/vitest.config.js CHANGED
@@ -8,6 +8,17 @@ module.exports = defineConfig({
8
8
  fileParallelism: false,
9
9
  maxWorkers: 1,
10
10
  isolate: false,
11
- include: ['test/arc4.js', 'test/protocol.js', 'test/srp.js', 'test/service.js', 'test/utf8-user-identification.js', 'test/index.js', 'test/db-crypt-config.js'],
11
+ include: [
12
+ 'test/arc4.js',
13
+ 'test/protocol.js',
14
+ 'test/srp.js',
15
+ 'test/service.js',
16
+ 'test/utf8-user-identification.js',
17
+ 'test/index.js',
18
+ 'test/db-crypt-config.js',
19
+ 'test/timezone.js',
20
+ 'test/decfloat.js',
21
+ 'test/mock-server.js'
22
+ ],
12
23
  },
13
24
  });
package/Roadmap.md DELETED
@@ -1,80 +0,0 @@
1
- # Node-Firebird Roadmap
2
-
3
- This document outlines the future development direction for the `node-firebird` library. Our primary goals are to modernize the codebase, implement support for the latest Firebird features, and improve the overall developer experience.
4
-
5
- ## Protocol Implementation Status
6
-
7
- The following table summarizes the current and planned implementation status of the Firebird wire protocol for each major version.
8
-
9
- | Firebird Version | Protocol Versions | Status |
10
- | :--- | :--- | :--- |
11
- | 2.5 | 10, 11, 12, 13 | ✅ Implemented |
12
- | 3.0 | 14, 15 | ✅ Implemented |
13
- | 4.0 | 16, 17 | ❌ Not Implemented |
14
- | 5.0 | N/A | ❌ Not Implemented |
15
- | 6.0 | N/A | ❌ Not Implemented |
16
-
17
- ## Firebird 3 Support
18
-
19
- Firebird 3 introduced Protocol 13, which brought significant changes focusing on security and performance. The following features have been implemented:
20
-
21
- - **Protocol Versions 14 and 15:** ✅ Implemented - newer wire protocol versions are now supported.
22
- - **Enhanced Authentication:** ✅ Implemented - Srp256 (SHA-256) authentication plugin is now supported alongside Srp (SHA-1) and Legacy_Auth.
23
- - **Wire Protocol Encryption:** ✅ Implemented - Arc4 (RC4) stream cipher encryption for all network traffic using SRP session keys.
24
- - **Wire Protocol Compression:** ✅ Implemented - zlib compression support for protocol version 13+.
25
- - **Packed (NULL-aware) Row Data:** ✅ Implemented - null bitmap support for protocol version 13+.
26
- - **op_cond_accept Handling:** ✅ Implemented - proper handling of conditional accept with authentication continuation.
27
- - **UTF-8 User Identification:** ✅ Implemented - all user identification is properly handled with UTF-8 encoding via `isc_dpb_utf8_filename` flag for Firebird 3+.
28
- - **Database Encryption Callback:** ✅ Implemented - support for database encryption key callback (`op_crypt_key_callback`) during the connect phase, allowing connections to encrypted databases. The `dbCryptConfig` connection option supports both plain text and base64-encoded encryption keys.
29
-
30
- The following features are planned for future implementation:
31
-
32
- ## Firebird 4 Support
33
-
34
- Firebird 4 introduced Protocol versions 16 and 17, continuing to build upon the foundation of Firebird 3. Key features to implement include:
35
-
36
- - **Protocol Versions 16 and 17:** Implement the latest protocol versions to support Firebird 4 features.
37
-
38
- ## Firebird 5 Support
39
-
40
- Firebird 5 introduces a host of new SQL features and performance improvements that will require significant client-side implementation:
41
-
42
- - **Bidirectional Cursors:** Implement support for scrollable cursors for remote database access.
43
- - **`RETURNING` Multiple Rows:** Enhance DML operations to support returning multiple rows.
44
- - **`SKIP LOCKED`:** Add support for the `SKIP LOCKED` clause in `SELECT WITH LOCK`, `UPDATE`, and `DELETE` statements.
45
- - **New Data Types and Functions:** Add support for new built-in functions and packages.
46
-
47
- ## Firebird 6 and Beyond
48
-
49
- As Firebird 6 and future versions are released, we will continue to monitor new features and plan for their implementation. Key areas of interest include:
50
-
51
- - **JSON Support:** Implement client-side support for the new SQL-compliant JSON functions.
52
- - **Tablespaces:** Add support for tablespaces.
53
- - **SQL Schemas:** Implement support for SQL schemas.
54
-
55
- ## Codebase Refactoring
56
-
57
- The current codebase is functional but could be significantly improved by adopting modern JavaScript and TypeScript features.
58
-
59
- ### Modern JavaScript Classes
60
-
61
- The existing codebase is written in a prototype-based style. We plan to refactor the entire library to use modern JavaScript classes (`class` syntax). This will improve readability, maintainability, and make the code easier to understand for new contributors.
62
-
63
- **Benefits:**
64
-
65
- - Improved code structure and organization.
66
- - Easier to reason about inheritance and object-oriented patterns.
67
- - Better alignment with modern JavaScript best practices.
68
-
69
- ### TypeScript Rewrite
70
-
71
- A full rewrite of the library in TypeScript is a long-term goal. This would bring the benefits of static typing, improved developer tooling, and a more robust codebase.
72
-
73
- **Benefits:**
74
-
75
- - **Type Safety:** Catch errors at compile-time instead of runtime.
76
- - **Improved Autocomplete:** Better editor support and developer experience.
77
- - **Self-documenting Code:** Types make the code easier to understand and use.
78
- - **Easier Refactoring:** Static analysis makes it easier to refactor code with confidence.
79
-
80
- We believe these changes will make `node-firebird` a more robust, modern, and developer-friendly library for accessing Firebird databases.