@bunnykit/orm 0.1.2 → 0.1.4

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.
@@ -82,6 +82,9 @@ export declare class Model<T extends Record<string, any> = Record<string, any>>
82
82
  static timestamps: boolean;
83
83
  static connection?: Connection;
84
84
  static dateFormat: string;
85
+ static keyType: "int" | "string" | "uuid";
86
+ static incrementing: boolean;
87
+ static usesUuids: boolean;
85
88
  static morphName?: string;
86
89
  static casts: Record<string, CastDefinition>;
87
90
  static fillable: string[];
@@ -103,6 +106,7 @@ export declare class Model<T extends Record<string, any> = Record<string, any>>
103
106
  static removeGlobalScope(name: string): void;
104
107
  static applyGlobalScopes(builder: Builder<any>): void;
105
108
  static getQualifiedDeletedAtColumn(): string;
109
+ static shouldAutoGeneratePrimaryKey(): Promise<boolean>;
106
110
  static create<M extends typeof Model>(this: M, attributes: Partial<InstanceType<M> extends Model<infer U> ? U : Record<string, any>>): Promise<InstanceType<M>>;
107
111
  static find<M extends typeof Model>(this: M, id: any): Promise<InstanceType<M> | null>;
108
112
  static first<M extends typeof Model>(this: M): Promise<InstanceType<M> | null>;
@@ -3,6 +3,7 @@ import { snakeCase } from "../utils.js";
3
3
  import { ObserverRegistry } from "./Observer.js";
4
4
  import { MorphTo, MorphOne, MorphMany, MorphToMany } from "./MorphRelations.js";
5
5
  import { BelongsToMany } from "./BelongsToMany.js";
6
+ import { Schema } from "../schema/Schema.js";
6
7
  const globalScopes = new WeakMap();
7
8
  function getGlobalScopes(model) {
8
9
  const scopes = new Map();
@@ -276,6 +277,9 @@ export class Model {
276
277
  static timestamps = true;
277
278
  static connection;
278
279
  static dateFormat = "YYYY-MM-DD HH:mm:ss";
280
+ static keyType = "int";
281
+ static incrementing = true;
282
+ static usesUuids = false;
279
283
  static morphName;
280
284
  static casts = {};
281
285
  static fillable = [];
@@ -370,6 +374,20 @@ export class Model {
370
374
  static getQualifiedDeletedAtColumn() {
371
375
  return `${this.getTable()}.${this.deletedAtColumn}`;
372
376
  }
377
+ static async shouldAutoGeneratePrimaryKey() {
378
+ if (this.usesUuids || this.keyType === "uuid")
379
+ return true;
380
+ const column = await Schema.getColumn(this.getTable(), this.primaryKey);
381
+ if (!column)
382
+ return false;
383
+ if (!column.primary)
384
+ return false;
385
+ if (column.autoIncrement)
386
+ return false;
387
+ const type = String(column.type || "").toLowerCase();
388
+ const numericTypes = new Set(["integer", "int", "bigint", "smallint", "tinyint", "real", "float", "double", "decimal", "numeric"]);
389
+ return !numericTypes.has(type);
390
+ }
373
391
  static async create(attributes) {
374
392
  const instance = new this();
375
393
  instance.fill(attributes);
@@ -632,9 +650,21 @@ export class Model {
632
650
  this.$attributes["created_at"] = now;
633
651
  this.$attributes["updated_at"] = now;
634
652
  }
635
- const result = await new Builder(constructor.getConnection(), constructor.getTable()).insertGetId(this.$attributes);
636
- if (result) {
637
- this.$attributes[constructor.primaryKey] = result;
653
+ const primaryKey = constructor.primaryKey;
654
+ const primaryKeyValue = this.getAttribute(primaryKey);
655
+ const shouldGeneratePrimaryKey = await constructor.shouldAutoGeneratePrimaryKey();
656
+ if ((primaryKeyValue === null || primaryKeyValue === undefined || primaryKeyValue === "") && shouldGeneratePrimaryKey) {
657
+ const generated = crypto.randomUUID();
658
+ this.$attributes[primaryKey] = generated;
659
+ }
660
+ if (shouldGeneratePrimaryKey || primaryKeyValue !== null && primaryKeyValue !== undefined && primaryKeyValue !== "") {
661
+ await new Builder(constructor.getConnection(), constructor.getTable()).insert(this.$attributes);
662
+ }
663
+ else {
664
+ const result = await new Builder(constructor.getConnection(), constructor.getTable()).insertGetId(this.$attributes);
665
+ if (result) {
666
+ this.$attributes[constructor.primaryKey] = result;
667
+ }
638
668
  }
639
669
  this.$exists = true;
640
670
  this.$original = { ...this.$attributes };
@@ -13,4 +13,10 @@ export declare class Schema {
13
13
  static rename(from: string, to: string): Promise<void>;
14
14
  static hasTable(table: string): Promise<boolean>;
15
15
  static hasColumn(table: string, column: string): Promise<boolean>;
16
+ static getColumn(table: string, column: string): Promise<{
17
+ name: string;
18
+ type: string;
19
+ primary: boolean;
20
+ autoIncrement: boolean;
21
+ } | null>;
16
22
  }
@@ -132,4 +132,31 @@ export class Schema {
132
132
  const result = await this.getConnection().query(sql);
133
133
  return result.length > 0;
134
134
  }
135
+ static async getColumn(table, column) {
136
+ const driver = this.getConnection().getDriverName();
137
+ if (driver === "sqlite") {
138
+ const rows = await this.getConnection().query(`PRAGMA table_info(${table})`);
139
+ const row = rows.find((item) => item.name === column);
140
+ return row ? { name: row.name, type: row.type, primary: row.pk > 0, autoIncrement: false } : null;
141
+ }
142
+ if (driver === "mysql") {
143
+ const rows = await this.getConnection().query(`SHOW COLUMNS FROM ${table} LIKE '${column}'`);
144
+ const row = rows[0];
145
+ return row ? { name: row.Field, type: row.Type, primary: row.Key === "PRI", autoIncrement: String(row.Extra || "").toLowerCase().includes("auto_increment") } : null;
146
+ }
147
+ const rows = await this.getConnection().query(`SELECT c.column_name, c.data_type, COALESCE(tc.constraint_type = 'PRIMARY KEY', false) AS primary_key
148
+ FROM information_schema.columns c
149
+ LEFT JOIN information_schema.key_column_usage kcu
150
+ ON c.table_schema = kcu.table_schema
151
+ AND c.table_name = kcu.table_name
152
+ AND c.column_name = kcu.column_name
153
+ LEFT JOIN information_schema.table_constraints tc
154
+ ON kcu.table_schema = tc.table_schema
155
+ AND kcu.constraint_name = tc.constraint_name
156
+ WHERE c.table_schema = 'public'
157
+ AND c.table_name = '${table}'
158
+ AND c.column_name = '${column}'`);
159
+ const row = rows[0];
160
+ return row ? { name: row.column_name, type: row.data_type, primary: !!row.primary_key, autoIncrement: false } : null;
161
+ }
135
162
  }
@@ -48,6 +48,8 @@ export class SQLiteGrammar extends Grammar {
48
48
  let sql = `${this.wrap(column.name)} ${this.getType(column)}`;
49
49
  if (column.autoIncrement)
50
50
  sql += this.modifyAutoIncrement(column);
51
+ else if (column.primary)
52
+ sql += " PRIMARY KEY";
51
53
  if (!column.nullable)
52
54
  sql += " NOT NULL";
53
55
  if (column.default !== undefined)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bunnykit/orm",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
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",