@prisma-idb/client-idb 0.11.0 → 0.12.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.
Files changed (26) hide show
  1. package/README.md +6 -0
  2. package/dist/client-auto.d.mts +2 -2
  3. package/dist/client-auto.mjs +1 -1
  4. package/dist/client.d.mts +1 -1
  5. package/dist/client.mjs +1 -1
  6. package/dist/{executor-Bp-hdRg2.d.mts → executor-CIEHpa1j.d.mts} +14 -7
  7. package/dist/{executor-Bp-hdRg2.d.mts.map → executor-CIEHpa1j.d.mts.map} +1 -1
  8. package/dist/{idb-orm-9XN3c9ly.d.mts → idb-orm-6OuwkeDM.d.mts} +2 -2
  9. package/dist/{idb-orm-9XN3c9ly.d.mts.map → idb-orm-6OuwkeDM.d.mts.map} +1 -1
  10. package/dist/{idb-orm-BitJ-EMq.mjs → idb-orm-B1Br1C0F.mjs} +18 -506
  11. package/dist/idb-orm-B1Br1C0F.mjs.map +1 -0
  12. package/dist/internal.d.mts +2 -2
  13. package/dist/internal.mjs +1 -1
  14. package/dist/{managed-client-CS78EjPu.d.mts → managed-client-DQlsxc4Z.d.mts} +3 -3
  15. package/dist/{managed-client-CS78EjPu.d.mts.map → managed-client-DQlsxc4Z.d.mts.map} +1 -1
  16. package/dist/{managed-client-CxlNmERd.mjs → managed-client-M3xMUk0n.mjs} +3 -3
  17. package/dist/{managed-client-CxlNmERd.mjs.map → managed-client-M3xMUk0n.mjs.map} +1 -1
  18. package/dist/{mutation-executor-SJqTuSbb.mjs → mutation-executor-inLFb0TE.mjs} +701 -146
  19. package/dist/mutation-executor-inLFb0TE.mjs.map +1 -0
  20. package/dist/{mutation-scope-1hS0ogGa.d.mts → mutation-scope-DrxFhek3.d.mts} +2 -2
  21. package/dist/{mutation-scope-1hS0ogGa.d.mts.map → mutation-scope-DrxFhek3.d.mts.map} +1 -1
  22. package/dist/orm.d.mts +3 -3
  23. package/dist/orm.mjs +2 -2
  24. package/package.json +6 -6
  25. package/dist/idb-orm-BitJ-EMq.mjs.map +0 -1
  26. package/dist/mutation-executor-SJqTuSbb.mjs.map +0 -1
@@ -1,8 +1,7 @@
1
- import { C as assertEnumValues, D as getRelation, E as getKeyPath, O as getStoreName, S as createMutationDefaultsCache, T as fieldValueToken, _ as requireTransactionExecutor, a as collectScalarFkStoreNames, b as applyCreateDefaults, c as executeDeleteWithReferentialActions, d as executeScalarCreateAllWithFkValidation, f as executeScalarCreateWithFkValidation, g as hasScalarFkFields, h as hasNestedMutationCallbacks, i as collectOnUpdateEnforcementStoreNames, l as executeNestedCreateMutation, m as hasEnforceableChildRelations, n as applyReferentialActionsForRowOnUpdate, o as executeBulkUpdateWithFkValidation, p as executeScalarUpdateWithFkValidation, s as executeDeleteAllWithReferentialActions, u as executeNestedUpdateMutation, v as validateScalarFks, w as extractKeyFromRow, x as applyUpdateDefaults, y as withMutationScope } from "./mutation-executor-SJqTuSbb.mjs";
1
+ import { A as combineFilterExprs, C as planQuery, D as createMutationDefaultsCache, E as applyUpdateDefaults, F as getStoreName, M as fieldValueToken, N as getKeyPath, O as assertEnumValues, P as getRelation, S as lowerRows, T as applyCreateDefaults, _ as readMutationRows, a as collectScalarFkStoreNames, b as withMutationScope, c as executeDeleteWithReferentialActions, d as executeScalarCreateAllWithFkValidation, f as executeScalarCreateWithFkValidation, g as hasScalarFkFields, h as hasNestedMutationCallbacks, i as collectOnUpdateEnforcementStoreNames, j as extractKeyFromRow, k as buildRowComparator, l as executeNestedCreateMutation, m as hasEnforceableChildRelations, n as applyReferentialActionsForRowOnUpdate, o as executeBulkUpdateWithFkValidation, p as executeScalarUpdateWithFkValidation, s as executeDeleteAllWithReferentialActions, u as executeNestedUpdateMutation, v as requireTransactionExecutor, w as buildCatalog, x as lowerCount, y as validateScalarFks } from "./mutation-executor-inLFb0TE.mjs";
2
2
  import { AsyncIterableResult } from "@prisma/orm-framework/components/runtime";
