@hedystia/db 2.0.6 → 2.0.8

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.
Files changed (67) hide show
  1. package/dist/cache/index.mjs +12 -0
  2. package/dist/cache/index.mjs.map +1 -0
  3. package/dist/cache/manager.mjs +156 -153
  4. package/dist/cache/manager.mjs.map +1 -1
  5. package/dist/cache/memory-store.mjs +113 -111
  6. package/dist/cache/memory-store.mjs.map +1 -1
  7. package/dist/cli/commands/migrate.cjs +78 -0
  8. package/dist/cli/commands/migrate.cjs.map +1 -0
  9. package/dist/cli/commands/migrate.mjs +83 -0
  10. package/dist/cli/commands/migrate.mjs.map +1 -0
  11. package/dist/cli/commands/migration.cjs +3 -2
  12. package/dist/cli/commands/migration.cjs.map +1 -1
  13. package/dist/cli/commands/migration.mjs +4 -3
  14. package/dist/cli/commands/migration.mjs.map +1 -1
  15. package/dist/cli.cjs +44 -5
  16. package/dist/cli.cjs.map +1 -1
  17. package/dist/cli.mjs +45 -5
  18. package/dist/cli.mjs.map +1 -1
  19. package/dist/core/database.cjs +75 -29
  20. package/dist/core/database.cjs.map +1 -1
  21. package/dist/core/database.d.cts +11 -0
  22. package/dist/core/database.d.mts +11 -0
  23. package/dist/core/database.mjs +91 -34
  24. package/dist/core/database.mjs.map +1 -1
  25. package/dist/core/repository.mjs +414 -410
  26. package/dist/core/repository.mjs.map +1 -1
  27. package/dist/drivers/driver.mjs +9 -7
  28. package/dist/drivers/driver.mjs.map +1 -1
  29. package/dist/drivers/file.mjs +315 -312
  30. package/dist/drivers/file.mjs.map +1 -1
  31. package/dist/drivers/index.mjs +15 -6
  32. package/dist/drivers/index.mjs.map +1 -1
  33. package/dist/drivers/mysql.mjs +261 -256
  34. package/dist/drivers/mysql.mjs.map +1 -1
  35. package/dist/drivers/sql-compiler.mjs +4 -1
  36. package/dist/drivers/sql-compiler.mjs.map +1 -1
  37. package/dist/drivers/sqlite.cjs +12 -1
  38. package/dist/drivers/sqlite.cjs.map +1 -1
  39. package/dist/drivers/sqlite.mjs +258 -242
  40. package/dist/drivers/sqlite.mjs.map +1 -1
  41. package/dist/errors.mjs +48 -64
  42. package/dist/errors.mjs.map +1 -1
  43. package/dist/index.mjs +21 -9
  44. package/dist/index.mjs.map +1 -1
  45. package/dist/migrations/templates.cjs +4 -3
  46. package/dist/migrations/templates.cjs.map +1 -1
  47. package/dist/migrations/templates.d.cts +3 -2
  48. package/dist/migrations/templates.d.mts +3 -2
  49. package/dist/migrations/templates.mjs +4 -3
  50. package/dist/migrations/templates.mjs.map +1 -1
  51. package/dist/schema/column.mjs +155 -157
  52. package/dist/schema/column.mjs.map +1 -1
  53. package/dist/schema/columns/index.mjs +103 -171
  54. package/dist/schema/columns/index.mjs.map +1 -1
  55. package/dist/schema/index.mjs +15 -0
  56. package/dist/schema/index.mjs.map +1 -0
  57. package/dist/schema/registry.mjs +122 -119
  58. package/dist/schema/registry.mjs.map +1 -1
  59. package/dist/schema/table.mjs +4 -1
  60. package/dist/schema/table.mjs.map +1 -1
  61. package/dist/sync/synchronizer.mjs +2 -1
  62. package/dist/sync/synchronizer.mjs.map +1 -1
  63. package/dist/utils/naming.cjs +9 -0
  64. package/dist/utils/naming.cjs.map +1 -1
  65. package/dist/utils/naming.mjs +9 -1
  66. package/dist/utils/naming.mjs.map +1 -1
  67. package/package.json +1 -1
