@prisma-next/sql-orm-client 0.9.0-dev.3 → 0.9.0-dev.4

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/index.d.mts CHANGED
@@ -516,57 +516,489 @@ declare class Collection<TContract extends Contract<SqlStorage>, ModelName exten
516
516
  /** @internal */
517
517
  readonly includeRefinementMode: boolean;
518
518
  constructor(ctx: CollectionContext<TContract>, modelName: ModelName, options?: CollectionInit<TContract>);
519
+ /**
520
+ * Narrow the collection with a `WHERE` predicate. Returns a new
521
+ * collection — chain further builders or run a terminal on it.
522
+ *
523
+ * Accepts a callback receiving a typed model accessor, a raw
524
+ * `WhereArg` expression, or a shorthand field/value object. Multiple
525
+ * calls are AND-combined.
526
+ *
527
+ * ```typescript
528
+ * // Callback form with column-level operators:
529
+ * const matches = await db.orm.User.where((u) => u.email.eq('alice@example.com')).all();
530
+ *
531
+ * // Shorthand object form:
532
+ * const user = await db.orm.User.where({ id: 1, active: true }).first();
533
+ *
534
+ * // Chained AND — still a builder, run a terminal to execute:
535
+ * const adults = await db.orm.User.where({ active: true }).where((u) => u.age.gt(18)).all();
536
+ * ```
537
+ */
519
538
  where(fn: (model: ModelAccessor<TContract, ModelName>) => WhereDirectInput): Collection<TContract, ModelName, Row, WithWhereState<State>>;
520
539
  where(input: WhereDirectInput): Collection<TContract, ModelName, Row, WithWhereState<State>>;
521
540
  where(fn: (model: ModelAccessor<TContract, ModelName>) => WhereArg): Collection<TContract, ModelName, Row, WithWhereState<State>>;
522
541
  where(filters: ShorthandWhereFilter<TContract, ModelName>): Collection<TContract, ModelName, Row, WithWhereState<State>>;
542
+ /**
543
+ * Narrow a polymorphic model to a specific variant. The returned
544
+ * collection has the variant's row shape and a discriminator filter
545
+ * is automatically applied. Chaining `.variant(...)` again replaces
546
+ * the previous variant filter.
547
+ *
548
+ * ```typescript
549
+ * // Read only admin users (STI):
550
+ * const admins = await db.orm.User.variant('Admin').all();
551
+ *
552
+ * // Iterate the rows:
553
+ * for await (const admin of db.orm.User.variant('Admin').all()) {
554
+ * console.log(admin.role);
555
+ * }
556
+ *
557
+ * // Insert under a variant — discriminator is injected automatically:
558
+ * await db.orm.User.variant('Admin').create({ name: 'Ada', role: 'super' });
559
+ * ```
560
+ */
523
561
  variant<V extends VariantNames<TContract, ModelName>>(variantName: V): Collection<TContract, ModelName, VariantModelRow<TContract, ModelName, V>, WithVariantState<WithWhereState<State>, V>>;
562
+ /**
563
+ * Eagerly load a related model. The relation appears on every
564
+ * returned row under its declared name; to-one relations are mapped
565
+ * to a single object (or `null`), to-many relations to an array.
566
+ *
567
+ * An optional refinement callback receives a child collection that
568
+ * can be further constrained, projected, ordered, paginated, or
569
+ * reduced to scalars via `count()`/`sum()`/etc. or to multiple
570
+ * sub-aggregates via `combine()`.
571
+ *
572
+ * ```typescript
573
+ * // Simple include — every user comes back with its posts array:
574
+ * const users = await db.orm.User.include('posts').all();
575
+ *
576
+ * // Refine the related collection:
577
+ * const withRecent = await db.orm.User.include('posts', (posts) =>
578
+ * posts.where({ published: true }).orderBy((p) => p.createdAt.desc()).take(5),
579
+ * ).all();
580
+ *
581
+ * // Reduce a to-many relation to a scalar value:
582
+ * const withCounts = await db.orm.User.include('posts', (posts) => posts.count()).all();
583
+ *
584
+ * // Multiple sub-views via combine():
585
+ * const overview = await db.orm.User.include('posts', (posts) =>
586
+ * posts.combine({ recent: posts.take(3), total: posts.count() }),
587
+ * ).all();
588
+ * ```
589
+ */
524
590
  include<RelName extends RelationNames<TContract, ModelName>, RelatedName extends RelatedModelName<TContract, ModelName, RelName> & string = RelatedModelName<TContract, ModelName, RelName> & string, IsToMany extends boolean = IsToManyRelation<TContract, ModelName, RelName>, RefinedResult extends IncludeRefinementResult<TContract, RelatedName, IsToMany> = IncludeRefinementCollection<TContract, RelatedName, DefaultModelRow<TContract, RelatedName>, CollectionTypeState, IsToMany>>(relationName: RelName, refineFn?: (collection: IncludeRefinementCollection<TContract, RelatedName, DefaultModelRow<TContract, RelatedName>, DefaultCollectionTypeState, IsToMany>) => RefinedResult): Collection<TContract, ModelName, SimplifyDeep<Row & { [K in RelName]: IncludeRefinementValue<TContract, ModelName, K, DefaultModelRow<TContract, RelatedName>, RefinedResult> }>, State>;