3
- import { andExpr, evaluateFilter, fieldFilter, nullCheckExpr, shorthandToFilterExpr } from "@prisma-idb/adapter-idb/runtime";
3
+ import { andExpr, fieldFilter, nullCheckExpr, shorthandToFilterExpr } from "@prisma-idb/adapter-idb/runtime";
4
4
  import { domainModelsAtDefaultNamespace } from "@prisma/orm-framework/contract/types";
5
- import { compareFieldValues, isValidIdbKey, keyEquals } from "@prisma-idb/target-idb/runtime";
6
5
  //#region src/core/model-accessor.ts
7
6
  /**
8
7
  * Proxy-based typed accessor handed to `where(fn)` callbacks.
@@ -94,39 +93,6 @@ function mergeAccessorState(state, overrides) {
94
93
  };
95
94
  }
96
95
  //#endregion
97
- //#region src/core/query-shaping.ts
98
- /**
99
- * Combine accumulated filter expressions with AND.
100
- *
101
- * Returns `undefined` when no filter is present so callers can skip building a
102
- * row-filter closure. Shared by {@link IdbStoreAccessorImpl} (top-level scans)
103
- * and the relation loader (refined `include()` child scans).
104
- */
105
- function combineFilterExprs(filters) {
106
- if (filters.length === 0) return void 0;
107
- if (filters.length === 1) return filters[0];
108
- return andExpr(filters);
109
- }
110
- /**
111
- * Build an in-memory comparator from an `orderBy` spec (field → direction).
112
- *
113
- * Returns `undefined` when there is nothing to sort by. Compares fields in
114
- * declaration order, with {@link compareFieldValues}: the same order an
115
- * IndexedDB index uses, so equal `Date`s tie and fall through to the next
116
- * field, and `null`s sort last (first when descending).
117
- */
118
- function buildRowComparator(orderBy) {
119
- if (orderBy === void 0) return void 0;
120
- return (a, b) => {
121
- for (const [field, dir] of Object.entries(orderBy)) {
122
- const cmp = compareFieldValues(a[field], b[field]);
123
- if (cmp === 0) continue;
124
- return dir === "desc" ? -cmp : cmp;
125
- }
126
- return 0;
127
- };
128
- }
129
- //#endregion
130
96
  //#region src/core/aggregate-builder.ts
