node-firebird 2.15.0 → 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 +16 -1
- package/lib/pool.js +16 -5
- package/lib/types.d.ts +6 -0
- package/lib/uri.d.ts +9 -5
- package/lib/uri.js +40 -9
- package/lib/wire/connection.js +1 -1
- package/lib/wire/socket.d.ts +1 -1
- package/lib/wire/socket.js +3 -2
- package/package.json +2 -2
- package/src/pool.ts +15 -5
- package/src/types.ts +6 -0
- package/src/uri.ts +43 -9
- package/src/wire/connection.ts +2 -1
- package/src/wire/socket.ts +3 -2
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/pool.js
CHANGED
|
@@ -258,6 +258,12 @@ class Pool extends events_1.default.EventEmitter {
|
|
|
258
258
|
self.internaldb.splice(self.internaldb.indexOf(db), 1);
|
|
259
259
|
self.emit('remove', db);
|
|
260
260
|
}
|
|
261
|
+
else if (self._destroyed) {
|
|
262
|
+
// destroy() deliberately does not wait for checked-out
|
|
263
|
+
// connections. Close one for good when its caller later
|
|
264
|
+
// releases it instead of returning it to a stopped pool.
|
|
265
|
+
self._retire(db);
|
|
266
|
+
}
|
|
261
267
|
else if (self._isExpired(db)) {
|
|
262
268
|
// worn out (maxUses / maxLifetimeMillis): close it
|
|
263
269
|
// for good instead of returning it to the idle
|
|
@@ -318,7 +324,7 @@ class Pool extends events_1.default.EventEmitter {
|
|
|
318
324
|
callback();
|
|
319
325
|
}
|
|
320
326
|
}
|
|
321
|
-
this.internaldb.forEach(function (db) {
|
|
327
|
+
this.internaldb.slice().forEach(function (db) {
|
|
322
328
|
if (db.connection._pooled === false) {
|
|
323
329
|
detachCallback();
|
|
324
330
|
return;
|
|
@@ -326,15 +332,20 @@ class Pool extends events_1.default.EventEmitter {
|
|
|
326
332
|
// check if the db is not free into the pool otherwise user should manual detach it
|
|
327
333
|
var _db_in_pool = self.pooldb.indexOf(db);
|
|
328
334
|
if (_db_in_pool !== -1) {
|
|
329
|
-
self.pooldb.splice(_db_in_pool, 1);
|
|
330
335
|
db.connection._pooled = false;
|
|
331
|
-
|
|
332
|
-
|
|
336
|
+
// Keep the connection marked idle until physical detach emits
|
|
337
|
+
// its detach event. The listener above will then ignore that
|
|
338
|
+
// event instead of counting it as an active release.
|
|
339
|
+
db.detach(function (err) {
|
|
340
|
+
self._forget(db);
|
|
341
|
+
detachCallback(err);
|
|
342
|
+
});
|
|
333
343
|
}
|
|
334
344
|
else {
|
|
335
345
|
// [Fix 5] Connection is currently in use (dbinuse > 0).
|
|
336
346
|
// The caller is responsible for releasing it via detach().
|
|
337
|
-
// Count it down here so the destroy() callback is not blocked forever
|
|
347
|
+
// Count it down here so the destroy() callback is not blocked forever;
|
|
348
|
+
// the detach listener retires it when the caller releases it.
|
|
338
349
|
detachCallback();
|
|
339
350
|
}
|
|
340
351
|
});
|
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://
|
|
49
|
-
* traditional [host[/port]:]database string when
|
|
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://
|
|
54
|
-
* or traditional host[/port]:database) everywhere options are
|
|
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
|
-
*
|
|
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
|
-
|
|
68
|
-
|
|
69
|
-
|
|
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://
|
|
176
|
-
* traditional [host[/port]:]database string when
|
|
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://
|
|
185
|
-
* or traditional host[/port]:database) everywhere options are
|
|
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) {
|
package/lib/wire/connection.js
CHANGED
|
@@ -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;
|
package/lib/wire/socket.d.ts
CHANGED
|
@@ -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.
|
package/lib/wire/socket.js
CHANGED
|
@@ -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
|
-
|
|
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.
|
|
3
|
+
"version": "2.16.0",
|
|
4
4
|
"description": "Pure JavaScript and Asynchronous Firebird client for Node.js.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"firebird",
|
|
@@ -58,6 +58,6 @@
|
|
|
58
58
|
"@types/node": "^26.1.1",
|
|
59
59
|
"oxlint": "^0.15.11",
|
|
60
60
|
"typescript": "^7.0.2",
|
|
61
|
-
"vitest": "^4.
|
|
61
|
+
"vitest": "^4.1.11"
|
|
62
62
|
}
|
|
63
63
|
}
|
package/src/pool.ts
CHANGED
|
@@ -281,6 +281,11 @@ class Pool extends Events.EventEmitter {
|
|
|
281
281
|
if (db.connection._isClosed || db.connection._isDetach || db.connection._pooled === false) {
|
|
282
282
|
self.internaldb.splice(self.internaldb.indexOf(db), 1);
|
|
283
283
|
self.emit('remove', db);
|
|
284
|
+
} else if (self._destroyed) {
|
|
285
|
+
// destroy() deliberately does not wait for checked-out
|
|
286
|
+
// connections. Close one for good when its caller later
|
|
287
|
+
// releases it instead of returning it to a stopped pool.
|
|
288
|
+
self._retire(db);
|
|
284
289
|
} else if (self._isExpired(db)) {
|
|
285
290
|
// worn out (maxUses / maxLifetimeMillis): close it
|
|
286
291
|
// for good instead of returning it to the idle
|
|
@@ -348,7 +353,7 @@ class Pool extends Events.EventEmitter {
|
|
|
348
353
|
}
|
|
349
354
|
}
|
|
350
355
|
|
|
351
|
-
this.internaldb.forEach(function(db) {
|
|
356
|
+
this.internaldb.slice().forEach(function(db) {
|
|
352
357
|
if (db.connection._pooled === false) {
|
|
353
358
|
detachCallback();
|
|
354
359
|
return;
|
|
@@ -356,14 +361,19 @@ class Pool extends Events.EventEmitter {
|
|
|
356
361
|
// check if the db is not free into the pool otherwise user should manual detach it
|
|
357
362
|
var _db_in_pool = self.pooldb.indexOf(db);
|
|
358
363
|
if (_db_in_pool !== -1) {
|
|
359
|
-
self.pooldb.splice(_db_in_pool, 1);
|
|
360
364
|
db.connection._pooled = false;
|
|
361
|
-
|
|
362
|
-
|
|
365
|
+
// Keep the connection marked idle until physical detach emits
|
|
366
|
+
// its detach event. The listener above will then ignore that
|
|
367
|
+
// event instead of counting it as an active release.
|
|
368
|
+
db.detach(function(err?: any) {
|
|
369
|
+
self._forget(db);
|
|
370
|
+
detachCallback(err);
|
|
371
|
+
});
|
|
363
372
|
} else {
|
|
364
373
|
// [Fix 5] Connection is currently in use (dbinuse > 0).
|
|
365
374
|
// The caller is responsible for releasing it via detach().
|
|
366
|
-
// Count it down here so the destroy() callback is not blocked forever
|
|
375
|
+
// Count it down here so the destroy() callback is not blocked forever;
|
|
376
|
+
// the detach listener retires it when the caller releases it.
|
|
367
377
|
detachCallback();
|
|
368
378
|
}
|
|
369
379
|
});
|
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
|
-
*
|
|
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
|
-
|
|
68
|
-
|
|
69
|
-
|
|
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://
|
|
186
|
-
* traditional [host[/port]:]database string when
|
|
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://
|
|
196
|
-
* or traditional host[/port]:database) everywhere options are
|
|
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 {
|
package/src/wire/connection.ts
CHANGED
|
@@ -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;
|
package/src/wire/socket.ts
CHANGED
|
@@ -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
|
-
|
|
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.
|