@proteinjs/db-ui 1.8.5 → 1.9.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/CHANGELOG.md CHANGED
@@ -3,6 +3,17 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ # [1.9.0](https://github.com/proteinjs/db/compare/@proteinjs/db-ui@1.8.5...@proteinjs/db-ui@1.9.0) (2026-08-14)
7
+
8
+
9
+ ### Features
10
+
11
+ * cursor-window paging owner; RecordIterator migrated off offset paging ([453c4a2](https://github.com/proteinjs/db/commit/453c4a2a5f87bd7b58b0042185100bcf9ae0e183))
12
+
13
+
14
+
15
+
16
+
6
17
  ## [1.8.5](https://github.com/proteinjs/db/compare/@proteinjs/db-ui@1.8.4...@proteinjs/db-ui@1.8.5) (2026-08-14)
7
18
 
8
19
 
@@ -28,7 +28,7 @@ require("react");
28
28
  require("string");
29
29
  require("uuid");
30
30
  /** Generate Source Graph */
31
- var sourceGraph = "{\"options\":{\"directed\":true,\"multigraph\":false,\"compound\":false},\"nodes\":[{\"v\":\"@proteinjs/db-ui/QueryCursorLoader\",\"value\":{\"packageName\":\"@proteinjs/db-ui\",\"name\":\"QueryCursorLoader\",\"filePath\":\"/home/runner/work/db/db/packages/ui/src/table/QueryCursorLoader.ts\",\"qualifiedName\":\"@proteinjs/db-ui/QueryCursorLoader\",\"isAbstract\":false,\"isStatic\":false,\"visibility\":\"public\",\"properties\":[{\"name\":\"reactQueryKeys\",\"type\":{\"packageName\":\"@proteinjs/ui\",\"name\":\"ReactQueryKeys\",\"filePath\":null,\"qualifiedName\":\"@proteinjs/ui/ReactQueryKeys\",\"typeParameters\":null,\"directParents\":null},\"isOptional\":false,\"isAbstract\":false,\"isStatic\":false,\"visibility\":\"public\"},{\"name\":\"sort\",\"type\":{\"packageName\":\"\",\"name\":\"SortCriteria<T>[]\",\"filePath\":null,\"qualifiedName\":\"/SortCriteria<T>[]\",\"typeParameters\":null,\"directParents\":null},\"isOptional\":false,\"isAbstract\":false,\"isStatic\":false,\"visibility\":\"private\"},{\"name\":\"table\",\"type\":{\"packageName\":\"\",\"name\":\"Table<T>\",\"filePath\":null,\"qualifiedName\":\"/Table<T>\",\"typeParameters\":null,\"directParents\":null},\"isOptional\":false,\"isAbstract\":false,\"isStatic\":false,\"visibility\":\"private\"},{\"name\":\"createQuery\",\"type\":{\"packageName\":\"\",\"name\":\"() => QueryBuilder<T>\",\"filePath\":null,\"qualifiedName\":\"/() => QueryBuilder<T>\",\"typeParameters\":null,\"directParents\":null},\"isOptional\":false,\"isAbstract\":false,\"isStatic\":false,\"visibility\":\"private\"},{\"name\":\"queryOptions\",\"type\":{\"packageName\":\"\",\"name\":\"QueryOptions<T>\",\"filePath\":null,\"qualifiedName\":\"/QueryOptions<T>\",\"typeParameters\":null,\"directParents\":null},\"isOptional\":true,\"isAbstract\":false,\"isStatic\":false,\"visibility\":\"private\"}],\"methods\":[{\"name\":\"loadWindow\",\"returnType\":{\"packageName\":\"\",\"name\":\"Promise<CursorWindow<T>>\",\"filePath\":null,\"qualifiedName\":\"/Promise<CursorWindow<T>>\",\"typeParameters\":null,\"directParents\":null},\"isAsync\":true,\"isOptional\":false,\"isAbstract\":false,\"isStatic\":false,\"visibility\":\"public\",\"parameters\":[{\"name\":\"cursor\",\"type\":{\"packageName\":\"\",\"name\":\"CursorValue | null\",\"filePath\":null,\"qualifiedName\":\"/CursorValue | null\",\"typeParameters\":null,\"directParents\":null}},{\"name\":\"windowSize\",\"type\":{\"packageName\":\"\",\"name\":\"number\",\"filePath\":null,\"qualifiedName\":\"/number\",\"typeParameters\":null,\"directParents\":null}}]},{\"name\":\"buildWindowQuery\",\"returnType\":{\"packageName\":\"\",\"name\":\"QueryBuilder<T>\",\"filePath\":null,\"qualifiedName\":\"/QueryBuilder<T>\",\"typeParameters\":null,\"directParents\":null},\"isAsync\":false,\"isOptional\":false,\"isAbstract\":false,\"isStatic\":false,\"visibility\":\"private\",\"parameters\":[{\"name\":\"cursor\",\"type\":{\"packageName\":\"\",\"name\":\"CursorValue | null\",\"filePath\":null,\"qualifiedName\":\"/CursorValue | null\",\"typeParameters\":null,\"directParents\":null}},{\"name\":\"windowSize\",\"type\":{\"packageName\":\"\",\"name\":\"number\",\"filePath\":null,\"qualifiedName\":\"/number\",\"typeParameters\":null,\"directParents\":null}}]},{\"name\":\"deriveNextCursor\",\"returnType\":{\"packageName\":\"\",\"name\":\"CursorValue | null\",\"filePath\":null,\"qualifiedName\":\"/CursorValue | null\",\"typeParameters\":null,\"directParents\":null},\"isAsync\":false,\"isOptional\":false,\"isAbstract\":false,\"isStatic\":false,\"visibility\":\"private\",\"parameters\":[{\"name\":\"rows\",\"type\":{\"packageName\":\"\",\"name\":\"T[]\",\"filePath\":null,\"qualifiedName\":\"/T[]\",\"typeParameters\":null,\"directParents\":null}},{\"name\":\"windowSize\",\"type\":{\"packageName\":\"\",\"name\":\"number\",\"filePath\":null,\"qualifiedName\":\"/number\",\"typeParameters\":null,\"directParents\":null}}]}],\"typeParameters\":[\"/T extends Record\"],\"directParentInterfaces\":[{\"packageName\":\"@proteinjs/ui\",\"name\":\"CursorLoader\",\"filePath\":null,\"qualifiedName\":\"@proteinjs/ui/CursorLoader\",\"properties\":[],\"methods\":[],\"typeParameters\":[\"/T\"],\"directParents\":[]}],\"directParentClasses\":[],\"sourceType\":2}},{\"v\":\"@proteinjs/ui/CursorLoader\"},{\"v\":\"@proteinjs/db-ui/QueryTableLoader\",\"value\":{\"packageName\":\"@proteinjs/db-ui\",\"name\":\"QueryTableLoader\",\"filePath\":\"/home/runner/work/db/db/packages/ui/src/table/QueryTableLoader.ts\",\"qualifiedName\":\"@proteinjs/db-ui/QueryTableLoader\",\"isAbstract\":false,\"isStatic\":false,\"visibility\":\"public\",\"properties\":[{\"name\":\"rowCountQb\",\"type\":{\"packageName\":\"\",\"name\":\"QueryBuilder<T>\",\"filePath\":null,\"qualifiedName\":\"/QueryBuilder<T>\",\"typeParameters\":null,\"directParents\":null},\"isOptional\":true,\"isAbstract\":false,\"isStatic\":false,\"visibility\":\"private\"},{\"name\":\"paginationQb\",\"type\":{\"packageName\":\"\",\"name\":\"QueryBuilder<T>\",\"filePath\":null,\"qualifiedName\":\"/QueryBuilder<T>\",\"typeParameters\":null,\"directParents\":null},\"isOptional\":true,\"isAbstract\":false,\"isStatic\":false,\"visibility\":\"private\"},{\"name\":\"reactQueryKeys\",\"type\":{\"packageName\":\"\",\"name\":\"TableLoader<T>['reactQueryKeys']\",\"filePath\":null,\"qualifiedName\":\"/TableLoader<T>['reactQueryKeys']\",\"typeParameters\":null,\"directParents\":null},\"isOptional\":false,\"isAbstract\":false,\"isStatic\":false,\"visibility\":\"public\"},{\"name\":\"table\",\"type\":{\"packageName\":\"\",\"name\":\"Table<T>\",\"filePath\":null,\"qualifiedName\":\"/Table<T>\",\"typeParameters\":null,\"directParents\":null},\"isOptional\":false,\"isAbstract\":false,\"isStatic\":false,\"visibility\":\"private\"},{\"name\":\"query\",\"type\":{\"packageName\":\"\",\"name\":\"Query<T>\",\"filePath\":null,\"qualifiedName\":\"/Query<T>\",\"typeParameters\":null,\"directParents\":null},\"isOptional\":true,\"isAbstract\":false,\"isStatic\":false,\"visibility\":\"private\"},{\"name\":\"sort\",\"type\":{\"packageName\":\"\",\"name\":\"SortCriteria<T>[]\",\"filePath\":null,\"qualifiedName\":\"/SortCriteria<T>[]\",\"typeParameters\":null,\"directParents\":null},\"isOptional\":true,\"isAbstract\":false,\"isStatic\":false,\"visibility\":\"private\"},{\"name\":\"queryOptions\",\"type\":{\"packageName\":\"\",\"name\":\"QueryOptions<T>\",\"filePath\":null,\"qualifiedName\":\"/QueryOptions<T>\",\"typeParameters\":null,\"directParents\":null},\"isOptional\":true,\"isAbstract\":false,\"isStatic\":false,\"visibility\":\"private\"}],\"methods\":[{\"name\":\"load\",\"returnType\":{\"packageName\":\"\",\"name\":\"Promise<RowWindow<T>>\",\"filePath\":null,\"qualifiedName\":\"/Promise<RowWindow<T>>\",\"typeParameters\":null,\"directParents\":null},\"isAsync\":true,\"isOptional\":false,\"isAbstract\":false,\"isStatic\":false,\"visibility\":\"public\",\"parameters\":[{\"name\":\"startIndex\",\"type\":{\"packageName\":\"\",\"name\":\"number\",\"filePath\":null,\"qualifiedName\":\"/number\",\"typeParameters\":null,\"directParents\":null}},{\"name\":\"endIndex\",\"type\":{\"packageName\":\"\",\"name\":\"number\",\"filePath\":null,\"qualifiedName\":\"/number\",\"typeParameters\":null,\"directParents\":null}},{\"name\":\"skipRowCount\",\"type\":{\"packageName\":\"\",\"name\":\"boolean\",\"filePath\":null,\"qualifiedName\":\"/boolean\",\"typeParameters\":null,\"directParents\":null}}]}],\"typeParameters\":[\"/T extends Record\"],\"directParentInterfaces\":[{\"packageName\":\"@proteinjs/ui\",\"name\":\"TableLoader\",\"filePath\":null,\"qualifiedName\":\"@proteinjs/ui/TableLoader\",\"properties\":[],\"methods\":[],\"typeParameters\":[\"/T\"],\"directParents\":[]}],\"directParentClasses\":[],\"sourceType\":2}},{\"v\":\"@proteinjs/ui/TableLoader\"},{\"v\":\"@proteinjs/db-ui/RecordFormCustomization\",\"value\":{\"packageName\":\"@proteinjs/db-ui\",\"name\":\"RecordFormCustomization\",\"filePath\":\"/home/runner/work/db/db/packages/ui/src/form/RecordFormCustomization.tsx\",\"qualifiedName\":\"@proteinjs/db-ui/RecordFormCustomization\",\"isAbstract\":false,\"isStatic\":false,\"visibility\":\"public\",\"properties\":[{\"name\":\"table\",\"type\":{\"packageName\":\"\",\"name\":\"Table<any>\",\"filePath\":null,\"qualifiedName\":\"/Table<any>\",\"typeParameters\":null,\"directParents\":null},\"isOptional\":false,\"isAbstract\":false,\"isStatic\":false,\"visibility\":\"public\"}],\"methods\":[{\"name\":\"getFormButtons\",\"returnType\":{\"packageName\":\"\",\"name\":\"FormButtons<any>\",\"filePath\":null,\"qualifiedName\":\"/FormButtons<any>\",\"typeParameters\":null,\"directParents\":null},\"isAsync\":false,\"isOptional\":false,\"isAbstract\":false,\"isStatic\":false,\"visibility\":\"public\",\"parameters\":[{\"name\":\"record\",\"type\":{\"packageName\":\"\",\"name\":\"any\",\"filePath\":null,\"qualifiedName\":\"/any\",\"typeParameters\":null,\"directParents\":null}},{\"name\":\"defaultFormButtons\",\"type\":{\"packageName\":\"\",\"name\":\"FormButtons<any>\",\"filePath\":null,\"qualifiedName\":\"/FormButtons<any>\",\"typeParameters\":null,\"directParents\":null}}]},{\"name\":\"getFieldLayout\",\"returnType\":{\"packageName\":\"\",\"name\":\"string[] | string[][]\",\"filePath\":null,\"qualifiedName\":\"/string[] | string[][]\",\"typeParameters\":null,\"directParents\":null},\"isAsync\":false,\"isOptional\":false,\"isAbstract\":false,\"isStatic\":false,\"visibility\":\"public\",\"parameters\":[{\"name\":\"record\",\"type\":{\"packageName\":\"\",\"name\":\"any\",\"filePath\":null,\"qualifiedName\":\"/any\",\"typeParameters\":null,\"directParents\":null}},{\"name\":\"defaultFieldLayout\",\"type\":{\"packageName\":\"\",\"name\":\"string[] | string[][]\",\"filePath\":null,\"qualifiedName\":\"/string[] | string[][]\",\"typeParameters\":null,\"directParents\":null}}]}],\"typeParameters\":[],\"directParentInterfaces\":[{\"packageName\":\"@proteinjs/reflection\",\"name\":\"Loadable\",\"filePath\":null,\"qualifiedName\":\"@proteinjs/reflection/Loadable\",\"properties\":[],\"methods\":[],\"typeParameters\":[],\"directParents\":[]}],\"directParentClasses\":[],\"sourceType\":2}},{\"v\":\"@proteinjs/reflection/Loadable\"},{\"v\":\"@proteinjs/db-ui/hashGeneratorPage\",\"value\":{\"packageName\":\"@proteinjs/db-ui\",\"name\":\"hashGeneratorPage\",\"filePath\":\"/home/runner/work/db/db/packages/ui/src/pages/HashGeneratorPage.tsx\",\"qualifiedName\":\"@proteinjs/db-ui/hashGeneratorPage\",\"type\":{\"packageName\":\"@proteinjs/ui\",\"name\":\"Page\",\"filePath\":null,\"qualifiedName\":\"@proteinjs/ui/Page\",\"typeParameters\":[],\"directParents\":[{\"packageName\":\"@proteinjs/ui\",\"name\":\"Page\",\"filePath\":null,\"qualifiedName\":\"@proteinjs/ui/Page\",\"typeParameters\":[],\"directParents\":null}]},\"isExported\":true,\"isConst\":true,\"sourceType\":0}},{\"v\":\"@proteinjs/ui/Page\"},{\"v\":\"@proteinjs/db-ui/recordFormPage\",\"value\":{\"packageName\":\"@proteinjs/db-ui\",\"name\":\"recordFormPage\",\"filePath\":\"/home/runner/work/db/db/packages/ui/src/pages/RecordFormPage.tsx\",\"qualifiedName\":\"@proteinjs/db-ui/recordFormPage\",\"type\":{\"packageName\":\"@proteinjs/ui\",\"name\":\"Page\",\"filePath\":null,\"qualifiedName\":\"@proteinjs/ui/Page\",\"typeParameters\":[],\"directParents\":[{\"packageName\":\"@proteinjs/ui\",\"name\":\"Page\",\"filePath\":null,\"qualifiedName\":\"@proteinjs/ui/Page\",\"typeParameters\":[],\"directParents\":null}]},\"isExported\":true,\"isConst\":true,\"sourceType\":0}},{\"v\":\"@proteinjs/db-ui/recordTablePage\",\"value\":{\"packageName\":\"@proteinjs/db-ui\",\"name\":\"recordTablePage\",\"filePath\":\"/home/runner/work/db/db/packages/ui/src/pages/RecordTablePage.tsx\",\"qualifiedName\":\"@proteinjs/db-ui/recordTablePage\",\"type\":{\"packageName\":\"@proteinjs/ui\",\"name\":\"Page\",\"filePath\":null,\"qualifiedName\":\"@proteinjs/ui/Page\",\"typeParameters\":[],\"directParents\":[{\"packageName\":\"@proteinjs/ui\",\"name\":\"Page\",\"filePath\":null,\"qualifiedName\":\"@proteinjs/ui/Page\",\"typeParameters\":[],\"directParents\":null}]},\"isExported\":true,\"isConst\":true,\"sourceType\":0}},{\"v\":\"@proteinjs/db-ui/tablesPage\",\"value\":{\"packageName\":\"@proteinjs/db-ui\",\"name\":\"tablesPage\",\"filePath\":\"/home/runner/work/db/db/packages/ui/src/pages/TablesPage.tsx\",\"qualifiedName\":\"@proteinjs/db-ui/tablesPage\",\"type\":{\"packageName\":\"@proteinjs/ui\",\"name\":\"Page\",\"filePath\":null,\"qualifiedName\":\"@proteinjs/ui/Page\",\"typeParameters\":[],\"directParents\":[{\"packageName\":\"@proteinjs/ui\",\"name\":\"Page\",\"filePath\":null,\"qualifiedName\":\"@proteinjs/ui/Page\",\"typeParameters\":[],\"directParents\":null}]},\"isExported\":true,\"isConst\":true,\"sourceType\":0}},{\"v\":\"@proteinjs/db-ui/uuidGeneratorPage\",\"value\":{\"packageName\":\"@proteinjs/db-ui\",\"name\":\"uuidGeneratorPage\",\"filePath\":\"/home/runner/work/db/db/packages/ui/src/pages/UuidGeneratorPage.tsx\",\"qualifiedName\":\"@proteinjs/db-ui/uuidGeneratorPage\",\"type\":{\"packageName\":\"@proteinjs/ui\",\"name\":\"Page\",\"filePath\":null,\"qualifiedName\":\"@proteinjs/ui/Page\",\"typeParameters\":[],\"directParents\":[{\"packageName\":\"@proteinjs/ui\",\"name\":\"Page\",\"filePath\":null,\"qualifiedName\":\"@proteinjs/ui/Page\",\"typeParameters\":[],\"directParents\":null}]},\"isExported\":true,\"isConst\":true,\"sourceType\":0}},{\"v\":\"@proteinjs/db-ui/SpecificTableProps\"},{\"v\":\"@proteinjs/db-ui/MigrationRecordFormCustomization\",\"value\":{\"packageName\":\"@proteinjs/db-ui\",\"name\":\"MigrationRecordFormCustomization\",\"filePath\":\"/home/runner/work/db/db/packages/ui/src/form/customizations/MigrationRecordFormCustomization.tsx\",\"qualifiedName\":\"@proteinjs/db-ui/MigrationRecordFormCustomization\",\"isAbstract\":false,\"isStatic\":false,\"visibility\":\"public\",\"properties\":[{\"name\":\"table\",\"type\":null,\"isOptional\":false,\"isAbstract\":false,\"isStatic\":false,\"visibility\":\"public\"}],\"methods\":[{\"name\":\"getFormButtons\",\"returnType\":{\"packageName\":\"\",\"name\":\"FormButtons<any>\",\"filePath\":null,\"qualifiedName\":\"/FormButtons<any>\",\"typeParameters\":null,\"directParents\":null},\"isAsync\":false,\"isOptional\":false,\"isAbstract\":false,\"isStatic\":false,\"visibility\":\"public\",\"parameters\":[{\"name\":\"migration\",\"type\":{\"packageName\":\"@proteinjs/db\",\"name\":\"Migration\",\"filePath\":null,\"qualifiedName\":\"@proteinjs/db/Migration\",\"typeParameters\":null,\"directParents\":null}},{\"name\":\"defaultFormButtons\",\"type\":{\"packageName\":\"\",\"name\":\"FormButtons<any>\",\"filePath\":null,\"qualifiedName\":\"/FormButtons<any>\",\"typeParameters\":null,\"directParents\":null}}]}],\"typeParameters\":[],\"directParentInterfaces\":[],\"directParentClasses\":[{\"packageName\":\"@proteinjs/db-ui\",\"name\":\"RecordFormCustomization\",\"filePath\":null,\"qualifiedName\":\"@proteinjs/db-ui/RecordFormCustomization\",\"isAbstract\":false,\"isStatic\":false,\"visibility\":\"public\",\"properties\":[],\"methods\":[],\"typeParameters\":[],\"directParentInterfaces\":[],\"directParentClasses\":[]}],\"sourceType\":2}}],\"edges\":[{\"v\":\"@proteinjs/db-ui/QueryCursorLoader\",\"w\":\"@proteinjs/ui/CursorLoader\",\"value\":\"implements interface\"},{\"v\":\"@proteinjs/db-ui/QueryTableLoader\",\"w\":\"@proteinjs/ui/TableLoader\",\"value\":\"implements interface\"},{\"v\":\"@proteinjs/db-ui/RecordFormCustomization\",\"w\":\"@proteinjs/reflection/Loadable\",\"value\":\"implements interface\"},{\"v\":\"@proteinjs/db-ui/hashGeneratorPage\",\"w\":\"@proteinjs/ui/Page\",\"value\":\"has type\"},{\"v\":\"@proteinjs/db-ui/recordFormPage\",\"w\":\"@proteinjs/ui/Page\",\"value\":\"has type\"},{\"v\":\"@proteinjs/db-ui/recordTablePage\",\"w\":\"@proteinjs/ui/Page\",\"value\":\"has type\"},{\"v\":\"@proteinjs/db-ui/tablesPage\",\"w\":\"@proteinjs/ui/Page\",\"value\":\"has type\"},{\"v\":\"@proteinjs/db-ui/uuidGeneratorPage\",\"w\":\"@proteinjs/ui/Page\",\"value\":\"has type\"},{\"v\":\"@proteinjs/db-ui/MigrationRecordFormCustomization\",\"w\":\"@proteinjs/db-ui/RecordFormCustomization\",\"value\":\"extends class\"}]}";
31
+ var sourceGraph = "{\"options\":{\"directed\":true,\"multigraph\":false,\"compound\":false},\"nodes\":[{\"v\":\"@proteinjs/db-ui/QueryCursorLoader\",\"value\":{\"packageName\":\"@proteinjs/db-ui\",\"name\":\"QueryCursorLoader\",\"filePath\":\"/home/runner/work/db/db/packages/ui/src/table/QueryCursorLoader.ts\",\"qualifiedName\":\"@proteinjs/db-ui/QueryCursorLoader\",\"isAbstract\":false,\"isStatic\":false,\"visibility\":\"public\",\"properties\":[{\"name\":\"reactQueryKeys\",\"type\":{\"packageName\":\"@proteinjs/ui\",\"name\":\"ReactQueryKeys\",\"filePath\":null,\"qualifiedName\":\"@proteinjs/ui/ReactQueryKeys\",\"typeParameters\":null,\"directParents\":null},\"isOptional\":false,\"isAbstract\":false,\"isStatic\":false,\"visibility\":\"public\"},{\"name\":\"pager\",\"type\":{\"packageName\":\"\",\"name\":\"CursorWindowPager<T>\",\"filePath\":null,\"qualifiedName\":\"/CursorWindowPager<T>\",\"typeParameters\":null,\"directParents\":null},\"isOptional\":false,\"isAbstract\":false,\"isStatic\":false,\"visibility\":\"private\"}],\"methods\":[{\"name\":\"loadWindow\",\"returnType\":{\"packageName\":\"\",\"name\":\"Promise<CursorWindow<T>>\",\"filePath\":null,\"qualifiedName\":\"/Promise<CursorWindow<T>>\",\"typeParameters\":null,\"directParents\":null},\"isAsync\":true,\"isOptional\":false,\"isAbstract\":false,\"isStatic\":false,\"visibility\":\"public\",\"parameters\":[{\"name\":\"cursor\",\"type\":{\"packageName\":\"\",\"name\":\"CursorValue | null\",\"filePath\":null,\"qualifiedName\":\"/CursorValue | null\",\"typeParameters\":null,\"directParents\":null}},{\"name\":\"windowSize\",\"type\":{\"packageName\":\"\",\"name\":\"number\",\"filePath\":null,\"qualifiedName\":\"/number\",\"typeParameters\":null,\"directParents\":null}}]}],\"typeParameters\":[\"/T extends Record\"],\"directParentInterfaces\":[{\"packageName\":\"@proteinjs/ui\",\"name\":\"CursorLoader\",\"filePath\":null,\"qualifiedName\":\"@proteinjs/ui/CursorLoader\",\"properties\":[],\"methods\":[],\"typeParameters\":[\"/T\"],\"directParents\":[]}],\"directParentClasses\":[],\"sourceType\":2}},{\"v\":\"@proteinjs/ui/CursorLoader\"},{\"v\":\"@proteinjs/db-ui/QueryTableLoader\",\"value\":{\"packageName\":\"@proteinjs/db-ui\",\"name\":\"QueryTableLoader\",\"filePath\":\"/home/runner/work/db/db/packages/ui/src/table/QueryTableLoader.ts\",\"qualifiedName\":\"@proteinjs/db-ui/QueryTableLoader\",\"isAbstract\":false,\"isStatic\":false,\"visibility\":\"public\",\"properties\":[{\"name\":\"rowCountQb\",\"type\":{\"packageName\":\"\",\"name\":\"QueryBuilder<T>\",\"filePath\":null,\"qualifiedName\":\"/QueryBuilder<T>\",\"typeParameters\":null,\"directParents\":null},\"isOptional\":true,\"isAbstract\":false,\"isStatic\":false,\"visibility\":\"private\"},{\"name\":\"paginationQb\",\"type\":{\"packageName\":\"\",\"name\":\"QueryBuilder<T>\",\"filePath\":null,\"qualifiedName\":\"/QueryBuilder<T>\",\"typeParameters\":null,\"directParents\":null},\"isOptional\":true,\"isAbstract\":false,\"isStatic\":false,\"visibility\":\"private\"},{\"name\":\"reactQueryKeys\",\"type\":{\"packageName\":\"\",\"name\":\"TableLoader<T>['reactQueryKeys']\",\"filePath\":null,\"qualifiedName\":\"/TableLoader<T>['reactQueryKeys']\",\"typeParameters\":null,\"directParents\":null},\"isOptional\":false,\"isAbstract\":false,\"isStatic\":false,\"visibility\":\"public\"},{\"name\":\"table\",\"type\":{\"packageName\":\"\",\"name\":\"Table<T>\",\"filePath\":null,\"qualifiedName\":\"/Table<T>\",\"typeParameters\":null,\"directParents\":null},\"isOptional\":false,\"isAbstract\":false,\"isStatic\":false,\"visibility\":\"private\"},{\"name\":\"query\",\"type\":{\"packageName\":\"\",\"name\":\"Query<T>\",\"filePath\":null,\"qualifiedName\":\"/Query<T>\",\"typeParameters\":null,\"directParents\":null},\"isOptional\":true,\"isAbstract\":false,\"isStatic\":false,\"visibility\":\"private\"},{\"name\":\"sort\",\"type\":{\"packageName\":\"\",\"name\":\"SortCriteria<T>[]\",\"filePath\":null,\"qualifiedName\":\"/SortCriteria<T>[]\",\"typeParameters\":null,\"directParents\":null},\"isOptional\":true,\"isAbstract\":false,\"isStatic\":false,\"visibility\":\"private\"},{\"name\":\"queryOptions\",\"type\":{\"packageName\":\"\",\"name\":\"QueryOptions<T>\",\"filePath\":null,\"qualifiedName\":\"/QueryOptions<T>\",\"typeParameters\":null,\"directParents\":null},\"isOptional\":true,\"isAbstract\":false,\"isStatic\":false,\"visibility\":\"private\"}],\"methods\":[{\"name\":\"load\",\"returnType\":{\"packageName\":\"\",\"name\":\"Promise<RowWindow<T>>\",\"filePath\":null,\"qualifiedName\":\"/Promise<RowWindow<T>>\",\"typeParameters\":null,\"directParents\":null},\"isAsync\":true,\"isOptional\":false,\"isAbstract\":false,\"isStatic\":false,\"visibility\":\"public\",\"parameters\":[{\"name\":\"startIndex\",\"type\":{\"packageName\":\"\",\"name\":\"number\",\"filePath\":null,\"qualifiedName\":\"/number\",\"typeParameters\":null,\"directParents\":null}},{\"name\":\"endIndex\",\"type\":{\"packageName\":\"\",\"name\":\"number\",\"filePath\":null,\"qualifiedName\":\"/number\",\"typeParameters\":null,\"directParents\":null}},{\"name\":\"skipRowCount\",\"type\":{\"packageName\":\"\",\"name\":\"boolean\",\"filePath\":null,\"qualifiedName\":\"/boolean\",\"typeParameters\":null,\"directParents\":null}}]}],\"typeParameters\":[\"/T extends Record\"],\"directParentInterfaces\":[{\"packageName\":\"@proteinjs/ui\",\"name\":\"TableLoader\",\"filePath\":null,\"qualifiedName\":\"@proteinjs/ui/TableLoader\",\"properties\":[],\"methods\":[],\"typeParameters\":[\"/T\"],\"directParents\":[]}],\"directParentClasses\":[],\"sourceType\":2}},{\"v\":\"@proteinjs/ui/TableLoader\"},{\"v\":\"@proteinjs/db-ui/RecordFormCustomization\",\"value\":{\"packageName\":\"@proteinjs/db-ui\",\"name\":\"RecordFormCustomization\",\"filePath\":\"/home/runner/work/db/db/packages/ui/src/form/RecordFormCustomization.tsx\",\"qualifiedName\":\"@proteinjs/db-ui/RecordFormCustomization\",\"isAbstract\":false,\"isStatic\":false,\"visibility\":\"public\",\"properties\":[{\"name\":\"table\",\"type\":{\"packageName\":\"\",\"name\":\"Table<any>\",\"filePath\":null,\"qualifiedName\":\"/Table<any>\",\"typeParameters\":null,\"directParents\":null},\"isOptional\":false,\"isAbstract\":false,\"isStatic\":false,\"visibility\":\"public\"}],\"methods\":[{\"name\":\"getFormButtons\",\"returnType\":{\"packageName\":\"\",\"name\":\"FormButtons<any>\",\"filePath\":null,\"qualifiedName\":\"/FormButtons<any>\",\"typeParameters\":null,\"directParents\":null},\"isAsync\":false,\"isOptional\":false,\"isAbstract\":false,\"isStatic\":false,\"visibility\":\"public\",\"parameters\":[{\"name\":\"record\",\"type\":{\"packageName\":\"\",\"name\":\"any\",\"filePath\":null,\"qualifiedName\":\"/any\",\"typeParameters\":null,\"directParents\":null}},{\"name\":\"defaultFormButtons\",\"type\":{\"packageName\":\"\",\"name\":\"FormButtons<any>\",\"filePath\":null,\"qualifiedName\":\"/FormButtons<any>\",\"typeParameters\":null,\"directParents\":null}}]},{\"name\":\"getFieldLayout\",\"returnType\":{\"packageName\":\"\",\"name\":\"string[] | string[][]\",\"filePath\":null,\"qualifiedName\":\"/string[] | string[][]\",\"typeParameters\":null,\"directParents\":null},\"isAsync\":false,\"isOptional\":false,\"isAbstract\":false,\"isStatic\":false,\"visibility\":\"public\",\"parameters\":[{\"name\":\"record\",\"type\":{\"packageName\":\"\",\"name\":\"any\",\"filePath\":null,\"qualifiedName\":\"/any\",\"typeParameters\":null,\"directParents\":null}},{\"name\":\"defaultFieldLayout\",\"type\":{\"packageName\":\"\",\"name\":\"string[] | string[][]\",\"filePath\":null,\"qualifiedName\":\"/string[] | string[][]\",\"typeParameters\":null,\"directParents\":null}}]}],\"typeParameters\":[],\"directParentInterfaces\":[{\"packageName\":\"@proteinjs/reflection\",\"name\":\"Loadable\",\"filePath\":null,\"qualifiedName\":\"@proteinjs/reflection/Loadable\",\"properties\":[],\"methods\":[],\"typeParameters\":[],\"directParents\":[]}],\"directParentClasses\":[],\"sourceType\":2}},{\"v\":\"@proteinjs/reflection/Loadable\"},{\"v\":\"@proteinjs/db-ui/hashGeneratorPage\",\"value\":{\"packageName\":\"@proteinjs/db-ui\",\"name\":\"hashGeneratorPage\",\"filePath\":\"/home/runner/work/db/db/packages/ui/src/pages/HashGeneratorPage.tsx\",\"qualifiedName\":\"@proteinjs/db-ui/hashGeneratorPage\",\"type\":{\"packageName\":\"@proteinjs/ui\",\"name\":\"Page\",\"filePath\":null,\"qualifiedName\":\"@proteinjs/ui/Page\",\"typeParameters\":[],\"directParents\":[{\"packageName\":\"@proteinjs/ui\",\"name\":\"Page\",\"filePath\":null,\"qualifiedName\":\"@proteinjs/ui/Page\",\"typeParameters\":[],\"directParents\":null}]},\"isExported\":true,\"isConst\":true,\"sourceType\":0}},{\"v\":\"@proteinjs/ui/Page\"},{\"v\":\"@proteinjs/db-ui/recordFormPage\",\"value\":{\"packageName\":\"@proteinjs/db-ui\",\"name\":\"recordFormPage\",\"filePath\":\"/home/runner/work/db/db/packages/ui/src/pages/RecordFormPage.tsx\",\"qualifiedName\":\"@proteinjs/db-ui/recordFormPage\",\"type\":{\"packageName\":\"@proteinjs/ui\",\"name\":\"Page\",\"filePath\":null,\"qualifiedName\":\"@proteinjs/ui/Page\",\"typeParameters\":[],\"directParents\":[{\"packageName\":\"@proteinjs/ui\",\"name\":\"Page\",\"filePath\":null,\"qualifiedName\":\"@proteinjs/ui/Page\",\"typeParameters\":[],\"directParents\":null}]},\"isExported\":true,\"isConst\":true,\"sourceType\":0}},{\"v\":\"@proteinjs/db-ui/recordTablePage\",\"value\":{\"packageName\":\"@proteinjs/db-ui\",\"name\":\"recordTablePage\",\"filePath\":\"/home/runner/work/db/db/packages/ui/src/pages/RecordTablePage.tsx\",\"qualifiedName\":\"@proteinjs/db-ui/recordTablePage\",\"type\":{\"packageName\":\"@proteinjs/ui\",\"name\":\"Page\",\"filePath\":null,\"qualifiedName\":\"@proteinjs/ui/Page\",\"typeParameters\":[],\"directParents\":[{\"packageName\":\"@proteinjs/ui\",\"name\":\"Page\",\"filePath\":null,\"qualifiedName\":\"@proteinjs/ui/Page\",\"typeParameters\":[],\"directParents\":null}]},\"isExported\":true,\"isConst\":true,\"sourceType\":0}},{\"v\":\"@proteinjs/db-ui/tablesPage\",\"value\":{\"packageName\":\"@proteinjs/db-ui\",\"name\":\"tablesPage\",\"filePath\":\"/home/runner/work/db/db/packages/ui/src/pages/TablesPage.tsx\",\"qualifiedName\":\"@proteinjs/db-ui/tablesPage\",\"type\":{\"packageName\":\"@proteinjs/ui\",\"name\":\"Page\",\"filePath\":null,\"qualifiedName\":\"@proteinjs/ui/Page\",\"typeParameters\":[],\"directParents\":[{\"packageName\":\"@proteinjs/ui\",\"name\":\"Page\",\"filePath\":null,\"qualifiedName\":\"@proteinjs/ui/Page\",\"typeParameters\":[],\"directParents\":null}]},\"isExported\":true,\"isConst\":true,\"sourceType\":0}},{\"v\":\"@proteinjs/db-ui/uuidGeneratorPage\",\"value\":{\"packageName\":\"@proteinjs/db-ui\",\"name\":\"uuidGeneratorPage\",\"filePath\":\"/home/runner/work/db/db/packages/ui/src/pages/UuidGeneratorPage.tsx\",\"qualifiedName\":\"@proteinjs/db-ui/uuidGeneratorPage\",\"type\":{\"packageName\":\"@proteinjs/ui\",\"name\":\"Page\",\"filePath\":null,\"qualifiedName\":\"@proteinjs/ui/Page\",\"typeParameters\":[],\"directParents\":[{\"packageName\":\"@proteinjs/ui\",\"name\":\"Page\",\"filePath\":null,\"qualifiedName\":\"@proteinjs/ui/Page\",\"typeParameters\":[],\"directParents\":null}]},\"isExported\":true,\"isConst\":true,\"sourceType\":0}},{\"v\":\"@proteinjs/db-ui/SpecificTableProps\"},{\"v\":\"@proteinjs/db-ui/MigrationRecordFormCustomization\",\"value\":{\"packageName\":\"@proteinjs/db-ui\",\"name\":\"MigrationRecordFormCustomization\",\"filePath\":\"/home/runner/work/db/db/packages/ui/src/form/customizations/MigrationRecordFormCustomization.tsx\",\"qualifiedName\":\"@proteinjs/db-ui/MigrationRecordFormCustomization\",\"isAbstract\":false,\"isStatic\":false,\"visibility\":\"public\",\"properties\":[{\"name\":\"table\",\"type\":null,\"isOptional\":false,\"isAbstract\":false,\"isStatic\":false,\"visibility\":\"public\"}],\"methods\":[{\"name\":\"getFormButtons\",\"returnType\":{\"packageName\":\"\",\"name\":\"FormButtons<any>\",\"filePath\":null,\"qualifiedName\":\"/FormButtons<any>\",\"typeParameters\":null,\"directParents\":null},\"isAsync\":false,\"isOptional\":false,\"isAbstract\":false,\"isStatic\":false,\"visibility\":\"public\",\"parameters\":[{\"name\":\"migration\",\"type\":{\"packageName\":\"@proteinjs/db\",\"name\":\"Migration\",\"filePath\":null,\"qualifiedName\":\"@proteinjs/db/Migration\",\"typeParameters\":null,\"directParents\":null}},{\"name\":\"defaultFormButtons\",\"type\":{\"packageName\":\"\",\"name\":\"FormButtons<any>\",\"filePath\":null,\"qualifiedName\":\"/FormButtons<any>\",\"typeParameters\":null,\"directParents\":null}}]}],\"typeParameters\":[],\"directParentInterfaces\":[],\"directParentClasses\":[{\"packageName\":\"@proteinjs/db-ui\",\"name\":\"RecordFormCustomization\",\"filePath\":null,\"qualifiedName\":\"@proteinjs/db-ui/RecordFormCustomization\",\"isAbstract\":false,\"isStatic\":false,\"visibility\":\"public\",\"properties\":[],\"methods\":[],\"typeParameters\":[],\"directParentInterfaces\":[],\"directParentClasses\":[]}],\"sourceType\":2}}],\"edges\":[{\"v\":\"@proteinjs/db-ui/QueryCursorLoader\",\"w\":\"@proteinjs/ui/CursorLoader\",\"value\":\"implements interface\"},{\"v\":\"@proteinjs/db-ui/QueryTableLoader\",\"w\":\"@proteinjs/ui/TableLoader\",\"value\":\"implements interface\"},{\"v\":\"@proteinjs/db-ui/RecordFormCustomization\",\"w\":\"@proteinjs/reflection/Loadable\",\"value\":\"implements interface\"},{\"v\":\"@proteinjs/db-ui/hashGeneratorPage\",\"w\":\"@proteinjs/ui/Page\",\"value\":\"has type\"},{\"v\":\"@proteinjs/db-ui/recordFormPage\",\"w\":\"@proteinjs/ui/Page\",\"value\":\"has type\"},{\"v\":\"@proteinjs/db-ui/recordTablePage\",\"w\":\"@proteinjs/ui/Page\",\"value\":\"has type\"},{\"v\":\"@proteinjs/db-ui/tablesPage\",\"w\":\"@proteinjs/ui/Page\",\"value\":\"has type\"},{\"v\":\"@proteinjs/db-ui/uuidGeneratorPage\",\"w\":\"@proteinjs/ui/Page\",\"value\":\"has type\"},{\"v\":\"@proteinjs/db-ui/MigrationRecordFormCustomization\",\"w\":\"@proteinjs/db-ui/RecordFormCustomization\",\"value\":\"extends class\"}]}";
32
32
  /** Generate Source Links */
33
33
  var QueryCursorLoader_1 = require("../src/table/QueryCursorLoader");
34
34
  var QueryTableLoader_1 = require("../src/table/QueryTableLoader");
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../generated/index.ts"],"names":[],"mappings":";AAAA,oCAAoC;;;;;;;;;;;;;;;;AAEpC,0BAAwB;AACxB,2BAAyB;AACzB,+BAA6B;AAC7B,yBAAuB;AACvB,yBAAuB;AACvB,iCAA+B;AAC/B,yBAAuB;AACvB,2BAAyB;AACzB,kBAAgB;AAChB,iBAAe;AACf,kBAAgB;AAChB,gBAAc;AAGd,4BAA4B;AAE5B,IAAM,WAAW,GAAG,+zfAA+zf,CAAC;AAGp1f,4BAA4B;AAE5B,oEAAmE;AACnE,kEAAiE;AACjE,+EAA8E;AAC9E,oEAAmE;AACnE,8DAA6D;AAC7D,gEAA+D;AAC/D,sDAAqD;AACrD,oEAAmE;AACnE,gHAA+G;AAE/G,IAAM,WAAW,GAAG;IACnB,oCAAoC,EAAE,qCAAiB;IACvD,mCAAmC,EAAE,mCAAgB;IACrD,0CAA0C,EAAE,iDAAuB;IACnE,oCAAoC,EAAE,qCAAiB;IACvD,iCAAiC,EAAE,+BAAc;IACjD,kCAAkC,EAAE,iCAAe;IACnD,6BAA6B,EAAE,uBAAU;IACzC,oCAAoC,EAAE,qCAAiB;IACvD,mDAAmD,EAAE,mEAAgC;CACrF,CAAC;AAGF,kCAAkC;AAElC,oDAAyD;AACzD,6BAAgB,CAAC,KAAK,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC;AAGjD,2CAAyB"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../generated/index.ts"],"names":[],"mappings":";AAAA,oCAAoC;;;;;;;;;;;;;;;;AAEpC,0BAAwB;AACxB,2BAAyB;AACzB,+BAA6B;AAC7B,yBAAuB;AACvB,yBAAuB;AACvB,iCAA+B;AAC/B,yBAAuB;AACvB,2BAAyB;AACzB,kBAAgB;AAChB,iBAAe;AACf,kBAAgB;AAChB,gBAAc;AAGd,4BAA4B;AAE5B,IAAM,WAAW,GAAG,wrbAAwrb,CAAC;AAG7sb,4BAA4B;AAE5B,oEAAmE;AACnE,kEAAiE;AACjE,+EAA8E;AAC9E,oEAAmE;AACnE,8DAA6D;AAC7D,gEAA+D;AAC/D,sDAAqD;AACrD,oEAAmE;AACnE,gHAA+G;AAE/G,IAAM,WAAW,GAAG;IACnB,oCAAoC,EAAE,qCAAiB;IACvD,mCAAmC,EAAE,mCAAgB;IACrD,0CAA0C,EAAE,iDAAuB;IACnE,oCAAoC,EAAE,qCAAiB;IACvD,iCAAiC,EAAE,+BAAc;IACjD,kCAAkC,EAAE,iCAAe;IACnD,6BAA6B,EAAE,uBAAU;IACzC,oCAAoC,EAAE,qCAAiB;IACvD,mDAAmD,EAAE,mEAAgC;CACrF,CAAC;AAGF,kCAAkC;AAElC,oDAAyD;AACzD,6BAAgB,CAAC,KAAK,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC;AAGjD,2CAAyB"}
@@ -1,33 +1,29 @@
1
1
  import { QueryBuilder, QueryOptions, Record, SortCriteria, Table } from '@proteinjs/db';
