@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.mjs CHANGED
@@ -2224,6 +2224,25 @@ var Collection = class Collection {
2224
2224
  if (!filter) return this;
2225
2225
  return this.#clone({ filters: [...this.state.filters, filter] });
2226
2226
  }
2227
+ /**
2228
+ * Narrow a polymorphic model to a specific variant. The returned
2229
+ * collection has the variant's row shape and a discriminator filter
2230
+ * is automatically applied. Chaining `.variant(...)` again replaces
2231
+ * the previous variant filter.
2232
+ *
2233
+ * ```typescript
2234
+ * // Read only admin users (STI):
2235
+ * const admins = await db.orm.User.variant('Admin').all();
2236
+ *
2237
+ * // Iterate the rows:
2238
+ * for await (const admin of db.orm.User.variant('Admin').all()) {
2239
+ * console.log(admin.role);
2240
+ * }
2241
+ *
2242
+ * // Insert under a variant — discriminator is injected automatically:
2243
+ * await db.orm.User.variant('Admin').create({ name: 'Ada', role: 'super' });
2244
+ * ```
2245
+ */
2227
2246
  variant(variantName) {
2228
2247
  const model = this.contract.models[this.modelName];
2229
2248
  const discriminator = model?.["discriminator"];
@@ -2239,6 +2258,34 @@ var Collection = class Collection {
2239
2258
  variantName
2240
2259
  });
2241
2260
  }
2261
+ /**
2262
+ * Eagerly load a related model. The relation appears on every
2263
+ * returned row under its declared name; to-one relations are mapped
2264
+ * to a single object (or `null`), to-many relations to an array.
2265
+ *
2266
+ * An optional refinement callback receives a child collection that
2267
+ * can be further constrained, projected, ordered, paginated, or
2268
+ * reduced to scalars via `count()`/`sum()`/etc. or to multiple
2269
+ * sub-aggregates via `combine()`.
2270
+ *
2271
+ * ```typescript
2272
+ * // Simple include — every user comes back with its posts array:
2273
+ * const users = await db.orm.User.include('posts').all();
2274
+ *
2275
+ * // Refine the related collection:
2276
+ * const withRecent = await db.orm.User.include('posts', (posts) =>
2277
+ * posts.where({ published: true }).orderBy((p) => p.createdAt.desc()).take(5),
2278
+ * ).all();
2279
+ *
2280
+ * // Reduce a to-many relation to a scalar value:
2281
+ * const withCounts = await db.orm.User.include('posts', (posts) => posts.count()).all();
2282
+ *
2283
+ * // Multiple sub-views via combine():
2284
+ * const overview = await db.orm.User.include('posts', (posts) =>
2285
+ * posts.combine({ recent: posts.take(3), total: posts.count() }),
2286
+ * ).all();
2287
+ * ```
2288
+ */
2242
2289
  include(relationName, refineFn) {
2243
2290
  const relation = resolveIncludeRelation(this.contract, this.modelName, relationName);
2244
2291
  let nestedState = emptyState();
@@ -2273,16 +2320,60 @@ var Collection = class Collection {
2273
2320
  };
2274
2321
  return this.#cloneWithRow({ includes: [...this.state.includes, includeExpr] });
2275
2322
  }
2323
+ /**
2324
+ * Project the row down to a subset of scalar fields. Previously
2325
+ * included relations are preserved on the resulting row shape; only
2326
+ * scalar columns are narrowed.
2327
+ *
2328
+ * ```typescript
2329
+ * const summaries = await db.orm.User.select('id', 'email').all();
2330
+ * // typeof summaries[number] === { id: ...; email: ... }
2331
+ *
2332
+ * for await (const row of db.orm.User.select('id', 'email').all()) {
2333
+ * console.log(row.id, row.email);
2334
+ * }
2335
+ * ```
2336
+ */
2276
2337
  select(...fields) {
2277
2338
  const selectedFields = mapFieldsToColumns(this.contract, this.modelName, fields);
2278
2339
  return this.#cloneWithRow({ selectedFields });
2279
2340
  }
