node-firebird 2.8.1 → 2.10.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.
Files changed (53) hide show
  1. package/README.md +268 -7
  2. package/lib/index.d.ts +17 -9
  3. package/lib/index.js +42 -3
  4. package/lib/named-params.d.ts +42 -0
  5. package/lib/named-params.js +133 -0
  6. package/lib/pool.js +1 -1
  7. package/lib/srp.d.ts +3 -3
  8. package/lib/types.d.ts +185 -25
  9. package/lib/uri.d.ts +57 -0
  10. package/lib/uri.js +193 -0
  11. package/lib/wire/connection.d.ts +92 -59
  12. package/lib/wire/connection.js +286 -55
  13. package/lib/wire/const.d.ts +9 -1
  14. package/lib/wire/const.js +21 -9
  15. package/lib/wire/database.d.ts +51 -26
  16. package/lib/wire/database.js +26 -8
  17. package/lib/wire/eventConnection.js +5 -3
  18. package/lib/wire/query-stream.d.ts +18 -0
  19. package/lib/wire/query-stream.js +73 -0
  20. package/lib/wire/serialize.d.ts +18 -2
  21. package/lib/wire/serialize.js +7 -0
  22. package/lib/wire/service.d.ts +42 -0
  23. package/lib/wire/service.js +145 -0
  24. package/lib/wire/socket.d.ts +3 -1
  25. package/lib/wire/socket.js +5 -2
  26. package/lib/wire/statement.d.ts +40 -20
  27. package/lib/wire/statement.js +26 -6
  28. package/lib/wire/transaction.d.ts +32 -18
  29. package/lib/wire/transaction.js +50 -8
  30. package/lib/wire/wire-types.d.ts +116 -0
  31. package/lib/wire/wire-types.js +10 -0
  32. package/lib/wire/xsqlvar.d.ts +18 -18
  33. package/package.json +1 -1
  34. package/src/index.ts +54 -15
  35. package/src/messages.ts +1 -1
  36. package/src/named-params.ts +145 -0
  37. package/src/pool.ts +1 -1
  38. package/src/srp.ts +6 -6
  39. package/src/types.ts +183 -25
  40. package/src/unix-crypt.ts +9 -9
  41. package/src/uri.ts +204 -0
  42. package/src/wire/connection.ts +481 -234
  43. package/src/wire/const.ts +21 -9
  44. package/src/wire/database.ts +75 -43
  45. package/src/wire/eventConnection.ts +8 -5
  46. package/src/wire/query-stream.ts +80 -0
  47. package/src/wire/serialize.ts +29 -0
  48. package/src/wire/service.ts +188 -6
  49. package/src/wire/socket.ts +17 -8
  50. package/src/wire/statement.ts +68 -31
  51. package/src/wire/transaction.ts +85 -33
  52. package/src/wire/wire-types.ts +127 -0
  53. package/src/wire/xsqlvar.ts +9 -7
package/src/types.ts CHANGED
@@ -5,6 +5,8 @@
5
5
  // They now live in the TypeScript source tree and are compiled into the
6
6
  // published declaration files.
7
7
 
8
+ import type { Readable } from 'stream';
9
+
8
10
  export type DatabaseCallback = (err: any, db: Database) => void;
9
11
  export type TransactionCallback = (err: any, transaction: Transaction) => void;
10
12
  export type QueryCallback = (err: any, result: any[]) => void;
@@ -100,9 +102,22 @@ export type BatchOptions = {
100
102
  chunkSize?: number;
101
103
  };
102
104
 
