@teleporthq/teleport-plugin-next-data-source 0.44.0 → 0.44.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (94) hide show
  1. package/__tests__/_helpers/first-page-runtime.ts +184 -0
  2. package/__tests__/_helpers/pagination-widget-fixture.ts +67 -1
  3. package/__tests__/browser-row-policy.test.ts +202 -2
  4. package/__tests__/data-source-request-locale.test.ts +4 -3
  5. package/__tests__/fetchers.test.ts +6 -2
  6. package/__tests__/infinite-scroll.test.ts +210 -5
  7. package/__tests__/page-data-visitor-read.test.ts +196 -0
  8. package/__tests__/plain-list-sort.test.ts +1 -1
  9. package/__tests__/plain-list-starting-sort.test.ts +323 -0
  10. package/__tests__/refetch-loading-state.test.ts +158 -2
  11. package/__tests__/starting-sort-first-page.test.ts +285 -0
  12. package/dist/cjs/cache/ast.d.ts +2 -0
  13. package/dist/cjs/cache/ast.d.ts.map +1 -1
  14. package/dist/cjs/cache/ast.js +5 -2
  15. package/dist/cjs/cache/ast.js.map +1 -1
  16. package/dist/cjs/data-source-fetchers.d.ts.map +1 -1
  17. package/dist/cjs/data-source-fetchers.js +10 -2
  18. package/dist/cjs/data-source-fetchers.js.map +1 -1
  19. package/dist/cjs/fetchers/teleport.d.ts.map +1 -1
  20. package/dist/cjs/fetchers/teleport.js +1 -1
  21. package/dist/cjs/fetchers/teleport.js.map +1 -1
  22. package/dist/cjs/fetchers/utils/browser-row-policy.d.ts.map +1 -1
  23. package/dist/cjs/fetchers/utils/browser-row-policy.js +5 -1
  24. package/dist/cjs/fetchers/utils/browser-row-policy.js.map +1 -1
  25. package/dist/cjs/index.d.ts.map +1 -1
  26. package/dist/cjs/index.js +14 -2
  27. package/dist/cjs/index.js.map +1 -1
  28. package/dist/cjs/infinite-scroll.d.ts +64 -2
  29. package/dist/cjs/infinite-scroll.d.ts.map +1 -1
  30. package/dist/cjs/infinite-scroll.js +102 -3
  31. package/dist/cjs/infinite-scroll.js.map +1 -1
  32. package/dist/cjs/loading-state.d.ts +23 -2
  33. package/dist/cjs/loading-state.d.ts.map +1 -1
  34. package/dist/cjs/loading-state.js +50 -15
  35. package/dist/cjs/loading-state.js.map +1 -1
  36. package/dist/cjs/pagination-plugin.d.ts.map +1 -1
  37. package/dist/cjs/pagination-plugin.js +200 -64
  38. package/dist/cjs/pagination-plugin.js.map +1 -1
  39. package/dist/cjs/sort-utils.d.ts +53 -1
  40. package/dist/cjs/sort-utils.d.ts.map +1 -1
  41. package/dist/cjs/sort-utils.js +105 -13
  42. package/dist/cjs/sort-utils.js.map +1 -1
  43. package/dist/cjs/tsconfig.tsbuildinfo +1 -1
  44. package/dist/cjs/utils.d.ts +3 -2
  45. package/dist/cjs/utils.d.ts.map +1 -1
  46. package/dist/cjs/utils.js +48 -18
  47. package/dist/cjs/utils.js.map +1 -1
  48. package/dist/esm/cache/ast.d.ts +2 -0
  49. package/dist/esm/cache/ast.d.ts.map +1 -1
  50. package/dist/esm/cache/ast.js +3 -1
  51. package/dist/esm/cache/ast.js.map +1 -1
  52. package/dist/esm/data-source-fetchers.d.ts.map +1 -1
  53. package/dist/esm/data-source-fetchers.js +10 -2
  54. package/dist/esm/data-source-fetchers.js.map +1 -1
  55. package/dist/esm/fetchers/teleport.d.ts.map +1 -1
  56. package/dist/esm/fetchers/teleport.js +1 -1
  57. package/dist/esm/fetchers/teleport.js.map +1 -1
  58. package/dist/esm/fetchers/utils/browser-row-policy.d.ts.map +1 -1
  59. package/dist/esm/fetchers/utils/browser-row-policy.js +6 -2
  60. package/dist/esm/fetchers/utils/browser-row-policy.js.map +1 -1
  61. package/dist/esm/index.d.ts.map +1 -1
  62. package/dist/esm/index.js +14 -2
  63. package/dist/esm/index.js.map +1 -1
  64. package/dist/esm/infinite-scroll.d.ts +64 -2
  65. package/dist/esm/infinite-scroll.d.ts.map +1 -1
  66. package/dist/esm/infinite-scroll.js +97 -2
  67. package/dist/esm/infinite-scroll.js.map +1 -1
  68. package/dist/esm/loading-state.d.ts +23 -2
  69. package/dist/esm/loading-state.d.ts.map +1 -1
  70. package/dist/esm/loading-state.js +50 -15
  71. package/dist/esm/loading-state.js.map +1 -1
  72. package/dist/esm/pagination-plugin.d.ts.map +1 -1
  73. package/dist/esm/pagination-plugin.js +204 -68
  74. package/dist/esm/pagination-plugin.js.map +1 -1
  75. package/dist/esm/sort-utils.d.ts +53 -1
  76. package/dist/esm/sort-utils.d.ts.map +1 -1
  77. package/dist/esm/sort-utils.js +100 -12
  78. package/dist/esm/sort-utils.js.map +1 -1
  79. package/dist/esm/tsconfig.tsbuildinfo +1 -1
  80. package/dist/esm/utils.d.ts +3 -2
  81. package/dist/esm/utils.d.ts.map +1 -1
  82. package/dist/esm/utils.js +49 -19
  83. package/dist/esm/utils.js.map +1 -1
  84. package/package.json +4 -4
  85. package/src/cache/ast.ts +4 -1
  86. package/src/data-source-fetchers.ts +33 -14
  87. package/src/fetchers/teleport.ts +8 -2
  88. package/src/fetchers/utils/browser-row-policy.ts +79 -5
  89. package/src/index.ts +8 -2
  90. package/src/infinite-scroll.ts +182 -4
  91. package/src/loading-state.ts +85 -13
  92. package/src/pagination-plugin.ts +369 -83
  93. package/src/sort-utils.ts +164 -21
  94. package/src/utils.ts +55 -18