591
+ /**
592
+ * Project the row down to a subset of scalar fields. Previously
593
+ * included relations are preserved on the resulting row shape; only
594
+ * scalar columns are narrowed.
595
+ *
596
+ * ```typescript
597
+ * const summaries = await db.orm.User.select('id', 'email').all();
598
+ * // typeof summaries[number] === { id: ...; email: ... }
599
+ *
600
+ * for await (const row of db.orm.User.select('id', 'email').all()) {
601
+ * console.log(row.id, row.email);
602
+ * }
603
+ * ```
604
+ */
525
605
  select<Fields extends readonly [keyof DefaultModelRow<TContract, ModelName> & string, ...(keyof DefaultModelRow<TContract, ModelName> & string)[]]>(...fields: Fields): Collection<TContract, ModelName, SimplifyDeep<Pick<DefaultModelRow<TContract, ModelName>, Fields[number]> & IncludedRelationsForRow<TContract, ModelName, Row>>, State>;
606
+ /**
607
+ * Append an `ORDER BY` clause. Pass a single selector callback or an
608
+ * array of callbacks; each receives a typed model accessor whose
609
+ * columns expose `.asc()` and `.desc()`. Multiple calls append to the
610
+ * existing list (left-to-right ordering preserved).
611
+ *
612
+ * Calling `orderBy(...)` unlocks `cursor(...)` and `distinctOn(...)`,
613
+ * which both require a defined sort order.
614
+ *
615
+ * ```typescript
616
+ * const newest = await db.orm.User.orderBy((u) => u.createdAt.desc()).all();
617
+ *
618
+ * const byName = await db.orm.User
619
+ * .orderBy([(u) => u.lastName.asc(), (u) => u.firstName.asc()])
620
+ * .all();
621
+ * ```
622
+ */
526
623
  orderBy(selection: ((model: ModelAccessor<TContract, ModelName>) => OrderByItem) | ReadonlyArray<(model: ModelAccessor<TContract, ModelName>) => OrderByItem>): Collection<TContract, ModelName, Row, WithOrderByState<State>>;
624
+ /**
625
+ * Switch to grouped-aggregate mode. Returns a `GroupedCollection`
626
+ * whose `.aggregate(...)` terminal produces one row per group with
627
+ * the chosen key columns plus the requested aggregates.
628
+ *
629
+ * ```typescript
630
+ * const stats = await db.orm.Post
631
+ * .where({ published: true })
632
+ * .groupBy('userId')
633
+ * .aggregate((agg) => ({ count: agg.count(), totalViews: agg.sum('views') }));
634
+ * // [{ userId: 1, count: 3, totalViews: 120 }, ...]
635
+ * ```
636
+ */
527
637
  groupBy<Fields extends readonly [keyof DefaultModelRow<TContract, ModelName> & string, ...(keyof DefaultModelRow<TContract, ModelName> & string)[]]>(...fields: Fields): GroupedCollection<TContract, ModelName, Fields>;
638
+ /**
639
+ * Scalar reducer — reduces a to-many relation to the number of
640
+ * related rows. Use inside an `include(...)` refinement callback as
641
+ * `include(..., (rel) => rel.count())`; throws if called elsewhere.
642
+ * The parent row's relation field becomes that count instead of an
643
+ * array.
644
+ *
645
+ * ```typescript
646
+ * const users = await db.orm.User.include('posts', (posts) => posts.count()).all();
647
+ * // each user row: { ...user, posts: number }
648
+ * ```
649
+ */
528
650
  count(): IncludeScalar<number>;