2
2
  import { CursorLoader, CursorValue, CursorWindow, ReactQueryKeys } from '@proteinjs/ui';
3
3
  /**
4
- * `CursorLoader` over the same QueryBuilder machinery as `QueryTableLoader`: each window
5
- * applies `cursorField < cursor` (`>` for an ascending sort) plus `paginate(0, windowSize)`
6
- * on a fresh build of the base query, so rows created or removed while paging can never
7
- * shift the window frame. The cursor field and direction derive from the PRIMARY sort
8
- * criterion.
4
+ * `CursorLoader` over the db-owned cursor-window machinery (`CursorWindowPager`): each window
5
+ * carries a lexicographic continuation past the anchor row — the primary sort axes plus a
6
+ * unique `id` tiebreak — and `paginate(0, windowSize)` on a fresh build of the base query, so
7
+ * rows created or removed while paging can never shift the window frame, and ties on the sort
8
+ * axis can never skip or duplicate boundary rows. Cursors are the pager's anchors encoded as
9
+ * strings (house `Serializer`, so dates/moments survive react-query page params).
9
10
  *
10
11
  * `dataKey` is the table name — table-level invalidation (`useTableMutation`) reaches every
11
12
  * cursor query over it; `dataQueryKey` is the serialized query + sort.
12
13
  */