131
97
  /**
132
98
  * Build the aggregate selector factory handed to `.aggregate(agg => …)` and
@@ -282,461 +248,6 @@ function createGroupedAccessor(init) {
282
248
  } };
283
249
  }
284
250
  //#endregion
285
- //#region src/core/planner/catalog.ts
286
- const catalogs = /* @__PURE__ */ new WeakMap();
287
- /** Build a catalog once per contract identity and store name. Contracts are immutable. */
288
- function buildCatalog(contract, storeName) {
289
- const cached = catalogs.get(contract)?.get(storeName);
290
- if (cached) return cached;
291
- const store = contract.storage.stores[storeName];
292
- if (!store) throw new Error(`Store "${storeName}" is not in the contract`);
293
- const fields = {};
294
- for (const model of Object.values(domainModelsAtDefaultNamespace(contract.domain))) {
295
- if (model.storage.storeName !== storeName) continue;
296
- for (const [name, field] of Object.entries(model.fields)) fields[name] = Object.freeze({
297
- codecId: field.type.kind === "scalar" ? field.type.codecId : void 0,
298
- nullable: field.nullable,
299
- collection: field.many === true || field.dict === true
300
- });
301
- }
302
- const catalog = Object.freeze({
303
- storeName,
304
- primaryKey: source(store.keyPath, true, false),
305
- indexes: Object.freeze(Object.entries(store.indexes ?? {}).map(([name, index]) => source(index.keyPath, index.unique, index.multiEntry ?? false, name))),
306
- fields: Object.freeze(fields)
307
- });
308
- let byStore = catalogs.get(contract);
309
- if (!byStore) {
310
- byStore = /* @__PURE__ */ new Map();
311
- catalogs.set(contract, byStore);
312
- }
313
- byStore.set(storeName, catalog);
314
- return catalog;
315
- }
316
- function source(keyPath, unique, multiEntry, indexName) {
317
- const path = typeof keyPath === "string" ? keyPath : Object.freeze([...keyPath]);
318
- return Object.freeze({
319
- keyPath: path,
320
- fields: Object.freeze(typeof path === "string" ? [path] : [...path]),
321
- unique,
322
- multiEntry,
323
- ...indexName === void 0 ? {} : { indexName }
324
- });
325
- }
326
- //#endregion
327
- //#region src/core/planner/plan.ts
328
- const COST = {
329
- UNIQUE_POINT: 0,
330
- PRIMARY_POINT: 1,
331
- INDEX_POINT: 2,
332
- PREFIX_POINT: 3,
333
- TWO_SIDED_RANGE: 4,
334
- ONE_SIDED_RANGE: 5,
335
- ORDER_ONLY_SCAN: 6
336
- };
337
- const KEY_PARTIAL_CODECS = /* @__PURE__ */ new Set(["idb/double@1", "idb/date@1"]);
338
- const KEY_CODECS = /* @__PURE__ */ new Set([
339
- "idb/string@1",
340
- "idb/int32@1",
341
- "idb/double@1",
342
- "idb/date@1",
343
- "idb/decimal@1",
344
- "idb/bytes@1"
345
- ]);
346
- /** Choose the cheapest safe access without opening IndexedDB or probing cardinality. */
347
- function planQuery(catalog, request) {
348
- const normalized = normalize(request.where);
349
- if (normalized.empty) return {
350
- access: { kind: "empty" },
351
- exact: true
352
- };
353
- const orders = Object.entries(request.orderBy ?? {});
354
- if (normalized.excluded || request.hasRelationFilter || orders.length > 1) return full(false);
355
- const constraints = /* @__PURE__ */ new Map();
356
- for (const atom of normalized.atoms) {
357
- let constraint = constraints.get(atom.field);
358
- if (!constraint) {
359
- constraint = { consumed: 0 };
360
- constraints.set(atom.field, constraint);
361
- }
362
- addAtom(constraint, atom, catalog.fields[atom.field]);
363
- }
364
- for (const [name, constraint] of constraints) {
365
- if (keyPartial(catalog.fields[name]) && !constraint.rejectsNonKeys) {
366
- delete constraint.lower;
367
- delete constraint.upper;
368
- constraint.consumed = 0;
369
- }
370
- if (contradictory(constraint)) return {
371
- access: { kind: "empty" },
372
- exact: true
373
- };
374
- }
375
- const sources = [catalog.primaryKey, ...catalog.indexes];
376
- const candidates = sources.flatMap((source, declaration) => {
377
- const candidate = filteringCandidate(catalog, source, constraints, declaration);
378
- return candidate ? [candidate] : [];
379
- });
380
- candidates.sort((a, b) => a.cost - b.cost || a.ranges.length - b.ranges.length || Number(a.source.indexName !== void 0) - Number(b.source.indexName !== void 0) || a.declaration - b.declaration);
381
- let selected = candidates[0];
382
- let direction;
383
- if (orders.length === 1 && request.take !== void 0) {
384
- const [field, order] = orders[0];
385
- if (selected) {
386
- if (singleFieldSource(selected.source, field)) direction = order === "asc" ? "next" : "prev";
387
- } else {
388
- const ordering = sources.find((source) => !source.multiEntry && singleFieldSource(source, field) && completeKeyField(catalog.fields[field]));
389
- if (ordering) {
390
- selected = {
391
- source: ordering,
392
- ranges: [void 0],
393
- cost: COST.ORDER_ONLY_SCAN,
394
- consumed: 0,
395
- declaration: sources.indexOf(ordering)
396
- };
397
- direction = order === "asc" ? "next" : "prev";
398
- }
399
- }
400
- }
401
- if (!selected) {
402
- const partialOrder = orders.length === 1 && request.take !== void 0 && keyPartial(catalog.fields[orders[0][0]]);
403
- return full(normalized.atoms.length + normalized.residual === 0 && !partialOrder);
404
- }
405
- return {
406
- access: {
407
- kind: "ranges",
408
- source: selected.source,
409
- ranges: selected.ranges
410
- },
411
- exact: selected.consumed === normalized.atoms.length && normalized.residual === 0,
412
- ...direction === void 0 ? {} : { direction }
413
- };
414
- }
415
- function full(exact) {
416
- return {
417
- access: { kind: "full" },
418
- exact
419
- };
420
- }
421
- function normalize(where) {
422
- const result = {
423
- atoms: [],
424
- residual: 0,
425
- excluded: false,
426
- empty: false
427
- };
428
- function visit(expr) {
429
- switch (expr.kind) {
430
- case "field":
431
- if (expr.op === "in" && (!Array.isArray(expr.value) || expr.value.length === 0)) result.empty = true;
432
- result.atoms.push(expr);
433
- break;
434
- case "and":
435
- expr.exprs.forEach(visit);
436
- break;
437
- case "or": {
438
- if (expr.exprs.length === 0) {
439
- result.empty = true;
440
- break;
441
- }
442
- const branches = expr.exprs;
443
- const first = branches[0];
444
- if (first.kind !== "field" || !branches.every((branch) => branch.kind === "field" && branch.field === first.field && (branch.op === "eq" || branch.op === "in"))) {
445
- result.excluded = true;
446
- break;
447
- }
448
- const values = branches.flatMap((branch) => {
449
- const atom = branch;
450
- return atom.op === "eq" ? [atom.value] : Array.isArray(atom.value) ? atom.value : [];
451
- });
452
- visit({
453
- kind: "field",
454
- field: first.field,
455
- op: "in",
456
- value: values
457
- });
458
- break;
459
- }
460
- case "not":
461
- result.excluded = true;
462
- break;
463
- case "null-check": result.residual++;
464
- }
465
- }
466
- if (where) visit(where);
467
- return result;
468
- }
469
- function keyTyped(field) {
470
- return field !== void 0 && !field.collection && KEY_CODECS.has(field.codecId ?? "");
471
- }
472
- function keyPartial(field) {
473
- return KEY_PARTIAL_CODECS.has(field?.codecId ?? "");
474
- }
475
- function completeKeyField(field, constraint) {
476
- return keyTyped(field) && field?.nullable === false && (!keyPartial(field) || constraint?.rejectsNonKeys === true);
477
- }
478
- function singleFieldSource(source, field) {
479
- return source.fields.length === 1 && source.fields[0] === field;
480
- }
481
- function addAtom(constraint, atom, field) {
482
- if (atom.op === "eq" || atom.op === "in") {
483
- const values = atom.op === "eq" ? [atom.value] : Array.isArray(atom.value) ? atom.value : [];
484
- if (!values.every(isValidIdbKey)) return;
485
- const points = uniqueKeys(values);
486
- constraint.points = constraint.points === void 0 ? points : constraint.points.filter((point) => points.some((value) => keyEquals(point, value)));
487
- constraint.rejectsNonKeys = true;
488
- constraint.consumed++;
489
- return;
490
- }
491
- if (!keyTyped(field)) return;
492
- if (atom.op === "startsWith") {
493
- if (field?.codecId !== "idb/string@1" || typeof atom.value !== "string") return;
494
- intersectBound(constraint, "lower", {
495
- value: atom.value,
496
- open: false
497
- });
498
- const upper = stringSuccessor(atom.value);
499
- if (upper !== void 0) intersectBound(constraint, "upper", {
500
- value: upper,
501
- open: true
502
- });
503
- constraint.startsWith = true;
504
- constraint.consumed++;
505
- } else if ([
506
- "gt",
507
- "gte",
508
- "lt",
509
- "lte"
510
- ].includes(atom.op) && isValidIdbKey(atom.value)) {
511
- const side = atom.op === "gt" || atom.op === "gte" ? "lower" : "upper";
512
- const strict = atom.op === "gt" || atom.op === "lt";
513
- intersectBound(constraint, side, {
514
- value: atom.value,
515
- open: strict
516
- });
517
- if (strict) constraint.rejectsNonKeys = true;
518
- constraint.consumed++;
519
- }
520
- }
521
- function uniqueKeys(values) {
522
- const sorted = [...values].sort(compareFieldValues);
523
- return sorted.filter((value, i) => i === 0 || compareFieldValues(sorted[i - 1], value) !== 0);
524
- }
525
- function intersectBound(constraint, side, bound) {
526
- const previous = constraint[side];
527
- const comparison = previous === void 0 ? 0 : compareFieldValues(bound.value, previous.value);
528
- if (!previous || (side === "lower" ? comparison > 0 : comparison < 0) || comparison === 0 && bound.open) constraint[side] = bound;
529
- }
530
- function insideBounds(value, constraint) {
531
- const lower = constraint.lower;
532
- const upper = constraint.upper;
533
- if (lower && (compareFieldValues(value, lower.value) < 0 || lower.open && keyEquals(value, lower.value))) return false;
534
- if (upper && (compareFieldValues(value, upper.value) > 0 || upper.open && keyEquals(value, upper.value))) return false;
535
- return true;
536
- }
537
- function contradictory(constraint) {
538
- if (constraint.points) {
539
- constraint.points = constraint.points.filter((value) => insideBounds(value, constraint));
540
- return constraint.points.length === 0;
541
- }
542
- if (!constraint.lower || !constraint.upper) return false;
543
- const comparison = compareFieldValues(constraint.lower.value, constraint.upper.value);
544
- return comparison > 0 || comparison === 0 && (constraint.lower.open || constraint.upper.open);
545
- }
546
- function filteringCandidate(catalog, source, constraints, declaration) {
547
- if (source.multiEntry || source.fields.length === 0) return void 0;
548
- if (source.fields.some((name) => keyPartial(catalog.fields[name]) && !constraints.get(name)?.rejectsNonKeys)) return void 0;
549
- let prefixes = [[]];
550
- let consumed = 0;
551
- let position = 0;
552
- while (position < source.fields.length) {
553
- const constraint = constraints.get(source.fields[position]);
554
- if (constraint?.points === void 0) break;
555
- if (prefixes.length * constraint.points.length > 1024) return void 0;
556
- prefixes = prefixes.flatMap((prefix) => constraint.points.map((point) => [...prefix, point]));
557
- consumed += constraint.consumed;
558
- position++;
559
- }
560
- const constraint = constraints.get(source.fields[position] ?? "");
561
- const hasRange = constraint !== void 0 && (constraint.lower !== void 0 || constraint.upper !== void 0);
562
- if (position === 0 && !hasRange) return void 0;
563
- const used = position + Number(hasRange);
564
- if (!source.fields.slice(used).every((field) => completeKeyField(catalog.fields[field], constraints.get(field)))) return void 0;
565
- const compound = typeof source.keyPath !== "string";
566
- if (position === source.fields.length) return {
567
- source,
568
- ranges: prefixes.map((prefix) => ({
569
- kind: "only",
570
- key: compound ? prefix : prefix[0]
571
- })),
572
- cost: source.indexName !== void 0 && source.unique ? COST.UNIQUE_POINT : source.indexName === void 0 ? COST.PRIMARY_POINT : COST.INDEX_POINT,
573
- consumed,
574
- declaration
575
- };
576
- if (hasRange) consumed += constraint.consumed;
577
- const ranges = prefixes.map((prefix) => compound ? compoundRange(prefix, constraint, source.fields.length - used) : scalarRange(constraint));
578
- if (ranges.some((range) => range.kind === "bound" && (compareFieldValues(range.lower, range.upper) > 0 || keyEquals(range.lower, range.upper) && (range.lowerOpen || range.upperOpen)))) return void 0;
579
- return {
580
- source,
581
- ranges,
582
- cost: hasRange ? constraint.lower && constraint.upper && !constraint.startsWith ? COST.TWO_SIDED_RANGE : COST.ONE_SIDED_RANGE : COST.PREFIX_POINT,
583
- consumed,
584
- declaration
585
- };
586
- }
587
- function scalarRange(constraint) {
588
- if (constraint.lower && constraint.upper) return {
589
- kind: "bound",
590
- lower: constraint.lower.value,
591
- upper: constraint.upper.value,
592
- lowerOpen: constraint.lower.open,
593
- upperOpen: constraint.upper.open
594
- };
595
- if (constraint.lower) return {
596
- kind: "lower",
597
- key: constraint.lower.value,
598
- open: constraint.lower.open
599
- };
600
- return {
601
- kind: "upper",
602
- key: constraint.upper.value,
603
- open: constraint.upper.open
604
- };
605
- }
606
- function compoundRange(prefix, constraint, trailing) {
607
- const lower = constraint?.lower;
608
- const upper = constraint?.upper;
609
- return {
610
- kind: "bound",
611
- lower: lower ? [
612
- ...prefix,
613
- lower.value,
614
- ...lower.open && trailing > 0 ? [[]] : []
615
- ] : prefix.length ? prefix : [-Infinity],
616
- upper: upper ? [
617
- ...prefix,
618
- upper.value,
619
- ...!upper.open && trailing > 0 ? [[]] : []
620
- ] : [...prefix, []],
621
- lowerOpen: lower?.open ?? false,
622
- upperOpen: upper ? upper.open || trailing > 0 : true
623
- };
624
- }
625
- function stringSuccessor(prefix) {
626
- for (let i = prefix.length - 1; i >= 0; i--) {
627
- const code = prefix.charCodeAt(i);
628
- if (code < 65535) return prefix.slice(0, i) + String.fromCharCode(code + 1);
629
- }
630
- }
631
- //#endregion
632
- //#region src/core/planner/lower.ts
633
- /**
634
- * Lower row access without creating IDBKeyRange objects or awaiting work.
635
- * Run `finish` after the executor has materialized all returned rows.
636
- * Without `orderBy`, row order and pagination selection are unspecified.
637
- * Rows tied on every ordering field may appear in any order, including at
638
- * pagination boundaries. Add a unique ordering field for deterministic pages.
639
- */
640
- function lowerRows(catalog, logical, meta, request, additionalFilter) {
641
- const filter = (row) => (request.where === void 0 || evaluateFilter(request.where, row)) && (additionalFilter === void 0 || additionalFilter(row));
642
- const comparator = logical.direction === void 0 ? buildRowComparator(request.orderBy) : void 0;
643
- const base = {
644
- meta,
645
- storeName: catalog.storeName
646
- };
647
- const pagination = {
648
- ...request.skip === void 0 ? {} : { skip: request.skip },
649
- ...request.take === void 0 ? {} : { take: request.take }
650
- };
651
- let shapedByDriver = false;
652
- let idbPlan;
653
- switch (logical.access.kind) {
654
- case "empty":
655
- idbPlan = {
656
- meta,
657
- kind: "batch",
658
- storeNames: [catalog.storeName],
659
- ops: []
660
- };
661
- break;
662
- case "full":
663
- idbPlan = {
664
- ...base,
665
- kind: "cursor-scan",
666
- filter,
667
- ...comparator === void 0 ? {} : { comparator },
668
- ...pagination
669
- };
670
- shapedByDriver = true;
671
- break;
672
- case "ranges": {
673
- const { source } = logical.access;
674
- const ranges = [...logical.access.ranges];
675
- if (logical.direction === "prev") ranges.reverse();
676
- const single = ranges.length === 1;
677
- const ops = ranges.map((range) => {
678
- if (range?.kind === "only" && source.indexName === void 0) return {
679
- ...base,
680
- kind: "key-get",
681
- key: range.key
682
- };
683
- const access = {
684
- ...base,
685
- ...source.indexName === void 0 ? {} : { indexName: source.indexName },
686
- ...range === void 0 ? {} : { range }
687
- };
688
- if (!single && logical.direction !== void 0 && request.take !== void 0) return {
689
- ...access,
690
- kind: "cursor-scan",
691
- direction: logical.direction,
692
- filter,
693
- take: (request.skip ?? 0) + request.take
694
- };
695
- if (single && request.take !== void 0) {
696
- shapedByDriver = true;
697
- return {
698
- ...access,
699
- kind: "cursor-scan",
700
- filter,
701
- ...logical.direction === void 0 ? {} : { direction: logical.direction },
702
- ...comparator === void 0 ? {} : { comparator },
703
- ...pagination
704
- };
705
- }
706
- if (logical.direction === "prev") return {
707
- ...access,
708
- kind: "cursor-scan",
709
- direction: "prev",
710
- filter
711
- };
712
- return {
713
- ...access,
714
- kind: "get-all"
715
- };
716
- });
717
- idbPlan = single ? ops[0] : {
718
- meta,
719
- kind: "batch",
720
- storeNames: [catalog.storeName],
721
- ops
722
- };
723
- break;
724
- }
725
- }
726
- return {
727
- idbPlan,
728
- finish(rows) {
729
- let result = rows.filter(filter);
730
- if (!shapedByDriver) {
731
- if (comparator !== void 0) result.sort(comparator);
732
- const skip = request.skip ?? 0;
733
- result = result.slice(skip, request.take === void 0 ? void 0 : skip + request.take);
734
- }
735
- return result;
736
- }
737
- };
738
- }
739
- //#endregion
740
251
  //#region src/core/relation-loader.ts
