@bunnykit/orm 0.1.19 → 0.1.20

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.
@@ -105,6 +105,7 @@ export declare class Model<T extends Record<string, any> = Record<string, any>>
105
105
  static attributes: Record<string, any>;
106
106
  static softDeletes: boolean;
107
107
  static deletedAtColumn: string;
108
+ static preventLazyLoading: boolean;
108
109
  $attributes: T;
109
110
  $original: Partial<T>;
110
111
  $exists: boolean;
@@ -112,6 +113,8 @@ export declare class Model<T extends Record<string, any> = Record<string, any>>
112
113
  $casts: Record<string, CastDefinition>;
113
114
  $connection?: Connection;
114
115
  constructor(attributes?: Partial<T>);
116
+ private defineAttributeProperty;
117
+ private syncAttributeProperties;
115
118
  static getTable(): string;
116
119
  static getConnection(): Connection;
117
120
  static setConnection(connection: Connection): void;
@@ -38,6 +38,15 @@ export class Relation {
38
38
  this.builder = related.on(parent.getConnection());
39
39
  this.foreignKey = foreignKey || this.defaultForeignKey();
40
40
  this.localKey = localKey || related.primaryKey;
41
+ // Wrap getResults with lazy-loading guard
42
+ const originalGetResults = this.getResults.bind(this);
43
+ this.getResults = async () => {
44
+ if (this.parent.constructor.preventLazyLoading) {
45
+ throw new Error(`Lazy loading is prevented on ${this.parent.constructor.name}. ` +
46
+ `Eager load the relation using with().`);
47
+ }
48
+ return await originalGetResults();
49
+ };
41
50
  }
42
51
  defaultForeignKey() {
43
52
  return `${snakeCase(this.parent.constructor.name)}_id`;
@@ -291,6 +300,7 @@ export class Model {
291
300
  static attributes = {};
292
301
  static softDeletes = false;
293
302
  static deletedAtColumn = "deleted_at";
303
+ static preventLazyLoading = false;
294
304
  $attributes = {};
295
305
  $original = {};
296
306
  $exists = false;
@@ -305,6 +315,9 @@ export class Model {
305
315
  if (attributes) {
306
316
  this.fill(attributes);
307
317
  }
318
+ this.syncAttributeProperties();
319
+ // Minimal Proxy fallback for dynamic property access on undefined keys.
320
+ // Pre-defined attribute getters/setters bypass the Proxy entirely.
308
321
  return new Proxy(this, {
309
322
  get(target, prop, receiver) {
310
323
  if (typeof prop === "string" && !(prop in target) && prop in target.$attributes) {
@@ -319,25 +332,23 @@ export class Model {
319
332
  }
320
333
  return Reflect.set(target, prop, value, receiver);
321
334
  },
322
- has(target, prop) {
323
- return prop in target || (typeof prop === "string" && prop in target.$attributes);
324
- },
325
- getOwnPropertyDescriptor(target, prop) {
326
- if (typeof prop === "string" && !(prop in target) && prop in target.$attributes) {
327
- return {
328
- enumerable: true,
329
- configurable: true,
330
- writable: true,
331
- value: target.getAttribute(prop),
332
- };
333
- }
334
- return Reflect.getOwnPropertyDescriptor(target, prop);
335
- },
336
- ownKeys(target) {
337
- return [...new Set([...Reflect.ownKeys(target), ...Object.keys(target.$attributes)])];
338
- },
339
335
  });
340
336
  }
337
+ defineAttributeProperty(key) {
338
+ if (key in this)
339
+ return;
340
+ Object.defineProperty(this, key, {
341
+ get: () => this.getAttribute(key),
342
+ set: (value) => this.setAttribute(key, value),
343
+ enumerable: true,
344
+ configurable: true,
345
+ });
346
+ }
347
+ syncAttributeProperties() {
348
+ for (const key of Object.keys(this.$attributes)) {
349
+ this.defineAttributeProperty(key);
350
+ }
351
+ }
341
352
  static getTable() {
342
353
  return this.table || snakeCase(this.name) + "s";
343
354
  }
@@ -662,6 +673,7 @@ export class Model {
662
673
  }
663
674
  setAttribute(key, value) {
664
675
  this.$attributes[key] = this.serializeCastAttribute(key, value);
676
+ this.defineAttributeProperty(key);
665
677
  }
666
678
  castAttribute(key, value) {
667
679
  const cast = this.getCastDefinition(key);
@@ -817,6 +829,7 @@ export class Model {
817
829
  await ObserverRegistry.dispatch("created", this);
818
830
  await ObserverRegistry.dispatch("saved", this);
819
831
  }
832
+ this.syncAttributeProperties();
820
833
  const identityMap = IdentityMap.current();
821
834
  if (identityMap) {
822
835
  const pk = this.getAttribute(constructor.primaryKey);
@@ -835,6 +848,7 @@ export class Model {
835
848
  if (!this.$exists) {
836
849
  this.$attributes["created_at"] = now;
837
850
  }
851
+ this.syncAttributeProperties();
838
852
  }
839
853
  async touch() {
840
854
  if (!this.$exists)
@@ -850,6 +864,7 @@ export class Model {
850
864
  .update({ updated_at: now });
851
865
  this.$attributes["updated_at"] = now;
852
866
  this.$original = { ...this.$attributes };
867
+ this.syncAttributeProperties();
853
868
  return true;
854
869
  }
855
870
  async increment(column, amount = 1, extra = {}) {
@@ -869,6 +884,7 @@ export class Model {
869
884
  this.$attributes[key] = value;
870
885
  }
871
886
  this.$original = { ...this.$attributes };
887
+ this.syncAttributeProperties();
872
888
  return this;
873
889
  }
874
890
  async decrement(column, amount = 1, extra = {}) {
@@ -893,6 +909,7 @@ export class Model {
893
909
  .update({ [constructor.deletedAtColumn]: deletedAt });
894
910
  this.$attributes[constructor.deletedAtColumn] = deletedAt;
895
911
  this.$original = { ...this.$attributes };
912
+ this.syncAttributeProperties();
896
913
  }
897
914
  else {
898
915
  const connection = this.getConnection();
@@ -918,6 +935,7 @@ export class Model {
918
935
  this.$attributes[constructor.deletedAtColumn] = null;
919
936
  this.$original = { ...this.$attributes };
920
937
  this.$exists = true;
938
+ this.syncAttributeProperties();
921
939
  return true;
922
940
  }
923
941
  async forceDelete() {
@@ -941,6 +959,7 @@ export class Model {
941
959
  if (result) {
942
960
  this.$attributes = result.$attributes;
943
961
  this.$original = { ...result.$attributes };
962
+ this.syncAttributeProperties();
944
963
  }
945
964
  return this;
946
965
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bunnykit/orm",
3
- "version": "0.1.19",
3
+ "version": "0.1.20",
4
4
  "description": "An Eloquent-inspired ORM for Bun's native SQL client supporting SQLite, MySQL, and PostgreSQL.",
5
5
  "license": "MIT",
6
6
  "packageManager": "bun@1.3.12",