@teleporthq/teleport-plugin-next-data-source 0.43.75 → 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 (199) hide show
  1. package/__tests__/_helpers/first-page-runtime.ts +184 -0
  2. package/__tests__/_helpers/pagination-widget-fixture.ts +154 -5
  3. package/__tests__/booking-item-transform.test.ts +120 -0
  4. package/__tests__/browser-row-policy.test.ts +210 -4
  5. package/__tests__/data-source-request-locale.test.ts +4 -3
  6. package/__tests__/fetchers.test.ts +7 -3
  7. package/__tests__/fixed-list-sort.test.ts +1 -1
  8. package/__tests__/infinite-scroll.test.ts +363 -9
  9. package/__tests__/list-sort-order.test.ts +61 -0
  10. package/__tests__/null-filter-where.test.ts +132 -0
  11. package/__tests__/page-data-visitor-read.test.ts +196 -0
  12. package/__tests__/pg-date-parser.test.ts +73 -0
  13. package/__tests__/plain-list-sort.test.ts +2 -2
  14. package/__tests__/plain-list-starting-sort.test.ts +323 -0
  15. package/__tests__/product-price-sort.ts +11 -6
  16. package/__tests__/range-control.test.ts +159 -0
  17. package/__tests__/refetch-loading-state.test.ts +158 -2
  18. package/__tests__/search-input-marker.test.ts +86 -0
  19. package/__tests__/search-url-sync.test.ts +34 -8
  20. package/__tests__/starting-sort-first-page.test.ts +285 -0
  21. package/__tests__/table-read-route-protected-tables.test.ts +33 -3
  22. package/dist/cjs/cache/ast.d.ts +2 -0
  23. package/dist/cjs/cache/ast.d.ts.map +1 -1
  24. package/dist/cjs/cache/ast.js +5 -2
  25. package/dist/cjs/cache/ast.js.map +1 -1
  26. package/dist/cjs/count-effect.d.ts +7 -1
  27. package/dist/cjs/count-effect.d.ts.map +1 -1
  28. package/dist/cjs/count-effect.js +15 -5
  29. package/dist/cjs/count-effect.js.map +1 -1
  30. package/dist/cjs/data-source-fetchers.d.ts.map +1 -1
  31. package/dist/cjs/data-source-fetchers.js +10 -2
  32. package/dist/cjs/data-source-fetchers.js.map +1 -1
  33. package/dist/cjs/fetchers/pg-client-import.d.ts +12 -0
  34. package/dist/cjs/fetchers/pg-client-import.d.ts.map +1 -0
  35. package/dist/cjs/fetchers/pg-client-import.js +15 -0
  36. package/dist/cjs/fetchers/pg-client-import.js.map +1 -0
  37. package/dist/cjs/fetchers/postgresql.d.ts.map +1 -1
  38. package/dist/cjs/fetchers/postgresql.js +3 -1
  39. package/dist/cjs/fetchers/postgresql.js.map +1 -1
  40. package/dist/cjs/fetchers/raw-query.d.ts.map +1 -1
  41. package/dist/cjs/fetchers/raw-query.js +2 -1
  42. package/dist/cjs/fetchers/raw-query.js.map +1 -1
  43. package/dist/cjs/fetchers/teleport.d.ts.map +1 -1
  44. package/dist/cjs/fetchers/teleport.js +4 -2
  45. package/dist/cjs/fetchers/teleport.js.map +1 -1
  46. package/dist/cjs/fetchers/utils/browser-row-policy.d.ts.map +1 -1
  47. package/dist/cjs/fetchers/utils/browser-row-policy.js +5 -1
  48. package/dist/cjs/fetchers/utils/browser-row-policy.js.map +1 -1
  49. package/dist/cjs/index.d.ts.map +1 -1
  50. package/dist/cjs/index.js +14 -2
  51. package/dist/cjs/index.js.map +1 -1
  52. package/dist/cjs/infinite-scroll.d.ts +89 -4
  53. package/dist/cjs/infinite-scroll.d.ts.map +1 -1
  54. package/dist/cjs/infinite-scroll.js +243 -33
  55. package/dist/cjs/infinite-scroll.js.map +1 -1
  56. package/dist/cjs/list-sort-order.d.ts +14 -0
  57. package/dist/cjs/list-sort-order.d.ts.map +1 -0
  58. package/dist/cjs/list-sort-order.js +36 -0
  59. package/dist/cjs/list-sort-order.js.map +1 -0
  60. package/dist/cjs/loading-state.d.ts +23 -2
  61. package/dist/cjs/loading-state.d.ts.map +1 -1
  62. package/dist/cjs/loading-state.js +50 -15
  63. package/dist/cjs/loading-state.js.map +1 -1
  64. package/dist/cjs/pagination-controls.d.ts +22 -3
  65. package/dist/cjs/pagination-controls.d.ts.map +1 -1
  66. package/dist/cjs/pagination-controls.js +29 -3
  67. package/dist/cjs/pagination-controls.js.map +1 -1
  68. package/dist/cjs/pagination-plugin.d.ts.map +1 -1
  69. package/dist/cjs/pagination-plugin.js +336 -164
  70. package/dist/cjs/pagination-plugin.js.map +1 -1
  71. package/dist/cjs/range-control.d.ts +45 -0
  72. package/dist/cjs/range-control.d.ts.map +1 -0
  73. package/dist/cjs/range-control.js +165 -0
  74. package/dist/cjs/range-control.js.map +1 -0
  75. package/dist/cjs/row-order.d.ts +3 -2
  76. package/dist/cjs/row-order.d.ts.map +1 -1
  77. package/dist/cjs/row-order.js +5 -4
  78. package/dist/cjs/row-order.js.map +1 -1
  79. package/dist/cjs/search-input.d.ts +42 -0
  80. package/dist/cjs/search-input.d.ts.map +1 -0
  81. package/dist/cjs/search-input.js +158 -0
  82. package/dist/cjs/search-input.js.map +1 -0
  83. package/dist/cjs/sort-utils.d.ts +53 -1
  84. package/dist/cjs/sort-utils.d.ts.map +1 -1
  85. package/dist/cjs/sort-utils.js +105 -13
  86. package/dist/cjs/sort-utils.js.map +1 -1
  87. package/dist/cjs/transformations/booking-item.d.ts +24 -0
  88. package/dist/cjs/transformations/booking-item.d.ts.map +1 -0
  89. package/dist/cjs/transformations/booking-item.js +27 -0
  90. package/dist/cjs/transformations/booking-item.js.map +1 -0
  91. package/dist/cjs/transformations/index.d.ts +4 -2
  92. package/dist/cjs/transformations/index.d.ts.map +1 -1
  93. package/dist/cjs/transformations/index.js +14 -5
  94. package/dist/cjs/transformations/index.js.map +1 -1
  95. package/dist/cjs/tsconfig.tsbuildinfo +1 -1
  96. package/dist/cjs/utils.d.ts +3 -2
  97. package/dist/cjs/utils.d.ts.map +1 -1
  98. package/dist/cjs/utils.js +48 -18
  99. package/dist/cjs/utils.js.map +1 -1
  100. package/dist/esm/cache/ast.d.ts +2 -0
  101. package/dist/esm/cache/ast.d.ts.map +1 -1
  102. package/dist/esm/cache/ast.js +3 -1
  103. package/dist/esm/cache/ast.js.map +1 -1
  104. package/dist/esm/count-effect.d.ts +7 -1
  105. package/dist/esm/count-effect.d.ts.map +1 -1
  106. package/dist/esm/count-effect.js +15 -5
  107. package/dist/esm/count-effect.js.map +1 -1
  108. package/dist/esm/data-source-fetchers.d.ts.map +1 -1
  109. package/dist/esm/data-source-fetchers.js +10 -2
  110. package/dist/esm/data-source-fetchers.js.map +1 -1
  111. package/dist/esm/fetchers/pg-client-import.d.ts +12 -0
  112. package/dist/esm/fetchers/pg-client-import.d.ts.map +1 -0
  113. package/dist/esm/fetchers/pg-client-import.js +12 -0
  114. package/dist/esm/fetchers/pg-client-import.js.map +1 -0
  115. package/dist/esm/fetchers/postgresql.d.ts.map +1 -1
  116. package/dist/esm/fetchers/postgresql.js +3 -1
  117. package/dist/esm/fetchers/postgresql.js.map +1 -1
  118. package/dist/esm/fetchers/raw-query.d.ts.map +1 -1
  119. package/dist/esm/fetchers/raw-query.js +2 -1
  120. package/dist/esm/fetchers/raw-query.js.map +1 -1
  121. package/dist/esm/fetchers/teleport.d.ts.map +1 -1
  122. package/dist/esm/fetchers/teleport.js +4 -2
  123. package/dist/esm/fetchers/teleport.js.map +1 -1
  124. package/dist/esm/fetchers/utils/browser-row-policy.d.ts.map +1 -1
  125. package/dist/esm/fetchers/utils/browser-row-policy.js +6 -2
  126. package/dist/esm/fetchers/utils/browser-row-policy.js.map +1 -1
  127. package/dist/esm/index.d.ts.map +1 -1
  128. package/dist/esm/index.js +14 -2
  129. package/dist/esm/index.js.map +1 -1
  130. package/dist/esm/infinite-scroll.d.ts +89 -4
  131. package/dist/esm/infinite-scroll.d.ts.map +1 -1
  132. package/dist/esm/infinite-scroll.js +237 -32
  133. package/dist/esm/infinite-scroll.js.map +1 -1
  134. package/dist/esm/list-sort-order.d.ts +14 -0
  135. package/dist/esm/list-sort-order.d.ts.map +1 -0
  136. package/dist/esm/list-sort-order.js +32 -0
  137. package/dist/esm/list-sort-order.js.map +1 -0
  138. package/dist/esm/loading-state.d.ts +23 -2
  139. package/dist/esm/loading-state.d.ts.map +1 -1
  140. package/dist/esm/loading-state.js +50 -15
  141. package/dist/esm/loading-state.js.map +1 -1
  142. package/dist/esm/pagination-controls.d.ts +22 -3
  143. package/dist/esm/pagination-controls.d.ts.map +1 -1
  144. package/dist/esm/pagination-controls.js +28 -2
  145. package/dist/esm/pagination-controls.js.map +1 -1
  146. package/dist/esm/pagination-plugin.d.ts.map +1 -1
  147. package/dist/esm/pagination-plugin.js +340 -168
  148. package/dist/esm/pagination-plugin.js.map +1 -1
  149. package/dist/esm/range-control.d.ts +45 -0
  150. package/dist/esm/range-control.d.ts.map +1 -0
  151. package/dist/esm/range-control.js +137 -0
  152. package/dist/esm/range-control.js.map +1 -0
  153. package/dist/esm/row-order.d.ts +3 -2
  154. package/dist/esm/row-order.d.ts.map +1 -1
  155. package/dist/esm/row-order.js +5 -4
  156. package/dist/esm/row-order.js.map +1 -1
  157. package/dist/esm/search-input.d.ts +42 -0
  158. package/dist/esm/search-input.d.ts.map +1 -0
  159. package/dist/esm/search-input.js +129 -0
  160. package/dist/esm/search-input.js.map +1 -0
  161. package/dist/esm/sort-utils.d.ts +53 -1
  162. package/dist/esm/sort-utils.d.ts.map +1 -1
  163. package/dist/esm/sort-utils.js +100 -12
  164. package/dist/esm/sort-utils.js.map +1 -1
  165. package/dist/esm/transformations/booking-item.d.ts +24 -0
  166. package/dist/esm/transformations/booking-item.d.ts.map +1 -0
  167. package/dist/esm/transformations/booking-item.js +23 -0
  168. package/dist/esm/transformations/booking-item.js.map +1 -0
  169. package/dist/esm/transformations/index.d.ts +4 -2
  170. package/dist/esm/transformations/index.d.ts.map +1 -1
  171. package/dist/esm/transformations/index.js +14 -5
  172. package/dist/esm/transformations/index.js.map +1 -1
  173. package/dist/esm/tsconfig.tsbuildinfo +1 -1
  174. package/dist/esm/utils.d.ts +3 -2
  175. package/dist/esm/utils.d.ts.map +1 -1
  176. package/dist/esm/utils.js +49 -19
  177. package/dist/esm/utils.js.map +1 -1
  178. package/package.json +5 -5
  179. package/src/cache/ast.ts +4 -1
  180. package/src/count-effect.ts +31 -8
  181. package/src/data-source-fetchers.ts +33 -14
  182. package/src/fetchers/pg-client-import.ts +16 -0
  183. package/src/fetchers/postgresql.ts +15 -9
  184. package/src/fetchers/raw-query.ts +2 -1
  185. package/src/fetchers/teleport.ts +19 -10
  186. package/src/fetchers/utils/browser-row-policy.ts +79 -5
  187. package/src/index.ts +8 -2
  188. package/src/infinite-scroll.ts +420 -81
  189. package/src/list-sort-order.ts +34 -0
  190. package/src/loading-state.ts +85 -13
  191. package/src/pagination-controls.ts +26 -3
  192. package/src/pagination-plugin.ts +532 -218
  193. package/src/range-control.ts +207 -0
  194. package/src/row-order.ts +5 -4
  195. package/src/search-input.ts +190 -0
  196. package/src/sort-utils.ts +164 -21
  197. package/src/transformations/booking-item.ts +71 -0
  198. package/src/transformations/index.ts +25 -2
  199. package/src/utils.ts +55 -18