741
252
  /**
742
253
  * Batch-load a single named relation for all rows in `rows` and attach the
@@ -1106,7 +617,6 @@ var IdbStoreAccessorImpl = class IdbStoreAccessorImpl {
1106
617
  async upsert(args) {
1107
618
  const keyPath = getKeyPath(this.#contract, this.#modelName);
1108
619
  const whereExpr = shorthandToFilterExpr(args.where);
1109
- const matches = (row) => whereExpr === void 0 || evaluateFilter(whereExpr, row);
1110
620
  const meta = this.#planMeta(this.#newGroupingKey());
1111
621
  const createRecord = args.create;
1112
622
  const patchRecord = args.update;
@@ -1125,13 +635,7 @@ var IdbStoreAccessorImpl = class IdbStoreAccessorImpl {
1125
635
  ...collectScalarFkStoreNames(this.#contract, this.#modelName, effectivePatch)
1126
636
  ])];
1127
637
  return withMutationScope(exec, storeNames, async (scope) => {
1128
- const existing = (await scope.execute({
1129
- meta,
1130
- kind: "cursor-scan",
1131
- storeName,
1132
- filter: matches,
1133
- take: 1
1134
- }))[0];
638
+ const existing = (await readMutationRows(scope, this.#contract, this.#modelName, whereExpr, 1))[0];
1135
639
  if (existing === void 0) {
1136
640
  await validateScalarFks(scope, this.#contract, this.#modelName, createWithDefaults);
1137
641
  return (await scope.execute({
@@ -1215,7 +719,6 @@ var IdbStoreAccessorImpl = class IdbStoreAccessorImpl {
1215
719
  */