105
+ /**
106
+ * Positional query parameters (array), or — when named placeholders are
107
+ * enabled via the `namedPlaceholders` connection/query option — values by
108
+ * placeholder name.
109
+ */
110
+ export type QueryParams = any[] | Record<string, any>;
111
+
103
112
  export type QueryOptions = {
104
113
  timeout?: number;
105
114
  scrollable?: boolean;
115
+ /**
116
+ * Per-query override of the `namedPlaceholders` connection option
117
+ * (e.g. disable it for one EXECUTE BLOCK statement whose body uses
118
+ * `:variable` PSQL references).
119
+ */
120
+ namedPlaceholders?: boolean;
106
121
  /**
107
122
  * Abort the query when the signal fires (Firebird 2.5+ / protocol 12+).
108
123
  * If the signal is already aborted the query is not sent at all and the
@@ -114,15 +129,32 @@ export type QueryOptions = {
114
129
  signal?: AbortSignal;
115
130
  }
116
131
 
132
+ export type QueryStreamOptions = QueryOptions & {
133
+ /**
134
+ * Rows buffered internally before fetching pauses (object-mode
135
+ * Readable highWaterMark, default 16).
136
+ */
137
+ highWaterMark?: number;
138
+ /** Emit array rows instead of objects (like db.execute). */
139
+ asObject?: boolean;
140
+ }
141
+
117
142
  export interface Database {
118
143
  detach(callback?: SimpleCallback): Database;
119
144
  transaction(options: TransactionOptions|Isolation|TransactionCallback, callback?: TransactionCallback): Database;
120
145
  newStatement(query: string, callback: (err: Error | null, statement: Statement) => void): Database;
121
- query(query: string, params: any[], callback: QueryCallback, options?: QueryOptions): Database;
122
- execute(query: string, params: any[], callback: QueryCallback, options?: QueryOptions): Database;
146
+ query(query: string, params: QueryParams, callback: QueryCallback, options?: QueryOptions): Database;
147
+ execute(query: string, params: QueryParams, callback: QueryCallback, options?: QueryOptions): Database;
123
148
  /** Bulk-execute in its own transaction, all-or-nothing (Firebird 4.0+). */
124
- executeBatch(query: string, rows: any[][], callback?: (err: any, result: BatchResult) => void, options?: BatchOptions): Database;
125
- sequentially(query: string, params: any[], rowCallback: SequentialCallback, callback: SimpleCallback, options?: QueryOptions | boolean): Database;
149
+ executeBatch(query: string, rows: QueryParams[], callback?: (err: any, result: BatchResult) => void, options?: BatchOptions): Database;
150
+ sequentially(query: string, params: QueryParams, rowCallback: SequentialCallback, callback: SimpleCallback, options?: QueryOptions | boolean): Database;
151
+ /**
152
+ * Run `query` and return an object-mode Readable emitting one row per
153
+ * chunk, with backpressure (fetching pauses while the buffer is full).
154
+ * Runs in its own transaction. Destroying the stream early aborts the
155
+ * fetch and releases the statement.
156
+ */
157
+ queryStream(query: string, params?: QueryParams, options?: QueryStreamOptions): Readable;
126
158
  drop(callback: SimpleCallback): void;
127
159
  escape(value: any): string;
128
160
  attachEvent(callback: any): this;
@@ -133,10 +165,10 @@ export interface Database {
133
165
 
134
166
  // Promise / async-await API (see README § Promises / async–await).
135
167
  // Result metadata is only available through the callback API.
136
- queryAsync<T = any>(query: string, params?: any[], options?: QueryOptions): Promise<T[]>;
137
- executeAsync<T = any>(query: string, params?: any[], options?: QueryOptions): Promise<T[]>;
138
- executeBatchAsync(query: string, rows: any[][], options?: BatchOptions): Promise<BatchResult>;
139
- sequentiallyAsync(query: string, params: any[] | undefined, rowCallback: SequentialCallback, options?: QueryOptions | boolean): Promise<void>;
168
+ queryAsync<T = any>(query: string, params?: QueryParams, options?: QueryOptions): Promise<T[]>;
169
+ executeAsync<T = any>(query: string, params?: QueryParams, options?: QueryOptions): Promise<T[]>;
170
+ executeBatchAsync(query: string, rows: QueryParams[], options?: BatchOptions): Promise<BatchResult>;
171
+ sequentiallyAsync(query: string, params: QueryParams | undefined, rowCallback: SequentialCallback, options?: QueryOptions | boolean): Promise<void>;
140
172
  sequentiallyAsync(query: string, rowCallback: SequentialCallback, options?: QueryOptions | boolean): Promise<void>;
141
173
  transactionAsync(options?: TransactionOptions | Isolation): Promise<Transaction>;
142
174
  startTransactionAsync(options?: TransactionOptions | Isolation): Promise<Transaction>;
@@ -158,21 +190,27 @@ export interface Database {
158
190
 
159
191
  export interface Transaction {
160
192
  newStatement(query: string, callback: (err: Error | null, statement: Statement) => void): void;
161
- query(query: string, params: any[], callback: QueryCallback, options?: QueryOptions): void;
162
- execute(query: string, params: any[], callback: QueryCallback, options?: QueryOptions): void;
193
+ query(query: string, params: QueryParams, callback: QueryCallback, options?: QueryOptions): void;
194
+ execute(query: string, params: QueryParams, callback: QueryCallback, options?: QueryOptions): void;
163
195
  /** Bulk-execute within this transaction; per-record failures do not roll back (Firebird 4.0+). */
164
- executeBatch(query: string, rows: any[][], callback?: (err: any, result: BatchResult) => void, options?: BatchOptions): void;
165
- sequentially(query: string, params: any[], rowCallback: SequentialCallback, callback: SimpleCallback, options?: QueryOptions | boolean): Database;
196
+ executeBatch(query: string, rows: QueryParams[], callback?: (err: any, result: BatchResult) => void, options?: BatchOptions): void;
197
+ sequentially(query: string, params: QueryParams, rowCallback: SequentialCallback, callback: SimpleCallback, options?: QueryOptions | boolean): Database;
198
+ /**
199
+ * Run `query` inside this transaction and return an object-mode
200
+ * Readable emitting one row per chunk, with backpressure. The
201
+ * transaction is NOT committed when the stream ends.
202
+ */
203
+ queryStream(query: string, params?: QueryParams, options?: QueryStreamOptions): Readable;
166
204
  commit(callback?: SimpleCallback): void;
167
205
  commitRetaining(callback?: SimpleCallback): void;
168
206
  rollback(callback?: SimpleCallback): void;
169
207
  rollbackRetaining(callback?: SimpleCallback): void;
170
208
 
171
209
  // Promise / async-await API
172
- queryAsync<T = any>(query: string, params?: any[], options?: QueryOptions): Promise<T[]>;
173
- executeAsync<T = any>(query: string, params?: any[], options?: QueryOptions): Promise<T[]>;
174
- executeBatchAsync(query: string, rows: any[][], options?: BatchOptions): Promise<BatchResult>;
175
- sequentiallyAsync(query: string, params: any[] | undefined, rowCallback: SequentialCallback, options?: QueryOptions | boolean): Promise<void>;
210
+ queryAsync<T = any>(query: string, params?: QueryParams, options?: QueryOptions): Promise<T[]>;
211
+ executeAsync<T = any>(query: string, params?: QueryParams, options?: QueryOptions): Promise<T[]>;
212
+ executeBatchAsync(query: string, rows: QueryParams[], options?: BatchOptions): Promise<BatchResult>;
213
+ sequentiallyAsync(query: string, params: QueryParams | undefined, rowCallback: SequentialCallback, options?: QueryOptions | boolean): Promise<void>;
176
214
  sequentiallyAsync(query: string, rowCallback: SequentialCallback, options?: QueryOptions | boolean): Promise<void>;
177
215
  newStatementAsync(query: string): Promise<Statement>;
178
216
  commitAsync(): Promise<void>;
@@ -185,17 +223,17 @@ export interface Statement {
185
223
  close(callback?: SimpleCallback): void;
186
224
  drop(callback?: SimpleCallback): void;
187
225
  release(callback?: SimpleCallback): void;
188
- execute(transaction: Transaction, params: any[], callback: QueryCallback, options?: QueryOptions): void;
226
+ execute(transaction: Transaction, params: QueryParams, callback: QueryCallback, options?: QueryOptions): void;
189
227
  fetch(transaction: Transaction, count: number, callback: QueryCallback): void;
190
228
  fetchScroll(transaction: Transaction, direction: 'NEXT' | 'PRIOR' | 'FIRST' | 'LAST' | 'ABSOLUTE' | 'RELATIVE' | number, offset: number, count: number, callback: QueryCallback): void;
191
229
  fetchAll(transaction: Transaction, callback: QueryCallback): void;
192
230
 
193
231
  /** Execute this prepared statement once per row (Firebird 4.0+ batch API). */
194
- executeBatch(transaction: Transaction, rows: any[][], callback?: (err: any, result: BatchResult) => void, options?: BatchOptions): void;
232
+ executeBatch(transaction: Transaction, rows: QueryParams[], callback?: (err: any, result: BatchResult) => void, options?: BatchOptions): void;
195
233
 
196
234
  // Promise / async-await API
197
- executeAsync(transaction: Transaction, params?: any[], options?: QueryOptions): Promise<any>;
198
- executeBatchAsync(transaction: Transaction, rows: any[][], options?: BatchOptions): Promise<BatchResult>;
235
+ executeAsync(transaction: Transaction, params?: QueryParams, options?: QueryOptions): Promise<any>;
236
+ executeBatchAsync(transaction: Transaction, rows: QueryParams[], options?: BatchOptions): Promise<BatchResult>;
199
237
  fetchAsync(transaction: Transaction, count: number | 'all'): Promise<any>;
200
238
  fetchScrollAsync(transaction: Transaction, direction: 'NEXT' | 'PRIOR' | 'FIRST' | 'LAST' | 'ABSOLUTE' | 'RELATIVE' | number, offset?: number, count?: number): Promise<any>;
201
239
  fetchAllAsync(transaction: Transaction): Promise<any>;
@@ -266,6 +304,26 @@ export interface Options {
266
304
  blobReadChunkSize?: number;
267
305
  wireCrypt?: number; // WIRE_CRYPT_DISABLE or WIRE_CRYPT_ENABLE
268
306
  wireCompression?: boolean;
307
+ /**
308
+ * Enable named placeholders: SQL may use `:name` markers and params may
309
+ * be a values-by-name object (`db.query('... WHERE id = :id', { id: 1 })`).
310
+ * Placeholders are rewritten client-side to positional `?` before
311
+ * preparing; positional arrays keep working unchanged. Off by default
312
+ * because `EXECUTE BLOCK` bodies use `:variable` for PSQL references —
313
+ * with this option on, run such statements with positional params or a
314
+ * per-query `namedPlaceholders: false` override.
315
+ */
316
+ namedPlaceholders?: boolean;
317
+ /**
318
+ * TCP keepalive probing to detect dead/stale connections (same option
319
+ * names as mysql2). On by default; set false to disable.
320
+ */
321
+ enableKeepAlive?: boolean;
322
+ /**
323
+ * Milliseconds a socket must be idle before the first TCP keepalive
324
+ * probe is sent (default 60000). Ignored when enableKeepAlive is false.
325
+ */
326
+ keepAliveInitialDelay?: number;
269
327
  pluginName?: string;
270
328
  parallelWorkers?: number;
271
329
  maxInlineBlobSize?: number;
@@ -297,11 +355,11 @@ export interface Options {
297
355
  /**
298
356
  * **Firebird 6.0+ only (Protocol 20+)**
299
357
  *
300
- * Sets the session's current schema at connection time. Equivalent to
301
- * executing `SET SCHEMA <name>` immediately after connecting.
302
- *
303
- * Unqualified object references (tables, procedures, etc.) that do not
304
- * match any schema in the `searchPath` fall back to `PUBLIC`.
358
+ * Sets the session's current schema at connection time. `CURRENT_SCHEMA`
359
+ * in Firebird is the first existing schema of the search path, so this
360
+ * option is implemented by putting the schema at the front of the
361
+ * `searchPath` sent to the server (with `PUBLIC` kept as a fallback when
362
+ * no explicit `searchPath` is given).
305
363
  *
306
364
  * Example: `defaultSchema: 'myapp'`
307
365
  */
@@ -323,6 +381,18 @@ export interface Options {
323
381
  * (typically `PUBLIC` then `SYSTEM`).
324
382
  */
325
383
  searchPath?: string | string[];
384
+ /**
385
+ * **Firebird 6.0+ only**
386
+ *
387
+ * Owner of a newly created database (`isc_dpb_owner`), allowing a
388
+ * superuser to create a database owned by another user
389
+ * ([firebird#7718](https://github.com/FirebirdSQL/firebird/issues/7718)).
390
+ * Only honored by `create`/`attachOrCreate` when the database is
391
+ * created; ignored on plain attach and by older servers.
392
+ *
393
+ * Example: `owner: 'APP_OWNER'`
394
+ */
395
+ owner?: string;
326
396
  /**
327
397
  * **Firebird 6.0+ only (Protocol 20+)**
328
398
  *
@@ -331,8 +401,58 @@ export interface Options {
331
401
  * text/BLOB columns back into JavaScript objects/arrays.
332
402
  */
333
403
  jsonAsObject?: boolean;
404
+ /**
405
+ * Custom type parser (mysql2-style). Called for every column value of
406
+ * every result row (including NULLs); whatever it returns becomes the
407
+ * value in the row. Call `next()` to get the value the driver would
408
+ * produce by default (after `blobAsText`/`jsonAsObject` are applied).
409
+ *
410
+ * ```js
411
+ * typeCast: (column, next) =>
412
+ * column.typeName === 'INT64' ? Number(next()) : next()
413
+ * ```
414
+ *
415
+ * Non-text BLOB columns reach the hook as the usual fetch function;
416
+ * text BLOBs with `blobAsText` reach it as the resolved string. The
417
+ * hook must be a pure function: a row can be decoded more than once
418
+ * when a response spans TCP packets.
419
+ */
420
+ typeCast?: TypeCastFunction;
421
+ /**
422
+ * Per-connection LRU cache of prepared statements (like mysql2's
423
+ * statement cache). `db.query`/`tx.query` and friends transparently
424
+ * reuse the prepared handle for a repeated SQL string, skipping the
425
+ * prepare round-trip on hot paths. The number is the maximum of idle
426
+ * cached statements; least-recently-used ones are dropped over the
427
+ * limit. 0 / unset = disabled. Statements that failed and DDL are
428
+ * never cached; concurrent runs of the same SQL never share a
429
+ * statement (extra preparations are simply not cached).
430
+ */
431
+ statementCacheSize?: number;
334
432
  }
335
433
 
434
+ /** Column metadata passed to the {@link Options.typeCast} hook. */
435
+ export interface TypeCastColumn {
436
+ /** Firebird SQL type code (see the exported `SQL_TYPES` map). */
437
+ type: number;
438
+ /** Friendly name of the type code: 'VARYING', 'INT64', 'BLOB', ... */
439
+ typeName: string;
440
+ /** Column subtype (e.g. 1 = text for BLOBs; charset id for strings). */
441
+ subType?: number;
442
+ /** Negative decimal scale for NUMERIC/DECIMAL columns (e.g. -2). */
443
+ scale?: number;
444
+ /** Declared length in bytes. */
445
+ length?: number;
446
+ /** Column name in the table. */
447
+ field?: string;
448
+ /** Table (relation) name. */
449
+ relation?: string;
450
+ /** Alias used in the SELECT list (the row key for object rows). */
451
+ alias?: string;
452
+ }
453
+
454
+ export type TypeCastFunction = (column: TypeCastColumn, next: () => any) => any;
455
+
336
456
  export interface SvcMgrOptions extends Options {
337
457
  manager: true; // Attach to ServiceManager
338
458
  }
@@ -542,4 +662,42 @@ export interface ServiceManager {
542
662
  hasRunningAction(options: ReadableOptions, callback: ReadableCallback): void;
543
663
  readusers(options: ReadableOptions, callback: ReadableCallback): void;
544
664
  readlimbo(options: ReadableOptions, callback: ReadableCallback): void;
665
+
666
+ // Promise / async-await API (see README § Promises / async–await).
667
+ detachAsync(force?: boolean): Promise<void>;
668
+ backupAsync(options: BackupOptions): Promise<NodeJS.ReadableStream>;
669
+ nbackupAsync(options: BackupOptions): Promise<NodeJS.ReadableStream>;
670
+ restoreAsync(options: RestoreOptions): Promise<NodeJS.ReadableStream>;
671
+ nrestoreAsync(options: NRestoreOptions): Promise<NodeJS.ReadableStream>;
672
+ setDialectAsync(db: string, dialect: 1 | 3): Promise<NodeJS.ReadableStream>;
673
+ setSweepintervalAsync(db: string, interval: number): Promise<any>;
674
+ setCachebufferAsync(db: string, nbpages: any): Promise<NodeJS.ReadableStream>;
675
+ BringOnlineAsync(db: string): Promise<NodeJS.ReadableStream>;
676
+ ShutdownAsync(db: string, kind: ShutdownKind, delay: number, mode?: ShutdownMode): Promise<NodeJS.ReadableStream>;
677
+ setShadowAsync(db: string, val: boolean): Promise<NodeJS.ReadableStream>;
678
+ setForcewriteAsync(db: string, val: boolean): Promise<NodeJS.ReadableStream>;
679
+ setReservespaceAsync(db: string, val: boolean): Promise<NodeJS.ReadableStream>;
680
+ setReadonlyModeAsync(db: string): Promise<NodeJS.ReadableStream>;
681
+ setReadwriteModeAsync(db: string): Promise<NodeJS.ReadableStream>;
682
+ validateAsync(options: ValidateOptions): Promise<NodeJS.ReadableStream>;
683
+ commitAsync(db: string, transactid: number): Promise<NodeJS.ReadableStream>;
684
+ rollbackAsync(db: string, transactid: number): Promise<NodeJS.ReadableStream>;
685
+ recoverAsync(db: string, transactid: number): Promise<NodeJS.ReadableStream>;
686
+ getStatsAsync(options: StatsOptions): Promise<NodeJS.ReadableStream>;
687
+ getLogAsync(options: ReadableOptions): Promise<NodeJS.ReadableStream>;
688
+ getUsersAsync(username?: string | null): Promise<ServerInfo>;
689
+ addUserAsync(username: string, password: string, info?: UserInfo): Promise<NodeJS.ReadableStream>;
690
+ editUserAsync(username: string, info: UserInfo): Promise<NodeJS.ReadableStream>;
691
+ removeUserAsync(username: string, rolename?: string | null): Promise<NodeJS.ReadableStream>;
692
+ getFbserverInfosAsync(infos?: ServerInfoReq, options?: { buffersize?: number, timeout?: number }): Promise<ServerInfo>;
693
+ startTraceAsync(options: TraceOptions): Promise<NodeJS.ReadableStream>;
694
+ suspendTraceAsync(options: TraceOptions): Promise<NodeJS.ReadableStream>;
695
+ resumeTraceAsync(options: TraceOptions): Promise<NodeJS.ReadableStream>;
696
+ stopTraceAsync(options: TraceOptions): Promise<NodeJS.ReadableStream>;
697
+ getTraceListAsync(options?: ReadableOptions): Promise<NodeJS.ReadableStream>;
698
+ readlineAsync(options?: ReadableOptions): Promise<{ result: number, line: string }>;
699
+ readeofAsync(options?: ReadableOptions): Promise<{ result: number, line: string }>;
700
+ hasRunningActionAsync(options?: ReadableOptions): Promise<any>;
701
+ readusersAsync(options?: ReadableOptions): Promise<any>;
702
+ readlimboAsync(options?: ReadableOptions): Promise<any>;
545
703
  }
package/src/unix-crypt.ts CHANGED
@@ -152,25 +152,25 @@ var SPTRANS=
152
152
  0x8200020, 32768, 0x208020 ]
153
153
  ];
154
154
 
155
- function hPermOp(a, n, m) {
155
+ function hPermOp(a: number, n: number, m: number) {
156
156
  var t = (a << 16 - n ^ a) & m;
157
157
  a = a ^ t ^ t >>> 16 - n;
158
158
  return a;
159
159
  }
160
160
 
161
- function intToFourBytes(iValue, b, offset) {
161
+ function intToFourBytes(iValue: number, b: Buffer, offset: number) {
162
162
  b[offset++] = iValue & 0xff;
163
163
  b[offset++] = iValue >>> 8 & 0xff;
164
164
  b[offset++] = iValue >>> 16 & 0xff;
165
165
  b[offset++] = iValue >>> 24 & 0xff;
166
166
  }
167
167
 
168
- function byteToUnsigned(b) {
168
+ function byteToUnsigned(b: number) {
169
169
  var value = b;
170
170
  return value < 0 ? value + 256 : value;
171
171
  }
172
172
 
173
- function fourBytesToInt(b, offset) {
173
+ function fourBytesToInt(b: Buffer, offset: number) {
174
174
  var value = byteToUnsigned(b[offset++]);
175
175
  value |= byteToUnsigned(b[offset++]) << 8;
176
176
  value |= byteToUnsigned(b[offset++]) << 16;
@@ -178,7 +178,7 @@ function fourBytesToInt(b, offset) {
178
178
  return value;
179
179
  }
180
180
 
181
- function permOp(a, b, n, m, results) {
181
+ function permOp(a: number, b: number, n: number, m: number, results: number[]) {
182
182
  var t = (a >>> n ^ b) & m;
183
183
  a ^= t << n;
184
184
  b ^= t;
@@ -186,8 +186,8 @@ function permOp(a, b, n, m, results) {
186
186
  results[1] = b;
187
187
  }
188
188
 
189
- function desSetKey(key) {
190
- var schedule = [];
189
+ function desSetKey(key: Buffer) {
190
+ var schedule: number[] = [];
191
191
  var c = fourBytesToInt(key, 0);
192
192
  var d = fourBytesToInt(key, 4);
193
193
  var results = [0, 0];
@@ -232,7 +232,7 @@ function desSetKey(key) {
232
232
  return schedule;
233
233
  }
234
234
 
235
- function dEncrypt(el, r, s, e0, e1, sArr) {
235
+ function dEncrypt(el: number, r: number, s: number, e0: number, e1: number, sArr: number[]) {
236
236
  var v = r ^ r >>> 16;
237
237
  var u = v & e0;
238
238
  v &= e1;
@@ -245,7 +245,7 @@ function dEncrypt(el, r, s, e0, e1, sArr) {
245
245
  return el;
246
246
  }
247
247
 
248
- function body(schedule, eSwap0, eSwap1) {
248
+ function body(schedule: number[], eSwap0: number, eSwap1: number) {
249
249
  var left = 0;
250
250
  var right = 0;
251
251
  var t = 0;
package/src/uri.ts ADDED
@@ -0,0 +1,204 @@
1
+ /***************************************
2
+ *
3
+ * Connection URI strings
4
+ *
5
+ ***************************************/
6
+
7
+ import type { Options } from './types';
8
+
9
+ /**
10
+ * Option keys coerced to boolean when they arrive as URI query parameters.
11
+ * "1"/"true"/"yes"/"on" (case-insensitive) → true, everything else → false.
12
+ */
13
+ const BOOLEAN_KEYS = new Set([
14
+ 'lowercase_keys', 'blobAsText', 'wireCompression', 'manager',
15
+ 'namedPlaceholders', 'enableKeepAlive',
16
+ ]);
17
+
18
+ /** Option keys coerced to number when they arrive as URI query parameters. */
19
+ const NUMBER_KEYS = new Set([
20
+ 'port', 'pageSize', 'timeout', 'retryConnectionInterval',
21
+ 'blobChunkSize', 'blobReadChunkSize', 'wireCrypt', 'parallelWorkers',
22
+ 'maxInlineBlobSize', 'maxNegotiatedProtocols', 'connectTimeout',
23
+ 'min', 'idleTimeoutMillis', 'keepAliveInitialDelay',
24
+ ]);
25
+
26
+ function coerce(key: string, value: string): any {
27
+ if (BOOLEAN_KEYS.has(key)) {
28
+ return /^(1|true|yes|on)$/i.test(value);
29
+ }
30
+ if (NUMBER_KEYS.has(key)) {
31
+ var n = Number(value);
32
+ if (Number.isNaN(n)) {
33
+ throw new Error('Invalid numeric value for connection URI option "' + key + '": ' + value);
34
+ }
35
+ return n;
36
+ }
37
+ return value;
38
+ }
39
+
40
+ /**
41
+ * Parse a firebird:// connection URI into an options object.
42
+ *
43
+ * firebird://user:password@host:port/database?option=value&...
44
+ *
45
+ * The database part:
46
+ * firebird://host/employee → alias "employee"
47
+ * firebird://host//var/db/prod.fdb → absolute path "/var/db/prod.fdb"
48
+ * firebird://host/var/db/prod.fdb → "/var/db/prod.fdb" (a database
49
+ * part with slashes is a path —
50
+ * aliases cannot contain "/")
51
+ * firebird://host/C:/db/prod.fdb → Windows path "C:/db/prod.fdb"
52
+ *
53
+ * Credentials and the database path are URL-decoded, so reserved characters
54
+ * can be percent-encoded (e.g. p%40ss for "p@ss"). Query parameters map
55
+ * 1:1 to option keys and are coerced to the option's type (booleans accept
56
+ * 1/true/yes/on). `user` and `password` may be given as query parameters
57
+ * instead of in the authority.
58
+ */
59
+ export function parseConnectionUri(uri: string): Options {
60
+ var url: URL;
61
+ try {
62
+ url = new URL(uri);
63
+ } catch (e) {
64
+ throw new Error('Invalid connection URI: ' + uri);
65
+ }
66
+
67
+ if (url.protocol !== 'firebird:') {
68
+ throw new Error('Unsupported connection URI scheme "' + url.protocol.replace(/:$/, '') +
69
+ '" (expected firebird://...)');
70
+ }
71
+
72
+ var options: any = {};
73
+
74
+ if (url.hostname) {
75
+ // URL keeps IPv6 hostnames bracketed ([::1]); net.connect wants them bare
76
+ options.host = url.hostname.replace(/^\[(.*)\]$/, '$1');
77
+ }
78
+ if (url.port) {
79
+ options.port = Number(url.port);
80
+ }
81
+ if (url.username) {
82
+ options.user = decodeURIComponent(url.username);
83
+ }
84
+ if (url.password) {
85
+ options.password = decodeURIComponent(url.password);
86
+ }
87
+
88
+ var database = decodeURIComponent(url.pathname || '');
89
+ if (database.startsWith('/')) {
90
+ database = database.slice(1);
91
+ }
92
+ // A database part with path separators is a filesystem path, not an
93
+ // alias (aliases cannot contain "/") — restore the leading slash unless
94
+ // it is a Windows drive path or already absolute (double-slash form).
95
+ if (database.includes('/') && !database.startsWith('/') && !/^[A-Za-z]:\//.test(database)) {
96
+ database = '/' + database;
97
+ }
98
+ if (database) {
99
+ options.database = database;
100
+ }
101
+
102
+ url.searchParams.forEach(function(value, key) {
103
+ options[key] = coerce(key, value);
104
+ });
105
+
106
+ return options as Options;
107
+ }
108
+
109
+ /**
110
+ * Parse a traditional ("old style") Firebird connection string:
111
+ *
112
+ * [host[/port]:]{path | alias}
113
+ *
114
+ * employee → alias "employee" (default host)
115
+ * /var/fb/prod.fdb → local path (default host)
116
+ * C:\fbdata\prod.fdb → Windows path — a single character
117
+ * before ":" is a drive letter, not
118
+ * a host (same rule as Firebird)
119
+ * db.example.com:employee → host + alias
120
+ * db.example.com/3051:/var/fb/prod.fdb → host + port + path
121
+ * myserver:C:\fbdata\prod.fdb → host + Windows path
122
+ * [::1]/3050:employee → IPv6 host + port + alias
123
+ *
124
+ * Unlike firebird:// URIs, traditional strings carry no credentials or
125
+ * options — the driver defaults apply (SYSDBA/masterkey, port 3050).
126
+ * The port must be numeric; /etc/services names are not resolved.
127
+ */
128
+ export function parseOldStyleConnectionString(str: string): Options {
129
+ var options: any = {};
130
+ var host: string | null = null;
131
+ var port: string | null = null;
132
+ var database = str;
133
+
134
+ var ipv6 = /^\[([^\]]+)\](?:\/([^:]*))?:(.*)$/.exec(str);
135
+ if (ipv6) {
136
+ host = ipv6[1];
137
+ port = ipv6[2] !== undefined ? ipv6[2] : null;
138
+ database = ipv6[3];
139
+ } else {
140
+ var colon = str.indexOf(':');
141
+ if (colon === 0) {
142
+ throw new Error('Invalid connection string (empty host): ' + str);
143
+ }
144
+ // colon === 1 → single character before ":" is a drive letter;
145
+ // colon === -1 → no host part. Both leave the whole string as database.
146
+ if (colon > 1) {
147
+ var hostPart = str.slice(0, colon);
148
+ database = str.slice(colon + 1);
149
+ var slash = hostPart.indexOf('/');
150
+ if (slash !== -1) {
151
+ host = hostPart.slice(0, slash);
152
+ port = hostPart.slice(slash + 1);
153
+ if (!host) {
154
+ throw new Error('Invalid connection string (empty host): ' + str);
155
+ }
156
+ } else {
157
+ host = hostPart;
158
+ }
159
+ }
160
+ }
161
+
162
+ if (!database) {
163
+ throw new Error('Invalid connection string (empty database): ' + str);
164
+ }
165
+
166
+ if (host) {
167
+ options.host = host;
168
+ }
169
+ if (port !== null) {
170
+ var n = Number(port);
171
+ if (!/^\d+$/.test(port) || n < 1 || n > 65535) {
172
+ throw new Error('Invalid port in connection string "' + str +
173
+ '" (service names are not supported — use a numeric port)');
174
+ }
175
+ options.port = n;
176
+ }
177
+ options.database = database;
178
+
179
+ return options as Options;
180
+ }
181
+
182
+ const URI_SCHEME = /^[A-Za-z][A-Za-z0-9+.-]*:\/\//;
183
+
184
+ /**
185
+ * Parse any connection string the driver accepts: a firebird:// URI, or a
186
+ * traditional [host[/port]:]database string when there is no scheme.
187
+ */
188
+ export function parseConnectionString(str: string): Options {
189
+ return URI_SCHEME.test(str)
190
+ ? parseConnectionUri(str)
191
+ : parseOldStyleConnectionString(str);
192
+ }
193
+
194
+ /**
195
+ * Accept either an options object or a connection string (firebird:// URI
196
+ * or traditional host[/port]:database) everywhere options are taken.
197
+ * Strings are parsed; objects pass through unchanged.
198
+ */
199
+ export function normalizeOptions<T>(options: T | string): T {
200
+ if (typeof options === 'string') {
201
+ return parseConnectionString(options) as T;
202
+ }
203
+ return options;
204
+ }