2341
+ /**
2342
+ * Append an `ORDER BY` clause. Pass a single selector callback or an
2343
+ * array of callbacks; each receives a typed model accessor whose
2344
+ * columns expose `.asc()` and `.desc()`. Multiple calls append to the
2345
+ * existing list (left-to-right ordering preserved).
2346
+ *
2347
+ * Calling `orderBy(...)` unlocks `cursor(...)` and `distinctOn(...)`,
2348
+ * which both require a defined sort order.
2349
+ *
2350
+ * ```typescript
2351
+ * const newest = await db.orm.User.orderBy((u) => u.createdAt.desc()).all();
2352
+ *
2353
+ * const byName = await db.orm.User
2354
+ * .orderBy([(u) => u.lastName.asc(), (u) => u.firstName.asc()])
2355
+ * .all();
2356
+ * ```
2357
+ */
2280
2358
  orderBy(selection) {
2281
2359
  const accessor = createModelAccessor(this.ctx.context, this.modelName);
2282
2360
  const nextOrders = (Array.isArray(selection) ? selection : [selection]).map((selector) => selector(accessor));
2283
2361
  const existing = this.state.orderBy ?? [];
2284
2362
  return this.#clone({ orderBy: [...existing, ...nextOrders] });
2285
2363
  }
2364
+ /**
2365
+ * Switch to grouped-aggregate mode. Returns a `GroupedCollection`
2366
+ * whose `.aggregate(...)` terminal produces one row per group with
2367
+ * the chosen key columns plus the requested aggregates.
2368
+ *
2369
+ * ```typescript
2370
+ * const stats = await db.orm.Post
2371
+ * .where({ published: true })
2372
+ * .groupBy('userId')
2373
+ * .aggregate((agg) => ({ count: agg.count(), totalViews: agg.sum('views') }));
2374
+ * // [{ userId: 1, count: 3, totalViews: 120 }, ...]
2375
+ * ```
2376
+ */
2286
2377
  groupBy(...fields) {
2287
2378
  const groupByColumns = mapFieldsToColumns(this.contract, this.modelName, fields);
2288
2379
  return new GroupedCollection(this.ctx, this.modelName, {
@@ -2293,30 +2384,105 @@ var Collection = class Collection {
2293
2384
  havingFilters: []
2294
2385
  });
2295
2386
  }
2387
+ /**
2388
+ * Scalar reducer — reduces a to-many relation to the number of
2389
+ * related rows. Use inside an `include(...)` refinement callback as
2390
+ * `include(..., (rel) => rel.count())`; throws if called elsewhere.
2391
+ * The parent row's relation field becomes that count instead of an
2392
+ * array.
2393
+ *
2394
+ * ```typescript
2395
+ * const users = await db.orm.User.include('posts', (posts) => posts.count()).all();
2396
+ * // each user row: { ...user, posts: number }
2397
+ * ```
2398
+ */
2296
2399
  count() {
2297
2400
  this.#assertIncludeRefinementMode("count()");
2298
2401
  return createIncludeScalar("count", this.state);
2299
2402
  }
2403
+ /**
2404
+ * Scalar reducer — reduces a to-many relation to the sum of `field`
2405
+ * across related rows. Returns `null` when there are no related
2406
+ * rows. Use inside an `include(...)` refinement callback; throws if
2407
+ * called elsewhere.
2408
+ *
2409
+ * ```typescript
2410
+ * const users = await db.orm.User.include('posts', (posts) => posts.sum('views')).all();
2411
+ * // each user row: { ...user, posts: number | null }
2412
+ * ```
2413
+ */
2300
2414
  sum(field) {
2301
2415
  this.#assertIncludeRefinementMode("sum()");
2302
2416
  const columnName = resolveFieldToColumn(this.contract, this.modelName, field);
2303
2417
  return createIncludeScalar("sum", this.state, columnName);
2304
2418
  }
2419
+ /**
2420
+ * Scalar reducer — reduces a to-many relation to the average of
2421
+ * `field` across related rows. Returns `null` when there are no
2422
+ * related rows. Use inside an `include(...)` refinement callback;
2423
+ * throws if called elsewhere.
2424
+ *
2425
+ * ```typescript
2426
+ * const users = await db.orm.User.include('posts', (posts) => posts.avg('views')).all();
2427
+ * // each user row: { ...user, posts: number | null }
2428
+ * ```
2429
+ */
2305
2430
  avg(field) {
2306
2431
  this.#assertIncludeRefinementMode("avg()");
2307
2432
  const columnName = resolveFieldToColumn(this.contract, this.modelName, field);
2308
2433
  return createIncludeScalar("avg", this.state, columnName);
2309
2434
  }
2435
+ /**
2436
+ * Scalar reducer — reduces a to-many relation to the minimum value
2437
+ * of `field` across related rows. Returns `null` when there are no
2438
+ * related rows. Use inside an `include(...)` refinement callback;
2439
+ * throws if called elsewhere.
2440
+ *
2441
+ * ```typescript
2442
+ * const users = await db.orm.User.include('posts', (posts) => posts.min('views')).all();
2443
+ * ```
2444
+ */
2310
2445
  min(field) {
2311
2446
  this.#assertIncludeRefinementMode("min()");
2312
2447
  const columnName = resolveFieldToColumn(this.contract, this.modelName, field);
2313
2448
  return createIncludeScalar("min", this.state, columnName);
2314
2449
  }
2450
+ /**
2451
+ * Scalar reducer — reduces a to-many relation to the maximum value
2452
+ * of `field` across related rows. Returns `null` when there are no
2453
+ * related rows. Use inside an `include(...)` refinement callback;
2454
+ * throws if called elsewhere.
2455
+ *
2456
+ * ```typescript
2457
+ * const users = await db.orm.User.include('posts', (posts) => posts.max('views')).all();
2458
+ * ```
2459
+ */
2315
2460
  max(field) {
2316
2461
  this.#assertIncludeRefinementMode("max()");
2317
2462
  const columnName = resolveFieldToColumn(this.contract, this.modelName, field);
2318
2463
  return createIncludeScalar("max", this.state, columnName);
2319
2464
  }
2465
+ /**
2466
+ * Produce multiple named sub-views of a to-many relation in a
2467
+ * single `include(...)`. Each branch is either another refined
2468
+ * collection (mapped to a row array on the parent) or a scalar
2469
+ * reducer such as `count()`/`sum(...)`. Only valid inside an
2470
+ * `include(...)` refinement callback for to-many relations.
2471
+ *
2472
+ * ```typescript
2473
+ * const users = await db.orm.User.include('posts', (posts) =>
2474
+ * posts.combine({
2475
+ * recent: posts.where({ published: true }).take(3),
2476
+ * total: posts.count(),
2477
+ * averageViews: posts.avg('views'),
2478
+ * }),
2479
+ * ).all();
2480
+ * // each user row: {
2481
+ * // ...user,
2482
+ * // posts: { recent: Post[]; total: number; averageViews: number | null };
2483
+ * // }
2484
+ * ```
2485
+ */
2320
2486
  combine(spec) {
2321
2487
  this.#assertIncludeRefinementMode("combine()");
2322
2488
  const branches = {};
@@ -2339,11 +2505,39 @@ var Collection = class Collection {
2339
2505
  }
2340
2506
  return createIncludeCombine(branches);
2341
2507
  }
2508
+ /**
2509
+ * Resume pagination from a known cursor position. Requires a prior
2510
+ * `orderBy(...)` so the cursor has a stable basis; provide a value
2511
+ * for every column referenced by the active `orderBy(...)` so each
2512
+ * ordered axis has a defined boundary.
2513
+ *
2514
+ * ```typescript
2515
+ * const page1 = await db.orm.Post
2516
+ * .orderBy((p) => p.createdAt.desc())
2517
+ * .take(20)
2518
+ * .all();
2519
+ *
2520
+ * const last = page1[page1.length - 1]!;
2521
+ * const page2 = await db.orm.Post
2522
+ * .orderBy((p) => p.createdAt.desc())
2523
+ * .cursor({ createdAt: last.createdAt })
2524
+ * .take(20)
2525
+ * .all();
2526
+ * ```
2527
+ */
2342
2528
  cursor(cursorValues) {
2343
2529
  const mappedCursor = mapCursorValuesToColumns(this.contract, this.modelName, cursorValues);
2344
2530
  if (Object.keys(mappedCursor).length === 0) return this;
2345
2531
  return this.#clone({ cursor: mappedCursor });
2346
2532
  }
2533
+ /**
2534
+ * Emit `SELECT DISTINCT` keyed on the given fields. Replaces any
2535
+ * previous `distinct(...)` / `distinctOn(...)` selection.
2536
+ *
2537
+ * ```typescript
2538
+ * const groups = await db.orm.User.distinct('country', 'role').all();
2539
+ * ```
2540
+ */
2347
2541
  distinct(...fields) {
2348
2542
  const distinctFields = mapFieldsToColumns(this.contract, this.modelName, fields);
2349
2543
  return this.#clone({
@@ -2351,6 +2545,20 @@ var Collection = class Collection {
2351
2545
  distinctOn: void 0
2352
2546
  });
2353
2547
  }