651
+ /**
652
+ * Scalar reducer — reduces a to-many relation to the sum of `field`
653
+ * across related rows. Returns `null` when there are no related
654
+ * rows. Use inside an `include(...)` refinement callback; throws if
655
+ * called elsewhere.
656
+ *
657
+ * ```typescript
658
+ * const users = await db.orm.User.include('posts', (posts) => posts.sum('views')).all();
659
+ * // each user row: { ...user, posts: number | null }
660
+ * ```
661
+ */
529
662
  sum<FieldName extends NumericFieldNames<TContract, ModelName>>(field: FieldName): IncludeScalar<number | null>;
663
+ /**
664
+ * Scalar reducer — reduces a to-many relation to the average of
665
+ * `field` across related rows. Returns `null` when there are no
666
+ * related rows. Use inside an `include(...)` refinement callback;
667
+ * throws if called elsewhere.
668
+ *
669
+ * ```typescript
670
+ * const users = await db.orm.User.include('posts', (posts) => posts.avg('views')).all();
671
+ * // each user row: { ...user, posts: number | null }
672
+ * ```
673
+ */
530
674
  avg<FieldName extends NumericFieldNames<TContract, ModelName>>(field: FieldName): IncludeScalar<number | null>;
675
+ /**
676
+ * Scalar reducer — reduces a to-many relation to the minimum value
677
+ * of `field` across related rows. Returns `null` when there are no
678
+ * related rows. Use inside an `include(...)` refinement callback;
679
+ * throws if called elsewhere.
680
+ *
681
+ * ```typescript
682
+ * const users = await db.orm.User.include('posts', (posts) => posts.min('views')).all();
683
+ * ```
684
+ */
531
685
  min<FieldName extends NumericFieldNames<TContract, ModelName>>(field: FieldName): IncludeScalar<number | null>;
686
+ /**
687
+ * Scalar reducer — reduces a to-many relation to the maximum value
688
+ * of `field` across related rows. Returns `null` when there are no
689
+ * related rows. Use inside an `include(...)` refinement callback;
690
+ * throws if called elsewhere.
691
+ *
692
+ * ```typescript
693
+ * const users = await db.orm.User.include('posts', (posts) => posts.max('views')).all();
694
+ * ```
695
+ */
532
696
  max<FieldName extends NumericFieldNames<TContract, ModelName>>(field: FieldName): IncludeScalar<number | null>;
697
+ /**
698
+ * Produce multiple named sub-views of a to-many relation in a
699
+ * single `include(...)`. Each branch is either another refined
700
+ * collection (mapped to a row array on the parent) or a scalar
701
+ * reducer such as `count()`/`sum(...)`. Only valid inside an
702
+ * `include(...)` refinement callback for to-many relations.
703
+ *
704
+ * ```typescript
705
+ * const users = await db.orm.User.include('posts', (posts) =>
706
+ * posts.combine({
707
+ * recent: posts.where({ published: true }).take(3),
708
+ * total: posts.count(),
709
+ * averageViews: posts.avg('views'),
710
+ * }),
711
+ * ).all();
712
+ * // each user row: {
713
+ * // ...user,
714
+ * // posts: { recent: Post[]; total: number; averageViews: number | null };
715
+ * // }
716
+ * ```
717
+ */
533
718
  combine<Spec extends Record<string, Collection<TContract, ModelName, unknown, CollectionTypeState> | IncludeScalar<unknown>>>(spec: Spec): IncludeCombine<{ [K in keyof Spec]: Spec[K] extends IncludeScalar<infer ScalarResult> ? ScalarResult : Spec[K] extends Collection<TContract, ModelName, infer BranchRow, CollectionTypeState> ? BranchRow[] : never }>;
