@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 CHANGED
@@ -1,8 +1,11 @@
1
1
  {
2
2
  "name": "@dyrected/db-mysql",
3
- "version": "4.0.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.0"
21
+ "@dyrected/core": "^2.4.1"
19
22
  },
20
23
  "devDependencies": {
21
24
  "tsup": "^8.0.0",
@@ -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 ▲ [WARNING] The condition "types" here will never be used as it comes after both "import" and "require" [package.json]
15
-
16
- package.json:13:6:
17
-  13 │ "types": "./dist/index.d.ts"
18
- ╵ ~~~~~~~
19
-
20
- The "import" condition comes earlier and will be used for all "import" statements:
21
-
22
- package.json:11:6:
23
-  11 │ "import": "./dist/index.js",
24
- ╵ ~~~~~~~~
25
-
26
- The "require" condition comes earlier and will be used for all "require" calls:
27
-
28
- package.json:12:6:
29
-  12 │ "require": "./dist/index.cjs",
30
- ╵ ~~~~~~~~~
31
-
32
-
33
-
34
-
35
- [10:58:11 PM] WARN ▲ [WARNING] The condition "types" here will never be used as it comes after both "import" and "require" [package.json]
36
-
37
- package.json:13:6:
38
-  13 │ "types": "./dist/index.d.ts"
39
- ╵ ~~~~~~~
40
-
41
- The "import" condition comes earlier and will be used for all "import" statements:
42
-
43
- package.json:11:6:
44
-  11 │ "import": "./dist/index.js",
45
- ╵ ~~~~~~~~
46
-
47
- The "require" condition comes earlier and will be used for all "require" calls:
48
-
49
- package.json:12:6:
50
-  12 │ "require": "./dist/index.cjs",
51
- ╵ ~~~~~~~~~
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);
package/tsconfig.json DELETED
@@ -1,9 +0,0 @@
1
- {
2
- "extends": "../../tsconfig.json",
3
- "compilerOptions": {
4
- "outDir": "./dist",
5
- "rootDir": "./src"
6
- },
7
- "include": ["src/**/*"],
8
- "exclude": ["node_modules", "dist"]
9
- }