@cleverbrush/knex-schema 3.0.1 → 4.0.0
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/README.md +156 -1
- package/dist/SchemaQueryBuilder.d.ts +457 -7
- package/dist/chunk-V4TH6K42.js +2 -0
- package/dist/chunk-V4TH6K42.js.map +1 -0
- package/dist/columns.d.ts +73 -4
- package/dist/ddl.d.ts +66 -0
- package/dist/entity.d.ts +264 -0
- package/dist/extension.d.ts +1176 -1
- package/dist/extension.js +2 -0
- package/dist/extension.js.map +1 -0
- package/dist/index.d.ts +10 -3
- package/dist/index.js +60 -1
- package/dist/index.js.map +1 -1
- package/dist/migration.d.ts +160 -0
- package/dist/raw.d.ts +35 -0
- package/dist/snapshot.d.ts +39 -0
- package/dist/types.d.ts +269 -0
- package/package.json +6 -2
|
@@ -1,7 +1,75 @@
|
|
|
1
1
|
import type { InferType } from '@cleverbrush/schema';
|
|
2
|
-
import { ObjectSchemaBuilder } from '@cleverbrush/schema';
|
|
2
|
+
import { EXTRA_TYPE_BRAND, METHOD_LITERAL_BRAND, ObjectSchemaBuilder } from '@cleverbrush/schema';
|
|
3
3
|
import type { Knex } from 'knex';
|
|
4
|
-
import
|
|
4
|
+
import { POLYMORPHIC_TYPE_BRAND } from './extension.js';
|
|
5
|
+
import type { ColumnRef, CursorPaginationResult, InsertType, JoinManySpec, JoinOneSpec, PaginationResult, SelectProjection, SelectSelector, WithJoinedMany, WithJoinedOne } from './types.js';
|
|
6
|
+
/**
|
|
7
|
+
* Extracts the scope names registered on a schema via `.scope(name, fn)`.
|
|
8
|
+
* Returns `never` when no scopes are defined (making `.scoped()` uncallable).
|
|
9
|
+
* Falls back to `string` for `any`-typed schemas to preserve loose behaviour.
|
|
10
|
+
*
|
|
11
|
+
* @internal
|
|
12
|
+
*/
|
|
13
|
+
type ScopesOf<S> = S extends {
|
|
14
|
+
readonly [METHOD_LITERAL_BRAND]?: infer N;
|
|
15
|
+
} ? Extract<N, string> : never;
|
|
16
|
+
/**
|
|
17
|
+
* Extracts the named projection map from a schema type.
|
|
18
|
+
* Returns a `Record<name, readonly keys[]>` type where each key is a
|
|
19
|
+
* registered projection name and the value is the tuple of property keys.
|
|
20
|
+
* Returns `Record<never, never>` (no projections) when the schema has none,
|
|
21
|
+
* making `.projected()` uncallable on undecorated schemas.
|
|
22
|
+
*
|
|
23
|
+
* @internal
|
|
24
|
+
*/
|
|
25
|
+
type ProjectionsOf<S> = S extends {
|
|
26
|
+
readonly [EXTRA_TYPE_BRAND]?: infer P;
|
|
27
|
+
} ? P extends Record<string, readonly string[]> ? P : Record<never, never> : Record<never, never>;
|
|
28
|
+
/**
|
|
29
|
+
* Extracts the string-key union for a specific projection name from a schema
|
|
30
|
+
* type. This indirection is needed because TypeScript cannot directly index
|
|
31
|
+
* `ProjectionsOf<S>[K]` with `number` inside a generic function signature.
|
|
32
|
+
*
|
|
33
|
+
* @internal
|
|
34
|
+
*/
|
|
35
|
+
type ProjectionKeysOf<S, K extends keyof ProjectionsOf<S> & string> = ProjectionsOf<S>[K] extends readonly (infer T extends string)[] ? T : string;
|
|
36
|
+
/**
|
|
37
|
+
* When a schema carries the `POLYMORPHIC_TYPE_BRAND` phantom type (set by
|
|
38
|
+
* `.withVariants()`), extract the discriminated-union result type from it.
|
|
39
|
+
* Otherwise fall back to `InferType<TLocalSchema>`.
|
|
40
|
+
*
|
|
41
|
+
* @internal
|
|
42
|
+
*/
|
|
43
|
+
type QueryResultType<TLocalSchema> = TLocalSchema extends {
|
|
44
|
+
readonly [POLYMORPHIC_TYPE_BRAND]?: infer U;
|
|
45
|
+
} ? NonNullable<U> : InferType<TLocalSchema>;
|
|
46
|
+
/**
|
|
47
|
+
* Intermediate builder returned by {@link SchemaQueryBuilder.onConflict}.
|
|
48
|
+
* Call `.merge()` or `.ignore()` to complete the upsert/insert-ignore operation.
|
|
49
|
+
*
|
|
50
|
+
* @internal
|
|
51
|
+
*/
|
|
52
|
+
export declare class OnConflictBuilder<TLocalSchema extends ObjectSchemaBuilder<any, any, any, any, any, any, any>, TResult> {
|
|
53
|
+
#private;
|
|
54
|
+
/** @internal */
|
|
55
|
+
constructor(knex: Knex, localSchema: TLocalSchema, _parent: SchemaQueryBuilder<TLocalSchema, TResult>, conflictColumns: string[]);
|
|
56
|
+
/**
|
|
57
|
+
* Insert and merge (update) conflicting rows.
|
|
58
|
+
*
|
|
59
|
+
* @param data - Row to insert.
|
|
60
|
+
* @param updateData - Optional partial object of columns to update on
|
|
61
|
+
* conflict. If omitted, all inserted columns are updated.
|
|
62
|
+
* @returns The resulting row.
|
|
63
|
+
*/
|
|
64
|
+
merge(data: InsertType<TLocalSchema>, updateData?: Partial<InferType<TLocalSchema>>): Promise<TResult>;
|
|
65
|
+
/**
|
|
66
|
+
* Insert and silently ignore conflicts.
|
|
67
|
+
*
|
|
68
|
+
* @param data - Row to insert.
|
|
69
|
+
* @returns The row if inserted, or `undefined` if the conflict was ignored.
|
|
70
|
+
*/
|
|
71
|
+
ignore(data: InsertType<TLocalSchema>): Promise<TResult | undefined>;
|
|
72
|
+
}
|
|
5
73
|
/**
|
|
6
74
|
* Type-safe, schema-driven query builder for Knex.
|
|
7
75
|
*
|
|
@@ -277,6 +345,36 @@ export declare class SchemaQueryBuilder<TLocalSchema extends ObjectSchemaBuilder
|
|
|
277
345
|
* @returns `this` for chaining.
|
|
278
346
|
*/
|
|
279
347
|
whereExists(callback: Knex.QueryCallback | Knex.QueryBuilder): this;
|
|
348
|
+
/**
|
|
349
|
+
* Add a `WHERE NOT EXISTS (subquery)` clause.
|
|
350
|
+
* @param callback - A Knex query callback or sub-query builder.
|
|
351
|
+
* @returns `this` for chaining.
|
|
352
|
+
*/
|
|
353
|
+
whereNotExists(callback: Knex.QueryCallback | Knex.QueryBuilder): this;
|
|
354
|
+
/**
|
|
355
|
+
* Add a PostgreSQL JSON path filter using `@?` / `@@` operators or
|
|
356
|
+
* a path-based equality test via `jsonb_path_query_first`.
|
|
357
|
+
*
|
|
358
|
+
* Only supported on `pg` clients — throws at runtime on others.
|
|
359
|
+
*
|
|
360
|
+
* @param column - Column reference for the `jsonb` column.
|
|
361
|
+
* @param path - Dot-separated property path (e.g. `'a.b.c'`) or a
|
|
362
|
+
* JSONPath expression string (e.g. `'$.a.b ? (@ == 1)'`).
|
|
363
|
+
* @param operator - Comparison operator (`=`, `!=`, `<`, `<=`, `>`, `>=`,
|
|
364
|
+
* `@?`, `@@`). Use `@?` / `@@` for JSONPath existence / predicate tests.
|
|
365
|
+
* @param value - The right-hand side value. Ignored for `@?` and `@@`.
|
|
366
|
+
* @returns `this` for chaining.
|
|
367
|
+
*
|
|
368
|
+
* @example
|
|
369
|
+
* ```ts
|
|
370
|
+
* // Filter rows where data->>'status' = 'active'
|
|
371
|
+
* query(db, Schema).whereJsonPath(t => t.data, 'status', '=', 'active');
|
|
372
|
+
*
|
|
373
|
+
* // JSONPath existence
|
|
374
|
+
* query(db, Schema).whereJsonPath(t => t.data, '$.tags[*] ? (@ == "sale")', '@?');
|
|
375
|
+
* ```
|
|
376
|
+
*/
|
|
377
|
+
whereJsonPath(column: ColumnRef<TLocalSchema>, path: string, operator?: string, value?: any): this;
|
|
280
378
|
/**
|
|
281
379
|
* Order the results by a column.
|
|
282
380
|
* @param column - Column reference or raw expression.
|
|
@@ -335,6 +433,22 @@ export declare class SchemaQueryBuilder<TLocalSchema extends ObjectSchemaBuilder
|
|
|
335
433
|
* @returns `this` for chaining.
|
|
336
434
|
*/
|
|
337
435
|
select(...columns: (ColumnRef<TLocalSchema> | Knex.Raw)[]): this;
|
|
436
|
+
/**
|
|
437
|
+
* DTO projection: select multiple aliased columns at once via a
|
|
438
|
+
* descriptor record. Returns a query whose result rows match the
|
|
439
|
+
* shape of the selector's return value (each value typed as the
|
|
440
|
+
* inferred schema-property type).
|
|
441
|
+
*
|
|
442
|
+
* @example
|
|
443
|
+
* ```ts
|
|
444
|
+
* const dtos = await query(db, UserSchema)
|
|
445
|
+
* .where(t => t.id, '>', 0)
|
|
446
|
+
* .select(t => ({ id: t.id, n: t.name }))
|
|
447
|
+
* .execute();
|
|
448
|
+
* // dtos: { id: number; n: string }[]
|
|
449
|
+
* ```
|
|
450
|
+
*/
|
|
451
|
+
select<TSel extends SelectSelector<TLocalSchema>>(selector: TSel): SchemaQueryBuilder<TLocalSchema, SelectProjection<ReturnType<TSel>>>;
|
|
338
452
|
/**
|
|
339
453
|
* Add `DISTINCT` to the select clause. Duplicate rows are eliminated.
|
|
340
454
|
* @param columns - One or more column references or raw expressions.
|
|
@@ -398,6 +512,53 @@ export declare class SchemaQueryBuilder<TLocalSchema extends ObjectSchemaBuilder
|
|
|
398
512
|
* @returns The full inserted rows in insertion order.
|
|
399
513
|
*/
|
|
400
514
|
insertMany(data: InsertType<TLocalSchema>[]): Promise<TResult[]>;
|
|
515
|
+
/**
|
|
516
|
+
* Insert a row (or rows) with an `ON CONFLICT` clause.
|
|
517
|
+
*
|
|
518
|
+
* Returns a chainable object with `.merge()` and `.ignore()` methods.
|
|
519
|
+
*
|
|
520
|
+
* - `.merge(updateData?)` — updates the conflicting row with the provided
|
|
521
|
+
* fields (or all insert fields if omitted).
|
|
522
|
+
* - `.ignore()` — skips the insert when a conflict occurs (INSERT IGNORE).
|
|
523
|
+
*
|
|
524
|
+
* @param conflictColumns - Column references that define the conflict target.
|
|
525
|
+
* @returns A chainable conflict builder.
|
|
526
|
+
*
|
|
527
|
+
* @example
|
|
528
|
+
* ```ts
|
|
529
|
+
* // Upsert: insert or update on conflict
|
|
530
|
+
* await query(db, UserSchema)
|
|
531
|
+
* .onConflict(t => t.email)
|
|
532
|
+
* .merge({ name: 'Bob' });
|
|
533
|
+
*
|
|
534
|
+
* // Insert and ignore on conflict
|
|
535
|
+
* await query(db, UserSchema)
|
|
536
|
+
* .onConflict(t => t.email)
|
|
537
|
+
* .ignore();
|
|
538
|
+
* ```
|
|
539
|
+
*/
|
|
540
|
+
onConflict(...conflictColumns: ColumnRef<TLocalSchema>[]): OnConflictBuilder<TLocalSchema, TResult>;
|
|
541
|
+
/**
|
|
542
|
+
* Insert or update a row based on a conflict target (upsert shorthand).
|
|
543
|
+
*
|
|
544
|
+
* Equivalent to calling `.onConflict(conflictColumns).merge(updateData)`.
|
|
545
|
+
*
|
|
546
|
+
* @param data - The row data to insert.
|
|
547
|
+
* @param opts - `{ conflictColumns, updateColumns? }`.
|
|
548
|
+
* @returns The resulting row (inserted or updated).
|
|
549
|
+
*
|
|
550
|
+
* @example
|
|
551
|
+
* ```ts
|
|
552
|
+
* const user = await query(db, UserSchema).upsert(
|
|
553
|
+
* { email: 'alice@example.com', name: 'Alice' },
|
|
554
|
+
* { conflictColumns: [t => t.email], updateColumns: [t => t.name] }
|
|
555
|
+
* );
|
|
556
|
+
* ```
|
|
557
|
+
*/
|
|
558
|
+
upsert(data: InsertType<TLocalSchema>, opts: {
|
|
559
|
+
conflictColumns: ColumnRef<TLocalSchema>[];
|
|
560
|
+
updateColumns?: ColumnRef<TLocalSchema>[];
|
|
561
|
+
}): Promise<TResult>;
|
|
401
562
|
/**
|
|
402
563
|
* Update all rows that match the current `WHERE` clause and return the
|
|
403
564
|
* updated records.
|
|
@@ -418,7 +579,12 @@ export declare class SchemaQueryBuilder<TLocalSchema extends ObjectSchemaBuilder
|
|
|
418
579
|
update(data: Partial<InferType<TLocalSchema>>): Promise<TResult[]>;
|
|
419
580
|
/**
|
|
420
581
|
* Delete all rows that match the current `WHERE` clause.
|
|
421
|
-
*
|
|
582
|
+
*
|
|
583
|
+
* If the schema has soft-delete enabled via `.softDelete()`, this performs
|
|
584
|
+
* an `UPDATE SET deleted_at = NOW()` instead of a real `DELETE`.
|
|
585
|
+
* Use {@link hardDelete} for permanent deletion.
|
|
586
|
+
*
|
|
587
|
+
* @returns The number of rows deleted (or soft-deleted).
|
|
422
588
|
*
|
|
423
589
|
* @example
|
|
424
590
|
* ```ts
|
|
@@ -426,6 +592,53 @@ export declare class SchemaQueryBuilder<TLocalSchema extends ObjectSchemaBuilder
|
|
|
426
592
|
* ```
|
|
427
593
|
*/
|
|
428
594
|
delete(): Promise<number>;
|
|
595
|
+
/**
|
|
596
|
+
* Bulk-insert many rows in chunks. The default chunk size of `500` keeps
|
|
597
|
+
* comfortably below Postgres' parameter-count limit of 65535. The chunk
|
|
598
|
+
* size also auto-shrinks when the number of bindings per row would
|
|
599
|
+
* exceed that ceiling.
|
|
600
|
+
*
|
|
601
|
+
* When `opts.onConflict` is supplied each chunk is wrapped in an
|
|
602
|
+
* `INSERT ... ON CONFLICT` clause:
|
|
603
|
+
*
|
|
604
|
+
* - `'ignore'` — `ON CONFLICT (...) DO NOTHING`
|
|
605
|
+
* - `'merge'` — `ON CONFLICT (...) DO UPDATE SET ...` (uses `conflictColumns` as
|
|
606
|
+
* the conflict target and updates every inserted column).
|
|
607
|
+
*
|
|
608
|
+
* `beforeInsert` / `afterInsert` hooks fire per row, identically to
|
|
609
|
+
* {@link insertMany}.
|
|
610
|
+
*
|
|
611
|
+
* @returns The inserted rows (excluding rows skipped by `'ignore'`).
|
|
612
|
+
*/
|
|
613
|
+
bulkInsert(rows: InsertType<TLocalSchema>[], opts?: {
|
|
614
|
+
chunkSize?: number;
|
|
615
|
+
onConflict?: 'ignore' | 'merge';
|
|
616
|
+
conflictColumns?: ColumnRef<TLocalSchema>[];
|
|
617
|
+
}): Promise<TResult[]>;
|
|
618
|
+
/**
|
|
619
|
+
* Bulk-upsert many rows in chunks. Equivalent to
|
|
620
|
+
* `.bulkInsert(rows, { onConflict: 'merge', conflictColumns })`.
|
|
621
|
+
*/
|
|
622
|
+
bulkUpsert(rows: InsertType<TLocalSchema>[], opts: {
|
|
623
|
+
conflictColumns: ColumnRef<TLocalSchema>[];
|
|
624
|
+
chunkSize?: number;
|
|
625
|
+
}): Promise<TResult[]>;
|
|
626
|
+
/**
|
|
627
|
+
* Bulk-update many rows in a single SQL statement using a CASE
|
|
628
|
+
* expression keyed on the entity's primary key.
|
|
629
|
+
*
|
|
630
|
+
* Each entry's `where` clause must fully match the entity's primary key
|
|
631
|
+
* columns (single or composite). Updates that touch different columns
|
|
632
|
+
* are coalesced into one statement; rows whose PK appears in `updates`
|
|
633
|
+
* but whose `set` does not contain a given column retain their existing
|
|
634
|
+
* value.
|
|
635
|
+
*
|
|
636
|
+
* @returns The number of rows affected.
|
|
637
|
+
*/
|
|
638
|
+
bulkUpdate(updates: ReadonlyArray<{
|
|
639
|
+
where: Partial<InferType<TLocalSchema>>;
|
|
640
|
+
set: Partial<InferType<TLocalSchema>>;
|
|
641
|
+
}>): Promise<number>;
|
|
429
642
|
/**
|
|
430
643
|
* Escape hatch: apply any Knex method to the underlying base query.
|
|
431
644
|
*
|
|
@@ -472,6 +685,228 @@ export declare class SchemaQueryBuilder<TLocalSchema extends ObjectSchemaBuilder
|
|
|
472
685
|
* ```
|
|
473
686
|
*/
|
|
474
687
|
transacting(trx: Knex.Transaction): SchemaQueryBuilder<TLocalSchema, TResult>;
|
|
688
|
+
/**
|
|
689
|
+
* Eager-load a named relation defined via `.hasMany()`, `.belongsTo()`,
|
|
690
|
+
* `.hasOne()`, or `.belongsToMany()` on the schema.
|
|
691
|
+
*
|
|
692
|
+
* @param relationName - The relation name passed to the schema's relation method.
|
|
693
|
+
* @param customize - Optional callback to customise the foreign query
|
|
694
|
+
* (e.g. add ordering, limits).
|
|
695
|
+
* @returns `this` for chaining.
|
|
696
|
+
*
|
|
697
|
+
* @example
|
|
698
|
+
* ```ts
|
|
699
|
+
* const posts = await query(db, PostWithRelations)
|
|
700
|
+
* .include('author')
|
|
701
|
+
* .include('tags');
|
|
702
|
+
* ```
|
|
703
|
+
*/
|
|
704
|
+
include(relationName: string, customize?: (q: SchemaQueryBuilder<any, any>) => void): this;
|
|
705
|
+
/**
|
|
706
|
+
* Eager-load a named relation declared inside a `withVariants` variant spec.
|
|
707
|
+
*
|
|
708
|
+
* The relation is loaded via a LEFT JOIN on the variant's alias table and
|
|
709
|
+
* only populated on rows whose discriminator matches `variantKey`.
|
|
710
|
+
*
|
|
711
|
+
* @param variantKey - The variant key (e.g. `'assigned'`).
|
|
712
|
+
* @param relationName - The relation name declared in `variants[key].relations`.
|
|
713
|
+
* @param customize - Optional callback to restrict which columns are
|
|
714
|
+
* selected from the foreign table (scope / projection).
|
|
715
|
+
*
|
|
716
|
+
* @example
|
|
717
|
+
* ```ts
|
|
718
|
+
* await query(db, TodoActivity)
|
|
719
|
+
* .includeVariant('assigned', 'assignee', q => q.projected('summary'));
|
|
720
|
+
* ```
|
|
721
|
+
*/
|
|
722
|
+
includeVariant(variantKey: string, relationName: string, customize?: (q: SchemaQueryBuilder<any, any>) => void): this;
|
|
723
|
+
/**
|
|
724
|
+
* Apply a named scope defined on the schema via `.scope(name, fn)`.
|
|
725
|
+
*
|
|
726
|
+
* The `name` parameter is constrained to the literal scope names registered
|
|
727
|
+
* on the schema, so IDEs show only valid completions and typos are caught
|
|
728
|
+
* at compile time.
|
|
729
|
+
*
|
|
730
|
+
* @param name - The scope name.
|
|
731
|
+
* @returns `this` for chaining.
|
|
732
|
+
*
|
|
733
|
+
* @example
|
|
734
|
+
* ```ts
|
|
735
|
+
* await query(db, Post).scoped('published').scoped('recent');
|
|
736
|
+
* ```
|
|
737
|
+
*/
|
|
738
|
+
/**
|
|
739
|
+
* Apply a **named projection** defined on the schema via
|
|
740
|
+
* `.projection(name, columns)`.
|
|
741
|
+
*
|
|
742
|
+
* Calling `.projected()` on the query builder does two things:
|
|
743
|
+
* 1. Restricts the SQL `SELECT` clause to the columns registered under
|
|
744
|
+
* `name` (SQL column names are resolved via `.hasColumnName()`).
|
|
745
|
+
* 2. Narrows the TypeScript result row type to `Pick<Row, Keys>` so
|
|
746
|
+
* accessing columns outside the projection is a compile-time error.
|
|
747
|
+
*
|
|
748
|
+
* The `name` parameter is constrained to the literal projection names
|
|
749
|
+
* registered on the schema — TypeScript will report an error for any
|
|
750
|
+
* unregistered name.
|
|
751
|
+
*
|
|
752
|
+
* Calling `.projected()` after `.select()`, any aggregate method
|
|
753
|
+
* (`.count()`, `.min()`, etc.), or a second `.projected()` call throws
|
|
754
|
+
* at runtime with a clear error message.
|
|
755
|
+
*
|
|
756
|
+
* @param name - The projection name.
|
|
757
|
+
* @returns A new builder whose result type is `Pick<Row, ProjectionKeys>`.
|
|
758
|
+
*
|
|
759
|
+
* @example
|
|
760
|
+
* ```ts
|
|
761
|
+
* const PostSchema = object({ id: number(), title: string(), body: string() })
|
|
762
|
+
* .hasTableName('posts')
|
|
763
|
+
* .projection('summary', 'id', 'title');
|
|
764
|
+
*
|
|
765
|
+
* const rows = await query(db, PostSchema)
|
|
766
|
+
* .scoped('published')
|
|
767
|
+
* .projected('summary');
|
|
768
|
+
* // rows: Array<Pick<Post, 'id' | 'title'>>
|
|
769
|
+
* // rows[0].body // ← TS error: not in projection
|
|
770
|
+
* ```
|
|
771
|
+
*
|
|
772
|
+
* @see {@link ddlExtension} `.projection()` for schema-side definition.
|
|
773
|
+
*/
|
|
774
|
+
projected<K extends keyof ProjectionsOf<TLocalSchema> & string>(name: K): SchemaQueryBuilder<TLocalSchema, Pick<TResult, ProjectionKeysOf<TLocalSchema, K> & keyof TResult>>;
|
|
775
|
+
scoped<K extends ScopesOf<TLocalSchema>>(name: K): this;
|
|
776
|
+
/**
|
|
777
|
+
* Bypass the default scope (and soft-delete scope) for this query.
|
|
778
|
+
*
|
|
779
|
+
* @returns `this` for chaining.
|
|
780
|
+
*
|
|
781
|
+
* @example
|
|
782
|
+
* ```ts
|
|
783
|
+
* await query(db, Post).unscoped().where(t => t.id, 1);
|
|
784
|
+
* ```
|
|
785
|
+
*/
|
|
786
|
+
unscoped(): this;
|
|
787
|
+
/**
|
|
788
|
+
* Add a WHERE condition that applies **only to rows matching a specific
|
|
789
|
+
* variant** of a polymorphic schema. Rows for other variants pass through
|
|
790
|
+
* unaffected (the condition is ORed away for non-matching discriminator values).
|
|
791
|
+
*
|
|
792
|
+
* The column name is resolved against the **variant's** schema properties.
|
|
793
|
+
*
|
|
794
|
+
* Only valid on a polymorphic schema (created via `.withVariants()`).
|
|
795
|
+
*
|
|
796
|
+
* @param key - The discriminator value identifying the variant (e.g. `'image'`).
|
|
797
|
+
* @param column - Property key on the variant's schema (e.g. `'width'`).
|
|
798
|
+
* @param operator - SQL comparison operator (`'='`, `'>'`, `'<'`, `'like'`, etc.).
|
|
799
|
+
* @param value - The value to compare against.
|
|
800
|
+
* @returns `this` for chaining.
|
|
801
|
+
*
|
|
802
|
+
* @example
|
|
803
|
+
* ```ts
|
|
804
|
+
* // Return all documents and only images wider than 1024 px
|
|
805
|
+
* const files = await query(db, FileSchema)
|
|
806
|
+
* .whereVariant('image', 'width', '>', 1024);
|
|
807
|
+
* ```
|
|
808
|
+
*/
|
|
809
|
+
whereVariant(key: string, column: string, operator: string, value: any): this;
|
|
810
|
+
/**
|
|
811
|
+
* Restrict the query to only return rows for the specified variant keys.
|
|
812
|
+
*
|
|
813
|
+
* Adds `WHERE <discriminator> IN (...)` to the query and skips the LEFT
|
|
814
|
+
* JOINs for excluded variants. This is more efficient than filtering after
|
|
815
|
+
* loading all variants.
|
|
816
|
+
*
|
|
817
|
+
* Only valid on a polymorphic schema (created via `.withVariants()`).
|
|
818
|
+
*
|
|
819
|
+
* @param keys - Discriminator values to include (e.g. `['image', 'document']`).
|
|
820
|
+
* @returns `this` for chaining.
|
|
821
|
+
*
|
|
822
|
+
* @example
|
|
823
|
+
* ```ts
|
|
824
|
+
* const images = await query(db, FileSchema).selectVariants(['image']);
|
|
825
|
+
* // images: Array<{ id; name; type: 'image'; width; height; format }>
|
|
826
|
+
* ```
|
|
827
|
+
*/
|
|
828
|
+
selectVariants(keys: string[]): this;
|
|
829
|
+
/**
|
|
830
|
+
* Include soft-deleted rows in the results.
|
|
831
|
+
*
|
|
832
|
+
* By default, schemas with `.softDelete()` automatically filter out
|
|
833
|
+
* rows where `deleted_at IS NOT NULL`. Call `.withDeleted()` to
|
|
834
|
+
* include them.
|
|
835
|
+
*
|
|
836
|
+
* @returns `this` for chaining.
|
|
837
|
+
*/
|
|
838
|
+
withDeleted(): this;
|
|
839
|
+
/**
|
|
840
|
+
* Return only soft-deleted rows (`WHERE deleted_at IS NOT NULL`).
|
|
841
|
+
*
|
|
842
|
+
* @returns `this` for chaining.
|
|
843
|
+
*/
|
|
844
|
+
onlyDeleted(): this;
|
|
845
|
+
/**
|
|
846
|
+
* Permanently delete rows matching the current WHERE clause, bypassing
|
|
847
|
+
* the soft-delete mechanism.
|
|
848
|
+
*
|
|
849
|
+
* @returns The number of rows deleted.
|
|
850
|
+
*/
|
|
851
|
+
hardDelete(): Promise<number>;
|
|
852
|
+
/**
|
|
853
|
+
* Restore soft-deleted rows by setting `deleted_at = NULL`.
|
|
854
|
+
*
|
|
855
|
+
* @returns The restored rows.
|
|
856
|
+
*/
|
|
857
|
+
restore(): Promise<TResult[]>;
|
|
858
|
+
/**
|
|
859
|
+
* Execute an offset-based paginated query.
|
|
860
|
+
*
|
|
861
|
+
* Runs a count query and a data query in parallel. Returns the page data
|
|
862
|
+
* along with pagination metadata.
|
|
863
|
+
*
|
|
864
|
+
* @param opts - `{ page, pageSize }` — 1-based page number and page size.
|
|
865
|
+
* @returns A {@link PaginationResult} with data, total count, and page info.
|
|
866
|
+
*
|
|
867
|
+
* @example
|
|
868
|
+
* ```ts
|
|
869
|
+
* const page = await query(db, Post)
|
|
870
|
+
* .where(t => t.status, 'published')
|
|
871
|
+
* .paginate({ page: 2, pageSize: 20 });
|
|
872
|
+
* // page.data, page.total, page.totalPages, page.hasNextPage, ...
|
|
873
|
+
* ```
|
|
874
|
+
*/
|
|
875
|
+
paginate(opts: {
|
|
876
|
+
page: number;
|
|
877
|
+
pageSize: number;
|
|
878
|
+
}): Promise<PaginationResult<TResult>>;
|
|
879
|
+
/**
|
|
880
|
+
* Execute a cursor-based (keyset) paginated query.
|
|
881
|
+
*
|
|
882
|
+
* More efficient than offset pagination for large datasets. Fetches one
|
|
883
|
+
* extra row to determine whether more data exists.
|
|
884
|
+
*
|
|
885
|
+
* @param opts - `{ cursor, limit, column?, direction? }`.
|
|
886
|
+
* @returns A {@link CursorPaginationResult} with data, next cursor, and
|
|
887
|
+
* `hasMore` flag.
|
|
888
|
+
*
|
|
889
|
+
* @example
|
|
890
|
+
* ```ts
|
|
891
|
+
* const page = await query(db, Post)
|
|
892
|
+
* .orderBy(t => t.createdAt, 'desc')
|
|
893
|
+
* .paginateAfter({ cursor: lastCreatedAt, limit: 20 });
|
|
894
|
+
* ```
|
|
895
|
+
*/
|
|
896
|
+
paginateAfter(opts: {
|
|
897
|
+
cursor?: any;
|
|
898
|
+
limit: number;
|
|
899
|
+
column?: ColumnRef<TLocalSchema>;
|
|
900
|
+
direction?: 'asc' | 'desc';
|
|
901
|
+
}): Promise<CursorPaginationResult<TResult>>;
|
|
902
|
+
/**
|
|
903
|
+
* Add a raw SQL expression to the SELECT clause.
|
|
904
|
+
*
|
|
905
|
+
* @param sql - Raw SQL (e.g. `'*, ts_rank(vector, query) AS rank'`).
|
|
906
|
+
* @param bindings - Optional parameter bindings.
|
|
907
|
+
* @returns `this` for chaining.
|
|
908
|
+
*/
|
|
909
|
+
selectRaw(sql: string, bindings?: any[]): this;
|
|
475
910
|
/**
|
|
476
911
|
* Return the raw SQL string that would be executed, for debugging.
|
|
477
912
|
* Does not execute the query against the database.
|
|
@@ -511,6 +946,20 @@ export declare class SchemaQueryBuilder<TLocalSchema extends ObjectSchemaBuilder
|
|
|
511
946
|
* ```
|
|
512
947
|
*/
|
|
513
948
|
first(): Promise<TResult | undefined>;
|
|
949
|
+
/**
|
|
950
|
+
* Execute the query and return an array of values for a single column.
|
|
951
|
+
*
|
|
952
|
+
* @param column - Column reference (property accessor or string key) for
|
|
953
|
+
* the column whose values should be returned.
|
|
954
|
+
* @returns A promise resolving to an array of values for that column.
|
|
955
|
+
*
|
|
956
|
+
* @example
|
|
957
|
+
* ```ts
|
|
958
|
+
* const names = await query(db, UserSchema).pluck(t => t.name);
|
|
959
|
+
* // names: string[]
|
|
960
|
+
* ```
|
|
961
|
+
*/
|
|
962
|
+
pluck<K extends keyof TResult & string>(column: ColumnRef<TLocalSchema>): Promise<TResult[K][]>;
|
|
514
963
|
/**
|
|
515
964
|
* Thenable implementation — allows the builder to be awaited directly
|
|
516
965
|
* without calling {@link execute} explicitly.
|
|
@@ -546,7 +995,7 @@ export declare class SchemaQueryBuilder<TLocalSchema extends ObjectSchemaBuilder
|
|
|
546
995
|
* const users = await query(db, UserSchema).where(t => t.name, 'like', 'A%');
|
|
547
996
|
* ```
|
|
548
997
|
*/
|
|
549
|
-
export declare function query<TLocalSchema extends ObjectSchemaBuilder<any, any, any, any, any, any, any>>(knex: Knex, schema: TLocalSchema): SchemaQueryBuilder<TLocalSchema,
|
|
998
|
+
export declare function query<TLocalSchema extends ObjectSchemaBuilder<any, any, any, any, any, any, any>>(knex: Knex, schema: TLocalSchema): SchemaQueryBuilder<TLocalSchema, QueryResultType<TLocalSchema>>;
|
|
550
999
|
/**
|
|
551
1000
|
* Create a typed {@link SchemaQueryBuilder} from an existing Knex query builder.
|
|
552
1001
|
*
|
|
@@ -565,11 +1014,11 @@ export declare function query<TLocalSchema extends ObjectSchemaBuilder<any, any,
|
|
|
565
1014
|
* const activeUsers = await query(db, UserSchema, base).where(t => t.age, '>', 18);
|
|
566
1015
|
* ```
|
|
567
1016
|
*/
|
|
568
|
-
export declare function query<TLocalSchema extends ObjectSchemaBuilder<any, any, any, any, any, any, any>>(knex: Knex, schema: TLocalSchema, baseQuery: Knex.QueryBuilder): SchemaQueryBuilder<TLocalSchema,
|
|
1017
|
+
export declare function query<TLocalSchema extends ObjectSchemaBuilder<any, any, any, any, any, any, any>>(knex: Knex, schema: TLocalSchema, baseQuery: Knex.QueryBuilder): SchemaQueryBuilder<TLocalSchema, QueryResultType<TLocalSchema>>;
|
|
569
1018
|
/** Bound query function returned by {@link createQuery}. */
|
|
570
1019
|
export interface BoundQuery {
|
|
571
|
-
<TLocalSchema extends ObjectSchemaBuilder<any, any, any, any, any, any, any>>(schema: TLocalSchema): SchemaQueryBuilder<TLocalSchema,
|
|
572
|
-
<TLocalSchema extends ObjectSchemaBuilder<any, any, any, any, any, any, any>>(schema: TLocalSchema, baseQuery: Knex.QueryBuilder): SchemaQueryBuilder<TLocalSchema,
|
|
1020
|
+
<TLocalSchema extends ObjectSchemaBuilder<any, any, any, any, any, any, any>>(schema: TLocalSchema): SchemaQueryBuilder<TLocalSchema, QueryResultType<TLocalSchema>>;
|
|
1021
|
+
<TLocalSchema extends ObjectSchemaBuilder<any, any, any, any, any, any, any>>(schema: TLocalSchema, baseQuery: Knex.QueryBuilder): SchemaQueryBuilder<TLocalSchema, QueryResultType<TLocalSchema>>;
|
|
573
1022
|
/**
|
|
574
1023
|
* Return a version of this bound factory whose queries all run within the
|
|
575
1024
|
* given Knex transaction. Equivalent to calling `.transacting(trx)` on
|
|
@@ -638,3 +1087,4 @@ export interface BoundQuery {
|
|
|
638
1087
|
* ```
|
|
639
1088
|
*/
|
|
640
1089
|
export declare function createQuery(knexInstance: Knex): BoundQuery;
|
|
1090
|
+
export {};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{arrayExtensions as p,defineExtension as d,NumberSchemaBuilder as x,numberExtensions as S,ObjectSchemaBuilder as g,StringSchemaBuilder as b,SYMBOL_SCHEMA_PROPERTY_DESCRIPTOR as E,stringExtensions as B,withExtensions as f}from"@cleverbrush/schema";import{EXTRA_TYPE_BRAND as Q,METHOD_LITERAL_BRAND as W}from"@cleverbrush/schema";var D=Symbol.for("@cleverbrush/knex-schema:primaryKey"),P=Symbol.for("@cleverbrush/knex-schema:compositePrimaryKey"),v=Symbol.for("@cleverbrush/knex-schema:polymorphicType");function r(n){return this.withExtension("columnName",n)}function T(n){return this.withExtension("tableName",n)}var w=d({string:{hasColumnName(n){return r.call(this,n)}},number:{hasColumnName(n){return r.call(this,n)}},boolean:{hasColumnName(n){return r.call(this,n)}},date:{hasColumnName(n){return r.call(this,n)}},any:{hasColumnName(n){return r.call(this,n)}},func:{hasColumnName(n){return r.call(this,n)}},array:{hasColumnName(n){return r.call(this,n)}},union:{hasColumnName(n){return r.call(this,n)}},generic:{hasColumnName(n){return r.call(this,n)}},object:{hasTableName(n){return T.call(this,n)}}}),R=d({number:{references(n,e="id"){return this.withExtension("references",{table:n,column:e})},onDelete(n){return this.withExtension("onDelete",n)},onUpdate(n){return this.withExtension("onUpdate",n)},defaultTo(n){return this.withExtension("defaultTo",n)},index(n){return this.withExtension("index",n??!0)},unique(n){return this.withExtension("unique",n??!0)},columnType(n){return this.withExtension("columnType",n)},bigint(){return this.withExtension("columnType","bigint")},smallint(){return this.withExtension("columnType","smallint")},decimal(n,e){return this.withExtension("columnType",`decimal(${n},${e})`)},defaultToRaw(n){return this.withExtension("defaultTo",{raw:n})},rowVersion(n){return this.withExtension("rowVersion",{strategy:n?.strategy??"increment"})}},string:{columnType(n){return this.withExtension("columnType",n)},text(){return this.withExtension("columnType","text")},asUuid(){return this.withExtension("columnType","uuid")},citext(){return this.withExtension("columnType","citext")},jsonb(){return this.withExtension("columnType","jsonb")},tsvector(){return this.withExtension("columnType","tsvector")},references(n,e="id"){return this.withExtension("references",{table:n,column:e})},onDelete(n){return this.withExtension("onDelete",n)},onUpdate(n){return this.withExtension("onUpdate",n)},unique(n){return this.withExtension("unique",n??!0)},index(n){return this.withExtension("index",n??!0)},defaultTo(n){return this.withExtension("defaultTo",n)},check(n){return this.withExtension("check",n)},defaultToRaw(n){return this.withExtension("defaultTo",{raw:n})},rowVersion(){return this.withExtension("rowVersion",{strategy:"manual"})}},boolean:{defaultTo(n){return this.withExtension("defaultTo",n)},columnType(n){return this.withExtension("columnType",n)},index(n){return this.withExtension("index",n??!0)},unique(n){return this.withExtension("unique",n??!0)}},date:{defaultTo(n){return this.withExtension("defaultTo",n)},columnType(n){return this.withExtension("columnType",n)},index(n){return this.withExtension("index",n??!0)},defaultToRaw(n){return this.withExtension("defaultTo",{raw:n})},timestamptz(){return this.withExtension("columnType","timestamptz")},dateOnly(){return this.withExtension("columnType","date")},rowVersion(){return this.withExtension("rowVersion",{strategy:"timestamp"})}},object:{columnType(n){return this.withExtension("columnType",n)},jsonb(){return this.withExtension("columnType","jsonb")},json(){return this.withExtension("columnType","json")},hasIndex(n,e){let a=this.getExtension("indexes")??[];return this.withExtension("indexes",[...a,{columns:n,...e}])},hasUnique(n,e){let a=this.getExtension("uniques")??[];return this.withExtension("uniques",[...a,{columns:n,name:e}])},hasCheck(n){let e=this.getExtension("checks")??[];return this.withExtension("checks",[...e,n])},hasRawColumn(n,e){let a=this.getExtension("rawColumns")??[];return this.withExtension("rawColumns",[...a,{name:n,definition:e}])},hasRawIndex(n){let e=this.getExtension("rawIndexes")??[];return this.withExtension("rawIndexes",[...e,n])},hasMany(n,e){let a=this.getExtension("relations")??[];return this.withExtension("relations",[...a,{type:"hasMany",name:n,...e}])},hasOne(n,e){let a=this.getExtension("relations")??[];return this.withExtension("relations",[...a,{type:"hasOne",name:n,...e}])},belongsTo(n,e){let a=this.getExtension("relations")??[];return this.withExtension("relations",[...a,{type:"belongsTo",name:n,...e}])},belongsToMany(n,e){let a=this.getExtension("relations")??[];return this.withExtension("relations",[...a,{type:"belongsToMany",name:n,...e}])},hasTimestamps(n){return this.withExtension("timestamps",{createdAt:n?.createdAt??"created_at",updatedAt:n?.updatedAt??"updated_at"})},softDelete(n){return this.withExtension("softDelete",{column:n?.column??"deleted_at"})},scope(n,e){let a=this.getExtension("scopes")??{};return this.withExtension("scopes",{...a,[n]:e})},projection(n,...e){let a=g.getPropertiesFor(this),y=e.map(h=>{if(typeof h=="string")return h;let t=h(a)[E];if(!t)throw new Error(`projection('${n}'): each accessor must return a valid PropertyDescriptor. Use \`t => t.propName\`.`);if(typeof t.propertyName!="string")throw new Error(`projection('${n}'): could not resolve property name from descriptor. Ensure the accessor returns a top-level property descriptor.`);return t.propertyName}),o=this.getExtension("projections")??{};if(Object.hasOwn(o,n))throw new Error(`projection('${n}'): a projection with this name is already registered on this schema. Each projection name must be unique.`);return this.withExtension("projections",{...o,[n]:{keys:y}})},defaultScope(n){return this.withExtension("defaultScope",n)},beforeInsert(n){let e=this.getExtension("beforeInsert")??[];return this.withExtension("beforeInsert",[...e,n])},afterInsert(n){let e=this.getExtension("afterInsert")??[];return this.withExtension("afterInsert",[...e,n])},beforeUpdate(n){let e=this.getExtension("beforeUpdate")??[];return this.withExtension("beforeUpdate",[...e,n])},beforeDelete(n){let e=this.getExtension("beforeDelete")??[];return this.withExtension("beforeDelete",[...e,n])}}});function I(n,e,a){let y={},o=[];for(let[i,t]of Object.entries(a)){let c=t.schema,m=c.introspect().properties??{};if(e in m){let l=m[e].introspect().equalsTo;if(l===void 0)throw new Error(`withVariants: variant "${i}" declares discriminator property "${e}" but it has no literal value \u2014 use string('${i}') instead of string()`);if(l!==i)throw new Error(`withVariants: variant "${i}" declares discriminator "${e}" = string('${l}') but the map key is '${i}' \u2014 they must match`)}let u;if(t.storage==="cti"){if(u=c.getExtension("tableName"),!u)throw new Error(`withVariants: CTI variant "${i}" schema must have .hasTableName() configured`);if(!t.foreignKeyColumn)throw new Error(`withVariants: CTI variant "${i}" must specify foreignKey (the FK property accessor on the variant schema)`)}y[i]={storage:t.storage,schema:c,foreignKey:t.foreignKeyColumn,tableName:u,allowOrphan:t.allowOrphan??!1,enforceCheck:t.enforceCheck??!1,relations:t.relations??[]},o.push(c)}let h={discriminatorKey:e,variants:y};return n.withExtension("variants",h).withExtension("polymorphicVariants",o)}var s=f(B,S,p,w,R),_=s.string,K=s.number,V=s.boolean,F=s.date,q=s.object,k=s.array,M=s.union,Y=s.func,U=s.any;(()=>{let n=x.prototype;typeof n.primaryKey!="function"&&(n.primaryKey=function(y){return this.withExtension("primaryKey",{autoIncrement:y?.autoIncrement??!0})});let e=b.prototype;typeof e.primaryKey!="function"&&(e.primaryKey=function(){return this.withExtension("primaryKey",{autoIncrement:!1})});let a=g.prototype;typeof a.hasPrimaryKey!="function"&&(a.hasPrimaryKey=function(y){return this.withExtension("compositePrimaryKey",y)})})();function $(n,e){let a=n.getExtension("columnName");return typeof a=="string"?a:e}function L(n){let e=n.getExtension("tableName");if(typeof e!="string")throw new Error('Schema does not have a table name. Use .hasTableName("table_name") to set one.');return e}function H(n){return n.getExtension("projections")??{}}function X(n){let e=n.getExtension("variants");return e??null}function z(n){return n.getExtension("polymorphicVariants")??[]}export{D as a,P as b,v as c,w as d,R as e,I as f,_ as g,K as h,V as i,F as j,q as k,k as l,M as m,Y as n,U as o,$ as p,L as q,H as r,X as s,z as t,Q as u,W as v};
|
|
2
|
+
//# sourceMappingURL=chunk-V4TH6K42.js.map
|