2548
+ /**
2549
+ * Emit `SELECT DISTINCT ON (fields)` — keep the first row per
2550
+ * distinct key according to the current `orderBy(...)`. Requires a
2551
+ * prior `orderBy(...)`; replaces any previous `distinct(...)` /
2552
+ * `distinctOn(...)` selection.
2553
+ *
2554
+ * ```typescript
2555
+ * // Latest post per user:
2556
+ * const latestPerUser = await db.orm.Post
2557
+ * .orderBy([(p) => p.userId.asc(), (p) => p.createdAt.desc()])
2558
+ * .distinctOn('userId')
2559
+ * .all();
2560
+ * ```
2561
+ */
2354
2562
  distinctOn(...fields) {
2355
2563
  const distinctOnFields = mapFieldsToColumns(this.contract, this.modelName, fields);
2356
2564
  return this.#clone({
@@ -2358,44 +2566,93 @@ var Collection = class Collection {
2358
2566
  distinctOn: distinctOnFields
2359
2567
  });
2360
2568
  }
2569
+ /**
2570
+ * Apply `LIMIT n`. Replaces any previous limit set on this collection.
2571
+ *
2572
+ * ```typescript
2573
+ * const firstTen = await db.orm.User.orderBy((u) => u.id.asc()).take(10).all();
2574
+ * ```
2575
+ */
2361
2576
  take(n) {
2362
2577
  return this.#clone({ limit: n });
2363
2578
  }