@@ -301,7 +301,7 @@ function buildFilterDestinationExpression(destination) {
301
301
  return teleport_plugin_common_1.ASTUtils.convertFilterDestinationToExpression(destination);
302
302
  }
303
303
  // Scan UIDL to find all data source usages and build a registry
304
- function buildStateRegistry(uidlNode) {
304
+ function buildStateRegistry(uidlNode, stateDefinitions) {
305
305
  var usages = [];
306
306
  var byDataSourceId = new Map();
307
307
  var byArrayMapperRenderProp = new Map();
@@ -358,16 +358,19 @@ function buildStateRegistry(uidlNode) {
358
358
  // is skipped for a dynamic sort) — the pre-rendered HTML carried no
359
359
  // posts, and the browser fetched page 1 a second time. Only a sort
360
360
  // bound to state (the admin panel's sortable columns) is dynamic.
361
- var dynamicSort = void 0;
362
- if ((!sorts || sorts.length === 0) && content.sort) {
363
- var fixedSort = (0, sort_utils_1.readFixedSort)(content.sort, content.sortDirection);
364
- if (fixedSort) {
365
- sorts = [fixedSort];
366
- }
367
- else {
368
- dynamicSort = (0, sort_utils_1.extractDynamicSort)(content.sort, content.sortDirection);
369
- }
361
+ //
362
+ // A sort the visitor picks still starts somewhere — the store's sort
363
+ // dropdown at "Name A-Z". Known at build time, it orders the
364
+ // server-side first page, which the list then shows while the visitor
365
+ // leaves the sort alone. Read as a plain list's page fetch reads it.
366
+ var repeaterSort = !sorts || sorts.length === 0
367
+ ? (0, sort_utils_1.readRepeaterSort)(content.sort, content.sortDirection, stateDefinitions)
368
+ : {};
369
+ if (repeaterSort.fixed) {
370
+ sorts = [repeaterSort.fixed];
370
371
  }
372
+ var dynamicSort = repeaterSort.dynamic;
373
+ var startingSort = repeaterSort.starting;
371
374
  // Extract filters from parent's resource params, keeping the group tree
372
375
  // the inspector authored (see `readFilterTree`).
373
376
  var filters = [];
@@ -450,6 +453,7 @@ function buildStateRegistry(uidlNode) {
450
453
  queryColumns: queryColumns,
451
454
  sorts: sorts,
452
455
  dynamicSort: dynamicSort,
456
+ startingSort: startingSort,
453
457
  filters: filters,
454
458
  filterStateIds: filterStateIds,
455
459
  filterPropIds: filterPropIds,
@@ -806,7 +810,7 @@ function pushFilterSortPageResetEffect(stateDeclarations, effectStatements, usag
806
810
  // ==================== MAIN PLUGIN ====================
807
811
  var createNextArrayMapperPaginationPlugin = function () {
808
812
  var paginationPlugin = function (structure) { return __awaiter(void 0, void 0, void 0, function () {
809
- var uidl, chunks, dependencies, options, componentChunk, variableDeclaration, declarator, arrowFunction, blockStatement, rangeControls, hasRangeControl, registry, getStaticPropsChunk, isPage, localized, stateDeclarations, effectStatements, needsUseRouter, returnIndex, insertIndex, dataProviders, dataProvidersWithRepeaters, usageIndexByDataSourceId, loadingStateDeclarations, usagesWithLoadingState, usageByProviderNode, cacheDeclarations, sharedOptions, cachedScopes, dataProvidersWithoutRepeaters, searchInputs, usageBySearchInput, _i, searchInputs_1, input, usage;
813
+ var uidl, chunks, dependencies, options, componentChunk, variableDeclaration, declarator, arrowFunction, blockStatement, rangeControls, hasRangeControl, registry, getStaticPropsChunk, isPage, localized, stateDeclarations, effectStatements, needsUseRouter, returnIndex, insertIndex, dataProviders, dataProvidersWithRepeaters, usageIndexByDataSourceId, loadingStateDeclarations, usagesWithLoadingState, usageByProviderNode, cacheDeclarations, firstPageDeclarations, sharedOptions, cachedScopes, dataProvidersWithoutRepeaters, searchInputs, usageBySearchInput, _i, searchInputs_1, input, usage;
810
814
  var _a, _b;
811
815
  return __generator(this, function (_c) {
812
816
  uidl = structure.uidl, chunks = structure.chunks, dependencies = structure.dependencies, options = structure.options;
@@ -832,7 +836,7 @@ var createNextArrayMapperPaginationPlugin = function () {
832
836
  blockStatement = arrowFunction.body;
833
837
  rangeControls = (0, range_control_1.findRangeControls)(blockStatement);
834
838
  hasRangeControl = rangeControls.length > 0;
835
- registry = buildStateRegistry(uidl.node);
839
+ registry = buildStateRegistry(uidl.node, uidl.stateDefinitions);
836
840
  if (registry.usages.length === 0) {
837
841
  return [2 /*return*/, structure];
838
842
  }
@@ -1167,6 +1171,7 @@ var createNextArrayMapperPaginationPlugin = function () {
1167
1171
  usagesWithLoadingState = new Set();
1168
1172
  usageByProviderNode = new Map();
1169
1173
  cacheDeclarations = [];
1174
+ firstPageDeclarations = isPage ? [] : undefined;
1170
1175
  dataProvidersWithRepeaters.forEach(function (dp) {
1171
1176
  var _a, _b;
1172
1177
  var nameAttr = dp.openingElement.attributes.find(function (attr) { return attr.type === 'JSXAttribute' && attr.name.name === 'name'; });
@@ -1187,10 +1192,10 @@ var createNextArrayMapperPaginationPlugin = function () {
1187
1192
  var fileName = (0, utils_1.generateSafeFileName)(usage.resourceDefinition.dataSourceType, usage.resourceDefinition.tableName, usage.resourceDefinition.dataSourceId);
1188
1193
  // Update DataProvider based on category
1189
1194
  if (usage.category === 'paginated+search') {
1190
- updateDataProviderForPaginatedSearch(dp, usage, vars, fileName, cacheDeclarations, localized);
1195
+ updateDataProviderForPaginatedSearch(dp, usage, vars, fileName, cacheDeclarations, localized, firstPageDeclarations);
1191
1196
  }
1192
1197
  else if (usage.category === 'paginated-only') {
1193
- updateDataProviderForPaginationOnly(dp, usage, vars, fileName, cacheDeclarations, localized);
1198
+ updateDataProviderForPaginationOnly(dp, usage, vars, fileName, cacheDeclarations, localized, firstPageDeclarations);
1194
1199
  }
1195
1200
  else if (usage.category === 'search-only') {
1196
1201
  updateDataProviderForSearchOnly(dp, usage, vars, fileName, cacheDeclarations, localized);
@@ -1202,9 +1207,14 @@ var createNextArrayMapperPaginationPlugin = function () {
1202
1207
  // change refetches, instead of leaving the previous rows on screen with
1203
1208
  // no feedback. Runs last so it sees the `fetchData` and
1204
1209
  // `persistDataDuringLoading` attributes the updaters above just wrote.
1210
+ // An appending list keeps its rows on screen while its next page loads;
1211
+ // only a list loading afresh falls through to the loading slot.
1205
1212
  var loadingVars = (0, loading_state_1.getLoadingStateVars)(usage.index);
1206
- if ((0, loading_state_1.applyLoadingStateToDataProvider)(dp, loadingVars)) {
1207
- loadingStateDeclarations.push.apply(loadingStateDeclarations, (0, loading_state_1.buildLoadingStateDeclarations)(loadingVars));
1213
+ var appendingRequest = usage.infiniteScroll && usage.paginated
1214
+ ? (0, infinite_scroll_1.buildAppendingRequestCheck)((0, infinite_scroll_1.getInfiniteScrollVars)(usage.index))
1215
+ : undefined;
1216
+ if ((0, loading_state_1.applyLoadingStateToDataProvider)(dp, loadingVars, appendingRequest)) {
1217
+ loadingStateDeclarations.push.apply(loadingStateDeclarations, (0, loading_state_1.buildLoadingStateDeclarations)(loadingVars, !!appendingRequest));
1208
1218
  // Recorded because the loading state is only declared for a provider
1209
1219
  // that HAS a loading slot; a Load More button wired later may only
1210
1220
  // reference `ds_N_isFetching` if it actually exists.
@@ -1249,6 +1259,11 @@ var createNextArrayMapperPaginationPlugin = function () {
1249
1259
  teleport_shared_1.ASTStatementOrder.insertStatementsAfterDependencies(blockStatement, cacheDeclarations);
1250
1260
  (0, ast_1.registerCacheClientImports)(dependencies, (_a = uidl.outputOptions) === null || _a === void 0 ? void 0 : _a.folderPath);
1251
1261
  }
1262
+ // After the cache declarations: a cached list's first page reads the
1263
+ // `ds_N_params` they hoisted.
1264
+ if (firstPageDeclarations === null || firstPageDeclarations === void 0 ? void 0 : firstPageDeclarations.length) {
1265
+ teleport_shared_1.ASTStatementOrder.insertStatementsAfterDependencies(blockStatement, firstPageDeclarations);
1266
+ }
1252
1267
  dataProvidersWithoutRepeaters = dataProviders.filter(function (dp) {
1253
1268
  var hasRepeater = findArrayMapperRenderPropInDataProvider(dp) !== undefined;
1254
1269
  return !hasRepeater;
@@ -1309,9 +1324,11 @@ var createNextArrayMapperPaginationPlugin = function () {
1309
1324
  wireRangeControlFor(control, owner, getStateVarsForUsage(owner));
1310
1325
  }
1311
1326
  });
1312
- // STEP 6: Update getStaticProps if this is a page
1327
+ // STEP 6: Update getStaticProps if this is a page. Its rows are what the
1328
+ // page renders, so they are read as a visitor is served them — unless only
1329
+ // the store's trusted readers can open the page (`pageDataReadArguments`).
1313
1330
  if (isPage) {
1314
- updateGetStaticProps(chunks, registry, dependencies, (_b = uidl.outputOptions) === null || _b === void 0 ? void 0 : _b.folderPath, localized);
1331
+ updateGetStaticProps(chunks, registry, dependencies, (_b = uidl.outputOptions) === null || _b === void 0 ? void 0 : _b.folderPath, localized, function () { return teleport_plugin_common_1.RouteUtils.pageDataReadArguments(uidl, options.auth); });
1315
1332
  }
1316
1333
  return [2 /*return*/, structure];
1317
1334
  });
@@ -1571,34 +1588,97 @@ function findPaginationNodesWithOwners(blockStatement, usageByProviderNode) {
1571
1588
  *
1572
1589
  * An uncached mapper keeps the inline `params={useMemo(...)}` it has always had,
1573
1590
  * byte for byte. A cached one hoists that memo into a component const so the
1574
- * peek can depend on the same object identity instead of rebuilding it.
1591
+ * peek can depend on the same object identity instead of rebuilding it; so does
1592
+ * an appending list that starts from its page's rows (`hoistInto`), whose first
1593
+ * page is filed in the accumulator under those params.
1575
1594
  *
1576
- * Returns the identifier holding the peeked rows, or `undefined` when this
1577
- * mapper does not cache.
1595
+ * Returns the identifier holding the params when they were hoisted, and the one
1596
+ * holding the peeked rows when this mapper caches.
1578
1597
  */
1579
1598
  function pushParamsAttribute(
1580
1599
  // tslint:disable-next-line:no-any
1581
- dp, usage, paramsProps, memoDeps, cacheDeclarations) {
1600
+ dp, usage, paramsProps, memoDeps, cacheDeclarations, hoistInto) {
1582
1601
  var _a;
1583
1602
  var paramsMemo = types.callExpression(types.identifier('useMemo'), [
1584
1603
  types.arrowFunctionExpression([], types.objectExpression(paramsProps)),
1585
1604
  types.arrayExpression(memoDeps),
1586
1605
  ]);
1587
- // A state-bound sort deliberately suppresses `initialData` (see the comment at
1588
- // the call site): the prefetch ran without sort params, so reusing anything on
1589
- // mount would mask the current sort. The cache peek is `initialData` by
1590
- // another name, so it has to be suppressed for exactly the same reason.
1606
+ // A state-bound sort gets no cache peek. The peek is `initialData` by another
1607
+ // name, and such a list takes the server's rows only while its sort is still
1608
+ // where the visitor starts (`buildStartingSortGuard`); the peek was never
1609
+ // extended to it.
1591
1610
  var cache = ((_a = usage.cache) === null || _a === void 0 ? void 0 : _a.client) && !usage.dynamicSort ? usage.cache : undefined;
1592
1611
  if (!cache) {
1593
- dp.openingElement.attributes.push(types.jsxAttribute(types.jsxIdentifier('params'), types.jsxExpressionContainer(paramsMemo)));
1594
- return undefined;
1612
+ if (!hoistInto) {
1613
+ dp.openingElement.attributes.push(types.jsxAttribute(types.jsxIdentifier('params'), types.jsxExpressionContainer(paramsMemo)));
1614
+ return {};
1615
+ }
1616
+ var paramsIdentifier = (0, ast_1.paramsVarFor)(usage.index);
1617
+ hoistInto.push(types.variableDeclaration('const', [
1618
+ types.variableDeclarator(types.identifier(paramsIdentifier), paramsMemo),
1619
+ ]));
1620
+ dp.openingElement.attributes.push(types.jsxAttribute(types.jsxIdentifier('params'), types.jsxExpressionContainer(types.identifier(paramsIdentifier))));
1621
+ return { paramsIdentifier: paramsIdentifier };
1595
1622
  }
1596
1623
  var hoisted = (0, ast_1.buildCachedParamsDeclarations)({ index: usage.index, paramsMemo: paramsMemo, cache: cache });
1597
1624
  cacheDeclarations.push.apply(cacheDeclarations, hoisted.declarations);
1598
1625
  dp.openingElement.attributes.push(types.jsxAttribute(types.jsxIdentifier('params'), types.jsxExpressionContainer(types.identifier(hoisted.paramsIdentifier))));
1599
- return hoisted.cachedIdentifier;
1626
+ return { paramsIdentifier: hoisted.paramsIdentifier, cachedIdentifier: hoisted.cachedIdentifier };
1627
+ }
1628
+ // Whether the rows `getStaticProps` fetched can be this list's first page at
1629
+ // all. A sort bound to state can only be served them when the build knows the
1630
+ // sort the visitor starts with (`startingSort`) and fetched by it; otherwise
1631
+ // the fetch has no sort, and its rows are not the list's first page.
1632
+ function prefetchFollowsSort(usage) {
1633
+ return !usage.dynamicSort || !!usage.startingSort;
1634
+ }
1635
+ // `sortBy === "name-asc"`: the visitor has not moved the sort from where it
1636
+ // starts, so the build-time rows are still its first page. `null` for a list
1637
+ // whose sort the visitor cannot pick, or one that reads no state.
1638
+ function buildStartingSortGuard(usage) {
1639
+ return usage.dynamicSort && usage.startingSort ? (0, sort_utils_1.buildSortAtStartGuard)(usage.startingSort) : null;
1640
+ }
1641
+ // `<condition> && <starting-sort guard>`, or the condition alone.
1642
+ function withStartingSortGuard(condition, usage) {
1643
+ var sortGuard = buildStartingSortGuard(usage);
1644
+ return sortGuard ? types.logicalExpression('&&', condition, sortGuard) : condition;
1645
+ }
1646
+ // An appending list on a page starts from the rows its `getStaticProps`
1647
+ // fetched, when they can be its first page (`prefetchFollowsSort`).
1648
+ function startsFromServerRows(usage) {
1649
+ return usage.infiniteScroll && usage.paginated && prefetchFollowsSort(usage);
1600
1650
  }
1601
- function updateDataProviderForPaginatedSearch(dp, usage, vars, fileName, cacheDeclarations, localized) {
1651
+ /**
1652
+ * `initialData={ds_N_firstPage}` on an appending list, beside the frozen first
1653
+ * page and the effect that files it as page 1 of the accumulator (see
1654
+ * `infinite-scroll.ts`).
1655
+ *
1656
+ * The rows stand in for page 1 only while they answer the list's query: no
1657
+ * search (`queryGuard`), the sort where the visitor starts, no filter set (the
1658
+ * guards the paged lists use), and at least one row — an empty build-time
1659
+ * answer, which a failed fetch also gives, leaves the browser to fetch page 1
1660
+ * as before.
1661
+ */
1662
+ function pushInfiniteScrollFirstPage(
1663
+ // tslint:disable-next-line:no-any
1664
+ dp, usage, vars, paramsIdentifier, declarations, queryGuard, instanceDeps) {
1665
+ var infiniteVars = (0, infinite_scroll_1.getInfiniteScrollVars)(usage.index);
1666
+ var serverPayload = function () {
1667
+ return types.optionalMemberExpression(types.identifier('props'), types.identifier(vars.propsPrefix), false, true);
1668
+ };
1669
+ var hasServerRows = types.binaryExpression('>', types.optionalMemberExpression((0, infinite_scroll_1.buildServerRowsExpression)(serverPayload(), usage.itemsPath), types.identifier('length'), false, true), types.numericLiteral(0));
1670
+ var startsFromServer = [
1671
+ queryGuard,
1672
+ buildStartingSortGuard(usage),
1673
+ buildNoUrlFilterGuard(usage),
1674
+ hasServerRows,
1675
+ ]
1676
+ .filter(function (guard) { return guard !== null; })
1677
+ .reduce(function (all, guard) { return types.logicalExpression('&&', all, guard); });
1678
+ declarations.push((0, infinite_scroll_1.buildFirstPageDeclaration)(infiniteVars, startsFromServer, serverPayload(), instanceDeps), (0, infinite_scroll_1.buildFirstPageSeedEffect)(infiniteVars, paramsIdentifier, usage.perPage, usage.itemsPath));
1679
+ dp.openingElement.attributes.push(types.jsxAttribute(types.jsxIdentifier('initialData'), types.jsxExpressionContainer(types.identifier(infiniteVars.firstPageVar))));
1680
+ }
1681
+ function updateDataProviderForPaginatedSearch(dp, usage, vars, fileName, cacheDeclarations, localized, firstPageDeclarations) {
1602
1682
  var attrs = dp.openingElement.attributes;
1603
1683
  // Remove existing params, key, initialData, fetchData, persistDataDuringLoading
1604
1684
  dp.openingElement.attributes = attrs.filter(function (attr) {
@@ -1631,20 +1711,29 @@ function updateDataProviderForPaginatedSearch(dp, usage, vars, fileName, cacheDe
1631
1711
  if (localized) {
1632
1712
  (0, request_locale_1.appendClientLocaleParam)(paramsProps, memoDeps);
1633
1713
  }
1634
- var cachedInitialData = pushParamsAttribute(dp, usage, paramsProps, memoDeps, cacheDeclarations);
1635
- // Add initialData. Skip the server-prefetched data entirely when a dynamic
1636
- // state-bound sort is active — the prefetch ran without sort parameters, so
1637
- // reusing it would mask the current sort state AND cause DataProvider to skip
1638
- // the first client fetch (its internal guard only skips when initialData is
1639
- // defined on mount). Without that skip, toggling sort correctly triggers a
1640
- // refetch via the useMemo params dependency chain.
1714
+ var seedInto = startsFromServerRows(usage) ? firstPageDeclarations : undefined;
1715
+ var _a = pushParamsAttribute(dp, usage, paramsProps, memoDeps, cacheDeclarations, seedInto), paramsIdentifier = _a.paramsIdentifier, cachedInitialData = _a.cachedIdentifier;
1716
+ // Add initialData: the server-prefetched page 1 while it is the page on show
1717
+ // — page 1, no search, no filter set, and a sort the visitor picks still where
1718
+ // they start (the prefetch ran by that sort). A list whose starting sort the
1719
+ // build cannot know never takes it: that prefetch ran without a sort, so
1720
+ // reusing it would mask the current sort AND make DataProvider skip its first
1721
+ // client fetch (its internal guard only skips when initialData is defined on
1722
+ // mount). Any other sort is fetched fresh, through the useMemo params.
1641
1723
  //
1642
- // An appending list skips it for a second reason: `initialData` makes
1643
- // DataProvider skip its first fetch, so page 1 would render from the prefetch
1644
- // and never enter the accumulator — appending page 2 would then REPLACE the
1645
- // visible rows with page 2 alone.
1646
- if (!usage.dynamicSort && !usage.infiniteScroll) {
1647
- var initialDataCondition = wrapInitialDataWithUrlFilterGuard(types.logicalExpression('&&', types.binaryExpression('===', types.memberExpression(types.identifier(vars.combinedStateVar), types.identifier('page')), types.numericLiteral(1)), types.unaryExpression('!', types.memberExpression(types.identifier(vars.combinedStateVar), types.identifier('debouncedQuery')), true)), usage);
1724
+ // An appending list takes its own: `initialData` makes DataProvider skip its
1725
+ // first fetch, so the prefetched page 1 has to be filed in the accumulator
1726
+ // too, or appending page 2 would REPLACE the visible rows with page 2 alone.
1727
+ // The provider remounts per query (its `key`), so the first page is frozen
1728
+ // per query.
1729
+ if (seedInto && paramsIdentifier) {
1730
+ var appliedQuery = function () {
1731
+ return types.memberExpression(types.identifier(vars.combinedStateVar), types.identifier('debouncedQuery'));
1732
+ };
1733
+ pushInfiniteScrollFirstPage(dp, usage, vars, paramsIdentifier, seedInto, types.unaryExpression('!', appliedQuery(), true), [appliedQuery()]);
1734
+ }
1735
+ else if (prefetchFollowsSort(usage) && !usage.infiniteScroll) {
1736
+ var initialDataCondition = wrapInitialDataWithUrlFilterGuard(withStartingSortGuard(types.logicalExpression('&&', types.binaryExpression('===', types.memberExpression(types.identifier(vars.combinedStateVar), types.identifier('page')), types.numericLiteral(1)), types.unaryExpression('!', types.memberExpression(types.identifier(vars.combinedStateVar), types.identifier('debouncedQuery')), true)), usage), usage);
1648
1737
  dp.openingElement.attributes.push(types.jsxAttribute(types.jsxIdentifier('initialData'), types.jsxExpressionContainer(types.conditionalExpression(initialDataCondition, types.optionalMemberExpression(types.identifier('props'), types.identifier(vars.propsPrefix), false, true), cachedInitialData ? types.identifier(cachedInitialData) : types.identifier('undefined')))));
1649
1738
  }
1650
1739
  // Add key. Sort is intentionally NOT part of the key — a key change would
@@ -1682,7 +1771,7 @@ function updateDataProviderForPaginatedSearch(dp, usage, vars, fileName, cacheDe
1682
1771
  // Add persistDataDuringLoading
1683
1772
  dp.openingElement.attributes.push(types.jsxAttribute(types.jsxIdentifier('persistDataDuringLoading'), types.jsxExpressionContainer(types.booleanLiteral(true))));
1684
1773
  }
1685
- function updateDataProviderForPaginationOnly(dp, usage, vars, fileName, cacheDeclarations, localized) {
1774
+ function updateDataProviderForPaginationOnly(dp, usage, vars, fileName, cacheDeclarations, localized, firstPageDeclarations) {
1686
1775
  var attrs = dp.openingElement.attributes;
1687
1776
  dp.openingElement.attributes = attrs.filter(function (attr) {
1688
1777
  var _a;
@@ -1711,12 +1800,17 @@ function updateDataProviderForPaginationOnly(dp, usage, vars, fileName, cacheDec
1711
1800
  (0, request_locale_1.appendClientLocaleParam)(paramsProps, memoDeps);
1712
1801
  }
1713
1802
  // Add params
1714
- var cachedInitialData = pushParamsAttribute(dp, usage, paramsProps, memoDeps, cacheDeclarations);
1715
- // Add initialData. See paginated+search updater for why we skip prefetch
1716
- // reuse when a dynamic state-bound sort is active, and why an appending list
1717
- // skips it too.
1718
- if (!usage.dynamicSort && !usage.infiniteScroll) {
1719
- dp.openingElement.attributes.push(types.jsxAttribute(types.jsxIdentifier('initialData'), types.jsxExpressionContainer(types.conditionalExpression(wrapInitialDataWithUrlFilterGuard(types.binaryExpression('===', types.identifier(vars.pageStateVar), types.numericLiteral(1)), usage), types.optionalMemberExpression(types.identifier('props'), types.identifier(vars.propsPrefix), false, true), cachedInitialData ? types.identifier(cachedInitialData) : types.identifier('undefined')))));
1803
+ var seedInto = startsFromServerRows(usage) ? firstPageDeclarations : undefined;
1804
+ var _a = pushParamsAttribute(dp, usage, paramsProps, memoDeps, cacheDeclarations, seedInto), paramsIdentifier = _a.paramsIdentifier, cachedInitialData = _a.cachedIdentifier;
1805
+ // Add initialData. See paginated+search updater for when a sort the visitor
1806
+ // picks may reuse the prefetch, and how an appending list takes its own. This
1807
+ // provider never remounts (no `key`), so its first page is frozen for the
1808
+ // page's lifetime.
1809
+ if (seedInto && paramsIdentifier) {
1810
+ pushInfiniteScrollFirstPage(dp, usage, vars, paramsIdentifier, seedInto, null, []);
1811
+ }
1812
+ else if (prefetchFollowsSort(usage) && !usage.infiniteScroll) {
1813
+ dp.openingElement.attributes.push(types.jsxAttribute(types.jsxIdentifier('initialData'), types.jsxExpressionContainer(types.conditionalExpression(wrapInitialDataWithUrlFilterGuard(withStartingSortGuard(types.binaryExpression('===', types.identifier(vars.pageStateVar), types.numericLiteral(1)), usage), usage), types.optionalMemberExpression(types.identifier('props'), types.identifier(vars.propsPrefix), false, true), cachedInitialData ? types.identifier(cachedInitialData) : types.identifier('undefined')))));
1720
1814
  }
1721
1815
  // Add key — sort is intentionally NOT included; see paginated+search updater,
1722
1816
  // which also explains why an appending list keys on nothing at all (there is
@@ -1766,11 +1860,11 @@ function updateDataProviderForSearchOnly(dp, usage, vars, fileName, cacheDeclara
1766
1860
  if (localized) {
1767
1861
  (0, request_locale_1.appendClientLocaleParam)(paramsProps, memoDeps);
1768
1862
  }
1769
- var cachedInitialData = pushParamsAttribute(dp, usage, paramsProps, memoDeps, cacheDeclarations);
1770
- // Add initialData. See paginated+search updater for why we skip prefetch
1771
- // reuse when a dynamic state-bound sort is active.
1772
- if (!usage.dynamicSort) {
1773
- dp.openingElement.attributes.push(types.jsxAttribute(types.jsxIdentifier('initialData'), types.jsxExpressionContainer(types.conditionalExpression(wrapInitialDataWithUrlFilterGuard(types.unaryExpression('!', types.identifier(vars.debouncedSearchQueryVar), true), usage), types.optionalMemberExpression(types.identifier('props'), types.identifier(vars.propsPrefix), false, true), cachedInitialData ? types.identifier(cachedInitialData) : types.identifier('undefined')))));
1863
+ var cachedInitialData = pushParamsAttribute(dp, usage, paramsProps, memoDeps, cacheDeclarations).cachedIdentifier;
1864
+ // Add initialData. See paginated+search updater for when a sort the visitor
1865
+ // picks may reuse the prefetch.
1866
+ if (prefetchFollowsSort(usage)) {
1867
+ dp.openingElement.attributes.push(types.jsxAttribute(types.jsxIdentifier('initialData'), types.jsxExpressionContainer(types.conditionalExpression(wrapInitialDataWithUrlFilterGuard(withStartingSortGuard(types.unaryExpression('!', types.identifier(vars.debouncedSearchQueryVar), true), usage), usage), types.optionalMemberExpression(types.identifier('props'), types.identifier(vars.propsPrefix), false, true), cachedInitialData ? types.identifier(cachedInitialData) : types.identifier('undefined')))));
1774
1868
  }
1775
1869
  // Add key — sort is intentionally NOT included; see paginated+search updater.
1776
1870
  dp.openingElement.attributes.push(types.jsxAttribute(types.jsxIdentifier('key'), types.jsxExpressionContainer(types.templateLiteral([
@@ -1781,14 +1875,42 @@ function updateDataProviderForSearchOnly(dp, usage, vars, fileName, cacheDeclara
1781
1875
  dp.openingElement.attributes.push(createFetchDataAttribute(fileName, ((_a = usage.cache) === null || _a === void 0 ? void 0 : _a.client) ? usage.cache : undefined));
1782
1876
  dp.openingElement.attributes.push(types.jsxAttribute(types.jsxIdentifier('persistDataDuringLoading'), types.jsxExpressionContainer(types.booleanLiteral(true))));
1783
1877
  }
1878
+ /**
1879
+ * A plain list on a page is seeded with the rows its `getStaticProps` fetched
1880
+ * (`extractDataSourceIntoGetStaticProps`), and a seeded provider skips its first
1881
+ * fetch. With a sort the visitor picks, those rows are the list's only while the
1882
+ * sort is where a visitor starts — the build fetched them by it:
1883
+ *
1884
+ * initialData={sortBy === "name-asc" ? props.items_products : undefined}
1885
+ *
1886
+ * — and any other sort is fetched fresh, through the params it changes. When the
1887
+ * build cannot know where the sort starts (`prefetchFollowsSort`), it fetched
1888
+ * the rows unsorted, and the list is never seeded with them. A plain list has no
1889
+ * search, and is only seeded without a filter a visitor sets.
1890
+ */
1891
+ function seedPlainListWhileSortStarts(dp, usage) {
1892
+ if (!usage.dynamicSort) {
1893
+ return;
1894
+ }
1895
+ var attrs = dp.openingElement.attributes;
1896
+ var index = attrs.findIndex(function (attr) { var _a; return attr.type === 'JSXAttribute' && ((_a = attr.name) === null || _a === void 0 ? void 0 : _a.name) === 'initialData'; });
1897
+ if (index === -1) {
1898
+ return;
1899
+ }
1900
+ if (!prefetchFollowsSort(usage)) {
1901
+ attrs.splice(index, 1);
1902
+ return;
1903
+ }
1904
+ var sortGuard = buildStartingSortGuard(usage);
1905
+ var seeded = attrs[index].value;
1906
+ if (!sortGuard || (seeded === null || seeded === void 0 ? void 0 : seeded.type) !== 'JSXExpressionContainer') {
1907
+ return;
1908
+ }
1909
+ attrs[index] = types.jsxAttribute(types.jsxIdentifier('initialData'), types.jsxExpressionContainer(types.conditionalExpression(sortGuard, seeded.expression, types.identifier('undefined'))));
1910
+ }
1784
1911
  function updateDataProviderForPlain(dp, fileName, usage, localized) {
1785
1912
  var _a, _b, _c, _d, _e, _f;
1786
- // A sort bound to state is the visitor's, which the server cannot know: rows
1787
- // pre-rendered in the table's order would stand in for it, since a seeded
1788
- // provider skips its first fetch — the paginated lists drop them the same way.
1789
- if (usage.dynamicSort) {
1790
- dp.openingElement.attributes = dp.openingElement.attributes.filter(function (attr) { var _a; return !(attr.type === 'JSXAttribute' && ((_a = attr.name) === null || _a === void 0 ? void 0 : _a.name) === 'initialData'); });
1791
- }
1913
+ seedPlainListWhileSortStarts(dp, usage);
1792
1914
  var attrs = dp.openingElement.attributes;
1793
1915
  var clientCache = ((_a = usage.cache) === null || _a === void 0 ? void 0 : _a.client) ? usage.cache : undefined;
1794
1916
  // Check if fetchData already exists
@@ -2242,7 +2364,15 @@ function ensureAPIRouteExists(extractedResources, usage, dataSources, transformO
2242
2364
  };
2243
2365
  }
2244
2366
  }
2245
- function updateGetStaticProps(chunks, registry, dependencies, folderPath, localized) {
2367
+ // `fetchCount(<params>, <read arguments>)`: the params may be left out only
2368
+ // when nothing follows them.
2369
+ var countReadArguments = function (countParams, readArguments) {
2370
+ return countParams.length > 0 || readArguments.length > 0
2371
+ ? __spreadArray([types.objectExpression(countParams)], readArguments, true) : [];
2372
+ };
2373
+ function updateGetStaticProps(chunks, registry, dependencies, folderPath, localized,
2374
+ // The arguments every read hands the module after its params (fresh nodes per call).
2375
+ readArguments) {
2246
2376
  var _a;
2247
2377
  var getStaticPropsChunk = chunks.find(function (c) { return c.name === 'getStaticProps'; });
2248
2378
  if (!getStaticPropsChunk || getStaticPropsChunk.type !== teleport_types_1.ChunkType.AST) {
@@ -2323,6 +2453,12 @@ function updateGetStaticProps(chunks, registry, dependencies, folderPath, locali
2323
2453
  })),
2324
2454
  ])));
2325
2455
  }
2456
+ else if (usage.dynamicSort && usage.startingSort) {
2457
+ // A sort the visitor picks: page 1 by the sort they start with, sent
2458
+ // as the very string the browser sends for it, so page 2 continues
2459
+ // this page exactly (see `buildStartingSortsValue`).
2460
+ fetchParams.push(types.objectProperty(types.identifier('sorts'), (0, sort_utils_1.buildStartingSortsValue)(usage.dynamicSort, usage.startingSort)));
2461
+ }
2326
2462
  // Add filters if present
2327
2463
  // Filter out dynamic destinations for getStaticProps (they can't be resolved at build time)
2328
2464
  var staticFilters = staticFiltersOf(usage);
@@ -2338,7 +2474,7 @@ function updateGetStaticProps(chunks, registry, dependencies, folderPath, locali
2338
2474
  var existingFetchIndex = arrayPattern.elements.findIndex(function (el) { return (el === null || el === void 0 ? void 0 : el.type) === 'Identifier' && el.name === vars.propsPrefix; });
2339
2475
  if (existingFetchIndex === -1) {
2340
2476
  arrayPattern.elements.push(types.identifier(vars.propsPrefix));
2341
- fetchesArray.elements.push(types.callExpression(types.memberExpression(types.callExpression(types.memberExpression(types.identifier(fetcherImportName), types.identifier('fetchData')), [types.objectExpression(fetchParams)]), types.identifier('catch')), [
2477
+ fetchesArray.elements.push(types.callExpression(types.memberExpression(types.callExpression(types.memberExpression(types.identifier(fetcherImportName), types.identifier('fetchData')), __spreadArray([types.objectExpression(fetchParams)], readArguments(), true)), types.identifier('catch')), [
2342
2478
  types.arrowFunctionExpression([types.identifier('error')], types.blockStatement([
2343
2479
  types.expressionStatement(types.callExpression(types.memberExpression(types.identifier('console'), types.identifier('error')), [
2344
2480
  types.stringLiteral("Error fetching ".concat(vars.propsPrefix, ":")),
@@ -2441,7 +2577,7 @@ function updateGetStaticProps(chunks, registry, dependencies, folderPath, locali
2441
2577
  countParams.push(types.objectProperty(types.identifier('filters'), (0, filter_utils_1.buildStaticFiltersStringifyCall)(staticFiltersForCount)));
2442
2578
  }
2443
2579
  (0, items_path_1.appendItemsPathParam)(countParams, firstUsage.itemsPath, 'collectionPath');
2444
- fetchesArray.elements.push(types.callExpression(types.memberExpression(types.callExpression(types.memberExpression(types.identifier(fetcherImportName), types.identifier('fetchCount')), countParams.length > 0 ? [types.objectExpression(countParams)] : []), types.identifier('catch')), [
2580
+ fetchesArray.elements.push(types.callExpression(types.memberExpression(types.callExpression(types.memberExpression(types.identifier(fetcherImportName), types.identifier('fetchCount')), countReadArguments(countParams, readArguments())), types.identifier('catch')), [
2445
2581
  types.arrowFunctionExpression([types.identifier('error')], types.blockStatement([
2446
2582
  types.expressionStatement(types.callExpression(types.memberExpression(types.identifier('console'), types.identifier('error')), [
2447
2583
  types.stringLiteral("Error fetching ".concat(countVarName, ":")),