@cleverbrush/knex-schema 0.0.0-beta-20260413145651

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.
@@ -0,0 +1,567 @@
1
+ import type { InferType } from '@cleverbrush/schema';
2
+ import { ObjectSchemaBuilder } from '@cleverbrush/schema';
3
+ import type { Knex } from 'knex';
4
+ import type { ColumnRef, InsertType, JoinManySpec, JoinOneSpec, WithJoinedMany, WithJoinedOne } from './types.js';
5
+ /**
6
+ * Type-safe, schema-driven query builder for Knex.
7
+ *
8
+ * `SchemaQueryBuilder` wraps a Knex.QueryBuilder and adds:
9
+ * - **Type-safe column references** — pass a property accessor (`t => t.name`)
10
+ * or a string property name; both are resolved to the correct SQL column
11
+ * through the schema's `hasColumnName()` metadata automatically.
12
+ * - **Eager loading without N+1** — {@link joinOne} and {@link joinMany} use
13
+ * PostgreSQL CTEs and `jsonb_agg` to load related rows in a single query.
14
+ * - **Bidirectional result mapping** — rows returned from Postgres (column
15
+ * names) are converted back to schema property names before being returned.
16
+ * - **Thenable protocol** — the builder itself is `await`-able so you can
17
+ * write `await query(db, Schema)` without calling {@link execute} explicitly.
18
+ *
19
+ * Create instances via the {@link query} factory function rather than
20
+ * calling the constructor directly.
21
+ *
22
+ * @typeParam TLocalSchema - The `ObjectSchemaBuilder` describing the main table.
23
+ * @typeParam TResult - The inferred row type, widened automatically as joins
24
+ * are registered via {@link joinOne} / {@link joinMany}.
25
+ *
26
+ * @example
27
+ * ```ts
28
+ * import knex from 'knex';
29
+ * import { query, object, string, number } from '@cleverbrush/knex-schema';
30
+ *
31
+ * const UserSchema = object({
32
+ * id: number(),
33
+ * name: string(),
34
+ * age: number().optional(),
35
+ * }).hasTableName('users');
36
+ *
37
+ * const db = knex({ client: 'pg', connection: process.env.DB_URL });
38
+ *
39
+ * // Fetch all users older than 18, ordered by name
40
+ * const adults = await query(db, UserSchema)
41
+ * .where(t => t.age, '>', 18)
42
+ * .orderBy(t => t.name);
43
+ * // adults: Array<{ id: number; name: string; age?: number }>
44
+ * ```
45
+ */
46
+ export declare class SchemaQueryBuilder<TLocalSchema extends ObjectSchemaBuilder<any, any, any, any, any, any, any>, TResult> {
47
+ #private;
48
+ /**
49
+ * @param knex - A configured Knex instance.
50
+ * @param localSchema - The `ObjectSchemaBuilder` for the primary table.
51
+ * Must have a table name set via `.hasTableName()`.
52
+ * @param baseQuery - Optional pre-configured `Knex.QueryBuilder` to use as
53
+ * the base query instead of the default `knex(tableName)`. Useful when you
54
+ * need custom joins, CTEs, or other Knex features not exposed by this API.
55
+ */
56
+ constructor(knex: Knex, localSchema: TLocalSchema, baseQuery?: Knex.QueryBuilder);
57
+ /**
58
+ * Eager-load a single related row (one-to-one / many-to-one relationship).
59
+ *
60
+ * The related rows are fetched using a single CTE + `jsonb_agg` — no N+1
61
+ * queries. The related object is attached to each result row under the
62
+ * field name specified by `spec.as`.
63
+ *
64
+ * @param spec - Join specification. Key fields:
65
+ * - `foreignSchema` — the `ObjectSchemaBuilder` of the related table.
66
+ * - `localColumn` — the local column that holds the foreign-table reference.
67
+ * - `foreignColumn` — the primary/unique key on the foreign table.
68
+ * - `as` — the property name to attach the related object under.
69
+ * - `required` — if `true` (default), rows without a matching related
70
+ * record are excluded (inner join); if `false`, they are included with
71
+ * `null` (left join).
72
+ * - `foreignQuery` — optional pre-filtered `Knex.QueryBuilder` for the
73
+ * foreign table (e.g. to apply scopes).
74
+ *
75
+ * @returns `this` (with an updated `TResult` type that includes the new field)
76
+ * for chaining.
77
+ *
78
+ * @example
79
+ * ```ts
80
+ * const PostSchema = object({
81
+ * id: number(),
82
+ * title: string(),
83
+ * authorId: number(),
84
+ * }).hasTableName('posts');
85
+ *
86
+ * const AuthorSchema = object({
87
+ * id: number(),
88
+ * name: string(),
89
+ * }).hasTableName('authors');
90
+ *
91
+ * const posts = await query(db, PostSchema)
92
+ * .joinOne({
93
+ * foreignSchema: AuthorSchema,
94
+ * localColumn: t => t.authorId,
95
+ * foreignColumn: t => t.id,
96
+ * as: 'author',
97
+ * });
98
+ * // posts[0].author.name — typed as string ✓
99
+ * ```
100
+ */
101
+ joinOne<TForeignSchema extends ObjectSchemaBuilder<any, any, any, any, any, any, any>, TFieldName extends string, TRequired extends boolean = true>(spec: JoinOneSpec<TLocalSchema, TForeignSchema, TFieldName, TRequired>): SchemaQueryBuilder<TLocalSchema, WithJoinedOne<TResult, TFieldName, TForeignSchema, TRequired>>;
102
+ /**
103
+ * Eager-load a collection of related rows (one-to-many relationship).
104
+ *
105
+ * Related rows are fetched via a single CTE + `jsonb_agg` query. The
106
+ * collection is attached to each result row under the field name specified
107
+ * by `spec.as`. Supports `limit`, `offset`, and `orderBy` per-parent
108
+ * using a `row_number()` window function to avoid fetching the full
109
+ * relation before slicing.
110
+ *
111
+ * @param spec - Join specification. Key fields:
112
+ * - `foreignSchema` — the `ObjectSchemaBuilder` of the related table.
113
+ * - `localColumn` — the primary/unique key on the local table.
114
+ * - `foreignColumn` — the column on the foreign table that references `localColumn`.
115
+ * - `as` — the property name to attach the array under.
116
+ * - `limit` / `offset` — optional pagination per parent row.
117
+ * - `orderBy` — optional `{ column, direction }` for the sub-collection.
118
+ * - `foreignQuery` — optional pre-filtered `Knex.QueryBuilder`.
119
+ *
120
+ * @returns `this` (with an updated `TResult` type that includes the new field)
121
+ * for chaining.
122
+ *
123
+ * @example
124
+ * ```ts
125
+ * const UserSchema = object({
126
+ * id: number(),
127
+ * name: string(),
128
+ * }).hasTableName('users');
129
+ *
130
+ * const PostSchema = object({
131
+ * id: number(),
132
+ * title: string(),
133
+ * authorId: number(),
134
+ * }).hasTableName('posts');
135
+ *
136
+ * const users = await query(db, UserSchema)
137
+ * .joinMany({
138
+ * foreignSchema: PostSchema,
139
+ * localColumn: t => t.id,
140
+ * foreignColumn: t => t.authorId,
141
+ * as: 'posts',
142
+ * limit: 5,
143
+ * orderBy: { column: t => t.id, direction: 'desc' },
144
+ * });
145
+ * // users[0].posts — typed as Array<{ id: number; title: string; authorId: number }>
146
+ * ```
147
+ */
148
+ joinMany<TForeignSchema extends ObjectSchemaBuilder<any, any, any, any, any, any, any>, TFieldName extends string>(spec: JoinManySpec<TLocalSchema, TForeignSchema, TFieldName>): SchemaQueryBuilder<TLocalSchema, WithJoinedMany<TResult, TFieldName, TForeignSchema>>;
149
+ /**
150
+ * Add a `WHERE` clause to the query.
151
+ *
152
+ * Accepts a column reference, an optional operator, and a value:
153
+ * - `where(t => t.age, '>', 18)` — property accessor + operator + value.
154
+ * - `where('age', 18)` — string key + value (defaults to `=`).
155
+ * - `where({ name: 'Alice' })` — record object; property keys are mapped
156
+ * to column names automatically.
157
+ * - `where(builder => { ... })` — Knex sub-builder callback for grouped
158
+ * conditions.
159
+ * - `where(knex.raw('...'))` — raw SQL expression.
160
+ *
161
+ * Multiple `.where()` calls are combined with `AND`.
162
+ *
163
+ * @returns `this` for chaining.
164
+ */
165
+ where(column: ColumnRef<TLocalSchema>, operator: string, value: any): this;
166
+ where(column: ColumnRef<TLocalSchema>, value: any): this;
167
+ where(raw: Knex.Raw, operator: string, value: any): this;
168
+ where(callback: (builder: Knex.QueryBuilder) => void): this;
169
+ where(record: Record<string, any>): this;
170
+ where(raw: Knex.Raw): this;
171
+ /**
172
+ * Alias for {@link where} — explicitly adds an `AND WHERE` clause.
173
+ * Identical to calling `.where()` when no logical-OR grouping is needed.
174
+ * @returns `this` for chaining.
175
+ */
176
+ andWhere(column: ColumnRef<TLocalSchema>, operator: string, value: any): this;
177
+ andWhere(column: ColumnRef<TLocalSchema>, value: any): this;
178
+ andWhere(record: Record<string, any>): this;
179
+ andWhere(callback: (builder: Knex.QueryBuilder) => void): this;
180
+ andWhere(raw: Knex.Raw): this;
181
+ /**
182
+ * Add an `OR WHERE` clause. Use this to create alternative filter branches.
183
+ * @returns `this` for chaining.
184
+ */
185
+ orWhere(column: ColumnRef<TLocalSchema>, operator: string, value: any): this;
186
+ orWhere(column: ColumnRef<TLocalSchema>, value: any): this;
187
+ orWhere(record: Record<string, any>): this;
188
+ orWhere(callback: (builder: Knex.QueryBuilder) => void): this;
189
+ orWhere(raw: Knex.Raw): this;
190
+ /**
191
+ * Add a `WHERE NOT` clause — negates the condition.
192
+ * @returns `this` for chaining.
193
+ */
194
+ whereNot(column: ColumnRef<TLocalSchema>, operator: string, value: any): this;
195
+ whereNot(column: ColumnRef<TLocalSchema>, value: any): this;
196
+ whereNot(record: Record<string, any>): this;
197
+ whereNot(callback: (builder: Knex.QueryBuilder) => void): this;
198
+ whereNot(raw: Knex.Raw): this;
199
+ /**
200
+ * Add a `WHERE column IN (values)` clause.
201
+ * @param column - Column reference (property accessor or string key).
202
+ * @param values - Array of values or a sub-query.
203
+ * @returns `this` for chaining.
204
+ */
205
+ whereIn(column: ColumnRef<TLocalSchema>, values: readonly any[] | Knex.QueryBuilder): this;
206
+ /**
207
+ * Add a `WHERE column NOT IN (values)` clause.
208
+ * @param column - Column reference.
209
+ * @param values - Array of values or a sub-query.
210
+ * @returns `this` for chaining.
211
+ */
212
+ whereNotIn(column: ColumnRef<TLocalSchema>, values: readonly any[] | Knex.QueryBuilder): this;
213
+ /**
214
+ * Add an `OR WHERE column IN (values)` clause.
215
+ * @returns `this` for chaining.
216
+ */
217
+ orWhereIn(column: ColumnRef<TLocalSchema>, values: readonly any[] | Knex.QueryBuilder): this;
218
+ /**
219
+ * Add an `OR WHERE column NOT IN (values)` clause.
220
+ * @returns `this` for chaining.
221
+ */
222
+ orWhereNotIn(column: ColumnRef<TLocalSchema>, values: readonly any[] | Knex.QueryBuilder): this;
223
+ /**
224
+ * Add a `WHERE column IS NULL` clause.
225
+ * @returns `this` for chaining.
226
+ */
227
+ whereNull(column: ColumnRef<TLocalSchema>): this;
228
+ /**
229
+ * Add a `WHERE column IS NOT NULL` clause.
230
+ * @returns `this` for chaining.
231
+ */
232
+ whereNotNull(column: ColumnRef<TLocalSchema>): this;
233
+ /**
234
+ * Add an `OR WHERE column IS NULL` clause.
235
+ * @returns `this` for chaining.
236
+ */
237
+ orWhereNull(column: ColumnRef<TLocalSchema>): this;
238
+ /**
239
+ * Add an `OR WHERE column IS NOT NULL` clause.
240
+ * @returns `this` for chaining.
241
+ */
242
+ orWhereNotNull(column: ColumnRef<TLocalSchema>): this;
243
+ /**
244
+ * Add a `WHERE column BETWEEN low AND high` clause.
245
+ * @param range - A two-element tuple `[low, high]`.
246
+ * @returns `this` for chaining.
247
+ */
248
+ whereBetween(column: ColumnRef<TLocalSchema>, range: readonly [any, any]): this;
249
+ /**
250
+ * Add a `WHERE column NOT BETWEEN low AND high` clause.
251
+ * @param range - A two-element tuple `[low, high]`.
252
+ * @returns `this` for chaining.
253
+ */
254
+ whereNotBetween(column: ColumnRef<TLocalSchema>, range: readonly [any, any]): this;
255
+ /**
256
+ * Add a case-sensitive `WHERE column LIKE value` clause.
257
+ * @param value - A SQL LIKE pattern (e.g. `'Alice%'`).
258
+ * @returns `this` for chaining.
259
+ */
260
+ whereLike(column: ColumnRef<TLocalSchema>, value: string): this;
261
+ /**
262
+ * Add a case-insensitive `WHERE column ILIKE value` clause (PostgreSQL).
263
+ * @param value - A SQL LIKE pattern (e.g. `'alice%'`).
264
+ * @returns `this` for chaining.
265
+ */
266
+ whereILike(column: ColumnRef<TLocalSchema>, value: string): this;
267
+ /**
268
+ * Add a raw `WHERE` clause. Useful for database-specific expressions.
269
+ * @param sql - Raw SQL string with optional `:binding:` or `?` placeholders.
270
+ * @param bindings - Values for the placeholders.
271
+ * @returns `this` for chaining.
272
+ */
273
+ whereRaw(sql: string, ...bindings: any[]): this;
274
+ /**
275
+ * Add a `WHERE EXISTS (subquery)` clause.
276
+ * @param callback - A Knex query callback or sub-query builder.
277
+ * @returns `this` for chaining.
278
+ */
279
+ whereExists(callback: Knex.QueryCallback | Knex.QueryBuilder): this;
280
+ /**
281
+ * Order the results by a column.
282
+ * @param column - Column reference or raw expression.
283
+ * @param direction - `'asc'` (default) or `'desc'`.
284
+ * @returns `this` for chaining.
285
+ *
286
+ * @example
287
+ * ```ts
288
+ * query(db, UserSchema).orderBy(t => t.name).orderBy(t => t.createdAt, 'desc');
289
+ * ```
290
+ */
291
+ orderBy(column: ColumnRef<TLocalSchema> | Knex.Raw, direction?: 'asc' | 'desc'): this;
292
+ /**
293
+ * Order the results by a raw SQL expression.
294
+ * @param sql - Raw SQL (e.g. `'LOWER(name) ASC'`).
295
+ * @returns `this` for chaining.
296
+ */
297
+ orderByRaw(sql: string, ...bindings: any[]): this;
298
+ /**
299
+ * Add a `GROUP BY` clause.
300
+ * @param columns - One or more column references or raw expressions.
301
+ * @returns `this` for chaining.
302
+ */
303
+ groupBy(...columns: (ColumnRef<TLocalSchema> | Knex.Raw)[]): this;
304
+ /**
305
+ * Add a raw `GROUP BY` expression.
306
+ * @returns `this` for chaining.
307
+ */
308
+ groupByRaw(sql: string, ...bindings: any[]): this;
309
+ /**
310
+ * Add a `HAVING column operator value` clause (used with `GROUP BY`).
311
+ * @returns `this` for chaining.
312
+ */
313
+ having(column: ColumnRef<TLocalSchema> | Knex.Raw, operator: string, value: any): this;
314
+ /**
315
+ * Add a raw `HAVING` expression.
316
+ * @returns `this` for chaining.
317
+ */
318
+ havingRaw(sql: string, ...bindings: any[]): this;
319
+ /**
320
+ * Limit the number of rows returned.
321
+ * @param n - Maximum number of rows.
322
+ * @returns `this` for chaining.
323
+ */
324
+ limit(n: number): this;
325
+ /**
326
+ * Skip the first `n` rows in the result set (for cursor/offset pagination).
327
+ * @param n - Number of rows to skip.
328
+ * @returns `this` for chaining.
329
+ */
330
+ offset(n: number): this;
331
+ /**
332
+ * Select specific columns instead of `*`. Each column reference is
333
+ * resolved to its SQL column name through the schema.
334
+ * @param columns - One or more column references or raw expressions.
335
+ * @returns `this` for chaining.
336
+ */
337
+ select(...columns: (ColumnRef<TLocalSchema> | Knex.Raw)[]): this;
338
+ /**
339
+ * Add `DISTINCT` to the select clause. Duplicate rows are eliminated.
340
+ * @param columns - One or more column references or raw expressions.
341
+ * @returns `this` for chaining.
342
+ */
343
+ distinct(...columns: (ColumnRef<TLocalSchema> | Knex.Raw)[]): this;
344
+ /**
345
+ * Add a `COUNT(*)` or `COUNT(column)` aggregate to the select list.
346
+ * @param column - Optional column to count (defaults to `*`).
347
+ * @returns `this` for chaining.
348
+ */
349
+ count(column?: ColumnRef<TLocalSchema> | Knex.Raw): this;
350
+ /**
351
+ * Add a `COUNT(DISTINCT column)` aggregate to the select list.
352
+ * @param column - Optional column (defaults to `*`).
353
+ * @returns `this` for chaining.
354
+ */
355
+ countDistinct(column?: ColumnRef<TLocalSchema> | Knex.Raw): this;
356
+ /**
357
+ * Add a `MIN(column)` aggregate.
358
+ * @returns `this` for chaining.
359
+ */
360
+ min(column: ColumnRef<TLocalSchema> | Knex.Raw): this;
361
+ /**
362
+ * Add a `MAX(column)` aggregate.
363
+ * @returns `this` for chaining.
364
+ */
365
+ max(column: ColumnRef<TLocalSchema> | Knex.Raw): this;
366
+ /**
367
+ * Add a `SUM(column)` aggregate.
368
+ * @returns `this` for chaining.
369
+ */
370
+ sum(column: ColumnRef<TLocalSchema> | Knex.Raw): this;
371
+ /**
372
+ * Add an `AVG(column)` aggregate.
373
+ * @returns `this` for chaining.
374
+ */
375
+ avg(column: ColumnRef<TLocalSchema> | Knex.Raw): this;
376
+ /**
377
+ * Insert a single row into the table and return the inserted record.
378
+ *
379
+ * Property keys are mapped to SQL column names via the schema's
380
+ * `hasColumnName()` metadata before the `INSERT` is executed. The
381
+ * returned row is mapped back to property names.
382
+ *
383
+ * @param data - The object to insert. Keys must be valid schema property names.
384
+ * @returns The full inserted row (including database-generated fields).
385
+ *
386
+ * @example
387
+ * ```ts
388
+ * const user = await query(db, UserSchema).insert({ name: 'Alice', age: 30 });
389
+ * // user.id is populated by the database DEFAULT / SERIAL
390
+ * ```
391
+ */
392
+ insert(data: InsertType<TLocalSchema>): Promise<TResult>;
393
+ /**
394
+ * Insert multiple rows in a single `INSERT` statement and return all
395
+ * inserted records.
396
+ *
397
+ * @param data - Array of objects to insert.
398
+ * @returns The full inserted rows in insertion order.
399
+ */
400
+ insertMany(data: InsertType<TLocalSchema>[]): Promise<TResult[]>;
401
+ /**
402
+ * Update all rows that match the current `WHERE` clause and return the
403
+ * updated records.
404
+ *
405
+ * Only the keys present in `data` are updated (partial update). Property
406
+ * keys are resolved to column names automatically.
407
+ *
408
+ * @param data - Partial schema object with fields to update.
409
+ * @returns All rows that were updated.
410
+ *
411
+ * @example
412
+ * ```ts
413
+ * const updated = await query(db, UserSchema)
414
+ * .where(t => t.id, userId)
415
+ * .update({ name: 'Bob' });
416
+ * ```
417
+ */
418
+ update(data: Partial<InferType<TLocalSchema>>): Promise<TResult[]>;
419
+ /**
420
+ * Delete all rows that match the current `WHERE` clause.
421
+ * @returns The number of rows deleted.
422
+ *
423
+ * @example
424
+ * ```ts
425
+ * const count = await query(db, UserSchema).where(t => t.id, id).delete();
426
+ * ```
427
+ */
428
+ delete(): Promise<number>;
429
+ /**
430
+ * Escape hatch: apply any Knex method to the underlying base query.
431
+ *
432
+ * Use this when you need a Knex feature not exposed by this API (e.g.
433
+ * `forUpdate()`, CTEs, `join()`, `union()`).
434
+ *
435
+ * @param fn - A callback that receives the raw `Knex.QueryBuilder` and
436
+ * may mutate it in place.
437
+ * @returns `this` for chaining.
438
+ *
439
+ * @example
440
+ * ```ts
441
+ * query(db, UserSchema).apply(qb => qb.forUpdate().noWait());
442
+ * ```
443
+ */
444
+ apply(fn: (builder: Knex.QueryBuilder) => void): this;
445
+ /**
446
+ * Return the raw SQL string that would be executed, for debugging.
447
+ * Does not execute the query against the database.
448
+ */
449
+ toQuery(): string;
450
+ /**
451
+ * Returns the underlying Knex query builder. Useful when passing this
452
+ * query as a `foreignQuery` in `.joinOne()` / `.joinMany()`, or any context
453
+ * that expects a raw `Knex.QueryBuilder`.
454
+ */
455
+ toKnexQuery(): Knex.QueryBuilder;
456
+ /**
457
+ * Alias for {@link toQuery} — returns the raw SQL string.
458
+ */
459
+ toString(): string;
460
+ /**
461
+ * Execute the query and return all matching rows, mapped back to schema
462
+ * property names.
463
+ *
464
+ * @returns A promise that resolves to an array of result objects typed as
465
+ * `TResult[]`.
466
+ *
467
+ * @example
468
+ * ```ts
469
+ * const users = await query(db, UserSchema).execute();
470
+ * ```
471
+ */
472
+ execute(): Promise<TResult[]>;
473
+ /**
474
+ * Execute the query and return only the first row, or `undefined` if no
475
+ * rows match.
476
+ *
477
+ * @example
478
+ * ```ts
479
+ * const user = await query(db, UserSchema).where(t => t.id, id).first();
480
+ * if (user) { /* ... *\/ }
481
+ * ```
482
+ */
483
+ first(): Promise<TResult | undefined>;
484
+ /**
485
+ * Thenable implementation — allows the builder to be awaited directly
486
+ * without calling {@link execute} explicitly.
487
+ *
488
+ * @example
489
+ * ```ts
490
+ * const users = await query(db, UserSchema).where(t => t.name, 'Alice');
491
+ * // Equivalent to: await query(db, UserSchema).where(...).execute()
492
+ * ```
493
+ */
494
+ then<TReturn1 = TResult[], TReturn2 = never>(onfulfilled?: ((value: TResult[]) => TReturn1 | PromiseLike<TReturn1>) | null, onrejected?: ((reason: any) => TReturn2 | PromiseLike<TReturn2>) | null): Promise<TReturn1 | TReturn2>;
495
+ }
496
+ /**
497
+ * Create a typed {@link SchemaQueryBuilder} for the table described by `schema`.
498
+ *
499
+ * The schema must have a table name configured via `.hasTableName()`.
500
+ * Column name mappings set via `.hasColumnName()` are applied automatically
501
+ * to all query methods. The returned builder is thenable — you can `await` it
502
+ * directly to execute the query and get `TResult[]`.
503
+ *
504
+ * @param knex - A configured Knex instance.
505
+ * @param schema - The `ObjectSchemaBuilder` describing the table.
506
+ * @returns A new {@link SchemaQueryBuilder} ready for chaining.
507
+ *
508
+ * @example
509
+ * ```ts
510
+ * import knex from 'knex';
511
+ * import { query, object, string, number } from '@cleverbrush/knex-schema';
512
+ *
513
+ * const UserSchema = object({ id: number(), name: string() }).hasTableName('users');
514
+ * const db = knex({ client: 'pg', connection: process.env.DB_URL });
515
+ *
516
+ * const users = await query(db, UserSchema).where(t => t.name, 'like', 'A%');
517
+ * ```
518
+ */
519
+ export declare function query<TLocalSchema extends ObjectSchemaBuilder<any, any, any, any, any, any, any>>(knex: Knex, schema: TLocalSchema): SchemaQueryBuilder<TLocalSchema, InferType<TLocalSchema>>;
520
+ /**
521
+ * Create a typed {@link SchemaQueryBuilder} from an existing Knex query builder.
522
+ *
523
+ * Use this overload when you need to supply a pre-configured base query —
524
+ * for example one that already has a sub-query, CTE, or a schema scope applied.
525
+ *
526
+ * @param knex - A configured Knex instance.
527
+ * @param schema - The `ObjectSchemaBuilder` describing the table.
528
+ * @param baseQuery - An existing `Knex.QueryBuilder` to use as the base.
529
+ * @returns A new {@link SchemaQueryBuilder} wrapping `baseQuery`.
530
+ *
531
+ * @example
532
+ * ```ts
533
+ * // Use a scoped base query (e.g. soft-delete filter applied globally)
534
+ * const base = db('users').where('deleted_at', null);
535
+ * const activeUsers = await query(db, UserSchema, base).where(t => t.age, '>', 18);
536
+ * ```
537
+ */
538
+ export declare function query<TLocalSchema extends ObjectSchemaBuilder<any, any, any, any, any, any, any>>(knex: Knex, schema: TLocalSchema, baseQuery: Knex.QueryBuilder): SchemaQueryBuilder<TLocalSchema, InferType<TLocalSchema>>;
539
+ /** Bound query function returned by {@link createQuery}. */
540
+ export interface BoundQuery {
541
+ <TLocalSchema extends ObjectSchemaBuilder<any, any, any, any, any, any, any>>(schema: TLocalSchema): SchemaQueryBuilder<TLocalSchema, InferType<TLocalSchema>>;
542
+ <TLocalSchema extends ObjectSchemaBuilder<any, any, any, any, any, any, any>>(schema: TLocalSchema, baseQuery: Knex.QueryBuilder): SchemaQueryBuilder<TLocalSchema, InferType<TLocalSchema>>;
543
+ }
544
+ /**
545
+ * Bind a Knex instance once and get back a `query(schema)` function that
546
+ * doesn't require repeating the knex argument on every call.
547
+ *
548
+ * @param knex - A configured Knex instance.
549
+ * @returns A bound query factory: `(schema, baseQuery?) => SchemaQueryBuilder`.
550
+ *
551
+ * @example
552
+ * ```ts
553
+ * import Knex from 'knex';
554
+ * import { createQuery } from '@cleverbrush/knex-schema';
555
+ *
556
+ * const knex = Knex({ client: 'pg', connection: process.env.DB_URL });
557
+ * const query = createQuery(knex);
558
+ *
559
+ * // No knex argument needed from here on
560
+ * const users = await query(UserSchema).where(t => t.role, '=', 'admin');
561
+ * const post = await query(PostSchema).where(t => t.id, '=', 42).first();
562
+ *
563
+ * // Optional base query (e.g. soft-delete scope applied globally)
564
+ * const active = query(UserSchema, knex('users').where('deleted_at', null));
565
+ * ```
566
+ */
567
+ export declare function createQuery(knexInstance: Knex): BoundQuery;
@@ -0,0 +1,27 @@
1
+ import { ObjectSchemaBuilder } from '@cleverbrush/schema';
2
+ import type { ColumnRef } from './types.js';
3
+ interface ColumnMapResult {
4
+ propToCol: Map<string, string>;
5
+ colToProp: Map<string, string>;
6
+ }
7
+ /**
8
+ * Build a bidirectional column map from an ObjectSchemaBuilder's properties.
9
+ * Uses `getExtension('columnName')` per property, falling back to the property key.
10
+ * Result is cached per schema instance via WeakMap.
11
+ */
12
+ export declare function buildColumnMap(schema: ObjectSchemaBuilder<any, any, any, any, any, any, any>): ColumnMapResult;
13
+ /**
14
+ * Resolve a ColumnRef to a plain SQL column name.
15
+ *
16
+ * - String refs are treated as **property keys** and translated to column
17
+ * names via the column map.
18
+ * - Function refs (property accessor) are resolved via PropertyDescriptorTree,
19
+ * then translated to column names.
20
+ */
21
+ export declare function resolveColumnRef(ref: ColumnRef<any>, schema: ObjectSchemaBuilder<any, any, any, any, any, any, any>, label: string): string;
22
+ /**
23
+ * Resolve a ColumnRef to the **property key** (not the column name).
24
+ * Used for result mapping.
25
+ */
26
+ export declare function resolvePropertyKey(ref: ColumnRef<any>, schema: ObjectSchemaBuilder<any, any, any, any, any, any, any>, label: string): string;
27
+ export {};