canxjs 1.2.2 → 1.2.3

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/index.js CHANGED
@@ -27164,6 +27164,7 @@ class TestResponse {
27164
27164
  // src/mvc/Model.ts
27165
27165
  var mysqlPool = null;
27166
27166
  var pgPool = null;
27167
+ var sqliteDb = null;
27167
27168
  var currentDriver = "mysql";
27168
27169
  var dbConfig = null;
27169
27170
  async function initDatabase(config) {
@@ -27194,6 +27195,10 @@ async function initDatabase(config) {
27194
27195
  idleTimeoutMillis: config.pool?.idle || 30000
27195
27196
  });
27196
27197
  console.log("[CanxJS] PostgreSQL connection pool created");
27198
+ } else if (config.driver === "sqlite") {
27199
+ const { Database } = await import("bun:sqlite");
27200
+ sqliteDb = new Database(config.database);
27201
+ console.log("[CanxJS] SQLite database connected");
27197
27202
  }
27198
27203
  }
27199
27204
  async function closeDatabase() {
@@ -27205,6 +27210,13 @@ async function closeDatabase() {
27205
27210
  await pgPool.end();
27206
27211
  pgPool = null;
27207
27212
  }
27213
+ if (sqliteDb) {
27214
+ sqliteDb.close();
27215
+ sqliteDb = null;
27216
+ }
27217
+ }
27218
+ function getCurrentDriver() {
27219
+ return currentDriver;
27208
27220
  }
27209
27221
  async function query(sql, params = []) {
27210
27222
  if (dbConfig?.logging)
@@ -27217,6 +27229,9 @@ async function query(sql, params = []) {
27217
27229
  const pgSql = sql.replace(/\?/g, () => `$${++idx}`);
27218
27230
  const result = await pgPool.query(pgSql, params);
27219
27231
  return result.rows;
27232
+ } else if (currentDriver === "sqlite" && sqliteDb) {
27233
+ const query2 = sqliteDb.query(sql);
27234
+ return query2.all(...params);
27220
27235
  }
27221
27236
  throw new Error("No database connection");
27222
27237
  }
@@ -27231,6 +27246,10 @@ async function execute(sql, params = []) {
27231
27246
  const pgSql = sql.replace(/\?/g, () => `$${++idx}`);
27232
27247
  const result = await pgPool.query(pgSql + " RETURNING *", params);
27233
27248
  return { affectedRows: result.rowCount || 0, insertId: result.rows[0]?.id || 0 };
27249
+ } else if (currentDriver === "sqlite" && sqliteDb) {
27250
+ const query2 = sqliteDb.query(sql);
27251
+ const result = query2.run(...params);
27252
+ return { affectedRows: result.changes, insertId: result.lastInsertRowid };
27234
27253
  }
27235
27254
  throw new Error("No database connection");
27236
27255
  }