719
+ /**
720
+ * Resume pagination from a known cursor position. Requires a prior
721
+ * `orderBy(...)` so the cursor has a stable basis; provide a value
722
+ * for every column referenced by the active `orderBy(...)` so each
723
+ * ordered axis has a defined boundary.
724
+ *
725
+ * ```typescript
726
+ * const page1 = await db.orm.Post
727
+ * .orderBy((p) => p.createdAt.desc())
728
+ * .take(20)
729
+ * .all();
730
+ *
731
+ * const last = page1[page1.length - 1]!;
732
+ * const page2 = await db.orm.Post
733
+ * .orderBy((p) => p.createdAt.desc())
734
+ * .cursor({ createdAt: last.createdAt })
735
+ * .take(20)
736
+ * .all();
737
+ * ```
738
+ */
534
739
  cursor(cursorValues: State['hasOrderBy'] extends true ? Partial<Record<keyof DefaultModelRow<TContract, ModelName> & string, unknown>> : never): Collection<TContract, ModelName, Row, State>;
740
+ /**
741
+ * Emit `SELECT DISTINCT` keyed on the given fields. Replaces any
742
+ * previous `distinct(...)` / `distinctOn(...)` selection.
743
+ *
744
+ * ```typescript
745
+ * const groups = await db.orm.User.distinct('country', 'role').all();
746
+ * ```
747
+ */
535
748
  distinct<Fields extends readonly [keyof DefaultModelRow<TContract, ModelName> & string, ...(keyof DefaultModelRow<TContract, ModelName> & string)[]]>(...fields: Fields): Collection<TContract, ModelName, Row, State>;
749
+ /**
750
+ * Emit `SELECT DISTINCT ON (fields)` — keep the first row per
751
+ * distinct key according to the current `orderBy(...)`. Requires a
752
+ * prior `orderBy(...)`; replaces any previous `distinct(...)` /
753
+ * `distinctOn(...)` selection.
754
+ *
755
+ * ```typescript
756
+ * // Latest post per user:
757
+ * const latestPerUser = await db.orm.Post
758
+ * .orderBy([(p) => p.userId.asc(), (p) => p.createdAt.desc()])
759
+ * .distinctOn('userId')
760
+ * .all();
761
+ * ```
762
+ */
536
763
  distinctOn<Fields extends readonly [keyof DefaultModelRow<TContract, ModelName> & string, ...(keyof DefaultModelRow<TContract, ModelName> & string)[]]>(...fields: State['hasOrderBy'] extends true ? Fields : never): Collection<TContract, ModelName, Row, State>;
764
+ /**
765
+ * Apply `LIMIT n`. Replaces any previous limit set on this collection.
766
+ *
767
+ * ```typescript
768
+ * const firstTen = await db.orm.User.orderBy((u) => u.id.asc()).take(10).all();
769
+ * ```
770
+ */
537
771
  take(n: number): Collection<TContract, ModelName, Row, State>;
772
+ /**
773
+ * Apply `OFFSET n`. Replaces any previous offset set on this collection.
774
+ *
775
+ * ```typescript
776
+ * const page2 = await db.orm.User
777
+ * .orderBy((u) => u.id.asc())
778
+ * .skip(10)
779
+ * .take(10)
780
+ * .all();
781
+ * ```
782
+ */
538
783
  skip(n: number): Collection<TContract, ModelName, Row, State>;
539
784
  /**
540
- * Read terminal: stream all rows matching the current state.
785
+ * Read terminal: execute the query and stream every matching row.
786
+ *
787
+ * The returned `AsyncIterableResult<Row>` is BOTH a thenable that
788
+ * resolves to `Row[]` (so `await` collects all rows into an array)
789
+ * AND an async iterable (so `for await` streams rows as they
790
+ * arrive, without buffering the whole result set in memory). Pick
791
+ * whichever fits the caller. A single result can only be consumed
792
+ * once.
793
+ *
794
+ * Streaming is the default and the expected execution model. The
795
+ * only scenarios that fall back to buffering internally before
796
+ * yielding are drivers that cannot expose a cursor to the
797
+ * underlying database, and — for queries with `include(...)` —
798
+ * targets whose SQL dialect supports neither lateral joins nor
799
+ * correlated subqueries (so child rows cannot be stitched in a
800
+ * single streaming query). These are implementation details below
801
+ * the public API; the iteration shape itself is genuinely
802
+ * streaming whenever the driver and plan allow it.
803
+ *
804
+ * ```typescript
805
+ * // Thenable — collect to an array:
806
+ * const users = await db.orm.User.all();
807
+ * for (const user of users) console.log(user.id);
808
+ *
809
+ * // Async iterable — stream rows as they arrive:
810
+ * for await (const user of db.orm.User.all()) {
811
+ * console.log(user.id);
812
+ * }
813
+ * ```
541
814
  *
542
815
  * Accepts an optional `configure` callback that receives a
543
816
  * `MetaBuilder<'read'>` so the caller can attach typed user
544
817
  * annotations to the executed plan. `meta.annotate(...)` enforces
545
818
  * applicability at the type level and at runtime; annotations are
546
819
  * merged into `plan.meta.annotations` at compile time.
820
+ *
821
+ * ```typescript
822
+ * await db.orm.User.all((meta) => meta.annotate(cacheAnnotation({ ttl: 60 })));
823
+ * ```
547
824
  */
