node-firebird 2.15.1 → 2.16.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 CHANGED
@@ -11,7 +11,7 @@
11
11
  - [Installation](#installation)
12
12
  - [Usage](#usage) — including [developing the driver](#developing-the-driver)
13
13
  - [Promises and async/await](#promises-and-asyncawait) — the `*Async` API plus `withConnection` / `withTransaction` helpers
14
- - [Connection types](#connection-types) — connection options, `firebird://` URIs and traditional connection strings, classic connections, pooling
14
+ - [Connection types](#connection-types) — connection options, `firebird://` / `inet://` URIs and traditional connection strings, classic connections, pooling
15
15
  - [Database object (db)](#database-object-db) — database, transaction and statement methods/options
16
16
  - [Examples](#examples) — parametrized queries, tagged-template queries (sql), named placeholders, nested result tables (nestTables), row-key transforms (transformKeys), result metadata / affected rows (withMeta), custom type parsers (typeCast), BLOBs, streaming big data, transactions, driver events, database events (POST_EVENT), service manager, charsets/encoding, Firebird 3.0–6.0 features
17
17
  - [Extensive Examples](#extensive-examples) — DECFLOAT/INT128, query cancellation (AbortSignal), batch execution (bulk inserts incl. BLOBs), bulk-insert stream (batchStream), statement timeouts, scrollable cursors, RETURNING multiple rows, SKIP LOCKED, advanced pooling
@@ -200,6 +200,7 @@ options.dbCryptConfig = undefined; // optional; database encryption key for encr
200
200
  options.connectTimeout = 10000; // optional; timeout in ms for a single pool.get() attach operation (default: no timeout)
201
201
  options.enableKeepAlive = true; // TCP keepalive probing to detect dead/stale connections (same option names as mysql2); set to false to disable
202
202
  options.keepAliveInitialDelay = 60000; // ms a socket must be idle before the first keepalive probe (ignored when enableKeepAlive is false)
203
+ options.ipFamily = 4; // optional; force IPv4 (4) or IPv6 (6) when resolving host — also set by inet4:// / inet6:// URIs
203
204
  options.parallelWorkers = undefined; // optional; request multiple thread workers for maintenance/index tasks (FB >= 5)
204
205
  options.maxInlineBlobSize = undefined; // optional; threshold size in bytes for inline blob transmission (default 65535, FB >= 5.0.3)
205
206
  options.maxNegotiatedProtocols = undefined; // optional; cap how many protocol versions are offered, oldest first (default: all, up to Protocol 20; set to 10 to stop at Protocol 19)
@@ -248,6 +249,20 @@ be passed as query parameters. IPv6 hosts use brackets:
248
249
  `firebird://[::1]:3050/employee`. The parser is exported as
249
250
  `Firebird.parseConnectionUri(uri)` if you need the resulting options object.
250
251
 
252
+ Firebird's own URL-style connection strings (Firebird 3+) are accepted with
253
+ the same rules, so a string that works with isql works here unchanged:
254
+
255
+ | URI | meaning |
256
+ | :--- | :--- |
257
+ | `inet://host/employee` | same as `firebird://host/employee` |
258
+ | `inet://host:3051//var/fb/prod.fdb` | host, port and an absolute path |
259
+ | `inet4://host/employee` | resolve `host` as IPv4 only (sets `ipFamily: 4`) |
260
+ | `inet6://[::1]/employee` | IPv6 only (sets `ipFamily: 6`) |
261
+
262
+ `inet://` strings may carry credentials and query options exactly like
263
+ `firebird://` ones. The local-only transports `xnet://` and `wnet://` are
264
+ rejected — this driver speaks TCP only.
265
+
251
266
  ### Traditional connection strings (old style)
252
267
 
253
268
  The classic Firebird connection string format — the same
package/lib/types.d.ts CHANGED
@@ -432,6 +432,12 @@ export interface Options {
432
432
  * probe is sent (default 60000). Ignored when enableKeepAlive is false.
433
433
  */
434
434
  keepAliveInitialDelay?: number;
435
+ /**
436
+ * Force the TCP socket to IPv4 (4) or IPv6 (6) when resolving `host`.
437
+ * Unset lets the resolver choose. Set automatically by `inet4://` /
438
+ * `inet6://` connection URIs.
439
+ */
440
+ ipFamily?: 4 | 6;
435
441
  pluginName?: string;
436
442
  parallelWorkers?: number;
437
443
  maxInlineBlobSize?: number;
package/lib/uri.d.ts CHANGED
@@ -5,9 +5,11 @@
5
5
  ***************************************/
6
6
  import type { Options } from './types';
7
7
  /**
8
- * Parse a firebird:// connection URI into an options object.
8
+ * Parse a firebird:// or inet:// connection URI into an options object.
9
9
  *
10
10
  * firebird://user:password@host:port/database?option=value&...
11
+ * inet://host:port/database (Firebird's own URL style;
12
+ * inet4://... / inet6://... also force IPv4 / IPv6)
11
13
  *
12
14
  * The database part:
13
15
  * firebird://host/employee → alias "employee"
@@ -45,13 +47,15 @@ export declare function parseConnectionUri(uri: string): Options;
45
47
  */
46
48
  export declare function parseOldStyleConnectionString(str: string): Options;
47
49
  /**
48
- * Parse any connection string the driver accepts: a firebird:// URI, or a
49
- * traditional [host[/port]:]database string when there is no scheme.
50
+ * Parse any connection string the driver accepts: a firebird:// or
51
+ * inet[4|6]:// URI, or a traditional [host[/port]:]database string when
52
+ * there is no scheme.
50
53
  */
51
54
  export declare function parseConnectionString(str: string): Options;
52
55
  /**
53
- * Accept either an options object or a connection string (firebird:// URI
54
- * or traditional host[/port]:database) everywhere options are taken.
56
+ * Accept either an options object or a connection string (firebird:// or
57
+ * inet:// URI, or traditional host[/port]:database) everywhere options are
58
+ * taken.
55
59
  * Strings are parsed; objects pass through unchanged.
56
60
  */
57
61
  export declare function normalizeOptions<T>(options: T | string): T;
package/lib/uri.js CHANGED
@@ -22,7 +22,7 @@ const NUMBER_KEYS = new Set([
22
22
  'port', 'pageSize', 'timeout', 'retryConnectionInterval',
23
23
  'blobChunkSize', 'blobReadChunkSize', 'wireCrypt', 'parallelWorkers',
24
24
  'maxInlineBlobSize', 'maxNegotiatedProtocols', 'connectTimeout',
25
- 'min', 'idleTimeoutMillis', 'keepAliveInitialDelay',
25
+ 'min', 'idleTimeoutMillis', 'keepAliveInitialDelay', 'ipFamily',
26
26
  ]);
27
27
  function coerce(key, value) {
28
28
  if (BOOLEAN_KEYS.has(key)) {
@@ -38,9 +38,29 @@ function coerce(key, value) {
38
38
  return value;
39
39
  }
40
40
  /**
41
- * Parse a firebird:// connection URI into an options object.
41
+ * URI schemes accepted by parseConnectionUri. `firebird://` is this driver's
42
+ * own scheme; `inet://`, `inet4://` and `inet6://` are Firebird's native
43
+ * URL-style connection strings (Firebird 3+), so a string that works with
44
+ * isql works here too. inet4/inet6 pin the socket's IP family (the
45
+ * `ipFamily` option); inet and firebird let the resolver pick.
46
+ */
47
+ const URI_SCHEMES = {
48
+ 'firebird:': undefined,
49
+ 'inet:': undefined,
50
+ 'inet4:': 4,
51
+ 'inet6:': 6,
52
+ };
53
+ /**
54
+ * Firebird protocols this driver cannot speak: they are local-machine IPC
55
+ * transports (shared memory / named pipes), not TCP.
56
+ */
57
+ const LOCAL_SCHEMES = /^(xnet|wnet):$/;
58
+ /**
59
+ * Parse a firebird:// or inet:// connection URI into an options object.
42
60
  *
43
61
  * firebird://user:password@host:port/database?option=value&...
62
+ * inet://host:port/database (Firebird's own URL style;
63
+ * inet4://... / inet6://... also force IPv4 / IPv6)
44
64
  *
45
65
  * The database part:
46
66
  * firebird://host/employee → alias "employee"
@@ -64,11 +84,20 @@ function parseConnectionUri(uri) {
64
84
  catch (e) {
65
85
  throw new Error('Invalid connection URI: ' + uri);
66
86
  }
67
- if (url.protocol !== 'firebird:') {
68
- throw new Error('Unsupported connection URI scheme "' + url.protocol.replace(/:$/, '') +
69
- '" (expected firebird://...)');
87
+ var scheme = url.protocol.toLowerCase();
88
+ if (LOCAL_SCHEMES.test(scheme)) {
89
+ throw new Error('Unsupported connection URI scheme "' + scheme.replace(/:$/, '') +
90
+ '" (local IPC transports are not available over the wire — use inet:// or firebird://)');
91
+ }
92
+ if (!Object.prototype.hasOwnProperty.call(URI_SCHEMES, scheme)) {
93
+ throw new Error('Unsupported connection URI scheme "' + scheme.replace(/:$/, '') +
94
+ '" (expected firebird://, inet://, inet4:// or inet6://)');
70
95
  }
71
96
  var options = {};
97
+ var ipFamily = URI_SCHEMES[scheme];
98
+ if (ipFamily !== undefined) {
99
+ options.ipFamily = ipFamily;
100
+ }
72
101
  if (url.hostname) {
73
102
  // URL keeps IPv6 hostnames bracketed ([::1]); net.connect wants them bare
74
103
  options.host = url.hostname.replace(/^\[(.*)\]$/, '$1');
@@ -172,8 +201,9 @@ function parseOldStyleConnectionString(str) {
172
201
  }
173
202
  const URI_SCHEME = /^[A-Za-z][A-Za-z0-9+.-]*:\/\//;
174
203
  /**
175
- * Parse any connection string the driver accepts: a firebird:// URI, or a
176
- * traditional [host[/port]:]database string when there is no scheme.
204
+ * Parse any connection string the driver accepts: a firebird:// or
205
+ * inet[4|6]:// URI, or a traditional [host[/port]:]database string when
206
+ * there is no scheme.
177
207
  */
178
208
  function parseConnectionString(str) {
179
209
  return URI_SCHEME.test(str)
@@ -181,8 +211,9 @@ function parseConnectionString(str) {
181
211
  : parseOldStyleConnectionString(str);
182
212
  }
183
213
  /**
184
- * Accept either an options object or a connection string (firebird:// URI
185
- * or traditional host[/port]:database) everywhere options are taken.
214
+ * Accept either an options object or a connection string (firebird:// or
215
+ * inet:// URI, or traditional host[/port]:database) everywhere options are
216
+ * taken.
186
217
  * Strings are parsed; objects pass through unchanged.
187
218
  */
188
219
  function normalizeOptions(options) {
@@ -168,7 +168,7 @@ class Connection {
168
168
  this._detachTimeout;
169
169
  this._detachCallback;
170
170
  this._detachAuto;
171
- this._socket = new socket_1.default(port, host, options.enableKeepAlive !== false, options.keepAliveInitialDelay);
171
+ this._socket = new socket_1.default(port, host, options.enableKeepAlive !== false, options.keepAliveInitialDelay, options.ipFamily);
172
172
  this._pending = [];
173
173
  this._isOpened = false;
174
174
  this._isClosed = false;
@@ -32,7 +32,7 @@ declare class Socket {
32
32
  encrypt: boolean;
33
33
  encryptCipher: any;
34
34
  decryptCipher: any;
35
- constructor(port: number, host: string, enableKeepAlive?: boolean, keepAliveInitialDelay?: number);
35
+ constructor(port: number, host: string, enableKeepAlive?: boolean, keepAliveInitialDelay?: number, ipFamily?: 4 | 6);
36
36
  /**
37
37
  * Decompress and/or decrypt data when received.
38
38
  * Override on data event.
@@ -82,9 +82,10 @@ class ChaChaCipher {
82
82
  */
83
83
  class Socket {
84
84
  static { this.Arc4 = Arc4; }
85
- constructor(port, host, enableKeepAlive = true, keepAliveInitialDelay = 60000) {
85
+ constructor(port, host, enableKeepAlive = true, keepAliveInitialDelay = 60000, ipFamily) {
86
86
  this.compress = false;
87
- this._socket = net_1.default.createConnection(port, host);
87
+ // family: 0 is "either" (Node's default); 4/6 pin the resolver
88
+ this._socket = net_1.default.createConnection({ port: port, host: host, family: ipFamily || 0 });
88
89
  this._socket.setNoDelay(true);
89
90
  // TCP keepalive probing detects dead/stale connections; the delay is
90
91
  // how long the socket must be idle before the first probe.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-firebird",
3
- "version": "2.15.1",
3
+ "version": "2.16.0",
4
4
  "description": "Pure JavaScript and Asynchronous Firebird client for Node.js.",
5
5
  "keywords": [
6
6
  "firebird",
package/src/types.ts CHANGED
@@ -493,6 +493,12 @@ export interface Options {
493
493
  * probe is sent (default 60000). Ignored when enableKeepAlive is false.
494
494
  */
495
495
  keepAliveInitialDelay?: number;
496
+ /**
497
+ * Force the TCP socket to IPv4 (4) or IPv6 (6) when resolving `host`.
498
+ * Unset lets the resolver choose. Set automatically by `inet4://` /
499
+ * `inet6://` connection URIs.
500
+ */
501
+ ipFamily?: 4 | 6;
496
502
  pluginName?: string;
497
503
  parallelWorkers?: number;
498
504
  maxInlineBlobSize?: number;
package/src/uri.ts CHANGED
@@ -20,7 +20,7 @@ const NUMBER_KEYS = new Set([
20
20
  'port', 'pageSize', 'timeout', 'retryConnectionInterval',
21
21
  'blobChunkSize', 'blobReadChunkSize', 'wireCrypt', 'parallelWorkers',
22
22
  'maxInlineBlobSize', 'maxNegotiatedProtocols', 'connectTimeout',
23
- 'min', 'idleTimeoutMillis', 'keepAliveInitialDelay',
23
+ 'min', 'idleTimeoutMillis', 'keepAliveInitialDelay', 'ipFamily',
24
24
  ]);
25
25
 
26
26
  function coerce(key: string, value: string): any {
@@ -38,9 +38,31 @@ function coerce(key: string, value: string): any {
38
38
  }
39
39
 
40
40
  /**
41
- * Parse a firebird:// connection URI into an options object.
41
+ * URI schemes accepted by parseConnectionUri. `firebird://` is this driver's
42
+ * own scheme; `inet://`, `inet4://` and `inet6://` are Firebird's native
43
+ * URL-style connection strings (Firebird 3+), so a string that works with
44
+ * isql works here too. inet4/inet6 pin the socket's IP family (the
45
+ * `ipFamily` option); inet and firebird let the resolver pick.
46
+ */
47
+ const URI_SCHEMES: Record<string, 4 | 6 | undefined> = {
48
+ 'firebird:': undefined,
49
+ 'inet:': undefined,
50
+ 'inet4:': 4,
51
+ 'inet6:': 6,
52
+ };
53
+
54
+ /**
55
+ * Firebird protocols this driver cannot speak: they are local-machine IPC
56
+ * transports (shared memory / named pipes), not TCP.
57
+ */
58
+ const LOCAL_SCHEMES = /^(xnet|wnet):$/;
59
+
60
+ /**
61
+ * Parse a firebird:// or inet:// connection URI into an options object.
42
62
  *
43
63
  * firebird://user:password@host:port/database?option=value&...
64
+ * inet://host:port/database (Firebird's own URL style;
65
+ * inet4://... / inet6://... also force IPv4 / IPv6)
44
66
  *
45
67
  * The database part:
46
68
  * firebird://host/employee → alias "employee"
@@ -64,13 +86,23 @@ export function parseConnectionUri(uri: string): Options {
64
86
  throw new Error('Invalid connection URI: ' + uri);
65
87
  }
66
88
 
67
- if (url.protocol !== 'firebird:') {
68
- throw new Error('Unsupported connection URI scheme "' + url.protocol.replace(/:$/, '') +
69
- '" (expected firebird://...)');
89
+ var scheme = url.protocol.toLowerCase();
90
+ if (LOCAL_SCHEMES.test(scheme)) {
91
+ throw new Error('Unsupported connection URI scheme "' + scheme.replace(/:$/, '') +
92
+ '" (local IPC transports are not available over the wire — use inet:// or firebird://)');
93
+ }
94
+ if (!Object.prototype.hasOwnProperty.call(URI_SCHEMES, scheme)) {
95
+ throw new Error('Unsupported connection URI scheme "' + scheme.replace(/:$/, '') +
96
+ '" (expected firebird://, inet://, inet4:// or inet6://)');
70
97
  }
71
98
 
72
99
  var options: any = {};
73
100
 
101
+ var ipFamily = URI_SCHEMES[scheme];
102
+ if (ipFamily !== undefined) {
103
+ options.ipFamily = ipFamily;
104
+ }
105
+
74
106
  if (url.hostname) {
75
107
  // URL keeps IPv6 hostnames bracketed ([::1]); net.connect wants them bare
76
108
  options.host = url.hostname.replace(/^\[(.*)\]$/, '$1');
@@ -182,8 +214,9 @@ export function parseOldStyleConnectionString(str: string): Options {
182
214
  const URI_SCHEME = /^[A-Za-z][A-Za-z0-9+.-]*:\/\//;
183
215
 
184
216
  /**
185
- * Parse any connection string the driver accepts: a firebird:// URI, or a
186
- * traditional [host[/port]:]database string when there is no scheme.
217
+ * Parse any connection string the driver accepts: a firebird:// or
218
+ * inet[4|6]:// URI, or a traditional [host[/port]:]database string when
219
+ * there is no scheme.
187
220
  */
188
221
  export function parseConnectionString(str: string): Options {
189
222
  return URI_SCHEME.test(str)
@@ -192,8 +225,9 @@ export function parseConnectionString(str: string): Options {
192
225
  }
193
226
 
194
227
  /**
195
- * Accept either an options object or a connection string (firebird:// URI
196
- * or traditional host[/port]:database) everywhere options are taken.
228
+ * Accept either an options object or a connection string (firebird:// or
229
+ * inet:// URI, or traditional host[/port]:database) everywhere options are
230
+ * taken.
197
231
  * Strings are parsed; objects pass through unchanged.
198
232
  */
199
233
  export function normalizeOptions<T>(options: T | string): T {
@@ -186,7 +186,8 @@ class Connection {
186
186
  this._detachAuto;
187
187
  this._socket = new Socket(port, host,
188
188
  options.enableKeepAlive !== false,
189
- options.keepAliveInitialDelay);
189
+ options.keepAliveInitialDelay,
190
+ options.ipFamily);
190
191
  this._pending = [];
191
192
  this._isOpened = false;
192
193
  this._isClosed = false;
@@ -109,8 +109,9 @@ class Socket {
109
109
  encryptCipher: any;
110
110
  decryptCipher: any;
111
111
 
112
- constructor(port: number, host: string, enableKeepAlive = true, keepAliveInitialDelay = 60000) {
113
- this._socket = net.createConnection(port, host);
112
+ constructor(port: number, host: string, enableKeepAlive = true, keepAliveInitialDelay = 60000, ipFamily?: 4 | 6) {
113
+ // family: 0 is "either" (Node's default); 4/6 pin the resolver
114
+ this._socket = net.createConnection({ port: port, host: host, family: ipFamily || 0 });
114
115
  this._socket.setNoDelay(true);
115
116
  // TCP keepalive probing detects dead/stale connections; the delay is
116
117
  // how long the socket must be idle before the first probe.