13
14
  export declare class QueryCursorLoader<T extends Record> implements CursorLoader<T> {
14
- private table;
15
- private createQuery;
16
- private queryOptions?;
17
15
  reactQueryKeys: ReactQueryKeys;
18
- private sort;
16
+ private pager;
19
17
  /**
20
18
  * @param table the table to load windows from
21
19
  * @param createQuery factory producing a FRESH conditions-only QueryBuilder per call —
22
- * builders are mutable, so every window builds its own to hang the cursor condition,
20
+ * builders are mutable, so every window builds its own to hang the cursor conditions,
23
21
  * sort, and pagination on
24
22
  * @param sort the sort criteria to apply to every window; the primary criterion is the
25
- * cursor axis
23
+ * cursor axis (the pager appends the `id` tiebreak)
26
24
  * @param queryOptions query options passed through to every window query
27
25
  */
28
- constructor(table: Table<T>, createQuery: () => QueryBuilder<T>, sort?: SortCriteria<T>[], queryOptions?: QueryOptions<T> | undefined);
26
+ constructor(table: Table<T>, createQuery: () => QueryBuilder<T>, sort?: SortCriteria<T>[], queryOptions?: QueryOptions<T>);
29
27
  loadWindow(cursor: CursorValue | null, windowSize: number): Promise<CursorWindow<T>>;
30
- private buildWindowQuery;
31
- private deriveNextCursor;
32
28
  }
33
29
  //# sourceMappingURL=QueryCursorLoader.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"QueryCursorLoader.d.ts","sourceRoot":"","sources":["../../../src/table/QueryCursorLoader.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,EAAE,YAAY,EAAE,KAAK,EAAS,MAAM,eAAe,CAAC;AAC/F,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAExF;;;;;;;;;GASG;AACH,qBAAa,iBAAiB,CAAC,CAAC,SAAS,MAAM,CAAE,YAAW,YAAY,CAAC,CAAC,CAAC;IAcvE,OAAO,CAAC,KAAK;IACb,OAAO,CAAC,WAAW;IAEnB,OAAO,CAAC,YAAY,CAAC;IAhBvB,cAAc,EAAE,cAAc,CAAC;IAC/B,OAAO,CAAC,IAAI,CAAoB;IAEhC;;;;;;;;OAQG;gBAEO,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,EACf,WAAW,EAAE,MAAM,YAAY,CAAC,CAAC,CAAC,EAC1C,IAAI,CAAC,EAAE,YAAY,CAAC,CAAC,CAAC,EAAE,EAChB,YAAY,CAAC,6BAAiB;IASlC,UAAU,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;IAM1F,OAAO,CAAC,gBAAgB;IAaxB,OAAO,CAAC,gBAAgB;CAWzB"}
1
+ {"version":3,"file":"QueryCursorLoader.d.ts","sourceRoot":"","sources":["../../../src/table/QueryCursorLoader.ts"],"names":[],"mappings":"AAAA,OAAO,EAAqB,YAAY,EAAE,YAAY,EAAE,MAAM,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AAC3G,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAExF;;;;;;;;;;GAUG;AACH,qBAAa,iBAAiB,CAAC,CAAC,SAAS,MAAM,CAAE,YAAW,YAAY,CAAC,CAAC,CAAC;IACzE,cAAc,EAAE,cAAc,CAAC;IAC/B,OAAO,CAAC,KAAK,CAAuB;IAEpC;;;;;;;;OAQG;gBAED,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,EACf,WAAW,EAAE,MAAM,YAAY,CAAC,CAAC,CAAC,EAClC,IAAI,CAAC,EAAE,YAAY,CAAC,CAAC,CAAC,EAAE,EACxB,YAAY,CAAC,EAAE,YAAY,CAAC,CAAC,CAAC;IAU1B,UAAU,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;CAQ3F"}
@@ -39,11 +39,12 @@ Object.defineProperty(exports, "__esModule", { value: true });
39
39
  exports.QueryCursorLoader = void 0;
40
40
  var db_1 = require("@proteinjs/db");