2579
+ /**
2580
+ * Apply `OFFSET n`. Replaces any previous offset set on this collection.
2581
+ *
2582
+ * ```typescript
2583
+ * const page2 = await db.orm.User
2584
+ * .orderBy((u) => u.id.asc())
2585
+ * .skip(10)
2586
+ * .take(10)
2587
+ * .all();
2588
+ * ```
2589
+ */
2364
2590
  skip(n) {
2365
2591
  return this.#clone({ offset: n });
2366
2592
  }
2367
2593
  /**
2368
- * Read terminal: stream all rows matching the current state.
2594
+ * Read terminal: execute the query and stream every matching row.
2595
+ *
2596
+ * The returned `AsyncIterableResult<Row>` is BOTH a thenable that
2597
+ * resolves to `Row[]` (so `await` collects all rows into an array)
2598
+ * AND an async iterable (so `for await` streams rows as they
2599
+ * arrive, without buffering the whole result set in memory). Pick
2600
+ * whichever fits the caller. A single result can only be consumed
2601
+ * once.
2602
+ *
2603
+ * Streaming is the default and the expected execution model. The
2604
+ * only scenarios that fall back to buffering internally before
2605
+ * yielding are drivers that cannot expose a cursor to the
2606
+ * underlying database, and — for queries with `include(...)` —
2607
+ * targets whose SQL dialect supports neither lateral joins nor
2608
+ * correlated subqueries (so child rows cannot be stitched in a
2609
+ * single streaming query). These are implementation details below
2610
+ * the public API; the iteration shape itself is genuinely
2611
+ * streaming whenever the driver and plan allow it.
2612
+ *
2613
+ * ```typescript
2614
+ * // Thenable — collect to an array:
2615
+ * const users = await db.orm.User.all();
2616
+ * for (const user of users) console.log(user.id);
2617
+ *
2618
+ * // Async iterable — stream rows as they arrive:
2619
+ * for await (const user of db.orm.User.all()) {
2620
+ * console.log(user.id);
2621
+ * }
2622
+ * ```
2369
2623
  *
2370
2624
  * Accepts an optional `configure` callback that receives a
2371
2625
  * `MetaBuilder<'read'>` so the caller can attach typed user
2372
2626
  * annotations to the executed plan. `meta.annotate(...)` enforces
2373
2627
  * applicability at the type level and at runtime; annotations are
2374
2628
  * merged into `plan.meta.annotations` at compile time.
2375
- */
2376
- all(configure) {
2377
- return this.#withAnnotationsFromMeta(configure, "all").#dispatch();
2378
- }
2379
- /**
2380
- * Read terminal: return the first matching row, or `null`.
2381
- *
2382
- * Accepts an optional `filter` (function or shorthand) followed by an
2383
- * optional `configure` callback that receives a `MetaBuilder<'read'>`
2384
- * for attaching typed annotations. To attach annotations without
2385
- * narrowing further, pass `undefined` as the filter (or chain
2386
- * `.where(...)` first):
2387
2629
  *
2388
2630
  * ```typescript
2389
- * await db.User.first({ id }, (meta) => meta.annotate(cacheAnnotation({ ttl: 60 })));
2390
- * await db.User.first(undefined, (meta) => meta.annotate(cacheAnnotation({ ttl: 60 })));
2631
+ * await db.orm.User.all((meta) => meta.annotate(cacheAnnotation({ ttl: 60 })));
2391
2632
  * ```
2392
2633
  */
