bun-query-builder 0.2.44 → 0.2.46

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/bin/cli.js CHANGED
@@ -140,7 +140,7 @@ class BrowserQueryBuilder {
140
140
  return this;
141
141
  }
142
142
  andWhere(column, operatorOrValue, value) {
143
- return this.where(column, operatorOrValue, value);
143
+ return value === undefined ? this.where(column, operatorOrValue) : this.where(column, operatorOrValue, value);
144
144
  }
145
145
  whereNull(column) {
146
146
  return this.where(column, "is", null);
@@ -11862,6 +11862,12 @@ import { Database } from "bun:sqlite";
11862
11862
  import { mkdirSync as mkdirSync5 } from "fs";
11863
11863
  import { dirname as dirname5 } from "path";
11864
11864
  import process21 from "process";
11865
+ function toBindings(params) {
11866
+ return params;
11867
+ }
11868
+ function asEmbeddedValue(value) {
11869
+ return value !== null && typeof value === "object" ? value : null;
11870
+ }
11865
11871
  function bindNumberedPlaceholders(sql, params) {
11866
11872
  if (params.length === 0 || !/\$\d/.test(sql))
11867
11873
  return { sql, params };
@@ -12050,11 +12056,13 @@ function createSQLiteSQL(filename) {
12050
12056
  const params = [];
12051
12057
  for (let i = 0;i < values.length; i++) {
12052
12058
  const value = values[i];
12053
- if (value && typeof value === "object" && typeof value.sql === "string" && Array.isArray(value.values)) {
12054
- sql += value.sql + (strings[i + 1] || "");
12055
- params.push(...value.values);
12056
- } else if (value && typeof value === "object" && (value.__raw || value.raw)) {
12057
- const rawValue = value.__raw ? value.value : typeof value.raw === "string" ? value.raw : value.raw();
12059
+ const embedded = asEmbeddedValue(value);
12060
+ if (embedded && typeof embedded.sql === "string" && Array.isArray(embedded.values)) {
12061
+ sql += embedded.sql + (strings[i + 1] || "");
12062
+ params.push(...embedded.values);
12063
+ } else if (embedded && (embedded.__raw || embedded.raw)) {
12064
+ const raw = embedded.raw;
12065
+ const rawValue = embedded.__raw ? embedded.value : typeof raw === "string" ? raw : raw();
12058
12066
  sql += rawValue + (strings[i + 1] || "");
12059
12067
  } else if (Array.isArray(value)) {
12060
12068
  const placeholders = value.map(() => "?").join(", ");
@@ -12072,10 +12080,10 @@ function createSQLiteSQL(filename) {
12072
12080
  try {
12073
12081
  const trimmed = sql.trim().toUpperCase();
12074
12082
  if (trimmed.startsWith("SELECT") || trimmed.startsWith("PRAGMA") || /\bRETURNING\b/.test(trimmed)) {
12075
- const result = wrapper.query(sql, params);
12083
+ const result = wrapper.query(sql, toBindings(params));
12076
12084
  return Promise.resolve(result);
12077
12085
  } else {
12078
- const result = wrapper.run(sql, params);
12086
+ const result = wrapper.run(sql, toBindings(params));
12079
12087
  return Promise.resolve(result);
12080
12088
  }
12081
12089
  } catch (error) {
@@ -12102,10 +12110,10 @@ function createSQLiteSQL(filename) {
12102
12110
  try {
12103
12111
  const trimmed = sql.trim().toUpperCase();
12104
12112
  if (trimmed.startsWith("SELECT") || trimmed.startsWith("PRAGMA") || /\bRETURNING\b/.test(trimmed)) {
12105
- const result = wrapper.query(sql, params);
12113
+ const result = wrapper.query(sql, toBindings(params));
12106
12114
  return Promise.resolve(result);
12107
12115
  } else {
12108
- const result = wrapper.run(sql, params);
12116
+ const result = wrapper.run(sql, toBindings(params));
12109
12117
  return Promise.resolve(result);
12110
12118
  }
12111
12119
  } catch (error) {
@@ -12128,7 +12136,7 @@ function createSQLiteSQL(filename) {
12128
12136
  };
12129
12137
  sqlFunction.query = (sql, params) => {
12130
12138
  try {
12131
- const result = wrapper.query(sql, params || []);
12139
+ const result = wrapper.query(sql, toBindings(params || []));
12132
12140
  return Promise.resolve(result);
12133
12141
  } catch (error) {
12134
12142
  return Promise.reject(error);
@@ -14936,6 +14944,10 @@ function createQueryBuilder(state) {
14936
14944
  built = null;
14937
14945
  return this;
14938
14946
  },
14947
+ $call(callback) {
14948
+ callback(this);
14949
+ return this;
14950
+ },
14939
14951
  union(other) {
14940
14952
  appendSetOp("UNION", other);
14941
14953
  return this;
@@ -26784,6 +26796,18 @@ class ModelQueryBuilder {
26784
26796
  return "";
26785
26797
  return this._trashed === "only" ? `${SOFT_DELETE_COLUMN} IS NOT NULL` : `${SOFT_DELETE_COLUMN} IS NULL`;
26786
26798
  }
26799
+ assertNoUnappliedClauses(method) {
26800
+ const ignored = [];
26801
+ if (this._limit !== undefined)
26802
+ ignored.push("limit()");
26803
+ if (this._offset !== undefined)
26804
+ ignored.push("offset()");
26805
+ if (this._orderBy.length > 0)
26806
+ ignored.push("orderBy()");
26807
+ if (ignored.length > 0) {
26808
+ throw new TypeError(`[orm] ${method}() cannot apply ${ignored.join(", ")} \u2014 an UPDATE/DELETE takes no ORDER BY or LIMIT on every supported dialect. ` + `Previously these were ignored, so the statement affected every matching row. Narrow it with where() instead.`);
26809
+ }
26810
+ }
26787
26811
  composeWhere(params) {
26788
26812
  const userClause = this._wheres.length > 0 ? this.buildWhereClauses(params) : "";
26789
26813
  const sd = this.softDeleteClause();
@@ -27033,6 +27057,7 @@ class ModelQueryBuilder {
27033
27057
  }
27034
27058
  async increment(column, amount = 1) {
27035
27059
  assertValidIdentifier(column, "increment(column)");
27060
+ this.assertNoUnappliedClauses("increment");
27036
27061
  const exec = getExecutor();
27037
27062
  const params = [amount];
27038
27063
  const incCol = sqlColumn(column);
@@ -27041,9 +27066,9 @@ class ModelQueryBuilder {
27041
27066
  sql2 += `, updated_at = ?`;
27042
27067
  params.push(formatNow());
27043
27068
  }
27044
- if (this._wheres.length > 0) {
27045
- const clauses = this.buildWhereClauses(params);
27046
- sql2 += ` WHERE ${clauses}`;
27069
+ const whereBody = this.composeWhere(params);
27070
+ if (whereBody) {
27071
+ sql2 += ` WHERE ${whereBody}`;
27047
27072
  }
27048
27073
  return (await exec.run(sql2, params)).changes;
27049
27074
  }
@@ -27142,15 +27167,18 @@ class ModelQueryBuilder {
27142
27167
  return Number(await this.aggregate("SUM", column) ?? 0) || 0;
27143
27168
  }
27144
27169
  async delete() {
27170
+ this.assertNoUnappliedClauses("delete");
27145
27171
  const exec = getExecutor();
27146
27172
  const params = [];
27147
27173
  let sql2 = `DELETE FROM ${this._definition.table}`;
27148
- if (this._wheres.length > 0) {
27149
- sql2 += ` WHERE ${this.buildWhereClauses(params)}`;
27174
+ const whereBody = this.composeWhere(params);
27175
+ if (whereBody) {
27176
+ sql2 += ` WHERE ${whereBody}`;
27150
27177
  }
27151
27178
  return (await exec.run(sql2, params)).changes;
27152
27179
  }
27153
27180
  async update(data) {
27181
+ this.assertNoUnappliedClauses("update");
27154
27182
  const exec = getExecutor();
27155
27183
  const entries = Object.entries(data);
27156
27184
  const sets = entries.map(([k2]) => `${sqlColumn(k2)} = ?`).join(", ");
@@ -27159,8 +27187,9 @@ class ModelQueryBuilder {
27159
27187
  params.push(formatNow());
27160
27188
  }
27161
27189
  let sql2 = `UPDATE ${this._definition.table} SET ${sets}${timestampsEnabled(this._definition) ? ", updated_at = ?" : ""}`;
27162
- if (this._wheres.length > 0) {
27163
- sql2 += ` WHERE ${this.buildWhereClauses(params)}`;
27190
+ const whereBody = this.composeWhere(params);
27191
+ if (whereBody) {
27192
+ sql2 += ` WHERE ${whereBody}`;
27164
27193
  }
27165
27194
  return (await exec.run(sql2, params)).changes;
27166
27195
  }
@@ -32515,7 +32544,7 @@ function getPrefix() {
32515
32544
  }
32516
32545
  var prefix = getPrefix();
32517
32546
  // package.json
32518
- var version2 = "0.2.44";
32547
+ var version2 = "0.2.46";
32519
32548
 
32520
32549
  // bin/cli.ts
32521
32550
  init_actions();
package/dist/browser.d.ts CHANGED
@@ -282,13 +282,19 @@ export declare class BrowserQueryError extends Error {
282
282
  export declare class BrowserQueryBuilder<T = any> {
283
283
  constructor(table: string);
284
284
  select(...columns: string[]): this;
285
- where(column: string, operatorOrValue: WhereOperator | any, value?: any): this;
286
- orWhere(column: string, operatorOrValue: WhereOperator | any, value?: any): this;
287
- andWhere(column: string, operatorOrValue: WhereOperator | any, value?: any): this;
285
+ where(column: string, value: unknown): this;
286
+ where(column: string, operator: WhereOperator, value: unknown): this;
287
+ where(column: string, operatorOrValue: unknown, value?: unknown): this;
288
+ orWhere(column: string, value: unknown): this;
289
+ orWhere(column: string, operator: WhereOperator, value: unknown): this;
290
+ orWhere(column: string, operatorOrValue: unknown, value?: unknown): this;
291
+ andWhere(column: string, value: unknown): this;
292
+ andWhere(column: string, operator: WhereOperator, value: unknown): this;
293
+ andWhere(column: string, operatorOrValue: unknown, value?: unknown): this;
288
294
  whereNull(column: string): this;
289
295
  whereNotNull(column: string): this;
290
- whereIn(column: string, values: any[]): this;
291
- whereNotIn(column: string, values: any[]): this;
296
+ whereIn(column: string, values: readonly unknown[]): this;
297
+ whereNotIn(column: string, values: readonly unknown[]): this;
292
298
  orderBy(column: string, direction?: 'asc' | 'desc'): this;
293
299
  orderByDesc(column: string): this;
294
300
  latest(column?: string): this;
package/dist/client.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { config } from './config';
2
2
  import { resetConnection } from './db';
3
- import type { DatabaseSchema } from './schema';
3
+ import type { AnyDatabaseSchema } from './schema';
4
4
  import type { DriverConnection } from './db';
5
5
  import type { QueryHooks, SupportedDialect } from './types';
6
6
  import type { SchemaMeta } from './meta';
@@ -50,7 +50,7 @@ export declare function raw(strings: TemplateStringsArray | string, ...values: u
50
50
  */
51
51
  export declare function escapeStringLiteral(value: string, dialect?: SupportedDialect): string;
52
52
  // eslint-disable-next-line pickier/no-unused-vars
53
- export declare function createQueryBuilder<DB extends DatabaseSchema<any>>(state?: Partial<InternalState>): QueryBuilder<DB>;
53
+ export declare function createQueryBuilder<DB extends AnyDatabaseSchema>(state?: Partial<InternalState>): QueryBuilder<DB>;
54
54
  /**
55
55
  * # `clearQueryCache()`
56
56
  *
@@ -80,7 +80,7 @@ declare interface RawExpression {
80
80
  export declare interface WhereRaw {
81
81
  raw: SqlFragment
82
82
  }
83
- export declare interface BaseSelectQueryBuilder<DB extends DatabaseSchema<any>, TTable extends keyof DB & string, TSelected, TJoined extends string = TTable,> {
83
+ export declare interface BaseSelectQueryBuilder<DB extends AnyDatabaseSchema, TTable extends keyof DB & string, TSelected, TJoined extends string = TTable,> {
84
84
  distinct: () => SelectQueryBuilder<DB, TTable, TSelected, TJoined>
85
85
  distinctOn: (...columns: (keyof DB[TTable]['columns'] & string | string)[]) => SelectQueryBuilder<DB, TTable, TSelected, TJoined>
86
86
  selectRaw: (fragment: SqlFragment) => SelectQueryBuilder<DB, TTable, TSelected, TJoined>
@@ -108,7 +108,7 @@ export declare interface BaseSelectQueryBuilder<DB extends DatabaseSchema<any>,
108
108
  orWhereGroup: (callback: (builder: SelectQueryBuilder<DB, TTable, TSelected, TJoined>) => unknown) => SelectQueryBuilder<DB, TTable, TSelected, TJoined>
109
109
  orWhereNull: (column: keyof DB[TTable]['columns'] & string) => SelectQueryBuilder<DB, TTable, TSelected, TJoined>
110
110
  orWhereNotNull: (column: keyof DB[TTable]['columns'] & string) => SelectQueryBuilder<DB, TTable, TSelected, TJoined>
111
- orWhereBetween: (column: keyof DB[TTable]['columns'] & string, start: any, end: any) => SelectQueryBuilder<DB, TTable, TSelected, TJoined>
111
+ orWhereBetween: <K extends keyof DB[TTable]['columns'] & string>(column: K, start: DB[TTable]['columns'][K], end: DB[TTable]['columns'][K]) => SelectQueryBuilder<DB, TTable, TSelected, TJoined>
112
112
  orWhereExists: (subquery: { toSQL: () => any }) => SelectQueryBuilder<DB, TTable, TSelected, TJoined>
113
113
  orWhereRaw: (fragment: SqlFragment) => SelectQueryBuilder<DB, TTable, TSelected, TJoined>
114
114
  whereAny: <K extends keyof DB[TTable]['columns'] & string>(columns: K[], op: WhereOperator, value: DB[TTable]['columns'][K]) => SelectQueryBuilder<DB, TTable, TSelected, TJoined>
@@ -180,6 +180,7 @@ export declare interface BaseSelectQueryBuilder<DB extends DatabaseSchema<any>,
180
180
  }
181
181
  selectAll?: () => SelectQueryBuilder<DB, TTable, DB[TTable]['columns'], TJoined>
182
182
  orderByRaw: (fragment: SqlFragment) => SelectQueryBuilder<DB, TTable, TSelected, TJoined>
183
+ $call: (callback: (query: SelectQueryBuilder<DB, TTable, TSelected, TJoined>) => unknown) => SelectQueryBuilder<DB, TTable, TSelected, TJoined>
183
184
  union: (other: { toSQL: () => string }) => SelectQueryBuilder<DB, TTable, TSelected, TJoined>
184
185
  unionAll: (other: { toSQL: () => string }) => SelectQueryBuilder<DB, TTable, TSelected, TJoined>
185
186
  forPage: (page: number, perPage: number) => SelectQueryBuilder<DB, TTable, TSelected, TJoined>
@@ -265,7 +266,7 @@ export declare interface BaseSelectQueryBuilder<DB extends DatabaseSchema<any>,
265
266
  denseRank?: (alias?: string, partitionBy?: string | string[], orderBy?: [string, 'asc' | 'desc'][]) => SelectQueryBuilder<DB, TTable, TSelected, TJoined>
266
267
  rank?: (alias?: string, partitionBy?: string | string[], orderBy?: [string, 'asc' | 'desc'][]) => SelectQueryBuilder<DB, TTable, TSelected, TJoined>
267
268
  }
268
- export declare interface InsertQueryBuilder<DB extends DatabaseSchema<any>, TTable extends keyof DB & string> {
269
+ export declare interface InsertQueryBuilder<DB extends AnyDatabaseSchema, TTable extends keyof DB & string> {
269
270
  values: (data: Partial<DB[TTable]['columns']> | Partial<DB[TTable]['columns']>[]) => InsertQueryBuilder<DB, TTable>
270
271
  returning: <K extends keyof DB[TTable]['columns'] & string>(...cols: K[]) => SelectQueryBuilder<DB, TTable, Pick<DB[TTable]['columns'], K>>
271
272
  toSQL: () => string
@@ -274,7 +275,7 @@ export declare interface InsertQueryBuilder<DB extends DatabaseSchema<any>, TTab
274
275
  executeTakeFirst: () => Promise<DB[TTable]['columns'] | undefined>
275
276
  executeTakeFirstOrThrow: () => Promise<DB[TTable]['columns']>
276
277
  }
277
- export declare interface UpdateQueryBuilder<DB extends DatabaseSchema<any>, TTable extends keyof DB & string> {
278
+ export declare interface UpdateQueryBuilder<DB extends AnyDatabaseSchema, TTable extends keyof DB & string> {
278
279
  set: (values: Partial<{ [K in keyof DB[TTable]['columns']]: DB[TTable]['columns'][K] | SqlFragment }>) => UpdateQueryBuilder<DB, TTable>
279
280
  where: <K extends keyof DB[TTable]['columns'] & string>(
280
281
  expr: WhereExpression<DB[TTable]['columns']> | K | SqlFragment,
@@ -290,7 +291,7 @@ export declare interface UpdateQueryBuilder<DB extends DatabaseSchema<any>, TTab
290
291
  executeTakeFirst: () => Promise<{ numUpdatedRows?: number }>
291
292
  executeTakeFirstOrThrow: () => Promise<{ numUpdatedRows: number }>
292
293
  }
293
- export declare interface DeleteQueryBuilder<DB extends DatabaseSchema<any>, TTable extends keyof DB & string> {
294
+ export declare interface DeleteQueryBuilder<DB extends AnyDatabaseSchema, TTable extends keyof DB & string> {
294
295
  where: <K extends keyof DB[TTable]['columns'] & string>(
295
296
  expr: WhereExpression<DB[TTable]['columns']> | K,
296
297
  op?: WhereOperator,
@@ -305,13 +306,13 @@ export declare interface DeleteQueryBuilder<DB extends DatabaseSchema<any>, TTab
305
306
  executeTakeFirst: () => Promise<{ numDeletedRows?: number }>
306
307
  executeTakeFirstOrThrow: () => Promise<{ numDeletedRows: number }>
307
308
  }
308
- export declare interface TableQueryBuilder<DB extends DatabaseSchema<any>, TTable extends keyof DB & string> {
309
+ export declare interface TableQueryBuilder<DB extends AnyDatabaseSchema, TTable extends keyof DB & string> {
309
310
  insert: (data: Partial<DB[TTable]['columns']> | Partial<DB[TTable]['columns']>[]) => InsertQueryBuilder<DB, TTable>
310
311
  update: (values: Partial<DB[TTable]['columns']>) => UpdateQueryBuilder<DB, TTable>
311
312
  delete: () => DeleteQueryBuilder<DB, TTable>
312
313
  select: <K extends keyof DB[TTable]['columns'] & string>(...columns: K[]) => SelectQueryBuilder<DB, TTable, Pick<DB[TTable]['columns'], K>>
313
314
  }
314
- export declare interface QueryBuilder<DB extends DatabaseSchema<any>> {
315
+ export declare interface QueryBuilder<DB extends AnyDatabaseSchema> {
315
316
  select: {
316
317
  <TTable extends keyof DB & string, K extends keyof DB[TTable]['columns'] & string>(
317
318
  table: TTable,
@@ -434,25 +435,25 @@ export declare interface QueryBuilder<DB extends DatabaseSchema<any>> {
434
435
  getRelationshipTarget?: (table: string, relationName: string) => string | null
435
436
  }
436
437
  declare interface InternalState {
437
- sql: any
438
+ sql: DriverConnection
438
439
  meta?: SchemaMeta
439
- schema?: any
440
+ schema?: AnyDatabaseSchema
440
441
  txDefaults?: TransactionOptions
441
442
  hooks?: QueryHooks
442
443
  inTransaction?: boolean
443
444
  }
444
445
  declare interface TxBackoff { baseMs?: number, maxMs?: number, factor?: number, jitter?: boolean }
445
- declare interface TxLoggerEvent { type: 'start' | 'retry' | 'commit' | 'rollback' | 'error', attempt: number, error?: any, durationMs?: number }
446
+ declare interface TxLoggerEvent { type: 'start' | 'retry' | 'commit' | 'rollback' | 'error', attempt: number, error?: unknown, durationMs?: number }
446
447
  export declare interface TransactionOptions {
447
448
  retries?: number
448
449
  isolation?: TransactionIsolation
449
- onRetry?: (attempt: number, error: any) => void
450
+ onRetry?: (attempt: number, error: unknown) => void
450
451
  afterCommit?: () => void
451
452
  sqlStates?: string[]
452
453
  backoff?: TxBackoff
453
454
  logger?: (event: TxLoggerEvent) => void
454
455
  readOnly?: boolean
455
- onRollback?: (error: any) => void
456
+ onRollback?: (error: unknown) => void
456
457
  afterRollback?: () => void
457
458
  }
458
459
  // Where condition helpers
@@ -497,10 +498,10 @@ export type SortDirection = 'asc' | 'desc';
497
498
  *
498
499
  * Helper type extracting a string union of column names for a given table.
499
500
  */
500
- export type ColumnName<DB extends DatabaseSchema<any>, TTable extends keyof DB & string> = keyof DB[TTable]['columns'] & string;
501
+ export type ColumnName<DB extends AnyDatabaseSchema, TTable extends keyof DB & string> = keyof DB[TTable]['columns'] & string;
501
502
  // Named row alias to improve IDE hover readability
502
- export type SelectedRow<DB extends DatabaseSchema<any>, _TTable extends keyof DB & string, TSelected,> = Readonly<TSelected>;
503
- declare type PrimaryKeyValue<DB extends DatabaseSchema<any>, TTable extends keyof DB & string> = DB[TTable]['primaryKey'] extends keyof DB[TTable]['columns']
503
+ export type SelectedRow<DB extends AnyDatabaseSchema, _TTable extends keyof DB & string, TSelected,> = Readonly<TSelected>;
504
+ declare type PrimaryKeyValue<DB extends AnyDatabaseSchema, TTable extends keyof DB & string> = DB[TTable]['primaryKey'] extends keyof DB[TTable]['columns']
504
505
  ? DB[TTable]['columns'][DB[TTable]['primaryKey']]
505
506
  : unknown;
506
507
  declare type NumericColumnName<Columns> = {
@@ -508,7 +509,7 @@ declare type NumericColumnName<Columns> = {
508
509
  ? K
509
510
  : Exclude<Columns[K], null | undefined> extends number ? K : never
510
511
  }[keyof Columns & string];
511
- declare type JoinColumn<DB extends DatabaseSchema<any>, TTables extends string> = TTables extends any
512
+ declare type JoinColumn<DB extends AnyDatabaseSchema, TTables extends string> = TTables extends any
512
513
  ? `${TTables}.${keyof DB[TTables]['columns'] & string}`
513
514
  : never;
514
515
  /**
@@ -521,20 +522,20 @@ declare type JoinColumn<DB extends DatabaseSchema<any>, TTables extends string>
521
522
  * untyped schemas keep compiling. A table that declares ZERO relations
522
523
  * yields `never` — every relation name is rejected.
523
524
  */
524
- export type TableRelationName<DB extends DatabaseSchema<any>, TTable extends keyof DB & string> = DB[TTable] extends { relations?: infer R }
525
+ export type TableRelationName<DB extends AnyDatabaseSchema, TTable extends keyof DB & string> = DB[TTable] extends { relations?: infer R }
525
526
  ? unknown extends R ? string : keyof NonNullable<R> & string
526
527
  : string;
527
- declare type RelatedTableName<DB extends DatabaseSchema<any>, TTable extends keyof DB & string, TRelation extends TableRelationName<DB, TTable>,> = DB[TTable] extends { relations?: infer Relations }
528
+ declare type RelatedTableName<DB extends AnyDatabaseSchema, TTable extends keyof DB & string, TRelation extends TableRelationName<DB, TTable>,> = DB[TTable] extends { relations?: infer Relations }
528
529
  ? TRelation extends keyof NonNullable<Relations>
529
530
  ? Extract<NonNullable<Relations>[TRelation], keyof DB & string>
530
531
  : keyof DB & string
531
532
  : keyof DB & string;
532
- declare type RelationQueryBuilder<DB extends DatabaseSchema<any>, TTable extends keyof DB & string, TRelation extends TableRelationName<DB, TTable>,> = SelectQueryBuilder<
533
+ declare type RelationQueryBuilder<DB extends AnyDatabaseSchema, TTable extends keyof DB & string, TRelation extends TableRelationName<DB, TTable>,> = SelectQueryBuilder<
533
534
  DB,
534
535
  RelatedTableName<DB, TTable, TRelation>,
535
536
  DB[RelatedTableName<DB, TTable, TRelation>]['columns']
536
537
  >;
537
- declare type RelationConstraintRecord<DB extends DatabaseSchema<any>, TTable extends keyof DB & string> = {
538
+ declare type RelationConstraintRecord<DB extends AnyDatabaseSchema, TTable extends keyof DB & string> = {
538
539
  [R in TableRelationName<DB, TTable>]: Partial<Record<R, (qb: RelationQueryBuilder<DB, TTable, R>) => unknown>>
539
540
  }[TableRelationName<DB, TTable>];
540
541
  /**
@@ -546,7 +547,7 @@ declare type RelationConstraintRecord<DB extends DatabaseSchema<any>, TTable ext
546
547
  * relations accepts nothing (the bare `Partial<Record<never, ...>>` would be
547
548
  * `{}`, which strings are assignable to — hence the explicit never guard).
548
549
  */
549
- export type WithRelationArg<DB extends DatabaseSchema<any>, TTable extends keyof DB & string> = [TableRelationName<DB, TTable>] extends [never]
550
+ export type WithRelationArg<DB extends AnyDatabaseSchema, TTable extends keyof DB & string> = [TableRelationName<DB, TTable>] extends [never]
550
551
  ? never
551
552
  :
552
553
  | TableRelationName<DB, TTable>
@@ -559,7 +560,7 @@ declare type SnakeToPascal<S extends string> = S extends `${infer H}_${infer T}`
559
560
  // Typed SQL builder (type-level only). We piggy-back on the runtime builder but
560
561
  // thread a phantom TSql string through method signatures so hovers can show the
561
562
  // composed SQL at compile-time for common operations.
562
- declare type _TypedDynamicWhereMethods<DB extends DatabaseSchema<any>, TTable extends keyof DB & string, TSelected, TJoined extends string, TSql extends string,> = {
563
+ declare type _TypedDynamicWhereMethods<DB extends AnyDatabaseSchema, TTable extends keyof DB & string, TSelected, TJoined extends string, TSql extends string,> = {
563
564
  [K in keyof DB[TTable]['columns'] & string as `where${SnakeToPascal<K>}`]: (
564
565
  value: DB[TTable]['columns'][K],
565
566
  ) => TypedSelectQueryBuilder<DB, TTable, TSelected, TJoined, `${TSql} WHERE ${K} = ?`>
@@ -577,7 +578,7 @@ declare type _TypedDynamicWhereMethods<DB extends DatabaseSchema<any>, TTable ex
577
578
  // untyped variant (returning a plain SelectQueryBuilder) would win overload
578
579
  // resolution, silently downgrading `toSQL()` from the composed literal SQL
579
580
  // type back to `string` after any dynamic-where call.
580
- export type TypedSelectQueryBuilder<DB extends DatabaseSchema<any>, TTable extends keyof DB & string, TSelected, TJoined extends string = TTable, TSql extends string = `SELECT * FROM ${TTable}`,> = Omit<
581
+ export type TypedSelectQueryBuilder<DB extends AnyDatabaseSchema, TTable extends keyof DB & string, TSelected, TJoined extends string = TTable, TSql extends string = `SELECT * FROM ${TTable}`,> = Omit<
581
582
  BaseSelectQueryBuilder<DB, TTable, TSelected, TJoined>,
582
583
  'toSQL' | 'where' | 'andWhere' | 'orWhere' | 'orderBy' | 'limit'
583
584
  > & _TypedDynamicWhereMethods<DB, TTable, TSelected, TJoined, TSql>
@@ -616,16 +617,16 @@ export type TypedSelectQueryBuilder<DB extends DatabaseSchema<any>, TTable exten
616
617
  ) => TypedSelectQueryBuilder<DB, TTable, TSelected, TJoined, `${TSql} ORDER BY ${C} ${D}`>
617
618
  limit: <N extends number>(n: N) => TypedSelectQueryBuilder<DB, TTable, TSelected, TJoined, `${TSql} LIMIT ${N}`>
618
619
  }
619
- declare type DynamicWhereMethods<DB extends DatabaseSchema<any>, TTable extends keyof DB & string, TSelected, TJoined extends string = TTable,> = {
620
+ declare type DynamicWhereMethods<DB extends AnyDatabaseSchema, TTable extends keyof DB & string, TSelected, TJoined extends string = TTable,> = {
620
621
  [K in keyof DB[TTable]['columns'] & string as `where${SnakeToPascal<K>}`]: (value: DB[TTable]['columns'][K]) => SelectQueryBuilder<DB, TTable, TSelected, TJoined>
621
622
  } & {
622
623
  [K in keyof DB[TTable]['columns'] & string as `orWhere${SnakeToPascal<K>}`]: (value: DB[TTable]['columns'][K]) => SelectQueryBuilder<DB, TTable, TSelected, TJoined>
623
624
  } & {
624
625
  [K in keyof DB[TTable]['columns'] & string as `andWhere${SnakeToPascal<K>}`]: (value: DB[TTable]['columns'][K]) => SelectQueryBuilder<DB, TTable, TSelected, TJoined>
625
626
  }
626
- export type SelectQueryBuilder<DB extends DatabaseSchema<any>, TTable extends keyof DB & string, TSelected, TJoined extends string = TTable,> = BaseSelectQueryBuilder<DB, TTable, TSelected, TJoined> & DynamicWhereMethods<DB, TTable, TSelected, TJoined>;
627
+ export type SelectQueryBuilder<DB extends AnyDatabaseSchema, TTable extends keyof DB & string, TSelected, TJoined extends string = TTable,> = BaseSelectQueryBuilder<DB, TTable, TSelected, TJoined> & DynamicWhereMethods<DB, TTable, TSelected, TJoined>;
627
628
  // Typed INSERT builder to expose a structured SQL literal in hovers
628
- export type TypedInsertQueryBuilder<DB extends DatabaseSchema<any>, TTable extends keyof DB & string, TSql extends string = `INSERT INTO ${TTable}`,> = Omit<InsertQueryBuilder<DB, TTable>, 'toSQL' | 'values' | 'returning'> & {
629
+ export type TypedInsertQueryBuilder<DB extends AnyDatabaseSchema, TTable extends keyof DB & string, TSql extends string = `INSERT INTO ${TTable}`,> = Omit<InsertQueryBuilder<DB, TTable>, 'toSQL' | 'values' | 'returning'> & {
629
630
  toSQL: () => TSql
630
631
  values: (
631
632
  data: Partial<DB[TTable]['columns']> | Partial<DB[TTable]['columns']>[],
@@ -642,8 +643,8 @@ export type TypedInsertQueryBuilder<DB extends DatabaseSchema<any>, TTable exten
642
643
  }
643
644
  declare type TransactionIsolation = 'read committed' | 'repeatable read' | 'serializable';
644
645
  declare class QueryCache {
645
- get(key: string): any | null;
646
- set(key: string, data: any, ttlMs: number): void;
646
+ get<T = unknown>(key: string): T | null;
647
+ set(key: string, data: unknown, ttlMs: number): void;
647
648
  clear(): void;
648
649
  setMaxSize(size: number): void;
649
650
  }
package/dist/db.d.ts CHANGED
@@ -1,6 +1,20 @@
1
1
  import { Database } from 'bun:sqlite';
2
2
  import { SQL } from 'bun';
3
+ import type { SQLQueryBindings } from 'bun:sqlite';
3
4
  import type { DatabaseConfig, PoolConfig, SupportedDialect } from './types';
5
+ /**
6
+ * The one place user values become driver bindings.
7
+ *
8
+ * Everything a caller interpolates arrives as `unknown` - that is honest, we
9
+ * do not know what it is. bun:sqlite accepts a narrower set. The conversion is
10
+ * unchecked because the driver itself is the validator: it throws a clear
11
+ * "can't bind" for anything it cannot take, and a check here would only
12
+ * duplicate that rule and drift from it. Kept as one named function so the
13
+ * boundary is visible, rather than an `any` at each of the six call sites.
14
+ */
15
+ export declare function toBindings(params: readonly unknown[]): SQLQueryBindings[];
16
+ /** A template value seen as a possible fragment; null when it is a plain bind param. */
17
+ export declare function asEmbeddedValue(value: unknown): EmbeddedValue | null;
4
18
  /**
5
19
  * SQLite wrapper that provides a SQL-like tagged template literal interface
6
20
  * using bun:sqlite's Database class for better compiled binary support.
@@ -29,7 +43,7 @@ import type { DatabaseConfig, PoolConfig, SupportedDialect } from './types';
29
43
  * numbered placeholders is returned untouched, so `?` queries and Bun's own
30
44
  * tagged-template output are unaffected.
31
45
  */
32
- export declare function bindNumberedPlaceholders(sql: string, params: any[]): { sql: string, params: any[] };
46
+ export declare function bindNumberedPlaceholders<T>(sql: string, params: T[]): { sql: string, params: T[] };
33
47
  export declare function splitSqlStatements(sql: string): string[];
34
48
  /**
35
49
  * Creates a database connection string based on the configured dialect and database settings.
@@ -108,6 +122,22 @@ export declare const bunSql: SQL;
108
122
  * Both Bun's native `SQL` query and our `createSQLiteSQL` wrapper satisfy this.
109
123
  * See stacksjs/bun-query-builder#1044.
110
124
  */
125
+ /**
126
+ * What a value interpolated into a tagged template can turn out to be.
127
+ *
128
+ * Three things arrive here: a plain bind param, a nested query (carries `sql`
129
+ * plus a `values` ARRAY), and a raw-SQL marker (carries `__raw`/`raw` and no
130
+ * `values`). Naming the shape is what lets the branches below read properties
131
+ * without an `any` - and the order of those branches is load-bearing, see
132
+ * stacksjs/bun-query-builder#1084.
133
+ */
134
+ export declare interface EmbeddedValue {
135
+ sql?: unknown
136
+ values?: unknown
137
+ __raw?: unknown
138
+ raw?: unknown
139
+ value?: unknown
140
+ }
111
141
  export declare interface DriverQuery {
112
142
  execute: () => Promise<any>
113
143
  values?: () => any
@@ -124,10 +154,10 @@ export declare interface DriverQuery {
124
154
  * casts be dropped. See stacksjs/bun-query-builder#1044.
125
155
  */
126
156
  export declare interface DriverConnection {
127
- (strings: TemplateStringsArray, ...values: any[]): DriverQuery
157
+ (strings: TemplateStringsArray, ...values: unknown[]): DriverQuery
128
158
  (value: any): any
129
- unsafe: (sql: string, values?: any[]) => AwaitableDriverQuery
130
- query?: (sql: string, params?: any[]) => any
159
+ unsafe: (sql: string, values?: unknown[]) => AwaitableDriverQuery
160
+ query?: (sql: string, params?: unknown[]) => any
131
161
  close?: () => Promise<void> | void
132
162
  _prepareStatement?: (sql: string) => any
133
163
  [key: string]: any
@@ -136,8 +166,8 @@ export declare interface DriverConnection {
136
166
  export type AwaitableDriverQuery = DriverQuery & PromiseLike<any>;
137
167
  declare class SQLiteWrapper {
138
168
  constructor(filename: string);
139
- query(sql: string, params?: any[]): any[];
140
- run(sql: string, params?: any[]): any;
169
+ query(sql: string, params?: SQLQueryBindings[]): any[];
170
+ run(sql: string, params?: SQLQueryBindings[]): any;
141
171
  close(): void;
142
172
  get database(): Database;
143
173
  }
@@ -11,12 +11,31 @@ export declare function createDynamoDBItemBuilder<T = any>(options: DynamoDBQuer
11
11
  * Create a DynamoDB client with fluent query builder methods
12
12
  */
13
13
  export declare function createDynamoDBClient(options: DynamoDBQueryBuilderOptions): DynamoDBClientMethods;
14
+ /**
15
+ * The AWS SDK client, described by the one method this package uses.
16
+ *
17
+ * `@aws-sdk/client-dynamodb` is not a dependency (callers bring their own),
18
+ * so the shape is structural: anything with a `send` satisfies it, including
19
+ * the real `DynamoDBClient` and a test double.
20
+ */
21
+ export declare interface DynamoDBClientLike {
22
+ send: (command: unknown) => Promise<unknown>
23
+ [key: string]: unknown
24
+ }
25
+ /** What DynamoDB reports about the capacity a call consumed. */
26
+ export declare interface DynamoDBConsumedCapacity {
27
+ TableName?: string
28
+ CapacityUnits?: number
29
+ ReadCapacityUnits?: number
30
+ WriteCapacityUnits?: number
31
+ [key: string]: unknown
32
+ }
14
33
  /**
15
34
  * DynamoDB Query Builder Options
16
35
  */
17
36
  export declare interface DynamoDBQueryBuilderOptions {
18
37
  config: DynamoDBConfig
19
- client?: any
38
+ client?: DynamoDBClientLike
20
39
  }
21
40
  /**
22
41
  * Result type for DynamoDB operations
@@ -27,7 +46,7 @@ export declare interface DynamoDBResult<T = any> {
27
46
  count?: number
28
47
  scannedCount?: number
29
48
  lastEvaluatedKey?: Record<string, any>
30
- consumedCapacity?: any
49
+ consumedCapacity?: DynamoDBConsumedCapacity
31
50
  }
32
51
  /**
33
52
  * DynamoDB Client Factory
@@ -41,6 +60,26 @@ export declare interface DynamoDBClientMethods {
41
60
  registerEntity: (mapping: SingleTableEntityMapping) => void
42
61
  buildKey: (entityType: string, values: Record<string, any>) => { pk: string, sk: string }
43
62
  }
63
+ /**
64
+ * A value DynamoDB can store or compare against.
65
+ *
66
+ * DynamoDB's type system is small and closed - scalars, binary, lists and
67
+ * maps - so the attribute surface below says that rather than `any`. A value
68
+ * outside this set could never have round-tripped through the wire format.
69
+ */
70
+ export type DynamoDBValue = | string
71
+ | number
72
+ | boolean
73
+ | null
74
+ | Uint8Array
75
+ | DynamoDBValue[]
76
+ | { [key: string]: DynamoDBValue }
77
+ /** Every operator a DynamoDB filter expression accepts. */
78
+ export type DynamoDBFilterOperator = | DynamoDBComparisonOperator
79
+ | 'contains'
80
+ | 'attribute_exists'
81
+ | 'attribute_not_exists'
82
+ | 'IN';
44
83
  /**
45
84
  * DynamoDB Query Builder
46
85
  *
@@ -51,23 +90,23 @@ export declare class DynamoDBQueryBuilder<T = any> {
51
90
  table(tableName: string): this;
52
91
  index(indexName: string): this;
53
92
  entity(entityType: string): this;
54
- whereKey(attribute: string, operator: DynamoDBComparisonOperator, value: any): this;
55
- wherePartitionKey(attribute: string, value: any): this;
56
- whereSortKey(attribute: string, value: any): this;
93
+ whereKey(attribute: string, operator: DynamoDBComparisonOperator, value: DynamoDBValue): this;
94
+ wherePartitionKey(attribute: string, value: DynamoDBValue): this;
95
+ whereSortKey(attribute: string, value: DynamoDBValue): this;
57
96
  whereSortKeyBeginsWith(attribute: string, prefix: string): this;
58
- whereSortKeyBetween(attribute: string, start: any, end: any): this;
59
- where(attribute: string, operator: DynamoDBComparisonOperator | 'contains' | 'attribute_exists' | 'attribute_not_exists' | 'IN', value?: any): this;
60
- whereEquals(attribute: string, value: any): this;
61
- whereLessThan(attribute: string, value: any): this;
62
- whereLessThanOrEqual(attribute: string, value: any): this;
63
- whereGreaterThan(attribute: string, value: any): this;
64
- whereGreaterThanOrEqual(attribute: string, value: any): this;
65
- whereBetween(attribute: string, start: any, end: any): this;
97
+ whereSortKeyBetween(attribute: string, start: DynamoDBValue, end: DynamoDBValue): this;
98
+ where(attribute: string, operator: DynamoDBFilterOperator, value?: DynamoDBValue): this;
99
+ whereEquals(attribute: string, value: DynamoDBValue): this;
100
+ whereLessThan(attribute: string, value: DynamoDBValue): this;
101
+ whereLessThanOrEqual(attribute: string, value: DynamoDBValue): this;
102
+ whereGreaterThan(attribute: string, value: DynamoDBValue): this;
103
+ whereGreaterThanOrEqual(attribute: string, value: DynamoDBValue): this;
104
+ whereBetween(attribute: string, start: DynamoDBValue, end: DynamoDBValue): this;
66
105
  whereBeginsWith(attribute: string, prefix: string): this;
67
- whereContains(attribute: string, value: any): this;
106
+ whereContains(attribute: string, value: DynamoDBValue): this;
68
107
  whereExists(attribute: string): this;
69
108
  whereNotExists(attribute: string): this;
70
- whereIn(attribute: string, values: any[]): this;
109
+ whereIn(attribute: string, values: DynamoDBValue[]): this;
71
110
  select(...attributes: string[]): this;
72
111
  limit(count: number): this;
73
112
  ascending(): this;
@@ -93,11 +132,11 @@ export declare class DynamoDBItemBuilder<T = any> {
93
132
  table(tableName: string): this;
94
133
  key(key: Record<string, any>): this;
95
134
  item(data: Record<string, any>): this;
96
- set(attribute: string, value: any): this;
135
+ set(attribute: string, value: DynamoDBValue): this;
97
136
  setMany(values: Record<string, any>): this;
98
137
  remove(attribute: string): this;
99
- add(attribute: string, value: any): this;
100
- deleteFromSet(attribute: string, values: any): this;
138
+ add(attribute: string, value: DynamoDBValue): this;
139
+ deleteFromSet(attribute: string, values: DynamoDBValue): this;
101
140
  condition(expression: string): this;
102
141
  ifNotExists(attribute?: string): this;
103
142
  ifExists(attribute?: string): this;
@@ -1,4 +1,4 @@
1
- import type { DynamoDBQueryBuilderOptions } from './dynamodb-client';
1
+ import type { DynamoDBFilterOperator, DynamoDBQueryBuilderOptions, DynamoDBValue } from './dynamodb-client';
2
2
  import type { DynamoDBTableDefinition } from './drivers/dynamodb';
3
3
  /**
4
4
  * Create a single table manager
@@ -135,7 +135,7 @@ export declare class SingleTableQueryBuilder<T> {
135
135
  whereSortKey(skData: Record<string, any>): this;
136
136
  whereSortKeyBeginsWith(prefix: string): this;
137
137
  useIndex(indexName: string, pkData: Record<string, any>): this;
138
- where(attribute: string, operator: any, value?: any): this;
138
+ where(attribute: string, operator: DynamoDBFilterOperator, value?: DynamoDBValue): this;
139
139
  select(...attributes: string[]): this;
140
140
  limit(count: number): this;
141
141
  descending(): this;
@@ -1,6 +1,7 @@
1
1
  import { DynamoDBItemBuilder, DynamoDBQueryBuilder } from './dynamodb-client';
2
2
  import { SingleTableManager, SingleTableRepository } from './dynamodb-single-table';
3
- import type { DynamoDBDriver } from './drivers/dynamodb';
3
+ import type { DynamoDBClientLike } from './dynamodb-client';
4
+ import type { DynamoDBDriver, DynamoDBTableDefinition } from './drivers/dynamodb';
4
5
  /**
5
6
  * Resolve key pattern placeholders with actual values
6
7
  */
@@ -8,7 +9,7 @@ export declare function resolveKeyPattern(keyPatterns: KeyPatterns, data: Record
8
9
  /**
9
10
  * Parse models from configuration (stub implementation)
10
11
  */
11
- export declare function parseStacksModels(_config: any): Promise<{ models: Map<string, ParsedModel> }>;
12
+ export declare function parseStacksModels(_config: unknown): Promise<{ models: Map<string, ParsedModel> }>;
12
13
  /**
13
14
  * Convert model instance to DynamoDB item format (stub)
14
15
  */
@@ -63,7 +64,7 @@ export declare interface ParsedAttribute {
63
64
  nullable: boolean
64
65
  unique: boolean
65
66
  hidden: boolean
66
- defaultValue?: any
67
+ defaultValue?: unknown
67
68
  cast?: string
68
69
  dynamoDbType: 'S' | 'N' | 'B' | 'BOOL' | 'NULL' | 'M' | 'L' | 'SS' | 'NS' | 'BS'
69
70
  }
@@ -127,7 +128,7 @@ export declare interface StacksModel {
127
128
  nullable?: boolean
128
129
  unique?: boolean
129
130
  hidden?: boolean
130
- default?: any
131
+ default?: unknown
131
132
  cast?: string
132
133
  validation?: { rule?: string }
133
134
  }>
@@ -177,7 +178,7 @@ export declare interface DynamoDBToolingConfig {
177
178
  */
178
179
  export declare class DynamoDBToolingAdapter {
179
180
  constructor(config: DynamoDBToolingConfig);
180
- setClient(client: any): this;
181
+ setClient(client: DynamoDBClientLike): this;
181
182
  discoverModels(): Promise<ParsedModel[]>;
182
183
  registerModel(model: StacksModel): ParsedModel;
183
184
  registerParsedModel(parsed: ParsedModel): ParsedModel;
@@ -192,6 +193,6 @@ export declare class DynamoDBToolingAdapter {
192
193
  parseItem<T = any>(item: Record<string, any>): { type: string, data: T } | undefined;
193
194
  getDriver(): DynamoDBDriver;
194
195
  getSingleTableManager(): SingleTableManager;
195
- generateTableDefinition(): any;
196
+ generateTableDefinition(): DynamoDBTableDefinition;
196
197
  generateAccessPatterns(modelName: string): AccessPattern[];
197
198
  }
package/dist/meta.d.ts CHANGED
@@ -17,6 +17,6 @@ export declare interface SchemaMeta {
17
17
  morphToMany?: Record<string, string>
18
18
  morphedByMany?: Record<string, string>
19
19
  }>
20
- scopes?: Record<string, Record<string, (qb: any, value?: any) => any>>
20
+ scopes?: Record<string, Record<string, (qb: any, value?: unknown) => unknown>>
21
21
  models?: ModelRecord
22
22
  }
package/dist/orm.d.ts CHANGED
@@ -16,18 +16,9 @@ export type {
16
16
  };
17
17
  /** Drops the `createModel` models. Paired with `clearModelRegistry()`. */
18
18
  export declare function clearLocalModels(): void;
19
- /**
20
- * Extract an affected-row count from a Bun SQL driver result.
21
- *
22
- * Verified against live Postgres (Bun 1.3): a non-RETURNING `UPDATE`/`DELETE`
23
- * returns an empty array carrying `{ count: <affected>, affectedRows: null,
24
- * command: 'UPDATE' }`. So `count` must be checked BEFORE the `Array.length`
25
- * fallback (which would be 0) — see stacksjs/bun-query-builder#1032. MySQL
26
- * surfaces `affectedRows` instead.
27
- */
28
- export declare function extractChanges(res: any): number;
19
+ export declare function extractChanges(res: WriteResultLike | null | undefined): number;
29
20
  /** Extract a generated primary key from a Bun SQL driver result. */
30
- export declare function extractInsertId(res: any): number | bigint | null;
21
+ export declare function extractInsertId(res: WriteResultLike | null | undefined): number | bigint | null;
31
22
  export declare function configureOrm(options: { database?: string | Database; verbose?: boolean }): void;
32
23
  /**
33
24
  * Return the underlying `bun:sqlite` Database backing the model layer.
@@ -140,8 +131,8 @@ export declare interface ModelDefinition {
140
131
  readonly attributes: {
141
132
  readonly [key: string]: TypedAttribute<unknown>
142
133
  }
143
- readonly get?: Record<string, (attributes: any) => unknown>
144
- readonly set?: Record<string, (attributes: any) => unknown>
134
+ readonly get?: Record<string, (attributes: Record<string, unknown>) => unknown>
135
+ readonly set?: Record<string, (attributes: Record<string, unknown>) => unknown>
145
136
  readonly scopes?: Record<string, (value: unknown) => unknown>
146
137
  readonly indexes?: readonly object[]
147
138
  readonly dashboard?: { readonly enabled?: boolean; readonly highlight?: boolean | number }
@@ -162,6 +153,33 @@ declare interface OrmExecutor {
162
153
  insert: (sql: string, params: unknown[], primaryKey: string) => Promise<RunResult>
163
154
  readonly sqliteDb?: Database
164
155
  }
156
+ /**
157
+ * Extract an affected-row count from a Bun SQL driver result.
158
+ *
159
+ * Verified against live Postgres (Bun 1.3): a non-RETURNING `UPDATE`/`DELETE`
160
+ * returns an empty array carrying `{ count: <affected>, affectedRows: null,
161
+ * command: 'UPDATE' }`. So `count` must be checked BEFORE the `Array.length`
162
+ * fallback (which would be 0) — see stacksjs/bun-query-builder#1032. MySQL
163
+ * surfaces `affectedRows` instead.
164
+ */
165
+ /**
166
+ * What a driver hands back after a write.
167
+ *
168
+ * Each one reports the affected count under its own name - and Postgres
169
+ * returns an empty array carrying `count`, so the shape is genuinely a union
170
+ * rather than one thing. Naming the fields beats `any`: a caller reading this
171
+ * type learns which drivers report what.
172
+ */
173
+ export declare interface WriteResultLike {
174
+ affectedRows?: number
175
+ count?: number
176
+ changes?: number
177
+ lastInsertRowid?: number | bigint
178
+ insertId?: number | bigint
179
+ command?: string
180
+ rows?: unknown[]
181
+ [key: string]: unknown
182
+ }
165
183
  /**
166
184
  * Resolve a relation from its name and the parent model's definition.
167
185
  * Uses the model registry to find the related model's definition.
package/dist/schema.d.ts CHANGED
@@ -171,15 +171,46 @@ export declare interface ModelOptions extends Base {
171
171
  morphToMany?: MorphToMany<ModelNames>
172
172
  morphedByMany?: MorphedByMany<ModelNames>
173
173
  scopes?: {
174
- [key: string]: (value: any) => any
174
+ [key: string]: (value: any) => unknown
175
175
  }
176
176
  get?: {
177
- [key: string]: (value: any) => any
177
+ [key: string]: (value: any) => unknown
178
178
  }
179
179
  set?: {
180
- [key: string]: (value: any) => any
180
+ [key: string]: (value: any) => unknown
181
181
  }
182
182
  }
183
+ /**
184
+ * # `DatabaseSchema<Models>`
185
+ *
186
+ * Maps model definitions to a concrete database schema shape containing the
187
+ * table columns and primary key. This is the primary input for the query
188
+ * builder's type-safety.
189
+ *
190
+ * The `relations` field is type-level only (phantom): `buildDatabaseSchema`
191
+ * never materializes it at runtime. It maps relation names to related table
192
+ * names so builder methods like `.with()` can narrow their accepted relation
193
+ * names per table.
194
+ *
195
+ * @example
196
+ * ```ts
197
+ * const models = defineModels({ User, Post })
198
+ * type Schema = DatabaseSchema<typeof models>
199
+ * ```
200
+ */
201
+ /**
202
+ * Any database schema, described by its shape rather than by `any`.
203
+ *
204
+ * Generic constraints throughout the client read `DB extends DatabaseSchema<any>`,
205
+ * which says "a schema of anything" and drags `any` into every published
206
+ * signature that mentions one. This says the same thing accurately: a schema
207
+ * is tables, each with columns, a primary key, and optionally relations.
208
+ */
209
+ export declare interface AnySchemaTable {
210
+ columns: Record<string, unknown>
211
+ primaryKey?: unknown
212
+ relations?: Record<string, string> | undefined
213
+ }
183
214
  /**
184
215
  * # `ValidatorMessage`
185
216
  *
@@ -316,24 +347,7 @@ declare type RelatedTableName<MRecord extends ModelRecord, ModelName> = ModelNam
316
347
  export type InferTableRelations<M, MRecord extends ModelRecord> = {
317
348
  [K in keyof ModelRelationsRecord<UnwrapModelDefinition<M>> & string]: RelatedTableName<MRecord, ModelRelationsRecord<UnwrapModelDefinition<M>>[K]>
318
349
  }
319
- /**
320
- * # `DatabaseSchema<Models>`
321
- *
322
- * Maps model definitions to a concrete database schema shape containing the
323
- * table columns and primary key. This is the primary input for the query
324
- * builder's type-safety.
325
- *
326
- * The `relations` field is type-level only (phantom): `buildDatabaseSchema`
327
- * never materializes it at runtime. It maps relation names to related table
328
- * names so builder methods like `.with()` can narrow their accepted relation
329
- * names per table.
330
- *
331
- * @example
332
- * ```ts
333
- * const models = defineModels({ User, Post })
334
- * type Schema = DatabaseSchema<typeof models>
335
- * ```
336
- */
350
+ export type AnyDatabaseSchema = Record<string, AnySchemaTable>;
337
351
  export type DatabaseSchema<MRecord extends ModelRecord> = {
338
352
  [MName in keyof MRecord & string as InferTableName<UnwrapModelDefinition<MRecord[MName]>>]: {
339
353
  columns: InferAttributes<UnwrapModelDefinition<MRecord[MName]>>
package/dist/seeder.d.ts CHANGED
@@ -17,12 +17,22 @@ export declare interface RunSeederOptions {
17
17
  class?: string
18
18
  verbose?: boolean
19
19
  }
20
+ /**
21
+ * The query builder handed to a seeder.
22
+ *
23
+ * A seeder is written against the app's own schema, which this base class
24
+ * cannot know, so the handle stays `any` deliberately - narrowing it here
25
+ * would reject every real seeder's typed table access. Named rather than
26
+ * inline so the reason travels with it.
27
+ */
28
+ // eslint-disable-next-line ts/no-explicit-any
29
+ export type SeederQueryBuilder = any;
20
30
  /**
21
31
  * Base seeder class that all seeders should extend.
22
32
  * Provides access to query builder and faker for generating test data.
23
33
  */
24
34
  export declare abstract class Seeder {
25
- abstract run(qb: any): Promise<void>;
35
+ abstract run(qb: SeederQueryBuilder): Promise<void>;
26
36
  get order(): number;
27
37
  get description(): string | undefined;
28
38
  }
@@ -32,7 +32,7 @@ export declare function renderWhereTerms(terms: readonly WhereTerm[]): string;
32
32
  * happened to come back empty — the same shape of silent, widening-or-narrowing
33
33
  * failure as the `IN ()` bug itself.
34
34
  */
35
- export declare function renderInPredicate(column: string, values: any[], negated: boolean, placeholders: string): string;
35
+ export declare function renderInPredicate(column: string, values: readonly unknown[], negated: boolean, placeholders: string): string;
36
36
  /**
37
37
  * Rewrite `?` placeholders into the numbered form Postgres requires.
38
38
  *
@@ -139,7 +139,7 @@ class BrowserQueryBuilder {
139
139
  return this;
140
140
  }
141
141
  andWhere(column, operatorOrValue, value) {
142
- return this.where(column, operatorOrValue, value);
142
+ return value === undefined ? this.where(column, operatorOrValue) : this.where(column, operatorOrValue, value);
143
143
  }
144
144
  whereNull(column) {
145
145
  return this.where(column, "is", null);
package/dist/src/index.js CHANGED
@@ -139,7 +139,7 @@ class BrowserQueryBuilder {
139
139
  return this;
140
140
  }
141
141
  andWhere(column, operatorOrValue, value) {
142
- return this.where(column, operatorOrValue, value);
142
+ return value === undefined ? this.where(column, operatorOrValue) : this.where(column, operatorOrValue, value);
143
143
  }
144
144
  whereNull(column) {
145
145
  return this.where(column, "is", null);
@@ -11861,6 +11861,12 @@ import { Database } from "bun:sqlite";
11861
11861
  import { mkdirSync as mkdirSync5 } from "fs";
11862
11862
  import { dirname as dirname5 } from "path";
11863
11863
  import process21 from "process";
11864
+ function toBindings(params) {
11865
+ return params;
11866
+ }
11867
+ function asEmbeddedValue(value) {
11868
+ return value !== null && typeof value === "object" ? value : null;
11869
+ }
11864
11870
  function bindNumberedPlaceholders(sql, params) {
11865
11871
  if (params.length === 0 || !/\$\d/.test(sql))
11866
11872
  return { sql, params };
@@ -12049,11 +12055,13 @@ function createSQLiteSQL(filename) {
12049
12055
  const params = [];
12050
12056
  for (let i = 0;i < values.length; i++) {
12051
12057
  const value = values[i];
12052
- if (value && typeof value === "object" && typeof value.sql === "string" && Array.isArray(value.values)) {
12053
- sql += value.sql + (strings[i + 1] || "");
12054
- params.push(...value.values);
12055
- } else if (value && typeof value === "object" && (value.__raw || value.raw)) {
12056
- const rawValue = value.__raw ? value.value : typeof value.raw === "string" ? value.raw : value.raw();
12058
+ const embedded = asEmbeddedValue(value);
12059
+ if (embedded && typeof embedded.sql === "string" && Array.isArray(embedded.values)) {
12060
+ sql += embedded.sql + (strings[i + 1] || "");
12061
+ params.push(...embedded.values);
12062
+ } else if (embedded && (embedded.__raw || embedded.raw)) {
12063
+ const raw = embedded.raw;
12064
+ const rawValue = embedded.__raw ? embedded.value : typeof raw === "string" ? raw : raw();
12057
12065
  sql += rawValue + (strings[i + 1] || "");
12058
12066
  } else if (Array.isArray(value)) {
12059
12067
  const placeholders = value.map(() => "?").join(", ");
@@ -12071,10 +12079,10 @@ function createSQLiteSQL(filename) {
12071
12079
  try {
12072
12080
  const trimmed = sql.trim().toUpperCase();
12073
12081
  if (trimmed.startsWith("SELECT") || trimmed.startsWith("PRAGMA") || /\bRETURNING\b/.test(trimmed)) {
12074
- const result = wrapper.query(sql, params);
12082
+ const result = wrapper.query(sql, toBindings(params));
12075
12083
  return Promise.resolve(result);
12076
12084
  } else {
12077
- const result = wrapper.run(sql, params);
12085
+ const result = wrapper.run(sql, toBindings(params));
12078
12086
  return Promise.resolve(result);
12079
12087
  }
12080
12088
  } catch (error) {
@@ -12101,10 +12109,10 @@ function createSQLiteSQL(filename) {
12101
12109
  try {
12102
12110
  const trimmed = sql.trim().toUpperCase();
12103
12111
  if (trimmed.startsWith("SELECT") || trimmed.startsWith("PRAGMA") || /\bRETURNING\b/.test(trimmed)) {
12104
- const result = wrapper.query(sql, params);
12112
+ const result = wrapper.query(sql, toBindings(params));
12105
12113
  return Promise.resolve(result);
12106
12114
  } else {
12107
- const result = wrapper.run(sql, params);
12115
+ const result = wrapper.run(sql, toBindings(params));
12108
12116
  return Promise.resolve(result);
12109
12117
  }
12110
12118
  } catch (error) {
@@ -12127,7 +12135,7 @@ function createSQLiteSQL(filename) {
12127
12135
  };
12128
12136
  sqlFunction.query = (sql, params) => {
12129
12137
  try {
12130
- const result = wrapper.query(sql, params || []);
12138
+ const result = wrapper.query(sql, toBindings(params || []));
12131
12139
  return Promise.resolve(result);
12132
12140
  } catch (error) {
12133
12141
  return Promise.reject(error);
@@ -14935,6 +14943,10 @@ function createQueryBuilder(state) {
14935
14943
  built = null;
14936
14944
  return this;
14937
14945
  },
14946
+ $call(callback) {
14947
+ callback(this);
14948
+ return this;
14949
+ },
14938
14950
  union(other) {
14939
14951
  appendSetOp("UNION", other);
14940
14952
  return this;
@@ -26783,6 +26795,18 @@ class ModelQueryBuilder {
26783
26795
  return "";
26784
26796
  return this._trashed === "only" ? `${SOFT_DELETE_COLUMN} IS NOT NULL` : `${SOFT_DELETE_COLUMN} IS NULL`;
26785
26797
  }
26798
+ assertNoUnappliedClauses(method) {
26799
+ const ignored = [];
26800
+ if (this._limit !== undefined)
26801
+ ignored.push("limit()");
26802
+ if (this._offset !== undefined)
26803
+ ignored.push("offset()");
26804
+ if (this._orderBy.length > 0)
26805
+ ignored.push("orderBy()");
26806
+ if (ignored.length > 0) {
26807
+ throw new TypeError(`[orm] ${method}() cannot apply ${ignored.join(", ")} \u2014 an UPDATE/DELETE takes no ORDER BY or LIMIT on every supported dialect. ` + `Previously these were ignored, so the statement affected every matching row. Narrow it with where() instead.`);
26808
+ }
26809
+ }
26786
26810
  composeWhere(params) {
26787
26811
  const userClause = this._wheres.length > 0 ? this.buildWhereClauses(params) : "";
26788
26812
  const sd = this.softDeleteClause();
@@ -27032,6 +27056,7 @@ class ModelQueryBuilder {
27032
27056
  }
27033
27057
  async increment(column, amount = 1) {
27034
27058
  assertValidIdentifier(column, "increment(column)");
27059
+ this.assertNoUnappliedClauses("increment");
27035
27060
  const exec = getExecutor();
27036
27061
  const params = [amount];
27037
27062
  const incCol = sqlColumn(column);
@@ -27040,9 +27065,9 @@ class ModelQueryBuilder {
27040
27065
  sql2 += `, updated_at = ?`;
27041
27066
  params.push(formatNow());
27042
27067
  }
27043
- if (this._wheres.length > 0) {
27044
- const clauses = this.buildWhereClauses(params);
27045
- sql2 += ` WHERE ${clauses}`;
27068
+ const whereBody = this.composeWhere(params);
27069
+ if (whereBody) {
27070
+ sql2 += ` WHERE ${whereBody}`;
27046
27071
  }
27047
27072
  return (await exec.run(sql2, params)).changes;
27048
27073
  }
@@ -27141,15 +27166,18 @@ class ModelQueryBuilder {
27141
27166
  return Number(await this.aggregate("SUM", column) ?? 0) || 0;
27142
27167
  }
27143
27168
  async delete() {
27169
+ this.assertNoUnappliedClauses("delete");
27144
27170
  const exec = getExecutor();
27145
27171
  const params = [];
27146
27172
  let sql2 = `DELETE FROM ${this._definition.table}`;
27147
- if (this._wheres.length > 0) {
27148
- sql2 += ` WHERE ${this.buildWhereClauses(params)}`;
27173
+ const whereBody = this.composeWhere(params);
27174
+ if (whereBody) {
27175
+ sql2 += ` WHERE ${whereBody}`;
27149
27176
  }
27150
27177
  return (await exec.run(sql2, params)).changes;
27151
27178
  }
27152
27179
  async update(data) {
27180
+ this.assertNoUnappliedClauses("update");
27153
27181
  const exec = getExecutor();
27154
27182
  const entries = Object.entries(data);
27155
27183
  const sets = entries.map(([k2]) => `${sqlColumn(k2)} = ?`).join(", ");
@@ -27158,8 +27186,9 @@ class ModelQueryBuilder {
27158
27186
  params.push(formatNow());
27159
27187
  }
27160
27188
  let sql2 = `UPDATE ${this._definition.table} SET ${sets}${timestampsEnabled(this._definition) ? ", updated_at = ?" : ""}`;
27161
- if (this._wheres.length > 0) {
27162
- sql2 += ` WHERE ${this.buildWhereClauses(params)}`;
27189
+ const whereBody = this.composeWhere(params);
27190
+ if (whereBody) {
27191
+ sql2 += ` WHERE ${whereBody}`;
27163
27192
  }
27164
27193
  return (await exec.run(sql2, params)).changes;
27165
27194
  }
package/dist/types.d.ts CHANGED
@@ -134,25 +134,25 @@ export declare interface SqlConfig {
134
134
  sharedLockSyntax?: 'FOR SHARE' | 'LOCK IN SHARE MODE'
135
135
  jsonContainsMode?: 'operator' | 'function'
136
136
  }
137
- /**
138
- * # `QueryHooks`
139
- *
140
- * Optional lifecycle hooks around query execution. These are invoked for any
141
- * statement executed through the builder (select/insert/update/delete/raw).
142
- */
137
+ /** Common fields on every query lifecycle event. */
138
+ export declare interface QueryEvent {
139
+ sql: string
140
+ params?: QueryParam[]
141
+ kind?: QueryKind
142
+ }
143
143
  export declare interface QueryHooks {
144
- onQueryStart?: (event: { sql: string, params?: any[], kind?: 'select' | 'insert' | 'update' | 'delete' | 'raw' }) => void
145
- onQueryEnd?: (event: { sql: string, params?: any[], durationMs: number, rowCount?: number, kind?: 'select' | 'insert' | 'update' | 'delete' | 'raw' }) => void
146
- onQueryError?: (event: { sql: string, params?: any[], error: any, durationMs: number, kind?: 'select' | 'insert' | 'update' | 'delete' | 'raw' }) => void
147
- startSpan?: (event: { sql: string, params?: any[], kind?: 'select' | 'insert' | 'update' | 'delete' | 'raw' }) => { end: (error?: any) => void }
144
+ onQueryStart?: (event: QueryEvent) => void
145
+ onQueryEnd?: (event: QueryEvent & { durationMs: number, rowCount?: number }) => void
146
+ onQueryError?: (event: QueryEvent & { error: unknown, durationMs: number }) => void
147
+ startSpan?: (event: QueryEvent) => { end: (error?: unknown) => void }
148
148
  slowQueryThresholdMs?: number
149
- onSlowQuery?: (event: { sql: string, params?: any[], durationMs: number, kind?: 'select' | 'insert' | 'update' | 'delete' | 'raw' }) => void
150
- beforeCreate?: (event: { table: string, data: any }) => void | Promise<void>
151
- afterCreate?: (event: { table: string, data: any, result: any }) => void | Promise<void>
152
- beforeUpdate?: (event: { table: string, data: any, where?: any }) => void | Promise<void>
153
- afterUpdate?: (event: { table: string, data: any, where?: any, result: any }) => void | Promise<void>
154
- beforeDelete?: (event: { table: string, where?: any }) => void | Promise<void>
155
- afterDelete?: (event: { table: string, where?: any, result: any }) => void | Promise<void>
149
+ onSlowQuery?: (event: QueryEvent & { durationMs: number }) => void
150
+ beforeCreate?: (event: { table: string, data: QueryRecord }) => void | Promise<void>
151
+ afterCreate?: (event: { table: string, data: QueryRecord, result: unknown }) => void | Promise<void>
152
+ beforeUpdate?: (event: { table: string, data: QueryRecord, where?: QueryWhere }) => void | Promise<void>
153
+ afterUpdate?: (event: { table: string, data: QueryRecord, where?: QueryWhere, result: unknown }) => void | Promise<void>
154
+ beforeDelete?: (event: { table: string, where?: QueryWhere }) => void | Promise<void>
155
+ afterDelete?: (event: { table: string, where?: QueryWhere, result: unknown }) => void | Promise<void>
156
156
  }
157
157
  /**
158
158
  * # `FeatureToggles`
@@ -208,7 +208,7 @@ export declare interface BrowserConfig {
208
208
  onUnauthorized?: () => void
209
209
  headers?: Record<string, string>
210
210
  timeout?: number
211
- transformResponse?: <T>(response: any) => T
211
+ transformResponse?: <T>(response: unknown) => T
212
212
  transformRequest?: <T>(data: T) => any
213
213
  }
214
214
  /**
@@ -284,7 +284,7 @@ export declare interface GenerateMigrationResult {
284
284
  sql: string
285
285
  sqlStatements: string[]
286
286
  hasChanges: boolean
287
- plan: any
287
+ plan: import('./migrations').MigrationPlan
288
288
  operations?: import('./migrations').MigrationOperation[]
289
289
  }
290
290
  export declare interface UnsafeOptions {
@@ -309,6 +309,20 @@ export declare interface UnsafeOptions {
309
309
  * - 'browser': Browser-compatible mode that uses fetch() API calls instead of direct database connections
310
310
  */
311
311
  export type SupportedDialect = 'postgres' | 'mysql' | 'singlestore' | 'vitess' | 'sqlite' | 'browser';
312
+ /**
313
+ * # `QueryHooks`
314
+ *
315
+ * Optional lifecycle hooks around query execution. These are invoked for any
316
+ * statement executed through the builder (select/insert/update/delete/raw).
317
+ */
318
+ /** A bound statement parameter: whatever a driver will accept as a value. */
319
+ export type QueryParam = string | number | bigint | boolean | Date | Uint8Array | null;
320
+ /** The kind of statement a hook is reporting on. */
321
+ export type QueryKind = 'select' | 'insert' | 'update' | 'delete' | 'raw';
322
+ /** The row shape a hook sees. Columns are unknown until the caller narrows. */
323
+ export type QueryRecord = Record<string, unknown>;
324
+ /** A `where` description as the mutation hooks receive it. */
325
+ export type QueryWhere = Record<string, unknown>;
312
326
  /**
313
327
  * # `DeepPartial`
314
328
  *
@@ -340,7 +354,7 @@ export type SupportedDialect = 'postgres' | 'mysql' | 'singlestore' | 'vitess' |
340
354
  * signature is not a property. `keyof (() => void)` is `never`, so the
341
355
  * hook collapses to an empty object type: no longer callable, and loose
342
356
  * enough to accept `42`. It also erases type parameters, which would turn
343
- * `BrowserConfig.transformResponse` (`<T>(response: any) => T`) into a
357
+ * `BrowserConfig.transformResponse` (`<T>(response: unknown) => T`) into a
344
358
  * shape with no signature at all. A hook or a token getter is a leaf.
345
359
  *
346
360
  * 2. ARRAYS SECOND, because the merge replaces them rather than merging them.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "bun-query-builder",
3
3
  "type": "module",
4
- "version": "0.2.44",
4
+ "version": "0.2.46",
5
5
  "description": "A simple yet performant query builder for TypeScript. Built with Bun.",
6
6
  "author": "Chris Breuer <chris@stacksjs.org>",
7
7
  "license": "MIT",