@powersync/service-module-mysql 0.0.0-dev-20260223080959 → 0.0.0-dev-20260224151854
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/CHANGELOG.md +12 -5
- package/dist/types/types.d.ts +25 -0
- package/dist/types/types.js +45 -1
- package/dist/types/types.js.map +1 -1
- package/dist/utils/mysql-utils.js +6 -0
- package/dist/utils/mysql-utils.js.map +1 -1
- package/package.json +8 -8
- package/src/types/types.ts +68 -1
- package/src/utils/mysql-utils.ts +8 -0
- package/test/src/config.test.ts +165 -0
- package/tsconfig.tsbuildinfo +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @powersync/service-module-mysql
|
|
2
2
|
|
|
3
|
-
## 0.0.0-dev-
|
|
3
|
+
## 0.0.0-dev-20260224151854
|
|
4
4
|
|
|
5
5
|
### Minor Changes
|
|
6
6
|
|
|
@@ -8,14 +8,21 @@
|
|
|
8
8
|
|
|
9
9
|
### Patch Changes
|
|
10
10
|
|
|
11
|
+
- 15aea77: Support connection parameters via database URL query string. PostgreSQL supports `connect_timeout`. MongoDB supports `connectTimeoutMS`, `socketTimeoutMS`, `serverSelectionTimeoutMS`, `maxPoolSize`, `maxIdleTimeMS`. MySQL supports `connectTimeout`, `connectionLimit`, `queueLimit`.
|
|
11
12
|
- Updated dependencies [0998251]
|
|
13
|
+
- Updated dependencies [65f3c89]
|
|
12
14
|
- Updated dependencies [1c45667]
|
|
15
|
+
- Updated dependencies [8785a3f]
|
|
13
16
|
- Updated dependencies [8a4c34e]
|
|
17
|
+
- Updated dependencies [b440093]
|
|
18
|
+
- Updated dependencies [d7ff4ad]
|
|
14
19
|
- Updated dependencies [8bd83e8]
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
- @powersync/service-
|
|
18
|
-
- @powersync/
|
|
20
|
+
- Updated dependencies [79a9729]
|
|
21
|
+
- Updated dependencies [5edd95f]
|
|
22
|
+
- @powersync/service-core@0.0.0-dev-20260224151854
|
|
23
|
+
- @powersync/service-types@0.0.0-dev-20260224151854
|
|
24
|
+
- @powersync/service-sync-rules@0.0.0-dev-20260224151854
|
|
25
|
+
- @powersync/lib-services-framework@0.0.0-dev-20260224151854
|
|
19
26
|
|
|
20
27
|
## 0.10.2
|
|
21
28
|
|
package/dist/types/types.d.ts
CHANGED
|
@@ -1,6 +1,17 @@
|
|
|
1
1
|
import { LookupFunction } from 'node:net';
|
|
2
2
|
import * as t from 'ts-codec';
|
|
3
3
|
export declare const MYSQL_CONNECTION_TYPE: "mysql";
|
|
4
|
+
/**
|
|
5
|
+
* Connection parameters that can be parsed from the MySQL URI query string.
|
|
6
|
+
*
|
|
7
|
+
* All values are in milliseconds (for timeouts) or counts (for limits).
|
|
8
|
+
* MySQL uses camelCase naming convention.
|
|
9
|
+
*/
|
|
10
|
+
export interface MySQLConnectionParams {
|
|
11
|
+
connectTimeout?: number;
|
|
12
|
+
connectionLimit?: number;
|
|
13
|
+
queueLimit?: number;
|
|
14
|
+
}
|
|
4
15
|
export interface NormalizedMySQLConnectionConfig {
|
|
5
16
|
id: string;
|
|
6
17
|
tag: string;
|
|
@@ -15,6 +26,7 @@ export interface NormalizedMySQLConnectionConfig {
|
|
|
15
26
|
client_private_key?: string;
|
|
16
27
|
lookup?: LookupFunction;
|
|
17
28
|
binlog_queue_memory_limit: number;
|
|
29
|
+
connectionParams: MySQLConnectionParams;
|
|
18
30
|
}
|
|
19
31
|
export declare const MySQLConnectionConfig: t.Intersection<t.Codec<{
|
|
20
32
|
type: string;
|
|
@@ -55,3 +67,16 @@ export type ResolvedConnectionConfig = MySQLConnectionConfig & NormalizedMySQLCo
|
|
|
55
67
|
* Returns destructured options.
|
|
56
68
|
*/
|
|
57
69
|
export declare function normalizeConnectionConfig(options: MySQLConnectionConfig): NormalizedMySQLConnectionConfig;
|
|
70
|
+
/**
|
|
71
|
+
* Parse a single numeric connection parameter from a URI query string value.
|
|
72
|
+
*
|
|
73
|
+
* Returns undefined if the value is missing, not a valid number, NaN, negative, zero, or Infinity.
|
|
74
|
+
*/
|
|
75
|
+
export declare function parseMySQLConnectionParam(value: string | null | undefined): number | undefined;
|
|
76
|
+
/**
|
|
77
|
+
* Parse connection parameters from a MySQL URI's query string.
|
|
78
|
+
*
|
|
79
|
+
* MySQL uses camelCase naming convention.
|
|
80
|
+
* Invalid values (NaN, negative, non-numeric) are silently ignored.
|
|
81
|
+
*/
|
|
82
|
+
export declare function parseMySQLConnectionParams(searchParams: URLSearchParams | undefined): MySQLConnectionParams;
|
package/dist/types/types.js
CHANGED
|
@@ -54,6 +54,9 @@ export function normalizeConnectionConfig(options) {
|
|
|
54
54
|
throw new ServiceError(ErrorCode.PSYNC_S1105, `MySQL connection: database required`);
|
|
55
55
|
}
|
|
56
56
|
const lookup = makeHostnameLookupFunction(hostname, { reject_ip_ranges: options.reject_ip_ranges ?? [] });
|
|
57
|
+
// Parse connection parameters from URL query string
|
|
58
|
+
const uriQuery = uri.query ? new URLSearchParams(uri.query) : undefined;
|
|
59
|
+
const connectionParams = parseMySQLConnectionParams(uriQuery);
|
|
57
60
|
return {
|
|
58
61
|
id: options.id ?? 'default',
|
|
59
62
|
tag: options.tag ?? 'default',
|
|
@@ -65,7 +68,48 @@ export function normalizeConnectionConfig(options) {
|
|
|
65
68
|
server_id: options.server_id ?? 1,
|
|
66
69
|
// Binlog processing queue memory limit before throttling is applied.
|
|
67
70
|
binlog_queue_memory_limit: options.binlog_queue_memory_limit ?? 50,
|
|
68
|
-
lookup
|
|
71
|
+
lookup,
|
|
72
|
+
connectionParams
|
|
69
73
|
};
|
|
70
74
|
}
|
|
75
|
+
/**
|
|
76
|
+
* Parse a single numeric connection parameter from a URI query string value.
|
|
77
|
+
*
|
|
78
|
+
* Returns undefined if the value is missing, not a valid number, NaN, negative, zero, or Infinity.
|
|
79
|
+
*/
|
|
80
|
+
export function parseMySQLConnectionParam(value) {
|
|
81
|
+
if (value == null) {
|
|
82
|
+
return undefined;
|
|
83
|
+
}
|
|
84
|
+
const parsed = Number(value);
|
|
85
|
+
if (isFinite(parsed) && parsed > 0) {
|
|
86
|
+
return parsed;
|
|
87
|
+
}
|
|
88
|
+
return undefined;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Parse connection parameters from a MySQL URI's query string.
|
|
92
|
+
*
|
|
93
|
+
* MySQL uses camelCase naming convention.
|
|
94
|
+
* Invalid values (NaN, negative, non-numeric) are silently ignored.
|
|
95
|
+
*/
|
|
96
|
+
export function parseMySQLConnectionParams(searchParams) {
|
|
97
|
+
const params = {};
|
|
98
|
+
if (searchParams == null) {
|
|
99
|
+
return params;
|
|
100
|
+
}
|
|
101
|
+
const connectTimeout = parseMySQLConnectionParam(searchParams.get('connectTimeout'));
|
|
102
|
+
if (connectTimeout != null) {
|
|
103
|
+
params.connectTimeout = connectTimeout;
|
|
104
|
+
}
|
|
105
|
+
const connectionLimit = parseMySQLConnectionParam(searchParams.get('connectionLimit'));
|
|
106
|
+
if (connectionLimit != null) {
|
|
107
|
+
params.connectionLimit = connectionLimit;
|
|
108
|
+
}
|
|
109
|
+
const queueLimit = parseMySQLConnectionParam(searchParams.get('queueLimit'));
|
|
110
|
+
if (queueLimit != null) {
|
|
111
|
+
params.queueLimit = queueLimit;
|
|
112
|
+
}
|
|
113
|
+
return params;
|
|
114
|
+
}
|
|
71
115
|
//# sourceMappingURL=types.js.map
|
package/dist/types/types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/types/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,0BAA0B,EAAE,YAAY,EAAE,MAAM,mCAAmC,CAAC;AACxG,OAAO,KAAK,aAAa,MAAM,0BAA0B,CAAC;AAE1D,OAAO,KAAK,CAAC,MAAM,UAAU,CAAC;AAC9B,OAAO,KAAK,KAAK,MAAM,QAAQ,CAAC;AAEhC,MAAM,CAAC,MAAM,qBAAqB,GAAG,OAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/types/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -26,6 +26,8 @@ export function createPool(config, options) {
|
|
|
26
26
|
cert: config.client_certificate
|
|
27
27
|
};
|
|
28
28
|
const hasSSLOptions = Object.values(sslOptions).some((v) => !!v);
|
|
29
|
+
// URL connection parameters provide defaults; explicit options take precedence
|
|
30
|
+
const params = config.connectionParams;
|
|
29
31
|
// TODO: Use config.lookup for DNS resolution
|
|
30
32
|
return mysql.createPool({
|
|
31
33
|
host: config.hostname,
|
|
@@ -39,6 +41,10 @@ export function createPool(config, options) {
|
|
|
39
41
|
timezone: 'Z', // Ensure no auto timezone manipulation of the dates occur
|
|
40
42
|
jsonStrings: true, // Return JSON columns as strings
|
|
41
43
|
dateStrings: true, // We parse and format them ourselves
|
|
44
|
+
// Apply URL connection parameters (explicit options override these via spread below)
|
|
45
|
+
...(params.connectTimeout != null ? { connectTimeout: params.connectTimeout } : {}),
|
|
46
|
+
...(params.connectionLimit != null ? { connectionLimit: params.connectionLimit } : {}),
|
|
47
|
+
...(params.queueLimit != null ? { queueLimit: params.queueLimit } : {}),
|
|
42
48
|
...(options || {})
|
|
43
49
|
});
|
|
44
50
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mysql-utils.js","sourceRoot":"","sources":["../../src/utils/mysql-utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,mCAAmC,CAAC;AAC3D,OAAO,KAAK,MAAM,QAAQ,CAAC;AAG3B,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AAUhD;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,OAA4B;IAC7D,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,GAAG,EAAE,EAAE,OAAO,GAAG,CAAC,EAAE,GAAG,OAAO,CAAC;IAChE,KAAK,IAAI,KAAK,GAAG,OAAO,GAAI,KAAK,EAAE,EAAE,CAAC;QACpC,IAAI,CAAC;YACH,MAAM,CAAC,KAAK,CAAC,oBAAoB,KAAK,EAAE,CAAC,CAAC;YAC1C,OAAO,UAAU,CAAC,KAAK,CAA+B,KAAK,EAAE,MAAM,CAAC,CAAC;QACvE,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,IAAI,KAAK,IAAI,CAAC,EAAE,CAAC;gBACf,MAAM,CAAC,CAAC;YACV,CAAC;YACD,MAAM,CAAC,IAAI,CAAC,uBAAuB,EAAE,CAAC,CAAC,CAAC;QAC1C,CAAC;IACH,CAAC;AACH,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,MAA6C,EAAE,OAA2B;IACnG,MAAM,UAAU,GAAG;QACjB,EAAE,EAAE,MAAM,CAAC,MAAM;QACjB,GAAG,EAAE,MAAM,CAAC,kBAAkB;QAC9B,IAAI,EAAE,MAAM,CAAC,kBAAkB;KAChC,CAAC;IACF,MAAM,aAAa,GAAG,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"mysql-utils.js","sourceRoot":"","sources":["../../src/utils/mysql-utils.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@powersync/service-module-mysql",
|
|
3
3
|
"repository": "https://github.com/powersync-ja/powersync-service",
|
|
4
4
|
"types": "dist/index.d.ts",
|
|
5
|
-
"version": "0.0.0-dev-
|
|
5
|
+
"version": "0.0.0-dev-20260224151854",
|
|
6
6
|
"license": "FSL-1.1-ALv2",
|
|
7
7
|
"main": "dist/index.js",
|
|
8
8
|
"type": "module",
|
|
@@ -30,18 +30,18 @@
|
|
|
30
30
|
"ts-codec": "^1.3.0",
|
|
31
31
|
"uri-js": "^4.4.1",
|
|
32
32
|
"uuid": "^11.1.0",
|
|
33
|
-
"@powersync/lib-services-framework": "0.0.0-dev-
|
|
34
|
-
"@powersync/service-core": "0.0.0-dev-
|
|
35
|
-
"@powersync/service-sync-rules": "0.0.0-dev-
|
|
36
|
-
"@powersync/service-types": "0.0.0-dev-
|
|
33
|
+
"@powersync/lib-services-framework": "0.0.0-dev-20260224151854",
|
|
34
|
+
"@powersync/service-core": "0.0.0-dev-20260224151854",
|
|
35
|
+
"@powersync/service-sync-rules": "0.0.0-dev-20260224151854",
|
|
36
|
+
"@powersync/service-types": "0.0.0-dev-20260224151854",
|
|
37
37
|
"@powersync/service-jsonbig": "0.17.12"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
40
|
"@types/async": "^3.2.24",
|
|
41
41
|
"@types/semver": "^7.7.1",
|
|
42
|
-
"@powersync/service-core-tests": "0.0.0-dev-
|
|
43
|
-
"@powersync/service-module-mongodb-storage": "0.0.0-dev-
|
|
44
|
-
"@powersync/service-module-postgres-storage": "0.0.0-dev-
|
|
42
|
+
"@powersync/service-core-tests": "0.0.0-dev-20260224151854",
|
|
43
|
+
"@powersync/service-module-mongodb-storage": "0.0.0-dev-20260224151854",
|
|
44
|
+
"@powersync/service-module-postgres-storage": "0.0.0-dev-20260224151854"
|
|
45
45
|
},
|
|
46
46
|
"scripts": {
|
|
47
47
|
"build": "tsc -b",
|
package/src/types/types.ts
CHANGED
|
@@ -6,6 +6,18 @@ import * as urijs from 'uri-js';
|
|
|
6
6
|
|
|
7
7
|
export const MYSQL_CONNECTION_TYPE = 'mysql' as const;
|
|
8
8
|
|
|
9
|
+
/**
|
|
10
|
+
* Connection parameters that can be parsed from the MySQL URI query string.
|
|
11
|
+
*
|
|
12
|
+
* All values are in milliseconds (for timeouts) or counts (for limits).
|
|
13
|
+
* MySQL uses camelCase naming convention.
|
|
14
|
+
*/
|
|
15
|
+
export interface MySQLConnectionParams {
|
|
16
|
+
connectTimeout?: number;
|
|
17
|
+
connectionLimit?: number;
|
|
18
|
+
queueLimit?: number;
|
|
19
|
+
}
|
|
20
|
+
|
|
9
21
|
export interface NormalizedMySQLConnectionConfig {
|
|
10
22
|
id: string;
|
|
11
23
|
tag: string;
|
|
@@ -25,6 +37,8 @@ export interface NormalizedMySQLConnectionConfig {
|
|
|
25
37
|
lookup?: LookupFunction;
|
|
26
38
|
|
|
27
39
|
binlog_queue_memory_limit: number;
|
|
40
|
+
|
|
41
|
+
connectionParams: MySQLConnectionParams;
|
|
28
42
|
}
|
|
29
43
|
|
|
30
44
|
export const MySQLConnectionConfig = service_types.configFile.DataSourceConfig.and(
|
|
@@ -105,6 +119,10 @@ export function normalizeConnectionConfig(options: MySQLConnectionConfig): Norma
|
|
|
105
119
|
|
|
106
120
|
const lookup = makeHostnameLookupFunction(hostname, { reject_ip_ranges: options.reject_ip_ranges ?? [] });
|
|
107
121
|
|
|
122
|
+
// Parse connection parameters from URL query string
|
|
123
|
+
const uriQuery = uri.query ? new URLSearchParams(uri.query) : undefined;
|
|
124
|
+
const connectionParams = parseMySQLConnectionParams(uriQuery);
|
|
125
|
+
|
|
108
126
|
return {
|
|
109
127
|
id: options.id ?? 'default',
|
|
110
128
|
tag: options.tag ?? 'default',
|
|
@@ -121,6 +139,55 @@ export function normalizeConnectionConfig(options: MySQLConnectionConfig): Norma
|
|
|
121
139
|
// Binlog processing queue memory limit before throttling is applied.
|
|
122
140
|
binlog_queue_memory_limit: options.binlog_queue_memory_limit ?? 50,
|
|
123
141
|
|
|
124
|
-
lookup
|
|
142
|
+
lookup,
|
|
143
|
+
|
|
144
|
+
connectionParams
|
|
125
145
|
};
|
|
126
146
|
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Parse a single numeric connection parameter from a URI query string value.
|
|
150
|
+
*
|
|
151
|
+
* Returns undefined if the value is missing, not a valid number, NaN, negative, zero, or Infinity.
|
|
152
|
+
*/
|
|
153
|
+
export function parseMySQLConnectionParam(value: string | null | undefined): number | undefined {
|
|
154
|
+
if (value == null) {
|
|
155
|
+
return undefined;
|
|
156
|
+
}
|
|
157
|
+
const parsed = Number(value);
|
|
158
|
+
if (isFinite(parsed) && parsed > 0) {
|
|
159
|
+
return parsed;
|
|
160
|
+
}
|
|
161
|
+
return undefined;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Parse connection parameters from a MySQL URI's query string.
|
|
166
|
+
*
|
|
167
|
+
* MySQL uses camelCase naming convention.
|
|
168
|
+
* Invalid values (NaN, negative, non-numeric) are silently ignored.
|
|
169
|
+
*/
|
|
170
|
+
export function parseMySQLConnectionParams(searchParams: URLSearchParams | undefined): MySQLConnectionParams {
|
|
171
|
+
const params: MySQLConnectionParams = {};
|
|
172
|
+
|
|
173
|
+
if (searchParams == null) {
|
|
174
|
+
return params;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
const connectTimeout = parseMySQLConnectionParam(searchParams.get('connectTimeout'));
|
|
178
|
+
if (connectTimeout != null) {
|
|
179
|
+
params.connectTimeout = connectTimeout;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const connectionLimit = parseMySQLConnectionParam(searchParams.get('connectionLimit'));
|
|
183
|
+
if (connectionLimit != null) {
|
|
184
|
+
params.connectionLimit = connectionLimit;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
const queueLimit = parseMySQLConnectionParam(searchParams.get('queueLimit'));
|
|
188
|
+
if (queueLimit != null) {
|
|
189
|
+
params.queueLimit = queueLimit;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
return params;
|
|
193
|
+
}
|
package/src/utils/mysql-utils.ts
CHANGED
|
@@ -37,6 +37,10 @@ export function createPool(config: types.NormalizedMySQLConnectionConfig, option
|
|
|
37
37
|
cert: config.client_certificate
|
|
38
38
|
};
|
|
39
39
|
const hasSSLOptions = Object.values(sslOptions).some((v) => !!v);
|
|
40
|
+
|
|
41
|
+
// URL connection parameters provide defaults; explicit options take precedence
|
|
42
|
+
const params = config.connectionParams;
|
|
43
|
+
|
|
40
44
|
// TODO: Use config.lookup for DNS resolution
|
|
41
45
|
return mysql.createPool({
|
|
42
46
|
host: config.hostname,
|
|
@@ -50,6 +54,10 @@ export function createPool(config: types.NormalizedMySQLConnectionConfig, option
|
|
|
50
54
|
timezone: 'Z', // Ensure no auto timezone manipulation of the dates occur
|
|
51
55
|
jsonStrings: true, // Return JSON columns as strings
|
|
52
56
|
dateStrings: true, // We parse and format them ourselves
|
|
57
|
+
// Apply URL connection parameters (explicit options override these via spread below)
|
|
58
|
+
...(params.connectTimeout != null ? { connectTimeout: params.connectTimeout } : {}),
|
|
59
|
+
...(params.connectionLimit != null ? { connectionLimit: params.connectionLimit } : {}),
|
|
60
|
+
...(params.queueLimit != null ? { queueLimit: params.queueLimit } : {}),
|
|
53
61
|
...(options || {})
|
|
54
62
|
});
|
|
55
63
|
}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import { describe, expect, test } from 'vitest';
|
|
2
|
+
import {
|
|
3
|
+
normalizeConnectionConfig,
|
|
4
|
+
parseMySQLConnectionParam,
|
|
5
|
+
parseMySQLConnectionParams
|
|
6
|
+
} from '@module/types/types.js';
|
|
7
|
+
|
|
8
|
+
describe('config', () => {
|
|
9
|
+
test('Should resolve database', () => {
|
|
10
|
+
const normalized = normalizeConnectionConfig({
|
|
11
|
+
type: 'mysql',
|
|
12
|
+
uri: 'mysql://user:pass@localhost:3306/mydb'
|
|
13
|
+
});
|
|
14
|
+
expect(normalized.database).equals('mydb');
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
describe('connection parameters', () => {
|
|
18
|
+
test('parses all connection parameters from URL query string', () => {
|
|
19
|
+
const normalized = normalizeConnectionConfig({
|
|
20
|
+
type: 'mysql',
|
|
21
|
+
uri: 'mysql://user:pass@localhost:3306/mydb?connectTimeout=5000&connectionLimit=20&queueLimit=100'
|
|
22
|
+
});
|
|
23
|
+
expect(normalized.connectionParams.connectTimeout).equals(5000);
|
|
24
|
+
expect(normalized.connectionParams.connectionLimit).equals(20);
|
|
25
|
+
expect(normalized.connectionParams.queueLimit).equals(100);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
test('URL without connection parameters returns empty connectionParams', () => {
|
|
29
|
+
const normalized = normalizeConnectionConfig({
|
|
30
|
+
type: 'mysql',
|
|
31
|
+
uri: 'mysql://user:pass@localhost:3306/mydb'
|
|
32
|
+
});
|
|
33
|
+
expect(normalized.connectionParams).toEqual({});
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
test('parameters can be partially specified', () => {
|
|
37
|
+
const normalized = normalizeConnectionConfig({
|
|
38
|
+
type: 'mysql',
|
|
39
|
+
uri: 'mysql://user:pass@localhost:3306/mydb?connectTimeout=5000&queueLimit=50'
|
|
40
|
+
});
|
|
41
|
+
expect(normalized.connectionParams.connectTimeout).equals(5000);
|
|
42
|
+
expect(normalized.connectionParams.queueLimit).equals(50);
|
|
43
|
+
expect(normalized.connectionParams.connectionLimit).toBeUndefined();
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
test('ignores invalid (non-numeric) connection parameter values', () => {
|
|
47
|
+
const normalized = normalizeConnectionConfig({
|
|
48
|
+
type: 'mysql',
|
|
49
|
+
uri: 'mysql://user:pass@localhost:3306/mydb?connectTimeout=abc&connectionLimit=xyz'
|
|
50
|
+
});
|
|
51
|
+
expect(normalized.connectionParams.connectTimeout).toBeUndefined();
|
|
52
|
+
expect(normalized.connectionParams.connectionLimit).toBeUndefined();
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
test('ignores negative connection parameter values', () => {
|
|
56
|
+
const normalized = normalizeConnectionConfig({
|
|
57
|
+
type: 'mysql',
|
|
58
|
+
uri: 'mysql://user:pass@localhost:3306/mydb?connectTimeout=-5000&connectionLimit=-10'
|
|
59
|
+
});
|
|
60
|
+
expect(normalized.connectionParams.connectTimeout).toBeUndefined();
|
|
61
|
+
expect(normalized.connectionParams.connectionLimit).toBeUndefined();
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
test('ignores zero connection parameter values', () => {
|
|
65
|
+
const normalized = normalizeConnectionConfig({
|
|
66
|
+
type: 'mysql',
|
|
67
|
+
uri: 'mysql://user:pass@localhost:3306/mydb?connectTimeout=0&connectionLimit=0&queueLimit=0'
|
|
68
|
+
});
|
|
69
|
+
expect(normalized.connectionParams.connectTimeout).toBeUndefined();
|
|
70
|
+
expect(normalized.connectionParams.connectionLimit).toBeUndefined();
|
|
71
|
+
expect(normalized.connectionParams.queueLimit).toBeUndefined();
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
test('works without URI (config-only)', () => {
|
|
75
|
+
const normalized = normalizeConnectionConfig({
|
|
76
|
+
type: 'mysql',
|
|
77
|
+
hostname: 'localhost',
|
|
78
|
+
port: 3306,
|
|
79
|
+
database: 'mydb',
|
|
80
|
+
username: 'user',
|
|
81
|
+
password: 'pass'
|
|
82
|
+
});
|
|
83
|
+
expect(normalized.connectionParams).toEqual({});
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
describe('parseMySQLConnectionParam', () => {
|
|
89
|
+
test('returns undefined when no value provided', () => {
|
|
90
|
+
expect(parseMySQLConnectionParam(undefined)).toBeUndefined();
|
|
91
|
+
expect(parseMySQLConnectionParam(null)).toBeUndefined();
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
test('parses valid numeric string', () => {
|
|
95
|
+
expect(parseMySQLConnectionParam('5000')).equals(5000);
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
test('parses fractional values', () => {
|
|
99
|
+
expect(parseMySQLConnectionParam('1500.5')).equals(1500.5);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
test('ignores non-numeric string', () => {
|
|
103
|
+
expect(parseMySQLConnectionParam('abc')).toBeUndefined();
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
test('ignores empty string', () => {
|
|
107
|
+
expect(parseMySQLConnectionParam('')).toBeUndefined();
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
test('ignores negative value', () => {
|
|
111
|
+
expect(parseMySQLConnectionParam('-5000')).toBeUndefined();
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
test('ignores zero', () => {
|
|
115
|
+
expect(parseMySQLConnectionParam('0')).toBeUndefined();
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
test('ignores Infinity', () => {
|
|
119
|
+
expect(parseMySQLConnectionParam('Infinity')).toBeUndefined();
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
test('ignores NaN', () => {
|
|
123
|
+
expect(parseMySQLConnectionParam('NaN')).toBeUndefined();
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
describe('parseMySQLConnectionParams', () => {
|
|
128
|
+
test('parses all supported parameters', () => {
|
|
129
|
+
const params = new URLSearchParams('connectTimeout=5000&connectionLimit=20&queueLimit=100');
|
|
130
|
+
const result = parseMySQLConnectionParams(params);
|
|
131
|
+
expect(result).toEqual({
|
|
132
|
+
connectTimeout: 5000,
|
|
133
|
+
connectionLimit: 20,
|
|
134
|
+
queueLimit: 100
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
test('returns empty object when no connection params present', () => {
|
|
139
|
+
const params = new URLSearchParams('someOther=value');
|
|
140
|
+
const result = parseMySQLConnectionParams(params);
|
|
141
|
+
expect(result).toEqual({});
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
test('returns empty object for undefined searchParams', () => {
|
|
145
|
+
const result = parseMySQLConnectionParams(undefined);
|
|
146
|
+
expect(result).toEqual({});
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
test('ignores invalid values and only includes valid ones', () => {
|
|
150
|
+
const params = new URLSearchParams('connectTimeout=5000&connectionLimit=abc&queueLimit=-10');
|
|
151
|
+
const result = parseMySQLConnectionParams(params);
|
|
152
|
+
expect(result).toEqual({
|
|
153
|
+
connectTimeout: 5000
|
|
154
|
+
});
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
test('handles partial parameter specification', () => {
|
|
158
|
+
const params = new URLSearchParams('connectTimeout=5000&queueLimit=100');
|
|
159
|
+
const result = parseMySQLConnectionParams(params);
|
|
160
|
+
expect(result).toEqual({
|
|
161
|
+
connectTimeout: 5000,
|
|
162
|
+
queueLimit: 100
|
|
163
|
+
});
|
|
164
|
+
});
|
|
165
|
+
});
|