2634
+ all(configure) {
2635
+ return this.#withAnnotationsFromMeta(configure, "all").#dispatch();
2636
+ }
2393
2637
  async first(filter, configure) {
2394
2638
  return (await (filter === void 0 ? this : typeof filter === "function" ? this.where(filter) : this.where(filter)).take(1).#withAnnotationsFromMeta(configure, "first").#dispatch().toArray())[0] ?? null;
2395
2639
  }
2396
2640
  /**
2397
2641
  * Read terminal: run an aggregate query (count, sum, avg, min, max)
2398
- * built via the `AggregateBuilder` callback.
2642
+ * built via the `AggregateBuilder` callback. Returns one object
2643
+ * with the requested aggregate values keyed by the aliases supplied
2644
+ * in the spec.
2645
+ *
2646
+ * ```typescript
2647
+ * const stats = await db.orm.Post
2648
+ * .where({ published: true })
2649
+ * .aggregate((agg) => ({
2650
+ * total: agg.count(),
2651
+ * averageViews: agg.avg('views'),
2652
+ * maxViews: agg.max('views'),
2653
+ * }));
2654
+ * // { total: 42, averageViews: 17.3, maxViews: 9001 }
2655
+ * ```
2399
2656
  *
2400
2657
  * Accepts an optional `configure` callback that receives a
2401
2658
  * `MetaBuilder<'read'>` for attaching typed annotations.
@@ -2410,20 +2667,6 @@ var Collection = class Collection {
2410
2667
  const compiled = mergeAnnotations(compileAggregate(this.contract, this.tableName, this.state.filters, aggregateSpec), annotationsMap);
2411
2668
  return normalizeAggregateResult(aggregateSpec, (await executeQueryPlan(this.ctx.runtime, compiled).toArray())[0] ?? {});
2412
2669
  }
2413
- /**
2414
- * Write terminal: insert one row and return it.
2415
- *
2416
- * Accepts an optional `configure` callback that receives a
2417
- * `MetaBuilder<'write'>` for attaching typed annotations.
2418
- * Annotations are merged into the compiled mutation plan's
2419
- * `meta.annotations`.
2420
- *
2421
- * Note: when the input contains nested-mutation callbacks, the
2422
- * operation is executed as a graph of internal queries via
2423
- * `withMutationScope`. In that path, annotations apply to the
2424
- * logical `create()` call but do not currently flow into each
2425
- * constituent SQL statement issued for the related rows.
2426
- */
2427
2670
  async create(data, configure) {
2428
2671
  assertReturningCapability(this.contract, "create()");
2429
2672
  const annotationsMap = this.#collectAnnotationsFromMeta(configure, "write", "create");
@@ -2443,6 +2686,33 @@ var Collection = class Collection {
2443
2686
  if (created) return created;
2444
2687
  throw new Error(`create() for model "${this.modelName}" did not return a row`);
2445
2688
  }
2689
+ /**
2690
+ * Write terminal: insert many rows and stream the inserted rows.
2691
+ *
2692
+ * The returned `AsyncIterableResult<Row>` is BOTH a thenable that
2693
+ * resolves to `Row[]` AND an async iterable that streams inserted
2694
+ * rows as they arrive. Use whichever shape fits the caller — but
2695
+ * only consume it once. Streaming is the default; some
2696
+ * driver/plan combinations may still buffer internally before
2697
+ * yielding.
2698
+ *
2699
+ * ```typescript
2700
+ * // Thenable — collect all inserted rows into an array:
2701
+ * const created = await db.orm.User.createAll([
2702
+ * { email: 'a@example.com' },
2703
+ * { email: 'b@example.com' },
2704
+ * ]);
2705
+ *
2706
+ * // Async iterable — stream inserted rows as they arrive:
2707
+ * for await (const row of db.orm.User.createAll(seedUsers)) {
2708
+ * console.log('inserted', row.id);
2709
+ * }
2710
+ * ```
2711
+ *
2712
+ * Accepts an optional `configure` callback that receives a
2713
+ * `MetaBuilder<'write'>` for attaching typed annotations to the
2714
+ * compiled insert plan.
2715
+ */
2446
2716
  createAll(data, configure) {
2447
2717
  return this.#createAllWithAnnotations(data, this.#collectAnnotationsFromMeta(configure, "write", "createAll"));
2448
2718
  }
@@ -2574,6 +2844,24 @@ var Collection = class Collection {
2574
2844
  return mapped;
2575
2845
  });
2576
2846
  }
