vv-db-connection 0.0.0-stage → 1.0.2

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 (36) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +193 -2
  3. package/distjs/src/db/adapters/fb.d.ts +14 -0
  4. package/distjs/src/db/adapters/fb.js +269 -0
  5. package/distjs/src/db/adapters/fb.js.map +1 -0
  6. package/distjs/src/db/adapters/mssql.d.ts +14 -0
  7. package/distjs/src/db/adapters/mssql.js +155 -0
  8. package/distjs/src/db/adapters/mssql.js.map +1 -0
  9. package/distjs/src/db/adapters/ora.d.ts +15 -0
  10. package/distjs/src/db/adapters/ora.js +137 -0
  11. package/distjs/src/db/adapters/ora.js.map +1 -0
  12. package/distjs/src/db/adapters/pg.d.ts +16 -0
  13. package/distjs/src/db/adapters/pg.js +153 -0
  14. package/distjs/src/db/adapters/pg.js.map +1 -0
  15. package/distjs/src/db/connection.d.ts +49 -0
  16. package/distjs/src/db/connection.js +5 -0
  17. package/distjs/src/db/connection.js.map +1 -0
  18. package/distjs/src/db/index.d.ts +16 -0
  19. package/distjs/src/db/index.js +131 -0
  20. package/distjs/src/db/index.js.map +1 -0
  21. package/distjs/src/db/kind.d.ts +2 -0
  22. package/distjs/src/db/kind.js +2 -0
  23. package/distjs/src/db/kind.js.map +1 -0
  24. package/distjs/src/db/placeholders.d.ts +2 -0
  25. package/distjs/src/db/placeholders.js +21 -0
  26. package/distjs/src/db/placeholders.js.map +1 -0
  27. package/distjs/src/db/types.d.ts +40 -0
  28. package/distjs/src/db/types.js +2 -0
  29. package/distjs/src/db/types.js.map +1 -0
  30. package/distjs/src/index.d.ts +4 -0
  31. package/distjs/src/index.js +2 -0
  32. package/distjs/src/index.js.map +1 -0
  33. package/distjs/src/tresult.d.ts +7 -0
  34. package/distjs/src/tresult.js +2 -0
  35. package/distjs/src/tresult.js.map +1 -0
  36. package/package.json +52 -4
