millas 0.2.17 → 0.2.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "millas",
3
- "version": "0.2.17",
3
+ "version": "0.2.19",
4
4
  "description": "A modern batteries-included backend framework for Node.js — built on Express, inspired by Laravel, Django, and FastAPI",
5
5
  "main": "src/index.js",
6
6
  "exports": {
@@ -102,8 +102,10 @@ class RequestContext {
102
102
  // Uploaded files (populated by multer or similar middleware)
103
103
  // req.files → multi-file upload { avatar: File, resume: File }
104
104
  // req.file → single-file upload (multer .single())
105
- this.files = millaReq.raw.files || {};
106
- this.file = millaReq.raw.file || null;
105
+ // Prefer UploadedFile-wrapped instances set by UploadMiddleware (_millaFile/_millaFiles).
106
+ // Fall back to raw multer objects only if UploadMiddleware hasn't run.
107
+ this.file = millaReq.raw._millaFile || millaReq.raw.file || null;
108
+ this.files = millaReq.raw._millaFiles || millaReq.raw.files || {};
107
109
 
108
110
  // ── headers ───────────────────────────────────────────────────────────────
109
111
  this.headers = millaReq.raw.headers || {};
@@ -121,7 +121,7 @@ class UploadMiddleware {
121
121
  */
122
122
  async handle(ctx, next) {
123
123
  const expressReq = ctx.req.raw;
124
- const contentType = expressReq.headers?.['content-type'] || '';
124
+ const contentType = ctx.headers?.['content-type'] || '';
125
125
 
126
126
  // Skip multer entirely for non-multipart requests.
127
127
  // ctx.file / ctx.files stay null/{} — the handler or shape validation
@@ -291,6 +291,7 @@ class Model {
291
291
  };
292
292
 
293
293
  payload = await this.beforeCreate(payload) ?? payload;
294
+ payload = this._serializeForDb(payload);
294
295
 
295
296
  const q = trx ? trx(this.table) : this._db();
296
297
  const [id] = await q.insert(payload);
@@ -658,6 +659,7 @@ class Model {
658
659
  };
659
660
 
660
661
  payload = await this.constructor.beforeUpdate(payload) ?? payload;
662
+ payload = this.constructor._serializeForDb(payload);
661
663
 
662
664
  const q = trx
663
665
  ? trx(this.constructor.table)
@@ -764,11 +766,43 @@ class Model {
764
766
  const fields = this.getFields();
765
767
  const cast = {};
766
768
  for (const [key, val] of Object.entries(row)) {
767
- cast[key] = (fields[key]?.type === 'boolean' && val != null) ? Boolean(val) : val;
769
+ cast[key] = this._castValue(val, fields[key]?.type);
768
770
  }
769
771
  return new this(cast);
770
772
  }
771
773
 
774
+ static _castValue(val, type) {
775
+ if (val == null) return val;
776
+ switch (type) {
777
+ case 'boolean': return Boolean(val);
778
+ case 'integer': return Number.isInteger(val) ? val : parseInt(val, 10);
779
+ case 'bigInteger':return typeof val === 'bigint' ? val : parseInt(val, 10);
780
+ case 'float':
781
+ case 'decimal': return typeof val === 'number' ? val : parseFloat(val);
782
+ case 'json': return typeof val === 'string' ? JSON.parse(val) : val;
783
+ case 'date':
784
+ case 'timestamp': return val instanceof Date ? val : new Date(val);
785
+ default: return val;
786
+ }
787
+ }
788
+
789
+ static _serializeValue(val, type) {
790
+ if (val == null) return val;
791
+ if (type === 'json') return typeof val === 'string' ? val : JSON.stringify(val);
792
+ if (type === 'boolean') return val ? 1 : 0;
793
+ if ((type === 'date' || type === 'timestamp') && val instanceof Date) return val.toISOString();
794
+ return val;
795
+ }
796
+
797
+ static _serializeForDb(data) {
798
+ const fieldDefs = this.getFields();
799
+ const result = {};
800
+ for (const [key, val] of Object.entries(data)) {
801
+ result[key] = this._serializeValue(val, fieldDefs[key]?.type);
802
+ }
803
+ return result;
804
+ }
805
+
772
806
  static async _hydrateFromTrx(id, trx) {
773
807
  const row = await trx(this.table).where(this.primaryKey, id).first();
774
808
  return row ? this._hydrate(row) : null;