1216
720
  deleteAll() {
1217
721
  const combined = this.#combinedFilterExpr();
1218
- const filter = combined !== void 0 ? (row) => evaluateFilter(combined, row) : void 0;
1219
722
  const contract = this.#contract;
1220
723
  const modelName = this.#modelName;
1221
724
  const executor = requireTransactionExecutor(this.#executor);
@@ -1224,7 +727,7 @@ var IdbStoreAccessorImpl = class IdbStoreAccessorImpl {
1224
727
  executor,
1225
728
  contract,
1226
729
  modelName,
1227
- ...filter !== void 0 ? { filter } : {}
730
+ ...combined === void 0 ? {} : { where: combined }
1228
731
  });
1229
732
  for (const row of rows) yield row;
1230
733
  })());
@@ -1237,18 +740,27 @@ var IdbStoreAccessorImpl = class IdbStoreAccessorImpl {
1237
740
  return this.#countTerminal();
1238
741
  }
1239
742
  async #countTerminal() {
1240
- const scan = this.#buildScanPlan(this.#newGroupingKey());
1241
743
  const combined = this.#combinedFilterExpr();
744
+ const { skip, take } = this.#state;
745
+ const request = {
746
+ ...combined === void 0 ? {} : { where: combined },
747
+ ...skip === void 0 ? {} : { skip },
748
+ ...take === void 0 ? {} : { take }
749
+ };
750
+ const meta = this.#planMeta(this.#newGroupingKey());
751
+ const catalog = buildCatalog(this.#contract, this.#storeName);
752
+ const lowered = lowerCount(catalog, planQuery(catalog, request), meta, request);
1242
753
  const ast = {
1243
754
  kind: "count",
1244
755
  modelName: this.#modelName,
1245
756
  ...combined !== void 0 ? { where: combined } : {}
1246
757
  };
1247
758
  const rows = await this.#executor.query({
1248
- ...scan.plan,
1249
- ast
759
+ meta,
760
+ ast,
761
+ idbPlan: lowered.idbPlan
1250
762
  }).toArray();
1251
- return scan.finish(rows).length;
763
+ return lowered.finish(rows);
1252
764
  }
1253
765
  /**
1254
766
  * Resolve an `include()` argument pair into an {@link IncludeEntry}: run the
@@ -1398,4 +910,4 @@ function idbOrm(options) {
1398
910
  //#endregion
1399
911
  export { idbOrm as t };
1400
912
 
1401
- //# sourceMappingURL=idb-orm-BitJ-EMq.mjs.map
913
+ //# sourceMappingURL=idb-orm-B1Br1C0F.mjs.map