@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.
- package/README.md +6 -0
- package/dist/client-auto.d.mts +2 -2
- package/dist/client-auto.mjs +1 -1
- package/dist/client.d.mts +1 -1
- package/dist/client.mjs +1 -1
- package/dist/{executor-Bp-hdRg2.d.mts → executor-CIEHpa1j.d.mts} +14 -7
- package/dist/{executor-Bp-hdRg2.d.mts.map → executor-CIEHpa1j.d.mts.map} +1 -1
- package/dist/{idb-orm-9XN3c9ly.d.mts → idb-orm-6OuwkeDM.d.mts} +2 -2
- package/dist/{idb-orm-9XN3c9ly.d.mts.map → idb-orm-6OuwkeDM.d.mts.map} +1 -1
- package/dist/{idb-orm-BitJ-EMq.mjs → idb-orm-B1Br1C0F.mjs} +18 -506
- package/dist/idb-orm-B1Br1C0F.mjs.map +1 -0
- package/dist/internal.d.mts +2 -2
- package/dist/internal.mjs +1 -1
- package/dist/{managed-client-CS78EjPu.d.mts → managed-client-DQlsxc4Z.d.mts} +3 -3
- package/dist/{managed-client-CS78EjPu.d.mts.map → managed-client-DQlsxc4Z.d.mts.map} +1 -1
- package/dist/{managed-client-CxlNmERd.mjs → managed-client-M3xMUk0n.mjs} +3 -3
- package/dist/{managed-client-CxlNmERd.mjs.map → managed-client-M3xMUk0n.mjs.map} +1 -1
- package/dist/{mutation-executor-SJqTuSbb.mjs → mutation-executor-inLFb0TE.mjs} +701 -146
- package/dist/mutation-executor-inLFb0TE.mjs.map +1 -0
- package/dist/{mutation-scope-1hS0ogGa.d.mts → mutation-scope-DrxFhek3.d.mts} +2 -2
- package/dist/{mutation-scope-1hS0ogGa.d.mts.map → mutation-scope-DrxFhek3.d.mts.map} +1 -1
- package/dist/orm.d.mts +3 -3
- package/dist/orm.mjs +2 -2
- package/package.json +6 -6
- package/dist/idb-orm-BitJ-EMq.mjs.map +0 -1
- package/dist/mutation-executor-SJqTuSbb.mjs.map +0 -1
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import { C as
|
|
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,
|
|
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
|
|
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
|
-
...
|
|
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
|
-
|
|
1249
|
-
ast
|
|
759
|
+
meta,
|
|
760
|
+
ast,
|
|
761
|
+
idbPlan: lowered.idbPlan
|
|
1250
762
|
}).toArray();
|
|
1251
|
-
return
|
|
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-
|
|
913
|
+
//# sourceMappingURL=idb-orm-B1Br1C0F.mjs.map
|