548
825
  all(configure?: (meta: MetaBuilder<'read'>) => void): AsyncIterableResult<Row>;
826
+ /**
827
+ * Read terminal: return the first matching row, or `null` if none
828
+ * match. Optionally accepts a filter (callback or shorthand object)
829
+ * followed by a `configure` callback for typed read annotations.
830
+ *
831
+ * To attach annotations without further narrowing, pass `undefined`
832
+ * as the filter (or chain `.where(...)` first):
833
+ *
834
+ * ```typescript
835
+ * // No filter — first row in the collection:
836
+ * const someone = await db.orm.User.first();
837
+ *
838
+ * // Shorthand filter:
839
+ * const alice = await db.orm.User.first({ email: 'alice@example.com' });
840
+ *
841
+ * // Callback filter:
842
+ * const old = await db.orm.User.first((u) => u.age.gt(60));
843
+ *
844
+ * // Annotate without filtering further:
845
+ * await db.orm.User.first(undefined, (meta) =>
846
+ * meta.annotate(cacheAnnotation({ ttl: 60 })),
847
+ * );
848
+ * ```
849
+ */
549
850
  first(): Promise<Row | null>;
550
851
  first(filter: undefined, configure: (meta: MetaBuilder<'read'>) => void): Promise<Row | null>;
551
852
  first(filter: (model: ModelAccessor<TContract, ModelName>) => WhereArg, configure?: (meta: MetaBuilder<'read'>) => void): Promise<Row | null>;
552
853
  first(filter: ShorthandWhereFilter<TContract, ModelName>, configure?: (meta: MetaBuilder<'read'>) => void): Promise<Row | null>;
553
854
  /**
554
855
  * Read terminal: run an aggregate query (count, sum, avg, min, max)
555
- * built via the `AggregateBuilder` callback.
856
+ * built via the `AggregateBuilder` callback. Returns one object
857
+ * with the requested aggregate values keyed by the aliases supplied
858
+ * in the spec.
859
+ *
860
+ * ```typescript
861
+ * const stats = await db.orm.Post
862
+ * .where({ published: true })
863
+ * .aggregate((agg) => ({
864
+ * total: agg.count(),
865
+ * averageViews: agg.avg('views'),
866
+ * maxViews: agg.max('views'),
867
+ * }));
868
+ * // { total: 42, averageViews: 17.3, maxViews: 9001 }
869
+ * ```
556
870
  *
557
871
  * Accepts an optional `configure` callback that receives a
558
872
  * `MetaBuilder<'read'>` for attaching typed annotations.
559
873
  * Annotations are merged into the compiled plan's `meta.annotations`.
560
874
  */
561
875
  aggregate<Spec extends AggregateSpec>(fn: (aggregate: AggregateBuilder<TContract, ModelName>) => Spec, configure?: (meta: MetaBuilder<'read'>) => void): Promise<AggregateResult<Spec>>;