@@ -27246,8 +27265,12 @@ class QueryBuilderImpl {
27246
27265
  groupClauses = [];
27247
27266
  havingClauses = [];
27248
27267
  bindings = [];
27249
- constructor(table) {
27268
+ modelClass;
27269
+ withRelations = [];
27270
+ _relationInfo;
27271
+ constructor(table, modelClass) {
27250
27272
  this.table = table;
27273
+ this.modelClass = modelClass;
27251
27274
  }
27252
27275
  select(...cols) {
27253
27276
  this.selectCols = cols.length ? cols : ["*"];
@@ -27259,6 +27282,10 @@ class QueryBuilderImpl {
27259
27282
  return this;
27260
27283
  }
27261
27284
  whereIn(col, vals) {
27285
+ if (vals.length === 0) {
27286
+ this.whereClauses.push("1 = 0");
27287
+ return this;
27288
+ }
27262
27289
  this.whereClauses.push(`${String(col)} IN (${vals.map(() => "?").join(",")})`);
27263
27290
  this.bindings.push(...vals);
27264
27291
  return this;
@@ -27310,6 +27337,10 @@ class QueryBuilderImpl {
27310
27337
  this.bindings.push(val);
27311
27338
  return this;
27312
27339
  }
27340
+ with(...relations) {
27341
+ this.withRelations.push(...relations);
27342
+ return this;
27343
+ }
27313
27344
  buildSelect() {
27314
27345
  let sql = `SELECT ${this.selectCols.join(", ")} FROM ${this.table}`;
27315
27346
  if (this.joinClauses.length)
@@ -27329,26 +27360,105 @@ class QueryBuilderImpl {
27329
27360
  return sql;
27330
27361
  }
27331
27362
  async get() {
27332
- return query(this.buildSelect(), this.bindings);
27363
+ const rows = await query(this.buildSelect(), this.bindings);
27364
+ let results = rows;
27365
+ if (this.modelClass) {
27366
+ results = rows.map((r) => new this.modelClass().fill(r));
27367
+ }
27368
+ if (this.withRelations.length > 0 && this.modelClass && results.length > 0) {
27369
+ await this.eagerLoad(results);
27370
+ }
27371
+ return results;
27372
+ }
27373
+ async eagerLoad(results) {
27374
+ const instance = new this.modelClass;
27375
+ for (const relationName of this.withRelations) {
27376
+ if (typeof instance[relationName] !== "function") {
27377
+ console.warn(`[Model] Relation method '${relationName}' not found on ${this.modelClass.name}`);
27378
+ continue;
27379
+ }
27380
+ const relationQuery = instance[relationName]();
27381
+ const info = relationQuery._relationInfo;
27382
+ if (!info) {
27383
+ console.warn(`[Model] Method '${relationName}' did not return a valid relation QueryBuilder`);
27384
+ continue;
27385
+ }
27386
+ if (info.type === "hasMany" || info.type === "hasOne") {
27387
+ const localKey = info.localKey || "id";
27388
+ const foreignKey = info.foreignKey;
27389
+ const parentIds = results.map((r) => r[localKey]).filter((id) => id !== undefined && id !== null);
27390
+ if (parentIds.length === 0)
27391
+ continue;
27392
+ const uniqueIds = [...new Set(parentIds)];
27393
+ const relatedResults = await info.relatedClass.query().whereIn(foreignKey, uniqueIds).get();
27394
+ for (const parent of results) {
27395
+ const parentId = parent[localKey];
27396
+ if (info.type === "hasMany") {
27397
+ parent.startRelation(relationName);
27398
+ parent.relations[relationName] = relatedResults.filter((r) => r[foreignKey] == parentId);
27399
+ } else {
27400
+ parent.startRelation(relationName);
27401
+ parent.relations[relationName] = relatedResults.find((r) => r[foreignKey] == parentId) || null;
27402
+ }
27403
+ }
27404
+ } else if (info.type === "belongsTo") {
27405
+ const foreignKey = info.foreignKey;
27406
+ const ownerKey = info.ownerKey || "id";
27407
+ const relatedIds = results.map((r) => r[foreignKey]).filter((id) => id !== undefined && id !== null);
27408
+ if (relatedIds.length === 0)
27409
+ continue;
27410
+ const uniqueIds = [...new Set(relatedIds)];
27411
+ const relatedResults = await info.relatedClass.query().whereIn(ownerKey, uniqueIds).get();
27412
+ for (const parent of results) {
27413
+ const relatedId = parent[foreignKey];
27414
+ parent.startRelation(relationName);
27415
+ parent.relations[relationName] = relatedResults.find((r) => r[ownerKey] == relatedId) || null;
27416
+ }
27417
+ } else if (info.type === "belongsToMany") {
27418
+ const localKey = "id";
27419
+ const parentIds = results.map((r) => r[localKey]).filter((id) => id !== undefined && id !== null);
27420
+ if (parentIds.length === 0)
27421
+ continue;
27422
+ const uniqueIds = [...new Set(parentIds)];
27423
+ const pivotSql = `SELECT * FROM ${info.pivotTable} WHERE ${info.foreignPivotKey} IN (${uniqueIds.map(() => "?").join(",")})`;
27424
+ const pivotRows = await query(pivotSql, uniqueIds);
27425
+ if (pivotRows.length === 0) {
27426
+ for (const parent of results) {
27427
+ parent.startRelation(relationName);
27428
+ parent.relations[relationName] = [];
27429
+ }
27430
+ continue;
27431
+ }
27432
+ const relatedIds = pivotRows.map((r) => r[info.relatedPivotKey]);
27433
+ const uniqueRelatedIds = [...new Set(relatedIds)];
27434
+ const relatedResults = await info.relatedClass.query().whereIn("id", uniqueRelatedIds).get();
27435
+ for (const parent of results) {
27436
+ parent.startRelation(relationName);
27437
+ const myPivotRows = pivotRows.filter((r) => r[info.foreignPivotKey] == parent[localKey]);
27438
+ const myRelatedIds = myPivotRows.map((r) => r[info.relatedPivotKey]);
27439
+ parent.relations[relationName] = relatedResults.filter((r) => myRelatedIds.includes(r.id));
27440
+ }
27441
+ }
27442
+ }
27333
27443
  }
27334
27444
  async first() {
27335
27445
  this.limitVal = 1;
27336
- const r = await this.get();
27337
- return r[0] || null;
27446
+ const rows = await this.get();
27447
+ return rows[0] || null;
27338
27448
  }
27339
27449
  async count() {
27340
27450
  this.selectCols = ["COUNT(*) as count"];
27341
- const r = await this.get();
27451
+ const r = await query(this.buildSelect(), this.bindings);
27342
27452
  return r[0]?.count || 0;
27343
27453
  }
27344
27454
  async sum(col) {
27345
27455
  this.selectCols = [`SUM(${String(col)}) as sum`];
27346
- const r = await this.get();
27456
+ const r = await query(this.buildSelect(), this.bindings);
27347
27457
  return r[0]?.sum || 0;
27348
27458
  }
27349
27459
  async avg(col) {
27350
27460
  this.selectCols = [`AVG(${String(col)}) as avg`];
27351
- const r = await this.get();
27461
+ const r = await query(this.buildSelect(), this.bindings);
27352
27462
  return r[0]?.avg || 0;
27353
27463
  }
27354
27464
  async insert(data) {
@@ -27358,6 +27468,9 @@ class QueryBuilderImpl {
27358
27468
  const placeholders = values.map(() => `(${keys.map(() => "?").join(",")})`).join(",");
27359
27469
  const sql = `INSERT INTO ${this.table} (${keys.join(",")}) VALUES ${placeholders}`;
27360
27470
  const result = await execute(sql, values.flat());
27471
+ if (this.modelClass) {
27472
+ return new this.modelClass().fill({ ...items[0], id: result.insertId });
27473
+ }
27361
27474
  return { ...items[0], id: result.insertId };
27362
27475
  }
27363
27476
  async update(data) {
@@ -27386,14 +27499,39 @@ class Model {
27386
27499
  static tableName;
27387
27500
  static primaryKey = "id";
27388
27501
  static timestamps = true;
27502
+ relations = {};
27503
+ constructor(data) {
27504
+ if (data)
27505
+ Object.assign(this, data);
27506
+ }
27389
27507
  static table() {
27390
- return new QueryBuilderImpl(this.tableName);
27508
+ return new QueryBuilderImpl(this.tableName, this);
27391
27509
  }
27392
27510
  static async find(id) {
27393
- return new QueryBuilderImpl(this.tableName).where(this.primaryKey, "=", id).first();
27511
+ return this.table().where(this.primaryKey, "=", id).first();
27512
+ }
27513
+ static query() {
27514
+ return new QueryBuilderImpl(this.tableName, this);
27394
27515
  }
27395
27516
  static async all() {
27396
- return new QueryBuilderImpl(this.tableName).get();
27517
+ return this.query().get();
27518
+ }
27519
+ startRelation(name) {
27520
+ if (!this.relations)
27521
+ this.relations = {};
27522
+ }
27523
+ async load(...relations) {
27524
+ const builder = new QueryBuilderImpl(this.constructor.tableName, this.constructor);
27525
+ builder.with(...relations);
27526
+ await builder.eagerLoad([this]);
27527
+ return this;
27528
+ }
27529
+ static with(...relations) {
27530
+ return this.query().with(...relations);
27531
+ }
27532
+ fill(data) {
27533
+ Object.assign(this, data);
27534
+ return this;
27397
27535
  }
27398
27536
  static async create(data) {
27399
27537
  if (this.timestamps) {
@@ -27401,7 +27539,7 @@ class Model {
27401
27539
  data.created_at = now;
27402
27540
  data.updated_at = now;
27403
27541
  }
27404
- return new QueryBuilderImpl(this.tableName).insert(data);
27542
+ return this.query().insert(data);
27405
27543
  }
27406
27544
  static async updateById(id, data) {
27407
27545
  if (this.timestamps) {
@@ -27412,8 +27550,49 @@ class Model {
27412
27550
  static async deleteById(id) {
27413
27551
  return new QueryBuilderImpl(this.tableName).where(this.primaryKey, "=", id).delete();
27414
27552
  }
27415
- static query() {
27416
- return new QueryBuilderImpl(this.tableName);
27553
+ hasOne(related, foreignKey, localKey = "id") {
27554
+ const fk = foreignKey || `${this.constructor.name.toLowerCase()}_id`;
27555
+ const pk = this[localKey];
27556
+ const qb = related.query();
27557
+ const info = { type: "hasOne", relatedClass: related, foreignKey: fk, localKey };
27558
+ qb._relationInfo = info;
27559
+ const promise = qb.where(fk, "=", pk).first();
27560
+ promise._relationInfo = info;
27561
+ return promise;
27562
+ }
27563
+ hasMany(related, foreignKey, localKey = "id") {
27564
+ const fk = foreignKey || `${this.constructor.name.toLowerCase()}_id`;
27565
+ const pk = this[localKey];
27566
+ const qb = related.query().where(fk, "=", pk);
27567
+ const info = { type: "hasMany", relatedClass: related, foreignKey: fk, localKey };
27568
+ qb._relationInfo = info;
27569
+ return qb;
27570
+ }
27571
+ belongsTo(related, foreignKey, ownerKey = "id") {
27572
+ const fk = foreignKey || `${related.name.toLowerCase()}_id`;
27573
+ const val = this[fk];
27574
+ const qb = related.query();
27575
+ const info = { type: "belongsTo", relatedClass: related, foreignKey: fk, ownerKey };
27576
+ qb._relationInfo = info;
27577
+ const promise = qb.where(ownerKey, "=", val).first();
27578
+ promise._relationInfo = info;
27579
+ return promise;
27580
+ }
27581
+ belongsToMany(related, pivotTable, foreignPivotKey, relatedPivotKey) {
27582
+ const foreignKey = foreignPivotKey || `${this.constructor.name.toLowerCase()}_id`;
27583
+ const relatedKey = relatedPivotKey || `${related.name.toLowerCase()}_id`;
27584
+ const pk = this.id;
27585
+ const qb = related.query();
27586
+ const info = {
27587
+ type: "belongsToMany",
27588
+ relatedClass: related,
27589
+ foreignKey: "",
27590
+ pivotTable,
27591
+ foreignPivotKey: foreignKey,
27592
+ relatedPivotKey: relatedKey
27593
+ };
27594
+ qb._relationInfo = info;
27595
+ return qb.select(`${related.tableName}.*`).join(pivotTable, `${pivotTable}.${relatedKey}`, "=", `${related.tableName}.id`).where(`${pivotTable}.${foreignKey}`, "=", pk);
27417
27596
  }
27418
27597
  }
27419
27598
  // src/mvc/View.ts
@@ -27987,6 +28166,66 @@ var jitCompiler = new JITCompiler;
27987
28166
  function createJITCompiler() {
27988
28167
  return new JITCompiler;
27989
28168
  }
28169
+ // src/auth/drivers/DatabaseSessionDriver.ts
28170
+ class SessionModel extends Model {
28171
+ static tableName = "sessions";
28172
+ static primaryKey = "id";
28173
+ static timestamps = false;
28174
+ }
28175
+
28176
+ class DatabaseSessionDriver {
28177
+ generateId() {
28178
+ return `sess_${Date.now().toString(36)}_${crypto.randomUUID().replace(/-/g, "")}`;
28179
+ }
28180
+ async create(userId, data, maxAge) {
28181
+ const id = this.generateId();
28182
+ const expiresAt = Date.now() + maxAge;
28183
+ const payload = JSON.stringify(data);
28184
+ await SessionModel.create({
28185
+ id,
28186
+ user_id: userId,
28187
+ payload,
28188
+ last_activity: new Date(Date.now()).toISOString().slice(0, 19).replace("T", " "),
28189
+ expires_at: expiresAt
28190
+ });
28191
+ return {
28192
+ id,
28193
+ userId,
28194
+ data,
28195
+ expiresAt
28196
+ };
28197
+ }
28198
+ async get(id) {
28199
+ const record = await SessionModel.find(id);
28200
+ if (!record)
28201
+ return null;
28202
+ const now = Date.now();
28203
+ if (record.expires_at < now) {
28204
+ this.destroy(id);
28205
+ return null;
28206
+ }
28207
+ let data = {};
28208
+ try {
28209
+ data = JSON.parse(record.payload);
28210
+ } catch (e) {
28211
+ }
28212
+ return {
28213
+ id,
28214
+ userId: record.user_id,
28215
+ data,
28216
+ expiresAt: record.expires_at
28217
+ };
28218
+ }
28219
+ async destroy(id) {
28220
+ const deleted = await SessionModel.deleteById(id);
28221
+ return deleted > 0;
28222
+ }
28223
+ async cleanup() {
28224
+ const now = Date.now();
28225
+ await SessionModel.query().where("expires_at", "<", now).delete();
28226
+ }
28227
+ }
28228
+
27990
28229
  // src/auth/Auth.ts
27991
28230
  async function hashPassword(password) {
27992
28231
  return Bun.password.hash(password, {
@@ -28115,7 +28354,7 @@ function roles(...allowedRoles) {
28115
28354
  };
28116
28355
  }
28117
28356
 
28118
- class SessionStore {
28357
+ class MemorySessionDriver {
28119
28358
  sessions = new Map;
28120
28359
  generateId() {
28121
28360
  return `sess_${Date.now().toString(36)}_${crypto.randomUUID().replace(/-/g, "")}`;
@@ -28151,12 +28390,37 @@ class SessionStore {
28151
28390
  }
28152
28391
  }
28153
28392
  }
28393
+
28394
+ class SessionStore {
28395
+ driver;
28396
+ constructor(driver) {
28397
+ this.driver = driver || new MemorySessionDriver;
28398
+ }
28399
+ useDatabase() {
28400
+ this.driver = new DatabaseSessionDriver;
28401
+ }
28402
+ use(driver) {
28403
+ this.driver = driver;
28404
+ }
28405
+ async create(userId, data = {}, maxAge = 86400000) {
28406
+ return this.driver.create(userId, data, maxAge);
28407
+ }
28408
+ async get(id) {
28409
+ return this.driver.get(id);
28410
+ }
28411
+ async destroy(id) {
28412
+ return this.driver.destroy(id);
28413
+ }
28414
+ async cleanup() {
28415
+ return this.driver.cleanup();
28416
+ }
28417
+ }
28154
28418
  var sessionStore = new SessionStore;
28155
28419
  function sessionAuth(cookieName = "canx_session") {
28156
28420
  return async (req, res, next) => {
28157
28421
  const sessionId = req.cookie(cookieName);
28158
28422
  if (sessionId) {
28159
- const session = sessionStore.get(sessionId);
28423
+ const session = await sessionStore.get(sessionId);
28160
28424
  if (session) {
28161
28425
  req.context.set("session", session);
28162
28426
  req.context.set("user", { sub: session.userId, ...session.data });
@@ -28263,6 +28527,11 @@ class TableBuilder {
28263
28527
  this.columns[this.columns.length - 1].unique = true;
28264
28528
  return this;
28265
28529
  }
28530
+ primary() {
28531
+ if (this.columns.length)
28532
+ this.columns[this.columns.length - 1].primary = true;
28533
+ return this;
28534
+ }
28266
28535
  index() {
28267
28536
  if (this.columns.length)
28268
28537
  this.columns[this.columns.length - 1].index = true;
@@ -28286,11 +28555,16 @@ class TableBuilder {
28286
28555
  return this;
28287
28556
  }
28288
28557
  toSQL() {
28558
+ const driver = getCurrentDriver();
28289
28559
  const cols = this.columns.map((col) => {
28290
28560
  let sql = `\`${col.name}\` `;
28291
28561
  switch (col.type) {
28292
28562
  case "id":
28293
- sql += "BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY";
28563
+ if (driver === "sqlite") {
28564
+ sql += "INTEGER PRIMARY KEY AUTOINCREMENT";
28565
+ } else {
28566
+ sql += "BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY";
28567
+ }
28294
28568
  break;
28295
28569
  case "string":
28296
28570
  sql += `VARCHAR(${col.length || 255})`;
@@ -28299,10 +28573,10 @@ class TableBuilder {
28299
28573
  sql += "TEXT";
28300
28574
  break;
28301
28575
  case "int":
28302
- sql += col.unsigned ? "INT UNSIGNED" : "INT";
28576
+ sql += col.unsigned ? driver === "sqlite" ? "INTEGER" : "INT UNSIGNED" : "INT";
28303
28577
  break;
28304
28578
  case "bigint":
28305
- sql += col.unsigned ? "BIGINT UNSIGNED" : "BIGINT";
28579
+ sql += col.unsigned ? driver === "sqlite" ? "INTEGER" : "BIGINT UNSIGNED" : "BIGINT";
28306
28580
  break;
28307
28581
  case "float":
28308
28582
  sql += "FLOAT";
@@ -28311,7 +28585,7 @@ class TableBuilder {
28311
28585
  sql += `DECIMAL(${col.length || 10}, 2)`;
28312
28586
  break;
28313
28587
  case "boolean":
28314
- sql += "TINYINT(1)";
28588
+ sql += driver === "sqlite" ? "INTEGER" : "TINYINT(1)";
28315
28589
  break;
28316
28590
  case "date":
28317
28591
  sql += "DATE";
@@ -28338,6 +28612,8 @@ class TableBuilder {
28338
28612
  }
28339
28613
  if (col.unique)
28340
28614
  sql += " UNIQUE";
28615
+ if (col.primary)
28616
+ sql += " PRIMARY KEY";
28341
28617
  }
28342
28618
  return sql;
28343
28619
  });
@@ -28350,10 +28626,11 @@ class TableBuilder {
28350
28626
  fk += ` ON UPDATE ${ref.onUpdate}`;
28351
28627
  return fk;
28352
28628
  });
28629
+ const suffix = driver === "sqlite" ? "" : " ENGINE=InnoDB DEFAULT CHARSET=utf8mb4";
28353
28630
  return `CREATE TABLE \`${this.tableName}\` (
28354
28631
  ${[...cols, ...fks].join(`,
28355
28632
  `)}
28356
- ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4`;
28633
+ )${suffix}`;
28357
28634
  }
28358
28635
  }
28359
28636
  var Schema2 = {
@@ -28375,10 +28652,20 @@ var Schema2 = {
28375
28652
  console.log(`[Migration] Renamed table: ${from} -> ${to}`);
28376
28653
  },
28377
28654
  async hasTable(table) {
28655
+ const driver = getCurrentDriver();
28656
+ if (driver === "sqlite") {
28657
+ const result2 = await query(`SELECT count(*) as count FROM sqlite_master WHERE type='table' AND name='${table}'`);
28658
+ return (result2[0]?.count || result2[0]?.["count(*)"]) > 0;
28659
+ }
28378
28660
  const result = await query(`SHOW TABLES LIKE '${table}'`);
28379
28661
  return result.length > 0;
28380
28662
  },
28381
28663
  async hasColumn(table, column) {
28664
+ const driver = getCurrentDriver();
28665
+ if (driver === "sqlite") {
28666
+ const result2 = await query(`PRAGMA table_info(${table})`);
28667
+ return result2.some((c) => c.name === column);
28668
+ }
28382
28669
  const result = await query(`SHOW COLUMNS FROM \`${table}\` LIKE '${column}'`);
28383
28670
  return result.length > 0;
28384
28671
  }
@@ -28421,7 +28708,7 @@ class Migrator {
28421
28708
  for (const migration of pending) {
28422
28709
  console.log(`[Migration] Running: ${migration.name}`);
28423
28710
  await migration.up();
28424
- await execute("INSERT INTO migrations (name, batch, executed_at) VALUES (?, ?, NOW())", [migration.name, batch]);
28711
+ await execute("INSERT INTO migrations (name, batch, executed_at) VALUES (?, ?, CURRENT_TIMESTAMP)", [migration.name, batch]);
28425
28712
  }
28426
28713
  console.log(`[Migration] Completed ${pending.length} migrations.`);
28427
28714
  }
@@ -30758,6 +31045,9 @@ function validate(data, schema) {
30758
31045
  if (name !== "required" && (value === undefined || value === null || value === "")) {
30759
31046
  continue;
30760
31047
  }
31048
+ if (["unique", "exists"].includes(name)) {
31049
+ continue;
31050
+ }
30761
31051
  let valid = false;
30762
31052
  if (validators[name]) {
30763
31053
  valid = validators[name](value);
@@ -30781,8 +31071,89 @@ function validate(data, schema) {
30781
31071
  }
30782
31072
  return { valid: errors2.size === 0, errors: errors2, data: validData };
30783
31073
  }
30784
- function validateAsync(data, schema) {
30785
- return Promise.resolve(validate(data, schema));
31074
+ var asyncValidators = {
31075
+ unique: async (v, p) => {
31076
+ if (!p)
31077
+ return false;
31078
+ const [table, column, exceptId, idColumn = "id"] = p.split(",").map((s) => s.trim());
31079
+ let sql = `SELECT COUNT(*) as count FROM ${table} WHERE ${column} = ?`;
31080
+ const params = [v];
31081
+ if (exceptId) {
31082
+ sql += ` AND ${idColumn} != ?`;
31083
+ params.push(exceptId);
31084
+ }
31085
+ try {
31086
+ const rows = await query(sql, params);
31087
+ return (rows[0]?.count || 0) === 0;
31088
+ } catch (e) {
31089
+ console.error("[Validator] Database error in unique:", e);
31090
+ return false;
31091
+ }
31092
+ },
31093
+ exists: async (v, p) => {
31094
+ if (!p)
31095
+ return false;
31096
+ const [table, column = "id"] = p.split(",").map((s) => s.trim());
31097
+ const sql = `SELECT COUNT(*) as count FROM ${table} WHERE ${column} = ?`;
31098
+ try {
31099
+ const rows = await query(sql, [v]);
31100
+ return (rows[0]?.count || 0) > 0;
31101
+ } catch (e) {
31102
+ console.error("[Validator] Database error in exists:", e);
31103
+ return false;
31104
+ }
31105
+ }
31106
+ };
31107
+ var defaultAsyncMessages = {
31108
+ unique: "Field {field} has already been taken",
31109
+ exists: "Selected {field} is invalid"
31110
+ };
31111
+ async function validateAsync(data, schema) {
31112
+ const errors2 = new Map;
31113
+ const validData = {};
31114
+ const syncResult = validate(data, schema);
31115
+ if (!syncResult.valid) {
31116
+ syncResult.errors.forEach((msgs, field) => errors2.set(field, msgs));
31117
+ } else {
31118
+ Object.assign(validData, syncResult.data);
31119
+ }
31120
+ for (const [field, rules] of Object.entries(schema)) {
31121
+ const value = data[field];
31122
+ let ruleList;
31123
+ let customMessages = {};
31124
+ if (Array.isArray(rules)) {
31125
+ ruleList = rules;
31126
+ } else if (typeof rules === "object") {
31127
+ ruleList = rules.rules;
31128
+ customMessages = rules.messages || {};
31129
+ } else {
31130
+ ruleList = [rules];
31131
+ }
31132
+ for (const rule of ruleList) {
31133
+ const { name, param } = parseRule(rule);
31134
+ if (!asyncValidators[name] || (value === undefined || value === null || value === "")) {
31135
+ continue;
31136
+ }
31137
+ if (errors2.has(field))
31138
+ continue;
31139
+ try {
31140
+ const valid = await asyncValidators[name](value, param);
31141
+ if (!valid) {
31142
+ const msg = customMessages[name] || defaultAsyncMessages[name] || defaultMessages[name] || `Validation failed for {field}`;
31143
+ const finalMsg = msg.replace("{field}", field).replace("{param}", param || "");
31144
+ const existing = errors2.get(field) || [];
31145
+ existing.push(finalMsg);
31146
+ errors2.set(field, existing);
31147
+ }
31148
+ } catch (err) {
31149
+ console.error("[Validator] Unexpected error in validateAsync", err);
31150
+ }
31151
+ }
31152
+ if (!errors2.has(field) && value !== undefined) {
31153
+ validData[field] = value;
31154
+ }
31155
+ }
31156
+ return { valid: errors2.size === 0, errors: errors2, data: validData };
30786
31157
  }
30787
31158
  var is = {
30788
31159
  email: (v) => validators.email(v),
@@ -32139,6 +32510,7 @@ export {
32139
32510
  EventServiceProvider,
32140
32511
  EventEmitter,
32141
32512
  Delete,
32513
+ DatabaseSessionDriver,
32142
32514
  DatabaseError,
32143
32515
  Controller,
32144
32516
  Container,