2847
+ /**
2848
+ * Write terminal: insert many rows without materializing the
2849
+ * inserted rows, returning the number of inserted records.
2850
+ *
2851
+ * Prefer `createAll(...)` when you need the returned rows; prefer
2852
+ * this when you only need to know how many rows were inserted (the
2853
+ * compiled plan skips `RETURNING`).
2854
+ *
2855
+ * ```typescript
2856
+ * const inserted = await db.orm.User.createCount([
2857
+ * { email: 'a@example.com' },
2858
+ * { email: 'b@example.com' },
2859
+ * ]);
2860
+ * // inserted === 2
2861
+ * ```
2862
+ *
2863
+ * Not supported on MTI variants — use `createAll(...)` instead.
2864
+ */
2577
2865
  async createCount(data, configure) {
2578
2866
  if (data.length === 0) return 0;
2579
2867
  this.#assertNotMtiVariant("createCount()");
@@ -2591,9 +2879,36 @@ var Collection = class Collection {
2591
2879
  return data.length;
2592
2880
  }
2593
2881
  /**
2594
- * Passing `update: {}` makes this behave like a conditional create.
2595
- * On conflict, `ON CONFLICT DO NOTHING RETURNING ...` may return zero rows,
2596
- * so this method may issue a follow-up reload query to return the existing row.
2882
+ * Write terminal: insert a row, or update the existing row on
2883
+ * conflict. Returns the resulting row (the inserted one or the
2884
+ * updated/existing one).
2885
+ *
2886
+ * `conflictOn` selects which unique constraint drives the conflict
2887
+ * resolution — omit to use the model's primary key.
2888
+ *
2889
+ * ```typescript
2890
+ * // Insert-or-update on email uniqueness:
2891
+ * await db.orm.User.upsert({
2892
+ * create: { email: 'alice@example.com', name: 'Alice' },
2893
+ * update: { name: 'Alice (updated)' },
2894
+ * conflictOn: { email: 'alice@example.com' },
2895
+ * });
2896
+ *
2897
+ * // Conditional create — `update: {}` keeps the existing row
2898
+ * // unchanged. `conflictOn` must reference the constraint that
2899
+ * // makes the row "already exist"; omit only when the conflict is
2900
+ * // on the primary key. On conflict,
2901
+ * // `ON CONFLICT DO NOTHING RETURNING ...` may return zero rows,
2902
+ * // so a follow-up reload is issued to fetch and return the
2903
+ * // existing row.
2904
+ * await db.orm.User.upsert({
2905
+ * create: { email: 'alice@example.com', name: 'Alice' },
2906
+ * update: {},
2907
+ * conflictOn: { email: 'alice@example.com' },
2908
+ * });
2909
+ * ```
2910
+ *
2911
+ * Not supported on MTI variants.
2597
2912
  */
2598
2913
  async upsert(input, configure) {
2599
2914
  assertReturningCapability(this.contract, "upsert()");
@@ -2629,7 +2944,30 @@ var Collection = class Collection {
2629
2944
  }
2630
2945
  /**
2631
2946
  * Write terminal: update matching rows and return the first one (or
2632
- * null when no row matched).
2947
+ * `null` when no row matched). Requires a prior `.where(...)` —
2948
+ * calling `update(...)` on an unfiltered collection is a type error.
2949
+ *
2950
+ * Related rows can be created or relinked through relation
2951
+ * callbacks on parent/child-owned relations (one-to-one or
2952
+ * one-to-many). The callback receives a mutator exposing
2953
+ * `create(...)`, `connect(...)`, and `disconnect(...)`. Nested
2954
+ * updates against existing related rows, and many-to-many relations
2955
+ * as nested-mutation targets, are not supported through this API.
2956
+ *
2957
+ * ```typescript
2958
+ * // Update one row by id:
2959
+ * const updated = await db.orm.User
2960
+ * .where({ id: 1 })
2961
+ * .update({ name: 'Alice Renamed' });
2962
+ *
2963
+ * // Update + relink — runs as a graph of internal mutations:
2964
+ * await db.orm.User
2965
+ * .where({ id: 1 })
2966
+ * .update({
2967
+ * name: 'Alice',
2968
+ * posts: (posts) => posts.connect([{ id: 5 }]),
2969
+ * });
2970
+ * ```
2633
2971
  *
2634
2972
  * Accepts an optional `configure` callback that receives a
2635
2973
  * `MetaBuilder<'write'>` for attaching typed annotations.
@@ -2662,6 +3000,31 @@ var Collection = class Collection {
2662
3000
  return (await scoped.#clone({ filters: [identityWhere] }).#updateAllWithAnnotations(data, annotationsMap))[0] ?? null;
2663
3001
  });
2664
3002
  }
3003
+ /**
3004
+ * Write terminal: update every matching row and stream the updated
3005
+ * rows. Requires a prior `.where(...)` filter.
3006
+ *
3007
+ * The returned `AsyncIterableResult<Row>` is BOTH a thenable that
3008
+ * resolves to `Row[]` AND an async iterable that streams updated
3009
+ * rows as they arrive. Use whichever fits; a result can only be
3010
+ * consumed once. Streaming is the default; some driver/plan
3011
+ * combinations may still buffer internally before yielding.
3012
+ *
3013
+ * ```typescript
3014
+ * // Thenable — collect updated rows into an array:
3015
+ * const updated = await db.orm.Post
3016
+ * .where({ published: false })
3017
+ * .updateAll({ published: true });
3018
+ *
3019
+ * // Async iterable — stream updated rows as they arrive:
3020
+ * for await (const row of db.orm.Post.where({ draft: true }).updateAll({ draft: false })) {
3021
+ * console.log('published', row.id);
3022
+ * }
3023
+ * ```
3024
+ *
3025
+ * Accepts an optional `configure` callback that receives a
3026
+ * `MetaBuilder<'write'>` for attaching typed annotations.
3027
+ */
2665
3028
  updateAll(data, configure) {
2666
3029
  return this.#updateAllWithAnnotations(data, this.#collectAnnotationsFromMeta(configure, "write", "updateAll"));
2667
3030
  }
@@ -2686,6 +3049,20 @@ var Collection = class Collection {
2686
3049
  mapRow: (mapped) => mapped
2687
3050
  });
2688
3051
  }