876
+ /**
877
+ * Write terminal: insert one row and return it (with any configured
878
+ * `select(...)` / `include(...)` projections applied to the returned
879
+ * shape).
880
+ *
881
+ * Related rows can be created or linked through relation callbacks
882
+ * on parent/child-owned relations (one-to-one or one-to-many).
883
+ * The callback receives a mutator exposing `create(...)` and
884
+ * `connect(...)`; `disconnect(...)` is only supported in nested
885
+ * `update(...)` mutations. Many-to-many relations are not yet
886
+ * supported as nested-mutation targets.
887
+ *
888
+ * ```typescript
889
+ * // Simple insert:
890
+ * const user = await db.orm.User.create({
891
+ * email: 'alice@example.com',
892
+ * name: 'Alice',
893
+ * });
894
+ *
895
+ * // Nested create on a child-owned to-many relation:
896
+ * const author = await db.orm.User.create({
897
+ * email: 'bob@example.com',
898
+ * posts: (posts) => posts.create([
899
+ * { title: 'Hello' },
900
+ * { title: 'World' },
901
+ * ]),
902
+ * });
903
+ *
904
+ * // Connect a child-owned post to an existing parent author:
905
+ * const reply = await db.orm.Post.create({
906
+ * title: 'Re: Hello',
907
+ * author: (author) => author.connect({ id: 1 }),
908
+ * });
909
+ * ```
910
+ *
911
+ * Accepts an optional `configure` callback that receives a
912
+ * `MetaBuilder<'write'>` for attaching typed annotations.
913
+ * Annotations are merged into the compiled mutation plan's
914
+ * `meta.annotations`.
915
+ *
916
+ * Note: when the input contains nested-mutation callbacks, the
917
+ * operation is executed as a graph of internal queries via
918
+ * `withMutationScope`. In that path, annotations apply to the
919
+ * logical `create()` call but do not currently flow into each
920
+ * constituent SQL statement issued for the related rows.
921
+ */
562
922
  create(data: ResolvedCreateInput<TContract, ModelName, State['variantName']>, configure?: (meta: MetaBuilder<'write'>) => void): Promise<Row>;
563
923
  create(data: MutationCreateInputWithRelations<TContract, ModelName>, configure?: (meta: MetaBuilder<'write'>) => void): Promise<Row>;
924
+ /**
925
+ * Write terminal: insert many rows and stream the inserted rows.
926
+ *
927
+ * The returned `AsyncIterableResult<Row>` is BOTH a thenable that
928
+ * resolves to `Row[]` AND an async iterable that streams inserted
929
+ * rows as they arrive. Use whichever shape fits the caller — but
930
+ * only consume it once. Streaming is the default; some
931
+ * driver/plan combinations may still buffer internally before
932
+ * yielding.
933
+ *
934
+ * ```typescript
935
+ * // Thenable — collect all inserted rows into an array:
936
+ * const created = await db.orm.User.createAll([
937
+ * { email: 'a@example.com' },
938
+ * { email: 'b@example.com' },
939
+ * ]);
940
+ *
941
+ * // Async iterable — stream inserted rows as they arrive:
942
+ * for await (const row of db.orm.User.createAll(seedUsers)) {
943
+ * console.log('inserted', row.id);
944
+ * }
945
+ * ```
946
+ *
947
+ * Accepts an optional `configure` callback that receives a
948
+ * `MetaBuilder<'write'>` for attaching typed annotations to the
949
+ * compiled insert plan.
950
+ */
564
951
  createAll(data: readonly ResolvedCreateInput<TContract, ModelName, State['variantName']>[], configure?: (meta: MetaBuilder<'write'>) => void): AsyncIterableResult<Row>;
952
+ /**
953
+ * Write terminal: insert many rows without materializing the
954
+ * inserted rows, returning the number of inserted records.
955
+ *
956
+ * Prefer `createAll(...)` when you need the returned rows; prefer
957
+ * this when you only need to know how many rows were inserted (the
958
+ * compiled plan skips `RETURNING`).
959
+ *
960
+ * ```typescript
961
+ * const inserted = await db.orm.User.createCount([
962
+ * { email: 'a@example.com' },
963
+ * { email: 'b@example.com' },
964
+ * ]);
965
+ * // inserted === 2
966
+ * ```
967
+ *
968
+ * Not supported on MTI variants — use `createAll(...)` instead.
969
+ */
565
970
  createCount(data: readonly ResolvedCreateInput<TContract, ModelName, State['variantName']>[], configure?: (meta: MetaBuilder<'write'>) => void): Promise<number>;