@@ -47,22 +47,24 @@ import { ChunkType, FileType, } from '@teleporthq/teleport-types';
47
47
  import * as types from '@babel/types';
48
48
  import { parseExpression } from '@babel/parser';
49
49
  import { ASTStatementOrder, GenericUtils, StringUtils } from '@teleporthq/teleport-shared';
50
- import { ASTUtils, URLQueryWriter, URLSearchParamSync } from '@teleporthq/teleport-plugin-common';
50
+ import { ASTUtils, RouteUtils, URLQueryWriter, URLSearchParamSync, } from '@teleporthq/teleport-plugin-common';
51
51
  import { collectFilterLeafConditions, generateSafeFileName, pruneFilterTree } from './utils';
52
52
  import { generateDataSourceFetcherWithCore } from './data-source-fetchers';
53
53
  import { buildProductTransformOptions } from './transformations';
54
- import { appendSortsParam, extractDynamicSort, readFixedSort } from './sort-utils';
54
+ import { appendSortsParam, buildSortAtStartGuard, buildStartingSortsValue, readRepeaterSort, } from './sort-utils';
55
55
  import { appendFiltersParam, buildStaticFiltersStringifyCall, pushStateIdsAsDeps, pushPropIdsAsDeps, } from './filter-utils';
56
56
  import { appendItemsPathParam, extractItemsPath } from './items-path';
57
57
  import { resolveRepeaterPerPage } from './repeater-page-size';
58
58
  import { applyLoadingStateToDataProvider, buildLoadingStateDeclarations, getLoadingStateVars, } from './loading-state';
59
59
  import { mergeServerCacheOptions, resolveCacheFromRepeaterContent } from './cache/config';
60
- import { buildCacheHydrationEffect, buildCacheStoreThen, buildCachedFetchBody, buildCachedParamsDeclarations, registerCacheClientImports, } from './cache/ast';
61
- import { convertControlToButton, dropInactivePaginationControls, findPaginationControl, setJSXExpressionAttribute, } from './pagination-controls';
60
+ import { buildCacheHydrationEffect, buildCacheStoreThen, buildCachedFetchBody, buildCachedParamsDeclarations, paramsVarFor, registerCacheClientImports, } from './cache/ast';
61
+ import { convertControlToButton, dropInactivePaginationControls, findAllPaginationControls, findPaginationControl, PAGINATION_FOR_ATTR, readJSXAttributeString, setJSXExpressionAttribute, } from './pagination-controls';
62
+ import { findRangeControls, wireRangeControl } from './range-control';
63
+ import { findAllSearchInputsInJSX, pairSearchInputs, wireSearchInput } from './search-input';
62
64
  import { buildCountFetchEffect, buildCountSeqDeclaration } from './count-effect';
63
65
  import { findAllSearchClearButtonsInJSX, findSiblingSearchInput, wireSearchClearButton, } from './search-clear';
64
66
  import { buildPageTokensDeclaration, wireNumberedPagination, } from './numbered-pagination';
65
- import { buildAccumulatingResponseHandler, buildInfiniteScrollDeclarations, buildSentinelElement, buildSentinelObserverEffect, getInfiniteScrollVars, } from './infinite-scroll';
67
+ import { buildAccumulatingRequestMark, buildAccumulatingResponseHandler, buildAppendingRequestCheck, buildFirstPageDeclaration, buildFirstPageSeedEffect, buildInfiniteScrollDeclarations, buildSentinelElement, buildSentinelObserverEffect, buildServerRowsExpression, getInfiniteScrollVars, } from './infinite-scroll';
66
68
  import { buildFilterSortPageResetEffect, buildPageResetRefDeclaration, hasNoPageResetDeps, } from './page-reset';
67
69
  import { buildPageUrlRefDeclaration, buildPageUrlSyncEffect } from './page-url-sync';
68
70
  import { isLocalizedProject } from './content-localization';
@@ -273,7 +275,7 @@ function buildFilterDestinationExpression(destination) {
273
275
  return ASTUtils.convertFilterDestinationToExpression(destination);
274
276
  }
275
277
  // Scan UIDL to find all data source usages and build a registry
