@dyrected/db-mysql 2.5.5 → 2.5.7
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.md +7 -7
- package/dist/index.cjs +109 -24
- package/dist/index.d.cts +7 -1
- package/dist/index.d.ts +7 -1
- package/dist/index.js +109 -24
- package/package.json +2 -2
package/LICENSE.md
CHANGED
|
@@ -2,17 +2,17 @@ Business Source License 1.1
|
|
|
2
2
|
|
|
3
3
|
Parameters
|
|
4
4
|
|
|
5
|
-
Licensor:
|
|
6
|
-
Licensed Work:
|
|
5
|
+
Licensor: Dyrected
|
|
6
|
+
Licensed Work: Dyrected
|
|
7
7
|
Additional Use Grant: Commercial use is permitted as long as it is not used to provide a hosted or managed service that competes with Dyrected.
|
|
8
|
-
Change Date:
|
|
9
|
-
Change License:
|
|
8
|
+
Change Date: 2030-05-18
|
|
9
|
+
Change License: Apache License 2.0
|
|
10
10
|
|
|
11
11
|
---
|
|
12
12
|
|
|
13
13
|
Business Source License 1.1
|
|
14
14
|
|
|
15
|
-
License text copyright © 2024 MariaDB plc, All Rights Reserved.
|
|
15
|
+
License text copyright © 2024 MariaDB plc, All Rights Reserved.
|
|
16
16
|
“Business Source License” is a trademark of MariaDB plc.
|
|
17
17
|
|
|
18
18
|
### Terms
|
|
@@ -31,7 +31,7 @@ Any use of the Licensed Work in violation of this License will automatically ter
|
|
|
31
31
|
|
|
32
32
|
This License does not grant you any right in any trademark or logo of Licensor or its affiliates (provided that you may use a trademark or logo of Licensor as expressly required by this License).
|
|
33
33
|
|
|
34
|
-
TO THE EXTENT PERMITTED BY APPLICABLE LAW, THE LICENSED WORK IS PROVIDED ON AN “AS IS” BASIS. LICENSOR HEREBY DISCLAIMS ALL WARRANTIES AND CONDITIONS, EXPRESS OR IMPLIED, INCLUDING (WITHOUT LIMITATION) WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, AND TITLE.
|
|
34
|
+
TO THE EXTENT PERMITTED BY APPLICABLE LAW, THE LICENSED WORK IS PROVIDED ON AN “AS IS” BASIS. LICENSOR HEREBY DISCLAIMS ALL WARRANTIES AND CONDITIONS, EXPRESS OR IMPLIED, INCLUDING (WITHOUT LIMITATION) WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, AND TITLE.
|
|
35
35
|
|
|
36
36
|
MariaDB hereby grants you permission to use this License’s text to license your works, and to refer to it using the trademark “Business Source License”, as long as you comply with the Covenants of Licensor below.
|
|
37
37
|
|
|
@@ -39,7 +39,7 @@ MariaDB hereby grants you permission to use this License’s text to license you
|
|
|
39
39
|
|
|
40
40
|
In consideration of the right to use this License’s text and the “Business Source License” name and trademark, Licensor covenants to MariaDB, and to all other recipients of the licensed work to be provided by Licensor:
|
|
41
41
|
|
|
42
|
-
1. To specify as the Change License the GPL Version 2.0 or any later version, or a license that is compatible with GPL Version 2.0 or a later version, where “compatible” means that software provided under the Change License can be included in a program with software provided under GPL Version 2.0 or a later version. Licensor may specify additional Change Licenses without limitation.
|
|
42
|
+
1. To specify as the Change License the GPL Version 2.0 or any later version, or a license that is compatible with GPL Version 2.0 or a later version, where “compatible” means that software provided under the Change License can be included in a program with software provided under GPL Version 2.0 or a later version. Licensor may specify additional Change Licenses without limitation.
|
|
43
43
|
|
|
44
44
|
2. To either: (a) specify an additional grant of rights to use that does not impose any additional restriction on the right granted in this License, as the Additional Use Grant; or (b) insert the text “None”.
|
|
45
45
|
|
package/dist/index.cjs
CHANGED
|
@@ -38,7 +38,74 @@ var import_core = require("@dyrected/core");
|
|
|
38
38
|
var import_promise = __toESM(require("mysql2/promise"), 1);
|
|
39
39
|
var MysqlAdapter = class {
|
|
40
40
|
pool;
|
|
41
|
+
config;
|
|
42
|
+
initPromise = null;
|
|
41
43
|
constructor(config) {
|
|
44
|
+
this.config = config;
|
|
45
|
+
this.ensureInitialized().catch((err) => {
|
|
46
|
+
if (err.code === "EADDRNOTAVAIL" || err.message?.includes("EADDRNOTAVAIL")) {
|
|
47
|
+
this.handleConnectionError(err);
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
handleConnectionError(err) {
|
|
52
|
+
if (err && (err.code === "EADDRNOTAVAIL" || err.message?.includes("EADDRNOTAVAIL"))) {
|
|
53
|
+
const customMessage = `
|
|
54
|
+
[dyrected/db-mysql] ERROR: MySQL connection failed with EADDRNOTAVAIL.
|
|
55
|
+
--------------------------------------------------------------------------------
|
|
56
|
+
This error often occurs when:
|
|
57
|
+
1. 'localhost' is resolved to an IPv6 address (::1) but MySQL is only listening on IPv4 (127.0.0.1).
|
|
58
|
+
2. The MySQL server is not running or is bound to a different port.
|
|
59
|
+
|
|
60
|
+
FIX INSTRUCTIONS:
|
|
61
|
+
- Try changing your host configuration in '.env' or config from 'localhost' to '127.0.0.1'.
|
|
62
|
+
- Make sure your local MySQL service is active and running on the configured port.
|
|
63
|
+
--------------------------------------------------------------------------------
|
|
64
|
+
`;
|
|
65
|
+
console.error(customMessage);
|
|
66
|
+
throw new Error(`MySQL Connection Failed: EADDRNOTAVAIL. Hint: Try using '127.0.0.1' instead of 'localhost'. Original: ${err.message}`);
|
|
67
|
+
}
|
|
68
|
+
throw err;
|
|
69
|
+
}
|
|
70
|
+
async ensureInitialized() {
|
|
71
|
+
if (!this.initPromise) {
|
|
72
|
+
this.initPromise = this.initialize();
|
|
73
|
+
}
|
|
74
|
+
await this.initPromise;
|
|
75
|
+
}
|
|
76
|
+
async initialize() {
|
|
77
|
+
const config = this.config;
|
|
78
|
+
let dbName = config.database;
|
|
79
|
+
let serverConfig = null;
|
|
80
|
+
if (config.url) {
|
|
81
|
+
try {
|
|
82
|
+
const parsed = new URL(config.url);
|
|
83
|
+
dbName = parsed.pathname.replace(/^\//, "");
|
|
84
|
+
const serverUrl = `${parsed.protocol}//${parsed.username}:${parsed.password}@${parsed.host}`;
|
|
85
|
+
serverConfig = serverUrl;
|
|
86
|
+
} catch (err) {
|
|
87
|
+
}
|
|
88
|
+
} else {
|
|
89
|
+
serverConfig = {
|
|
90
|
+
host: config.host ?? "localhost",
|
|
91
|
+
port: config.port ?? 3306,
|
|
92
|
+
user: config.user,
|
|
93
|
+
password: config.password
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
if (dbName && serverConfig) {
|
|
97
|
+
try {
|
|
98
|
+
const tempConn = await import_promise.default.createConnection(serverConfig);
|
|
99
|
+
await tempConn.query(`CREATE DATABASE IF NOT EXISTS \`${dbName}\``);
|
|
100
|
+
await tempConn.end();
|
|
101
|
+
console.log(`[dyrected/db-mysql] Database "${dbName}" checked/created successfully`);
|
|
102
|
+
} catch (err) {
|
|
103
|
+
console.warn(`[dyrected/db-mysql] Auto-creation of database "${dbName}" skipped/failed:`, err.message);
|
|
104
|
+
if (err.code === "EADDRNOTAVAIL" || err.message?.includes("EADDRNOTAVAIL")) {
|
|
105
|
+
this.handleConnectionError(err);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
42
109
|
if (config.url) {
|
|
43
110
|
this.pool = import_promise.default.createPool(config.url);
|
|
44
111
|
} else {
|
|
@@ -48,26 +115,42 @@ var MysqlAdapter = class {
|
|
|
48
115
|
user: config.user,
|
|
49
116
|
password: config.password,
|
|
50
117
|
database: config.database,
|
|
51
|
-
// Return dates as strings for consistency with other adapters
|
|
52
118
|
dateStrings: true
|
|
53
119
|
});
|
|
54
120
|
}
|
|
55
|
-
|
|
121
|
+
try {
|
|
122
|
+
await this.pool.query(`
|
|
123
|
+
CREATE TABLE IF NOT EXISTS dyrected_internal (
|
|
124
|
+
\`key\` VARCHAR(255) PRIMARY KEY,
|
|
125
|
+
value JSON NOT NULL
|
|
126
|
+
)
|
|
127
|
+
`);
|
|
128
|
+
} catch (err) {
|
|
129
|
+
this.handleConnectionError(err);
|
|
130
|
+
}
|
|
56
131
|
}
|
|
57
|
-
async
|
|
58
|
-
await this.
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
)
|
|
63
|
-
|
|
132
|
+
async query(sql, params) {
|
|
133
|
+
await this.ensureInitialized();
|
|
134
|
+
try {
|
|
135
|
+
return await this.pool.query(sql, params);
|
|
136
|
+
} catch (err) {
|
|
137
|
+
this.handleConnectionError(err);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
async execute(sql, params) {
|
|
141
|
+
await this.ensureInitialized();
|
|
142
|
+
try {
|
|
143
|
+
return await this.pool.execute(sql, params);
|
|
144
|
+
} catch (err) {
|
|
145
|
+
this.handleConnectionError(err);
|
|
146
|
+
}
|
|
64
147
|
}
|
|
65
148
|
getTableName(slug) {
|
|
66
149
|
return `collection_${slug}`;
|
|
67
150
|
}
|
|
68
151
|
async ensureTable(slug, fields = []) {
|
|
69
152
|
const tableName = this.getTableName(slug);
|
|
70
|
-
await this.
|
|
153
|
+
await this.query(`
|
|
71
154
|
CREATE TABLE IF NOT EXISTS \`${tableName}\` (
|
|
72
155
|
id VARCHAR(36) PRIMARY KEY,
|
|
73
156
|
data JSON NOT NULL,
|
|
@@ -75,7 +158,7 @@ var MysqlAdapter = class {
|
|
|
75
158
|
updated_at DATETIME(3) DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)
|
|
76
159
|
)
|
|
77
160
|
`);
|
|
78
|
-
const [cols] = await this.
|
|
161
|
+
const [cols] = await this.query(`SHOW COLUMNS FROM \`${tableName}\``);
|
|
79
162
|
const existingCols = cols.map((c) => c.Field);
|
|
80
163
|
for (const field of fields) {
|
|
81
164
|
if (field.promoted && !existingCols.includes(field.name)) {
|
|
@@ -83,7 +166,7 @@ var MysqlAdapter = class {
|
|
|
83
166
|
let sqlType = "TEXT";
|
|
84
167
|
if (field.type === "number") sqlType = "DECIMAL(19,4)";
|
|
85
168
|
if (field.type === "boolean") sqlType = "TINYINT(1)";
|
|
86
|
-
await this.
|
|
169
|
+
await this.query(`ALTER TABLE \`${tableName}\` ADD COLUMN \`${field.name}\` ${sqlType}`);
|
|
87
170
|
}
|
|
88
171
|
}
|
|
89
172
|
}
|
|
@@ -93,7 +176,7 @@ var MysqlAdapter = class {
|
|
|
93
176
|
const limit = args.limit ?? 10;
|
|
94
177
|
const page = args.page ?? 1;
|
|
95
178
|
const offset = (page - 1) * limit;
|
|
96
|
-
const [cols] = await this.
|
|
179
|
+
const [cols] = await this.query(`SHOW COLUMNS FROM \`${tableName}\``);
|
|
97
180
|
const existingCols = cols.map((c) => c.Field);
|
|
98
181
|
let whereSql = "";
|
|
99
182
|
let whereParams = [];
|
|
@@ -113,12 +196,12 @@ var MysqlAdapter = class {
|
|
|
113
196
|
}
|
|
114
197
|
const rawSort = args.sort ?? "created_at DESC";
|
|
115
198
|
const sort = rawSort.replace(/\bcreatedAt\b/g, "created_at").replace(/\bupdatedAt\b/g, "updated_at");
|
|
116
|
-
const [countRows] = await this.
|
|
199
|
+
const [countRows] = await this.query(
|
|
117
200
|
`SELECT COUNT(*) AS total FROM \`${tableName}\` ${whereSql}`,
|
|
118
201
|
whereParams
|
|
119
202
|
);
|
|
120
203
|
const total = Number(countRows[0].total);
|
|
121
|
-
const [rows] = await this.
|
|
204
|
+
const [rows] = await this.query(
|
|
122
205
|
`SELECT * FROM \`${tableName}\` ${whereSql} ORDER BY ${sort} LIMIT ? OFFSET ?`,
|
|
123
206
|
[...whereParams, limit, offset]
|
|
124
207
|
);
|
|
@@ -142,7 +225,7 @@ var MysqlAdapter = class {
|
|
|
142
225
|
async findOne(params) {
|
|
143
226
|
await this.ensureTable(params.collection);
|
|
144
227
|
const tableName = this.getTableName(params.collection);
|
|
145
|
-
const [rows] = await this.
|
|
228
|
+
const [rows] = await this.query(`SELECT * FROM \`${tableName}\` WHERE id = ?`, [params.id]);
|
|
146
229
|
const row = rows[0];
|
|
147
230
|
if (!row) return null;
|
|
148
231
|
return {
|
|
@@ -155,7 +238,7 @@ var MysqlAdapter = class {
|
|
|
155
238
|
async create(params) {
|
|
156
239
|
await this.ensureTable(params.collection);
|
|
157
240
|
const tableName = this.getTableName(params.collection);
|
|
158
|
-
const [cols] = await this.
|
|
241
|
+
const [cols] = await this.query(`SHOW COLUMNS FROM \`${tableName}\``);
|
|
159
242
|
const existingCols = cols.map((c) => c.Field);
|
|
160
243
|
const id = params.data.id ?? Math.random().toString(36).substring(7);
|
|
161
244
|
const now = (/* @__PURE__ */ new Date()).toISOString().replace("T", " ").replace("Z", "");
|
|
@@ -173,13 +256,13 @@ var MysqlAdapter = class {
|
|
|
173
256
|
const colNames = ["id", "data", "created_at", "updated_at", ...Object.keys(promotedValues).map((k) => `\`${k}\``)];
|
|
174
257
|
const placeholders = colNames.map(() => "?").join(", ");
|
|
175
258
|
const values = [id, JSON.stringify(data), now, now, ...Object.values(promotedValues)];
|
|
176
|
-
await this.
|
|
259
|
+
await this.query(`INSERT INTO \`${tableName}\` (${colNames.join(", ")}) VALUES (${placeholders})`, values);
|
|
177
260
|
return { id, ...data, createdAt: now, updatedAt: now };
|
|
178
261
|
}
|
|
179
262
|
async update(params) {
|
|
180
263
|
await this.ensureTable(params.collection);
|
|
181
264
|
const tableName = this.getTableName(params.collection);
|
|
182
|
-
const [cols] = await this.
|
|
265
|
+
const [cols] = await this.query(`SHOW COLUMNS FROM \`${tableName}\``);
|
|
183
266
|
const existingCols = cols.map((c) => c.Field);
|
|
184
267
|
const existing = await this.findOne({ collection: params.collection, id: params.id });
|
|
185
268
|
const now = (/* @__PURE__ */ new Date()).toISOString().replace("T", " ").replace("Z", "");
|
|
@@ -196,7 +279,7 @@ var MysqlAdapter = class {
|
|
|
196
279
|
}
|
|
197
280
|
const setClauses = ["data = ?", "updated_at = ?", ...Object.keys(promotedValues).map((k) => `\`${k}\` = ?`)];
|
|
198
281
|
const values = [JSON.stringify(merged), now, ...Object.values(promotedValues), params.id];
|
|
199
|
-
await this.
|
|
282
|
+
await this.query(`UPDATE \`${tableName}\` SET ${setClauses.join(", ")} WHERE id = ?`, values);
|
|
200
283
|
return { id: params.id, ...merged, createdAt: existing?.createdAt, updatedAt: now };
|
|
201
284
|
}
|
|
202
285
|
async sync(collections) {
|
|
@@ -207,10 +290,10 @@ var MysqlAdapter = class {
|
|
|
207
290
|
async delete(params) {
|
|
208
291
|
await this.ensureTable(params.collection);
|
|
209
292
|
const tableName = this.getTableName(params.collection);
|
|
210
|
-
await this.
|
|
293
|
+
await this.query(`DELETE FROM \`${tableName}\` WHERE id = ?`, [params.id]);
|
|
211
294
|
}
|
|
212
295
|
async getGlobal(params) {
|
|
213
|
-
const [rows] = await this.
|
|
296
|
+
const [rows] = await this.query("SELECT value FROM dyrected_internal WHERE `key` = ?", [
|
|
214
297
|
`global_${params.slug}`
|
|
215
298
|
]);
|
|
216
299
|
const row = rows[0];
|
|
@@ -218,7 +301,7 @@ var MysqlAdapter = class {
|
|
|
218
301
|
return typeof row.value === "string" ? JSON.parse(row.value) : row.value;
|
|
219
302
|
}
|
|
220
303
|
async updateGlobal(params) {
|
|
221
|
-
await this.
|
|
304
|
+
await this.execute(
|
|
222
305
|
"INSERT INTO dyrected_internal (`key`, value) VALUES (?, ?) ON DUPLICATE KEY UPDATE value = VALUES(value)",
|
|
223
306
|
[`global_${params.slug}`, JSON.stringify(params.data)]
|
|
224
307
|
);
|
|
@@ -226,7 +309,9 @@ var MysqlAdapter = class {
|
|
|
226
309
|
}
|
|
227
310
|
/** Gracefully close the connection pool. Call on process exit. */
|
|
228
311
|
async close() {
|
|
229
|
-
|
|
312
|
+
if (this.pool) {
|
|
313
|
+
await this.pool.end();
|
|
314
|
+
}
|
|
230
315
|
}
|
|
231
316
|
};
|
|
232
317
|
var mysqlAdapter = (config) => new MysqlAdapter(config);
|
package/dist/index.d.cts
CHANGED
|
@@ -12,8 +12,14 @@ interface MysqlAdapterConfig {
|
|
|
12
12
|
}
|
|
13
13
|
declare class MysqlAdapter implements DatabaseAdapter {
|
|
14
14
|
private pool;
|
|
15
|
+
private config;
|
|
16
|
+
private initPromise;
|
|
15
17
|
constructor(config: MysqlAdapterConfig);
|
|
16
|
-
private
|
|
18
|
+
private handleConnectionError;
|
|
19
|
+
private ensureInitialized;
|
|
20
|
+
private initialize;
|
|
21
|
+
private query;
|
|
22
|
+
execute(sql: string, params?: any[]): Promise<any>;
|
|
17
23
|
private getTableName;
|
|
18
24
|
private ensureTable;
|
|
19
25
|
find(args: {
|
package/dist/index.d.ts
CHANGED
|
@@ -12,8 +12,14 @@ interface MysqlAdapterConfig {
|
|
|
12
12
|
}
|
|
13
13
|
declare class MysqlAdapter implements DatabaseAdapter {
|
|
14
14
|
private pool;
|
|
15
|
+
private config;
|
|
16
|
+
private initPromise;
|
|
15
17
|
constructor(config: MysqlAdapterConfig);
|
|
16
|
-
private
|
|
18
|
+
private handleConnectionError;
|
|
19
|
+
private ensureInitialized;
|
|
20
|
+
private initialize;
|
|
21
|
+
private query;
|
|
22
|
+
execute(sql: string, params?: any[]): Promise<any>;
|
|
17
23
|
private getTableName;
|
|
18
24
|
private ensureTable;
|
|
19
25
|
find(args: {
|
package/dist/index.js
CHANGED
|
@@ -3,7 +3,74 @@ import { parseSqlWhere } from "@dyrected/core";
|
|
|
3
3
|
import mysql from "mysql2/promise";
|
|
4
4
|
var MysqlAdapter = class {
|
|
5
5
|
pool;
|
|
6
|
+
config;
|
|
7
|
+
initPromise = null;
|
|
6
8
|
constructor(config) {
|
|
9
|
+
this.config = config;
|
|
10
|
+
this.ensureInitialized().catch((err) => {
|
|
11
|
+
if (err.code === "EADDRNOTAVAIL" || err.message?.includes("EADDRNOTAVAIL")) {
|
|
12
|
+
this.handleConnectionError(err);
|
|
13
|
+
}
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
handleConnectionError(err) {
|
|
17
|
+
if (err && (err.code === "EADDRNOTAVAIL" || err.message?.includes("EADDRNOTAVAIL"))) {
|
|
18
|
+
const customMessage = `
|
|
19
|
+
[dyrected/db-mysql] ERROR: MySQL connection failed with EADDRNOTAVAIL.
|
|
20
|
+
--------------------------------------------------------------------------------
|
|
21
|
+
This error often occurs when:
|
|
22
|
+
1. 'localhost' is resolved to an IPv6 address (::1) but MySQL is only listening on IPv4 (127.0.0.1).
|
|
23
|
+
2. The MySQL server is not running or is bound to a different port.
|
|
24
|
+
|
|
25
|
+
FIX INSTRUCTIONS:
|
|
26
|
+
- Try changing your host configuration in '.env' or config from 'localhost' to '127.0.0.1'.
|
|
27
|
+
- Make sure your local MySQL service is active and running on the configured port.
|
|
28
|
+
--------------------------------------------------------------------------------
|
|
29
|
+
`;
|
|
30
|
+
console.error(customMessage);
|
|
31
|
+
throw new Error(`MySQL Connection Failed: EADDRNOTAVAIL. Hint: Try using '127.0.0.1' instead of 'localhost'. Original: ${err.message}`);
|
|
32
|
+
}
|
|
33
|
+
throw err;
|
|
34
|
+
}
|
|
35
|
+
async ensureInitialized() {
|
|
36
|
+
if (!this.initPromise) {
|
|
37
|
+
this.initPromise = this.initialize();
|
|
38
|
+
}
|
|
39
|
+
await this.initPromise;
|
|
40
|
+
}
|
|
41
|
+
async initialize() {
|
|
42
|
+
const config = this.config;
|
|
43
|
+
let dbName = config.database;
|
|
44
|
+
let serverConfig = null;
|
|
45
|
+
if (config.url) {
|
|
46
|
+
try {
|
|
47
|
+
const parsed = new URL(config.url);
|
|
48
|
+
dbName = parsed.pathname.replace(/^\//, "");
|
|
49
|
+
const serverUrl = `${parsed.protocol}//${parsed.username}:${parsed.password}@${parsed.host}`;
|
|
50
|
+
serverConfig = serverUrl;
|
|
51
|
+
} catch (err) {
|
|
52
|
+
}
|
|
53
|
+
} else {
|
|
54
|
+
serverConfig = {
|
|
55
|
+
host: config.host ?? "localhost",
|
|
56
|
+
port: config.port ?? 3306,
|
|
57
|
+
user: config.user,
|
|
58
|
+
password: config.password
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
if (dbName && serverConfig) {
|
|
62
|
+
try {
|
|
63
|
+
const tempConn = await mysql.createConnection(serverConfig);
|
|
64
|
+
await tempConn.query(`CREATE DATABASE IF NOT EXISTS \`${dbName}\``);
|
|
65
|
+
await tempConn.end();
|
|
66
|
+
console.log(`[dyrected/db-mysql] Database "${dbName}" checked/created successfully`);
|
|
67
|
+
} catch (err) {
|
|
68
|
+
console.warn(`[dyrected/db-mysql] Auto-creation of database "${dbName}" skipped/failed:`, err.message);
|
|
69
|
+
if (err.code === "EADDRNOTAVAIL" || err.message?.includes("EADDRNOTAVAIL")) {
|
|
70
|
+
this.handleConnectionError(err);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
7
74
|
if (config.url) {
|
|
8
75
|
this.pool = mysql.createPool(config.url);
|
|
9
76
|
} else {
|
|
@@ -13,26 +80,42 @@ var MysqlAdapter = class {
|
|
|
13
80
|
user: config.user,
|
|
14
81
|
password: config.password,
|
|
15
82
|
database: config.database,
|
|
16
|
-
// Return dates as strings for consistency with other adapters
|
|
17
83
|
dateStrings: true
|
|
18
84
|
});
|
|
19
85
|
}
|
|
20
|
-
|
|
86
|
+
try {
|
|
87
|
+
await this.pool.query(`
|
|
88
|
+
CREATE TABLE IF NOT EXISTS dyrected_internal (
|
|
89
|
+
\`key\` VARCHAR(255) PRIMARY KEY,
|
|
90
|
+
value JSON NOT NULL
|
|
91
|
+
)
|
|
92
|
+
`);
|
|
93
|
+
} catch (err) {
|
|
94
|
+
this.handleConnectionError(err);
|
|
95
|
+
}
|
|
21
96
|
}
|
|
22
|
-
async
|
|
23
|
-
await this.
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
)
|
|
28
|
-
|
|
97
|
+
async query(sql, params) {
|
|
98
|
+
await this.ensureInitialized();
|
|
99
|
+
try {
|
|
100
|
+
return await this.pool.query(sql, params);
|
|
101
|
+
} catch (err) {
|
|
102
|
+
this.handleConnectionError(err);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
async execute(sql, params) {
|
|
106
|
+
await this.ensureInitialized();
|
|
107
|
+
try {
|
|
108
|
+
return await this.pool.execute(sql, params);
|
|
109
|
+
} catch (err) {
|
|
110
|
+
this.handleConnectionError(err);
|
|
111
|
+
}
|
|
29
112
|
}
|
|
30
113
|
getTableName(slug) {
|
|
31
114
|
return `collection_${slug}`;
|
|
32
115
|
}
|
|
33
116
|
async ensureTable(slug, fields = []) {
|
|
34
117
|
const tableName = this.getTableName(slug);
|
|
35
|
-
await this.
|
|
118
|
+
await this.query(`
|
|
36
119
|
CREATE TABLE IF NOT EXISTS \`${tableName}\` (
|
|
37
120
|
id VARCHAR(36) PRIMARY KEY,
|
|
38
121
|
data JSON NOT NULL,
|
|
@@ -40,7 +123,7 @@ var MysqlAdapter = class {
|
|
|
40
123
|
updated_at DATETIME(3) DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)
|
|
41
124
|
)
|
|
42
125
|
`);
|
|
43
|
-
const [cols] = await this.
|
|
126
|
+
const [cols] = await this.query(`SHOW COLUMNS FROM \`${tableName}\``);
|
|
44
127
|
const existingCols = cols.map((c) => c.Field);
|
|
45
128
|
for (const field of fields) {
|
|
46
129
|
if (field.promoted && !existingCols.includes(field.name)) {
|
|
@@ -48,7 +131,7 @@ var MysqlAdapter = class {
|
|
|
48
131
|
let sqlType = "TEXT";
|
|
49
132
|
if (field.type === "number") sqlType = "DECIMAL(19,4)";
|
|
50
133
|
if (field.type === "boolean") sqlType = "TINYINT(1)";
|
|
51
|
-
await this.
|
|
134
|
+
await this.query(`ALTER TABLE \`${tableName}\` ADD COLUMN \`${field.name}\` ${sqlType}`);
|
|
52
135
|
}
|
|
53
136
|
}
|
|
54
137
|
}
|
|
@@ -58,7 +141,7 @@ var MysqlAdapter = class {
|
|
|
58
141
|
const limit = args.limit ?? 10;
|
|
59
142
|
const page = args.page ?? 1;
|
|
60
143
|
const offset = (page - 1) * limit;
|
|
61
|
-
const [cols] = await this.
|
|
144
|
+
const [cols] = await this.query(`SHOW COLUMNS FROM \`${tableName}\``);
|
|
62
145
|
const existingCols = cols.map((c) => c.Field);
|
|
63
146
|
let whereSql = "";
|
|
64
147
|
let whereParams = [];
|
|
@@ -78,12 +161,12 @@ var MysqlAdapter = class {
|
|
|
78
161
|
}
|
|
79
162
|
const rawSort = args.sort ?? "created_at DESC";
|
|
80
163
|
const sort = rawSort.replace(/\bcreatedAt\b/g, "created_at").replace(/\bupdatedAt\b/g, "updated_at");
|
|
81
|
-
const [countRows] = await this.
|
|
164
|
+
const [countRows] = await this.query(
|
|
82
165
|
`SELECT COUNT(*) AS total FROM \`${tableName}\` ${whereSql}`,
|
|
83
166
|
whereParams
|
|
84
167
|
);
|
|
85
168
|
const total = Number(countRows[0].total);
|
|
86
|
-
const [rows] = await this.
|
|
169
|
+
const [rows] = await this.query(
|
|
87
170
|
`SELECT * FROM \`${tableName}\` ${whereSql} ORDER BY ${sort} LIMIT ? OFFSET ?`,
|
|
88
171
|
[...whereParams, limit, offset]
|
|
89
172
|
);
|
|
@@ -107,7 +190,7 @@ var MysqlAdapter = class {
|
|
|
107
190
|
async findOne(params) {
|
|
108
191
|
await this.ensureTable(params.collection);
|
|
109
192
|
const tableName = this.getTableName(params.collection);
|
|
110
|
-
const [rows] = await this.
|
|
193
|
+
const [rows] = await this.query(`SELECT * FROM \`${tableName}\` WHERE id = ?`, [params.id]);
|
|
111
194
|
const row = rows[0];
|
|
112
195
|
if (!row) return null;
|
|
113
196
|
return {
|
|
@@ -120,7 +203,7 @@ var MysqlAdapter = class {
|
|
|
120
203
|
async create(params) {
|
|
121
204
|
await this.ensureTable(params.collection);
|
|
122
205
|
const tableName = this.getTableName(params.collection);
|
|
123
|
-
const [cols] = await this.
|
|
206
|
+
const [cols] = await this.query(`SHOW COLUMNS FROM \`${tableName}\``);
|
|
124
207
|
const existingCols = cols.map((c) => c.Field);
|
|
125
208
|
const id = params.data.id ?? Math.random().toString(36).substring(7);
|
|
126
209
|
const now = (/* @__PURE__ */ new Date()).toISOString().replace("T", " ").replace("Z", "");
|
|
@@ -138,13 +221,13 @@ var MysqlAdapter = class {
|
|
|
138
221
|
const colNames = ["id", "data", "created_at", "updated_at", ...Object.keys(promotedValues).map((k) => `\`${k}\``)];
|
|
139
222
|
const placeholders = colNames.map(() => "?").join(", ");
|
|
140
223
|
const values = [id, JSON.stringify(data), now, now, ...Object.values(promotedValues)];
|
|
141
|
-
await this.
|
|
224
|
+
await this.query(`INSERT INTO \`${tableName}\` (${colNames.join(", ")}) VALUES (${placeholders})`, values);
|
|
142
225
|
return { id, ...data, createdAt: now, updatedAt: now };
|
|
143
226
|
}
|
|
144
227
|
async update(params) {
|
|
145
228
|
await this.ensureTable(params.collection);
|
|
146
229
|
const tableName = this.getTableName(params.collection);
|
|
147
|
-
const [cols] = await this.
|
|
230
|
+
const [cols] = await this.query(`SHOW COLUMNS FROM \`${tableName}\``);
|
|
148
231
|
const existingCols = cols.map((c) => c.Field);
|
|
149
232
|
const existing = await this.findOne({ collection: params.collection, id: params.id });
|
|
150
233
|
const now = (/* @__PURE__ */ new Date()).toISOString().replace("T", " ").replace("Z", "");
|
|
@@ -161,7 +244,7 @@ var MysqlAdapter = class {
|
|
|
161
244
|
}
|
|
162
245
|
const setClauses = ["data = ?", "updated_at = ?", ...Object.keys(promotedValues).map((k) => `\`${k}\` = ?`)];
|
|
163
246
|
const values = [JSON.stringify(merged), now, ...Object.values(promotedValues), params.id];
|
|
164
|
-
await this.
|
|
247
|
+
await this.query(`UPDATE \`${tableName}\` SET ${setClauses.join(", ")} WHERE id = ?`, values);
|
|
165
248
|
return { id: params.id, ...merged, createdAt: existing?.createdAt, updatedAt: now };
|
|
166
249
|
}
|
|
167
250
|
async sync(collections) {
|
|
@@ -172,10 +255,10 @@ var MysqlAdapter = class {
|
|
|
172
255
|
async delete(params) {
|
|
173
256
|
await this.ensureTable(params.collection);
|
|
174
257
|
const tableName = this.getTableName(params.collection);
|
|
175
|
-
await this.
|
|
258
|
+
await this.query(`DELETE FROM \`${tableName}\` WHERE id = ?`, [params.id]);
|
|
176
259
|
}
|
|
177
260
|
async getGlobal(params) {
|
|
178
|
-
const [rows] = await this.
|
|
261
|
+
const [rows] = await this.query("SELECT value FROM dyrected_internal WHERE `key` = ?", [
|
|
179
262
|
`global_${params.slug}`
|
|
180
263
|
]);
|
|
181
264
|
const row = rows[0];
|
|
@@ -183,7 +266,7 @@ var MysqlAdapter = class {
|
|
|
183
266
|
return typeof row.value === "string" ? JSON.parse(row.value) : row.value;
|
|
184
267
|
}
|
|
185
268
|
async updateGlobal(params) {
|
|
186
|
-
await this.
|
|
269
|
+
await this.execute(
|
|
187
270
|
"INSERT INTO dyrected_internal (`key`, value) VALUES (?, ?) ON DUPLICATE KEY UPDATE value = VALUES(value)",
|
|
188
271
|
[`global_${params.slug}`, JSON.stringify(params.data)]
|
|
189
272
|
);
|
|
@@ -191,7 +274,9 @@ var MysqlAdapter = class {
|
|
|
191
274
|
}
|
|
192
275
|
/** Gracefully close the connection pool. Call on process exit. */
|
|
193
276
|
async close() {
|
|
194
|
-
|
|
277
|
+
if (this.pool) {
|
|
278
|
+
await this.pool.end();
|
|
279
|
+
}
|
|
195
280
|
}
|
|
196
281
|
};
|
|
197
282
|
var mysqlAdapter = (config) => new MysqlAdapter(config);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dyrected/db-mysql",
|
|
3
|
-
"version": "2.5.
|
|
3
|
+
"version": "2.5.7",
|
|
4
4
|
"description": "MySQL adapter for Dyrected CMS",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
},
|
|
19
19
|
"dependencies": {
|
|
20
20
|
"mysql2": "^3.22.3",
|
|
21
|
-
"@dyrected/core": "^2.5.
|
|
21
|
+
"@dyrected/core": "^2.5.7"
|
|
22
22
|
},
|
|
23
23
|
"devDependencies": {
|
|
24
24
|
"tsup": "^8.0.0",
|