@@ -1,438 +1,442 @@
1
- import { QueryError } from "../errors.mjs";
2
- import { FileDriver } from "../drivers/file.mjs";
3
- import { compileBulkInsert, compileDelete, compileInsert, compileSelect, compileUpdate, compileWhere } from "../drivers/sql-compiler.mjs";
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
- * Repository implementation that provides CRUD operations for a table
7
- * @template T - The row type for this table
8
- */
9
- var TableRepository = class {
10
- tableName;
11
- driver;
12
- cache;
13
- registry;
14
- meta;
15
- columnMap;
16
- reverseColumnMap;
17
- hasAliases;
18
- tableCacheConfig;
19
- jsonColumns;
20
- jsonCodeKeys;
21
- constructor(tableName, driver, cache, registry, tableCacheConfig) {
22
- this.tableName = tableName;
23
- this.driver = driver;
24
- this.cache = cache;
25
- this.registry = registry;
26
- this.tableCacheConfig = tableCacheConfig;
27
- const meta = registry.getTable(tableName);
28
- if (!meta) throw new QueryError(`Table "${tableName}" is not registered`);
29
- this.meta = meta;
30
- this.columnMap = registry.getColumnMap(tableName);
31
- this.reverseColumnMap = registry.getReverseColumnMap(tableName);
32
- this.hasAliases = Object.entries(this.columnMap).some(([codeKey, dbName]) => codeKey !== dbName);
33
- this.jsonColumns = new Set(meta.columns.filter((c) => c.type === "json" || c.type === "array").map((c) => c.name));
34
- this.jsonCodeKeys = /* @__PURE__ */ new Set();
35
- for (const col of meta.columns) if (col.type === "json" || col.type === "array") {
36
- const codeKey = this.reverseColumnMap[col.name] ?? col.name;
37
- this.jsonCodeKeys.add(codeKey);
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
- * Find all rows matching the query options
42
- * @param {QueryOptions<T>} [options] - Query options
43
- * @returns {Promise<T[]>} Array of matching rows
44
- */
45
- async find(options) {
46
- return this.cache.getOrSetWithTableConfig(this.tableName, "find", options, async () => {
47
- const dbOptions = this.mapOptionsToDb(options);
48
- let rows;
49
- if (this.driver instanceof FileDriver) rows = this.mapRows(this.findFile(dbOptions));
50
- else rows = this.mapRows(await this.findSQL(dbOptions));
51
- if (options?.with) rows = await this.loadRelations(rows, options.with);
52
- this.cacheEntities(rows);
53
- return rows;
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
- const params = [];
104
- const sql = compileInsert(this.tableName, cleaned, params);
105
- const result = await this.driver.execute(sql, params);
106
- const pk = this.registry.getPrimaryKey(this.tableName);
107
- if (pk && result.insertId) cleaned[pk] = result.insertId;
108
- const mapped = this.toCodeKeys(cleaned);
109
- this.cacheEntity(mapped);
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
- const cleanedData = data.map((item) => this.toDbKeys(this.cleanData(item)));
126
- const params = [];
127
- const sql = compileBulkInsert(this.tableName, cleanedData, params);
128
- const result = await this.driver.execute(sql, params);
129
- const pk = this.registry.getPrimaryKey(this.tableName);
130
- if (pk && result.insertId) for (let i = 0; i < cleanedData.length; i++) {
131
- const row = cleanedData[i];
132
- if (row) row[pk] = result.insertId + i;
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
- const finalRows = this.mapRows(cleanedData);
135
- this.cacheEntities(finalRows);
136
- return finalRows;
137
- }
138
- /**
139
- * Update rows matching the where clause
140
- * @param {UpdateOptions<T>} options - Update options with where and data
141
- * @returns {Promise<T[]>} The updated rows
142
- */
143
- async update(options) {
144
- if (!options.where || Object.keys(options.where).length === 0) throw new QueryError("Update requires a where clause");
145
- this.cache.invalidateTable(this.tableName);
146
- const cleaned = this.toDbKeys(this.cleanData(options.data));
147
- const dbWhere = this.mapWhereToDb(options.where);
148
- if (this.driver instanceof FileDriver) {
149
- const filter = this.buildFileFilter(dbWhere);
150
- this.driver.updateRows(this.tableName, filter, cleaned);
151
- return this.find({ where: options.where });
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
- const params = [];
154
- const sql = compileUpdate(this.tableName, cleaned, dbWhere, params);
155
- await this.driver.execute(sql, params);
156
- return this.find({ where: options.where });
157
- }
158
- /**
159
- * Delete rows matching the where clause
160
- * @param {DeleteOptions<T>} options - Delete options with where clause
161
- * @returns {Promise<number>} Number of deleted rows
162
- */
163
- async delete(options) {
164
- if (!options.where || Object.keys(options.where).length === 0) throw new QueryError("Delete requires a where clause");
165
- this.cache.invalidateTable(this.tableName);
166
- const dbWhere = this.mapWhereToDb(options.where);
167
- if (this.driver instanceof FileDriver) {
168
- const filter = this.buildFileFilter(dbWhere);
169
- return this.driver.deleteRows(this.tableName, filter);
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
- const params = [];
172
- const sql = compileDelete(this.tableName, dbWhere, params);
173
- return (await this.driver.execute(sql, params)).affectedRows;
174
- }
175
- /**
176
- * Count rows matching the where clause
177
- * @param {Pick<QueryOptions<T>, "where">} [options] - Count options
178
- * @returns {Promise<number>} Row count
179
- */
180
- async count(options) {
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 = dbWhere ? this.buildFileFilter(dbWhere) : void 0;
185
- return this.driver.countRows(this.tableName, filter);
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
- let sql = `SELECT COUNT(*) as count FROM \`${this.tableName}\``;
189
- if (dbWhere && Object.keys(dbWhere).length > 0) sql += ` WHERE ${compileWhere(dbWhere, params)}`;
190
- return (await this.driver.query(sql, params))[0]?.count ?? 0;
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
- await this.driver.execute(`DELETE FROM \`${this.tableName}\``);
227
- }
228
- async findSQL(options) {
229
- const params = [];
230
- const sql = compileSelect(this.tableName, {
231
- select: options?.select,
232
- where: options?.where,
233
- orderBy: options?.orderBy,
234
- take: options?.take,
235
- skip: options?.skip
236
- }, params);
237
- return this.driver.query(sql, params);
238
- }
239
- findFile(options) {
240
- const fd = this.driver;
241
- const filter = options?.where ? this.buildFileFilter(options.where) : void 0;
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
- if (options?.orderBy) {
252
- const entries = Object.entries(options.orderBy);
253
- rows.sort((a, b) => {
254
- for (const [col, dir] of entries) {
255
- const av = a[col];
256
- const bv = b[col];
257
- if (av < bv) return dir === "asc" ? -1 : 1;
258
- if (av > bv) return dir === "asc" ? 1 : -1;
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
- return 0;
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
- if (options?.skip) rows = rows.slice(options.skip);
264
- if (options?.take) rows = rows.slice(0, options.take);
265
- return rows;
266
- }
267
- buildFileFilter(where) {
268
- return (row) => this.matchWhere(row, where);
269
- }
270
- matchWhere(row, where) {
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
- if (key === "AND" && Array.isArray(value)) {
277
- if (!value.every((sub) => this.matchWhere(row, sub))) return false;
278
- continue;
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 (value !== null && typeof value === "object" && !Array.isArray(value)) {
281
- const cond = value;
282
- const rowVal = row[key];
283
- if (cond.eq !== void 0 && rowVal !== cond.eq) return false;
284
- if (cond.neq !== void 0 && rowVal === cond.neq) return false;
285
- if (cond.gt !== void 0 && !(rowVal > cond.gt)) return false;
286
- if (cond.gte !== void 0 && !(rowVal >= cond.gte)) return false;
287
- if (cond.lt !== void 0 && !(rowVal < cond.lt)) return false;
288
- if (cond.lte !== void 0 && !(rowVal <= cond.lte)) return false;
289
- if (cond.like !== void 0 && !String(rowVal).match(new RegExp(cond.like.replace(/%/g, ".*"), "i"))) return false;
290
- if (cond.notLike !== void 0 && String(rowVal).match(new RegExp(cond.notLike.replace(/%/g, ".*"), "i"))) return false;
291
- if (cond.in !== void 0 && !cond.in.includes(rowVal)) return false;
292
- if (cond.notIn?.includes(rowVal)) return false;
293
- if (cond.isNull === true && rowVal !== null && rowVal !== void 0) return false;
294
- if (cond.isNull === false && (rowVal === null || rowVal === void 0)) return false;
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
- return true;
301
- }
302
- async loadRelations(rows, withOpts) {
303
- if (rows.length === 0) return rows;
304
- const relations = this.registry.getRelations(this.tableName);
305
- for (const [relationName, opts] of Object.entries(withOpts)) {
306
- if (!opts) continue;
307
- const relation = relations.find((r) => r.relationName === relationName);
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
- } else {
326
- const pk = this.registry.getPrimaryKey(this.tableName);
327
- if (!pk) continue;
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
- for (const row of rows) {
340
- const key = row[pk];
341
- row[relationName] = relatedMap.get(key) ?? [];
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
- return rows;
346
- }
347
- async findRelated(tableName, column, ids, options) {
348
- if (this.driver instanceof FileDriver) {
349
- const fd = this.driver;
350
- const filter = (row) => ids.includes(row[column]);
351
- return fd.findRows(tableName, filter);
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
- const params = [];
354
- const placeholders = ids.map(() => "?").join(", ");
355
- params.push(...ids);
356
- let sql = `SELECT * FROM \`${tableName}\` WHERE \`${column}\` IN (${placeholders})`;
357
- if (options.orderBy) {
358
- const orderParts = Object.entries(options.orderBy).map(([col, dir]) => `\`${col}\` ${dir.toUpperCase()}`);
359
- if (orderParts.length > 0) sql += ` ORDER BY ${orderParts.join(", ")}`;
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
- if (options.take) sql += ` LIMIT ${options.take}`;
362
- return this.driver.query(sql, params);
363
- }
364
- cleanData(data) {
365
- const cleaned = {};
366
- const dbColumnNames = new Set(this.meta.columns.map((c) => c.name));
367
- const codeKeys = new Set(Object.keys(this.columnMap));
368
- const shouldSerialize = !(this.driver instanceof FileDriver);
369
- 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);
370
- else cleaned[key] = value;
371
- return cleaned;
372
- }
373
- cacheEntities(rows) {
374
- const pk = this.registry.getPrimaryKey(this.tableName);
375
- if (!pk) return;
376
- for (const row of rows) if (row[pk] != null) this.cache.setEntity(this.tableName, row[pk], row);
377
- }
378
- cacheEntity(row) {
379
- const pk = this.registry.getPrimaryKey(this.tableName);
380
- if (!pk || row[pk] == null) return;
381
- this.cache.setEntity(this.tableName, row[pk], row);
382
- }
383
- toDbKeys(data) {
384
- if (!this.hasAliases) return data;
385
- const result = {};
386
- for (const [key, value] of Object.entries(data)) result[this.columnMap[key] ?? key] = value;
387
- return result;
388
- }
389
- toCodeKeys(row) {
390
- if (!this.hasAliases) return row;
391
- const result = {};
392
- for (const [key, value] of Object.entries(row)) result[this.reverseColumnMap[key] ?? key] = value;
393
- return result;
394
- }
395
- mapWhereToDb(where) {
396
- if (!this.hasAliases) return where;
397
- const result = {};
398
- for (const [key, value] of Object.entries(where)) if (key === "OR" || key === "AND") result[key] = value.map((sub) => this.mapWhereToDb(sub));
399
- else result[this.columnMap[key] ?? key] = value;
400
- return result;
401
- }
402
- mapSelectToDb(select) {
403
- if (!this.hasAliases) return select;
404
- return select.map((key) => this.columnMap[key] ?? key);
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
- export { TableRepository };
439
+ init_repository();
440
+ export { TableRepository, init_repository };
437
441
 
438
442
  //# sourceMappingURL=repository.mjs.map