276
- function buildStateRegistry(uidlNode) {
278
+ function buildStateRegistry(uidlNode, stateDefinitions) {
277
279
  var usages = [];
278
280
  var byDataSourceId = new Map();
279
281
  var byArrayMapperRenderProp = new Map();
@@ -330,16 +332,19 @@ function buildStateRegistry(uidlNode) {
330
332
  // is skipped for a dynamic sort) — the pre-rendered HTML carried no
331
333
  // posts, and the browser fetched page 1 a second time. Only a sort
332
334
  // bound to state (the admin panel's sortable columns) is dynamic.
333
- var dynamicSort = void 0;
334
- if ((!sorts || sorts.length === 0) && content.sort) {
335
- var fixedSort = readFixedSort(content.sort, content.sortDirection);
336
- if (fixedSort) {
337
- sorts = [fixedSort];
338
- }
339
- else {
340
- dynamicSort = extractDynamicSort(content.sort, content.sortDirection);
341
- }
335
+ //
336
+ // A sort the visitor picks still starts somewhere — the store's sort
337
+ // dropdown at "Name A-Z". Known at build time, it orders the
338
+ // server-side first page, which the list then shows while the visitor
339
+ // leaves the sort alone. Read as a plain list's page fetch reads it.
340
+ var repeaterSort = !sorts || sorts.length === 0
341
+ ? readRepeaterSort(content.sort, content.sortDirection, stateDefinitions)
342
+ : {};
343
+ if (repeaterSort.fixed) {
344
+ sorts = [repeaterSort.fixed];
342
345
  }
346
+ var dynamicSort = repeaterSort.dynamic;
347
+ var startingSort = repeaterSort.starting;
343
348
  // Extract filters from parent's resource params, keeping the group tree
344
349
  // the inspector authored (see `readFilterTree`).
345
350
  var filters = [];
@@ -407,6 +412,7 @@ function buildStateRegistry(uidlNode) {
407
412
  index: index++,
408
413
  dataSourceIdentifier: parentDataSource.identifier,
409
414
  arrayMapperRenderProp: arrayMapperRenderProp,
415
+ nodeId: typeof content.nodeId === 'string' ? content.nodeId : undefined,
410
416
  resourceDefinition: {
411
417
  dataSourceId: parentDataSource.resourceDef.dataSourceId || '',
412
418
  tableName: parentDataSource.resourceDef.tableName || '',
@@ -421,6 +427,7 @@ function buildStateRegistry(uidlNode) {
421
427
  queryColumns: queryColumns,
422
428
  sorts: sorts,
423
429
  dynamicSort: dynamicSort,
430
+ startingSort: startingSort,
424
431
  filters: filters,
425
432
  filterStateIds: filterStateIds,
426
433
  filterPropIds: filterPropIds,
@@ -502,6 +509,8 @@ function getStateVarsForUsage(usage) {
502
509
  setPageStateVar: "setDs_".concat(idx, "_page"),
503
510
  maxPagesStateVar: "ds_".concat(idx, "_maxPages"),
504
511
  setMaxPagesStateVar: "setDs_".concat(idx, "_maxPages"),
512
+ totalStateVar: "ds_".concat(idx, "_total"),
513
+ setTotalStateVar: "setDs_".concat(idx, "_total"),
505
514
  searchQueryVar: "ds_".concat(idx, "_searchQuery"),
506
515
  setSearchQueryVar: "setDs_".concat(idx, "_searchQuery"),
507
516
  debouncedSearchQueryVar: "ds_".concat(idx, "_debouncedQuery"),
@@ -579,19 +588,27 @@ function pushSearchUrlSyncEffects(effectStatements, usage, vars) {
579
588
  effectStatements.push(URLSearchParamSync.buildUrlWriteBackEffect(usage.searchUrlParamKey, buildDebouncedValueExpr(), buildDebouncedValueExpr(), undefined, adoptedRefVar));
580
589
  // read-back: `?<searchUrlParamKey>=` → the input AND the applied query at once
581
590
  // (`ds_N_adoptUrlSearch`), so an address is not a typed change: it skips the
582
- // debounce and keeps the page — `?searchKeyword=x&page=3` lands on page 3.
591
+ // debounce and keeps a page the address carries — `?searchKeyword=x&page=3`
592
+ // lands on page 3.
583
593
  effectStatements.push(URLSearchParamSync.buildUrlReadBackEffect(usage.searchUrlParamKey, adopterVar, undefined, adoptedRefVar));
584
594
  }
585
595
  // `const ds_N_adoptUrlSearch = (update) => { … }`: the setter the search's URL
586
596
  // read-back calls with its `(prev) => next` updater. It moves the input and the
587
- // applied (debounced) query together; the combined paginated state keeps its
588
- // page, which a typed change resets to 1 in the debounce effect.
597
+ // applied (debounced) query together. A list whose page is in the address
598
+ // keeps its page — the page sync sets it from the same address. Any other
599
+ // list starts the new query on page 1, as a typed change does in the debounce
600
+ // effect: an appending list has usually advanced past page 1 by the time the
601
+ // router is ready, and the query's first answer would otherwise be its page 2
602
+ // — empty for a query only a few rows match.
589
603
  function buildUrlSearchAdopter(adopterVar, usage, vars) {
590
604
  var update = types.identifier('update');
605
+ var pageReset = usageHasPageUrlSync(usage)
606
+ ? []
607
+ : [types.objectProperty(types.identifier('page'), types.numericLiteral(1))];
591
608
  var applyToDebounced = usage.category === 'paginated+search'
592
609
  ? // setDs_N_state((state) => {
593
610
  // const next = update(state.debouncedQuery)
594
- // return next === state.debouncedQuery ? state : { ...state, debouncedQuery: next }
611
+ // return next === state.debouncedQuery ? state : { ...state, page: 1, debouncedQuery: next }
595
612
  // })
596
613
  types.callExpression(types.identifier(vars.setCombinedStateVar), [
597
614
  types.arrowFunctionExpression([types.identifier('state')], types.blockStatement([
@@ -600,10 +617,11 @@ function buildUrlSearchAdopter(adopterVar, usage, vars) {
600
617
  types.memberExpression(types.identifier('state'), types.identifier('debouncedQuery')),
601
618
  ])),
602
619
  ]),
603
- types.returnStatement(types.conditionalExpression(types.binaryExpression('===', types.identifier('next'), types.memberExpression(types.identifier('state'), types.identifier('debouncedQuery'))), types.identifier('state'), types.objectExpression([
604
- types.spreadElement(types.identifier('state')),
620
+ types.returnStatement(types.conditionalExpression(types.binaryExpression('===', types.identifier('next'), types.memberExpression(types.identifier('state'), types.identifier('debouncedQuery'))), types.identifier('state'), types.objectExpression(__spreadArray(__spreadArray([
621
+ types.spreadElement(types.identifier('state'))
622
+ ], pageReset, true), [
605
623
  types.objectProperty(types.identifier('debouncedQuery'), types.identifier('next')),
606
- ]))),
624
+ ], false)))),
607
625
  ])),
608
626
  ])
609
627
  : types.callExpression(types.identifier(vars.setDebouncedSearchQueryVar), [update]);
@@ -766,7 +784,7 @@ function pushFilterSortPageResetEffect(stateDeclarations, effectStatements, usag
766
784
  // ==================== MAIN PLUGIN ====================
767
785
  export var createNextArrayMapperPaginationPlugin = function () {
768
786
  var paginationPlugin = function (structure) { return __awaiter(void 0, void 0, void 0, function () {
769
- var uidl, chunks, dependencies, options, componentChunk, variableDeclaration, declarator, arrowFunction, blockStatement, registry, getStaticPropsChunk, isPage, localized, stateDeclarations, effectStatements, needsUseRouter, returnIndex, insertIndex, dataProviders, dataProvidersWithRepeaters, usageIndexByDataSourceId, loadingStateDeclarations, usagesWithLoadingState, usageByProviderNode, cacheDeclarations, sharedOptions, cachedScopes, dataProvidersWithoutRepeaters, searchInputs, searchEnabledUsages, usageBySearchInput;
787
+ 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;
770
788
  var _a, _b;
771
789
  return __generator(this, function (_c) {
772
790
  uidl = structure.uidl, chunks = structure.chunks, dependencies = structure.dependencies, options = structure.options;
@@ -790,7 +808,9 @@ export var createNextArrayMapperPaginationPlugin = function () {
790
808
  return [2 /*return*/, structure];
791
809
  }
792
810
  blockStatement = arrowFunction.body;
793
- registry = buildStateRegistry(uidl.node);
811
+ rangeControls = findRangeControls(blockStatement);
812
+ hasRangeControl = rangeControls.length > 0;
813
+ registry = buildStateRegistry(uidl.node, uidl.stateDefinitions);
794
814
  if (registry.usages.length === 0) {
795
815
  return [2 /*return*/, structure];
796
816
  }
@@ -958,11 +978,15 @@ export var createNextArrayMapperPaginationPlugin = function () {
958
978
  pushPropIdsAsDeps(countEffectDeps, countEffectSeen, usage.filterPropIds);
959
979
  // Same goes for URL-driven filters (already documented above).
960
980
  pushUrlSearchParamMemoDeps(countEffectDeps, usage);
981
+ if (hasRangeControl) {
982
+ stateDeclarations.push(buildTotalDeclaration(vars));
983
+ }
961
984
  stateDeclarations.push(buildCountSeqDeclaration(vars.countSeqRefVar));
962
985
  effectStatements.push(buildCountFetchEffect({
963
986
  fileName: fileName,
964
987
  perPage: usage.perPage,
965
988
  setMaxPagesVar: vars.setMaxPagesStateVar,
989
+ setTotalVar: hasRangeControl ? vars.setTotalStateVar : undefined,
966
990
  urlParams: urlParams,
967
991
  deps: countEffectDeps,
968
992
  countSeqRefVar: vars.countSeqRefVar,
@@ -1004,7 +1028,9 @@ export var createNextArrayMapperPaginationPlugin = function () {
1004
1028
  // "Next" button, but not to label a numbered strip or to know when an
1005
1029
  // infinite list has reached its end, so those two modes fetch a live
1006
1030
  // count as well.
1007
- var needsLiveCount = !isPage || usage.paginationMode === 'numbered' || usage.infiniteScroll;
1031
+ // A range text needs the live count too: the build-time count only
1032
+ // ever became a page count, never the total it shows.
1033
+ var needsLiveCount = !isPage || usage.paginationMode === 'numbered' || usage.infiniteScroll || hasRangeControl;
1008
1034
  if (needsLiveCount) {
1009
1035
  var fileName = generateSafeFileName(usage.resourceDefinition.dataSourceType, usage.resourceDefinition.tableName, usage.resourceDefinition.dataSourceId);
1010
1036
  // ⛔ This list has no search input, so no `query` param — but it may
@@ -1026,11 +1052,15 @@ export var createNextArrayMapperPaginationPlugin = function () {
1026
1052
  pushStateIdsAsDeps(countDeps, countDepsSeen, usage.filterStateIds);
1027
1053
  pushPropIdsAsDeps(countDeps, countDepsSeen, usage.filterPropIds);
1028
1054
  pushUrlSearchParamMemoDeps(countDeps, usage);
1055
+ if (hasRangeControl) {
1056
+ stateDeclarations.push(buildTotalDeclaration(vars));
1057
+ }
1029
1058
  stateDeclarations.push(buildCountSeqDeclaration(vars.countSeqRefVar));
1030
1059
  effectStatements.push(buildCountFetchEffect({
1031
1060
  fileName: fileName,
1032
1061
  perPage: usage.perPage,
1033
1062
  setMaxPagesVar: vars.setMaxPagesStateVar,
1063
+ setTotalVar: hasRangeControl ? vars.setTotalStateVar : undefined,
1034
1064
  urlParams: countUrlParams,
1035
1065
  deps: countDeps,
1036
1066
  countSeqRefVar: vars.countSeqRefVar,
@@ -1115,6 +1145,7 @@ export var createNextArrayMapperPaginationPlugin = function () {
1115
1145
  usagesWithLoadingState = new Set();
1116
1146
  usageByProviderNode = new Map();
1117
1147
  cacheDeclarations = [];
1148
+ firstPageDeclarations = isPage ? [] : undefined;
1118
1149
  dataProvidersWithRepeaters.forEach(function (dp) {
1119
1150
  var _a, _b;
1120
1151
  var nameAttr = dp.openingElement.attributes.find(function (attr) { return attr.type === 'JSXAttribute' && attr.name.name === 'name'; });
@@ -1135,10 +1166,10 @@ export var createNextArrayMapperPaginationPlugin = function () {
1135
1166
  var fileName = generateSafeFileName(usage.resourceDefinition.dataSourceType, usage.resourceDefinition.tableName, usage.resourceDefinition.dataSourceId);
1136
1167
  // Update DataProvider based on category
1137
1168
  if (usage.category === 'paginated+search') {
1138
- updateDataProviderForPaginatedSearch(dp, usage, vars, fileName, cacheDeclarations, localized);
1169
+ updateDataProviderForPaginatedSearch(dp, usage, vars, fileName, cacheDeclarations, localized, firstPageDeclarations);
1139
1170
  }
1140
1171
  else if (usage.category === 'paginated-only') {
1141
- updateDataProviderForPaginationOnly(dp, usage, vars, fileName, cacheDeclarations, localized);
1172
+ updateDataProviderForPaginationOnly(dp, usage, vars, fileName, cacheDeclarations, localized, firstPageDeclarations);
1142
1173
  }
1143
1174
  else if (usage.category === 'search-only') {
1144
1175
  updateDataProviderForSearchOnly(dp, usage, vars, fileName, cacheDeclarations, localized);
@@ -1150,9 +1181,14 @@ export var createNextArrayMapperPaginationPlugin = function () {
1150
1181
  // change refetches, instead of leaving the previous rows on screen with
1151
1182
  // no feedback. Runs last so it sees the `fetchData` and
1152
1183
  // `persistDataDuringLoading` attributes the updaters above just wrote.
1184
+ // An appending list keeps its rows on screen while its next page loads;
1185
+ // only a list loading afresh falls through to the loading slot.
1153
1186
  var loadingVars = getLoadingStateVars(usage.index);
1154
- if (applyLoadingStateToDataProvider(dp, loadingVars)) {
1155
- loadingStateDeclarations.push.apply(loadingStateDeclarations, buildLoadingStateDeclarations(loadingVars));
1187
+ var appendingRequest = usage.infiniteScroll && usage.paginated
1188
+ ? buildAppendingRequestCheck(getInfiniteScrollVars(usage.index))
1189
+ : undefined;
1190
+ if (applyLoadingStateToDataProvider(dp, loadingVars, appendingRequest)) {
1191
+ loadingStateDeclarations.push.apply(loadingStateDeclarations, buildLoadingStateDeclarations(loadingVars, !!appendingRequest));
1156
1192
  // Recorded because the loading state is only declared for a provider
1157
1193
  // that HAS a loading slot; a Load More button wired later may only
1158
1194
  // reference `ds_N_isFetching` if it actually exists.
@@ -1161,7 +1197,9 @@ export var createNextArrayMapperPaginationPlugin = function () {
1161
1197
  // An auto-loading list needs something at the end of the rows for its
1162
1198
  // IntersectionObserver to watch.
1163
1199
  if (usage.infiniteScroll && usage.paginated && !usage.infiniteScrollLoadMore) {
1164
- insertSentinelAfterProvider(blockStatement, dp, buildSentinelElement(getInfiniteScrollVars(usage.index)));
1200
+ insertSentinelAfterProvider(blockStatement, dp, function (parentTag) {
1201
+ return buildSentinelElement(getInfiniteScrollVars(usage.index), parentTag);
1202
+ });
1165
1203
  }
1166
1204
  // Create API route for all categories (including 'plain' for components)
1167
1205
  ensureAPIRouteExists(options.extractedResources, usage, options.dataSources, buildProductTransformOptions(options));
@@ -1195,6 +1233,11 @@ export var createNextArrayMapperPaginationPlugin = function () {
1195
1233
  ASTStatementOrder.insertStatementsAfterDependencies(blockStatement, cacheDeclarations);
1196
1234
  registerCacheClientImports(dependencies, (_a = uidl.outputOptions) === null || _a === void 0 ? void 0 : _a.folderPath);
1197
1235
  }
1236
+ // After the cache declarations: a cached list's first page reads the
1237
+ // `ds_N_params` they hoisted.
1238
+ if (firstPageDeclarations === null || firstPageDeclarations === void 0 ? void 0 : firstPageDeclarations.length) {
1239
+ ASTStatementOrder.insertStatementsAfterDependencies(blockStatement, firstPageDeclarations);
1240
+ }
1198
1241
  dataProvidersWithoutRepeaters = dataProviders.filter(function (dp) {
1199
1242
  var hasRepeater = findArrayMapperRenderPropInDataProvider(dp) !== undefined;
1200
1243
  return !hasRepeater;
@@ -1203,17 +1246,14 @@ export var createNextArrayMapperPaginationPlugin = function () {
1203
1246
  stabilizeDataProviderWithoutRepeater(dp);
1204
1247
  });
1205
1248
  searchInputs = findAllSearchInputsInJSX(blockStatement);
1206
- searchEnabledUsages = registry.usages.filter(function (u) { return u.searchEnabled; });
1207
- usageBySearchInput = new Map();
1208
- searchInputs.forEach(function (input, idx) {
1209
- if (idx >= searchEnabledUsages.length) {
1210
- return;
1249
+ usageBySearchInput = pairSearchInputs(searchInputs, registry.usages);
1250
+ for (_i = 0, searchInputs_1 = searchInputs; _i < searchInputs_1.length; _i++) {
1251
+ input = searchInputs_1[_i];
1252
+ usage = usageBySearchInput.get(input.node);
1253
+ if (usage) {
1254
+ wireSearchInput(input, getStateVarsForUsage(usage));
1211
1255
  }
1212
- var usage = searchEnabledUsages[idx];
1213
- var vars = getStateVarsForUsage(usage);
1214
- wireSearchInput(input.node, vars);
1215
- usageBySearchInput.set(input.node, usage);
1216
- });
1256
+ }
1217
1257
  // STEP 4.5: Wire the clear buttons, each to the input it shares a parent with.
1218
1258
  findAllSearchClearButtonsInJSX(blockStatement).forEach(function (button) {
1219
1259
  var input = findSiblingSearchInput(button, searchInputs.map(function (entry) { return entry.node; }));
@@ -1240,9 +1280,29 @@ export var createNextArrayMapperPaginationPlugin = function () {
1240
1280
  }
1241
1281
  wirePaginationWidget(node, usage, getStateVarsForUsage(usage), usagesWithLoadingState.has(usage.index));
1242
1282
  });
1243
- // STEP 6: Update getStaticProps if this is a page
1283
+ // STEP 5b: Wire the range texts that name their mapper.
1284
+ //
1285
+ // A `range` control carrying `data-tq-pagination-for` belongs to the mapper
1286
+ // whose editor node id it names, wherever it sits — a template's footer or
1287
+ // toolbar is laid out by the author, not inside a pagination container, so
1288
+ // the container-ownership rule above cannot reach it. One without the
1289
+ // attribute was wired by its container in STEP 5; one naming a mapper this
1290
+ // component does not render stays as drawn.
1291
+ rangeControls.forEach(function (control) {
1292
+ var ownerNodeId = readJSXAttributeString(control, PAGINATION_FOR_ATTR);
1293
+ if (!ownerNodeId) {
1294
+ return;
1295
+ }
1296
+ var owner = registry.usages.find(function (usage) { return usage.nodeId === ownerNodeId; });
1297
+ if (owner) {
1298
+ wireRangeControlFor(control, owner, getStateVarsForUsage(owner));
1299
+ }
1300
+ });
1301
+ // STEP 6: Update getStaticProps if this is a page. Its rows are what the
1302
+ // page renders, so they are read as a visitor is served them — unless only
1303
+ // the store's trusted readers can open the page (`pageDataReadArguments`).
1244
1304
  if (isPage) {
1245
- updateGetStaticProps(chunks, registry, dependencies, (_b = uidl.outputOptions) === null || _b === void 0 ? void 0 : _b.folderPath, localized);
1305
+ updateGetStaticProps(chunks, registry, dependencies, (_b = uidl.outputOptions) === null || _b === void 0 ? void 0 : _b.folderPath, localized, function () { return RouteUtils.pageDataReadArguments(uidl, options.auth); });
1246
1306
  }
1247
1307
  return [2 /*return*/, structure];
1248
1308
  });
@@ -1257,39 +1317,59 @@ export var createNextArrayMapperPaginationPlugin = function () {
1257
1317
  * It has to be a sibling: the provider's own child is a render-prop function,
1258
1318
  * and anything placed inside it would be re-created on every fetch — the
1259
1319
  * observer would then be watching an element that is no longer in the document.
1320
+ * The sentinel is built for the element it lands in (`buildSentinelElement`):
1321
+ * a fragment is transparent, a component's children may go anywhere.
1260
1322
  *
1261
1323
  * Returns false when the provider has no element parent to splice into (a
1262
1324
  * provider returned directly from the component), leaving the list on its
1263
1325
  * count-driven `hasMore` rather than injecting a stray node.
1264
1326
  */
1265
- function insertSentinelAfterProvider(blockStatement, provider, sentinel) {
1327
+ function insertSentinelAfterProvider(blockStatement, provider, buildSentinel) {
1266
1328
  var inserted = false;
1267
- var traverse = function (node) {
1329
+ var elementTagOf = function (node, enclosing) {
1330
+ var _a;
1331
+ if ((node === null || node === void 0 ? void 0 : node.type) === 'JSXFragment') {
1332
+ return enclosing;
1333
+ }
1334
+ if ((node === null || node === void 0 ? void 0 : node.type) !== 'JSXElement') {
1335
+ return enclosing;
1336
+ }
1337
+ var name = (_a = node.openingElement) === null || _a === void 0 ? void 0 : _a.name;
1338
+ if ((name === null || name === void 0 ? void 0 : name.type) !== 'JSXIdentifier') {
1339
+ return null;
1340
+ }
1341
+ if (name.name === 'Fragment') {
1342
+ return enclosing;
1343
+ }
1344
+ return /^[a-z]/.test(name.name) ? name.name : null;
1345
+ };
1346
+ var traverse = function (node, enclosing) {
1268
1347
  if (!node || inserted) {
1269
1348
  return;
1270
1349
  }
1350
+ var tag = elementTagOf(node, enclosing);
1271
1351
  if (Array.isArray(node.children)) {
1272
1352
  var at = node.children.indexOf(provider);
1273
1353
  if (at !== -1) {
1274
- node.children.splice(at + 1, 0, sentinel);
1354
+ node.children.splice(at + 1, 0, buildSentinel(tag));
1275
1355
  inserted = true;
1276
1356
  return;
1277
1357
  }
1278
- node.children.forEach(traverse);
1358
+ node.children.forEach(function (child) { return traverse(child, tag); });
1279
1359
  }
1280
1360
  if (node.body) {
1281
1361
  if (Array.isArray(node.body)) {
1282
- node.body.forEach(traverse);
1362
+ node.body.forEach(function (child) { return traverse(child, tag); });
1283
1363
  }
1284
1364
  else {
1285
- traverse(node.body);
1365
+ traverse(node.body, tag);
1286
1366
  }
1287
1367
  }
1288
1368
  if (node.consequent) {
1289
- traverse(node.consequent);
1369
+ traverse(node.consequent, tag);
1290
1370
  }
1291
1371
  if (node.alternate) {
1292
- traverse(node.alternate);
1372
+ traverse(node.alternate, tag);
1293
1373
  }
1294
1374
  // `{activeTabIndex === 0 && (<div>…<DataProvider/>…</div>)}` — a tab
1295
1375
  // panel, a gated section: the provider sits on the RIGHT of a logical
@@ -1297,22 +1377,22 @@ function insertSentinelAfterProvider(blockStatement, provider, sentinel) {
1297
1377
  // an argument. Left unvisited, the products list under the first tab of
1298
1378
  // a shop was never paginated or seeded, and rendered nothing.
1299
1379
  if (node.left) {
1300
- traverse(node.left);
1380
+ traverse(node.left, tag);
1301
1381
  }
1302
1382
  if (node.right) {
1303
- traverse(node.right);
1383
+ traverse(node.right, tag);
1304
1384
  }
1305
1385
  if (node.test) {
1306
- traverse(node.test);
1386
+ traverse(node.test, tag);
1307
1387
  }
1308
1388
  if (node.expression) {
1309
- traverse(node.expression);
1389
+ traverse(node.expression, tag);
1310
1390
  }
1311
1391
  if (node.argument) {
1312
- traverse(node.argument);
1392
+ traverse(node.argument, tag);
1313
1393
  }
1314
1394
  };
1315
- traverse(blockStatement);
1395
+ traverse(blockStatement, null);
1316
1396
  return inserted;
1317
1397
  }
1318
1398
  function findAllDataProvidersInJSX(blockStatement) {
@@ -1407,61 +1487,6 @@ function findArrayMapperRenderPropInDataProvider(dataProvider) {
1407
1487
  }
1408
1488
  return renderItemAttr.value.expression.params[0].name;
1409
1489
  }
1410
- function findAllSearchInputsInJSX(blockStatement) {
1411
- var results = [];
1412
- var traverse = function (node) {
1413
- var _a, _b, _c, _d, _e, _f;
1414
- if (!node) {
1415
- return;
1416
- }
1417
- if (node.type === 'JSXElement' && ((_b = (_a = node.openingElement) === null || _a === void 0 ? void 0 : _a.name) === null || _b === void 0 ? void 0 : _b.name) === 'input') {
1418
- var classAttr = (_c = node.openingElement.attributes) === null || _c === void 0 ? void 0 : _c.find(function (attr) { return attr.type === 'JSXAttribute' && attr.name.name === 'className'; });
1419
- var className = ((_d = classAttr === null || classAttr === void 0 ? void 0 : classAttr.value) === null || _d === void 0 ? void 0 : _d.value) || ((_f = (_e = classAttr === null || classAttr === void 0 ? void 0 : classAttr.value) === null || _e === void 0 ? void 0 : _e.expression) === null || _f === void 0 ? void 0 : _f.value) || '';
1420
- if (className.includes('search-input')) {
1421
- results.push({ node: node, className: className });
1422
- }
1423
- }
1424
- if (node.children && Array.isArray(node.children)) {
1425
- node.children.forEach(function (c) { return traverse(c); });
1426
- }
1427
- if (node.body) {
1428
- if (Array.isArray(node.body)) {
1429
- node.body.forEach(function (s) { return traverse(s); });
1430
- }
1431
- else {
1432
- traverse(node.body);
1433
- }
1434
- }
1435
- if (node.consequent) {
1436
- traverse(node.consequent);
1437
- }
1438
- if (node.alternate) {
1439
- traverse(node.alternate);
1440
- }
1441
- // `{activeTabIndex === 0 && (<div>…<DataProvider/>…</div>)}` — a tab
1442
- // panel, a gated section: the provider sits on the RIGHT of a logical
1443
- // expression (or in a ternary's `test`), which is not a child, a body or
1444
- // an argument. Left unvisited, the products list under the first tab of
1445
- // a shop was never paginated or seeded, and rendered nothing.
1446
- if (node.left) {
1447
- traverse(node.left);
1448
- }
1449
- if (node.right) {
1450
- traverse(node.right);
1451
- }
1452
- if (node.test) {
1453
- traverse(node.test);
1454
- }
1455
- if (node.expression) {
1456
- traverse(node.expression);
1457
- }
1458
- if (node.argument) {
1459
- traverse(node.argument);
1460
- }
1461
- };
1462
- traverse(blockStatement);
1463
- return results;
1464
- }
1465
1490
  /**
1466
1491
  * Every pagination widget in the JSX, paired with the mapper it belongs to.
1467
1492
  *
@@ -1536,34 +1561,97 @@ function findPaginationNodesWithOwners(blockStatement, usageByProviderNode) {
1536
1561
  *
1537
1562
  * An uncached mapper keeps the inline `params={useMemo(...)}` it has always had,
1538
1563
  * byte for byte. A cached one hoists that memo into a component const so the
1539
- * peek can depend on the same object identity instead of rebuilding it.
1564
+ * peek can depend on the same object identity instead of rebuilding it; so does
1565
+ * an appending list that starts from its page's rows (`hoistInto`), whose first
1566
+ * page is filed in the accumulator under those params.
1540
1567
  *
1541
- * Returns the identifier holding the peeked rows, or `undefined` when this
1542
- * mapper does not cache.
1568
+ * Returns the identifier holding the params when they were hoisted, and the one
1569
+ * holding the peeked rows when this mapper caches.
1543
1570
  */
1544
1571
  function pushParamsAttribute(
1545
1572
  // tslint:disable-next-line:no-any
1546
- dp, usage, paramsProps, memoDeps, cacheDeclarations) {
1573
+ dp, usage, paramsProps, memoDeps, cacheDeclarations, hoistInto) {
1547
1574
  var _a;
1548
1575
  var paramsMemo = types.callExpression(types.identifier('useMemo'), [
1549
1576
  types.arrowFunctionExpression([], types.objectExpression(paramsProps)),
1550
1577
  types.arrayExpression(memoDeps),
1551
1578
  ]);
1552
- // A state-bound sort deliberately suppresses `initialData` (see the comment at
1553
- // the call site): the prefetch ran without sort params, so reusing anything on
1554
- // mount would mask the current sort. The cache peek is `initialData` by
1555
- // another name, so it has to be suppressed for exactly the same reason.
1579
+ // A state-bound sort gets no cache peek. The peek is `initialData` by another
1580
+ // name, and such a list takes the server's rows only while its sort is still
1581
+ // where the visitor starts (`buildStartingSortGuard`); the peek was never
1582
+ // extended to it.
1556
1583
  var cache = ((_a = usage.cache) === null || _a === void 0 ? void 0 : _a.client) && !usage.dynamicSort ? usage.cache : undefined;
1557
1584
  if (!cache) {
1558
- dp.openingElement.attributes.push(types.jsxAttribute(types.jsxIdentifier('params'), types.jsxExpressionContainer(paramsMemo)));
1559
- return undefined;
1585
+ if (!hoistInto) {
1586
+ dp.openingElement.attributes.push(types.jsxAttribute(types.jsxIdentifier('params'), types.jsxExpressionContainer(paramsMemo)));
1587
+ return {};
1588
+ }
1589
+ var paramsIdentifier = paramsVarFor(usage.index);
1590
+ hoistInto.push(types.variableDeclaration('const', [
1591
+ types.variableDeclarator(types.identifier(paramsIdentifier), paramsMemo),
1592
+ ]));
1593
+ dp.openingElement.attributes.push(types.jsxAttribute(types.jsxIdentifier('params'), types.jsxExpressionContainer(types.identifier(paramsIdentifier))));
1594
+ return { paramsIdentifier: paramsIdentifier };
1560
1595
  }
1561
1596
  var hoisted = buildCachedParamsDeclarations({ index: usage.index, paramsMemo: paramsMemo, cache: cache });
1562
1597
  cacheDeclarations.push.apply(cacheDeclarations, hoisted.declarations);
1563
1598
  dp.openingElement.attributes.push(types.jsxAttribute(types.jsxIdentifier('params'), types.jsxExpressionContainer(types.identifier(hoisted.paramsIdentifier))));
1564
- return hoisted.cachedIdentifier;
1599
+ return { paramsIdentifier: hoisted.paramsIdentifier, cachedIdentifier: hoisted.cachedIdentifier };
1600
+ }
1601
+ // Whether the rows `getStaticProps` fetched can be this list's first page at
1602
+ // all. A sort bound to state can only be served them when the build knows the
1603
+ // sort the visitor starts with (`startingSort`) and fetched by it; otherwise
1604
+ // the fetch has no sort, and its rows are not the list's first page.
1605
+ function prefetchFollowsSort(usage) {
1606
+ return !usage.dynamicSort || !!usage.startingSort;
1607
+ }
1608
+ // `sortBy === "name-asc"`: the visitor has not moved the sort from where it
1609
+ // starts, so the build-time rows are still its first page. `null` for a list
1610
+ // whose sort the visitor cannot pick, or one that reads no state.
1611
+ function buildStartingSortGuard(usage) {
1612
+ return usage.dynamicSort && usage.startingSort ? buildSortAtStartGuard(usage.startingSort) : null;
1613
+ }
1614
+ // `<condition> && <starting-sort guard>`, or the condition alone.
1615
+ function withStartingSortGuard(condition, usage) {
1616
+ var sortGuard = buildStartingSortGuard(usage);
1617
+ return sortGuard ? types.logicalExpression('&&', condition, sortGuard) : condition;
1618
+ }
1619
+ // An appending list on a page starts from the rows its `getStaticProps`
1620
+ // fetched, when they can be its first page (`prefetchFollowsSort`).
1621
+ function startsFromServerRows(usage) {
1622
+ return usage.infiniteScroll && usage.paginated && prefetchFollowsSort(usage);
1623
+ }
1624
+ /**
1625
+ * `initialData={ds_N_firstPage}` on an appending list, beside the frozen first
1626
+ * page and the effect that files it as page 1 of the accumulator (see
1627
+ * `infinite-scroll.ts`).
1628
+ *
1629
+ * The rows stand in for page 1 only while they answer the list's query: no
1630
+ * search (`queryGuard`), the sort where the visitor starts, no filter set (the
1631
+ * guards the paged lists use), and at least one row — an empty build-time
1632
+ * answer, which a failed fetch also gives, leaves the browser to fetch page 1
1633
+ * as before.
1634
+ */
1635
+ function pushInfiniteScrollFirstPage(
1636
+ // tslint:disable-next-line:no-any
1637
+ dp, usage, vars, paramsIdentifier, declarations, queryGuard, instanceDeps) {
1638
+ var infiniteVars = getInfiniteScrollVars(usage.index);
1639
+ var serverPayload = function () {
1640
+ return types.optionalMemberExpression(types.identifier('props'), types.identifier(vars.propsPrefix), false, true);
1641
+ };
1642
+ var hasServerRows = types.binaryExpression('>', types.optionalMemberExpression(buildServerRowsExpression(serverPayload(), usage.itemsPath), types.identifier('length'), false, true), types.numericLiteral(0));
1643
+ var startsFromServer = [
1644
+ queryGuard,
1645
+ buildStartingSortGuard(usage),
1646
+ buildNoUrlFilterGuard(usage),
1647
+ hasServerRows,
1648
+ ]
1649
+ .filter(function (guard) { return guard !== null; })
1650
+ .reduce(function (all, guard) { return types.logicalExpression('&&', all, guard); });
1651
+ declarations.push(buildFirstPageDeclaration(infiniteVars, startsFromServer, serverPayload(), instanceDeps), buildFirstPageSeedEffect(infiniteVars, paramsIdentifier, usage.perPage, usage.itemsPath));
1652
+ dp.openingElement.attributes.push(types.jsxAttribute(types.jsxIdentifier('initialData'), types.jsxExpressionContainer(types.identifier(infiniteVars.firstPageVar))));
1565
1653
  }
1566
- function updateDataProviderForPaginatedSearch(dp, usage, vars, fileName, cacheDeclarations, localized) {
1654
+ function updateDataProviderForPaginatedSearch(dp, usage, vars, fileName, cacheDeclarations, localized, firstPageDeclarations) {
1567
1655
  var attrs = dp.openingElement.attributes;
1568
1656
  // Remove existing params, key, initialData, fetchData, persistDataDuringLoading
1569
1657
  dp.openingElement.attributes = attrs.filter(function (attr) {
@@ -1596,20 +1684,29 @@ function updateDataProviderForPaginatedSearch(dp, usage, vars, fileName, cacheDe
1596
1684
  if (localized) {
1597
1685
  appendClientLocaleParam(paramsProps, memoDeps);
1598
1686
  }
1599
- var cachedInitialData = pushParamsAttribute(dp, usage, paramsProps, memoDeps, cacheDeclarations);
1600
- // Add initialData. Skip the server-prefetched data entirely when a dynamic
1601
- // state-bound sort is active — the prefetch ran without sort parameters, so
1602
- // reusing it would mask the current sort state AND cause DataProvider to skip
1603
- // the first client fetch (its internal guard only skips when initialData is
1604
- // defined on mount). Without that skip, toggling sort correctly triggers a
1605
- // refetch via the useMemo params dependency chain.
1687
+ var seedInto = startsFromServerRows(usage) ? firstPageDeclarations : undefined;
1688
+ var _a = pushParamsAttribute(dp, usage, paramsProps, memoDeps, cacheDeclarations, seedInto), paramsIdentifier = _a.paramsIdentifier, cachedInitialData = _a.cachedIdentifier;
1689
+ // Add initialData: the server-prefetched page 1 while it is the page on show
1690
+ // — page 1, no search, no filter set, and a sort the visitor picks still where
1691
+ // they start (the prefetch ran by that sort). A list whose starting sort the
1692
+ // build cannot know never takes it: that prefetch ran without a sort, so
1693
+ // reusing it would mask the current sort AND make DataProvider skip its first
1694
+ // client fetch (its internal guard only skips when initialData is defined on
1695
+ // mount). Any other sort is fetched fresh, through the useMemo params.
1606
1696
  //
1607
- // An appending list skips it for a second reason: `initialData` makes
1608
- // DataProvider skip its first fetch, so page 1 would render from the prefetch
1609
- // and never enter the accumulator — appending page 2 would then REPLACE the
1610
- // visible rows with page 2 alone.
1611
- if (!usage.dynamicSort && !usage.infiniteScroll) {
1612
- 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);
1697
+ // An appending list takes its own: `initialData` makes DataProvider skip its
1698
+ // first fetch, so the prefetched page 1 has to be filed in the accumulator
1699
+ // too, or appending page 2 would REPLACE the visible rows with page 2 alone.
1700
+ // The provider remounts per query (its `key`), so the first page is frozen
1701
+ // per query.
1702
+ if (seedInto && paramsIdentifier) {
1703
+ var appliedQuery = function () {
1704
+ return types.memberExpression(types.identifier(vars.combinedStateVar), types.identifier('debouncedQuery'));
1705
+ };
1706
+ pushInfiniteScrollFirstPage(dp, usage, vars, paramsIdentifier, seedInto, types.unaryExpression('!', appliedQuery(), true), [appliedQuery()]);
1707
+ }
1708
+ else if (prefetchFollowsSort(usage) && !usage.infiniteScroll) {
1709
+ 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);
1613
1710
  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')))));
1614
1711
  }
1615
1712
  // Add key. Sort is intentionally NOT part of the key — a key change would
@@ -1647,7 +1744,7 @@ function updateDataProviderForPaginatedSearch(dp, usage, vars, fileName, cacheDe
1647
1744
  // Add persistDataDuringLoading
1648
1745
  dp.openingElement.attributes.push(types.jsxAttribute(types.jsxIdentifier('persistDataDuringLoading'), types.jsxExpressionContainer(types.booleanLiteral(true))));
1649
1746
  }
1650
- function updateDataProviderForPaginationOnly(dp, usage, vars, fileName, cacheDeclarations, localized) {
1747
+ function updateDataProviderForPaginationOnly(dp, usage, vars, fileName, cacheDeclarations, localized, firstPageDeclarations) {
1651
1748
  var attrs = dp.openingElement.attributes;
1652
1749
  dp.openingElement.attributes = attrs.filter(function (attr) {
1653
1750
  var _a;
@@ -1676,12 +1773,17 @@ function updateDataProviderForPaginationOnly(dp, usage, vars, fileName, cacheDec
1676
1773
  appendClientLocaleParam(paramsProps, memoDeps);
1677
1774
  }
1678
1775
  // Add params
1679
- var cachedInitialData = pushParamsAttribute(dp, usage, paramsProps, memoDeps, cacheDeclarations);
1680
- // Add initialData. See paginated+search updater for why we skip prefetch
1681
- // reuse when a dynamic state-bound sort is active, and why an appending list
1682
- // skips it too.
1683
- if (!usage.dynamicSort && !usage.infiniteScroll) {
1684
- 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')))));
1776
+ var seedInto = startsFromServerRows(usage) ? firstPageDeclarations : undefined;
1777
+ var _a = pushParamsAttribute(dp, usage, paramsProps, memoDeps, cacheDeclarations, seedInto), paramsIdentifier = _a.paramsIdentifier, cachedInitialData = _a.cachedIdentifier;
1778
+ // Add initialData. See paginated+search updater for when a sort the visitor
1779
+ // picks may reuse the prefetch, and how an appending list takes its own. This
1780
+ // provider never remounts (no `key`), so its first page is frozen for the
1781
+ // page's lifetime.
1782
+ if (seedInto && paramsIdentifier) {
1783
+ pushInfiniteScrollFirstPage(dp, usage, vars, paramsIdentifier, seedInto, null, []);
1784
+ }
1785
+ else if (prefetchFollowsSort(usage) && !usage.infiniteScroll) {
1786
+ 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')))));
1685
1787
  }
1686
1788
  // Add key — sort is intentionally NOT included; see paginated+search updater,
1687
1789
  // which also explains why an appending list keys on nothing at all (there is
@@ -1731,11 +1833,11 @@ function updateDataProviderForSearchOnly(dp, usage, vars, fileName, cacheDeclara
1731
1833
  if (localized) {
1732
1834
  appendClientLocaleParam(paramsProps, memoDeps);
1733
1835
  }
1734
- var cachedInitialData = pushParamsAttribute(dp, usage, paramsProps, memoDeps, cacheDeclarations);
1735
- // Add initialData. See paginated+search updater for why we skip prefetch
1736
- // reuse when a dynamic state-bound sort is active.
1737
- if (!usage.dynamicSort) {
1738
- 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')))));
1836
+ var cachedInitialData = pushParamsAttribute(dp, usage, paramsProps, memoDeps, cacheDeclarations).cachedIdentifier;
1837
+ // Add initialData. See paginated+search updater for when a sort the visitor
1838
+ // picks may reuse the prefetch.
1839
+ if (prefetchFollowsSort(usage)) {
1840
+ 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')))));
1739
1841
  }
1740
1842
  // Add key — sort is intentionally NOT included; see paginated+search updater.
1741
1843
  dp.openingElement.attributes.push(types.jsxAttribute(types.jsxIdentifier('key'), types.jsxExpressionContainer(types.templateLiteral([
@@ -1746,14 +1848,42 @@ function updateDataProviderForSearchOnly(dp, usage, vars, fileName, cacheDeclara
1746
1848
  dp.openingElement.attributes.push(createFetchDataAttribute(fileName, ((_a = usage.cache) === null || _a === void 0 ? void 0 : _a.client) ? usage.cache : undefined));
1747
1849
  dp.openingElement.attributes.push(types.jsxAttribute(types.jsxIdentifier('persistDataDuringLoading'), types.jsxExpressionContainer(types.booleanLiteral(true))));
1748
1850
  }
1851
+ /**
1852
+ * A plain list on a page is seeded with the rows its `getStaticProps` fetched
1853
+ * (`extractDataSourceIntoGetStaticProps`), and a seeded provider skips its first
1854
+ * fetch. With a sort the visitor picks, those rows are the list's only while the
1855
+ * sort is where a visitor starts — the build fetched them by it:
1856
+ *
1857
+ * initialData={sortBy === "name-asc" ? props.items_products : undefined}
1858
+ *
1859
+ * — and any other sort is fetched fresh, through the params it changes. When the
1860
+ * build cannot know where the sort starts (`prefetchFollowsSort`), it fetched
1861
+ * the rows unsorted, and the list is never seeded with them. A plain list has no
1862
+ * search, and is only seeded without a filter a visitor sets.
1863
+ */
1864
+ function seedPlainListWhileSortStarts(dp, usage) {
1865
+ if (!usage.dynamicSort) {
1866
+ return;
1867
+ }
1868
+ var attrs = dp.openingElement.attributes;
1869
+ var index = attrs.findIndex(function (attr) { var _a; return attr.type === 'JSXAttribute' && ((_a = attr.name) === null || _a === void 0 ? void 0 : _a.name) === 'initialData'; });
1870
+ if (index === -1) {
1871
+ return;
1872
+ }
1873
+ if (!prefetchFollowsSort(usage)) {
1874
+ attrs.splice(index, 1);
1875
+ return;
1876
+ }
1877
+ var sortGuard = buildStartingSortGuard(usage);
1878
+ var seeded = attrs[index].value;
1879
+ if (!sortGuard || (seeded === null || seeded === void 0 ? void 0 : seeded.type) !== 'JSXExpressionContainer') {
1880
+ return;
1881
+ }
1882
+ attrs[index] = types.jsxAttribute(types.jsxIdentifier('initialData'), types.jsxExpressionContainer(types.conditionalExpression(sortGuard, seeded.expression, types.identifier('undefined'))));
1883
+ }
1749
1884
  function updateDataProviderForPlain(dp, fileName, usage, localized) {
1750
1885
  var _a, _b, _c, _d, _e, _f;
1751
- // A sort bound to state is the visitor's, which the server cannot know: rows
1752
- // pre-rendered in the table's order would stand in for it, since a seeded
1753
- // provider skips its first fetch — the paginated lists drop them the same way.
1754
- if (usage.dynamicSort) {
1755
- 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'); });
1756
- }
1886
+ seedPlainListWhileSortStarts(dp, usage);
1757
1887
  var attrs = dp.openingElement.attributes;
1758
1888
  var clientCache = ((_a = usage.cache) === null || _a === void 0 ? void 0 : _a.client) ? usage.cache : undefined;
1759
1889
  // Check if fetchData already exists
@@ -1906,21 +2036,18 @@ accumulate) {
1906
2036
  ? buildCacheStoreThen(cache)
1907
2037
  : uncachedTail;
1908
2038
  var networkChain = types.callExpression(types.memberExpression(parsedJson, types.identifier('then')), [responseTail]);
2039
+ // An appending list records the request it now stands on before making it
2040
+ // (see `buildAccumulatingRequestMark`).
1909
2041
  var arrow = cache
1910
2042
  ? types.arrowFunctionExpression([types.identifier('params')], buildCachedFetchBody(networkChain, cache))
1911
- : types.arrowFunctionExpression([types.identifier('params')], networkChain);
2043
+ : accumulate
2044
+ ? types.arrowFunctionExpression([types.identifier('params')], types.blockStatement([
2045
+ buildAccumulatingRequestMark(getInfiniteScrollVars(accumulate.usage.index)),
2046
+ types.returnStatement(networkChain),
2047
+ ]))
2048
+ : types.arrowFunctionExpression([types.identifier('params')], networkChain);
1912
2049
  return types.jsxAttribute(types.jsxIdentifier('fetchData'), types.jsxExpressionContainer(types.callExpression(types.identifier('useCallback'), [arrow, types.arrayExpression([])])));
1913
2050
  }
1914
- function wireSearchInput(inputNode, vars) {
1915
- // Remove existing onChange and value
1916
- inputNode.openingElement.attributes = inputNode.openingElement.attributes.filter(function (attr) { var _a; return !['onChange', 'value'].includes((_a = attr.name) === null || _a === void 0 ? void 0 : _a.name); });
1917
- // Add onChange
1918
- inputNode.openingElement.attributes.push(types.jsxAttribute(types.jsxIdentifier('onChange'), types.jsxExpressionContainer(types.arrowFunctionExpression([types.identifier('e')], types.callExpression(types.identifier(vars.setSearchQueryVar), [
1919
- types.memberExpression(types.memberExpression(types.identifier('e'), types.identifier('target')), types.identifier('value')),
1920
- ])))));
1921
- // Add value
1922
- inputNode.openingElement.attributes.push(types.jsxAttribute(types.jsxIdentifier('value'), types.jsxExpressionContainer(types.identifier(vars.searchQueryVar))));
1923
- }
1924
2051
  /**
1925
2052
  * The `fetchData` attribute for one paginated usage.
1926
2053
  *
@@ -1936,6 +2063,32 @@ function buildFetchDataForUsage(fileName, usage) {
1936
2063
  }
1937
2064
  return createFetchDataAttribute(fileName, ((_a = usage.cache) === null || _a === void 0 ? void 0 : _a.client) ? usage.cache : undefined);
1938
2065
  }
2066
+ /** `const [ds_N_total, setDs_N_total] = useState(null)` — declared only beside a range control. */
2067
+ function buildTotalDeclaration(vars) {
2068
+ return types.variableDeclaration('const', [
2069
+ types.variableDeclarator(types.arrayPattern([
2070
+ types.identifier(vars.totalStateVar),
2071
+ types.identifier(vars.setTotalStateVar),
2072
+ ]), types.callExpression(types.identifier('useState'), [types.nullLiteral()])),
2073
+ ]);
2074
+ }
2075
+ /**
2076
+ * Fills one `range` text with the live row range of `usage` (see
2077
+ * `range-control.ts`). A mapper that does not page has no count to show, so
2078
+ * its range text is left as drawn.
2079
+ */
2080
+ function wireRangeControlFor(control, usage, vars) {
2081
+ if (!usage.paginated) {
2082
+ return;
2083
+ }
2084
+ var currentPageExpr = getPageAccessors(usage, vars).currentPageExpr;
2085
+ wireRangeControl(control, {
2086
+ totalVar: vars.totalStateVar,
2087
+ currentPageExpr: currentPageExpr(),
2088
+ perPage: usage.perPage,
2089
+ infiniteScroll: usage.infiniteScroll,
2090
+ });
2091
+ }
1939
2092
  /**
1940
2093
  * Wires the authored Load More button of an appending list.
1941
2094
  *
@@ -1975,6 +2128,11 @@ function wireLoadMoreButton(paginationNode, usage, vars, hasLoadingState) {
1975
2128
  * buttons beside its Load More.
1976
2129
  */
1977
2130
  function wirePaginationWidget(paginationNode, usage, vars, hasLoadingState) {
2131
+ // A range text inside the container takes the container's owner, like the
2132
+ // buttons beside it; one naming its own mapper is wired in STEP 5b instead.
2133
+ findAllPaginationControls(paginationNode, 'range')
2134
+ .filter(function (control) { return !readJSXAttributeString(control, PAGINATION_FOR_ATTR); })
2135
+ .forEach(function (control) { return wireRangeControlFor(control, usage, vars); });
1978
2136
  if (usage.infiniteScroll) {
1979
2137
  // Only the Load More button survives; page controls make no sense for a
1980
2138
  // list the visitor never leaves.
@@ -2179,7 +2337,15 @@ function ensureAPIRouteExists(extractedResources, usage, dataSources, transformO
2179
2337
  };
2180
2338
  }
2181
2339
  }
2182
- function updateGetStaticProps(chunks, registry, dependencies, folderPath, localized) {
2340
+ // `fetchCount(<params>, <read arguments>)`: the params may be left out only
2341
+ // when nothing follows them.
2342
+ var countReadArguments = function (countParams, readArguments) {
2343
+ return countParams.length > 0 || readArguments.length > 0
2344
+ ? __spreadArray([types.objectExpression(countParams)], readArguments, true) : [];
2345
+ };
2346
+ function updateGetStaticProps(chunks, registry, dependencies, folderPath, localized,
2347
+ // The arguments every read hands the module after its params (fresh nodes per call).
2348
+ readArguments) {
2183
2349
  var _a;
2184
2350
  var getStaticPropsChunk = chunks.find(function (c) { return c.name === 'getStaticProps'; });
2185
2351
  if (!getStaticPropsChunk || getStaticPropsChunk.type !== ChunkType.AST) {
@@ -2260,6 +2426,12 @@ function updateGetStaticProps(chunks, registry, dependencies, folderPath, locali
2260
2426
  })),
2261
2427
  ])));
2262
2428
  }
2429
+ else if (usage.dynamicSort && usage.startingSort) {
2430
+ // A sort the visitor picks: page 1 by the sort they start with, sent
2431
+ // as the very string the browser sends for it, so page 2 continues
2432
+ // this page exactly (see `buildStartingSortsValue`).
2433
+ fetchParams.push(types.objectProperty(types.identifier('sorts'), buildStartingSortsValue(usage.dynamicSort, usage.startingSort)));
2434
+ }
2263
2435
  // Add filters if present
2264
2436
  // Filter out dynamic destinations for getStaticProps (they can't be resolved at build time)
2265
2437
  var staticFilters = staticFiltersOf(usage);
@@ -2275,7 +2447,7 @@ function updateGetStaticProps(chunks, registry, dependencies, folderPath, locali
2275
2447
  var existingFetchIndex = arrayPattern.elements.findIndex(function (el) { return (el === null || el === void 0 ? void 0 : el.type) === 'Identifier' && el.name === vars.propsPrefix; });
2276
2448
  if (existingFetchIndex === -1) {
2277
2449
  arrayPattern.elements.push(types.identifier(vars.propsPrefix));
2278
- fetchesArray.elements.push(types.callExpression(types.memberExpression(types.callExpression(types.memberExpression(types.identifier(fetcherImportName), types.identifier('fetchData')), [types.objectExpression(fetchParams)]), types.identifier('catch')), [
2450
+ 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')), [
2279
2451
  types.arrowFunctionExpression([types.identifier('error')], types.blockStatement([
2280
2452
  types.expressionStatement(types.callExpression(types.memberExpression(types.identifier('console'), types.identifier('error')), [
2281
2453
  types.stringLiteral("Error fetching ".concat(vars.propsPrefix, ":")),
@@ -2378,7 +2550,7 @@ function updateGetStaticProps(chunks, registry, dependencies, folderPath, locali
2378
2550
  countParams.push(types.objectProperty(types.identifier('filters'), buildStaticFiltersStringifyCall(staticFiltersForCount)));
2379
2551
  }
2380
2552
  appendItemsPathParam(countParams, firstUsage.itemsPath, 'collectionPath');
2381
- 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')), [
2553
+ fetchesArray.elements.push(types.callExpression(types.memberExpression(types.callExpression(types.memberExpression(types.identifier(fetcherImportName), types.identifier('fetchCount')), countReadArguments(countParams, readArguments())), types.identifier('catch')), [
2382
2554
  types.arrowFunctionExpression([types.identifier('error')], types.blockStatement([
2383
2555
  types.expressionStatement(types.callExpression(types.memberExpression(types.identifier('console'), types.identifier('error')), [
2384
2556
  types.stringLiteral("Error fetching ".concat(countVarName, ":")),