566
971
  /**
567
- * Passing `update: {}` makes this behave like a conditional create.
568
- * On conflict, `ON CONFLICT DO NOTHING RETURNING ...` may return zero rows,
569
- * so this method may issue a follow-up reload query to return the existing row.
972
+ * Write terminal: insert a row, or update the existing row on
973
+ * conflict. Returns the resulting row (the inserted one or the
974
+ * updated/existing one).
975
+ *
976
+ * `conflictOn` selects which unique constraint drives the conflict
977
+ * resolution — omit to use the model's primary key.
978
+ *
979
+ * ```typescript
980
+ * // Insert-or-update on email uniqueness:
981
+ * await db.orm.User.upsert({
982
+ * create: { email: 'alice@example.com', name: 'Alice' },
983
+ * update: { name: 'Alice (updated)' },
984
+ * conflictOn: { email: 'alice@example.com' },
985
+ * });
986
+ *
987
+ * // Conditional create — `update: {}` keeps the existing row
988
+ * // unchanged. `conflictOn` must reference the constraint that
989
+ * // makes the row "already exist"; omit only when the conflict is
990
+ * // on the primary key. On conflict,
991
+ * // `ON CONFLICT DO NOTHING RETURNING ...` may return zero rows,
992
+ * // so a follow-up reload is issued to fetch and return the
993
+ * // existing row.
994
+ * await db.orm.User.upsert({
995
+ * create: { email: 'alice@example.com', name: 'Alice' },
996
+ * update: {},
997
+ * conflictOn: { email: 'alice@example.com' },
998
+ * });
999
+ * ```
1000
+ *
1001
+ * Not supported on MTI variants.
570
1002
  */
571
1003
  upsert(input: {
572
1004
  create: ResolvedCreateInput<TContract, ModelName, State['variantName']>;
@@ -575,7 +1007,30 @@ declare class Collection<TContract extends Contract<SqlStorage>, ModelName exten
575
1007
  }, configure?: (meta: MetaBuilder<'write'>) => void): Promise<Row>;
576
1008
  /**
577
1009
  * Write terminal: update matching rows and return the first one (or
578
- * null when no row matched).
1010
+ * `null` when no row matched). Requires a prior `.where(...)` —
1011
+ * calling `update(...)` on an unfiltered collection is a type error.
1012
+ *
1013
+ * Related rows can be created or relinked through relation
1014
+ * callbacks on parent/child-owned relations (one-to-one or
1015
+ * one-to-many). The callback receives a mutator exposing
1016
+ * `create(...)`, `connect(...)`, and `disconnect(...)`. Nested
1017
+ * updates against existing related rows, and many-to-many relations
1018
+ * as nested-mutation targets, are not supported through this API.
1019
+ *
1020
+ * ```typescript
1021
+ * // Update one row by id:
1022
+ * const updated = await db.orm.User
1023
+ * .where({ id: 1 })
1024
+ * .update({ name: 'Alice Renamed' });
1025
+ *
1026
+ * // Update + relink — runs as a graph of internal mutations:
1027
+ * await db.orm.User
1028
+ * .where({ id: 1 })
1029
+ * .update({
1030
+ * name: 'Alice',
1031
+ * posts: (posts) => posts.connect([{ id: 5 }]),
1032
+ * });
1033
+ * ```
579
1034
  *
580
1035
  * Accepts an optional `configure` callback that receives a
581
1036
  * `MetaBuilder<'write'>` for attaching typed annotations.
@@ -587,17 +1042,97 @@ declare class Collection<TContract extends Contract<SqlStorage>, ModelName exten
587
1042
  * statement issued for the related rows.
588
1043
  */
589
1044
  update(data: State['hasWhere'] extends true ? MutationUpdateInput<TContract, ModelName> : never, configure?: (meta: MetaBuilder<'write'>) => void): Promise<Row | null>;
1045
+ /**
1046
+ * Write terminal: update every matching row and stream the updated
1047
+ * rows. Requires a prior `.where(...)` filter.
1048
+ *
1049
+ * The returned `AsyncIterableResult<Row>` is BOTH a thenable that
1050
+ * resolves to `Row[]` AND an async iterable that streams updated
1051
+ * rows as they arrive. Use whichever fits; a result can only be
1052
+ * consumed once. Streaming is the default; some driver/plan
1053
+ * combinations may still buffer internally before yielding.
1054
+ *
1055
+ * ```typescript
1056
+ * // Thenable — collect updated rows into an array:
1057
+ * const updated = await db.orm.Post
1058
+ * .where({ published: false })
1059
+ * .updateAll({ published: true });
1060
+ *
1061
+ * // Async iterable — stream updated rows as they arrive:
1062
+ * for await (const row of db.orm.Post.where({ draft: true }).updateAll({ draft: false })) {
1063
+ * console.log('published', row.id);
1064
+ * }
1065
+ * ```
1066
+ *
1067
+ * Accepts an optional `configure` callback that receives a
1068
+ * `MetaBuilder<'write'>` for attaching typed annotations.
1069
+ */
590
1070
  updateAll(data: State['hasWhere'] extends true ? Partial<DefaultModelRow<TContract, ModelName>> : never, configure?: (meta: MetaBuilder<'write'>) => void): AsyncIterableResult<Row>;
