@bunnykit/orm 0.1.14 → 0.1.16

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.
@@ -20,8 +20,8 @@ export declare class Connection {
20
20
  withoutSchema(): Connection;
21
21
  qualifyTable(table: string): string;
22
22
  private quoteIdentifier;
23
- query(sqlString: string): Promise<any[]>;
24
- run(sqlString: string): Promise<any>;
23
+ query(sqlString: string, bindings?: any[]): Promise<any[]>;
24
+ run(sqlString: string, bindings?: any[]): Promise<any>;
25
25
  beginTransaction(): Promise<void>;
26
26
  commit(): Promise<void>;
27
27
  rollback(): Promise<void>;
@@ -75,12 +75,11 @@ export class Connection {
75
75
  quoteIdentifier(value) {
76
76
  return `"${value.replace(/"/g, '""')}"`;
77
77
  }
78
- async query(sqlString) {
79
- // Use unsafe for generated SQL strings
80
- return (await this.driver.unsafe(sqlString));
78
+ async query(sqlString, bindings) {
79
+ return (await this.driver.unsafe(sqlString, bindings));
81
80
  }
82
- async run(sqlString) {
83
- return await this.driver.unsafe(sqlString);
81
+ async run(sqlString, bindings) {
82
+ return await this.driver.unsafe(sqlString, bindings);
84
83
  }
85
84
  async beginTransaction() {
86
85
  await this.driver.unsafe("BEGIN");
@@ -106,7 +105,7 @@ export class Connection {
106
105
  return await this.transaction(callback);
107
106
  }
108
107
  return await this.transaction(async (connection) => {
109
- await connection.run(`SET LOCAL ${setting} = ${connection.getGrammar().escape(tenantId)}`);
108
+ await connection.run(`SET LOCAL ${setting} = ${connection.getGrammar().placeholder(1)}`, [tenantId]);
110
109
  return await callback(connection);
111
110
  });
112
111
  }
@@ -21,15 +21,28 @@ export type TenantResolution = {
21
21
  setting?: string;
22
22
  };
23
23
  export type TenantResolver = (tenantId: string) => TenantResolution | Promise<TenantResolution>;
24
+ export interface PoolConfig {
25
+ maxConnections?: number;
26
+ minConnections?: number;
27
+ idleTimeout?: number;
28
+ }
24
29
  export declare class ConnectionManager {
25
30
  private static defaultConnection?;
26
31
  private static connections;
32
+ private static pools;
33
+ private static poolConfigs;
27
34
  private static tenantResolver?;
28
35
  private static tenantCache;
29
36
  static setDefault(connection: Connection): void;
30
37
  static getDefault(): Connection | undefined;
38
+ static setPoolConfig(name: string, config: PoolConfig): void;
39
+ static getPoolConfig(name: string): PoolConfig | undefined;
40
+ private static getPooledConnection;
41
+ private static releasePooledConnection;
31
42
  static add(name: string, connection: Connection | ConnectionConfig): Connection;
32
43
  static get(name: string): Connection | undefined;
44
+ static getPooled(name: string, config?: ConnectionConfig): Promise<Connection>;
45
+ static release(name: string, connection: Connection): void;
33
46
  static require(name: string): Connection;
34
47
  static setTenantResolver(resolver: TenantResolver): void;
35
48
  static resolveTenant(tenantId: string): Promise<ActiveTenantContext>;
@@ -2,6 +2,8 @@ import { Connection } from "./Connection.js";
2
2
  export class ConnectionManager {
3
3
  static defaultConnection;
4
4
  static connections = new Map();
5
+ static pools = new Map();
6
+ static poolConfigs = new Map();
5
7
  static tenantResolver;
6
8
  static tenantCache = new Map();
7
9
  static setDefault(connection) {
@@ -10,6 +12,67 @@ export class ConnectionManager {
10
12
  static getDefault() {
11
13
  return this.defaultConnection;
12
14
  }
15
+ static setPoolConfig(name, config) {
16
+ this.poolConfigs.set(name, { maxConnections: 10, minConnections: 1, idleTimeout: 30000, ...config });
17
+ }
18
+ static getPoolConfig(name) {
19
+ return this.poolConfigs.get(name);
20
+ }
21
+ static async getPooledConnection(name, config) {
22
+ const poolConfig = this.poolConfigs.get(name) || { maxConnections: 10, minConnections: 1, idleTimeout: 30000 };
23
+ let pool = this.pools.get(name);
24
+ if (!pool) {
25
+ pool = [];
26
+ this.pools.set(name, pool);
27
+ }
28
+ const now = Date.now();
29
+ const idleTimeout = poolConfig.idleTimeout || 30000;
30
+ while (pool.length > 0) {
31
+ const idx = pool.findIndex((c) => !c.inUse && (now - c.lastUsed) < idleTimeout);
32
+ if (idx === -1)
33
+ break;
34
+ const pooled = pool[idx];
35
+ pool.splice(idx, 1);
36
+ try {
37
+ pooled.connection.query("SELECT 1").catch(() => null);
38
+ pooled.inUse = true;
39
+ return pooled.connection;
40
+ }
41
+ catch {
42
+ await pooled.connection.close().catch(() => null);
43
+ }
44
+ }
45
+ if (pool.length < (poolConfig.maxConnections || 10)) {
46
+ const connection = new Connection(config);
47
+ pool.push({ connection, lastUsed: Date.now(), inUse: true });
48
+ return connection;
49
+ }
50
+ return new Promise((resolve, reject) => {
51
+ const checkInterval = setInterval(() => {
52
+ const available = pool.find((c) => !c.inUse);
53
+ if (available) {
54
+ clearInterval(checkInterval);
55
+ available.inUse = true;
56
+ available.lastUsed = Date.now();
57
+ resolve(available.connection);
58
+ }
59
+ }, 50);
60
+ setTimeout(() => {
61
+ clearInterval(checkInterval);
62
+ reject(new Error(`Connection pool exhausted for "${name}"`));
63
+ }, 30000);
64
+ });
65
+ }
66
+ static releasePooledConnection(name, connection) {
67
+ const pool = this.pools.get(name);
68
+ if (!pool)
69
+ return;
70
+ const pooled = pool.find((p) => p.connection === connection);
71
+ if (pooled) {
72
+ pooled.inUse = false;
73
+ pooled.lastUsed = Date.now();
74
+ }
75
+ }
13
76
  static add(name, connection) {
14
77
  const resolved = connection instanceof Connection ? connection : new Connection(connection);
15
78
  this.connections.set(name, resolved);
@@ -18,6 +81,18 @@ export class ConnectionManager {
18
81
  static get(name) {
19
82
  return this.connections.get(name);
20
83
  }
84
+ static async getPooled(name, config) {
85
+ if (config) {
86
+ return this.getPooledConnection(name, config);
87
+ }
88
+ const existing = this.connections.get(name);
89
+ if (existing)
90
+ return existing;
91
+ throw new Error(`No connection registered for "${name}". Use add() first or provide config.`);
92
+ }
93
+ static release(name, connection) {
94
+ this.releasePooledConnection(name, connection);
95
+ }
21
96
  static require(name) {
22
97
  const connection = this.get(name);
23
98
  if (!connection) {
@@ -93,9 +168,15 @@ export class ConnectionManager {
93
168
  }
94
169
  static async closeAll() {
95
170
  const connections = new Set(this.connections.values());
171
+ for (const pool of this.pools.values()) {
172
+ for (const { connection } of pool) {
173
+ connections.add(connection);
174
+ }
175
+ }
96
176
  if (this.defaultConnection)
97
177
  connections.add(this.defaultConnection);
98
178
  this.connections.clear();
179
+ this.pools.clear();
99
180
  this.tenantCache.clear();
100
181
  for (const connection of connections) {
101
182
  await connection.close();
@@ -30,6 +30,8 @@ export declare class Builder<T = Record<string, any>> {
30
30
  unions: UnionClause[];
31
31
  fromRaw?: string;
32
32
  updateJoins: string[];
33
+ bindings: any[];
34
+ private parameterize;
33
35
  constructor(connection: Connection, table: string);
34
36
  private get grammar();
35
37
  setModel(model: ModelConstructor): this;
@@ -129,6 +131,7 @@ export declare class Builder<T = Record<string, any>> {
129
131
  clone(): Builder<T>;
130
132
  wrapColumn(value: string): string;
131
133
  escapeValue(value: any): string;
134
+ private addBinding;
132
135
  private compileWhereClause;
133
136
  private compileWheres;
134
137
  private compileNestedWheres;
@@ -157,7 +160,7 @@ export declare class Builder<T = Record<string, any>> {
157
160
  paginate(perPage?: number, page?: number): Promise<Paginator<T>>;
158
161
  chunk(count: number, callback: (items: T[]) => void | Promise<void>): Promise<void>;
159
162
  each(count: number, callback: (item: T) => void | Promise<void>): Promise<void>;
160
- cursor(): AsyncGenerator<T>;
163
+ cursor(keyset?: Record<string, any>): AsyncGenerator<T>;
161
164
  lazy(count?: number): AsyncGenerator<T>;
162
165
  insert(data: ModelAttributeInput<T> | ModelAttributeInput<T>[]): Promise<any>;
163
166
  insertGetId(data: ModelAttributeInput<T>, idColumn?: ModelColumn<T>): Promise<any>;
@@ -18,6 +18,8 @@ export class Builder {
18
18
  unions = [];
19
19
  fromRaw;
20
20
  updateJoins = [];
21
+ bindings = [];
22
+ parameterize = false;
21
23
  constructor(connection, table) {
22
24
  this.connection = connection;
23
25
  this.tableName = table;
@@ -257,11 +259,11 @@ export class Builder {
257
259
  return this;
258
260
  }
259
261
  having(column, operator, value) {
260
- this.havings.push({ sql: `${this.grammar.wrap(column)} ${operator} ${this.grammar.escape(value)}`, boolean: "and" });
262
+ this.havings.push({ column, operator, value, boolean: "and" });
261
263
  return this;
262
264
  }
263
265
  orHaving(column, operator, value) {
264
- this.havings.push({ sql: `${this.grammar.wrap(column)} ${operator} ${this.grammar.escape(value)}`, boolean: "or" });
266
+ this.havings.push({ column, operator, value, boolean: "or" });
265
267
  return this;
266
268
  }
267
269
  havingRaw(sql, boolean = "and") {
@@ -455,6 +457,7 @@ export class Builder {
455
457
  cloned.unions = [...this.unions];
456
458
  cloned.fromRaw = this.fromRaw;
457
459
  cloned.updateJoins = [...this.updateJoins];
460
+ cloned.bindings = [...this.bindings];
458
461
  return cloned;
459
462
  }
460
463
  wrapColumn(value) {
@@ -463,13 +466,21 @@ export class Builder {
463
466
  escapeValue(value) {
464
467
  return this.grammar.escape(value);
465
468
  }
469
+ addBinding(value) {
470
+ this.bindings.push(value);
471
+ return this.grammar.placeholder(this.bindings.length);
472
+ }
466
473
  compileWhereClause(where, prefix) {
467
474
  if (where.type === "basic") {
468
- return `${prefix} ${this.grammar.wrap(where.column)} ${where.operator} ${this.grammar.escape(where.value)}`;
475
+ const value = this.parameterize ? this.addBinding(where.value) : this.grammar.escape(where.value);
476
+ return `${prefix} ${this.grammar.wrap(where.column)} ${where.operator} ${value}`;
469
477
  }
470
478
  else if (where.type === "in") {
471
479
  const op = where.operator === "NOT IN" ? "NOT IN" : "IN";
472
- return `${prefix} ${this.grammar.wrap(where.column)} ${op} (${where.value.map((v) => this.grammar.escape(v)).join(", ")})`;
480
+ const values = this.parameterize
481
+ ? where.value.map((v) => this.addBinding(v)).join(", ")
482
+ : where.value.map((v) => this.grammar.escape(v)).join(", ");
483
+ return `${prefix} ${this.grammar.wrap(where.column)} ${op} (${values})`;
473
484
  }
474
485
  else if (where.type === "null") {
475
486
  const op = where.operator === "NOT NULL" ? "IS NOT NULL" : "IS NULL";
@@ -477,7 +488,9 @@ export class Builder {
477
488
  }
478
489
  else if (where.type === "between") {
479
490
  const op = where.operator === "NOT BETWEEN" ? "NOT BETWEEN" : "BETWEEN";
480
- return `${prefix} ${this.grammar.wrap(where.column)} ${op} ${this.grammar.escape(where.value[0])} AND ${this.grammar.escape(where.value[1])}`;
491
+ const low = this.parameterize ? this.addBinding(where.value[0]) : this.grammar.escape(where.value[0]);
492
+ const high = this.parameterize ? this.addBinding(where.value[1]) : this.grammar.escape(where.value[1]);
493
+ return `${prefix} ${this.grammar.wrap(where.column)} ${op} ${low} AND ${high}`;
481
494
  }
482
495
  else if (where.type === "raw") {
483
496
  return `${prefix} ${where.column}`;
@@ -526,7 +539,11 @@ export class Builder {
526
539
  return "";
527
540
  const clauses = this.havings.map((h, index) => {
528
541
  const prefix = index === 0 ? "" : h.boolean.toUpperCase() + " ";
529
- return prefix + h.sql;
542
+ if (h.sql) {
543
+ return prefix + h.sql;
544
+ }
545
+ const value = this.parameterize ? this.addBinding(h.value) : this.grammar.escape(h.value);
546
+ return prefix + `${this.grammar.wrap(h.column)} ${h.operator} ${value}`;
530
547
  });
531
548
  return `HAVING ${clauses.join(" ")}`;
532
549
  }
@@ -565,7 +582,11 @@ export class Builder {
565
582
  return sql.replace(/\s+/g, " ").trim();
566
583
  }
567
584
  async get() {
568
- const results = await this.connection.query(this.toSql());
585
+ this.bindings = [];
586
+ this.parameterize = true;
587
+ const sql = this.toSql();
588
+ this.parameterize = false;
589
+ const results = await this.connection.query(sql, this.bindings);
569
590
  const rows = Array.from(results);
570
591
  if (this.model) {
571
592
  const models = rows.map((row) => {
@@ -697,14 +718,34 @@ export class Builder {
697
718
  }
698
719
  });
699
720
  }
700
- async *cursor() {
701
- let offset = 0;
702
- while (true) {
703
- const items = await this.clone().offset(offset).limit(1).get();
704
- if (items.length === 0)
705
- break;
706
- yield items[0];
707
- offset++;
721
+ async *cursor(keyset) {
722
+ const model = this.model;
723
+ const primaryKey = model ? model.primaryKey || "id" : "id";
724
+ const orderColumn = this.orders[0]?.column || primaryKey;
725
+ const orderDirection = this.orders[0]?.direction || "asc";
726
+ const builder = this.clone();
727
+ builder.orders = [{ column: orderColumn, direction: orderDirection }];
728
+ builder.offsetValue = undefined;
729
+ if (keyset) {
730
+ const op = orderDirection === "asc" ? ">" : "<";
731
+ builder.wheres.push({
732
+ type: "basic",
733
+ column: orderColumn,
734
+ operator: op,
735
+ value: keyset[orderColumn],
736
+ boolean: "and",
737
+ scope: undefined,
738
+ });
739
+ }
740
+ const items = await builder.limit(1).get();
741
+ if (items.length === 0)
742
+ return;
743
+ yield items[0];
744
+ const nextKeyset = items[0] && typeof items[0] === "object"
745
+ ? { [orderColumn]: items[0][orderColumn] }
746
+ : undefined;
747
+ if (nextKeyset) {
748
+ yield* this.cursor(nextKeyset);
708
749
  }
709
750
  }
710
751
  async *lazy(count = 1000) {
@@ -726,11 +767,15 @@ export class Builder {
726
767
  if (records.length === 0)
727
768
  return;
728
769
  const columns = Object.keys(records[0]);
770
+ const bindings = [];
729
771
  const values = records.map((record) => {
730
- return `(${columns.map((col) => this.grammar.escape(record[col])).join(", ")})`;
772
+ return `(${columns.map((col) => {
773
+ bindings.push(record[col]);
774
+ return this.grammar.placeholder(bindings.length);
775
+ }).join(", ")})`;
731
776
  });
732
777
  const sql = `INSERT INTO ${this.grammar.wrap(this.tableName)} (${columns.map((c) => this.grammar.wrap(c)).join(", ")}) VALUES ${values.join(", ")}`;
733
- return await this.connection.run(sql);
778
+ return await this.connection.run(sql, bindings);
734
779
  }
735
780
  async insertGetId(data, idColumn = "id") {
736
781
  const result = await this.insert(data);
@@ -741,43 +786,65 @@ export class Builder {
741
786
  if (records.length === 0)
742
787
  return;
743
788
  const columns = Object.keys(records[0]);
789
+ const bindings = [];
744
790
  const values = records.map((record) => {
745
- return `(${columns.map((col) => this.grammar.escape(record[col])).join(", ")})`;
791
+ return `(${columns.map((col) => {
792
+ bindings.push(record[col]);
793
+ return this.grammar.placeholder(bindings.length);
794
+ }).join(", ")})`;
746
795
  });
747
796
  const sql = this.grammar.compileInsertOrIgnore(this.grammar.wrap(this.tableName), columns, values);
748
- return await this.connection.run(sql);
797
+ return await this.connection.run(sql, bindings);
749
798
  }
750
799
  async upsert(data, uniqueBy, updateColumns) {
751
800
  const records = Array.isArray(data) ? data : [data];
752
801
  if (records.length === 0)
753
802
  return;
754
803
  const columns = Object.keys(records[0]);
804
+ const bindings = [];
755
805
  const values = records.map((record) => {
756
- return `(${columns.map((col) => this.grammar.escape(record[col])).join(", ")})`;
806
+ return `(${columns.map((col) => {
807
+ bindings.push(record[col]);
808
+ return this.grammar.placeholder(bindings.length);
809
+ }).join(", ")})`;
757
810
  });
758
811
  const uniqueCols = Array.isArray(uniqueBy) ? uniqueBy : [uniqueBy];
759
812
  const updateCols = updateColumns ?? columns.filter((c) => !uniqueCols.includes(c));
760
813
  const sql = this.grammar.compileUpsert(this.grammar.wrap(this.tableName), columns, values, uniqueCols, updateCols);
761
- return await this.connection.run(sql);
814
+ return await this.connection.run(sql, bindings);
762
815
  }
763
816
  async update(data) {
764
- const sets = Object.entries(data)
765
- .map(([key, value]) => `${this.grammar.wrap(key)} = ${this.grammar.escape(value)}`)
766
- .join(", ");
767
- const sql = this.grammar.compileUpdate(this.grammar.wrap(this.tableName), sets.split(", "), this.compileWheres(), this.updateJoins);
768
- return await this.connection.run(sql);
817
+ this.bindings = [];
818
+ this.parameterize = true;
819
+ const sets = Object.entries(data).map(([key, value]) => {
820
+ this.bindings.push(value);
821
+ return `${this.grammar.wrap(key)} = ${this.grammar.placeholder(this.bindings.length)}`;
822
+ });
823
+ const whereSql = this.compileWheres();
824
+ this.parameterize = false;
825
+ const sql = this.grammar.compileUpdate(this.grammar.wrap(this.tableName), sets, whereSql, this.updateJoins);
826
+ return await this.connection.run(sql, this.bindings);
769
827
  }
770
828
  async delete() {
771
- const sql = this.grammar.compileDelete(this.grammar.wrap(this.tableName), this.compileWheres(), this.updateJoins, this.limitValue);
772
- return await this.connection.run(sql);
829
+ this.bindings = [];
830
+ this.parameterize = true;
831
+ const whereSql = this.compileWheres();
832
+ this.parameterize = false;
833
+ const sql = this.grammar.compileDelete(this.grammar.wrap(this.tableName), whereSql, this.updateJoins, this.limitValue);
834
+ return await this.connection.run(sql, this.bindings);
773
835
  }
774
836
  async increment(column, amount = 1, extra = {}) {
837
+ this.bindings = [];
838
+ this.parameterize = true;
775
839
  const sets = [`${this.grammar.wrap(column)} = ${this.grammar.wrap(column)} + ${amount}`];
776
840
  for (const [key, value] of Object.entries(extra)) {
777
- sets.push(`${this.grammar.wrap(key)} = ${this.grammar.escape(value)}`);
841
+ this.bindings.push(value);
842
+ sets.push(`${this.grammar.wrap(key)} = ${this.grammar.placeholder(this.bindings.length)}`);
778
843
  }
779
- const sql = `UPDATE ${this.grammar.wrap(this.tableName)} SET ${sets.join(", ")} ${this.compileWheres()}`;
780
- return await this.connection.run(sql.trim());
844
+ const whereSql = this.compileWheres();
845
+ this.parameterize = false;
846
+ const sql = `UPDATE ${this.grammar.wrap(this.tableName)} SET ${sets.join(", ")} ${whereSql}`;
847
+ return await this.connection.run(sql.trim(), this.bindings);
781
848
  }
782
849
  async decrement(column, amount = 1, extra = {}) {
783
850
  return this.increment(column, -amount, extra);
@@ -819,8 +886,11 @@ export class Builder {
819
886
  throw new Error("dd() called — execution halted.");
820
887
  }
821
888
  async explain() {
889
+ this.bindings = [];
890
+ this.parameterize = true;
822
891
  const sql = this.grammar.compileExplain(this.toSql());
823
- const results = await this.connection.query(sql);
892
+ this.parameterize = false;
893
+ const results = await this.connection.query(sql, this.bindings);
824
894
  return Array.from(results);
825
895
  }
826
896
  take(count) {
@@ -1,6 +1,7 @@
1
1
  export declare abstract class Grammar {
2
2
  abstract wrap(value: string): string;
3
3
  wrapArray(values: string[]): string[];
4
+ abstract placeholder(index: number): string;
4
5
  escape(value: any): string;
5
6
  abstract compileRandomOrder(): string;
6
7
  compileOffset(offset: number, _limit?: number): string;
@@ -1,6 +1,7 @@
1
1
  import { Grammar } from "./Grammar.js";
2
2
  export declare class MySqlGrammar extends Grammar {
3
3
  wrap(value: string): string;
4
+ placeholder(_index: number): string;
4
5
  compileRandomOrder(): string;
5
6
  compileDateWhere(type: string, column: string, operator: string, value: any): string;
6
7
  compileInsertOrIgnore(table: string, columns: string[], values: string[]): string;
@@ -12,6 +12,9 @@ export class MySqlGrammar extends Grammar {
12
12
  return value;
13
13
  return `\`${value}\``;
14
14
  }
15
+ placeholder(_index) {
16
+ return "?";
17
+ }
15
18
  compileRandomOrder() {
16
19
  return "ORDER BY RAND()";
17
20
  }
@@ -1,6 +1,7 @@
1
1
  import { Grammar } from "./Grammar.js";
2
2
  export declare class PostgresGrammar extends Grammar {
3
3
  wrap(value: string): string;
4
+ placeholder(index: number): string;
4
5
  compileRandomOrder(): string;
5
6
  compileDateWhere(type: string, column: string, operator: string, value: any): string;
6
7
  compileInsertOrIgnore(table: string, columns: string[], values: string[]): string;
@@ -12,6 +12,9 @@ export class PostgresGrammar extends Grammar {
12
12
  return value;
13
13
  return `"${value}"`;
14
14
  }
15
+ placeholder(index) {
16
+ return `$${index}`;
17
+ }
15
18
  compileRandomOrder() {
16
19
  return "ORDER BY RANDOM()";
17
20
  }
@@ -1,6 +1,7 @@
1
1
  import { Grammar } from "./Grammar.js";
2
2
  export declare class SQLiteGrammar extends Grammar {
3
3
  wrap(value: string): string;
4
+ placeholder(_index: number): string;
4
5
  compileRandomOrder(): string;
5
6
  compileOffset(offset: number, limit?: number): string;
6
7
  compileDateWhere(type: string, column: string, operator: string, value: any): string;
@@ -12,6 +12,9 @@ export class SQLiteGrammar extends Grammar {
12
12
  return value;
13
13
  return `"${value}"`;
14
14
  }
15
+ placeholder(_index) {
16
+ return "?";
17
+ }
15
18
  compileRandomOrder() {
16
19
  return "ORDER BY RANDOM()";
17
20
  }
@@ -41,7 +41,10 @@ export interface OrderClause {
41
41
  direction: "asc" | "desc";
42
42
  }
43
43
  export interface HavingClause {
44
- sql: string;
44
+ column?: string;
45
+ operator?: string;
46
+ value?: any;
47
+ sql?: string;
45
48
  boolean: "and" | "or";
46
49
  }
47
50
  export interface UnionClause {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bunnykit/orm",
3
- "version": "0.1.14",
3
+ "version": "0.1.16",
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",