3052
+ /**
3053
+ * Write terminal: update every matching row without returning them,
3054
+ * resolving to the count of rows that were updated. Requires a prior
3055
+ * `.where(...)` filter.
3056
+ *
3057
+ * Prefer `updateAll(...)` when you need the updated rows; prefer
3058
+ * this when you only need the affected-row count.
3059
+ *
3060
+ * ```typescript
3061
+ * const count = await db.orm.Post
3062
+ * .where({ published: false })
3063
+ * .updateCount({ published: true });
3064
+ * ```
3065
+ */
2689
3066
  async updateCount(data, configure) {
2690
3067
  const mappedData = mapModelDataToStorageRow(this.contract, this.modelName, data);
2691
3068
  if (Object.keys(mappedData).length === 0) return 0;
@@ -2704,8 +3081,14 @@ var Collection = class Collection {
2704
3081
  return matchingRows.length;
2705
3082
  }
2706
3083
  /**
2707
- * Write terminal: delete matching rows and return the first one (or
2708
- * null when no row matched).
3084
+ * Write terminal: delete matching rows and return the first deleted
3085
+ * row (or `null` when no row matched). Requires a prior `.where(...)`
3086
+ * — calling `delete()` on an unfiltered collection is a type error.
3087
+ *
3088
+ * ```typescript
3089
+ * const deleted = await db.orm.User.where({ id: 1 }).delete();
3090
+ * if (deleted) console.log('deleted', deleted.email);
3091
+ * ```
2709
3092
  *
2710
3093
  * Accepts an optional `configure` callback that receives a
2711
3094
  * `MetaBuilder<'write'>` for attaching typed annotations.
@@ -2720,6 +3103,29 @@ var Collection = class Collection {
2720
3103
  return (await scoped.#clone({ filters: [identityWhere] }).#executeDeleteReturning(annotationsMap).toArray())[0] ?? null;
2721
3104
  });
2722
3105
  }
3106
+ /**
3107
+ * Write terminal: delete every matching row and stream the deleted
3108
+ * rows. Requires a prior `.where(...)` filter.
3109
+ *
3110
+ * The returned `AsyncIterableResult<Row>` is BOTH a thenable that
3111
+ * resolves to `Row[]` AND an async iterable that streams deleted
3112
+ * rows as they arrive. Use whichever fits; a result can only be
3113
+ * consumed once. Streaming is the default; some driver/plan
3114
+ * combinations may still buffer internally before yielding.
3115
+ *
3116
+ * ```typescript
3117
+ * // Thenable — collect the deleted rows into an array:
3118
+ * const deleted = await db.orm.Post.where({ archived: true }).deleteAll();
3119
+ *
3120
+ * // Async iterable — stream deleted rows as they arrive:
3121
+ * for await (const row of db.orm.Post.where({ archived: true }).deleteAll()) {
3122
+ * console.log('removed', row.id);
3123
+ * }
3124
+ * ```
3125
+ *
3126
+ * Accepts an optional `configure` callback that receives a
3127
+ * `MetaBuilder<'write'>` for attaching typed annotations.
3128
+ */
2723
3129
  deleteAll(configure) {
2724
3130
  return this.#deleteAllWithAnnotations(this.#collectAnnotationsFromMeta(configure, "write", "deleteAll"));
2725
3131
  }
@@ -2741,6 +3147,18 @@ var Collection = class Collection {
2741
3147
  mapRow: (mapped) => mapped
2742
3148
  });
2743
3149
  }
3150
+ /**
3151
+ * Write terminal: delete every matching row without returning them,
3152
+ * resolving to the count of rows that were deleted. Requires a prior
3153
+ * `.where(...)` filter.
3154
+ *
3155
+ * Prefer `deleteAll(...)` when you need the deleted rows; prefer
3156
+ * this when you only need the affected-row count.
3157
+ *
3158
+ * ```typescript
3159
+ * const removed = await db.orm.Post.where({ archived: true }).deleteCount();
3160
+ * ```
3161
+ */
2744
3162
  async deleteCount(configure) {
2745
3163
  const annotationsMap = this.#collectAnnotationsFromMeta(configure, "write", "deleteCount");
2746
3164
  const primaryKeyColumn = resolvePrimaryKeyColumn(this.contract, this.tableName);