@hedystia/db 2.0.6 → 2.0.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/dist/cache/index.mjs +12 -0
- package/dist/cache/index.mjs.map +1 -0
- package/dist/cache/manager.mjs +156 -153
- package/dist/cache/manager.mjs.map +1 -1
- package/dist/cache/memory-store.mjs +113 -111
- package/dist/cache/memory-store.mjs.map +1 -1
- package/dist/cli/commands/migrate.cjs +78 -0
- package/dist/cli/commands/migrate.cjs.map +1 -0
- package/dist/cli/commands/migrate.mjs +83 -0
- package/dist/cli/commands/migrate.mjs.map +1 -0
- package/dist/cli.cjs +36 -0
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.mjs +37 -0
- package/dist/cli.mjs.map +1 -1
- package/dist/core/database.cjs +72 -29
- package/dist/core/database.cjs.map +1 -1
- package/dist/core/database.d.cts +11 -0
- package/dist/core/database.d.mts +11 -0
- package/dist/core/database.mjs +88 -34
- package/dist/core/database.mjs.map +1 -1
- package/dist/core/repository.mjs +414 -410
- package/dist/core/repository.mjs.map +1 -1
- package/dist/drivers/driver.mjs +9 -7
- package/dist/drivers/driver.mjs.map +1 -1
- package/dist/drivers/file.mjs +315 -312
- package/dist/drivers/file.mjs.map +1 -1
- package/dist/drivers/index.mjs +15 -6
- package/dist/drivers/index.mjs.map +1 -1
- package/dist/drivers/mysql.mjs +261 -256
- package/dist/drivers/mysql.mjs.map +1 -1
- package/dist/drivers/sql-compiler.mjs +4 -1
- package/dist/drivers/sql-compiler.mjs.map +1 -1
- package/dist/drivers/sqlite.cjs +12 -1
- package/dist/drivers/sqlite.cjs.map +1 -1
- package/dist/drivers/sqlite.mjs +258 -242
- package/dist/drivers/sqlite.mjs.map +1 -1
- package/dist/errors.mjs +48 -64
- package/dist/errors.mjs.map +1 -1
- package/dist/index.mjs +21 -9
- package/dist/index.mjs.map +1 -1
- package/dist/schema/column.mjs +155 -157
- package/dist/schema/column.mjs.map +1 -1
- package/dist/schema/columns/index.mjs +103 -171
- package/dist/schema/columns/index.mjs.map +1 -1
- package/dist/schema/index.mjs +15 -0
- package/dist/schema/index.mjs.map +1 -0
- package/dist/schema/registry.mjs +122 -119
- package/dist/schema/registry.mjs.map +1 -1
- package/dist/schema/table.mjs +4 -1
- package/dist/schema/table.mjs.map +1 -1
- package/dist/sync/synchronizer.mjs +2 -1
- package/dist/sync/synchronizer.mjs.map +1 -1
- package/package.json +1 -1
package/dist/core/repository.mjs
CHANGED
|
@@ -1,438 +1,442 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import { __esmMin } from "../_virtual/_rolldown/runtime.mjs";
|
|
2
|
+
import { QueryError, init_errors } from "../errors.mjs";
|
|
3
|
+
import { FileDriver, init_file } from "../drivers/file.mjs";
|
|
4
|
+
import { compileBulkInsert, compileDelete, compileInsert, compileSelect, compileUpdate, compileWhere, init_sql_compiler } from "../drivers/sql-compiler.mjs";
|
|
4
5
|
//#region src/core/repository.ts
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
const
|
|
37
|
-
|
|
6
|
+
var TableRepository;
|
|
7
|
+
var init_repository = __esmMin((() => {
|
|
8
|
+
init_file();
|
|
9
|
+
init_sql_compiler();
|
|
10
|
+
init_errors();
|
|
11
|
+
TableRepository = class {
|
|
12
|
+
tableName;
|
|
13
|
+
driver;
|
|
14
|
+
cache;
|
|
15
|
+
registry;
|
|
16
|
+
meta;
|
|
17
|
+
columnMap;
|
|
18
|
+
reverseColumnMap;
|
|
19
|
+
hasAliases;
|
|
20
|
+
tableCacheConfig;
|
|
21
|
+
jsonColumns;
|
|
22
|
+
jsonCodeKeys;
|
|
23
|
+
constructor(tableName, driver, cache, registry, tableCacheConfig) {
|
|
24
|
+
this.tableName = tableName;
|
|
25
|
+
this.driver = driver;
|
|
26
|
+
this.cache = cache;
|
|
27
|
+
this.registry = registry;
|
|
28
|
+
this.tableCacheConfig = tableCacheConfig;
|
|
29
|
+
const meta = registry.getTable(tableName);
|
|
30
|
+
if (!meta) throw new QueryError(`Table "${tableName}" is not registered`);
|
|
31
|
+
this.meta = meta;
|
|
32
|
+
this.columnMap = registry.getColumnMap(tableName);
|
|
33
|
+
this.reverseColumnMap = registry.getReverseColumnMap(tableName);
|
|
34
|
+
this.hasAliases = Object.entries(this.columnMap).some(([codeKey, dbName]) => codeKey !== dbName);
|
|
35
|
+
this.jsonColumns = new Set(meta.columns.filter((c) => c.type === "json" || c.type === "array").map((c) => c.name));
|
|
36
|
+
this.jsonCodeKeys = /* @__PURE__ */ new Set();
|
|
37
|
+
for (const col of meta.columns) if (col.type === "json" || col.type === "array") {
|
|
38
|
+
const codeKey = this.reverseColumnMap[col.name] ?? col.name;
|
|
39
|
+
this.jsonCodeKeys.add(codeKey);
|
|
40
|
+
}
|
|
38
41
|
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
}, this.tableCacheConfig);
|
|
55
|
-
}
|
|
56
|
-
/**
|
|
57
|
-
* Find all rows matching the query options (alias for find)
|
|
58
|
-
* @param {QueryOptions<T>} [options] - Query options
|
|
59
|
-
* @returns {Promise<T[]>} Array of matching rows
|
|
60
|
-
*/
|
|
61
|
-
async findMany(options) {
|
|
62
|
-
return this.find(options);
|
|
63
|
-
}
|
|
64
|
-
/**
|
|
65
|
-
* Find the first row matching the query options
|
|
66
|
-
* @param {QueryOptions<T>} [options] - Query options
|
|
67
|
-
* @returns {Promise<T | null>} The first matching row or null
|
|
68
|
-
*/
|
|
69
|
-
async findFirst(options) {
|
|
70
|
-
return this.cache.getOrSetWithTableConfig(this.tableName, "findFirst", options, async () => {
|
|
71
|
-
const opts = {
|
|
72
|
-
...this.mapOptionsToDb(options),
|
|
73
|
-
take: 1
|
|
74
|
-
};
|
|
75
|
-
let rows;
|
|
76
|
-
if (this.driver instanceof FileDriver) rows = this.mapRows(this.findFile(opts));
|
|
77
|
-
else rows = this.mapRows(await this.findSQL(opts));
|
|
78
|
-
if (rows.length === 0) return null;
|
|
79
|
-
if (options?.with) rows = await this.loadRelations(rows, options.with);
|
|
80
|
-
const row = rows[0] ?? null;
|
|
81
|
-
if (row) this.cacheEntity(row);
|
|
82
|
-
return row;
|
|
83
|
-
}, this.tableCacheConfig);
|
|
84
|
-
}
|
|
85
|
-
/**
|
|
86
|
-
* Insert one or more rows
|
|
87
|
-
* @param {Partial<T> | Partial<T>[]} data - Data to insert
|
|
88
|
-
* @returns {Promise<T>} The inserted row
|
|
89
|
-
*/
|
|
90
|
-
async insert(data) {
|
|
91
|
-
const single = Array.isArray(data) ? data[0] : data;
|
|
92
|
-
if (!single) throw new QueryError("Insert data cannot be empty");
|
|
93
|
-
this.cache.invalidateTable(this.tableName);
|
|
94
|
-
const cleaned = this.toDbKeys(this.cleanData(single));
|
|
95
|
-
if (this.driver instanceof FileDriver) {
|
|
96
|
-
const id = this.driver.insertRow(this.tableName, cleaned);
|
|
97
|
-
const pk = this.registry.getPrimaryKey(this.tableName);
|
|
98
|
-
if (pk) cleaned[pk] = id;
|
|
99
|
-
const result = this.toCodeKeys(cleaned);
|
|
100
|
-
this.cacheEntity(result);
|
|
101
|
-
return result;
|
|
42
|
+
/**
|
|
43
|
+
* Find all rows matching the query options
|
|
44
|
+
* @param {QueryOptions<T>} [options] - Query options
|
|
45
|
+
* @returns {Promise<T[]>} Array of matching rows
|
|
46
|
+
*/
|
|
47
|
+
async find(options) {
|
|
48
|
+
return this.cache.getOrSetWithTableConfig(this.tableName, "find", options, async () => {
|
|
49
|
+
const dbOptions = this.mapOptionsToDb(options);
|
|
50
|
+
let rows;
|
|
51
|
+
if (this.driver instanceof FileDriver) rows = this.mapRows(this.findFile(dbOptions));
|
|
52
|
+
else rows = this.mapRows(await this.findSQL(dbOptions));
|
|
53
|
+
if (options?.with) rows = await this.loadRelations(rows, options.with);
|
|
54
|
+
this.cacheEntities(rows);
|
|
55
|
+
return rows;
|
|
56
|
+
}, this.tableCacheConfig);
|
|
102
57
|
}
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
return mapped;
|
|
111
|
-
}
|
|
112
|
-
/**
|
|
113
|
-
* Insert multiple rows
|
|
114
|
-
* @param {Partial<T>[]} data - Array of data to insert
|
|
115
|
-
* @returns {Promise<T[]>} The inserted rows
|
|
116
|
-
*/
|
|
117
|
-
async insertMany(data) {
|
|
118
|
-
if (data.length === 0) return [];
|
|
119
|
-
this.cache.invalidateTable(this.tableName);
|
|
120
|
-
if (this.driver instanceof FileDriver) {
|
|
121
|
-
const results = [];
|
|
122
|
-
for (const item of data) results.push(await this.insert(item));
|
|
123
|
-
return results;
|
|
58
|
+
/**
|
|
59
|
+
* Find all rows matching the query options (alias for find)
|
|
60
|
+
* @param {QueryOptions<T>} [options] - Query options
|
|
61
|
+
* @returns {Promise<T[]>} Array of matching rows
|
|
62
|
+
*/
|
|
63
|
+
async findMany(options) {
|
|
64
|
+
return this.find(options);
|
|
124
65
|
}
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
66
|
+
/**
|
|
67
|
+
* Find the first row matching the query options
|
|
68
|
+
* @param {QueryOptions<T>} [options] - Query options
|
|
69
|
+
* @returns {Promise<T | null>} The first matching row or null
|
|
70
|
+
*/
|
|
71
|
+
async findFirst(options) {
|
|
72
|
+
return this.cache.getOrSetWithTableConfig(this.tableName, "findFirst", options, async () => {
|
|
73
|
+
const opts = {
|
|
74
|
+
...this.mapOptionsToDb(options),
|
|
75
|
+
take: 1
|
|
76
|
+
};
|
|
77
|
+
let rows;
|
|
78
|
+
if (this.driver instanceof FileDriver) rows = this.mapRows(this.findFile(opts));
|
|
79
|
+
else rows = this.mapRows(await this.findSQL(opts));
|
|
80
|
+
if (rows.length === 0) return null;
|
|
81
|
+
if (options?.with) rows = await this.loadRelations(rows, options.with);
|
|
82
|
+
const row = rows[0] ?? null;
|
|
83
|
+
if (row) this.cacheEntity(row);
|
|
84
|
+
return row;
|
|
85
|
+
}, this.tableCacheConfig);
|
|
133
86
|
}
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
87
|
+
/**
|
|
88
|
+
* Insert one or more rows
|
|
89
|
+
* @param {Partial<T> | Partial<T>[]} data - Data to insert
|
|
90
|
+
* @returns {Promise<T>} The inserted row
|
|
91
|
+
*/
|
|
92
|
+
async insert(data) {
|
|
93
|
+
const single = Array.isArray(data) ? data[0] : data;
|
|
94
|
+
if (!single) throw new QueryError("Insert data cannot be empty");
|
|
95
|
+
this.cache.invalidateTable(this.tableName);
|
|
96
|
+
const cleaned = this.toDbKeys(this.cleanData(single));
|
|
97
|
+
if (this.driver instanceof FileDriver) {
|
|
98
|
+
const id = this.driver.insertRow(this.tableName, cleaned);
|
|
99
|
+
const pk = this.registry.getPrimaryKey(this.tableName);
|
|
100
|
+
if (pk) cleaned[pk] = id;
|
|
101
|
+
const result = this.toCodeKeys(cleaned);
|
|
102
|
+
this.cacheEntity(result);
|
|
103
|
+
return result;
|
|
104
|
+
}
|
|
105
|
+
const params = [];
|
|
106
|
+
const sql = compileInsert(this.tableName, cleaned, params);
|
|
107
|
+
const result = await this.driver.execute(sql, params);
|
|
108
|
+
const pk = this.registry.getPrimaryKey(this.tableName);
|
|
109
|
+
if (pk && result.insertId) cleaned[pk] = result.insertId;
|
|
110
|
+
const mapped = this.toCodeKeys(cleaned);
|
|
111
|
+
this.cacheEntity(mapped);
|
|
112
|
+
return mapped;
|
|
152
113
|
}
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
const
|
|
169
|
-
|
|
114
|
+
/**
|
|
115
|
+
* Insert multiple rows
|
|
116
|
+
* @param {Partial<T>[]} data - Array of data to insert
|
|
117
|
+
* @returns {Promise<T[]>} The inserted rows
|
|
118
|
+
*/
|
|
119
|
+
async insertMany(data) {
|
|
120
|
+
if (data.length === 0) return [];
|
|
121
|
+
this.cache.invalidateTable(this.tableName);
|
|
122
|
+
if (this.driver instanceof FileDriver) {
|
|
123
|
+
const results = [];
|
|
124
|
+
for (const item of data) results.push(await this.insert(item));
|
|
125
|
+
return results;
|
|
126
|
+
}
|
|
127
|
+
const cleanedData = data.map((item) => this.toDbKeys(this.cleanData(item)));
|
|
128
|
+
const params = [];
|
|
129
|
+
const sql = compileBulkInsert(this.tableName, cleanedData, params);
|
|
130
|
+
const result = await this.driver.execute(sql, params);
|
|
131
|
+
const pk = this.registry.getPrimaryKey(this.tableName);
|
|
132
|
+
if (pk && result.insertId) for (let i = 0; i < cleanedData.length; i++) {
|
|
133
|
+
const row = cleanedData[i];
|
|
134
|
+
if (row) row[pk] = result.insertId + i;
|
|
135
|
+
}
|
|
136
|
+
const finalRows = this.mapRows(cleanedData);
|
|
137
|
+
this.cacheEntities(finalRows);
|
|
138
|
+
return finalRows;
|
|
170
139
|
}
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
return this.cache.getOrSetWithTableConfig(this.tableName, "count", options, async () => {
|
|
182
|
-
const dbWhere = options?.where ? this.mapWhereToDb(options.where) : void 0;
|
|
140
|
+
/**
|
|
141
|
+
* Update rows matching the where clause
|
|
142
|
+
* @param {UpdateOptions<T>} options - Update options with where and data
|
|
143
|
+
* @returns {Promise<T[]>} The updated rows
|
|
144
|
+
*/
|
|
145
|
+
async update(options) {
|
|
146
|
+
if (!options.where || Object.keys(options.where).length === 0) throw new QueryError("Update requires a where clause");
|
|
147
|
+
this.cache.invalidateTable(this.tableName);
|
|
148
|
+
const cleaned = this.toDbKeys(this.cleanData(options.data));
|
|
149
|
+
const dbWhere = this.mapWhereToDb(options.where);
|
|
183
150
|
if (this.driver instanceof FileDriver) {
|
|
184
|
-
const filter =
|
|
185
|
-
|
|
151
|
+
const filter = this.buildFileFilter(dbWhere);
|
|
152
|
+
this.driver.updateRows(this.tableName, filter, cleaned);
|
|
153
|
+
return this.find({ where: options.where });
|
|
186
154
|
}
|
|
187
155
|
const params = [];
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
return
|
|
191
|
-
}, this.tableCacheConfig);
|
|
192
|
-
}
|
|
193
|
-
/**
|
|
194
|
-
* Check if any row exists matching the where clause
|
|
195
|
-
* @param {Pick<QueryOptions<T>, "where">} options - Exists options
|
|
196
|
-
* @returns {Promise<boolean>} Whether a matching row exists
|
|
197
|
-
*/
|
|
198
|
-
async exists(options) {
|
|
199
|
-
return await this.count(options) > 0;
|
|
200
|
-
}
|
|
201
|
-
/**
|
|
202
|
-
* Insert or update a row based on the where clause
|
|
203
|
-
* @param {object} options - Upsert options
|
|
204
|
-
* @param {WhereClause<T>} options.where - Condition to check
|
|
205
|
-
* @param {Partial<T>} options.create - Data to insert if not found
|
|
206
|
-
* @param {Partial<T>} options.update - Data to update if found
|
|
207
|
-
* @returns {Promise<T>} The upserted row
|
|
208
|
-
*/
|
|
209
|
-
async upsert(options) {
|
|
210
|
-
const existing = await this.findFirst({ where: options.where });
|
|
211
|
-
if (existing) return (await this.update({
|
|
212
|
-
where: options.where,
|
|
213
|
-
data: options.update
|
|
214
|
-
}))[0] ?? existing;
|
|
215
|
-
return this.insert(options.create);
|
|
216
|
-
}
|
|
217
|
-
/**
|
|
218
|
-
* Remove all rows from the table
|
|
219
|
-
*/
|
|
220
|
-
async truncate() {
|
|
221
|
-
this.cache.invalidateTable(this.tableName);
|
|
222
|
-
if (this.driver instanceof FileDriver) {
|
|
223
|
-
this.driver.truncateTable(this.tableName);
|
|
224
|
-
return;
|
|
156
|
+
const sql = compileUpdate(this.tableName, cleaned, dbWhere, params);
|
|
157
|
+
await this.driver.execute(sql, params);
|
|
158
|
+
return this.find({ where: options.where });
|
|
225
159
|
}
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
where
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
let rows = fd.findRows(this.tableName, filter);
|
|
243
|
-
if (options?.select) {
|
|
244
|
-
const selectSet = new Set(options.select);
|
|
245
|
-
rows = rows.map((row) => {
|
|
246
|
-
const filtered = {};
|
|
247
|
-
for (const key of selectSet) filtered[key] = row[key];
|
|
248
|
-
return filtered;
|
|
249
|
-
});
|
|
160
|
+
/**
|
|
161
|
+
* Delete rows matching the where clause
|
|
162
|
+
* @param {DeleteOptions<T>} options - Delete options with where clause
|
|
163
|
+
* @returns {Promise<number>} Number of deleted rows
|
|
164
|
+
*/
|
|
165
|
+
async delete(options) {
|
|
166
|
+
if (!options.where || Object.keys(options.where).length === 0) throw new QueryError("Delete requires a where clause");
|
|
167
|
+
this.cache.invalidateTable(this.tableName);
|
|
168
|
+
const dbWhere = this.mapWhereToDb(options.where);
|
|
169
|
+
if (this.driver instanceof FileDriver) {
|
|
170
|
+
const filter = this.buildFileFilter(dbWhere);
|
|
171
|
+
return this.driver.deleteRows(this.tableName, filter);
|
|
172
|
+
}
|
|
173
|
+
const params = [];
|
|
174
|
+
const sql = compileDelete(this.tableName, dbWhere, params);
|
|
175
|
+
return (await this.driver.execute(sql, params)).affectedRows;
|
|
250
176
|
}
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
177
|
+
/**
|
|
178
|
+
* Count rows matching the where clause
|
|
179
|
+
* @param {Pick<QueryOptions<T>, "where">} [options] - Count options
|
|
180
|
+
* @returns {Promise<number>} Row count
|
|
181
|
+
*/
|
|
182
|
+
async count(options) {
|
|
183
|
+
return this.cache.getOrSetWithTableConfig(this.tableName, "count", options, async () => {
|
|
184
|
+
const dbWhere = options?.where ? this.mapWhereToDb(options.where) : void 0;
|
|
185
|
+
if (this.driver instanceof FileDriver) {
|
|
186
|
+
const filter = dbWhere ? this.buildFileFilter(dbWhere) : void 0;
|
|
187
|
+
return this.driver.countRows(this.tableName, filter);
|
|
259
188
|
}
|
|
260
|
-
|
|
261
|
-
|
|
189
|
+
const params = [];
|
|
190
|
+
let sql = `SELECT COUNT(*) as count FROM \`${this.tableName}\``;
|
|
191
|
+
if (dbWhere && Object.keys(dbWhere).length > 0) sql += ` WHERE ${compileWhere(dbWhere, params)}`;
|
|
192
|
+
return (await this.driver.query(sql, params))[0]?.count ?? 0;
|
|
193
|
+
}, this.tableCacheConfig);
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Check if any row exists matching the where clause
|
|
197
|
+
* @param {Pick<QueryOptions<T>, "where">} options - Exists options
|
|
198
|
+
* @returns {Promise<boolean>} Whether a matching row exists
|
|
199
|
+
*/
|
|
200
|
+
async exists(options) {
|
|
201
|
+
return await this.count(options) > 0;
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Insert or update a row based on the where clause
|
|
205
|
+
* @param {object} options - Upsert options
|
|
206
|
+
* @param {WhereClause<T>} options.where - Condition to check
|
|
207
|
+
* @param {Partial<T>} options.create - Data to insert if not found
|
|
208
|
+
* @param {Partial<T>} options.update - Data to update if found
|
|
209
|
+
* @returns {Promise<T>} The upserted row
|
|
210
|
+
*/
|
|
211
|
+
async upsert(options) {
|
|
212
|
+
const existing = await this.findFirst({ where: options.where });
|
|
213
|
+
if (existing) return (await this.update({
|
|
214
|
+
where: options.where,
|
|
215
|
+
data: options.update
|
|
216
|
+
}))[0] ?? existing;
|
|
217
|
+
return this.insert(options.create);
|
|
262
218
|
}
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
for (const [key, value] of Object.entries(where)) {
|
|
272
|
-
if (key === "OR" && Array.isArray(value)) {
|
|
273
|
-
if (!value.some((sub) => this.matchWhere(row, sub))) return false;
|
|
274
|
-
continue;
|
|
219
|
+
/**
|
|
220
|
+
* Remove all rows from the table
|
|
221
|
+
*/
|
|
222
|
+
async truncate() {
|
|
223
|
+
this.cache.invalidateTable(this.tableName);
|
|
224
|
+
if (this.driver instanceof FileDriver) {
|
|
225
|
+
this.driver.truncateTable(this.tableName);
|
|
226
|
+
return;
|
|
275
227
|
}
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
228
|
+
await this.driver.execute(`DELETE FROM \`${this.tableName}\``);
|
|
229
|
+
}
|
|
230
|
+
async findSQL(options) {
|
|
231
|
+
const params = [];
|
|
232
|
+
const sql = compileSelect(this.tableName, {
|
|
233
|
+
select: options?.select,
|
|
234
|
+
where: options?.where,
|
|
235
|
+
orderBy: options?.orderBy,
|
|
236
|
+
take: options?.take,
|
|
237
|
+
skip: options?.skip
|
|
238
|
+
}, params);
|
|
239
|
+
return this.driver.query(sql, params);
|
|
240
|
+
}
|
|
241
|
+
findFile(options) {
|
|
242
|
+
const fd = this.driver;
|
|
243
|
+
const filter = options?.where ? this.buildFileFilter(options.where) : void 0;
|
|
244
|
+
let rows = fd.findRows(this.tableName, filter);
|
|
245
|
+
if (options?.select) {
|
|
246
|
+
const selectSet = new Set(options.select);
|
|
247
|
+
rows = rows.map((row) => {
|
|
248
|
+
const filtered = {};
|
|
249
|
+
for (const key of selectSet) filtered[key] = row[key];
|
|
250
|
+
return filtered;
|
|
251
|
+
});
|
|
279
252
|
}
|
|
280
|
-
if (
|
|
281
|
-
const
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
if (cond.between !== void 0) {
|
|
296
|
-
if (rowVal < cond.between[0] || rowVal > cond.between[1]) return false;
|
|
297
|
-
}
|
|
298
|
-
} else if (row[key] !== value) return false;
|
|
253
|
+
if (options?.orderBy) {
|
|
254
|
+
const entries = Object.entries(options.orderBy);
|
|
255
|
+
rows.sort((a, b) => {
|
|
256
|
+
for (const [col, dir] of entries) {
|
|
257
|
+
const av = a[col];
|
|
258
|
+
const bv = b[col];
|
|
259
|
+
if (av < bv) return dir === "asc" ? -1 : 1;
|
|
260
|
+
if (av > bv) return dir === "asc" ? 1 : -1;
|
|
261
|
+
}
|
|
262
|
+
return 0;
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
if (options?.skip) rows = rows.slice(options.skip);
|
|
266
|
+
if (options?.take) rows = rows.slice(0, options.take);
|
|
267
|
+
return rows;
|
|
299
268
|
}
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
if (!relation) continue;
|
|
309
|
-
if (relation.from.table === this.tableName) {
|
|
310
|
-
const ids = rows.map((r) => r[relation.from.column]).filter((v) => v != null);
|
|
311
|
-
if (ids.length === 0) continue;
|
|
312
|
-
const uniqueIds = [...new Set(ids)];
|
|
313
|
-
const relatedOpts = typeof opts === "object" ? opts : {};
|
|
314
|
-
const related = await this.findRelated(relation.to.table, relation.to.column, uniqueIds, relatedOpts);
|
|
315
|
-
const relatedMap = /* @__PURE__ */ new Map();
|
|
316
|
-
for (const r of related) {
|
|
317
|
-
const key = r[relation.to.column];
|
|
318
|
-
if (!relatedMap.has(key)) relatedMap.set(key, []);
|
|
319
|
-
relatedMap.get(key).push(r);
|
|
320
|
-
}
|
|
321
|
-
for (const row of rows) {
|
|
322
|
-
const key = row[relation.from.column];
|
|
323
|
-
row[relationName] = relatedMap.get(key) ?? [];
|
|
269
|
+
buildFileFilter(where) {
|
|
270
|
+
return (row) => this.matchWhere(row, where);
|
|
271
|
+
}
|
|
272
|
+
matchWhere(row, where) {
|
|
273
|
+
for (const [key, value] of Object.entries(where)) {
|
|
274
|
+
if (key === "OR" && Array.isArray(value)) {
|
|
275
|
+
if (!value.some((sub) => this.matchWhere(row, sub))) return false;
|
|
276
|
+
continue;
|
|
324
277
|
}
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
const ids = rows.map((r) => r[pk]).filter((v) => v != null);
|
|
329
|
-
if (ids.length === 0) continue;
|
|
330
|
-
const uniqueIds = [...new Set(ids)];
|
|
331
|
-
const relatedOpts = typeof opts === "object" ? opts : {};
|
|
332
|
-
const related = await this.findRelated(relation.to.table, relation.to.column, uniqueIds, relatedOpts);
|
|
333
|
-
const relatedMap = /* @__PURE__ */ new Map();
|
|
334
|
-
for (const r of related) {
|
|
335
|
-
const key = r[relation.to.column];
|
|
336
|
-
if (!relatedMap.has(key)) relatedMap.set(key, []);
|
|
337
|
-
relatedMap.get(key).push(r);
|
|
278
|
+
if (key === "AND" && Array.isArray(value)) {
|
|
279
|
+
if (!value.every((sub) => this.matchWhere(row, sub))) return false;
|
|
280
|
+
continue;
|
|
338
281
|
}
|
|
339
|
-
|
|
340
|
-
const
|
|
341
|
-
|
|
282
|
+
if (value !== null && typeof value === "object" && !Array.isArray(value)) {
|
|
283
|
+
const cond = value;
|
|
284
|
+
const rowVal = row[key];
|
|
285
|
+
if (cond.eq !== void 0 && rowVal !== cond.eq) return false;
|
|
286
|
+
if (cond.neq !== void 0 && rowVal === cond.neq) return false;
|
|
287
|
+
if (cond.gt !== void 0 && !(rowVal > cond.gt)) return false;
|
|
288
|
+
if (cond.gte !== void 0 && !(rowVal >= cond.gte)) return false;
|
|
289
|
+
if (cond.lt !== void 0 && !(rowVal < cond.lt)) return false;
|
|
290
|
+
if (cond.lte !== void 0 && !(rowVal <= cond.lte)) return false;
|
|
291
|
+
if (cond.like !== void 0 && !String(rowVal).match(new RegExp(cond.like.replace(/%/g, ".*"), "i"))) return false;
|
|
292
|
+
if (cond.notLike !== void 0 && String(rowVal).match(new RegExp(cond.notLike.replace(/%/g, ".*"), "i"))) return false;
|
|
293
|
+
if (cond.in !== void 0 && !cond.in.includes(rowVal)) return false;
|
|
294
|
+
if (cond.notIn?.includes(rowVal)) return false;
|
|
295
|
+
if (cond.isNull === true && rowVal !== null && rowVal !== void 0) return false;
|
|
296
|
+
if (cond.isNull === false && (rowVal === null || rowVal === void 0)) return false;
|
|
297
|
+
if (cond.between !== void 0) {
|
|
298
|
+
if (rowVal < cond.between[0] || rowVal > cond.between[1]) return false;
|
|
299
|
+
}
|
|
300
|
+
} else if (row[key] !== value) return false;
|
|
301
|
+
}
|
|
302
|
+
return true;
|
|
303
|
+
}
|
|
304
|
+
async loadRelations(rows, withOpts) {
|
|
305
|
+
if (rows.length === 0) return rows;
|
|
306
|
+
const relations = this.registry.getRelations(this.tableName);
|
|
307
|
+
for (const [relationName, opts] of Object.entries(withOpts)) {
|
|
308
|
+
if (!opts) continue;
|
|
309
|
+
const relation = relations.find((r) => r.relationName === relationName);
|
|
310
|
+
if (!relation) continue;
|
|
311
|
+
if (relation.from.table === this.tableName) {
|
|
312
|
+
const ids = rows.map((r) => r[relation.from.column]).filter((v) => v != null);
|
|
313
|
+
if (ids.length === 0) continue;
|
|
314
|
+
const uniqueIds = [...new Set(ids)];
|
|
315
|
+
const relatedOpts = typeof opts === "object" ? opts : {};
|
|
316
|
+
const related = await this.findRelated(relation.to.table, relation.to.column, uniqueIds, relatedOpts);
|
|
317
|
+
const relatedMap = /* @__PURE__ */ new Map();
|
|
318
|
+
for (const r of related) {
|
|
319
|
+
const key = r[relation.to.column];
|
|
320
|
+
if (!relatedMap.has(key)) relatedMap.set(key, []);
|
|
321
|
+
relatedMap.get(key).push(r);
|
|
322
|
+
}
|
|
323
|
+
for (const row of rows) {
|
|
324
|
+
const key = row[relation.from.column];
|
|
325
|
+
row[relationName] = relatedMap.get(key) ?? [];
|
|
326
|
+
}
|
|
327
|
+
} else {
|
|
328
|
+
const pk = this.registry.getPrimaryKey(this.tableName);
|
|
329
|
+
if (!pk) continue;
|
|
330
|
+
const ids = rows.map((r) => r[pk]).filter((v) => v != null);
|
|
331
|
+
if (ids.length === 0) continue;
|
|
332
|
+
const uniqueIds = [...new Set(ids)];
|
|
333
|
+
const relatedOpts = typeof opts === "object" ? opts : {};
|
|
334
|
+
const related = await this.findRelated(relation.to.table, relation.to.column, uniqueIds, relatedOpts);
|
|
335
|
+
const relatedMap = /* @__PURE__ */ new Map();
|
|
336
|
+
for (const r of related) {
|
|
337
|
+
const key = r[relation.to.column];
|
|
338
|
+
if (!relatedMap.has(key)) relatedMap.set(key, []);
|
|
339
|
+
relatedMap.get(key).push(r);
|
|
340
|
+
}
|
|
341
|
+
for (const row of rows) {
|
|
342
|
+
const key = row[pk];
|
|
343
|
+
row[relationName] = relatedMap.get(key) ?? [];
|
|
344
|
+
}
|
|
342
345
|
}
|
|
343
346
|
}
|
|
347
|
+
return rows;
|
|
344
348
|
}
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
349
|
+
async findRelated(tableName, column, ids, options) {
|
|
350
|
+
if (this.driver instanceof FileDriver) {
|
|
351
|
+
const fd = this.driver;
|
|
352
|
+
const filter = (row) => ids.includes(row[column]);
|
|
353
|
+
return fd.findRows(tableName, filter);
|
|
354
|
+
}
|
|
355
|
+
const params = [];
|
|
356
|
+
const placeholders = ids.map(() => "?").join(", ");
|
|
357
|
+
params.push(...ids);
|
|
358
|
+
let sql = `SELECT * FROM \`${tableName}\` WHERE \`${column}\` IN (${placeholders})`;
|
|
359
|
+
if (options.orderBy) {
|
|
360
|
+
const orderParts = Object.entries(options.orderBy).map(([col, dir]) => `\`${col}\` ${dir.toUpperCase()}`);
|
|
361
|
+
if (orderParts.length > 0) sql += ` ORDER BY ${orderParts.join(", ")}`;
|
|
362
|
+
}
|
|
363
|
+
if (options.take) sql += ` LIMIT ${options.take}`;
|
|
364
|
+
return this.driver.query(sql, params);
|
|
352
365
|
}
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
const
|
|
359
|
-
|
|
366
|
+
cleanData(data) {
|
|
367
|
+
const cleaned = {};
|
|
368
|
+
const dbColumnNames = new Set(this.meta.columns.map((c) => c.name));
|
|
369
|
+
const codeKeys = new Set(Object.keys(this.columnMap));
|
|
370
|
+
const shouldSerialize = !(this.driver instanceof FileDriver);
|
|
371
|
+
for (const [key, value] of Object.entries(data)) if (codeKeys.has(key) || dbColumnNames.has(key)) if (shouldSerialize && (this.jsonCodeKeys.has(key) || this.jsonColumns.has(key)) && value != null && typeof value === "object") cleaned[key] = JSON.stringify(value);
|
|
372
|
+
else cleaned[key] = value;
|
|
373
|
+
return cleaned;
|
|
360
374
|
}
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
mapOrderByToDb(orderBy) {
|
|
407
|
-
if (!this.hasAliases) return orderBy;
|
|
408
|
-
const result = {};
|
|
409
|
-
for (const [key, value] of Object.entries(orderBy)) result[this.columnMap[key] ?? key] = value;
|
|
410
|
-
return result;
|
|
411
|
-
}
|
|
412
|
-
mapOptionsToDb(options) {
|
|
413
|
-
if (!options || !this.hasAliases) return options;
|
|
414
|
-
const mapped = { ...options };
|
|
415
|
-
if (options.where) mapped.where = this.mapWhereToDb(options.where);
|
|
416
|
-
if (options.select) mapped.select = this.mapSelectToDb(options.select);
|
|
417
|
-
if (options.orderBy) mapped.orderBy = this.mapOrderByToDb(options.orderBy);
|
|
418
|
-
return mapped;
|
|
419
|
-
}
|
|
420
|
-
mapRows(rows) {
|
|
421
|
-
const shouldDeserialize = this.jsonCodeKeys.size > 0 && !(this.driver instanceof FileDriver);
|
|
422
|
-
if (!this.hasAliases && !shouldDeserialize) return rows;
|
|
423
|
-
return rows.map((row) => {
|
|
424
|
-
const mapped = this.hasAliases ? this.toCodeKeys(row) : { ...row };
|
|
425
|
-
if (shouldDeserialize) for (const key of this.jsonCodeKeys) {
|
|
426
|
-
const val = mapped[key];
|
|
427
|
-
if (typeof val === "string") try {
|
|
428
|
-
mapped[key] = JSON.parse(val);
|
|
429
|
-
} catch {}
|
|
430
|
-
}
|
|
375
|
+
cacheEntities(rows) {
|
|
376
|
+
const pk = this.registry.getPrimaryKey(this.tableName);
|
|
377
|
+
if (!pk) return;
|
|
378
|
+
for (const row of rows) if (row[pk] != null) this.cache.setEntity(this.tableName, row[pk], row);
|
|
379
|
+
}
|
|
380
|
+
cacheEntity(row) {
|
|
381
|
+
const pk = this.registry.getPrimaryKey(this.tableName);
|
|
382
|
+
if (!pk || row[pk] == null) return;
|
|
383
|
+
this.cache.setEntity(this.tableName, row[pk], row);
|
|
384
|
+
}
|
|
385
|
+
toDbKeys(data) {
|
|
386
|
+
if (!this.hasAliases) return data;
|
|
387
|
+
const result = {};
|
|
388
|
+
for (const [key, value] of Object.entries(data)) result[this.columnMap[key] ?? key] = value;
|
|
389
|
+
return result;
|
|
390
|
+
}
|
|
391
|
+
toCodeKeys(row) {
|
|
392
|
+
if (!this.hasAliases) return row;
|
|
393
|
+
const result = {};
|
|
394
|
+
for (const [key, value] of Object.entries(row)) result[this.reverseColumnMap[key] ?? key] = value;
|
|
395
|
+
return result;
|
|
396
|
+
}
|
|
397
|
+
mapWhereToDb(where) {
|
|
398
|
+
if (!this.hasAliases) return where;
|
|
399
|
+
const result = {};
|
|
400
|
+
for (const [key, value] of Object.entries(where)) if (key === "OR" || key === "AND") result[key] = value.map((sub) => this.mapWhereToDb(sub));
|
|
401
|
+
else result[this.columnMap[key] ?? key] = value;
|
|
402
|
+
return result;
|
|
403
|
+
}
|
|
404
|
+
mapSelectToDb(select) {
|
|
405
|
+
if (!this.hasAliases) return select;
|
|
406
|
+
return select.map((key) => this.columnMap[key] ?? key);
|
|
407
|
+
}
|
|
408
|
+
mapOrderByToDb(orderBy) {
|
|
409
|
+
if (!this.hasAliases) return orderBy;
|
|
410
|
+
const result = {};
|
|
411
|
+
for (const [key, value] of Object.entries(orderBy)) result[this.columnMap[key] ?? key] = value;
|
|
412
|
+
return result;
|
|
413
|
+
}
|
|
414
|
+
mapOptionsToDb(options) {
|
|
415
|
+
if (!options || !this.hasAliases) return options;
|
|
416
|
+
const mapped = { ...options };
|
|
417
|
+
if (options.where) mapped.where = this.mapWhereToDb(options.where);
|
|
418
|
+
if (options.select) mapped.select = this.mapSelectToDb(options.select);
|
|
419
|
+
if (options.orderBy) mapped.orderBy = this.mapOrderByToDb(options.orderBy);
|
|
431
420
|
return mapped;
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
|
|
421
|
+
}
|
|
422
|
+
mapRows(rows) {
|
|
423
|
+
const shouldDeserialize = this.jsonCodeKeys.size > 0 && !(this.driver instanceof FileDriver);
|
|
424
|
+
if (!this.hasAliases && !shouldDeserialize) return rows;
|
|
425
|
+
return rows.map((row) => {
|
|
426
|
+
const mapped = this.hasAliases ? this.toCodeKeys(row) : { ...row };
|
|
427
|
+
if (shouldDeserialize) for (const key of this.jsonCodeKeys) {
|
|
428
|
+
const val = mapped[key];
|
|
429
|
+
if (typeof val === "string") try {
|
|
430
|
+
mapped[key] = JSON.parse(val);
|
|
431
|
+
} catch {}
|
|
432
|
+
}
|
|
433
|
+
return mapped;
|
|
434
|
+
});
|
|
435
|
+
}
|
|
436
|
+
};
|
|
437
|
+
}));
|
|
435
438
|
//#endregion
|
|
436
|
-
|
|
439
|
+
init_repository();
|
|
440
|
+
export { TableRepository, init_repository };
|
|
437
441
|
|
|
438
442
|
//# sourceMappingURL=repository.mjs.map
|