@@ -0,0 +1,155 @@
1
+ import sql from 'mssql';
2
+ import { mapPlaceholders } from '../placeholders.js';
3
+ // Converts :name placeholders to @name for mssql named parameters. A colon inside a
4
+ // string literal, a quoted identifier or a comment is left as it is.
5
+ function buildMssqlQuery(query) {
6
+ return mapPlaceholders(query, (name) => `@${name}`);
7
+ }
8
+ function toSchema(columns) {
9
+ return Object.values(columns).map((col) => ({
10
+ name: col.name,
11
+ kind: 'unknown',
12
+ len: col.length,
13
+ scale: col.scale,
14
+ precision: col.precision,
15
+ }));
16
+ }
17
+ function buildPoolConfig(p) {
18
+ const base = {
19
+ server: p.server,
20
+ port: p.port ?? 1433,
21
+ ...(p.database ? { database: p.database } : {}),
22
+ options: {
23
+ trustServerCertificate: p.trustServerCertificate ?? false,
24
+ encrypt: p.encrypt ?? false,
25
+ },
26
+ };
27
+ if (p.auth.kind === 'ntlm') {
28
+ return { ...base, authentication: { type: 'ntlm', options: { domain: p.auth.domain, userName: p.auth.user, password: p.auth.password } } };
29
+ }
30
+ return { ...base, user: p.auth.user, password: p.auth.password };
31
+ }
32
+ export class MssqlAdapter {
33
+ p;
34
+ constructor(p) {
35
+ this.p = p;
36
+ }
37
+ kind() { return 'mssql'; }
38
+ title() { return `${this.p.server}:${this.p.port || 'DEFAULT'} [${this.p.database ?? 'default'}]`; }
39
+ user() {
40
+ if (this.p.auth.kind === 'ntlm')
41
+ return `${this.p.auth.domain}\\${this.p.auth.user}`;
42
+ return this.p.auth.user;
43
+ }
44
+ versionQuery() { return 'SELECT @@VERSION AS ver'; }
45
+ pingQuery() { return 'SELECT 1 AS p'; }
46
+ async execStream(query, params, callback, signal) {
47
+ const pool = await new sql.ConnectionPool(buildPoolConfig(this.p)).connect();
48
+ if (signal?.aborted) {
49
+ try {
50
+ await pool.close();
51
+ }
52
+ catch { }
53
+ return;
54
+ }
55
+ return new Promise((resolve) => {
56
+ const request = pool.request();
57
+ request.stream = true;
58
+ for (const p of params) {
59
+ request.input(p.name, p.value);
60
+ }
61
+ let error;
62
+ let done = false;
63
+ const onAbort = () => {
64
+ if (done)
65
+ return;
66
+ done = true;
67
+ try {
68
+ request.cancel();
69
+ }
70
+ catch { }
71
+ void pool.close().catch(() => { });
72
+ resolve();
73
+ };
74
+ signal?.addEventListener('abort', onAbort);
75
+ request.on('recordset', (columns) => {
76
+ if (!done)
77
+ callback({ kind: 'schema', schema: toSchema(columns) });
78
+ });
79
+ request.on('row', (row) => {
80
+ if (!done)
81
+ callback({ kind: 'row', row });
82
+ });
83
+ request.on('error', (err) => {
84
+ error = String(err);
85
+ });
86
+ request.on('done', async () => {
87
+ if (done)
88
+ return;
89
+ done = true;
90
+ signal?.removeEventListener('abort', onAbort);
91
+ try {
92
+ await pool.close();
93
+ }
94
+ catch { }
95
+ callback({ kind: 'finish', error });
96
+ resolve();
97
+ });
98
+ request.query(buildMssqlQuery(query));
99
+ });
100
+ }
101
+ async execTxDynamic(cb, timeoutMs) {
102
+ const pool = await new sql.ConnectionPool(buildPoolConfig(this.p)).connect();
103
+ const txWork = async () => {
104
+ const tx = new sql.Transaction(pool);
105
+ try {
106
+ await tx.begin();
107
+ const exec = (query, params) => {
108
+ const req = new sql.Request(tx);
109
+ const q = buildMssqlQuery(query);
110
+ for (const p of params)
111
+ req.input(p.name, p.value);
112
+ return new Promise((resolve, reject) => {
113
+ const onUncaught = (err) => reject(err);
114
+ process.once('uncaughtException', onUncaught);
115
+ req.query(q)
116
+ .then((res) => { process.removeListener('uncaughtException', onUncaught); resolve({ rows: (res.recordset ?? []) }); })
117
+ .catch((err) => { process.removeListener('uncaughtException', onUncaught); reject(err); });
118
+ });
119
+ };
120
+ await cb(exec);
121
+ await tx.commit();
122
+ return { ok: true, result: undefined };
123
+ }
124
+ catch (err) {
125
+ try {
126
+ await tx.rollback();
127
+ }
128
+ catch { }
129
+ return { ok: false, error: String(err) };
130
+ }
131
+ finally {
132
+ try {
133
+ await pool.close();
134
+ }
135
+ catch { }
136
+ }
137
+ };
138
+ if (!timeoutMs)
139
+ return txWork();
140
+ let timer;
141
+ const timeoutPromise = new Promise((resolve) => {
142
+ timer = setTimeout(() => {
143
+ void pool.close().catch(() => { });
144
+ resolve({ ok: false, error: 'TIMEOUT' });
145
+ }, timeoutMs);
146
+ });
147
+ try {
148
+ return await Promise.race([txWork(), timeoutPromise]);
149
+ }
150
+ finally {
151
+ clearTimeout(timer);
152
+ }
153
+ }
154
+ }
155
+ //# sourceMappingURL=mssql.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mssql.js","sourceRoot":"","sources":["../../../../src/db/adapters/mssql.ts"],"names":[],"mappings":"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","sourcesContent":["import sql from 'mssql'\nimport type { IDbAdapter, TDbCol, TDbParam, TDbStreamEvent, TDbTxCb, TDbTxResult } from '../types'\nimport type { TConnMssql } from '../connection'\nimport type { TResult } from '../../tresult'\nimport { mapPlaceholders } from '../placeholders'\n\n// Converts :name placeholders to @name for mssql named parameters. A colon inside a\n// string literal, a quoted identifier or a comment is left as it is.\nfunction buildMssqlQuery(query: string): string {\n\treturn mapPlaceholders(query, (name) => `@${name}`)\n}\n\nfunction toSchema(columns: Record<string, any>): TDbCol[] {\n\treturn Object.values(columns).map((col) => ({\n\t\tname: col.name,\n\t\tkind: 'unknown',\n\t\tlen: col.length,\n\t\tscale: col.scale,\n\t\tprecision: col.precision,\n\t}))\n}\n\nfunction buildPoolConfig(p: TConnMssql): sql.config {\n\tconst base: sql.config = {\n\t\tserver: p.server,\n\t\tport: p.port ?? 1433,\n\t\t...(p.database ? { database: p.database } : {}),\n\t\toptions: {\n\t\t\ttrustServerCertificate: p.trustServerCertificate ?? false,\n\t\t\tencrypt: p.encrypt ?? false,\n\t\t},\n\t}\n\tif (p.auth.kind === 'ntlm') {\n\t\treturn { ...base, authentication: { type: 'ntlm', options: { domain: p.auth.domain, userName: p.auth.user, password: p.auth.password } } }\n\t}\n\treturn { ...base, user: p.auth.user, password: p.auth.password }\n}\n\nexport class MssqlAdapter implements IDbAdapter {\n\tconstructor(private readonly p: TConnMssql) {}\n\n\tkind(): 'mssql' { return 'mssql' }\n\ttitle(): string { return `${this.p.server}:${this.p.port || 'DEFAULT'} [${this.p.database ?? 'default'}]` }\n\tuser(): string {\n\t\tif (this.p.auth.kind === 'ntlm') return `${this.p.auth.domain}\\\\${this.p.auth.user}`\n\t\treturn this.p.auth.user\n\t}\n\tversionQuery(): string { return 'SELECT @@VERSION AS ver' }\n\tpingQuery(): string { return 'SELECT 1 AS p' }\n\n\tasync execStream(query: string, params: TDbParam[], callback: (event: TDbStreamEvent) => void, signal?: AbortSignal): Promise<void> {\n\t\tconst pool = await new sql.ConnectionPool(buildPoolConfig(this.p)).connect()\n\t\tif (signal?.aborted) {\n\t\t\ttry { await pool.close() } catch {}\n\t\t\treturn\n\t\t}\n\n\t\treturn new Promise<void>((resolve) => {\n\t\t\tconst request = pool.request()\n\t\t\trequest.stream = true\n\t\t\tfor (const p of params) {\n\t\t\t\trequest.input(p.name, p.value)\n\t\t\t}\n\n\t\t\tlet error: string | undefined\n\t\t\tlet done = false\n\n\t\t\tconst onAbort = (): void => {\n\t\t\t\tif (done) return\n\t\t\t\tdone = true\n\t\t\t\ttry { request.cancel() } catch {}\n\t\t\t\tvoid pool.close().catch(() => {})\n\t\t\t\tresolve()\n\t\t\t}\n\t\t\tsignal?.addEventListener('abort', onAbort)\n\n\t\t\trequest.on('recordset', (columns: Record<string, unknown>) => {\n\t\t\t\tif (!done) callback({ kind: 'schema', schema: toSchema(columns) })\n\t\t\t})\n\t\t\trequest.on('row', (row: object) => {\n\t\t\t\tif (!done) callback({ kind: 'row', row })\n\t\t\t})\n\t\t\trequest.on('error', (err: Error) => {\n\t\t\t\terror = String(err)\n\t\t\t})\n\t\t\trequest.on('done', async () => {\n\t\t\t\tif (done) return\n\t\t\t\tdone = true\n\t\t\t\tsignal?.removeEventListener('abort', onAbort)\n\t\t\t\ttry { await pool.close() } catch {}\n\t\t\t\tcallback({ kind: 'finish', error })\n\t\t\t\tresolve()\n\t\t\t})\n\n\t\t\trequest.query(buildMssqlQuery(query))\n\t\t})\n\t}\n\n\tasync execTxDynamic(cb: TDbTxCb, timeoutMs?: number): Promise<TResult<void>> {\n\t\tconst pool = await new sql.ConnectionPool(buildPoolConfig(this.p)).connect()\n\t\tconst txWork = async (): Promise<TResult<void>> => {\n\t\t\tconst tx = new sql.Transaction(pool)\n\t\t\ttry {\n\t\t\t\tawait tx.begin()\n\t\t\t\tconst exec = (query: string, params: TDbParam[]): Promise<TDbTxResult> => {\n\t\t\t\t\tconst req = new sql.Request(tx)\n\t\t\t\t\tconst q = buildMssqlQuery(query)\n\t\t\t\t\tfor (const p of params) req.input(p.name, p.value)\n\t\t\t\t\treturn new Promise<TDbTxResult>((resolve, reject) => {\n\t\t\t\t\t\tconst onUncaught = (err: Error) => reject(err)\n\t\t\t\t\t\tprocess.once('uncaughtException', onUncaught)\n\t\t\t\t\t\treq.query(q)\n\t\t\t\t\t\t\t.then((res) => { process.removeListener('uncaughtException', onUncaught); resolve({ rows: (res.recordset ?? []) as Record<string, unknown>[] }) })\n\t\t\t\t\t\t\t.catch((err) => { process.removeListener('uncaughtException', onUncaught); reject(err) })\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t\tawait cb(exec)\n\t\t\t\tawait tx.commit()\n\t\t\t\treturn { ok: true, result: undefined }\n\t\t\t} catch (err) {\n\t\t\t\ttry { await tx.rollback() } catch {}\n\t\t\t\treturn { ok: false, error: String(err) }\n\t\t\t} finally {\n\t\t\t\ttry { await pool.close() } catch {}\n\t\t\t}\n\t\t}\n\t\tif (!timeoutMs) return txWork()\n\t\tlet timer: ReturnType<typeof setTimeout> | undefined\n\t\tconst timeoutPromise = new Promise<TResult<void>>((resolve) => {\n\t\t\ttimer = setTimeout(() => {\n\t\t\t\tvoid pool.close().catch(() => {})\n\t\t\t\tresolve({ ok: false, error: 'TIMEOUT' })\n\t\t\t}, timeoutMs)\n\t\t})\n\t\ttry {\n\t\t\treturn await Promise.race([txWork(), timeoutPromise])\n\t\t} finally {\n\t\t\tclearTimeout(timer)\n\t\t}\n\t}\n}\n"]}
@@ -0,0 +1,15 @@
1
+ import type { IDbAdapter, TDbParam, TDbStreamEvent, TDbTxCb } from '../types.js';
2
+ import type { TConnOra } from '../connection.js';
3
+ import type { TResult } from '../../tresult.js';
4
+ export declare class OraAdapter implements IDbAdapter {
5
+ private readonly p;
6
+ constructor(p: TConnOra);
7
+ kind(): 'ora';
8
+ title(): string;
9
+ user(): string;
10
+ versionQuery(): string;
11
+ pingQuery(): string;
12
+ private _connParams;
13
+ execStream(query: string, params: TDbParam[], callback: (event: TDbStreamEvent) => void, signal?: AbortSignal): Promise<void>;
14
+ execTxDynamic(cb: TDbTxCb, timeoutMs?: number): Promise<TResult<void>>;
15
+ }
@@ -0,0 +1,137 @@
1
+ import oracledb from 'oracledb';
2
+ function toSchema(meta) {
3
+ return meta.map((m) => ({
4
+ name: m.name,
5
+ kind: 'unknown',
6
+ len: m.byteSize,
7
+ scale: m.scale,
8
+ precision: m.precision,
9
+ }));
10
+ }
11
+ export class OraAdapter {
12
+ p;
13
+ constructor(p) {
14
+ this.p = p;
15
+ }
16
+ kind() { return 'ora'; }
17
+ title() { return `${this.p.host}:${this.p.port || 'DEFAULT'} [${this.p.service}]`; }
18
+ user() { return this.p.user; }
19
+ versionQuery() { return 'SELECT banner AS ver FROM v$version WHERE rownum = 1'; }
20
+ pingQuery() { return 'SELECT 1 AS p FROM dual'; }
21
+ _connParams() {
22
+ const privilegeMap = {
23
+ SYSDBA: oracledb.SYSDBA,
24
+ SYSOPER: oracledb.SYSOPER,
25
+ SYSASM: oracledb.SYSASM,
26
+ SYSBACKUP: oracledb.SYSBACKUP,
27
+ SYSDG: oracledb.SYSDG,
28
+ SYSKM: oracledb.SYSKM,
29
+ SYSRAC: oracledb.SYSRAC,
30
+ };
31
+ return {
32
+ connectString: `${this.p.host}:${this.p.port ?? 1521}/${this.p.service}`,
33
+ user: this.p.user,
34
+ password: this.p.password,
35
+ ...(this.p.privilege && { privilege: privilegeMap[this.p.privilege] }),
36
+ };
37
+ }
38
+ async execStream(query, params, callback, signal) {
39
+ const conn = await oracledb.getConnection(this._connParams());
40
+ let onAbort;
41
+ try {
42
+ const binds = {};
43
+ for (const p of params) {
44
+ binds[p.name] = p.value;
45
+ }
46
+ const stream = conn.queryStream(query, binds, { outFormat: oracledb.OUT_FORMAT_OBJECT });
47
+ await new Promise((resolve) => {
48
+ let done = false;
49
+ const finish = (error) => {
50
+ if (done)
51
+ return;
52
+ done = true;
53
+ callback({ kind: 'finish', error });
54
+ resolve();
55
+ };
56
+ stream.on('metadata', (meta) => {
57
+ callback({ kind: 'schema', schema: toSchema(meta) });
58
+ });
59
+ stream.on('data', (row) => {
60
+ callback({ kind: 'row', row });
61
+ });
62
+ stream.on('error', (err) => finish(String(err)));
63
+ stream.on('end', () => finish());
64
+ // break() interrupts the call running on the server; the stream is destroyed
65
+ // in case the server does not answer even that.
66
+ onAbort = () => {
67
+ void conn.break().catch(() => { });
68
+ stream.destroy();
69
+ finish(String(signal?.reason ?? 'aborted'));
70
+ };
71
+ if (signal?.aborted)
72
+ onAbort();
73
+ else
74
+ signal?.addEventListener('abort', onAbort);
75
+ });
76
+ }
77
+ finally {
78
+ if (onAbort)
79
+ signal?.removeEventListener('abort', onAbort);
80
+ try {
81
+ await conn.close();
82
+ }
83
+ catch { }
84
+ }
85
+ }
86
+ async execTxDynamic(cb, timeoutMs) {
87
+ const conn = await oracledb.getConnection(this._connParams());
88
+ const txWork = async () => {
89
+ try {
90
+ const exec = (query, params) => {
91
+ const binds = {};
92
+ for (const p of params)
93
+ binds[p.name] = p.value;
94
+ return new Promise((resolve, reject) => {
95
+ const onUncaught = (err) => reject(err);
96
+ process.once('uncaughtException', onUncaught);
97
+ conn.execute(query, binds, { autoCommit: false, outFormat: oracledb.OUT_FORMAT_OBJECT })
98
+ .then((res) => { process.removeListener('uncaughtException', onUncaught); resolve({ rows: (res.rows ?? []) }); })
99
+ .catch((err) => { process.removeListener('uncaughtException', onUncaught); reject(err); });
100
+ });
101
+ };
102
+ await cb(exec);
103
+ await conn.commit();
104
+ return { ok: true, result: undefined };
105
+ }
106
+ catch (err) {
107
+ try {
108
+ await conn.rollback();
109
+ }
110
+ catch { }
111
+ return { ok: false, error: String(err) };
112
+ }
113
+ finally {
114
+ try {
115
+ await conn.close();
116
+ }
117
+ catch { }
118
+ }
119
+ };
120
+ if (!timeoutMs)
121
+ return txWork();
122
+ let timer;
123
+ const timeoutPromise = new Promise((resolve) => {
124
+ timer = setTimeout(() => {
125
+ void conn.close().catch(() => { });
126
+ resolve({ ok: false, error: 'TIMEOUT' });
127
+ }, timeoutMs);
128
+ });
129
+ try {
130
+ return await Promise.race([txWork(), timeoutPromise]);
131
+ }
132
+ finally {
133
+ clearTimeout(timer);
134
+ }
135
+ }
136
+ }
137
+ //# sourceMappingURL=ora.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ora.js","sourceRoot":"","sources":["../../../../src/db/adapters/ora.ts"],"names":[],"mappings":"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","sourcesContent":["import oracledb from 'oracledb'\nimport type { IDbAdapter, TDbCol, TDbParam, TDbStreamEvent, TDbTxCb, TDbTxResult } from '../types'\nimport type { TConnOra } from '../connection'\nimport type { TResult } from '../../tresult'\n\ntype OraMetadata = { name: string; dbTypeName: string; byteSize?: number; scale?: number; precision?: number }\n\nfunction toSchema(meta: OraMetadata[]): TDbCol[] {\n\treturn meta.map((m) => ({\n\t\tname: m.name,\n\t\tkind: 'unknown',\n\t\tlen: m.byteSize,\n\t\tscale: m.scale,\n\t\tprecision: m.precision,\n\t}))\n}\n\nexport class OraAdapter implements IDbAdapter {\n\tconstructor(private readonly p: TConnOra) {}\n\n\tkind(): 'ora' { return 'ora' }\n\ttitle(): string { return `${this.p.host}:${this.p.port || 'DEFAULT'} [${this.p.service}]` }\n\tuser(): string { return this.p.user }\n\tversionQuery(): string { return 'SELECT banner AS ver FROM v$version WHERE rownum = 1' }\n\tpingQuery(): string { return 'SELECT 1 AS p FROM dual' }\n\n\tprivate _connParams() {\n\t\tconst privilegeMap: Record<string, number> = {\n\t\t\tSYSDBA: oracledb.SYSDBA,\n\t\t\tSYSOPER: oracledb.SYSOPER,\n\t\t\tSYSASM: oracledb.SYSASM,\n\t\t\tSYSBACKUP: oracledb.SYSBACKUP,\n\t\t\tSYSDG: oracledb.SYSDG,\n\t\t\tSYSKM: oracledb.SYSKM,\n\t\t\tSYSRAC: oracledb.SYSRAC,\n\t\t}\n\t\treturn {\n\t\t\tconnectString: `${this.p.host}:${this.p.port ?? 1521}/${this.p.service}`,\n\t\t\tuser: this.p.user,\n\t\t\tpassword: this.p.password,\n\t\t\t...(this.p.privilege && { privilege: privilegeMap[this.p.privilege] }),\n\t\t}\n\t}\n\n\tasync execStream(query: string, params: TDbParam[], callback: (event: TDbStreamEvent) => void, signal?: AbortSignal): Promise<void> {\n\t\tconst conn = await oracledb.getConnection(this._connParams())\n\t\tlet onAbort: (() => void) | undefined\n\t\ttry {\n\t\t\ttype OraBindValue = oracledb.BindParameter | string | number | bigint | Date | Buffer | null | undefined\n\t\t\tconst binds: Record<string, OraBindValue> = {}\n\t\t\tfor (const p of params) {\n\t\t\t\tbinds[p.name] = p.value as OraBindValue\n\t\t\t}\n\t\t\tconst stream = conn.queryStream(query, binds, { outFormat: oracledb.OUT_FORMAT_OBJECT })\n\t\t\tawait new Promise<void>((resolve) => {\n\t\t\t\tlet done = false\n\t\t\t\tconst finish = (error?: string) => {\n\t\t\t\t\tif (done) return\n\t\t\t\t\tdone = true\n\t\t\t\t\tcallback({ kind: 'finish', error })\n\t\t\t\t\tresolve()\n\t\t\t\t}\n\t\t\t\tstream.on('metadata', (meta: OraMetadata[]) => {\n\t\t\t\t\tcallback({ kind: 'schema', schema: toSchema(meta) })\n\t\t\t\t})\n\t\t\t\tstream.on('data', (row: object) => {\n\t\t\t\t\tcallback({ kind: 'row', row })\n\t\t\t\t})\n\t\t\t\tstream.on('error', (err: Error) => finish(String(err)))\n\t\t\t\tstream.on('end', () => finish())\n\t\t\t\t// break() interrupts the call running on the server; the stream is destroyed\n\t\t\t\t// in case the server does not answer even that.\n\t\t\t\tonAbort = () => {\n\t\t\t\t\tvoid conn.break().catch(() => {})\n\t\t\t\t\tstream.destroy()\n\t\t\t\t\tfinish(String(signal?.reason ?? 'aborted'))\n\t\t\t\t}\n\t\t\t\tif (signal?.aborted) onAbort()\n\t\t\t\telse signal?.addEventListener('abort', onAbort)\n\t\t\t})\n\t\t} finally {\n\t\t\tif (onAbort) signal?.removeEventListener('abort', onAbort)\n\t\t\ttry { await conn.close() } catch {}\n\t\t}\n\t}\n\n\tasync execTxDynamic(cb: TDbTxCb, timeoutMs?: number): Promise<TResult<void>> {\n\t\tconst conn = await oracledb.getConnection(this._connParams())\n\t\tconst txWork = async (): Promise<TResult<void>> => {\n\t\t\ttry {\n\t\t\t\tconst exec = (query: string, params: TDbParam[]): Promise<TDbTxResult> => {\n\t\t\t\t\ttype OraBindValue = oracledb.BindParameter | string | number | bigint | Date | Buffer | null | undefined\n\t\t\t\t\tconst binds: Record<string, OraBindValue> = {}\n\t\t\t\t\tfor (const p of params) binds[p.name] = p.value as OraBindValue\n\t\t\t\t\treturn new Promise<TDbTxResult>((resolve, reject) => {\n\t\t\t\t\t\tconst onUncaught = (err: Error) => reject(err)\n\t\t\t\t\t\tprocess.once('uncaughtException', onUncaught)\n\t\t\t\t\t\tconn.execute<Record<string, unknown>>(query, binds, { autoCommit: false, outFormat: oracledb.OUT_FORMAT_OBJECT })\n\t\t\t\t\t\t\t.then((res) => { process.removeListener('uncaughtException', onUncaught); resolve({ rows: (res.rows ?? []) as Record<string, unknown>[] }) })\n\t\t\t\t\t\t\t.catch((err) => { process.removeListener('uncaughtException', onUncaught); reject(err) })\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t\tawait cb(exec)\n\t\t\t\tawait conn.commit()\n\t\t\t\treturn { ok: true, result: undefined }\n\t\t\t} catch (err) {\n\t\t\t\ttry { await conn.rollback() } catch {}\n\t\t\t\treturn { ok: false, error: String(err) }\n\t\t\t} finally {\n\t\t\t\ttry { await conn.close() } catch {}\n\t\t\t}\n\t\t}\n\t\tif (!timeoutMs) return txWork()\n\t\tlet timer: ReturnType<typeof setTimeout> | undefined\n\t\tconst timeoutPromise = new Promise<TResult<void>>((resolve) => {\n\t\t\ttimer = setTimeout(() => {\n\t\t\t\tvoid conn.close().catch(() => {})\n\t\t\t\tresolve({ ok: false, error: 'TIMEOUT' })\n\t\t\t}, timeoutMs)\n\t\t})\n\t\ttry {\n\t\t\treturn await Promise.race([txWork(), timeoutPromise])\n\t\t} finally {\n\t\t\tclearTimeout(timer)\n\t\t}\n\t}\n}\n"]}
@@ -0,0 +1,16 @@
1
+ import type { IDbAdapter, TDbParam, TDbStreamEvent, TDbTxCb } from '../types.js';
2
+ import type { TConnPg } from '../connection.js';
3
+ import type { TResult } from '../../tresult.js';
4
+ export declare class PgAdapter implements IDbAdapter {
5
+ private readonly p;
6
+ constructor(p: TConnPg);
7
+ kind(): 'pg';
8
+ title(): string;
9
+ user(): string;
10
+ versionQuery(): string;
11
+ pingQuery(): string;
12
+ private _clientConfig;
13
+ private _cancelBackend;
14
+ execStream(query: string, params: TDbParam[], callback: (event: TDbStreamEvent) => void, signal?: AbortSignal): Promise<void>;
15
+ execTxDynamic(cb: TDbTxCb, timeoutMs?: number): Promise<TResult<void>>;
16
+ }
@@ -0,0 +1,153 @@
1
+ import { Client } from 'pg';
2
+ import { mapPlaceholders } from '../placeholders.js';
3
+ function toSchema(fields) {
4
+ return fields.map((f) => ({ name: f.name, kind: 'unknown' }));
5
+ }
6
+ // Converts :name placeholders to $1, $2, ... and collects values.
7
+ // Same :name used multiple times maps to the same $N. A colon inside a string literal, a
8
+ // quoted identifier or a comment is left as it is.
9
+ function buildQuery(query, params) {
10
+ const nameToIndex = new Map();
11
+ const values = [];
12
+ const text = mapPlaceholders(query, (name) => {
13
+ if (!nameToIndex.has(name)) {
14
+ const found = params.find((p) => p.name === name);
15
+ if (!found)
16
+ throw new Error(`pg: param not found: ${name}`);
17
+ values.push(found.value);
18
+ nameToIndex.set(name, values.length);
19
+ }
20
+ return `$${nameToIndex.get(name)}`;
21
+ });
22
+ return { text, values };
23
+ }
24
+ export class PgAdapter {
25
+ p;
26
+ constructor(p) {
27
+ this.p = p;
28
+ }
29
+ kind() { return 'pg'; }
30
+ title() { return `${this.p.host}:${this.p.port || 'DEFAULT'} [${this.p.database}]`; }
31
+ user() { return this.p.user; }
32
+ versionQuery() { return 'SELECT version() AS ver'; }
33
+ pingQuery() { return 'SELECT 1 AS p'; }
34
+ _clientConfig() {
35
+ return {
36
+ host: this.p.host,
37
+ port: this.p.port ?? 5432,
38
+ database: this.p.database,
39
+ user: this.p.user,
40
+ password: this.p.password,
41
+ ssl: this.p.ssl ? { rejectUnauthorized: false } : undefined,
42
+ options: '-c client_encoding=UTF8',
43
+ };
44
+ }
45
+ // A backend does not notice its client is gone until it has something to send, so a
46
+ // query cut off on our side would go on to its very end on the server. It is cancelled
47
+ // from a connection of its own; a server that does not answer that either is given up
48
+ // on after a while - this runs in the background and nobody waits for it.
49
+ _cancelBackend(pid) {
50
+ const c = new Client({ ...this._clientConfig(), connectionTimeoutMillis: 30_000, query_timeout: 30_000 });
51
+ c.on('error', () => { });
52
+ void c.connect()
53
+ .then(() => c.query('SELECT pg_cancel_backend($1)', [pid]))
54
+ .catch(() => { })
55
+ .finally(() => { void c.end().catch(() => { }); });
56
+ }
57
+ async execStream(query, params, callback, signal) {
58
+ const client = new Client(this._clientConfig());
59
+ // The socket is destroyed rather than ended: a graceful end waits for a server
60
+ // that may be the very thing that stopped answering. The query then fails, and the
61
+ // client also emits "error" - with nobody listening that would be thrown.
62
+ client.on('error', () => { });
63
+ const onAbort = () => {
64
+ const internals = client;
65
+ if (internals.processID)
66
+ this._cancelBackend(internals.processID);
67
+ try {
68
+ internals.connection?.stream?.destroy();
69
+ }
70
+ catch { }
71
+ };
72
+ signal?.addEventListener('abort', onAbort);
73
+ try {
74
+ await client.connect();
75
+ const { text, values } = buildQuery(query, params);
76
+ const result = await client.query({ text, values });
77
+ callback({ kind: 'schema', schema: toSchema(result.fields) });
78
+ for (const row of result.rows) {
79
+ callback({ kind: 'row', row });
80
+ }
81
+ callback({ kind: 'finish' });
82
+ }
83
+ catch (err) {
84
+ callback({ kind: 'finish', error: String(err) });
85
+ }
86
+ finally {
87
+ signal?.removeEventListener('abort', onAbort);
88
+ try {
89
+ await client.end();
90
+ }
91
+ catch { }
92
+ }
93
+ }
94
+ async execTxDynamic(cb, timeoutMs) {
95
+ const client = new Client({
96
+ host: this.p.host,
97
+ port: this.p.port ?? 5432,
98
+ database: this.p.database,
99
+ user: this.p.user,
100
+ password: this.p.password,
101
+ ssl: this.p.ssl ? { rejectUnauthorized: false } : undefined,
102
+ options: '-c client_encoding=UTF8',
103
+ });
104
+ const txWork = async () => {
105
+ try {
106
+ await client.connect();
107
+ await client.query('BEGIN');
108
+ const exec = (query, params) => {
109
+ const { text, values } = buildQuery(query, params);
110
+ return new Promise((resolve, reject) => {
111
+ const onUncaught = (err) => reject(err);
112
+ process.once('uncaughtException', onUncaught);
113
+ client.query({ text, values })
114
+ .then((res) => { process.removeListener('uncaughtException', onUncaught); resolve({ rows: res.rows }); })
115
+ .catch((err) => { process.removeListener('uncaughtException', onUncaught); reject(err); });
116
+ });
117
+ };
118
+ await cb(exec);
119
+ await client.query('COMMIT');
120
+ return { ok: true, result: undefined };
121
+ }
122
+ catch (err) {
123
+ try {
124
+ await client.query('ROLLBACK');
125
+ }
126
+ catch { }
127
+ return { ok: false, error: String(err) };
128
+ }
129
+ finally {
130
+ try {
131
+ await client.end();
132
+ }
133
+ catch { }
134
+ }
135
+ };
136
+ if (!timeoutMs)
137
+ return txWork();
138
+ let timer;
139
+ const timeoutPromise = new Promise((resolve) => {
140
+ timer = setTimeout(() => {
141
+ void client.end().catch(() => { });
142
+ resolve({ ok: false, error: 'TIMEOUT' });
143
+ }, timeoutMs);
144
+ });
145
+ try {
146
+ return await Promise.race([txWork(), timeoutPromise]);
147
+ }
148
+ finally {
149
+ clearTimeout(timer);
150
+ }
151
+ }
152
+ }
153
+ //# sourceMappingURL=pg.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pg.js","sourceRoot":"","sources":["../../../../src/db/adapters/pg.ts"],"names":[],"mappings":"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","sourcesContent":["import { Client, type FieldDef } from 'pg'\nimport type { IDbAdapter, TDbCol, TDbParam, TDbStreamEvent, TDbTxCb, TDbTxResult } from '../types'\nimport type { TConnPg } from '../connection'\nimport type { TResult } from '../../tresult'\nimport { mapPlaceholders } from '../placeholders'\n\nfunction toSchema(fields: FieldDef[]): TDbCol[] {\n\treturn fields.map((f) => ({ name: f.name, kind: 'unknown' }))\n}\n\n// Converts :name placeholders to $1, $2, ... and collects values.\n// Same :name used multiple times maps to the same $N. A colon inside a string literal, a\n// quoted identifier or a comment is left as it is.\nfunction buildQuery(query: string, params: TDbParam[]): { text: string; values: unknown[] } {\n\tconst nameToIndex = new Map<string, number>()\n\tconst values: unknown[] = []\n\tconst text = mapPlaceholders(query, (name) => {\n\t\tif (!nameToIndex.has(name)) {\n\t\t\tconst found = params.find((p) => p.name === name)\n\t\t\tif (!found) throw new Error(`pg: param not found: ${name}`)\n\t\t\tvalues.push(found.value)\n\t\t\tnameToIndex.set(name, values.length)\n\t\t}\n\t\treturn `$${nameToIndex.get(name)}`\n\t})\n\treturn { text, values }\n}\n\nexport class PgAdapter implements IDbAdapter {\n\tconstructor(private readonly p: TConnPg) {}\n\n\tkind(): 'pg' { return 'pg' }\n\ttitle(): string { return `${this.p.host}:${this.p.port || 'DEFAULT'} [${this.p.database}]` }\n\tuser(): string { return this.p.user }\n\tversionQuery(): string { return 'SELECT version() AS ver' }\n\tpingQuery(): string { return 'SELECT 1 AS p' }\n\n\tprivate _clientConfig() {\n\t\treturn {\n\t\t\thost: this.p.host,\n\t\t\tport: this.p.port ?? 5432,\n\t\t\tdatabase: this.p.database,\n\t\t\tuser: this.p.user,\n\t\t\tpassword: this.p.password,\n\t\t\tssl: this.p.ssl ? { rejectUnauthorized: false } : undefined,\n\t\t\toptions: '-c client_encoding=UTF8',\n\t\t}\n\t}\n\n\t// A backend does not notice its client is gone until it has something to send, so a\n\t// query cut off on our side would go on to its very end on the server. It is cancelled\n\t// from a connection of its own; a server that does not answer that either is given up\n\t// on after a while - this runs in the background and nobody waits for it.\n\tprivate _cancelBackend(pid: number): void {\n\t\tconst c = new Client({ ...this._clientConfig(), connectionTimeoutMillis: 30_000, query_timeout: 30_000 })\n\t\tc.on('error', () => {})\n\t\tvoid c.connect()\n\t\t\t.then(() => c.query('SELECT pg_cancel_backend($1)', [pid]))\n\t\t\t.catch(() => {})\n\t\t\t.finally(() => { void c.end().catch(() => {}) })\n\t}\n\n\tasync execStream(query: string, params: TDbParam[], callback: (event: TDbStreamEvent) => void, signal?: AbortSignal): Promise<void> {\n\t\tconst client = new Client(this._clientConfig())\n\t\t// The socket is destroyed rather than ended: a graceful end waits for a server\n\t\t// that may be the very thing that stopped answering. The query then fails, and the\n\t\t// client also emits \"error\" - with nobody listening that would be thrown.\n\t\tclient.on('error', () => {})\n\t\tconst onAbort = (): void => {\n\t\t\tconst internals = client as unknown as { processID?: number; connection?: { stream?: { destroy(): void } } }\n\t\t\tif (internals.processID) this._cancelBackend(internals.processID)\n\t\t\ttry { internals.connection?.stream?.destroy() } catch {}\n\t\t}\n\t\tsignal?.addEventListener('abort', onAbort)\n\t\ttry {\n\t\t\tawait client.connect()\n\t\t\tconst { text, values } = buildQuery(query, params)\n\t\t\tconst result = await client.query({ text, values })\n\t\t\tcallback({ kind: 'schema', schema: toSchema(result.fields) })\n\t\t\tfor (const row of result.rows) {\n\t\t\t\tcallback({ kind: 'row', row })\n\t\t\t}\n\t\t\tcallback({ kind: 'finish' })\n\t\t} catch (err) {\n\t\t\tcallback({ kind: 'finish', error: String(err) })\n\t\t} finally {\n\t\t\tsignal?.removeEventListener('abort', onAbort)\n\t\t\ttry { await client.end() } catch {}\n\t\t}\n\t}\n\n\tasync execTxDynamic(cb: TDbTxCb, timeoutMs?: number): Promise<TResult<void>> {\n\t\tconst client = new Client({\n\t\t\thost: this.p.host,\n\t\t\tport: this.p.port ?? 5432,\n\t\t\tdatabase: this.p.database,\n\t\t\tuser: this.p.user,\n\t\t\tpassword: this.p.password,\n\t\t\tssl: this.p.ssl ? { rejectUnauthorized: false } : undefined,\n\t\t\toptions: '-c client_encoding=UTF8',\n\t\t})\n\t\tconst txWork = async (): Promise<TResult<void>> => {\n\t\t\ttry {\n\t\t\t\tawait client.connect()\n\t\t\t\tawait client.query('BEGIN')\n\t\t\t\tconst exec = (query: string, params: TDbParam[]): Promise<TDbTxResult> => {\n\t\t\t\t\tconst { text, values } = buildQuery(query, params)\n\t\t\t\t\treturn new Promise<TDbTxResult>((resolve, reject) => {\n\t\t\t\t\t\tconst onUncaught = (err: Error) => reject(err)\n\t\t\t\t\t\tprocess.once('uncaughtException', onUncaught)\n\t\t\t\t\t\tclient.query({ text, values })\n\t\t\t\t\t\t\t.then((res) => { process.removeListener('uncaughtException', onUncaught); resolve({ rows: res.rows as Record<string, unknown>[] }) })\n\t\t\t\t\t\t\t.catch((err) => { process.removeListener('uncaughtException', onUncaught); reject(err) })\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t\tawait cb(exec)\n\t\t\t\tawait client.query('COMMIT')\n\t\t\t\treturn { ok: true, result: undefined }\n\t\t\t} catch (err) {\n\t\t\t\ttry { await client.query('ROLLBACK') } catch {}\n\t\t\t\treturn { ok: false, error: String(err) }\n\t\t\t} finally {\n\t\t\t\ttry { await client.end() } catch {}\n\t\t\t}\n\t\t}\n\t\tif (!timeoutMs) return txWork()\n\t\tlet timer: ReturnType<typeof setTimeout> | undefined\n\t\tconst timeoutPromise = new Promise<TResult<void>>((resolve) => {\n\t\t\ttimer = setTimeout(() => {\n\t\t\t\tvoid client.end().catch(() => {})\n\t\t\t\tresolve({ ok: false, error: 'TIMEOUT' })\n\t\t\t}, timeoutMs)\n\t\t})\n\t\ttry {\n\t\t\treturn await Promise.race([txWork(), timeoutPromise])\n\t\t} finally {\n\t\t\tclearTimeout(timer)\n\t\t}\n\t}\n}\n"]}
@@ -0,0 +1,49 @@
1
+ export type TMssqlAuth = {
2
+ kind: 'mssql';
3
+ user: string;
4
+ password: string;
5
+ } | {
6
+ kind: 'ntlm';
7
+ domain: string;
8
+ user: string;
9
+ password: string;
10
+ };
11
+ export type TConnMssql = {
12
+ kind: 'mssql';
13
+ server: string;
14
+ port?: number;
15
+ database?: string;
16
+ auth: TMssqlAuth;
17
+ trustServerCertificate?: boolean;
18
+ encrypt?: boolean;
19
+ };
20
+ export type TConnPg = {
21
+ kind: 'pg';
22
+ host: string;
23
+ port?: number;
24
+ database: string;
25
+ user: string;
26
+ password: string;
27
+ ssl?: boolean;
28
+ };
29
+ export type TOraPrivilege = 'SYSDBA' | 'SYSOPER' | 'SYSASM' | 'SYSBACKUP' | 'SYSDG' | 'SYSKM' | 'SYSRAC';
30
+ export type TConnOra = {
31
+ kind: 'ora';
32
+ host: string;
33
+ port?: number;
34
+ service: string;
35
+ user: string;
36
+ password: string;
37
+ privilege?: TOraPrivilege;
38
+ };
39
+ export type TConnFb = {
40
+ kind: 'fb';
41
+ host: string;
42
+ port?: number;
43
+ database: string;
44
+ user: string;
45
+ password: string;
46
+ charset?: 'UTF8' | 'WIN1251';
47
+ };
48
+ export type TConnectionParam = TConnMssql | TConnOra | TConnPg | TConnFb;
49
+ export type TDbKind = TConnectionParam['kind'];
@@ -0,0 +1,5 @@
1
+ // Connection settings for the supported databases. The package owns these types —
2
+ // it does not depend on vv-config-conn, so a consumer passes the connection as a
3
+ // plain object and its schema lives here.
4
+ export {};
5
+ //# sourceMappingURL=connection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connection.js","sourceRoot":"","sources":["../../../src/db/connection.ts"],"names":[],"mappings":"AAAA,kFAAkF;AAClF,iFAAiF;AACjF,0CAA0C","sourcesContent":["// Connection settings for the supported databases. The package owns these types —\n// it does not depend on vv-config-conn, so a consumer passes the connection as a\n// plain object and its schema lives here.\n\nexport type TMssqlAuth =\n\t| { kind: 'mssql'; user: string; password: string }\n\t| { kind: 'ntlm'; domain: string; user: string; password: string }\n\nexport type TConnMssql = {\n\tkind: 'mssql'\n\tserver: string\n\tport?: number\n\tdatabase?: string\n\tauth: TMssqlAuth\n\ttrustServerCertificate?: boolean\n\tencrypt?: boolean\n}\n\nexport type TConnPg = {\n\tkind: 'pg'\n\thost: string\n\tport?: number\n\tdatabase: string\n\tuser: string\n\tpassword: string\n\tssl?: boolean\n}\n\nexport type TOraPrivilege = 'SYSDBA' | 'SYSOPER' | 'SYSASM' | 'SYSBACKUP' | 'SYSDG' | 'SYSKM' | 'SYSRAC'\n\nexport type TConnOra = {\n\tkind: 'ora'\n\thost: string\n\tport?: number\n\tservice: string\n\tuser: string\n\tpassword: string\n\tprivilege?: TOraPrivilege\n}\n\nexport type TConnFb = {\n\tkind: 'fb'\n\thost: string\n\tport?: number\n\tdatabase: string\n\tuser: string\n\tpassword: string\n\tcharset?: 'UTF8' | 'WIN1251'\n}\n\n// The discriminated union over all supported connections; its \"kind\" is the single\n// source for the list of databases this package talks to.\nexport type TConnectionParam = TConnMssql | TConnOra | TConnPg | TConnFb\nexport type TDbKind = TConnectionParam['kind']\n"]}
@@ -0,0 +1,16 @@
1
+ import type { TResult } from '../tresult.js';
2
+ import type { TConnectionParam } from './connection.js';
3
+ import type { TDbParam, TDbRes, TDbStreamEvent, TDbTxCb } from './types.js';
4
+ export declare class DbConnection {
5
+ private _adapter;
6
+ constructor(connection: TConnectionParam);
7
+ exec(query: string, params: TDbParam[]): Promise<TResult<TDbRes[]>>;
8
+ execStream(query: string, params: TDbParam[], callback: (event: TDbStreamEvent) => void): Promise<void>;
9
+ version(): Promise<TResult<string>>;
10
+ kind(): 'pg' | 'mssql' | 'ora' | 'fb';
11
+ title(): string;
12
+ user(): string;
13
+ execTxDynamic(cb: TDbTxCb, timeoutMs?: number): Promise<TResult<void>>;
14
+ ping(): Promise<TResult<null>>;
15
+ }
16
+ export declare function createDbConnection(connection: TConnectionParam): DbConnection;