41
41
  /**
42
- * `CursorLoader` over the same QueryBuilder machinery as `QueryTableLoader`: each window
43
- * applies `cursorField < cursor` (`>` for an ascending sort) plus `paginate(0, windowSize)`
44
- * on a fresh build of the base query, so rows created or removed while paging can never
45
- * shift the window frame. The cursor field and direction derive from the PRIMARY sort
46
- * criterion.
42
+ * `CursorLoader` over the db-owned cursor-window machinery (`CursorWindowPager`): each window
43
+ * carries a lexicographic continuation past the anchor row — the primary sort axes plus a
44
+ * unique `id` tiebreak — and `paginate(0, windowSize)` on a fresh build of the base query, so
45
+ * rows created or removed while paging can never shift the window frame, and ties on the sort
46
+ * axis can never skip or duplicate boundary rows. Cursors are the pager's anchors encoded as
47
+ * strings (house `Serializer`, so dates/moments survive react-query page params).
47
48
  *
48
49
  * `dataKey` is the table name — table-level invalidation (`useTableMutation`) reaches every
49
50
  * cursor query over it; `dataQueryKey` is the serialized query + sort.
@@ -52,60 +53,38 @@ var QueryCursorLoader = /** @class */ (function () {
52
53
  /**
53
54
  * @param table the table to load windows from
54
55
  * @param createQuery factory producing a FRESH conditions-only QueryBuilder per call —
55
- * builders are mutable, so every window builds its own to hang the cursor condition,
56
+ * builders are mutable, so every window builds its own to hang the cursor conditions,
56
57
  * sort, and pagination on
57
58
  * @param sort the sort criteria to apply to every window; the primary criterion is the
58
- * cursor axis
59
+ * cursor axis (the pager appends the `id` tiebreak)
59
60
  * @param queryOptions query options passed through to every window query
60
61
  */
61
62
  function QueryCursorLoader(table, createQuery, sort, queryOptions) {
62
- this.table = table;
63
- this.createQuery = createQuery;
64
- this.queryOptions = queryOptions;
65
- this.sort = sort ? sort : [{ field: 'created', desc: true }];
63
+ var effectiveSort = sort ? sort : [{ field: 'created', desc: true }];
64
+ this.pager = new db_1.CursorWindowPager(table, createQuery, effectiveSort, { queryOptions: queryOptions });
66
65
  this.reactQueryKeys = {
67
- dataKey: this.table.name,
68
- dataQueryKey: JSON.stringify({ query: this.createQuery(), sort: this.sort }),
66
+ dataKey: table.name,
67
+ dataQueryKey: JSON.stringify({ query: createQuery(), sort: effectiveSort }),
69
68
  };
70
69
  }
71
70
  QueryCursorLoader.prototype.loadWindow = function (cursor, windowSize) {
72
71
  return __awaiter(this, void 0, void 0, function () {
73
- var qb, rows;
72
+ var anchor, window;
74
73
  return __generator(this, function (_a) {
75
74
  switch (_a.label) {
76
75
  case 0:
77
- qb = this.buildWindowQuery(cursor, windowSize);
78
- return [4 /*yield*/, (0, db_1.getDb)().query(this.table, qb, this.queryOptions)];
76
+ anchor = cursor === null ? null : db_1.CursorWindowPager.decodeAnchor(cursor);
77
+ return [4 /*yield*/, this.pager.loadWindow(anchor, windowSize)];
79
78
  case 1:
80
- rows = _a.sent();
81
- return [2 /*return*/, { rows: rows, nextCursor: this.deriveNextCursor(rows, windowSize) }];
79
+ window = _a.sent();
80
+ return [2 /*return*/, {
81
+ rows: window.rows,
82
+ nextCursor: window.nextAnchor ? db_1.CursorWindowPager.encodeAnchor(window.nextAnchor) : null,
83
+ }];
82
84
  }
83
85
  });
84
86
  });
85
87
  };
86
- QueryCursorLoader.prototype.buildWindowQuery = function (cursor, windowSize) {
87
- var qb = this.createQuery();
88
- var primarySort = this.sort[0];
89
- if (cursor !== null) {
90
- qb.condition({
91
- field: primarySort.field,
92
- operator: primarySort.desc ? '<' : '>',
93
- value: cursor,
94
- });
95
- }
96
- return qb.sort(this.sort).paginate({ start: 0, end: windowSize });
97
- };
98
- QueryCursorLoader.prototype.deriveNextCursor = function (rows, windowSize) {
99
- // A short window means the data set is exhausted; a full one implies more may exist —
100
- // the next (short or empty) window settles it.
101
- if (rows.length < windowSize) {
102
- return null;
103
- }
104
- // NULL cursor-field rows collect at the tail under the sort, and a null tail can't
105
- // anchor a cursor: the data set's dated rows are exhausted, so paging stops here.
106
- var tail = rows[rows.length - 1][this.sort[0].field];
107
- return tail == null ? null : tail;
108
- };
109
88
  return QueryCursorLoader;
110
89
  }());
111
90
  exports.QueryCursorLoader = QueryCursorLoader;
