@prometheus-ags/entity-graph-core 3.0.2 → 3.0.3
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/CHANGELOG.md +8 -0
- package/dist/index.cjs +245 -134
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +159 -92
- package/dist/index.d.ts +159 -92
- package/dist/index.mjs +245 -134
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# @prometheus-ags/entity-graph-core
|
|
2
2
|
|
|
3
|
+
## 3.0.3
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Ingest fetched entity lists, lifecycle metadata, and list projections in one
|
|
8
|
+
atomic graph publication. Core and React list paths no longer publish once per
|
|
9
|
+
returned row.
|
|
10
|
+
|
|
3
11
|
## 3.0.0
|
|
4
12
|
|
|
5
13
|
### Patch Changes
|
package/dist/index.cjs
CHANGED
|
@@ -428,6 +428,135 @@ function hasMergeStrategy(type) {
|
|
|
428
428
|
return byType.has(type) || globalDefault !== null;
|
|
429
429
|
}
|
|
430
430
|
|
|
431
|
+
// src/view/evaluator.ts
|
|
432
|
+
function matchesFilter(entity, filter) {
|
|
433
|
+
if (Array.isArray(filter)) return filter.every((clause) => matchesClause(entity, clause));
|
|
434
|
+
return matchesGroup(entity, filter);
|
|
435
|
+
}
|
|
436
|
+
function matchesGroup(entity, group) {
|
|
437
|
+
const { logic, clauses } = group;
|
|
438
|
+
if (logic === "and") return clauses.every((c) => "logic" in c ? matchesGroup(entity, c) : matchesClause(entity, c));
|
|
439
|
+
return clauses.some((c) => "logic" in c ? matchesGroup(entity, c) : matchesClause(entity, c));
|
|
440
|
+
}
|
|
441
|
+
function matchesClause(entity, clause) {
|
|
442
|
+
const { field, op, value, predicate } = clause;
|
|
443
|
+
const fv = getNestedValue(entity, field);
|
|
444
|
+
switch (op) {
|
|
445
|
+
case "eq":
|
|
446
|
+
return fv === value;
|
|
447
|
+
case "neq":
|
|
448
|
+
return fv !== value;
|
|
449
|
+
case "gt":
|
|
450
|
+
return fv > value;
|
|
451
|
+
case "gte":
|
|
452
|
+
return fv >= value;
|
|
453
|
+
case "lt":
|
|
454
|
+
return fv < value;
|
|
455
|
+
case "lte":
|
|
456
|
+
return fv <= value;
|
|
457
|
+
case "in":
|
|
458
|
+
return Array.isArray(value) && value.includes(fv);
|
|
459
|
+
case "nin":
|
|
460
|
+
return Array.isArray(value) && !value.includes(fv);
|
|
461
|
+
case "isNull":
|
|
462
|
+
return fv == null;
|
|
463
|
+
case "isNotNull":
|
|
464
|
+
return fv != null;
|
|
465
|
+
case "contains":
|
|
466
|
+
return typeof fv === "string" && typeof value === "string" && fv.toLowerCase().includes(value.toLowerCase());
|
|
467
|
+
case "startsWith":
|
|
468
|
+
return typeof fv === "string" && typeof value === "string" && fv.toLowerCase().startsWith(value.toLowerCase());
|
|
469
|
+
case "endsWith":
|
|
470
|
+
return typeof fv === "string" && typeof value === "string" && fv.toLowerCase().endsWith(value.toLowerCase());
|
|
471
|
+
case "between": {
|
|
472
|
+
const [lo, hi] = value;
|
|
473
|
+
return fv >= lo && fv <= hi;
|
|
474
|
+
}
|
|
475
|
+
case "arrayContains":
|
|
476
|
+
return Array.isArray(fv) && fv.includes(value);
|
|
477
|
+
case "arrayOverlaps":
|
|
478
|
+
return Array.isArray(fv) && Array.isArray(value) && value.some((v) => fv.includes(v));
|
|
479
|
+
case "matches":
|
|
480
|
+
return typeof fv === "string" && new RegExp(value).test(fv);
|
|
481
|
+
case "custom":
|
|
482
|
+
return predicate ? predicate(fv, entity) : true;
|
|
483
|
+
default:
|
|
484
|
+
return true;
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
function matchesSearch(entity, query, fields) {
|
|
488
|
+
if (!query.trim()) return true;
|
|
489
|
+
const lq = query.toLowerCase();
|
|
490
|
+
return fields.some((field) => {
|
|
491
|
+
const v = getNestedValue(entity, field);
|
|
492
|
+
return typeof v === "string" && v.toLowerCase().includes(lq);
|
|
493
|
+
});
|
|
494
|
+
}
|
|
495
|
+
function compareEntities(a, b, sort) {
|
|
496
|
+
for (const clause of sort) {
|
|
497
|
+
const r = compareByClause(a, b, clause);
|
|
498
|
+
if (r !== 0) return r;
|
|
499
|
+
}
|
|
500
|
+
return 0;
|
|
501
|
+
}
|
|
502
|
+
function compareByClause(a, b, clause) {
|
|
503
|
+
const { field, direction, nulls = "last", comparator } = clause;
|
|
504
|
+
const av = getNestedValue(a, field);
|
|
505
|
+
const bv = getNestedValue(b, field);
|
|
506
|
+
const aNull = av == null;
|
|
507
|
+
const bNull = bv == null;
|
|
508
|
+
if (aNull && bNull) return 0;
|
|
509
|
+
if (aNull) return nulls === "first" ? -1 : 1;
|
|
510
|
+
if (bNull) return nulls === "first" ? 1 : -1;
|
|
511
|
+
let cmp;
|
|
512
|
+
if (comparator) cmp = comparator(av, bv);
|
|
513
|
+
else if (typeof av === "string" && typeof bv === "string") cmp = av.localeCompare(bv, void 0, { sensitivity: "base", numeric: true });
|
|
514
|
+
else if (typeof av === "number" && typeof bv === "number") cmp = av - bv;
|
|
515
|
+
else cmp = String(av).localeCompare(String(bv));
|
|
516
|
+
return direction === "desc" ? -cmp : cmp;
|
|
517
|
+
}
|
|
518
|
+
function findInsertionIndex(entity, sortedIds, getEntity, sort) {
|
|
519
|
+
let lo = 0;
|
|
520
|
+
let hi = sortedIds.length;
|
|
521
|
+
while (lo < hi) {
|
|
522
|
+
const mid = lo + hi >>> 1;
|
|
523
|
+
const me = getEntity(sortedIds[mid]);
|
|
524
|
+
if (!me) {
|
|
525
|
+
lo = mid + 1;
|
|
526
|
+
continue;
|
|
527
|
+
}
|
|
528
|
+
if (compareEntities(entity, me, sort) <= 0) hi = mid;
|
|
529
|
+
else lo = mid + 1;
|
|
530
|
+
}
|
|
531
|
+
return lo;
|
|
532
|
+
}
|
|
533
|
+
function applyView(ids, getEntity, filter, sort, search) {
|
|
534
|
+
let entries = [];
|
|
535
|
+
for (const id of ids) {
|
|
536
|
+
const entity = getEntity(id);
|
|
537
|
+
if (!entity) continue;
|
|
538
|
+
entries.push({ id, entity });
|
|
539
|
+
}
|
|
540
|
+
if (filter && entries.length > 0) entries = entries.filter(({ entity }) => matchesFilter(entity, filter));
|
|
541
|
+
if (search?.query) entries = entries.filter(({ entity }) => matchesSearch(entity, search.query, search.fields));
|
|
542
|
+
if (sort && sort.length > 0) entries.sort((a, b) => compareEntities(a.entity, b.entity, sort));
|
|
543
|
+
return entries.map((e) => e.id);
|
|
544
|
+
}
|
|
545
|
+
function checkCompleteness(loadedCount, total, hasNextPage) {
|
|
546
|
+
if (!hasNextPage && total !== null && loadedCount >= total) return { isComplete: true, reason: "all-loaded" };
|
|
547
|
+
if (hasNextPage) return { isComplete: false, reason: "has-more-pages" };
|
|
548
|
+
return { isComplete: true, reason: "no-more-pages" };
|
|
549
|
+
}
|
|
550
|
+
function getNestedValue(obj, path) {
|
|
551
|
+
const parts = path.replace(/\[(\d+)\]/g, ".$1").split(".");
|
|
552
|
+
let current = obj;
|
|
553
|
+
for (const part of parts) {
|
|
554
|
+
if (current == null || typeof current !== "object") return void 0;
|
|
555
|
+
current = current[part];
|
|
556
|
+
}
|
|
557
|
+
return current;
|
|
558
|
+
}
|
|
559
|
+
|
|
431
560
|
// src/graph.ts
|
|
432
561
|
var EMPTY_IDS = [];
|
|
433
562
|
var EMPTY_ENTITY_STATE = {
|
|
@@ -512,6 +641,117 @@ function createGraphStore() {
|
|
|
512
641
|
if (!s.syncMetadata[key]) s.syncMetadata[key] = defaultSyncMetadata();
|
|
513
642
|
}
|
|
514
643
|
}),
|
|
644
|
+
ingestFetchedList: (type, entries, options = {}) => set((s) => {
|
|
645
|
+
const fetchedAt = Date.now();
|
|
646
|
+
const ids = entries.map(({ id }) => id);
|
|
647
|
+
const batches = [
|
|
648
|
+
{ type, entries },
|
|
649
|
+
...options.sideBatches ?? []
|
|
650
|
+
];
|
|
651
|
+
const origins = /* @__PURE__ */ new Map();
|
|
652
|
+
for (const batch of batches) {
|
|
653
|
+
if (!s.entities[batch.type]) s.entities[batch.type] = {};
|
|
654
|
+
for (const { id } of batch.entries) {
|
|
655
|
+
const key = ek(batch.type, id);
|
|
656
|
+
if (!origins.has(key)) {
|
|
657
|
+
origins.set(key, s.syncMetadata[key]?.origin ?? "server");
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
for (const batch of batches) {
|
|
662
|
+
const strategy = getMergeStrategy(batch.type);
|
|
663
|
+
for (const { id, data } of batch.entries) {
|
|
664
|
+
const key = ek(batch.type, id);
|
|
665
|
+
const ctx = {
|
|
666
|
+
type: batch.type,
|
|
667
|
+
id,
|
|
668
|
+
origin: origins.get(key) ?? "server",
|
|
669
|
+
updatedAt: fetchedAt
|
|
670
|
+
};
|
|
671
|
+
s.entities[batch.type][id] = strategy(s.entities[batch.type][id], data, ctx);
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
const completed = /* @__PURE__ */ new Set();
|
|
675
|
+
for (const batch of batches) {
|
|
676
|
+
for (const { id } of batch.entries) {
|
|
677
|
+
const key = ek(batch.type, id);
|
|
678
|
+
if (completed.has(key)) continue;
|
|
679
|
+
completed.add(key);
|
|
680
|
+
if (!s.entityStates[key]) s.entityStates[key] = defaultEntityState();
|
|
681
|
+
s.entityStates[key].lastFetched = fetchedAt;
|
|
682
|
+
s.entityStates[key].isFetching = false;
|
|
683
|
+
s.entityStates[key].error = null;
|
|
684
|
+
s.entityStates[key].stale = false;
|
|
685
|
+
s.syncMetadata[key] = {
|
|
686
|
+
...s.syncMetadata[key] ?? defaultSyncMetadata(),
|
|
687
|
+
synced: true,
|
|
688
|
+
origin: "server",
|
|
689
|
+
updatedAt: fetchedAt
|
|
690
|
+
};
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
const readDraftEntity = (id) => {
|
|
694
|
+
const base = s.entities[type]?.[id];
|
|
695
|
+
if (!base) return null;
|
|
696
|
+
const patch = s.patches[type]?.[id];
|
|
697
|
+
const metadata = s.syncMetadata[ek(type, id)] ?? defaultSyncMetadata();
|
|
698
|
+
return {
|
|
699
|
+
...base,
|
|
700
|
+
...patch ?? {},
|
|
701
|
+
$synced: metadata.synced,
|
|
702
|
+
$origin: metadata.origin,
|
|
703
|
+
$updatedAt: metadata.updatedAt
|
|
704
|
+
};
|
|
705
|
+
};
|
|
706
|
+
for (const target of options.lists ?? []) {
|
|
707
|
+
const existing = s.lists[target.key] ?? defaultListState();
|
|
708
|
+
const targetIds = target.ids ?? ids;
|
|
709
|
+
const nextIds = target.mode === "append" ? Array.from(/* @__PURE__ */ new Set([...existing.ids, ...targetIds])) : targetIds;
|
|
710
|
+
s.lists[target.key] = {
|
|
711
|
+
...existing,
|
|
712
|
+
...target.meta ?? {},
|
|
713
|
+
ids: nextIds,
|
|
714
|
+
isFetching: false,
|
|
715
|
+
isFetchingMore: false,
|
|
716
|
+
error: null,
|
|
717
|
+
lastError: null,
|
|
718
|
+
stale: false,
|
|
719
|
+
lastFetched: fetchedAt
|
|
720
|
+
};
|
|
721
|
+
}
|
|
722
|
+
for (const projection of options.projections ?? []) {
|
|
723
|
+
const existing = s.lists[projection.key] ?? defaultListState();
|
|
724
|
+
const nextIds = [...existing.ids];
|
|
725
|
+
const { filter, sort, search } = projection.view;
|
|
726
|
+
for (const id of new Set(ids)) {
|
|
727
|
+
if (nextIds.includes(id)) continue;
|
|
728
|
+
const entity = readDraftEntity(id);
|
|
729
|
+
if (!entity) continue;
|
|
730
|
+
const matches = (!filter || matchesFilter(entity, filter)) && (!search?.query || matchesSearch(entity, search.query, search.fields));
|
|
731
|
+
if (!matches) continue;
|
|
732
|
+
if (sort && sort.length > 0) {
|
|
733
|
+
const index = findInsertionIndex(entity, nextIds, readDraftEntity, sort);
|
|
734
|
+
nextIds.splice(index, 0, id);
|
|
735
|
+
} else {
|
|
736
|
+
nextIds.unshift(id);
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
s.lists[projection.key] = projection.completeFetch ? {
|
|
740
|
+
...existing,
|
|
741
|
+
ids: nextIds,
|
|
742
|
+
isFetching: false,
|
|
743
|
+
isFetchingMore: false,
|
|
744
|
+
error: null,
|
|
745
|
+
lastError: null,
|
|
746
|
+
stale: false,
|
|
747
|
+
lastFetched: fetchedAt
|
|
748
|
+
} : { ...existing, ids: nextIds };
|
|
749
|
+
}
|
|
750
|
+
for (const key of options.finishListFetches ?? []) {
|
|
751
|
+
if (!s.lists[key]) s.lists[key] = defaultListState();
|
|
752
|
+
s.lists[key].isFetching = false;
|
|
753
|
+
}
|
|
754
|
+
}),
|
|
515
755
|
replaceEntity: (type, id, data) => set((s) => {
|
|
516
756
|
if (!s.entities[type]) s.entities[type] = {};
|
|
517
757
|
s.entities[type][id] = data;
|
|
@@ -1006,12 +1246,12 @@ async function fetchList(opts, params, engineOpts, isLoadMore = false, storeApi
|
|
|
1006
1246
|
try {
|
|
1007
1247
|
const response = await fetch(params);
|
|
1008
1248
|
const normalized = response.items.map(normalize);
|
|
1009
|
-
storeApi.getState().upsertEntities(type, normalized.map(({ id, data }) => ({ id, data })));
|
|
1010
|
-
for (const { id } of normalized) storeApi.getState().setEntityFetched(type, id);
|
|
1011
|
-
const ids = normalized.map(({ id }) => id);
|
|
1012
1249
|
const meta = { total: response.total ?? null, nextCursor: response.nextCursor ?? null, prevCursor: response.prevCursor ?? null, hasNextPage: response.hasNextPage ?? !!response.nextCursor, hasPrevPage: response.hasPrevPage ?? !!response.prevCursor, currentPage: response.page ?? null, pageSize: response.pageSize ?? null };
|
|
1013
|
-
|
|
1014
|
-
|
|
1250
|
+
storeApi.getState().ingestFetchedList(
|
|
1251
|
+
type,
|
|
1252
|
+
normalized.map(({ id, data }) => ({ id, data })),
|
|
1253
|
+
{ lists: [{ key, mode: mode === "append" && isLoadMore ? "append" : "replace", meta }] }
|
|
1254
|
+
);
|
|
1015
1255
|
if (sideEffects) sideEffects(response.items, storeApi);
|
|
1016
1256
|
opts.onSuccess?.(response);
|
|
1017
1257
|
} catch (err) {
|
|
@@ -1872,135 +2112,6 @@ function subscribeTimeTravel(cb) {
|
|
|
1872
2112
|
return () => listeners.delete(cb);
|
|
1873
2113
|
}
|
|
1874
2114
|
|
|
1875
|
-
// src/view/evaluator.ts
|
|
1876
|
-
function matchesFilter(entity, filter) {
|
|
1877
|
-
if (Array.isArray(filter)) return filter.every((clause) => matchesClause(entity, clause));
|
|
1878
|
-
return matchesGroup(entity, filter);
|
|
1879
|
-
}
|
|
1880
|
-
function matchesGroup(entity, group) {
|
|
1881
|
-
const { logic, clauses } = group;
|
|
1882
|
-
if (logic === "and") return clauses.every((c) => "logic" in c ? matchesGroup(entity, c) : matchesClause(entity, c));
|
|
1883
|
-
return clauses.some((c) => "logic" in c ? matchesGroup(entity, c) : matchesClause(entity, c));
|
|
1884
|
-
}
|
|
1885
|
-
function matchesClause(entity, clause) {
|
|
1886
|
-
const { field, op, value, predicate } = clause;
|
|
1887
|
-
const fv = getNestedValue(entity, field);
|
|
1888
|
-
switch (op) {
|
|
1889
|
-
case "eq":
|
|
1890
|
-
return fv === value;
|
|
1891
|
-
case "neq":
|
|
1892
|
-
return fv !== value;
|
|
1893
|
-
case "gt":
|
|
1894
|
-
return fv > value;
|
|
1895
|
-
case "gte":
|
|
1896
|
-
return fv >= value;
|
|
1897
|
-
case "lt":
|
|
1898
|
-
return fv < value;
|
|
1899
|
-
case "lte":
|
|
1900
|
-
return fv <= value;
|
|
1901
|
-
case "in":
|
|
1902
|
-
return Array.isArray(value) && value.includes(fv);
|
|
1903
|
-
case "nin":
|
|
1904
|
-
return Array.isArray(value) && !value.includes(fv);
|
|
1905
|
-
case "isNull":
|
|
1906
|
-
return fv == null;
|
|
1907
|
-
case "isNotNull":
|
|
1908
|
-
return fv != null;
|
|
1909
|
-
case "contains":
|
|
1910
|
-
return typeof fv === "string" && typeof value === "string" && fv.toLowerCase().includes(value.toLowerCase());
|
|
1911
|
-
case "startsWith":
|
|
1912
|
-
return typeof fv === "string" && typeof value === "string" && fv.toLowerCase().startsWith(value.toLowerCase());
|
|
1913
|
-
case "endsWith":
|
|
1914
|
-
return typeof fv === "string" && typeof value === "string" && fv.toLowerCase().endsWith(value.toLowerCase());
|
|
1915
|
-
case "between": {
|
|
1916
|
-
const [lo, hi] = value;
|
|
1917
|
-
return fv >= lo && fv <= hi;
|
|
1918
|
-
}
|
|
1919
|
-
case "arrayContains":
|
|
1920
|
-
return Array.isArray(fv) && fv.includes(value);
|
|
1921
|
-
case "arrayOverlaps":
|
|
1922
|
-
return Array.isArray(fv) && Array.isArray(value) && value.some((v) => fv.includes(v));
|
|
1923
|
-
case "matches":
|
|
1924
|
-
return typeof fv === "string" && new RegExp(value).test(fv);
|
|
1925
|
-
case "custom":
|
|
1926
|
-
return predicate ? predicate(fv, entity) : true;
|
|
1927
|
-
default:
|
|
1928
|
-
return true;
|
|
1929
|
-
}
|
|
1930
|
-
}
|
|
1931
|
-
function matchesSearch(entity, query, fields) {
|
|
1932
|
-
if (!query.trim()) return true;
|
|
1933
|
-
const lq = query.toLowerCase();
|
|
1934
|
-
return fields.some((field) => {
|
|
1935
|
-
const v = getNestedValue(entity, field);
|
|
1936
|
-
return typeof v === "string" && v.toLowerCase().includes(lq);
|
|
1937
|
-
});
|
|
1938
|
-
}
|
|
1939
|
-
function compareEntities(a, b, sort) {
|
|
1940
|
-
for (const clause of sort) {
|
|
1941
|
-
const r = compareByClause(a, b, clause);
|
|
1942
|
-
if (r !== 0) return r;
|
|
1943
|
-
}
|
|
1944
|
-
return 0;
|
|
1945
|
-
}
|
|
1946
|
-
function compareByClause(a, b, clause) {
|
|
1947
|
-
const { field, direction, nulls = "last", comparator } = clause;
|
|
1948
|
-
const av = getNestedValue(a, field);
|
|
1949
|
-
const bv = getNestedValue(b, field);
|
|
1950
|
-
const aNull = av == null;
|
|
1951
|
-
const bNull = bv == null;
|
|
1952
|
-
if (aNull && bNull) return 0;
|
|
1953
|
-
if (aNull) return nulls === "first" ? -1 : 1;
|
|
1954
|
-
if (bNull) return nulls === "first" ? 1 : -1;
|
|
1955
|
-
let cmp;
|
|
1956
|
-
if (comparator) cmp = comparator(av, bv);
|
|
1957
|
-
else if (typeof av === "string" && typeof bv === "string") cmp = av.localeCompare(bv, void 0, { sensitivity: "base", numeric: true });
|
|
1958
|
-
else if (typeof av === "number" && typeof bv === "number") cmp = av - bv;
|
|
1959
|
-
else cmp = String(av).localeCompare(String(bv));
|
|
1960
|
-
return direction === "desc" ? -cmp : cmp;
|
|
1961
|
-
}
|
|
1962
|
-
function findInsertionIndex(entity, sortedIds, getEntity, sort) {
|
|
1963
|
-
let lo = 0;
|
|
1964
|
-
let hi = sortedIds.length;
|
|
1965
|
-
while (lo < hi) {
|
|
1966
|
-
const mid = lo + hi >>> 1;
|
|
1967
|
-
const me = getEntity(sortedIds[mid]);
|
|
1968
|
-
if (!me) {
|
|
1969
|
-
lo = mid + 1;
|
|
1970
|
-
continue;
|
|
1971
|
-
}
|
|
1972
|
-
if (compareEntities(entity, me, sort) <= 0) hi = mid;
|
|
1973
|
-
else lo = mid + 1;
|
|
1974
|
-
}
|
|
1975
|
-
return lo;
|
|
1976
|
-
}
|
|
1977
|
-
function applyView(ids, getEntity, filter, sort, search) {
|
|
1978
|
-
let entries = [];
|
|
1979
|
-
for (const id of ids) {
|
|
1980
|
-
const entity = getEntity(id);
|
|
1981
|
-
if (!entity) continue;
|
|
1982
|
-
entries.push({ id, entity });
|
|
1983
|
-
}
|
|
1984
|
-
if (filter && entries.length > 0) entries = entries.filter(({ entity }) => matchesFilter(entity, filter));
|
|
1985
|
-
if (search?.query) entries = entries.filter(({ entity }) => matchesSearch(entity, search.query, search.fields));
|
|
1986
|
-
if (sort && sort.length > 0) entries.sort((a, b) => compareEntities(a.entity, b.entity, sort));
|
|
1987
|
-
return entries.map((e) => e.id);
|
|
1988
|
-
}
|
|
1989
|
-
function checkCompleteness(loadedCount, total, hasNextPage) {
|
|
1990
|
-
if (!hasNextPage && total !== null && loadedCount >= total) return { isComplete: true, reason: "all-loaded" };
|
|
1991
|
-
if (hasNextPage) return { isComplete: false, reason: "has-more-pages" };
|
|
1992
|
-
return { isComplete: true, reason: "no-more-pages" };
|
|
1993
|
-
}
|
|
1994
|
-
function getNestedValue(obj, path) {
|
|
1995
|
-
const parts = path.replace(/\[(\d+)\]/g, ".$1").split(".");
|
|
1996
|
-
let current = obj;
|
|
1997
|
-
for (const part of parts) {
|
|
1998
|
-
if (current == null || typeof current !== "object") return void 0;
|
|
1999
|
-
current = current[part];
|
|
2000
|
-
}
|
|
2001
|
-
return current;
|
|
2002
|
-
}
|
|
2003
|
-
|
|
2004
2115
|
// src/view/incremental.ts
|
|
2005
2116
|
var IncrementalView = class {
|
|
2006
2117
|
opts;
|