@dyrected/db-mysql 4.0.0 â 4.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.
- package/package.json +5 -2
- package/.turbo/turbo-build.log +0 -62
- package/CHANGELOG.md +0 -28
- package/src/index.ts +0 -261
- package/tsconfig.json +0 -9
package/package.json
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dyrected/db-mysql",
|
|
3
|
-
"version": "4.0.
|
|
3
|
+
"version": "4.0.2",
|
|
4
4
|
"description": "MySQL adapter for Dyrected CMS",
|
|
5
5
|
"type": "module",
|
|
6
|
+
"files": [
|
|
7
|
+
"dist"
|
|
8
|
+
],
|
|
6
9
|
"main": "./dist/index.cjs",
|
|
7
10
|
"module": "./dist/index.js",
|
|
8
11
|
"types": "./dist/index.d.ts",
|
|
@@ -15,7 +18,7 @@
|
|
|
15
18
|
},
|
|
16
19
|
"dependencies": {
|
|
17
20
|
"mysql2": "^3.22.3",
|
|
18
|
-
"@dyrected/core": "^2.4.
|
|
21
|
+
"@dyrected/core": "^2.4.1"
|
|
19
22
|
},
|
|
20
23
|
"devDependencies": {
|
|
21
24
|
"tsup": "^8.0.0",
|
package/.turbo/turbo-build.log
DELETED
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
> @dyrected/db-mysql@4.0.0 build /Users/busola/Work/dyrected/packages/db-mysql
|
|
4
|
-
> tsup src/index.ts --format esm,cjs --dts --clean
|
|
5
|
-
|
|
6
|
-
CLI Building entry: src/index.ts
|
|
7
|
-
CLI Using tsconfig: tsconfig.json
|
|
8
|
-
CLI tsup v8.5.1
|
|
9
|
-
CLI Target: esnext
|
|
10
|
-
CLI Cleaning output folder
|
|
11
|
-
ESM Build start
|
|
12
|
-
CJS Build start
|
|
13
|
-
|
|
14
|
-
[10:58:11 PM] WARN [33mâ² [43;33m[[43;30mWARNING[43;33m][0m [1mThe condition "types" here will never be used as it comes after both "import" and "require"[0m [package.json]
|
|
15
|
-
|
|
16
|
-
package.json:13:6:
|
|
17
|
-
[37m 13 â [32m"types"[37m: "./dist/index.d.ts"
|
|
18
|
-
âµ [32m~~~~~~~[0m
|
|
19
|
-
|
|
20
|
-
The "import" condition comes earlier and will be used for all "import" statements:
|
|
21
|
-
|
|
22
|
-
package.json:11:6:
|
|
23
|
-
[37m 11 â [32m"import"[37m: "./dist/index.js",
|
|
24
|
-
âµ [32m~~~~~~~~[0m
|
|
25
|
-
|
|
26
|
-
The "require" condition comes earlier and will be used for all "require" calls:
|
|
27
|
-
|
|
28
|
-
package.json:12:6:
|
|
29
|
-
[37m 12 â [32m"require"[37m: "./dist/index.cjs",
|
|
30
|
-
âµ [32m~~~~~~~~~[0m
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
[10:58:11 PM] WARN [33mâ² [43;33m[[43;30mWARNING[43;33m][0m [1mThe condition "types" here will never be used as it comes after both "import" and "require"[0m [package.json]
|
|
36
|
-
|
|
37
|
-
package.json:13:6:
|
|
38
|
-
[37m 13 â [32m"types"[37m: "./dist/index.d.ts"
|
|
39
|
-
âµ [32m~~~~~~~[0m
|
|
40
|
-
|
|
41
|
-
The "import" condition comes earlier and will be used for all "import" statements:
|
|
42
|
-
|
|
43
|
-
package.json:11:6:
|
|
44
|
-
[37m 11 â [32m"import"[37m: "./dist/index.js",
|
|
45
|
-
âµ [32m~~~~~~~~[0m
|
|
46
|
-
|
|
47
|
-
The "require" condition comes earlier and will be used for all "require" calls:
|
|
48
|
-
|
|
49
|
-
package.json:12:6:
|
|
50
|
-
[37m 12 â [32m"require"[37m: "./dist/index.cjs",
|
|
51
|
-
âµ [32m~~~~~~~~~[0m
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
CJS dist/index.cjs 9.32 KB
|
|
56
|
-
CJS âĦï¸ Build success in 518ms
|
|
57
|
-
ESM dist/index.js 7.65 KB
|
|
58
|
-
ESM âĦï¸ Build success in 519ms
|
|
59
|
-
DTS Build start
|
|
60
|
-
DTS âĦï¸ Build success in 17150ms
|
|
61
|
-
DTS dist/index.d.ts 1.51 KB
|
|
62
|
-
DTS dist/index.d.cts 1.51 KB
|
package/CHANGELOG.md
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
# @dyrected/db-mysql
|
|
2
|
-
|
|
3
|
-
## 4.0.0
|
|
4
|
-
|
|
5
|
-
### Minor Changes
|
|
6
|
-
|
|
7
|
-
- Infrastructure standardization, MySQL adapter improvements, and SDK robustness testing.
|
|
8
|
-
|
|
9
|
-
### Patch Changes
|
|
10
|
-
|
|
11
|
-
- Updated dependencies
|
|
12
|
-
- @dyrected/core@2.4.0
|
|
13
|
-
|
|
14
|
-
## 3.0.0
|
|
15
|
-
|
|
16
|
-
### Minor Changes
|
|
17
|
-
|
|
18
|
-
- Standardize database infrastructure and implement field promotion.
|
|
19
|
-
- **Field Promotion**: Added 'promoted' option to Collection fields to extract JSON data into native SQL columns for indexing and performance.
|
|
20
|
-
- **Lazy Migrations**: Added 'renameTo' support for seamless field renames without breaking existing data.
|
|
21
|
-
- **Auto-Seeding**: Standardized 'initialData' seeding logic across all adapters.
|
|
22
|
-
- **MySQL Adapter**: New robust MySQL adapter implementation.
|
|
23
|
-
- **Strict Filtering**: Improved query translation parity across all SQL-based adapters.
|
|
24
|
-
|
|
25
|
-
### Patch Changes
|
|
26
|
-
|
|
27
|
-
- Updated dependencies
|
|
28
|
-
- @dyrected/core@2.3.0
|
package/src/index.ts
DELETED
|
@@ -1,261 +0,0 @@
|
|
|
1
|
-
import { DatabaseAdapter, PaginatedResult, parseSqlWhere } from "@dyrected/core";
|
|
2
|
-
import mysql from "mysql2/promise";
|
|
3
|
-
|
|
4
|
-
export interface MysqlAdapterConfig {
|
|
5
|
-
/** Full MySQL connection URL: mysql://user:pass@host:3306/dbname */
|
|
6
|
-
url?: string;
|
|
7
|
-
/** Alternative: individual connection options */
|
|
8
|
-
host?: string;
|
|
9
|
-
port?: number;
|
|
10
|
-
user?: string;
|
|
11
|
-
password?: string;
|
|
12
|
-
database?: string;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export class MysqlAdapter implements DatabaseAdapter {
|
|
16
|
-
private pool: any;
|
|
17
|
-
|
|
18
|
-
constructor(config: MysqlAdapterConfig) {
|
|
19
|
-
if (config.url) {
|
|
20
|
-
this.pool = mysql.createPool(config.url);
|
|
21
|
-
} else {
|
|
22
|
-
this.pool = mysql.createPool({
|
|
23
|
-
host: config.host ?? "localhost",
|
|
24
|
-
port: config.port ?? 3306,
|
|
25
|
-
user: config.user,
|
|
26
|
-
password: config.password,
|
|
27
|
-
database: config.database,
|
|
28
|
-
// Return dates as strings for consistency with other adapters
|
|
29
|
-
dateStrings: true,
|
|
30
|
-
});
|
|
31
|
-
}
|
|
32
|
-
// Fire-and-forget: create internal table on startup
|
|
33
|
-
this.initInternalTables().catch((err) => console.error("[dyrected/db-mysql] Failed to init internal tables:", err));
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
private async initInternalTables() {
|
|
37
|
-
await this.pool.query(`
|
|
38
|
-
CREATE TABLE IF NOT EXISTS dyrected_internal (
|
|
39
|
-
\`key\` VARCHAR(255) PRIMARY KEY,
|
|
40
|
-
value JSON NOT NULL
|
|
41
|
-
)
|
|
42
|
-
`);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
private getTableName(slug: string): string {
|
|
46
|
-
return `collection_${slug}`;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
private async ensureTable(slug: string, fields: any[] = []) {
|
|
50
|
-
const tableName = this.getTableName(slug);
|
|
51
|
-
await this.pool.query(`
|
|
52
|
-
CREATE TABLE IF NOT EXISTS \`${tableName}\` (
|
|
53
|
-
id VARCHAR(36) PRIMARY KEY,
|
|
54
|
-
data JSON NOT NULL,
|
|
55
|
-
created_at DATETIME(3) DEFAULT CURRENT_TIMESTAMP(3),
|
|
56
|
-
updated_at DATETIME(3) DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)
|
|
57
|
-
)
|
|
58
|
-
`);
|
|
59
|
-
|
|
60
|
-
// Inspect columns for promoted fields
|
|
61
|
-
const [cols] = await this.pool.query(`SHOW COLUMNS FROM \`${tableName}\``);
|
|
62
|
-
const existingCols = cols.map((c: any) => c.Field);
|
|
63
|
-
|
|
64
|
-
for (const field of fields) {
|
|
65
|
-
if (field.promoted && !existingCols.includes(field.name)) {
|
|
66
|
-
console.log(`[dyrected/mysql] Promoting field "${field.name}" to column in ${tableName}`);
|
|
67
|
-
let sqlType = "TEXT";
|
|
68
|
-
if (field.type === "number") sqlType = "DECIMAL(19,4)";
|
|
69
|
-
if (field.type === "boolean") sqlType = "TINYINT(1)";
|
|
70
|
-
|
|
71
|
-
await this.pool.query(`ALTER TABLE \`${tableName}\` ADD COLUMN \`${field.name}\` ${sqlType}`);
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
async find(args: {
|
|
77
|
-
collection: string;
|
|
78
|
-
where?: any;
|
|
79
|
-
limit?: number;
|
|
80
|
-
page?: number;
|
|
81
|
-
sort?: string;
|
|
82
|
-
}): Promise<PaginatedResult> {
|
|
83
|
-
await this.ensureTable(args.collection);
|
|
84
|
-
const tableName = this.getTableName(args.collection);
|
|
85
|
-
|
|
86
|
-
const limit = args.limit ?? 10;
|
|
87
|
-
const page = args.page ?? 1;
|
|
88
|
-
const offset = (page - 1) * limit;
|
|
89
|
-
|
|
90
|
-
// Inspect columns for promoted fields
|
|
91
|
-
const [cols] = await this.pool.query(`SHOW COLUMNS FROM \`${tableName}\``);
|
|
92
|
-
const existingCols = cols.map((c: any) => c.Field);
|
|
93
|
-
|
|
94
|
-
// Build WHERE clause via shared DSL translator (MySQL JSON path syntax)
|
|
95
|
-
let whereSql = "";
|
|
96
|
-
let whereParams: any[] = [];
|
|
97
|
-
if (args.where && Object.keys(args.where).length > 0) {
|
|
98
|
-
const result = parseSqlWhere(
|
|
99
|
-
args.where,
|
|
100
|
-
(field: string) => {
|
|
101
|
-
if (existingCols.includes(field) && !["id", "data"].includes(field)) {
|
|
102
|
-
return `\`${field}\``;
|
|
103
|
-
}
|
|
104
|
-
return `JSON_UNQUOTE(JSON_EXTRACT(data, '$.${field}'))`;
|
|
105
|
-
},
|
|
106
|
-
"?",
|
|
107
|
-
);
|
|
108
|
-
whereSql = `WHERE ${result.sql}`;
|
|
109
|
-
whereParams = result.params;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
// Normalize camelCase sort fields â snake_case columns
|
|
113
|
-
const rawSort = args.sort ?? "created_at DESC";
|
|
114
|
-
const sort = rawSort.replace(/\bcreatedAt\b/g, "created_at").replace(/\bupdatedAt\b/g, "updated_at");
|
|
115
|
-
|
|
116
|
-
// Count with filter applied for accurate pagination
|
|
117
|
-
const [countRows] = await this.pool.query(
|
|
118
|
-
`SELECT COUNT(*) AS total FROM \`${tableName}\` ${whereSql}`,
|
|
119
|
-
whereParams,
|
|
120
|
-
);
|
|
121
|
-
const total = Number(countRows[0].total);
|
|
122
|
-
|
|
123
|
-
// Fetch page of data
|
|
124
|
-
const [rows] = await this.pool.query(
|
|
125
|
-
`SELECT * FROM \`${tableName}\` ${whereSql} ORDER BY ${sort} LIMIT ? OFFSET ?`,
|
|
126
|
-
[...whereParams, limit, offset],
|
|
127
|
-
);
|
|
128
|
-
|
|
129
|
-
const docs = rows.map((r: any) => ({
|
|
130
|
-
id: r.id,
|
|
131
|
-
...JSON.parse(typeof r.data === "string" ? r.data : JSON.stringify(r.data)),
|
|
132
|
-
createdAt: r.created_at,
|
|
133
|
-
updatedAt: r.updated_at,
|
|
134
|
-
}));
|
|
135
|
-
|
|
136
|
-
const totalPages = Math.ceil(total / limit);
|
|
137
|
-
return {
|
|
138
|
-
docs,
|
|
139
|
-
total,
|
|
140
|
-
limit,
|
|
141
|
-
page,
|
|
142
|
-
totalPages,
|
|
143
|
-
hasNextPage: page < totalPages,
|
|
144
|
-
hasPrevPage: page > 1,
|
|
145
|
-
};
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
async findOne(params: { collection: string; id: string }) {
|
|
149
|
-
await this.ensureTable(params.collection);
|
|
150
|
-
const tableName = this.getTableName(params.collection);
|
|
151
|
-
const [rows] = await this.pool.query(`SELECT * FROM \`${tableName}\` WHERE id = ?`, [params.id]);
|
|
152
|
-
const row = rows[0];
|
|
153
|
-
if (!row) return null;
|
|
154
|
-
return {
|
|
155
|
-
id: row.id,
|
|
156
|
-
...JSON.parse(typeof row.data === "string" ? row.data : JSON.stringify(row.data)),
|
|
157
|
-
createdAt: row.created_at,
|
|
158
|
-
updatedAt: row.updated_at,
|
|
159
|
-
};
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
async create(params: { collection: string; data: any }) {
|
|
163
|
-
await this.ensureTable(params.collection);
|
|
164
|
-
const tableName = this.getTableName(params.collection);
|
|
165
|
-
|
|
166
|
-
// Inspect columns for promoted fields
|
|
167
|
-
const [cols] = await this.pool.query(`SHOW COLUMNS FROM \`${tableName}\``);
|
|
168
|
-
const existingCols = cols.map((c: any) => c.Field);
|
|
169
|
-
|
|
170
|
-
const id = params.data.id ?? Math.random().toString(36).substring(7);
|
|
171
|
-
const now = new Date().toISOString().replace("T", " ").replace("Z", "");
|
|
172
|
-
|
|
173
|
-
const data = { ...params.data };
|
|
174
|
-
delete data.id;
|
|
175
|
-
delete data.createdAt;
|
|
176
|
-
delete data.updatedAt;
|
|
177
|
-
|
|
178
|
-
// Extract promoted fields
|
|
179
|
-
const promotedValues: Record<string, any> = {};
|
|
180
|
-
for (const col of existingCols) {
|
|
181
|
-
if (["id", "data", "created_at", "updated_at"].includes(col)) continue;
|
|
182
|
-
if (data[col] !== undefined) {
|
|
183
|
-
promotedValues[col] = data[col];
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
const colNames = ["id", "data", "created_at", "updated_at", ...Object.keys(promotedValues).map((k) => `\`${k}\``)];
|
|
188
|
-
const placeholders = colNames.map(() => "?").join(", ");
|
|
189
|
-
const values = [id, JSON.stringify(data), now, now, ...Object.values(promotedValues)];
|
|
190
|
-
|
|
191
|
-
await this.pool.query(`INSERT INTO \`${tableName}\` (${colNames.join(", ")}) VALUES (${placeholders})`, values);
|
|
192
|
-
return { id, ...data, createdAt: now, updatedAt: now };
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
async update(params: { collection: string; id: string; data: any }) {
|
|
196
|
-
await this.ensureTable(params.collection);
|
|
197
|
-
const tableName = this.getTableName(params.collection);
|
|
198
|
-
|
|
199
|
-
// Inspect columns for promoted fields
|
|
200
|
-
const [cols] = await this.pool.query(`SHOW COLUMNS FROM \`${tableName}\``);
|
|
201
|
-
const existingCols = cols.map((c: any) => c.Field);
|
|
202
|
-
|
|
203
|
-
const existing = await this.findOne({ collection: params.collection, id: params.id });
|
|
204
|
-
const now = new Date().toISOString().replace("T", " ").replace("Z", "");
|
|
205
|
-
const merged = { ...(existing ?? {}), ...params.data };
|
|
206
|
-
delete (merged as any).id;
|
|
207
|
-
delete (merged as any).createdAt;
|
|
208
|
-
delete (merged as any).updatedAt;
|
|
209
|
-
|
|
210
|
-
// Extract promoted fields
|
|
211
|
-
const promotedValues: Record<string, any> = {};
|
|
212
|
-
for (const col of existingCols) {
|
|
213
|
-
if (["id", "data", "created_at", "updated_at"].includes(col)) continue;
|
|
214
|
-
if (merged[col] !== undefined) {
|
|
215
|
-
promotedValues[col] = merged[col];
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
const setClauses = ["data = ?", "updated_at = ?", ...Object.keys(promotedValues).map((k) => `\`${k}\` = ?`)];
|
|
220
|
-
const values = [JSON.stringify(merged), now, ...Object.values(promotedValues), params.id];
|
|
221
|
-
|
|
222
|
-
await this.pool.query(`UPDATE \`${tableName}\` SET ${setClauses.join(", ")} WHERE id = ?`, values);
|
|
223
|
-
return { id: params.id, ...merged, createdAt: existing?.createdAt, updatedAt: now };
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
async sync(collections: any[]) {
|
|
227
|
-
for (const col of collections) {
|
|
228
|
-
await this.ensureTable(col.slug, col.fields);
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
async delete(params: { collection: string; id: string }) {
|
|
233
|
-
await this.ensureTable(params.collection);
|
|
234
|
-
const tableName = this.getTableName(params.collection);
|
|
235
|
-
await this.pool.query(`DELETE FROM \`${tableName}\` WHERE id = ?`, [params.id]);
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
async getGlobal(params: { slug: string }) {
|
|
239
|
-
const [rows] = await this.pool.query("SELECT value FROM dyrected_internal WHERE `key` = ?", [
|
|
240
|
-
`global_${params.slug}`,
|
|
241
|
-
]);
|
|
242
|
-
const row = rows[0];
|
|
243
|
-
if (!row) return {};
|
|
244
|
-
return typeof row.value === "string" ? JSON.parse(row.value) : row.value;
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
async updateGlobal(params: { slug: string; data: any }) {
|
|
248
|
-
await this.pool.execute(
|
|
249
|
-
"INSERT INTO dyrected_internal (`key`, value) VALUES (?, ?) ON DUPLICATE KEY UPDATE value = VALUES(value)",
|
|
250
|
-
[`global_${params.slug}`, JSON.stringify(params.data)],
|
|
251
|
-
);
|
|
252
|
-
return params.data;
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
/** Gracefully close the connection pool. Call on process exit. */
|
|
256
|
-
async close() {
|
|
257
|
-
await this.pool.end();
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
export const mysqlAdapter = (config: MysqlAdapterConfig) => new MysqlAdapter(config);
|