@@ -1 +1 @@
1
- {"version":3,"file":"QueryCursorLoader.js","sourceRoot":"","sources":["../../../src/table/QueryCursorLoader.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,oCAA+F;AAG/F;;;;;;;;;GASG;AACH;IAIE;;;;;;;;OAQG;IACH,2BACU,KAAe,EACf,WAAkC,EAC1C,IAAwB,EAChB,YAA8B;QAH9B,UAAK,GAAL,KAAK,CAAU;QACf,gBAAW,GAAX,WAAW,CAAuB;QAElC,iBAAY,GAAZ,YAAY,CAAkB;QAEtC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,SAAoB,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;QACxE,IAAI,CAAC,cAAc,GAAG;YACpB,OAAO,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI;YACxB,YAAY,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,WAAW,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC;SAC7E,CAAC;IACJ,CAAC;IAEK,sCAAU,GAAhB,UAAiB,MAA0B,EAAE,UAAkB;;;;;;wBACvD,EAAE,GAAG,IAAI,CAAC,gBAAgB,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;wBACxC,qBAAM,IAAA,UAAK,GAAE,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,EAAE,EAAE,EAAE,IAAI,CAAC,YAAY,CAAC,EAAA;;wBAA7D,IAAI,GAAG,SAAsD;wBACnE,sBAAO,EAAE,IAAI,MAAA,EAAE,UAAU,EAAE,IAAI,CAAC,gBAAgB,CAAC,IAAI,EAAE,UAAU,CAAC,EAAE,EAAC;;;;KACtE;IAEO,4CAAgB,GAAxB,UAAyB,MAA0B,EAAE,UAAkB;QACrE,IAAM,EAAE,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC;QAC9B,IAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACjC,IAAI,MAAM,KAAK,IAAI,EAAE;YACnB,EAAE,CAAC,SAAS,CAAC;gBACX,KAAK,EAAE,WAAW,CAAC,KAAK;gBACxB,QAAQ,EAAE,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG;gBACtC,KAAK,EAAE,MAAoB;aAC5B,CAAC,CAAC;SACJ;QACD,OAAO,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG,EAAE,UAAU,EAAE,CAAC,CAAC;IACpE,CAAC;IAEO,4CAAgB,GAAxB,UAAyB,IAAS,EAAE,UAAkB;QACpD,sFAAsF;QACtF,+CAA+C;QAC/C,IAAI,IAAI,CAAC,MAAM,GAAG,UAAU,EAAE;YAC5B,OAAO,IAAI,CAAC;SACb;QACD,mFAAmF;QACnF,kFAAkF;QAClF,IAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;QACvD,OAAO,IAAI,IAAI,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAE,IAA+B,CAAC;IAChE,CAAC;IACH,wBAAC;AAAD,CAAC,AAxDD,IAwDC;AAxDY,8CAAiB"}
1
+ {"version":3,"file":"QueryCursorLoader.js","sourceRoot":"","sources":["../../../src/table/QueryCursorLoader.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,oCAA2G;AAG3G;;;;;;;;;;GAUG;AACH;IAIE;;;;;;;;OAQG;IACH,2BACE,KAAe,EACf,WAAkC,EAClC,IAAwB,EACxB,YAA8B;QAE9B,IAAM,aAAa,GAAG,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,SAAoB,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;QAClF,IAAI,CAAC,KAAK,GAAG,IAAI,sBAAiB,CAAI,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,EAAE,YAAY,cAAA,EAAE,CAAC,CAAC;QAC3F,IAAI,CAAC,cAAc,GAAG;YACpB,OAAO,EAAE,KAAK,CAAC,IAAI;YACnB,YAAY,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,EAAE,IAAI,EAAE,aAAa,EAAE,CAAC;SAC5E,CAAC;IACJ,CAAC;IAEK,sCAAU,GAAhB,UAAiB,MAA0B,EAAE,UAAkB;;;;;;wBACvD,MAAM,GAAG,MAAM,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,sBAAiB,CAAC,YAAY,CAAC,MAAgB,CAAC,CAAC;wBAC1E,qBAAM,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,MAAM,EAAE,UAAU,CAAC,EAAA;;wBAAxD,MAAM,GAAG,SAA+C;wBAC9D,sBAAO;gCACL,IAAI,EAAE,MAAM,CAAC,IAAI;gCACjB,UAAU,EAAE,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,sBAAiB,CAAC,YAAY,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,IAAI;6BACzF,EAAC;;;;KACH;IACH,wBAAC;AAAD,CAAC,AAnCD,IAmCC;AAnCY,8CAAiB"}
@@ -1,2 +1,2 @@
1
- export {};
1
+ import '../generated/index';
2
2
  //# sourceMappingURL=QueryCursorLoader.test.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"QueryCursorLoader.test.d.ts","sourceRoot":"","sources":["../../test/QueryCursorLoader.test.ts"],"names":[],"mappings":""}
1
+ {"version":3,"file":"QueryCursorLoader.test.d.ts","sourceRoot":"","sources":["../../test/QueryCursorLoader.test.ts"],"names":[],"mappings":"AAcA,OAAO,oBAAoB,CAAC"}
@@ -1,15 +1,4 @@
1
1
  "use strict";
2
- var __assign = (this && this.__assign) || function () {
3
- __assign = Object.assign || function(t) {
4
- for (var s, i = 1, n = arguments.length; i < n; i++) {
5
- s = arguments[i];
6
- for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
7
- t[p] = s[p];
8
- }
9
- return t;
10
- };
11
- return __assign.apply(this, arguments);
12
- };
13
2
  var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
14
3
  function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
15
4
  return new (P || (P = Promise))(function (resolve, reject) {
@@ -51,30 +40,39 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
51
40
  };
52
41
  Object.defineProperty(exports, "__esModule", { value: true });
53
42
  /**
54
- * QueryCursorLoader (src/table/QueryCursorLoader.ts) at the pure level: react-query key
55
- * derivation, cursor threading into the window query, and next-cursor derivation — including
56
- * the null-tail exhaustion rule (NULL cursor-field rows collect at the tail under the sort;
57
- * a null tail can't anchor a cursor, so paging stops). Window queries are inspected through
58
- * the QueryBuilder graph; loadWindow's db round-trip is exercised with a stubbed getDb.
43
+ * QueryCursorLoader (src/table/QueryCursorLoader.ts) as the thin `CursorLoader` adapter over
44
+ * the db-owned `CursorWindowPager`: react-query key derivation, string-cursor encode/decode
45
+ * threading into the pager's lexicographic window queries (primary axis + `id` tiebreak), and
46
+ * exhaustion (short window, and the null-tail rule: NULL sort-value rows collect at the tail
47
+ * under the sort; a null can't anchor a continuation, so paging stops). The db round trip is
48
+ * injected through the pager's `options.db` seam via the house internals-cast pattern — no
49
+ * module mocking.
59
50
  */
60
51
  var moment_1 = __importDefault(require("moment"));
61
- var stagedRows = { rows: [] };
62
- var queries = [];
52
+ var db_1 = require("@proteinjs/db");
53
+ var QueryCursorLoader_1 = require("../src/table/QueryCursorLoader");
54
+ // Load the reflection source graph so the house Serializer's custom serializers (moments)
55
+ // are registered — cursor encode/decode fidelity rides them.
56
+ require("../generated/index");
57
+ var table = { name: 'test_content' };
58
+ var sort = [{ field: 'lastActivityAt', desc: true }];
59
+ var createQuery = function () { return new db_1.QueryBuilder(table.name); };
60
+ var staged = { rows: [] };
61
+ var executed = [];
63
62
  var stubDb = {
64
63
  query: function (_table, qb) { return __awaiter(void 0, void 0, void 0, function () {
65
64
  return __generator(this, function (_a) {
66
- queries.push(qb);
67
- return [2 /*return*/, stagedRows.rows];
65
+ executed.push(qb);
66
+ return [2 /*return*/, staged.rows];
68
67
  });
69
68
  }); },
70
69
  };
71
- jest.mock('@proteinjs/db', function () { return (__assign(__assign({}, jest.requireActual('@proteinjs/db')), { getDb: function () { return stubDb; } })); });
72
- var db_1 = require("@proteinjs/db");
73
- var QueryCursorLoader_1 = require("../src/table/QueryCursorLoader");
74
- var table = { name: 'test_content' };
75
- var sort = [{ field: 'lastActivityAt', desc: true }];
76
- var createQuery = function () { return new db_1.QueryBuilder(table.name); };
77
- var internals = function (loader) { return loader; };
70
+ var loaderWithStubDb = function (sortCriteria, query) {
71
+ if (query === void 0) { query = createQuery; }
72
+ var loader = new QueryCursorLoader_1.QueryCursorLoader(table, query, sortCriteria);
73
+ loader.pager.options = { db: stubDb };
74
+ return loader;
75
+ };
78
76
  var graphNodes = function (qb) { return qb.graph.nodes().map(function (id) { return qb.graph.node(id); }); };
79
77
  var conditionNodes = function (qb) { return graphNodes(qb).filter(function (node) { return (node === null || node === void 0 ? void 0 : node.type) === 'CONDITION'; }); };
80
78
  var paginationNode = function (qb) { return graphNodes(qb).find(function (node) { return (node === null || node === void 0 ? void 0 : node.type) === 'PAGINATION'; }); };
@@ -83,11 +81,11 @@ var dated = function (id, at) { return ({
83
81
  id: id,
84
82
  created: (0, moment_1.default)(),
85
83
  updated: (0, moment_1.default)(),
86
- lastActivityAt: at ? (0, moment_1.default)(at) : undefined,
84
+ lastActivityAt: at ? (0, moment_1.default)(at) : null,
87
85
  }); };
88
86
  beforeEach(function () {
89
- stagedRows.rows = [];
90
- queries.length = 0;
87
+ staged.rows = [];
88
+ executed.length = 0;
91
89
  });
92
90
  describe('reactQueryKeys', function () {
93
91
  it('names the data set after the table and the query after its serialized query + sort', function () {
@@ -103,90 +101,119 @@ describe('reactQueryKeys', function () {
103
101
  expect(resorted.reactQueryKeys.dataQueryKey).not.toBe(loader.reactQueryKeys.dataQueryKey);
104
102
  });
105
103
  });
106
- describe('buildWindowQuery — cursor threading', function () {
107
- it('the first window carries no cursor condition, only the sort and the window pagination', function () {
108
- var loader = new QueryCursorLoader_1.QueryCursorLoader(table, createQuery, sort);
109
- var qb = internals(loader).buildWindowQuery(null, 30);
110
- expect(conditionNodes(qb)).toHaveLength(0);
111
- expect(paginationNode(qb)).toMatchObject({ start: 0, end: 30 });
112
- expect(sortNodes(qb)).toHaveLength(1);
113
- expect(sortNodes(qb)[0].criteria).toMatchObject({ field: 'lastActivityAt', desc: true });
114
- });
115
- it('a cursor becomes `field < cursor` on the primary sort field for a descending sort', function () {
116
- var loader = new QueryCursorLoader_1.QueryCursorLoader(table, createQuery, sort);
117
- var cursor = (0, moment_1.default)('2026-08-10T12:00:00.000Z');
118
- var qb = internals(loader).buildWindowQuery(cursor, 30);
119
- var conditions = conditionNodes(qb);
120
- expect(conditions).toHaveLength(1);
121
- expect(conditions[0]).toMatchObject({ field: 'lastActivityAt', operator: '<' });
122
- expect(conditions[0].value).toBe(cursor);
123
- expect(paginationNode(qb)).toMatchObject({ start: 0, end: 30 });
124
- });
125
- it('an ascending primary sort flips the cursor operator to `>`', function () {
126
- var loader = new QueryCursorLoader_1.QueryCursorLoader(table, createQuery, [{ field: 'lastActivityAt', desc: false }]);
127
- var qb = internals(loader).buildWindowQuery(5, 10);
128
- expect(conditionNodes(qb)[0]).toMatchObject({ field: 'lastActivityAt', operator: '>', value: 5 });
129
- });
130
- it('every window builds on a FRESH query — cursor conditions never accumulate across windows', function () {
131
- var loader = new QueryCursorLoader_1.QueryCursorLoader(table, createQuery, sort);
132
- internals(loader).buildWindowQuery((0, moment_1.default)('2026-08-10T12:00:00.000Z'), 30);
133
- var second = internals(loader).buildWindowQuery((0, moment_1.default)('2026-08-01T12:00:00.000Z'), 30);
134
- expect(conditionNodes(second)).toHaveLength(1);
135
- });
136
- });
137
- describe('deriveNextCursor — exhaustion', function () {
138
- var loader = new QueryCursorLoader_1.QueryCursorLoader(table, createQuery, sort);
139
- it('a short window is exhausted', function () {
140
- expect(internals(loader).deriveNextCursor([dated('a', '2026-08-10T12:00:00.000Z')], 3)).toBeNull();
141
- expect(internals(loader).deriveNextCursor([], 3)).toBeNull();
142
- });
143
- it('a full window anchors the next cursor at its tail', function () {
144
- var rows = [
145
- dated('a', '2026-08-10T12:00:00.000Z'),
146
- dated('b', '2026-08-09T12:00:00.000Z'),
147
- dated('c', '2026-08-08T12:00:00.000Z'),
148
- ];
149
- var next = internals(loader).deriveNextCursor(rows, 3);
150
- expect(next).toBe(rows[2].lastActivityAt);
151
- });
152
- it('a full window with a NULL cursor-field tail is exhausted (null rows collect at the tail; no anchor)', function () {
153
- var rows = [dated('a', '2026-08-10T12:00:00.000Z'), dated('b', '2026-08-09T12:00:00.000Z'), dated('c', null)];
154
- expect(internals(loader).deriveNextCursor(rows, 3)).toBeNull();
155
- });
156
- });
157
- describe('loadWindow', function () {
158
- it('returns the queried rows with the derived next cursor', function () { return __awaiter(void 0, void 0, void 0, function () {
159
- var loader, rows, window;
104
+ describe('loadWindow — cursor threading', function () {
105
+ it('the first window carries no cursor conditions the sort (with id tiebreak) and the window pagination', function () { return __awaiter(void 0, void 0, void 0, function () {
106
+ var loader, qb;
160
107
  return __generator(this, function (_a) {
161
108
  switch (_a.label) {
162
109
  case 0:
163
- loader = new QueryCursorLoader_1.QueryCursorLoader(table, createQuery, sort);
110
+ loader = loaderWithStubDb(sort);
111
+ staged.rows = [];
112
+ return [4 /*yield*/, loader.loadWindow(null, 30)];
113
+ case 1:
114
+ _a.sent();
115
+ expect(executed).toHaveLength(1);
116
+ qb = executed[0];
117
+ expect(conditionNodes(qb)).toHaveLength(0);
118
+ expect(paginationNode(qb)).toMatchObject({ start: 0, end: 30 });
119
+ expect(sortNodes(qb).map(function (node) { return node.criteria; })).toEqual([
120
+ { field: 'lastActivityAt', desc: true },
121
+ { field: 'id', desc: false },
122
+ ]);
123
+ return [2 /*return*/];
124
+ }
125
+ });
126
+ }); });
127
+ it('a full window returns a string cursor; feeding it back threads the lexicographic continuation', function () { return __awaiter(void 0, void 0, void 0, function () {
128
+ var loader, rows, first, qb, conditions, past;
129
+ return __generator(this, function (_a) {
130
+ switch (_a.label) {
131
+ case 0:
132
+ loader = loaderWithStubDb(sort);
164
133
  rows = [
165
134
  dated('a', '2026-08-10T12:00:00.000Z'),
166
135
  dated('b', '2026-08-09T12:00:00.000Z'),
167
136
  dated('c', '2026-08-08T12:00:00.000Z'),
168
137
  ];
169
- stagedRows.rows = rows;
138
+ staged.rows = rows;
139
+ return [4 /*yield*/, loader.loadWindow(null, 3)];
140
+ case 1:
141
+ first = _a.sent();
142
+ expect(first.rows).toBe(rows);
143
+ expect(typeof first.nextCursor).toBe('string');
144
+ staged.rows = [];
145
+ return [4 /*yield*/, loader.loadWindow(first.nextCursor, 3)];
146
+ case 2:
147
+ _a.sent();
148
+ qb = executed[1];
149
+ conditions = conditionNodes(qb);
150
+ expect(conditions).toHaveLength(3);
151
+ past = conditions.find(function (node) { return node.operator === '<'; });
152
+ expect(past).toMatchObject({ field: 'lastActivityAt' });
153
+ expect(moment_1.default.isMoment(past.value)).toBe(true);
154
+ expect(past.value.toISOString()).toBe(rows[2].lastActivityAt.toISOString());
155
+ expect(conditions.find(function (node) { return node.operator === '='; })).toMatchObject({ field: 'lastActivityAt' });
156
+ expect(conditions.find(function (node) { return node.operator === '>'; })).toMatchObject({ field: 'id', value: 'c' });
157
+ return [2 /*return*/];
158
+ }
159
+ });
160
+ }); });
161
+ it('every window builds on a FRESH query — cursor conditions never accumulate across windows', function () { return __awaiter(void 0, void 0, void 0, function () {
162
+ var loader, first, second;
163
+ return __generator(this, function (_a) {
164
+ switch (_a.label) {
165
+ case 0:
166
+ loader = loaderWithStubDb(sort);
167
+ staged.rows = [
168
+ dated('a', '2026-08-10T12:00:00.000Z'),
169
+ dated('b', '2026-08-09T12:00:00.000Z'),
170
+ dated('c', '2026-08-08T12:00:00.000Z'),
171
+ ];
172
+ return [4 /*yield*/, loader.loadWindow(null, 3)];
173
+ case 1:
174
+ first = _a.sent();
175
+ staged.rows = [
176
+ dated('d', '2026-08-07T12:00:00.000Z'),
177
+ dated('e', '2026-08-06T12:00:00.000Z'),
178
+ dated('f', '2026-08-05T12:00:00.000Z'),
179
+ ];
180
+ return [4 /*yield*/, loader.loadWindow(first.nextCursor, 3)];
181
+ case 2:
182
+ second = _a.sent();
183
+ staged.rows = [];
184
+ return [4 /*yield*/, loader.loadWindow(second.nextCursor, 3)];
185
+ case 3:
186
+ _a.sent();
187
+ expect(conditionNodes(executed[2])).toHaveLength(3);
188
+ return [2 /*return*/];
189
+ }
190
+ });
191
+ }); });
192
+ });
193
+ describe('loadWindow — exhaustion', function () {
194
+ it('a short window is exhausted (null cursor)', function () { return __awaiter(void 0, void 0, void 0, function () {
195
+ var loader, window;
196
+ return __generator(this, function (_a) {
197
+ switch (_a.label) {
198
+ case 0:
199
+ loader = loaderWithStubDb(sort);
200
+ staged.rows = [dated('a', '2026-08-10T12:00:00.000Z')];
170
201
  return [4 /*yield*/, loader.loadWindow(null, 3)];
171
202
  case 1:
172
203
  window = _a.sent();
173
- expect(window.rows).toBe(rows);
174
- expect(window.nextCursor).toBe(rows[2].lastActivityAt);
175
- // The executed query is the built window query: pagination sized to the window.
176
- expect(queries).toHaveLength(1);
177
- expect(paginationNode(queries[0])).toMatchObject({ start: 0, end: 3 });
204
+ expect(window.nextCursor).toBeNull();
178
205
  return [2 /*return*/];
179
206
  }
180
207
  });
181
208
  }); });
182
- it('returns an exhausted window (null cursor) when the query comes back short', function () { return __awaiter(void 0, void 0, void 0, function () {
209
+ it('a full window with a NULL sort-value tail is exhausted (null rows collect at the tail; no anchor)', function () { return __awaiter(void 0, void 0, void 0, function () {
183
210
  var loader, window;
184
211
  return __generator(this, function (_a) {
185
212
  switch (_a.label) {
186
213
  case 0:
187
- loader = new QueryCursorLoader_1.QueryCursorLoader(table, createQuery, sort);
188
- stagedRows.rows = [dated('a', '2026-08-10T12:00:00.000Z')];
189
- return [4 /*yield*/, loader.loadWindow((0, moment_1.default)('2026-08-11T12:00:00.000Z'), 3)];
214
+ loader = loaderWithStubDb(sort);
215
+ staged.rows = [dated('a', '2026-08-10T12:00:00.000Z'), dated('b', '2026-08-09T12:00:00.000Z'), dated('c', null)];
216
+ return [4 /*yield*/, loader.loadWindow(null, 3)];
190
217
  case 1:
191
218
  window = _a.sent();
192
219
  expect(window.nextCursor).toBeNull();
@@ -1 +1 @@
1
- {"version":3,"file":"QueryCursorLoader.test.js","sourceRoot":"","sources":["../../test/QueryCursorLoader.test.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"QueryCursorLoader.test.js","sourceRoot":"","sources":["../../test/QueryCursorLoader.test.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;;;;;;;;GAQG;AACH,kDAA4B;AAC5B,oCAAsE;AACtE,oEAAmE;AACnE,0FAA0F;AAC1F,6DAA6D;AAC7D,8BAA4B;AAU5B,IAAM,KAAK,GAAG,EAAE,IAAI,EAAE,cAAc,EAAgB,CAAC;AACrD,IAAM,IAAI,GAAwB,CAAC,EAAE,KAAK,EAAE,gBAAgB,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;AAC5E,IAAM,WAAW,GAAG,cAAM,OAAA,IAAI,iBAAY,CAAM,KAAK,CAAC,IAAI,CAAC,EAAjC,CAAiC,CAAC;AAE5D,IAAM,MAAM,GAAoB,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC;AAC7C,IAAM,QAAQ,GAAwB,EAAE,CAAC;AACzC,IAAM,MAAM,GAAG;IACb,KAAK,EAAE,UAAO,MAAe,EAAE,EAAqB;;YAClD,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YAClB,sBAAO,MAAM,CAAC,IAAI,EAAC;;SACpB;CACe,CAAC;AAKnB,IAAM,gBAAgB,GAAG,UAAC,YAAkC,EAAE,KAA4C;IAA5C,sBAAA,EAAA,mBAA4C;IACxG,IAAM,MAAM,GAAG,IAAI,qCAAiB,CAAM,KAAK,EAAE,KAAK,EAAE,YAAY,CAAC,CAAC;IACrE,MAAqC,CAAC,KAAK,CAAC,OAAO,GAAG,EAAE,EAAE,EAAE,MAAM,EAAE,CAAC;IACtE,OAAO,MAAM,CAAC;AAChB,CAAC,CAAC;AAEF,IAAM,UAAU,GAAG,UAAC,EAAqB,IAAY,OAAA,EAAE,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC,GAAG,CAAC,UAAC,EAAU,IAAK,OAAA,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,EAAjB,CAAiB,CAAC,EAAvD,CAAuD,CAAC;AAC7G,IAAM,cAAc,GAAG,UAAC,EAAqB,IAAK,OAAA,UAAU,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,UAAC,IAAI,IAAK,OAAA,CAAA,IAAI,aAAJ,IAAI,uBAAJ,IAAI,CAAE,IAAI,MAAK,WAAW,EAA1B,CAA0B,CAAC,EAA3D,CAA2D,CAAC;AAC9G,IAAM,cAAc,GAAG,UAAC,EAAqB,IAAK,OAAA,UAAU,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,UAAC,IAAI,IAAK,OAAA,CAAA,IAAI,aAAJ,IAAI,uBAAJ,IAAI,CAAE,IAAI,MAAK,YAAY,EAA3B,CAA2B,CAAC,EAA1D,CAA0D,CAAC;AAC7G,IAAM,SAAS,GAAG,UAAC,EAAqB,IAAK,OAAA,UAAU,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,UAAC,IAAI,IAAK,OAAA,CAAA,IAAI,aAAJ,IAAI,uBAAJ,IAAI,CAAE,IAAI,MAAK,MAAM,EAArB,CAAqB,CAAC,EAAtD,CAAsD,CAAC;AAEpG,IAAM,KAAK,GAAG,UAAC,EAAU,EAAE,EAAiB,IAAU,OAAA,CAAC;IACrD,EAAE,IAAA;IACF,OAAO,EAAE,IAAA,gBAAM,GAAE;IACjB,OAAO,EAAE,IAAA,gBAAM,GAAE;IACjB,cAAc,EAAE,EAAE,CAAC,CAAC,CAAC,IAAA,gBAAM,EAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI;CACvC,CAAC,EALoD,CAKpD,CAAC;AAEH,UAAU,CAAC;IACT,MAAM,CAAC,IAAI,GAAG,EAAE,CAAC;IACjB,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC;AACtB,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,gBAAgB,EAAE;IACzB,EAAE,CAAC,oFAAoF,EAAE;QACvF,IAAM,MAAM,GAAG,IAAI,qCAAiB,CAAM,KAAK,EAAE,WAAW,EAAE,IAAI,CAAC,CAAC;QACpE,MAAM,CAAC,MAAM,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;QAC3D,8EAA8E;QAC9E,IAAM,IAAI,GAAG,IAAI,qCAAiB,CAAM,KAAK,EAAE,WAAW,EAAE,IAAI,CAAC,CAAC;QAClE,MAAM,CAAC,IAAI,CAAC,cAAc,CAAC,YAAY,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,cAAc,CAAC,YAAY,CAAC,CAAC;QAClF,8DAA8D;QAC9D,IAAM,QAAQ,GAAG,IAAI,qCAAiB,CACpC,KAAK,EACL,cAAM,OAAA,WAAW,EAAE,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,EAA1E,CAA0E,EAChF,IAAI,CACL,CAAC;QACF,MAAM,CAAC,QAAQ,CAAC,cAAc,CAAC,YAAY,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,cAAc,CAAC,YAAY,CAAC,CAAC;QAC1F,IAAM,QAAQ,GAAG,IAAI,qCAAiB,CAAM,KAAK,EAAE,WAAW,EAAE,CAAC,EAAE,KAAK,EAAE,gBAAgB,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC;QAC5G,MAAM,CAAC,QAAQ,CAAC,cAAc,CAAC,YAAY,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,cAAc,CAAC,YAAY,CAAC,CAAC;IAC5F,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,+BAA+B,EAAE;IACxC,EAAE,CAAC,uGAAuG,EAAE;;;;;oBACpG,MAAM,GAAG,gBAAgB,CAAC,IAAI,CAAC,CAAC;oBACtC,MAAM,CAAC,IAAI,GAAG,EAAE,CAAC;oBACjB,qBAAM,MAAM,CAAC,UAAU,CAAC,IAAI,EAAE,EAAE,CAAC,EAAA;;oBAAjC,SAAiC,CAAC;oBAClC,MAAM,CAAC,QAAQ,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;oBAC3B,EAAE,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;oBACvB,MAAM,CAAC,cAAc,CAAC,EAAE,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;oBAC3C,MAAM,CAAC,cAAc,CAAC,EAAE,CAAC,CAAC,CAAC,aAAa,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG,EAAE,EAAE,EAAE,CAAC,CAAC;oBAChE,MAAM,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,UAAC,IAAI,IAAK,OAAA,IAAI,CAAC,QAAQ,EAAb,CAAa,CAAC,CAAC,CAAC,OAAO,CAAC;wBACzD,EAAE,KAAK,EAAE,gBAAgB,EAAE,IAAI,EAAE,IAAI,EAAE;wBACvC,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE;qBAC7B,CAAC,CAAC;;;;SACJ,CAAC,CAAC;IAEH,EAAE,CAAC,+FAA+F,EAAE;;;;;oBAC5F,MAAM,GAAG,gBAAgB,CAAC,IAAI,CAAC,CAAC;oBAChC,IAAI,GAAG;wBACX,KAAK,CAAC,GAAG,EAAE,0BAA0B,CAAC;wBACtC,KAAK,CAAC,GAAG,EAAE,0BAA0B,CAAC;wBACtC,KAAK,CAAC,GAAG,EAAE,0BAA0B,CAAC;qBACvC,CAAC;oBACF,MAAM,CAAC,IAAI,GAAG,IAAI,CAAC;oBACL,qBAAM,MAAM,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC,CAAC,EAAA;;oBAAxC,KAAK,GAAG,SAAgC;oBAC9C,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;oBAC9B,MAAM,CAAC,OAAO,KAAK,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;oBAE/C,MAAM,CAAC,IAAI,GAAG,EAAE,CAAC;oBACjB,qBAAM,MAAM,CAAC,UAAU,CAAC,KAAK,CAAC,UAAU,EAAE,CAAC,CAAC,EAAA;;oBAA5C,SAA4C,CAAC;oBACvC,EAAE,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;oBAGjB,UAAU,GAAG,cAAc,CAAC,EAAE,CAAC,CAAC;oBACtC,MAAM,CAAC,UAAU,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;oBAC7B,IAAI,GAAG,UAAU,CAAC,IAAI,CAAC,UAAC,IAAI,IAAK,OAAA,IAAI,CAAC,QAAQ,KAAK,GAAG,EAArB,CAAqB,CAAC,CAAC;oBAC9D,MAAM,CAAC,IAAI,CAAC,CAAC,aAAa,CAAC,EAAE,KAAK,EAAE,gBAAgB,EAAE,CAAC,CAAC;oBACxD,MAAM,CAAC,gBAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;oBAC/C,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,cAAe,CAAC,WAAW,EAAE,CAAC,CAAC;oBAC7E,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,UAAC,IAAI,IAAK,OAAA,IAAI,CAAC,QAAQ,KAAK,GAAG,EAArB,CAAqB,CAAC,CAAC,CAAC,aAAa,CAAC,EAAE,KAAK,EAAE,gBAAgB,EAAE,CAAC,CAAC;oBACpG,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,UAAC,IAAI,IAAK,OAAA,IAAI,CAAC,QAAQ,KAAK,GAAG,EAArB,CAAqB,CAAC,CAAC,CAAC,aAAa,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC;;;;SACrG,CAAC,CAAC;IAEH,EAAE,CAAC,0FAA0F,EAAE;;;;;oBACvF,MAAM,GAAG,gBAAgB,CAAC,IAAI,CAAC,CAAC;oBACtC,MAAM,CAAC,IAAI,GAAG;wBACZ,KAAK,CAAC,GAAG,EAAE,0BAA0B,CAAC;wBACtC,KAAK,CAAC,GAAG,EAAE,0BAA0B,CAAC;wBACtC,KAAK,CAAC,GAAG,EAAE,0BAA0B,CAAC;qBACvC,CAAC;oBACY,qBAAM,MAAM,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC,CAAC,EAAA;;oBAAxC,KAAK,GAAG,SAAgC;oBAC9C,MAAM,CAAC,IAAI,GAAG;wBACZ,KAAK,CAAC,GAAG,EAAE,0BAA0B,CAAC;wBACtC,KAAK,CAAC,GAAG,EAAE,0BAA0B,CAAC;wBACtC,KAAK,CAAC,GAAG,EAAE,0BAA0B,CAAC;qBACvC,CAAC;oBACa,qBAAM,MAAM,CAAC,UAAU,CAAC,KAAK,CAAC,UAAU,EAAE,CAAC,CAAC,EAAA;;oBAArD,MAAM,GAAG,SAA4C;oBAC3D,MAAM,CAAC,IAAI,GAAG,EAAE,CAAC;oBACjB,qBAAM,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,UAAU,EAAE,CAAC,CAAC,EAAA;;oBAA7C,SAA6C,CAAC;oBAC9C,MAAM,CAAC,cAAc,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;;;;SACrD,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,yBAAyB,EAAE;IAClC,EAAE,CAAC,2CAA2C,EAAE;;;;;oBACxC,MAAM,GAAG,gBAAgB,CAAC,IAAI,CAAC,CAAC;oBACtC,MAAM,CAAC,IAAI,GAAG,CAAC,KAAK,CAAC,GAAG,EAAE,0BAA0B,CAAC,CAAC,CAAC;oBACxC,qBAAM,MAAM,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC,CAAC,EAAA;;oBAAzC,MAAM,GAAG,SAAgC;oBAC/C,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,QAAQ,EAAE,CAAC;;;;SACtC,CAAC,CAAC;IAEH,EAAE,CAAC,mGAAmG,EAAE;;;;;oBAChG,MAAM,GAAG,gBAAgB,CAAC,IAAI,CAAC,CAAC;oBACtC,MAAM,CAAC,IAAI,GAAG,CAAC,KAAK,CAAC,GAAG,EAAE,0BAA0B,CAAC,EAAE,KAAK,CAAC,GAAG,EAAE,0BAA0B,CAAC,EAAE,KAAK,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC,CAAC;oBAClG,qBAAM,MAAM,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC,CAAC,EAAA;;oBAAzC,MAAM,GAAG,SAAgC;oBAC/C,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,QAAQ,EAAE,CAAC;;;;SACtC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -16,7 +16,7 @@ import 'uuid';
16
16
  /** Generate Source Graph */
17
17
 
18
18
  const sourceGraph =
19
- '{"options":{"directed":true,"multigraph":false,"compound":false},"nodes":[{"v":"@proteinjs/db-ui/QueryCursorLoader","value":{"packageName":"@proteinjs/db-ui","name":"QueryCursorLoader","filePath":"/home/runner/work/db/db/packages/ui/src/table/QueryCursorLoader.ts","qualifiedName":"@proteinjs/db-ui/QueryCursorLoader","isAbstract":false,"isStatic":false,"visibility":"public","properties":[{"name":"reactQueryKeys","type":{"packageName":"@proteinjs/ui","name":"ReactQueryKeys","filePath":null,"qualifiedName":"@proteinjs/ui/ReactQueryKeys","typeParameters":null,"directParents":null},"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public"},{"name":"sort","type":{"packageName":"","name":"SortCriteria<T>[]","filePath":null,"qualifiedName":"/SortCriteria<T>[]","typeParameters":null,"directParents":null},"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"private"},{"name":"table","type":{"packageName":"","name":"Table<T>","filePath":null,"qualifiedName":"/Table<T>","typeParameters":null,"directParents":null},"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"private"},{"name":"createQuery","type":{"packageName":"","name":"() => QueryBuilder<T>","filePath":null,"qualifiedName":"/() => QueryBuilder<T>","typeParameters":null,"directParents":null},"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"private"},{"name":"queryOptions","type":{"packageName":"","name":"QueryOptions<T>","filePath":null,"qualifiedName":"/QueryOptions<T>","typeParameters":null,"directParents":null},"isOptional":true,"isAbstract":false,"isStatic":false,"visibility":"private"}],"methods":[{"name":"loadWindow","returnType":{"packageName":"","name":"Promise<CursorWindow<T>>","filePath":null,"qualifiedName":"/Promise<CursorWindow<T>>","typeParameters":null,"directParents":null},"isAsync":true,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public","parameters":[{"name":"cursor","type":{"packageName":"","name":"CursorValue | null","filePath":null,"qualifiedName":"/CursorValue | null","typeParameters":null,"directParents":null}},{"name":"windowSize","type":{"packageName":"","name":"number","filePath":null,"qualifiedName":"/number","typeParameters":null,"directParents":null}}]},{"name":"buildWindowQuery","returnType":{"packageName":"","name":"QueryBuilder<T>","filePath":null,"qualifiedName":"/QueryBuilder<T>","typeParameters":null,"directParents":null},"isAsync":false,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"private","parameters":[{"name":"cursor","type":{"packageName":"","name":"CursorValue | null","filePath":null,"qualifiedName":"/CursorValue | null","typeParameters":null,"directParents":null}},{"name":"windowSize","type":{"packageName":"","name":"number","filePath":null,"qualifiedName":"/number","typeParameters":null,"directParents":null}}]},{"name":"deriveNextCursor","returnType":{"packageName":"","name":"CursorValue | null","filePath":null,"qualifiedName":"/CursorValue | null","typeParameters":null,"directParents":null},"isAsync":false,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"private","parameters":[{"name":"rows","type":{"packageName":"","name":"T[]","filePath":null,"qualifiedName":"/T[]","typeParameters":null,"directParents":null}},{"name":"windowSize","type":{"packageName":"","name":"number","filePath":null,"qualifiedName":"/number","typeParameters":null,"directParents":null}}]}],"typeParameters":["/T extends Record"],"directParentInterfaces":[{"packageName":"@proteinjs/ui","name":"CursorLoader","filePath":null,"qualifiedName":"@proteinjs/ui/CursorLoader","properties":[],"methods":[],"typeParameters":["/T"],"directParents":[]}],"directParentClasses":[],"sourceType":2}},{"v":"@proteinjs/ui/CursorLoader"},{"v":"@proteinjs/db-ui/QueryTableLoader","value":{"packageName":"@proteinjs/db-ui","name":"QueryTableLoader","filePath":"/home/runner/work/db/db/packages/ui/src/table/QueryTableLoader.ts","qualifiedName":"@proteinjs/db-ui/QueryTableLoader","isAbstract":false,"isStatic":false,"visibility":"public","properties":[{"name":"rowCountQb","type":{"packageName":"","name":"QueryBuilder<T>","filePath":null,"qualifiedName":"/QueryBuilder<T>","typeParameters":null,"directParents":null},"isOptional":true,"isAbstract":false,"isStatic":false,"visibility":"private"},{"name":"paginationQb","type":{"packageName":"","name":"QueryBuilder<T>","filePath":null,"qualifiedName":"/QueryBuilder<T>","typeParameters":null,"directParents":null},"isOptional":true,"isAbstract":false,"isStatic":false,"visibility":"private"},{"name":"reactQueryKeys","type":{"packageName":"","name":"TableLoader<T>[\'reactQueryKeys\']","filePath":null,"qualifiedName":"/TableLoader<T>[\'reactQueryKeys\']","typeParameters":null,"directParents":null},"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public"},{"name":"table","type":{"packageName":"","name":"Table<T>","filePath":null,"qualifiedName":"/Table<T>","typeParameters":null,"directParents":null},"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"private"},{"name":"query","type":{"packageName":"","name":"Query<T>","filePath":null,"qualifiedName":"/Query<T>","typeParameters":null,"directParents":null},"isOptional":true,"isAbstract":false,"isStatic":false,"visibility":"private"},{"name":"sort","type":{"packageName":"","name":"SortCriteria<T>[]","filePath":null,"qualifiedName":"/SortCriteria<T>[]","typeParameters":null,"directParents":null},"isOptional":true,"isAbstract":false,"isStatic":false,"visibility":"private"},{"name":"queryOptions","type":{"packageName":"","name":"QueryOptions<T>","filePath":null,"qualifiedName":"/QueryOptions<T>","typeParameters":null,"directParents":null},"isOptional":true,"isAbstract":false,"isStatic":false,"visibility":"private"}],"methods":[{"name":"load","returnType":{"packageName":"","name":"Promise<RowWindow<T>>","filePath":null,"qualifiedName":"/Promise<RowWindow<T>>","typeParameters":null,"directParents":null},"isAsync":true,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public","parameters":[{"name":"startIndex","type":{"packageName":"","name":"number","filePath":null,"qualifiedName":"/number","typeParameters":null,"directParents":null}},{"name":"endIndex","type":{"packageName":"","name":"number","filePath":null,"qualifiedName":"/number","typeParameters":null,"directParents":null}},{"name":"skipRowCount","type":{"packageName":"","name":"boolean","filePath":null,"qualifiedName":"/boolean","typeParameters":null,"directParents":null}}]}],"typeParameters":["/T extends Record"],"directParentInterfaces":[{"packageName":"@proteinjs/ui","name":"TableLoader","filePath":null,"qualifiedName":"@proteinjs/ui/TableLoader","properties":[],"methods":[],"typeParameters":["/T"],"directParents":[]}],"directParentClasses":[],"sourceType":2}},{"v":"@proteinjs/ui/TableLoader"},{"v":"@proteinjs/db-ui/RecordFormCustomization","value":{"packageName":"@proteinjs/db-ui","name":"RecordFormCustomization","filePath":"/home/runner/work/db/db/packages/ui/src/form/RecordFormCustomization.tsx","qualifiedName":"@proteinjs/db-ui/RecordFormCustomization","isAbstract":false,"isStatic":false,"visibility":"public","properties":[{"name":"table","type":{"packageName":"","name":"Table<any>","filePath":null,"qualifiedName":"/Table<any>","typeParameters":null,"directParents":null},"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public"}],"methods":[{"name":"getFormButtons","returnType":{"packageName":"","name":"FormButtons<any>","filePath":null,"qualifiedName":"/FormButtons<any>","typeParameters":null,"directParents":null},"isAsync":false,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public","parameters":[{"name":"record","type":{"packageName":"","name":"any","filePath":null,"qualifiedName":"/any","typeParameters":null,"directParents":null}},{"name":"defaultFormButtons","type":{"packageName":"","name":"FormButtons<any>","filePath":null,"qualifiedName":"/FormButtons<any>","typeParameters":null,"directParents":null}}]},{"name":"getFieldLayout","returnType":{"packageName":"","name":"string[] | string[][]","filePath":null,"qualifiedName":"/string[] | string[][]","typeParameters":null,"directParents":null},"isAsync":false,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public","parameters":[{"name":"record","type":{"packageName":"","name":"any","filePath":null,"qualifiedName":"/any","typeParameters":null,"directParents":null}},{"name":"defaultFieldLayout","type":{"packageName":"","name":"string[] | string[][]","filePath":null,"qualifiedName":"/string[] | string[][]","typeParameters":null,"directParents":null}}]}],"typeParameters":[],"directParentInterfaces":[{"packageName":"@proteinjs/reflection","name":"Loadable","filePath":null,"qualifiedName":"@proteinjs/reflection/Loadable","properties":[],"methods":[],"typeParameters":[],"directParents":[]}],"directParentClasses":[],"sourceType":2}},{"v":"@proteinjs/reflection/Loadable"},{"v":"@proteinjs/db-ui/hashGeneratorPage","value":{"packageName":"@proteinjs/db-ui","name":"hashGeneratorPage","filePath":"/home/runner/work/db/db/packages/ui/src/pages/HashGeneratorPage.tsx","qualifiedName":"@proteinjs/db-ui/hashGeneratorPage","type":{"packageName":"@proteinjs/ui","name":"Page","filePath":null,"qualifiedName":"@proteinjs/ui/Page","typeParameters":[],"directParents":[{"packageName":"@proteinjs/ui","name":"Page","filePath":null,"qualifiedName":"@proteinjs/ui/Page","typeParameters":[],"directParents":null}]},"isExported":true,"isConst":true,"sourceType":0}},{"v":"@proteinjs/ui/Page"},{"v":"@proteinjs/db-ui/recordFormPage","value":{"packageName":"@proteinjs/db-ui","name":"recordFormPage","filePath":"/home/runner/work/db/db/packages/ui/src/pages/RecordFormPage.tsx","qualifiedName":"@proteinjs/db-ui/recordFormPage","type":{"packageName":"@proteinjs/ui","name":"Page","filePath":null,"qualifiedName":"@proteinjs/ui/Page","typeParameters":[],"directParents":[{"packageName":"@proteinjs/ui","name":"Page","filePath":null,"qualifiedName":"@proteinjs/ui/Page","typeParameters":[],"directParents":null}]},"isExported":true,"isConst":true,"sourceType":0}},{"v":"@proteinjs/db-ui/recordTablePage","value":{"packageName":"@proteinjs/db-ui","name":"recordTablePage","filePath":"/home/runner/work/db/db/packages/ui/src/pages/RecordTablePage.tsx","qualifiedName":"@proteinjs/db-ui/recordTablePage","type":{"packageName":"@proteinjs/ui","name":"Page","filePath":null,"qualifiedName":"@proteinjs/ui/Page","typeParameters":[],"directParents":[{"packageName":"@proteinjs/ui","name":"Page","filePath":null,"qualifiedName":"@proteinjs/ui/Page","typeParameters":[],"directParents":null}]},"isExported":true,"isConst":true,"sourceType":0}},{"v":"@proteinjs/db-ui/tablesPage","value":{"packageName":"@proteinjs/db-ui","name":"tablesPage","filePath":"/home/runner/work/db/db/packages/ui/src/pages/TablesPage.tsx","qualifiedName":"@proteinjs/db-ui/tablesPage","type":{"packageName":"@proteinjs/ui","name":"Page","filePath":null,"qualifiedName":"@proteinjs/ui/Page","typeParameters":[],"directParents":[{"packageName":"@proteinjs/ui","name":"Page","filePath":null,"qualifiedName":"@proteinjs/ui/Page","typeParameters":[],"directParents":null}]},"isExported":true,"isConst":true,"sourceType":0}},{"v":"@proteinjs/db-ui/uuidGeneratorPage","value":{"packageName":"@proteinjs/db-ui","name":"uuidGeneratorPage","filePath":"/home/runner/work/db/db/packages/ui/src/pages/UuidGeneratorPage.tsx","qualifiedName":"@proteinjs/db-ui/uuidGeneratorPage","type":{"packageName":"@proteinjs/ui","name":"Page","filePath":null,"qualifiedName":"@proteinjs/ui/Page","typeParameters":[],"directParents":[{"packageName":"@proteinjs/ui","name":"Page","filePath":null,"qualifiedName":"@proteinjs/ui/Page","typeParameters":[],"directParents":null}]},"isExported":true,"isConst":true,"sourceType":0}},{"v":"@proteinjs/db-ui/SpecificTableProps"},{"v":"@proteinjs/db-ui/MigrationRecordFormCustomization","value":{"packageName":"@proteinjs/db-ui","name":"MigrationRecordFormCustomization","filePath":"/home/runner/work/db/db/packages/ui/src/form/customizations/MigrationRecordFormCustomization.tsx","qualifiedName":"@proteinjs/db-ui/MigrationRecordFormCustomization","isAbstract":false,"isStatic":false,"visibility":"public","properties":[{"name":"table","type":null,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public"}],"methods":[{"name":"getFormButtons","returnType":{"packageName":"","name":"FormButtons<any>","filePath":null,"qualifiedName":"/FormButtons<any>","typeParameters":null,"directParents":null},"isAsync":false,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public","parameters":[{"name":"migration","type":{"packageName":"@proteinjs/db","name":"Migration","filePath":null,"qualifiedName":"@proteinjs/db/Migration","typeParameters":null,"directParents":null}},{"name":"defaultFormButtons","type":{"packageName":"","name":"FormButtons<any>","filePath":null,"qualifiedName":"/FormButtons<any>","typeParameters":null,"directParents":null}}]}],"typeParameters":[],"directParentInterfaces":[],"directParentClasses":[{"packageName":"@proteinjs/db-ui","name":"RecordFormCustomization","filePath":null,"qualifiedName":"@proteinjs/db-ui/RecordFormCustomization","isAbstract":false,"isStatic":false,"visibility":"public","properties":[],"methods":[],"typeParameters":[],"directParentInterfaces":[],"directParentClasses":[]}],"sourceType":2}}],"edges":[{"v":"@proteinjs/db-ui/QueryCursorLoader","w":"@proteinjs/ui/CursorLoader","value":"implements interface"},{"v":"@proteinjs/db-ui/QueryTableLoader","w":"@proteinjs/ui/TableLoader","value":"implements interface"},{"v":"@proteinjs/db-ui/RecordFormCustomization","w":"@proteinjs/reflection/Loadable","value":"implements interface"},{"v":"@proteinjs/db-ui/hashGeneratorPage","w":"@proteinjs/ui/Page","value":"has type"},{"v":"@proteinjs/db-ui/recordFormPage","w":"@proteinjs/ui/Page","value":"has type"},{"v":"@proteinjs/db-ui/recordTablePage","w":"@proteinjs/ui/Page","value":"has type"},{"v":"@proteinjs/db-ui/tablesPage","w":"@proteinjs/ui/Page","value":"has type"},{"v":"@proteinjs/db-ui/uuidGeneratorPage","w":"@proteinjs/ui/Page","value":"has type"},{"v":"@proteinjs/db-ui/MigrationRecordFormCustomization","w":"@proteinjs/db-ui/RecordFormCustomization","value":"extends class"}]}';
19
+ '{"options":{"directed":true,"multigraph":false,"compound":false},"nodes":[{"v":"@proteinjs/db-ui/QueryCursorLoader","value":{"packageName":"@proteinjs/db-ui","name":"QueryCursorLoader","filePath":"/home/runner/work/db/db/packages/ui/src/table/QueryCursorLoader.ts","qualifiedName":"@proteinjs/db-ui/QueryCursorLoader","isAbstract":false,"isStatic":false,"visibility":"public","properties":[{"name":"reactQueryKeys","type":{"packageName":"@proteinjs/ui","name":"ReactQueryKeys","filePath":null,"qualifiedName":"@proteinjs/ui/ReactQueryKeys","typeParameters":null,"directParents":null},"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public"},{"name":"pager","type":{"packageName":"","name":"CursorWindowPager<T>","filePath":null,"qualifiedName":"/CursorWindowPager<T>","typeParameters":null,"directParents":null},"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"private"}],"methods":[{"name":"loadWindow","returnType":{"packageName":"","name":"Promise<CursorWindow<T>>","filePath":null,"qualifiedName":"/Promise<CursorWindow<T>>","typeParameters":null,"directParents":null},"isAsync":true,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public","parameters":[{"name":"cursor","type":{"packageName":"","name":"CursorValue | null","filePath":null,"qualifiedName":"/CursorValue | null","typeParameters":null,"directParents":null}},{"name":"windowSize","type":{"packageName":"","name":"number","filePath":null,"qualifiedName":"/number","typeParameters":null,"directParents":null}}]}],"typeParameters":["/T extends Record"],"directParentInterfaces":[{"packageName":"@proteinjs/ui","name":"CursorLoader","filePath":null,"qualifiedName":"@proteinjs/ui/CursorLoader","properties":[],"methods":[],"typeParameters":["/T"],"directParents":[]}],"directParentClasses":[],"sourceType":2}},{"v":"@proteinjs/ui/CursorLoader"},{"v":"@proteinjs/db-ui/QueryTableLoader","value":{"packageName":"@proteinjs/db-ui","name":"QueryTableLoader","filePath":"/home/runner/work/db/db/packages/ui/src/table/QueryTableLoader.ts","qualifiedName":"@proteinjs/db-ui/QueryTableLoader","isAbstract":false,"isStatic":false,"visibility":"public","properties":[{"name":"rowCountQb","type":{"packageName":"","name":"QueryBuilder<T>","filePath":null,"qualifiedName":"/QueryBuilder<T>","typeParameters":null,"directParents":null},"isOptional":true,"isAbstract":false,"isStatic":false,"visibility":"private"},{"name":"paginationQb","type":{"packageName":"","name":"QueryBuilder<T>","filePath":null,"qualifiedName":"/QueryBuilder<T>","typeParameters":null,"directParents":null},"isOptional":true,"isAbstract":false,"isStatic":false,"visibility":"private"},{"name":"reactQueryKeys","type":{"packageName":"","name":"TableLoader<T>[\'reactQueryKeys\']","filePath":null,"qualifiedName":"/TableLoader<T>[\'reactQueryKeys\']","typeParameters":null,"directParents":null},"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public"},{"name":"table","type":{"packageName":"","name":"Table<T>","filePath":null,"qualifiedName":"/Table<T>","typeParameters":null,"directParents":null},"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"private"},{"name":"query","type":{"packageName":"","name":"Query<T>","filePath":null,"qualifiedName":"/Query<T>","typeParameters":null,"directParents":null},"isOptional":true,"isAbstract":false,"isStatic":false,"visibility":"private"},{"name":"sort","type":{"packageName":"","name":"SortCriteria<T>[]","filePath":null,"qualifiedName":"/SortCriteria<T>[]","typeParameters":null,"directParents":null},"isOptional":true,"isAbstract":false,"isStatic":false,"visibility":"private"},{"name":"queryOptions","type":{"packageName":"","name":"QueryOptions<T>","filePath":null,"qualifiedName":"/QueryOptions<T>","typeParameters":null,"directParents":null},"isOptional":true,"isAbstract":false,"isStatic":false,"visibility":"private"}],"methods":[{"name":"load","returnType":{"packageName":"","name":"Promise<RowWindow<T>>","filePath":null,"qualifiedName":"/Promise<RowWindow<T>>","typeParameters":null,"directParents":null},"isAsync":true,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public","parameters":[{"name":"startIndex","type":{"packageName":"","name":"number","filePath":null,"qualifiedName":"/number","typeParameters":null,"directParents":null}},{"name":"endIndex","type":{"packageName":"","name":"number","filePath":null,"qualifiedName":"/number","typeParameters":null,"directParents":null}},{"name":"skipRowCount","type":{"packageName":"","name":"boolean","filePath":null,"qualifiedName":"/boolean","typeParameters":null,"directParents":null}}]}],"typeParameters":["/T extends Record"],"directParentInterfaces":[{"packageName":"@proteinjs/ui","name":"TableLoader","filePath":null,"qualifiedName":"@proteinjs/ui/TableLoader","properties":[],"methods":[],"typeParameters":["/T"],"directParents":[]}],"directParentClasses":[],"sourceType":2}},{"v":"@proteinjs/ui/TableLoader"},{"v":"@proteinjs/db-ui/RecordFormCustomization","value":{"packageName":"@proteinjs/db-ui","name":"RecordFormCustomization","filePath":"/home/runner/work/db/db/packages/ui/src/form/RecordFormCustomization.tsx","qualifiedName":"@proteinjs/db-ui/RecordFormCustomization","isAbstract":false,"isStatic":false,"visibility":"public","properties":[{"name":"table","type":{"packageName":"","name":"Table<any>","filePath":null,"qualifiedName":"/Table<any>","typeParameters":null,"directParents":null},"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public"}],"methods":[{"name":"getFormButtons","returnType":{"packageName":"","name":"FormButtons<any>","filePath":null,"qualifiedName":"/FormButtons<any>","typeParameters":null,"directParents":null},"isAsync":false,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public","parameters":[{"name":"record","type":{"packageName":"","name":"any","filePath":null,"qualifiedName":"/any","typeParameters":null,"directParents":null}},{"name":"defaultFormButtons","type":{"packageName":"","name":"FormButtons<any>","filePath":null,"qualifiedName":"/FormButtons<any>","typeParameters":null,"directParents":null}}]},{"name":"getFieldLayout","returnType":{"packageName":"","name":"string[] | string[][]","filePath":null,"qualifiedName":"/string[] | string[][]","typeParameters":null,"directParents":null},"isAsync":false,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public","parameters":[{"name":"record","type":{"packageName":"","name":"any","filePath":null,"qualifiedName":"/any","typeParameters":null,"directParents":null}},{"name":"defaultFieldLayout","type":{"packageName":"","name":"string[] | string[][]","filePath":null,"qualifiedName":"/string[] | string[][]","typeParameters":null,"directParents":null}}]}],"typeParameters":[],"directParentInterfaces":[{"packageName":"@proteinjs/reflection","name":"Loadable","filePath":null,"qualifiedName":"@proteinjs/reflection/Loadable","properties":[],"methods":[],"typeParameters":[],"directParents":[]}],"directParentClasses":[],"sourceType":2}},{"v":"@proteinjs/reflection/Loadable"},{"v":"@proteinjs/db-ui/hashGeneratorPage","value":{"packageName":"@proteinjs/db-ui","name":"hashGeneratorPage","filePath":"/home/runner/work/db/db/packages/ui/src/pages/HashGeneratorPage.tsx","qualifiedName":"@proteinjs/db-ui/hashGeneratorPage","type":{"packageName":"@proteinjs/ui","name":"Page","filePath":null,"qualifiedName":"@proteinjs/ui/Page","typeParameters":[],"directParents":[{"packageName":"@proteinjs/ui","name":"Page","filePath":null,"qualifiedName":"@proteinjs/ui/Page","typeParameters":[],"directParents":null}]},"isExported":true,"isConst":true,"sourceType":0}},{"v":"@proteinjs/ui/Page"},{"v":"@proteinjs/db-ui/recordFormPage","value":{"packageName":"@proteinjs/db-ui","name":"recordFormPage","filePath":"/home/runner/work/db/db/packages/ui/src/pages/RecordFormPage.tsx","qualifiedName":"@proteinjs/db-ui/recordFormPage","type":{"packageName":"@proteinjs/ui","name":"Page","filePath":null,"qualifiedName":"@proteinjs/ui/Page","typeParameters":[],"directParents":[{"packageName":"@proteinjs/ui","name":"Page","filePath":null,"qualifiedName":"@proteinjs/ui/Page","typeParameters":[],"directParents":null}]},"isExported":true,"isConst":true,"sourceType":0}},{"v":"@proteinjs/db-ui/recordTablePage","value":{"packageName":"@proteinjs/db-ui","name":"recordTablePage","filePath":"/home/runner/work/db/db/packages/ui/src/pages/RecordTablePage.tsx","qualifiedName":"@proteinjs/db-ui/recordTablePage","type":{"packageName":"@proteinjs/ui","name":"Page","filePath":null,"qualifiedName":"@proteinjs/ui/Page","typeParameters":[],"directParents":[{"packageName":"@proteinjs/ui","name":"Page","filePath":null,"qualifiedName":"@proteinjs/ui/Page","typeParameters":[],"directParents":null}]},"isExported":true,"isConst":true,"sourceType":0}},{"v":"@proteinjs/db-ui/tablesPage","value":{"packageName":"@proteinjs/db-ui","name":"tablesPage","filePath":"/home/runner/work/db/db/packages/ui/src/pages/TablesPage.tsx","qualifiedName":"@proteinjs/db-ui/tablesPage","type":{"packageName":"@proteinjs/ui","name":"Page","filePath":null,"qualifiedName":"@proteinjs/ui/Page","typeParameters":[],"directParents":[{"packageName":"@proteinjs/ui","name":"Page","filePath":null,"qualifiedName":"@proteinjs/ui/Page","typeParameters":[],"directParents":null}]},"isExported":true,"isConst":true,"sourceType":0}},{"v":"@proteinjs/db-ui/uuidGeneratorPage","value":{"packageName":"@proteinjs/db-ui","name":"uuidGeneratorPage","filePath":"/home/runner/work/db/db/packages/ui/src/pages/UuidGeneratorPage.tsx","qualifiedName":"@proteinjs/db-ui/uuidGeneratorPage","type":{"packageName":"@proteinjs/ui","name":"Page","filePath":null,"qualifiedName":"@proteinjs/ui/Page","typeParameters":[],"directParents":[{"packageName":"@proteinjs/ui","name":"Page","filePath":null,"qualifiedName":"@proteinjs/ui/Page","typeParameters":[],"directParents":null}]},"isExported":true,"isConst":true,"sourceType":0}},{"v":"@proteinjs/db-ui/SpecificTableProps"},{"v":"@proteinjs/db-ui/MigrationRecordFormCustomization","value":{"packageName":"@proteinjs/db-ui","name":"MigrationRecordFormCustomization","filePath":"/home/runner/work/db/db/packages/ui/src/form/customizations/MigrationRecordFormCustomization.tsx","qualifiedName":"@proteinjs/db-ui/MigrationRecordFormCustomization","isAbstract":false,"isStatic":false,"visibility":"public","properties":[{"name":"table","type":null,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public"}],"methods":[{"name":"getFormButtons","returnType":{"packageName":"","name":"FormButtons<any>","filePath":null,"qualifiedName":"/FormButtons<any>","typeParameters":null,"directParents":null},"isAsync":false,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public","parameters":[{"name":"migration","type":{"packageName":"@proteinjs/db","name":"Migration","filePath":null,"qualifiedName":"@proteinjs/db/Migration","typeParameters":null,"directParents":null}},{"name":"defaultFormButtons","type":{"packageName":"","name":"FormButtons<any>","filePath":null,"qualifiedName":"/FormButtons<any>","typeParameters":null,"directParents":null}}]}],"typeParameters":[],"directParentInterfaces":[],"directParentClasses":[{"packageName":"@proteinjs/db-ui","name":"RecordFormCustomization","filePath":null,"qualifiedName":"@proteinjs/db-ui/RecordFormCustomization","isAbstract":false,"isStatic":false,"visibility":"public","properties":[],"methods":[],"typeParameters":[],"directParentInterfaces":[],"directParentClasses":[]}],"sourceType":2}}],"edges":[{"v":"@proteinjs/db-ui/QueryCursorLoader","w":"@proteinjs/ui/CursorLoader","value":"implements interface"},{"v":"@proteinjs/db-ui/QueryTableLoader","w":"@proteinjs/ui/TableLoader","value":"implements interface"},{"v":"@proteinjs/db-ui/RecordFormCustomization","w":"@proteinjs/reflection/Loadable","value":"implements interface"},{"v":"@proteinjs/db-ui/hashGeneratorPage","w":"@proteinjs/ui/Page","value":"has type"},{"v":"@proteinjs/db-ui/recordFormPage","w":"@proteinjs/ui/Page","value":"has type"},{"v":"@proteinjs/db-ui/recordTablePage","w":"@proteinjs/ui/Page","value":"has type"},{"v":"@proteinjs/db-ui/tablesPage","w":"@proteinjs/ui/Page","value":"has type"},{"v":"@proteinjs/db-ui/uuidGeneratorPage","w":"@proteinjs/ui/Page","value":"has type"},{"v":"@proteinjs/db-ui/MigrationRecordFormCustomization","w":"@proteinjs/db-ui/RecordFormCustomization","value":"extends class"}]}';
20
20
 
21
21
  /** Generate Source Links */
22
22
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@proteinjs/db-ui",
3
- "version": "1.8.5",
3
+ "version": "1.9.0",
4
4
  "description": "Db ui components",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -26,7 +26,7 @@
26
26
  "@emotion/styled": "11.11.0",
27
27
  "@mui/icons-material": "5.14.11",
28
28
  "@mui/material": "5.14.11",
29
- "@proteinjs/db": "^1.30.0",
29
+ "@proteinjs/db": "^1.31.0",
30
30
  "@proteinjs/reflection": "^1.1.12",
31
31
  "@proteinjs/ui": "^4.5.0",
32
32
  "@proteinjs/util": "^1.6.1",
@@ -54,5 +54,5 @@
54
54
  },
55
55
  "main": "./dist/generated/index.js",
56
56
  "types": "./dist/generated/index.d.ts",
57
- "gitHead": "91f8bc0e7c5873a61bd9ef6e4a1b380c24acc922"
57
+ "gitHead": "d84e548336f96b2541c1fbbe6a9ad6074df610c7"
58
58
  }
@@ -1,70 +1,50 @@
1
- import { QueryBuilder, QueryOptions, Record, SortCriteria, Table, getDb } from '@proteinjs/db';
1
+ import { CursorWindowPager, QueryBuilder, QueryOptions, Record, SortCriteria, Table } from '@proteinjs/db';
2
2
  import { CursorLoader, CursorValue, CursorWindow, ReactQueryKeys } from '@proteinjs/ui';
3
3
 
4
4
  /**
5
- * `CursorLoader` over the same QueryBuilder machinery as `QueryTableLoader`: each window
6
- * applies `cursorField < cursor` (`>` for an ascending sort) plus `paginate(0, windowSize)`
7
- * on a fresh build of the base query, so rows created or removed while paging can never
8
- * shift the window frame. The cursor field and direction derive from the PRIMARY sort
9
- * criterion.
5
+ * `CursorLoader` over the db-owned cursor-window machinery (`CursorWindowPager`): each window
6
+ * carries a lexicographic continuation past the anchor row — the primary sort axes plus a
7
+ * unique `id` tiebreak — and `paginate(0, windowSize)` on a fresh build of the base query, so
8
+ * rows created or removed while paging can never shift the window frame, and ties on the sort
9
+ * axis can never skip or duplicate boundary rows. Cursors are the pager's anchors encoded as
10
+ * strings (house `Serializer`, so dates/moments survive react-query page params).
10
11
  *
11
12
  * `dataKey` is the table name — table-level invalidation (`useTableMutation`) reaches every
12
13
  * cursor query over it; `dataQueryKey` is the serialized query + sort.
13
14
  */
14
15
  export class QueryCursorLoader<T extends Record> implements CursorLoader<T> {
15
16
  reactQueryKeys: ReactQueryKeys;
16
- private sort: SortCriteria<T>[];
17
+ private pager: CursorWindowPager<T>;
17
18
 
18
19
  /**
19
20
  * @param table the table to load windows from
20
21
  * @param createQuery factory producing a FRESH conditions-only QueryBuilder per call —
21
- * builders are mutable, so every window builds its own to hang the cursor condition,
22
+ * builders are mutable, so every window builds its own to hang the cursor conditions,
22
23
  * sort, and pagination on
23
24
  * @param sort the sort criteria to apply to every window; the primary criterion is the
24
- * cursor axis
25
+ * cursor axis (the pager appends the `id` tiebreak)
25
26
  * @param queryOptions query options passed through to every window query
26
27
  */
27
28
  constructor(
28
- private table: Table<T>,
29
- private createQuery: () => QueryBuilder<T>,
29
+ table: Table<T>,
30
+ createQuery: () => QueryBuilder<T>,
30
31
  sort?: SortCriteria<T>[],
31
- private queryOptions?: QueryOptions<T>
32
+ queryOptions?: QueryOptions<T>
32
33
  ) {
33
- this.sort = sort ? sort : [{ field: 'created' as keyof T, desc: true }];
34
+ const effectiveSort = sort ? sort : [{ field: 'created' as keyof T, desc: true }];
35
+ this.pager = new CursorWindowPager<T>(table, createQuery, effectiveSort, { queryOptions });
34
36
  this.reactQueryKeys = {
35
- dataKey: this.table.name,
36
- dataQueryKey: JSON.stringify({ query: this.createQuery(), sort: this.sort }),
37
+ dataKey: table.name,
38
+ dataQueryKey: JSON.stringify({ query: createQuery(), sort: effectiveSort }),
37
39
  };
38
40
  }
39
41
 
40
42
  async loadWindow(cursor: CursorValue | null, windowSize: number): Promise<CursorWindow<T>> {
41
- const qb = this.buildWindowQuery(cursor, windowSize);
42
- const rows = await getDb().query(this.table, qb, this.queryOptions);
43
- return { rows, nextCursor: this.deriveNextCursor(rows, windowSize) };
44
- }
45
-
46
- private buildWindowQuery(cursor: CursorValue | null, windowSize: number): QueryBuilder<T> {
47
- const qb = this.createQuery();
48
- const primarySort = this.sort[0];
49
- if (cursor !== null) {
50
- qb.condition({
51
- field: primarySort.field,
52
- operator: primarySort.desc ? '<' : '>',
53
- value: cursor as T[keyof T],
54
- });
55
- }
56
- return qb.sort(this.sort).paginate({ start: 0, end: windowSize });
57
- }
58
-
59
- private deriveNextCursor(rows: T[], windowSize: number): CursorValue | null {
60
- // A short window means the data set is exhausted; a full one implies more may exist —
61
- // the next (short or empty) window settles it.
62
- if (rows.length < windowSize) {
63
- return null;
64
- }
65
- // NULL cursor-field rows collect at the tail under the sort, and a null tail can't
66
- // anchor a cursor: the data set's dated rows are exhausted, so paging stops here.
67
- const tail = rows[rows.length - 1][this.sort[0].field];
68
- return tail == null ? null : (tail as unknown as CursorValue);
43
+ const anchor = cursor === null ? null : CursorWindowPager.decodeAnchor(cursor as string);
44
+ const window = await this.pager.loadWindow(anchor, windowSize);
45
+ return {
46
+ rows: window.rows,
47
+ nextCursor: window.nextAnchor ? CursorWindowPager.encodeAnchor(window.nextAnchor) : null,
48
+ };
69
49
  }
70
50
  }
@@ -1,35 +1,24 @@
1
1
  /**
2
- * QueryCursorLoader (src/table/QueryCursorLoader.ts) at the pure level: react-query key
3
- * derivation, cursor threading into the window query, and next-cursor derivation — including
4
- * the null-tail exhaustion rule (NULL cursor-field rows collect at the tail under the sort;
5
- * a null tail can't anchor a cursor, so paging stops). Window queries are inspected through
6
- * the QueryBuilder graph; loadWindow's db round-trip is exercised with a stubbed getDb.
2
+ * QueryCursorLoader (src/table/QueryCursorLoader.ts) as the thin `CursorLoader` adapter over
3
+ * the db-owned `CursorWindowPager`: react-query key derivation, string-cursor encode/decode
4
+ * threading into the pager's lexicographic window queries (primary axis + `id` tiebreak), and
5
+ * exhaustion (short window, and the null-tail rule: NULL sort-value rows collect at the tail
6
+ * under the sort; a null can't anchor a continuation, so paging stops). The db round trip is
7
+ * injected through the pager's `options.db` seam via the house internals-cast pattern — no
8
+ * module mocking.
7
9
  */
8
10
  import moment from 'moment';
9
-
10
- const stagedRows: { rows: unknown[] } = { rows: [] };
11
- const queries: unknown[] = [];
12
- const stubDb = {
13
- query: async (_table: unknown, qb: unknown) => {
14
- queries.push(qb);
15
- return stagedRows.rows;
16
- },
17
- };
18
-
19
- jest.mock('@proteinjs/db', () => ({
20
- ...jest.requireActual('@proteinjs/db'),
21
- getDb: () => stubDb,
22
- }));
23
-
24
- import { QueryBuilder, SortCriteria, Table } from '@proteinjs/db';
25
- import { CursorValue } from '@proteinjs/ui';
11
+ import { Db, QueryBuilder, SortCriteria, Table } from '@proteinjs/db';
26
12
  import { QueryCursorLoader } from '../src/table/QueryCursorLoader';
13
+ // Load the reflection source graph so the house Serializer's custom serializers (moments)
14
+ // are registered — cursor encode/decode fidelity rides them.
15
+ import '../generated/index';
27
16
 
28
17
  type Row = {
29
18
  id: string;
30
19
  created: moment.Moment;
31
20
  updated: moment.Moment;
32
- lastActivityAt?: moment.Moment;
21
+ lastActivityAt?: moment.Moment | null;
33
22
  isPinned?: boolean;
34
23
  };
35
24
 
@@ -37,13 +26,23 @@ const table = { name: 'test_content' } as Table<Row>;
37
26
  const sort: SortCriteria<Row>[] = [{ field: 'lastActivityAt', desc: true }];
38
27
  const createQuery = () => new QueryBuilder<Row>(table.name);
39
28
 
40
- /** House pattern: private helpers reachable via a typed cast on the instance. */
41
- type LoaderInternals = {
42
- buildWindowQuery(cursor: CursorValue | null, windowSize: number): QueryBuilder<Row>;
43
- deriveNextCursor(rows: Row[], windowSize: number): CursorValue | null;
44
- };
29
+ const staged: { rows: Row[] } = { rows: [] };
30
+ const executed: QueryBuilder<Row>[] = [];
31
+ const stubDb = {
32
+ query: async (_table: unknown, qb: QueryBuilder<Row>) => {
33
+ executed.push(qb);
34
+ return staged.rows;
35
+ },
36
+ } as unknown as Db;
37
+
38
+ /** House pattern: private internals reachable via a typed cast on the instance. */
39
+ type LoaderInternals = { pager: { options?: { db?: Db } } };
45
40
 
46
- const internals = (loader: QueryCursorLoader<Row>) => loader as unknown as LoaderInternals;
41
+ const loaderWithStubDb = (sortCriteria?: SortCriteria<Row>[], query: () => QueryBuilder<Row> = createQuery) => {
42
+ const loader = new QueryCursorLoader<Row>(table, query, sortCriteria);
43
+ (loader as unknown as LoaderInternals).pager.options = { db: stubDb };
44
+ return loader;
45
+ };
47
46
 
48
47
  const graphNodes = (qb: QueryBuilder<Row>): any[] => qb.graph.nodes().map((id: string) => qb.graph.node(id));
49
48
  const conditionNodes = (qb: QueryBuilder<Row>) => graphNodes(qb).filter((node) => node?.type === 'CONDITION');
@@ -54,12 +53,12 @@ const dated = (id: string, at: string | null): Row => ({
54
53
  id,
55
54
  created: moment(),
56
55
  updated: moment(),
57
- lastActivityAt: at ? moment(at) : undefined,
56
+ lastActivityAt: at ? moment(at) : null,
58
57
  });
59
58
 
60
59
  beforeEach(() => {
61
- stagedRows.rows = [];
62
- queries.length = 0;
60
+ staged.rows = [];
61
+ executed.length = 0;
63
62
  });
64
63
 
65
64
  describe('reactQueryKeys', () => {
@@ -81,86 +80,80 @@ describe('reactQueryKeys', () => {
81
80
  });
82
81
  });
83
82
 
84
- describe('buildWindowQuery — cursor threading', () => {
85
- it('the first window carries no cursor condition, only the sort and the window pagination', () => {
86
- const loader = new QueryCursorLoader<Row>(table, createQuery, sort);
87
- const qb = internals(loader).buildWindowQuery(null, 30);
83
+ describe('loadWindow — cursor threading', () => {
84
+ it('the first window carries no cursor conditions the sort (with id tiebreak) and the window pagination', async () => {
85
+ const loader = loaderWithStubDb(sort);
86
+ staged.rows = [];
87
+ await loader.loadWindow(null, 30);
88
+ expect(executed).toHaveLength(1);
89
+ const qb = executed[0];
88
90
  expect(conditionNodes(qb)).toHaveLength(0);
89
91
  expect(paginationNode(qb)).toMatchObject({ start: 0, end: 30 });
90
- expect(sortNodes(qb)).toHaveLength(1);
91
- expect(sortNodes(qb)[0].criteria).toMatchObject({ field: 'lastActivityAt', desc: true });
92
- });
93
-
94
- it('a cursor becomes `field < cursor` on the primary sort field for a descending sort', () => {
95
- const loader = new QueryCursorLoader<Row>(table, createQuery, sort);
96
- const cursor = moment('2026-08-10T12:00:00.000Z');
97
- const qb = internals(loader).buildWindowQuery(cursor, 30);
98
- const conditions = conditionNodes(qb);
99
- expect(conditions).toHaveLength(1);
100
- expect(conditions[0]).toMatchObject({ field: 'lastActivityAt', operator: '<' });
101
- expect(conditions[0].value).toBe(cursor);
102
- expect(paginationNode(qb)).toMatchObject({ start: 0, end: 30 });
92
+ expect(sortNodes(qb).map((node) => node.criteria)).toEqual([
93
+ { field: 'lastActivityAt', desc: true },
94
+ { field: 'id', desc: false },
95
+ ]);
103
96
  });
104
97
 
105
- it('an ascending primary sort flips the cursor operator to `>`', () => {
106
- const loader = new QueryCursorLoader<Row>(table, createQuery, [{ field: 'lastActivityAt', desc: false }]);
107
- const qb = internals(loader).buildWindowQuery(5, 10);
108
- expect(conditionNodes(qb)[0]).toMatchObject({ field: 'lastActivityAt', operator: '>', value: 5 });
109
- });
110
-
111
- it('every window builds on a FRESH query — cursor conditions never accumulate across windows', () => {
112
- const loader = new QueryCursorLoader<Row>(table, createQuery, sort);
113
- internals(loader).buildWindowQuery(moment('2026-08-10T12:00:00.000Z'), 30);
114
- const second = internals(loader).buildWindowQuery(moment('2026-08-01T12:00:00.000Z'), 30);
115
- expect(conditionNodes(second)).toHaveLength(1);
116
- });
117
- });
118
-
119
- describe('deriveNextCursor — exhaustion', () => {
120
- const loader = new QueryCursorLoader<Row>(table, createQuery, sort);
121
-
122
- it('a short window is exhausted', () => {
123
- expect(internals(loader).deriveNextCursor([dated('a', '2026-08-10T12:00:00.000Z')], 3)).toBeNull();
124
- expect(internals(loader).deriveNextCursor([], 3)).toBeNull();
125
- });
126
-
127
- it('a full window anchors the next cursor at its tail', () => {
98
+ it('a full window returns a string cursor; feeding it back threads the lexicographic continuation', async () => {
99
+ const loader = loaderWithStubDb(sort);
128
100
  const rows = [
129
101
  dated('a', '2026-08-10T12:00:00.000Z'),
130
102
  dated('b', '2026-08-09T12:00:00.000Z'),
131
103
  dated('c', '2026-08-08T12:00:00.000Z'),
132
104
  ];
133
- const next = internals(loader).deriveNextCursor(rows, 3);
134
- expect(next).toBe(rows[2].lastActivityAt);
135
- });
136
-
137
- it('a full window with a NULL cursor-field tail is exhausted (null rows collect at the tail; no anchor)', () => {
138
- const rows = [dated('a', '2026-08-10T12:00:00.000Z'), dated('b', '2026-08-09T12:00:00.000Z'), dated('c', null)];
139
- expect(internals(loader).deriveNextCursor(rows, 3)).toBeNull();
105
+ staged.rows = rows;
106
+ const first = await loader.loadWindow(null, 3);
107
+ expect(first.rows).toBe(rows);
108
+ expect(typeof first.nextCursor).toBe('string');
109
+
110
+ staged.rows = [];
111
+ await loader.loadWindow(first.nextCursor, 3);
112
+ const qb = executed[1];
113
+ // Past-on-axis OR (equal-on-axis AND past-on-id), anchored at the tail row 'c' — with the
114
+ // moment surviving the string cursor round trip.
115
+ const conditions = conditionNodes(qb);
116
+ expect(conditions).toHaveLength(3);
117
+ const past = conditions.find((node) => node.operator === '<');
118
+ expect(past).toMatchObject({ field: 'lastActivityAt' });
119
+ expect(moment.isMoment(past.value)).toBe(true);
120
+ expect(past.value.toISOString()).toBe(rows[2].lastActivityAt!.toISOString());
121
+ expect(conditions.find((node) => node.operator === '=')).toMatchObject({ field: 'lastActivityAt' });
122
+ expect(conditions.find((node) => node.operator === '>')).toMatchObject({ field: 'id', value: 'c' });
140
123
  });
141
- });
142
124
 
143
- describe('loadWindow', () => {
144
- it('returns the queried rows with the derived next cursor', async () => {
145
- const loader = new QueryCursorLoader<Row>(table, createQuery, sort);
146
- const rows = [
125
+ it('every window builds on a FRESH query — cursor conditions never accumulate across windows', async () => {
126
+ const loader = loaderWithStubDb(sort);
127
+ staged.rows = [
147
128
  dated('a', '2026-08-10T12:00:00.000Z'),
148
129
  dated('b', '2026-08-09T12:00:00.000Z'),
149
130
  dated('c', '2026-08-08T12:00:00.000Z'),
150
131
  ];
151
- stagedRows.rows = rows;
132
+ const first = await loader.loadWindow(null, 3);
133
+ staged.rows = [
134
+ dated('d', '2026-08-07T12:00:00.000Z'),
135
+ dated('e', '2026-08-06T12:00:00.000Z'),
136
+ dated('f', '2026-08-05T12:00:00.000Z'),
137
+ ];
138
+ const second = await loader.loadWindow(first.nextCursor, 3);
139
+ staged.rows = [];
140
+ await loader.loadWindow(second.nextCursor, 3);
141
+ expect(conditionNodes(executed[2])).toHaveLength(3);
142
+ });
143
+ });
144
+
145
+ describe('loadWindow — exhaustion', () => {
146
+ it('a short window is exhausted (null cursor)', async () => {
147
+ const loader = loaderWithStubDb(sort);
148
+ staged.rows = [dated('a', '2026-08-10T12:00:00.000Z')];
152
149
  const window = await loader.loadWindow(null, 3);
153
- expect(window.rows).toBe(rows);
154
- expect(window.nextCursor).toBe(rows[2].lastActivityAt);
155
- // The executed query is the built window query: pagination sized to the window.
156
- expect(queries).toHaveLength(1);
157
- expect(paginationNode(queries[0] as QueryBuilder<Row>)).toMatchObject({ start: 0, end: 3 });
150
+ expect(window.nextCursor).toBeNull();
158
151
  });
159
152
 
160
- it('returns an exhausted window (null cursor) when the query comes back short', async () => {
161
- const loader = new QueryCursorLoader<Row>(table, createQuery, sort);
162
- stagedRows.rows = [dated('a', '2026-08-10T12:00:00.000Z')];
163
- const window = await loader.loadWindow(moment('2026-08-11T12:00:00.000Z'), 3);
153
+ it('a full window with a NULL sort-value tail is exhausted (null rows collect at the tail; no anchor)', async () => {
154
+ const loader = loaderWithStubDb(sort);
155
+ staged.rows = [dated('a', '2026-08-10T12:00:00.000Z'), dated('b', '2026-08-09T12:00:00.000Z'), dated('c', null)];
156
+ const window = await loader.loadWindow(null, 3);
164
157
  expect(window.nextCursor).toBeNull();
165
158
  });
166
159
  });