ReviewOS

also looking at this

stacks/bun-query-builder

feat(paginate): add a spelling that cannot be transposed

#1130
Merged glennmichael123 wants to merge fix/1092-paginate-options into main
3 files +208 -5
packages/bun-query-builder/src/orm.tsmodified+20-3
Changes to packages/bun-query-builder/src/orm.ts
@@ -26,6 +26,7 @@
2626
2727import { Database, type SQLQueryBindings } from 'bun:sqlite'
2828import type { FactoryFaker } from './faker-compat'
29import type { PaginateOptions } from './client'
2930import type { SupportedDialect } from './types'
3031import type { RelationCardinality } from './type-inference'
3132import { isNumericPlanType, normalizeAttributeType, sqliteAffinityFor } from './column-types'
@@ -3116,7 +3117,7 @@ class ModelQueryBuilder<
31163117 }
31173118 }
31183119
3119 async paginate(page = 1, perPage = 15): Promise<{
3120 async paginate(page: number | PaginateOptions = 1, perPage = 15): Promise<{
31203121 data: ModelInstance<TDef, TSelected>[]
31213122 total: number
31223123 page: number
@@ -3126,7 +3127,17 @@ class ModelQueryBuilder<
31263127 isEmpty: boolean
31273128 from: number | null
31283129 to: number | null
3130 meta: { perPage: number, page: number, total: number, lastPage: number }
31293131 }> {
3132 // Options form: `paginate({ page, perPage })`. This API is (page, perPage)
3133 // and the query builder's is (perPage, page); both arguments are numbers,
3134 // so transposing them returns a different page rather than raising
3135 // anything. Naming them cannot be got wrong. See #1092.
3136 if (typeof page === 'object' && page !== null) {
3137 const options = page
3138 perPage = options.perPage ?? 15
3139 page = options.page ?? 1
3140 }
31303141 const total = await this.count()
31313142 const lastPage = Math.ceil(total / perPage)
31323143 this._limit = perPage
@@ -3142,6 +3153,12 @@ class ModelQueryBuilder<
31423153 isEmpty: data.length === 0,
31433154 from: data.length > 0 ? (page - 1) * perPage + 1 : null,
31443155 to: data.length > 0 ? (page - 1) * perPage + data.length : null,
3156 // Mirrors the query builder's result, which nests under `meta`. The flat
3157 // fields above stay exactly as they were, so this is additive: code
3158 // written against either shape now works against both, and a helper that
3159 // reads `result.meta.total` stops depending on which API produced it.
3160 // See #1092.
3161 meta: { perPage, page, total, lastPage },
31453162 }
31463163 }
31473164
@@ -3319,7 +3336,7 @@ export type ModelStatic<TDef extends ModelDefinition> = StaticWhereOverloads<TDe
33193336 count: () => Promise<number>
33203337 exists: () => Promise<boolean>
33213338 doesntExist: () => Promise<boolean>
3322 paginate: (page?: number, perPage?: number) => Promise<{
3339 paginate: (page?: number | PaginateOptions, perPage?: number) => Promise<{
33233340 data: ModelRecord<TDef>[]
33243341 total: number
33253342 page: number
@@ -3457,7 +3474,7 @@ function createModelInternal<const TDef extends ModelDefinition>(definition: TDe
34573474 count: () => new ModelQueryBuilder<TDef>(definition).count(),
34583475 exists: () => new ModelQueryBuilder<TDef>(definition).exists(),
34593476 doesntExist: () => new ModelQueryBuilder<TDef>(definition).doesntExist(),
3460 paginate: (page?: number, perPage?: number) => new ModelQueryBuilder<TDef>(definition).paginate(page, perPage),
3477 paginate: (page?: number | PaginateOptions, perPage?: number) => new ModelQueryBuilder<TDef>(definition).paginate(page as any, perPage),
34613478
34623479 whereBetween<K extends Cols>(column: K, range: [min: K extends keyof Attrs ? Attrs[K] : unknown, max: K extends keyof Attrs ? Attrs[K] : unknown]) {
34633480 return new ModelQueryBuilder<TDef>(definition).whereBetween(column, range as any)