@holo-js/db-mysql 0.3.28 → 0.4.1
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/LICENSE +21 -0
- package/dist/.holo-build-fingerprint +1 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.mjs +76 -18
- package/package.json +15 -9
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Holo-JS contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
de31065d1e429a5a696c9248de1565c4929497bd2c459886080a858ebc43c67e
|
package/dist/index.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ export { DriverAdapter, DriverExecutionResult, DriverQueryResult } from '@holo-j
|
|
|
4
4
|
|
|
5
5
|
interface MySQLQueryableLike {
|
|
6
6
|
query(sql: string, bindings?: readonly unknown[]): Promise<readonly [unknown, unknown]>;
|
|
7
|
+
execute?(sql: string, bindings?: readonly unknown[]): Promise<readonly [unknown, unknown]>;
|
|
7
8
|
}
|
|
8
9
|
interface MySQLClientLike extends MySQLQueryableLike {
|
|
9
10
|
release?(): void;
|
|
@@ -50,4 +51,5 @@ declare class MySQLAdapter<TConfig extends PoolOptions = PoolOptions> implements
|
|
|
50
51
|
declare function createMySQLAdapter<TConfig extends PoolOptions = PoolOptions>(options?: MySQLAdapterOptions<TConfig>): MySQLAdapter<TConfig>;
|
|
51
52
|
declare const mysqlDatabaseDriverFactory: DatabaseDriverFactory;
|
|
52
53
|
|
|
53
|
-
export { MySQLAdapter,
|
|
54
|
+
export { MySQLAdapter, createMySQLAdapter, mysqlDatabaseDriverFactory };
|
|
55
|
+
export type { MySQLAdapterOptions, MySQLClientLike, MySQLPoolLike, MySQLQueryableLike };
|
package/dist/index.mjs
CHANGED
|
@@ -1,10 +1,45 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
2
|
import { holoRuntimeInternals } from "@holo-js/db";
|
|
3
3
|
import mysql from "mysql2/promise";
|
|
4
|
+
var { formatWallClock, parseCalendarDate, parseWallClockTimestamp } = holoRuntimeInternals;
|
|
4
5
|
var TransactionError = class extends Error {
|
|
5
6
|
};
|
|
6
|
-
|
|
7
|
-
|
|
7
|
+
var ZERO_DATE_PART = /-00(?:-|\s|$)/;
|
|
8
|
+
function toDriverBinding(value, timeZone) {
|
|
9
|
+
if (!(value instanceof Date)) return value;
|
|
10
|
+
return Number.isNaN(value.getTime()) ? null : formatWallClock(value, timeZone);
|
|
11
|
+
}
|
|
12
|
+
function toDriverBindings(bindings) {
|
|
13
|
+
const timeZone = holoRuntimeInternals.getApplicationTimezone();
|
|
14
|
+
return bindings.map((value) => toDriverBinding(value, timeZone));
|
|
15
|
+
}
|
|
16
|
+
var DATE_TYPE_NAMES = ["date", "datetime", "timestamp"];
|
|
17
|
+
var rawTypeCast = (field) => field.buffer();
|
|
18
|
+
function dateStringTypes(config) {
|
|
19
|
+
const uriOption = typeof config.uri === "string" ? new URL(config.uri).searchParams.get("dateStrings") : null;
|
|
20
|
+
const option = config.dateStrings ?? (uriOption === "true" ? true : uriOption === "false" || uriOption === null ? void 0 : uriOption);
|
|
21
|
+
if (option === true || config.typeCast === false) return new Set(DATE_TYPE_NAMES);
|
|
22
|
+
if (typeof option === "string") return /* @__PURE__ */ new Set([option.toLowerCase()]);
|
|
23
|
+
if (Array.isArray(option)) return new Set(option.map((type) => String(type).toLowerCase()));
|
|
24
|
+
return /* @__PURE__ */ new Set();
|
|
25
|
+
}
|
|
26
|
+
function applicationTypeCast(config) {
|
|
27
|
+
const fallback = typeof config.typeCast === "function" ? config.typeCast : config.typeCast === false ? rawTypeCast : void 0;
|
|
28
|
+
const stringTypes = dateStringTypes(config);
|
|
29
|
+
return (field, next) => {
|
|
30
|
+
if (field.type !== "DATE" && field.type !== "DATETIME" && field.type !== "TIMESTAMP") {
|
|
31
|
+
return fallback ? fallback(field, next) : next();
|
|
32
|
+
}
|
|
33
|
+
const value = field.string();
|
|
34
|
+
if (value === null) return null;
|
|
35
|
+
if (stringTypes.has(field.type.toLowerCase()) || ZERO_DATE_PART.test(value)) return value;
|
|
36
|
+
const timeZone = holoRuntimeInternals.getApplicationTimezone();
|
|
37
|
+
if (field.type === "DATE") return parseCalendarDate(value, timeZone) ?? value;
|
|
38
|
+
return parseWallClockTimestamp(value, timeZone, "earlier") ?? value;
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
function sessionTimeZone(timeZone) {
|
|
42
|
+
return timeZone === "UTC" ? "+00:00" : timeZone;
|
|
8
43
|
}
|
|
9
44
|
function quoteDatabaseIdentifier(database) {
|
|
10
45
|
return `\`${database.replaceAll("`", "``")}\``;
|
|
@@ -41,11 +76,23 @@ function resolveBootstrapTarget(config) {
|
|
|
41
76
|
}
|
|
42
77
|
return void 0;
|
|
43
78
|
}
|
|
79
|
+
async function runStatement(queryable, sql, bindings) {
|
|
80
|
+
if (bindings.length === 0) {
|
|
81
|
+
return queryable.query(sql);
|
|
82
|
+
}
|
|
83
|
+
if (!queryable.execute) {
|
|
84
|
+
throw new Error("The MySQL driver must support prepared statements (execute) to run statements with bindings.");
|
|
85
|
+
}
|
|
86
|
+
return queryable.execute(sql, bindings.map((value) => value === void 0 ? null : value));
|
|
87
|
+
}
|
|
44
88
|
function wrapMySQLClient(client) {
|
|
45
89
|
const rawClient = client;
|
|
46
90
|
return {
|
|
47
91
|
async query(sql, bindings = []) {
|
|
48
|
-
return rawClient.query(sql,
|
|
92
|
+
return bindings.length === 0 ? rawClient.query(sql) : rawClient.query(sql, toDriverBindings(bindings));
|
|
93
|
+
},
|
|
94
|
+
async execute(sql, bindings = []) {
|
|
95
|
+
return rawClient.execute(sql, toDriverBindings(bindings));
|
|
49
96
|
},
|
|
50
97
|
release: rawClient.release?.bind(rawClient),
|
|
51
98
|
end: rawClient.end?.bind(rawClient)
|
|
@@ -55,7 +102,10 @@ function wrapMySQLPool(pool) {
|
|
|
55
102
|
const rawPool = pool;
|
|
56
103
|
return {
|
|
57
104
|
async query(sql, bindings = []) {
|
|
58
|
-
return rawPool.query(sql,
|
|
105
|
+
return bindings.length === 0 ? rawPool.query(sql) : rawPool.query(sql, toDriverBindings(bindings));
|
|
106
|
+
},
|
|
107
|
+
async execute(sql, bindings = []) {
|
|
108
|
+
return rawPool.execute(sql, toDriverBindings(bindings));
|
|
59
109
|
},
|
|
60
110
|
async getConnection() {
|
|
61
111
|
return wrapMySQLClient(await rawPool.getConnection());
|
|
@@ -63,13 +113,21 @@ function wrapMySQLPool(pool) {
|
|
|
63
113
|
end: rawPool.end.bind(rawPool)
|
|
64
114
|
};
|
|
65
115
|
}
|
|
116
|
+
var DEFAULT_MAX_PREPARED_STATEMENTS = 256;
|
|
66
117
|
function createNativeMySQLPool(config) {
|
|
67
|
-
const pool = mysql.createPool({
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
118
|
+
const pool = mysql.createPool({
|
|
119
|
+
maxPreparedStatements: DEFAULT_MAX_PREPARED_STATEMENTS,
|
|
120
|
+
...config,
|
|
121
|
+
timezone: config.timezone ?? "Z",
|
|
122
|
+
typeCast: applicationTypeCast(config)
|
|
123
|
+
});
|
|
124
|
+
pool.pool.on("connection", (connection) => {
|
|
125
|
+
connection.query("SET time_zone = ?", [sessionTimeZone(holoRuntimeInternals.getApplicationTimezone())], (error) => {
|
|
126
|
+
if (!error) return;
|
|
127
|
+
holoRuntimeInternals.reportError(error, { capability: "db", operation: "mysql.sessionTimeZone" });
|
|
128
|
+
connection.destroy();
|
|
71
129
|
});
|
|
72
|
-
}
|
|
130
|
+
});
|
|
73
131
|
return wrapMySQLPool(pool);
|
|
74
132
|
}
|
|
75
133
|
function isMySQLDatabaseMissing(error) {
|
|
@@ -85,7 +143,7 @@ var MySQLAdapter = class {
|
|
|
85
143
|
transactionClients;
|
|
86
144
|
constructor(options = {}) {
|
|
87
145
|
this.supportsConcurrentTransactionScopes = !options.client;
|
|
88
|
-
this.directClient = options.client
|
|
146
|
+
this.directClient = options.client;
|
|
89
147
|
this.pool = options.pool;
|
|
90
148
|
this.createPoolInstance = options.createPool ?? (options.client || options.pool ? void 0 : createNativeMySQLPool);
|
|
91
149
|
this.config = options.config ?? (options.uri ? { uri: options.uri } : {});
|
|
@@ -138,7 +196,7 @@ var MySQLAdapter = class {
|
|
|
138
196
|
}
|
|
139
197
|
async query(sql, bindings = []) {
|
|
140
198
|
const queryable = await this.getQueryable();
|
|
141
|
-
const [rows] = await queryable
|
|
199
|
+
const [rows] = await runStatement(queryable, sql, bindings);
|
|
142
200
|
const normalized = rows;
|
|
143
201
|
return {
|
|
144
202
|
rows: Array.isArray(normalized) ? [...normalized] : [],
|
|
@@ -150,7 +208,7 @@ var MySQLAdapter = class {
|
|
|
150
208
|
}
|
|
151
209
|
async execute(sql, bindings = []) {
|
|
152
210
|
const queryable = await this.getQueryable();
|
|
153
|
-
const [result] = await queryable
|
|
211
|
+
const [result] = await runStatement(queryable, sql, bindings);
|
|
154
212
|
const execution = result;
|
|
155
213
|
return {
|
|
156
214
|
affectedRows: typeof execution.affectedRows === "number" ? execution.affectedRows : 0,
|
|
@@ -204,10 +262,7 @@ var MySQLAdapter = class {
|
|
|
204
262
|
}
|
|
205
263
|
const bootstrapPool = this.createPoolInstance(target.config);
|
|
206
264
|
try {
|
|
207
|
-
const [existing] = await bootstrapPool.
|
|
208
|
-
"SELECT SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME = ?",
|
|
209
|
-
[target.database]
|
|
210
|
-
);
|
|
265
|
+
const [existing] = await runStatement(bootstrapPool, "SELECT SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME = ?", [target.database]);
|
|
211
266
|
if (Array.isArray(existing) && existing.length === 0) {
|
|
212
267
|
await bootstrapPool.query(`CREATE DATABASE ${quoteDatabaseIdentifier(target.database)}`, []);
|
|
213
268
|
}
|
|
@@ -235,13 +290,16 @@ var mysqlDatabaseDriverFactory = Object.freeze({
|
|
|
235
290
|
driver: "mysql",
|
|
236
291
|
supportsConcurrentTransactionScopes: true,
|
|
237
292
|
create(connection) {
|
|
238
|
-
|
|
293
|
+
const ssl = connection.ssl === true ? {} : connection.ssl || void 0;
|
|
294
|
+
const urlSetsSsl = connection.url ? new URL(connection.url).searchParams.has("ssl") : false;
|
|
295
|
+
const sslOptions = ssl && !urlSetsSsl ? { ssl } : {};
|
|
296
|
+
return connection.url ? createMySQLAdapter({ config: { uri: connection.url, ...sslOptions } }) : createMySQLAdapter({ config: {
|
|
239
297
|
host: connection.host,
|
|
240
298
|
port: connection.port,
|
|
241
299
|
user: connection.username,
|
|
242
300
|
password: connection.password,
|
|
243
301
|
database: connection.database,
|
|
244
|
-
|
|
302
|
+
...sslOptions
|
|
245
303
|
} });
|
|
246
304
|
}
|
|
247
305
|
});
|
package/package.json
CHANGED
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@holo-js/db-mysql",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.1",
|
|
4
4
|
"description": "Holo-JS Framework - MySQL database adapter",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/cobraprojects/holo-js.git",
|
|
10
|
+
"directory": "packages/db-mysql"
|
|
11
|
+
},
|
|
7
12
|
"exports": {
|
|
8
13
|
".": {
|
|
9
14
|
"types": "./dist/index.d.ts",
|
|
@@ -14,7 +19,8 @@
|
|
|
14
19
|
"main": "./dist/index.mjs",
|
|
15
20
|
"types": "./dist/index.d.ts",
|
|
16
21
|
"files": [
|
|
17
|
-
"dist"
|
|
22
|
+
"dist",
|
|
23
|
+
"LICENSE"
|
|
18
24
|
],
|
|
19
25
|
"scripts": {
|
|
20
26
|
"build": "tsup",
|
|
@@ -24,18 +30,18 @@
|
|
|
24
30
|
"test:integration": "HOLO_MYSQL_INTEGRATION=1 vitest --run tests/mysql.test.ts"
|
|
25
31
|
},
|
|
26
32
|
"peerDependencies": {
|
|
27
|
-
"@holo-js/db": "^0.
|
|
33
|
+
"@holo-js/db": "^0.4.1"
|
|
28
34
|
},
|
|
29
35
|
"dependencies": {
|
|
30
|
-
"mysql2": "^3.
|
|
36
|
+
"mysql2": "^3.24.6"
|
|
31
37
|
},
|
|
32
38
|
"devDependencies": {
|
|
33
|
-
"@holo-js/db": "^0.
|
|
34
|
-
"tsup": "^
|
|
35
|
-
"typescript": "^
|
|
36
|
-
"vitest": "^
|
|
39
|
+
"@holo-js/db": "^0.4.1",
|
|
40
|
+
"tsup": "^9.0.0",
|
|
41
|
+
"typescript": "^6.0.3",
|
|
42
|
+
"vitest": "^5.0.5"
|
|
37
43
|
},
|
|
38
44
|
"engines": {
|
|
39
|
-
"node": "^22.
|
|
45
|
+
"node": "^22.18.0 || ^24.0.0 || ^26.0.0"
|
|
40
46
|
}
|
|
41
47
|
}
|