1071
+ /**
1072
+ * Write terminal: update every matching row without returning them,
1073
+ * resolving to the count of rows that were updated. Requires a prior
1074
+ * `.where(...)` filter.
1075
+ *
1076
+ * Prefer `updateAll(...)` when you need the updated rows; prefer
1077
+ * this when you only need the affected-row count.
1078
+ *
1079
+ * ```typescript
1080
+ * const count = await db.orm.Post
1081
+ * .where({ published: false })
1082
+ * .updateCount({ published: true });
1083
+ * ```
1084
+ */
591
1085
  updateCount(data: State['hasWhere'] extends true ? Partial<DefaultModelRow<TContract, ModelName>> : never, configure?: (meta: MetaBuilder<'write'>) => void): Promise<number>;
592
1086
  /**
593
- * Write terminal: delete matching rows and return the first one (or
594
- * null when no row matched).
1087
+ * Write terminal: delete matching rows and return the first deleted
1088
+ * row (or `null` when no row matched). Requires a prior `.where(...)`
1089
+ * — calling `delete()` on an unfiltered collection is a type error.
1090
+ *
1091
+ * ```typescript
1092
+ * const deleted = await db.orm.User.where({ id: 1 }).delete();
1093
+ * if (deleted) console.log('deleted', deleted.email);
1094
+ * ```
595
1095
  *
596
1096
  * Accepts an optional `configure` callback that receives a
597
1097
  * `MetaBuilder<'write'>` for attaching typed annotations.
598
1098
  */
599
1099
  delete(this: State['hasWhere'] extends true ? Collection<TContract, ModelName, Row, State> : never, configure?: (meta: MetaBuilder<'write'>) => void): Promise<Row | null>;
1100
+ /**
1101
+ * Write terminal: delete every matching row and stream the deleted
1102
+ * rows. Requires a prior `.where(...)` filter.
1103
+ *
1104
+ * The returned `AsyncIterableResult<Row>` is BOTH a thenable that
1105
+ * resolves to `Row[]` AND an async iterable that streams deleted
1106
+ * rows as they arrive. Use whichever fits; a result can only be
1107
+ * consumed once. Streaming is the default; some driver/plan
1108
+ * combinations may still buffer internally before yielding.
1109
+ *
1110
+ * ```typescript
1111
+ * // Thenable — collect the deleted rows into an array:
1112
+ * const deleted = await db.orm.Post.where({ archived: true }).deleteAll();
1113
+ *
1114
+ * // Async iterable — stream deleted rows as they arrive:
1115
+ * for await (const row of db.orm.Post.where({ archived: true }).deleteAll()) {
1116
+ * console.log('removed', row.id);
1117
+ * }
1118
+ * ```
1119
+ *
1120
+ * Accepts an optional `configure` callback that receives a
1121
+ * `MetaBuilder<'write'>` for attaching typed annotations.
1122
+ */
600
1123
  deleteAll(this: State['hasWhere'] extends true ? Collection<TContract, ModelName, Row, State> : never, configure?: (meta: MetaBuilder<'write'>) => void): AsyncIterableResult<Row>;
1124
+ /**
1125
+ * Write terminal: delete every matching row without returning them,
1126
+ * resolving to the count of rows that were deleted. Requires a prior
1127
+ * `.where(...)` filter.
1128
+ *
1129
+ * Prefer `deleteAll(...)` when you need the deleted rows; prefer
1130
+ * this when you only need the affected-row count.
1131
+ *
1132
+ * ```typescript
1133
+ * const removed = await db.orm.Post.where({ archived: true }).deleteCount();
1134
+ * ```
1135
+ */
601
1136
  deleteCount(this: State['hasWhere'] extends true ? Collection<TContract, ModelName, Row, State> : never, configure?: (meta: MetaBuilder<'write'>) => void): Promise<number>;
602
1137
  }
603
1138
  //#endregion