@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
@@ -85,6 +85,8 @@ var loading_state_1 = require("./loading-state");
85
85
  var config_1 = require("./cache/config");
86
86
  var ast_1 = require("./cache/ast");
87
87
  var pagination_controls_1 = require("./pagination-controls");
88
+ var range_control_1 = require("./range-control");
89
+ var search_input_1 = require("./search-input");
88
90
  var count_effect_1 = require("./count-effect");
89
91
  var search_clear_1 = require("./search-clear");
90
92
  var numbered_pagination_1 = require("./numbered-pagination");
@@ -299,7 +301,7 @@ function buildFilterDestinationExpression(destination) {
299
301
  return teleport_plugin_common_1.ASTUtils.convertFilterDestinationToExpression(destination);
300
302
  }
301
303
  // Scan UIDL to find all data source usages and build a registry
302
- function buildStateRegistry(uidlNode) {
304
+ function buildStateRegistry(uidlNode, stateDefinitions) {
303
305
  var usages = [];
304
306
  var byDataSourceId = new Map();
305
307
  var byArrayMapperRenderProp = new Map();
@@ -356,16 +358,19 @@ function buildStateRegistry(uidlNode) {
356
358
  // is skipped for a dynamic sort) — the pre-rendered HTML carried no
357
359
  // posts, and the browser fetched page 1 a second time. Only a sort
358
360
  // bound to state (the admin panel's sortable columns) is dynamic.
359
- var dynamicSort = void 0;
360
- if ((!sorts || sorts.length === 0) && content.sort) {
361
- var fixedSort = (0, sort_utils_1.readFixedSort)(content.sort, content.sortDirection);
362
- if (fixedSort) {
363
- sorts = [fixedSort];
364
- }
365
- else {
366
- dynamicSort = (0, sort_utils_1.extractDynamicSort)(content.sort, content.sortDirection);
367
- }
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];
368
371
  }
372
+ var dynamicSort = repeaterSort.dynamic;
373
+ var startingSort = repeaterSort.starting;
369
374
  // Extract filters from parent's resource params, keeping the group tree
370
375
  // the inspector authored (see `readFilterTree`).
371
376
  var filters = [];
@@ -433,6 +438,7 @@ function buildStateRegistry(uidlNode) {
433
438
  index: index++,
434
439
  dataSourceIdentifier: parentDataSource.identifier,
435
440
  arrayMapperRenderProp: arrayMapperRenderProp,
441
+ nodeId: typeof content.nodeId === 'string' ? content.nodeId : undefined,
436
442
  resourceDefinition: {
437
443
  dataSourceId: parentDataSource.resourceDef.dataSourceId || '',
438
444
  tableName: parentDataSource.resourceDef.tableName || '',
@@ -447,6 +453,7 @@ function buildStateRegistry(uidlNode) {
447
453
  queryColumns: queryColumns,
448
454
  sorts: sorts,
449
455
  dynamicSort: dynamicSort,
456
+ startingSort: startingSort,
450
457
  filters: filters,
451
458
  filterStateIds: filterStateIds,
452
459
  filterPropIds: filterPropIds,
@@ -528,6 +535,8 @@ function getStateVarsForUsage(usage) {
528
535
  setPageStateVar: "setDs_".concat(idx, "_page"),
529
536
  maxPagesStateVar: "ds_".concat(idx, "_maxPages"),
530
537
  setMaxPagesStateVar: "setDs_".concat(idx, "_maxPages"),
538
+ totalStateVar: "ds_".concat(idx, "_total"),
539
+ setTotalStateVar: "setDs_".concat(idx, "_total"),
531
540
  searchQueryVar: "ds_".concat(idx, "_searchQuery"),
532
541
  setSearchQueryVar: "setDs_".concat(idx, "_searchQuery"),
533
542
  debouncedSearchQueryVar: "ds_".concat(idx, "_debouncedQuery"),
@@ -605,19 +614,27 @@ function pushSearchUrlSyncEffects(effectStatements, usage, vars) {
605
614
  effectStatements.push(teleport_plugin_common_1.URLSearchParamSync.buildUrlWriteBackEffect(usage.searchUrlParamKey, buildDebouncedValueExpr(), buildDebouncedValueExpr(), undefined, adoptedRefVar));
606
615
  // read-back: `?<searchUrlParamKey>=` → the input AND the applied query at once
607
616
  // (`ds_N_adoptUrlSearch`), so an address is not a typed change: it skips the
608
- // debounce and keeps the page — `?searchKeyword=x&page=3` lands on page 3.
617
+ // debounce and keeps a page the address carries — `?searchKeyword=x&page=3`
618
+ // lands on page 3.
609
619
  effectStatements.push(teleport_plugin_common_1.URLSearchParamSync.buildUrlReadBackEffect(usage.searchUrlParamKey, adopterVar, undefined, adoptedRefVar));
610
620
  }
611
621
  // `const ds_N_adoptUrlSearch = (update) => { … }`: the setter the search's URL
612
622
  // read-back calls with its `(prev) => next` updater. It moves the input and the
613
- // applied (debounced) query together; the combined paginated state keeps its
614
- // page, which a typed change resets to 1 in the debounce effect.
623
+ // applied (debounced) query together. A list whose page is in the address
624
+ // keeps its page — the page sync sets it from the same address. Any other
625
+ // list starts the new query on page 1, as a typed change does in the debounce
626
+ // effect: an appending list has usually advanced past page 1 by the time the
627
+ // router is ready, and the query's first answer would otherwise be its page 2
628
+ // — empty for a query only a few rows match.
615
629
  function buildUrlSearchAdopter(adopterVar, usage, vars) {
616
630
  var update = types.identifier('update');
631
+ var pageReset = usageHasPageUrlSync(usage)
632
+ ? []
633
+ : [types.objectProperty(types.identifier('page'), types.numericLiteral(1))];
617
634
  var applyToDebounced = usage.category === 'paginated+search'
618
635
  ? // setDs_N_state((state) => {
619
636
  // const next = update(state.debouncedQuery)
620
- // return next === state.debouncedQuery ? state : { ...state, debouncedQuery: next }
637
+ // return next === state.debouncedQuery ? state : { ...state, page: 1, debouncedQuery: next }
621
638
  // })
622
639
  types.callExpression(types.identifier(vars.setCombinedStateVar), [
623
640
  types.arrowFunctionExpression([types.identifier('state')], types.blockStatement([
@@ -626,10 +643,11 @@ function buildUrlSearchAdopter(adopterVar, usage, vars) {
626
643
  types.memberExpression(types.identifier('state'), types.identifier('debouncedQuery')),
627
644
  ])),
628
645
  ]),
629
- types.returnStatement(types.conditionalExpression(types.binaryExpression('===', types.identifier('next'), types.memberExpression(types.identifier('state'), types.identifier('debouncedQuery'))), types.identifier('state'), types.objectExpression([
630
- types.spreadElement(types.identifier('state')),
646
+ types.returnStatement(types.conditionalExpression(types.binaryExpression('===', types.identifier('next'), types.memberExpression(types.identifier('state'), types.identifier('debouncedQuery'))), types.identifier('state'), types.objectExpression(__spreadArray(__spreadArray([
647
+ types.spreadElement(types.identifier('state'))
648
+ ], pageReset, true), [
631
649
  types.objectProperty(types.identifier('debouncedQuery'), types.identifier('next')),
632
- ]))),
650
+ ], false)))),
633
651
  ])),
634
652
  ])
635
653
  : types.callExpression(types.identifier(vars.setDebouncedSearchQueryVar), [update]);
@@ -792,7 +810,7 @@ function pushFilterSortPageResetEffect(stateDeclarations, effectStatements, usag
792
810
  // ==================== MAIN PLUGIN ====================
793
811
  var createNextArrayMapperPaginationPlugin = function () {
794
812
  var paginationPlugin = function (structure) { return __awaiter(void 0, void 0, void 0, function () {
795
- 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;
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;
796
814
  var _a, _b;
797
815
  return __generator(this, function (_c) {
798
816
  uidl = structure.uidl, chunks = structure.chunks, dependencies = structure.dependencies, options = structure.options;
@@ -816,7 +834,9 @@ var createNextArrayMapperPaginationPlugin = function () {
816
834
  return [2 /*return*/, structure];
817
835
  }
818
836
  blockStatement = arrowFunction.body;
819
- registry = buildStateRegistry(uidl.node);
837
+ rangeControls = (0, range_control_1.findRangeControls)(blockStatement);
838
+ hasRangeControl = rangeControls.length > 0;
839
+ registry = buildStateRegistry(uidl.node, uidl.stateDefinitions);
820
840
  if (registry.usages.length === 0) {
821
841
  return [2 /*return*/, structure];
822
842
  }
@@ -984,11 +1004,15 @@ var createNextArrayMapperPaginationPlugin = function () {
984
1004
  (0, filter_utils_1.pushPropIdsAsDeps)(countEffectDeps, countEffectSeen, usage.filterPropIds);
985
1005
  // Same goes for URL-driven filters (already documented above).
986
1006
  pushUrlSearchParamMemoDeps(countEffectDeps, usage);
1007
+ if (hasRangeControl) {
1008
+ stateDeclarations.push(buildTotalDeclaration(vars));
1009
+ }
987
1010
  stateDeclarations.push((0, count_effect_1.buildCountSeqDeclaration)(vars.countSeqRefVar));
988
1011
  effectStatements.push((0, count_effect_1.buildCountFetchEffect)({
989
1012
  fileName: fileName,
990
1013
  perPage: usage.perPage,
991
1014
  setMaxPagesVar: vars.setMaxPagesStateVar,
1015
+ setTotalVar: hasRangeControl ? vars.setTotalStateVar : undefined,
992
1016
  urlParams: urlParams,
993
1017
  deps: countEffectDeps,
994
1018
  countSeqRefVar: vars.countSeqRefVar,
@@ -1030,7 +1054,9 @@ var createNextArrayMapperPaginationPlugin = function () {
1030
1054
  // "Next" button, but not to label a numbered strip or to know when an
1031
1055
  // infinite list has reached its end, so those two modes fetch a live
1032
1056
  // count as well.
1033
- var needsLiveCount = !isPage || usage.paginationMode === 'numbered' || usage.infiniteScroll;
1057
+ // A range text needs the live count too: the build-time count only
1058
+ // ever became a page count, never the total it shows.
1059
+ var needsLiveCount = !isPage || usage.paginationMode === 'numbered' || usage.infiniteScroll || hasRangeControl;
1034
1060
  if (needsLiveCount) {
1035
1061
  var fileName = (0, utils_1.generateSafeFileName)(usage.resourceDefinition.dataSourceType, usage.resourceDefinition.tableName, usage.resourceDefinition.dataSourceId);
1036
1062
  // ⛔ This list has no search input, so no `query` param — but it may
@@ -1052,11 +1078,15 @@ var createNextArrayMapperPaginationPlugin = function () {
1052
1078
  (0, filter_utils_1.pushStateIdsAsDeps)(countDeps, countDepsSeen, usage.filterStateIds);
1053
1079
  (0, filter_utils_1.pushPropIdsAsDeps)(countDeps, countDepsSeen, usage.filterPropIds);
1054
1080
  pushUrlSearchParamMemoDeps(countDeps, usage);
1081
+ if (hasRangeControl) {
1082
+ stateDeclarations.push(buildTotalDeclaration(vars));
1083
+ }
1055
1084
  stateDeclarations.push((0, count_effect_1.buildCountSeqDeclaration)(vars.countSeqRefVar));
1056
1085
  effectStatements.push((0, count_effect_1.buildCountFetchEffect)({
1057
1086
  fileName: fileName,
1058
1087
  perPage: usage.perPage,
1059
1088
  setMaxPagesVar: vars.setMaxPagesStateVar,
1089
+ setTotalVar: hasRangeControl ? vars.setTotalStateVar : undefined,
1060
1090
  urlParams: countUrlParams,
1061
1091
  deps: countDeps,
1062
1092
  countSeqRefVar: vars.countSeqRefVar,
@@ -1141,6 +1171,7 @@ var createNextArrayMapperPaginationPlugin = function () {
1141
1171
  usagesWithLoadingState = new Set();
1142
1172
  usageByProviderNode = new Map();
1143
1173
  cacheDeclarations = [];
1174
+ firstPageDeclarations = isPage ? [] : undefined;
1144
1175
  dataProvidersWithRepeaters.forEach(function (dp) {
1145
1176
  var _a, _b;
1146
1177
  var nameAttr = dp.openingElement.attributes.find(function (attr) { return attr.type === 'JSXAttribute' && attr.name.name === 'name'; });
@@ -1161,10 +1192,10 @@ var createNextArrayMapperPaginationPlugin = function () {
1161
1192
  var fileName = (0, utils_1.generateSafeFileName)(usage.resourceDefinition.dataSourceType, usage.resourceDefinition.tableName, usage.resourceDefinition.dataSourceId);
1162
1193
  // Update DataProvider based on category
1163
1194
  if (usage.category === 'paginated+search') {
1164
- updateDataProviderForPaginatedSearch(dp, usage, vars, fileName, cacheDeclarations, localized);
1195
+ updateDataProviderForPaginatedSearch(dp, usage, vars, fileName, cacheDeclarations, localized, firstPageDeclarations);
1165
1196
  }
1166
1197
  else if (usage.category === 'paginated-only') {
1167
- updateDataProviderForPaginationOnly(dp, usage, vars, fileName, cacheDeclarations, localized);
1198
+ updateDataProviderForPaginationOnly(dp, usage, vars, fileName, cacheDeclarations, localized, firstPageDeclarations);
1168
1199
  }
1169
1200
  else if (usage.category === 'search-only') {
1170
1201
  updateDataProviderForSearchOnly(dp, usage, vars, fileName, cacheDeclarations, localized);
@@ -1176,9 +1207,14 @@ var createNextArrayMapperPaginationPlugin = function () {
1176
1207
  // change refetches, instead of leaving the previous rows on screen with
1177
1208
  // no feedback. Runs last so it sees the `fetchData` and
1178
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.
1179
1212
  var loadingVars = (0, loading_state_1.getLoadingStateVars)(usage.index);
1180
- if ((0, loading_state_1.applyLoadingStateToDataProvider)(dp, loadingVars)) {
1181
- 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));
1182
1218
  // Recorded because the loading state is only declared for a provider
1183
1219
  // that HAS a loading slot; a Load More button wired later may only
1184
1220
  // reference `ds_N_isFetching` if it actually exists.
@@ -1187,7 +1223,9 @@ var createNextArrayMapperPaginationPlugin = function () {
1187
1223
  // An auto-loading list needs something at the end of the rows for its
1188
1224
  // IntersectionObserver to watch.
1189
1225
  if (usage.infiniteScroll && usage.paginated && !usage.infiniteScrollLoadMore) {
1190
- insertSentinelAfterProvider(blockStatement, dp, (0, infinite_scroll_1.buildSentinelElement)((0, infinite_scroll_1.getInfiniteScrollVars)(usage.index)));
1226
+ insertSentinelAfterProvider(blockStatement, dp, function (parentTag) {
1227
+ return (0, infinite_scroll_1.buildSentinelElement)((0, infinite_scroll_1.getInfiniteScrollVars)(usage.index), parentTag);
1228
+ });
1191
1229
  }
1192
1230
  // Create API route for all categories (including 'plain' for components)
1193
1231
  ensureAPIRouteExists(options.extractedResources, usage, options.dataSources, (0, transformations_1.buildProductTransformOptions)(options));
@@ -1221,6 +1259,11 @@ var createNextArrayMapperPaginationPlugin = function () {
1221
1259
  teleport_shared_1.ASTStatementOrder.insertStatementsAfterDependencies(blockStatement, cacheDeclarations);
1222
1260
  (0, ast_1.registerCacheClientImports)(dependencies, (_a = uidl.outputOptions) === null || _a === void 0 ? void 0 : _a.folderPath);
1223
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
+ }
1224
1267
  dataProvidersWithoutRepeaters = dataProviders.filter(function (dp) {
1225
1268
  var hasRepeater = findArrayMapperRenderPropInDataProvider(dp) !== undefined;
1226
1269
  return !hasRepeater;
@@ -1228,18 +1271,15 @@ var createNextArrayMapperPaginationPlugin = function () {
1228
1271
  dataProvidersWithoutRepeaters.forEach(function (dp) {
1229
1272
  stabilizeDataProviderWithoutRepeater(dp);
1230
1273
  });
1231
- searchInputs = findAllSearchInputsInJSX(blockStatement);
1232
- searchEnabledUsages = registry.usages.filter(function (u) { return u.searchEnabled; });
1233
- usageBySearchInput = new Map();
1234
- searchInputs.forEach(function (input, idx) {
1235
- if (idx >= searchEnabledUsages.length) {
1236
- return;
1274
+ searchInputs = (0, search_input_1.findAllSearchInputsInJSX)(blockStatement);
1275
+ usageBySearchInput = (0, search_input_1.pairSearchInputs)(searchInputs, registry.usages);
1276
+ for (_i = 0, searchInputs_1 = searchInputs; _i < searchInputs_1.length; _i++) {
1277
+ input = searchInputs_1[_i];
1278
+ usage = usageBySearchInput.get(input.node);
1279
+ if (usage) {
1280
+ (0, search_input_1.wireSearchInput)(input, getStateVarsForUsage(usage));
1237
1281
  }
1238
- var usage = searchEnabledUsages[idx];
1239
- var vars = getStateVarsForUsage(usage);
1240
- wireSearchInput(input.node, vars);
1241
- usageBySearchInput.set(input.node, usage);
1242
- });
1282
+ }
1243
1283
  // STEP 4.5: Wire the clear buttons, each to the input it shares a parent with.
1244
1284
  (0, search_clear_1.findAllSearchClearButtonsInJSX)(blockStatement).forEach(function (button) {
1245
1285
  var input = (0, search_clear_1.findSiblingSearchInput)(button, searchInputs.map(function (entry) { return entry.node; }));
@@ -1266,9 +1306,29 @@ var createNextArrayMapperPaginationPlugin = function () {
1266
1306
  }
1267
1307
  wirePaginationWidget(node, usage, getStateVarsForUsage(usage), usagesWithLoadingState.has(usage.index));
1268
1308
  });
1269
- // STEP 6: Update getStaticProps if this is a page
1309
+ // STEP 5b: Wire the range texts that name their mapper.
1310
+ //
1311
+ // A `range` control carrying `data-tq-pagination-for` belongs to the mapper
1312
+ // whose editor node id it names, wherever it sits — a template's footer or
1313
+ // toolbar is laid out by the author, not inside a pagination container, so
1314
+ // the container-ownership rule above cannot reach it. One without the
1315
+ // attribute was wired by its container in STEP 5; one naming a mapper this
1316
+ // component does not render stays as drawn.
1317
+ rangeControls.forEach(function (control) {
1318
+ var ownerNodeId = (0, pagination_controls_1.readJSXAttributeString)(control, pagination_controls_1.PAGINATION_FOR_ATTR);
1319
+ if (!ownerNodeId) {
1320
+ return;
1321
+ }
1322
+ var owner = registry.usages.find(function (usage) { return usage.nodeId === ownerNodeId; });
1323
+ if (owner) {
1324
+ wireRangeControlFor(control, owner, getStateVarsForUsage(owner));
1325
+ }
1326
+ });
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`).
1270
1330
  if (isPage) {
1271
- 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); });
1272
1332
  }
1273
1333
  return [2 /*return*/, structure];
1274
1334
  });
@@ -1284,39 +1344,59 @@ exports.createNextArrayMapperPaginationPlugin = createNextArrayMapperPaginationP
1284
1344
  * It has to be a sibling: the provider's own child is a render-prop function,
1285
1345
  * and anything placed inside it would be re-created on every fetch — the
1286
1346
  * observer would then be watching an element that is no longer in the document.
1347
+ * The sentinel is built for the element it lands in (`buildSentinelElement`):
1348
+ * a fragment is transparent, a component's children may go anywhere.
1287
1349
  *
1288
1350
  * Returns false when the provider has no element parent to splice into (a
1289
1351
  * provider returned directly from the component), leaving the list on its
1290
1352
  * count-driven `hasMore` rather than injecting a stray node.
1291
1353
  */
1292
- function insertSentinelAfterProvider(blockStatement, provider, sentinel) {
1354
+ function insertSentinelAfterProvider(blockStatement, provider, buildSentinel) {
1293
1355
  var inserted = false;
1294
- var traverse = function (node) {
1356
+ var elementTagOf = function (node, enclosing) {
1357
+ var _a;
1358
+ if ((node === null || node === void 0 ? void 0 : node.type) === 'JSXFragment') {
1359
+ return enclosing;
1360
+ }
1361
+ if ((node === null || node === void 0 ? void 0 : node.type) !== 'JSXElement') {
1362
+ return enclosing;
1363
+ }
1364
+ var name = (_a = node.openingElement) === null || _a === void 0 ? void 0 : _a.name;
1365
+ if ((name === null || name === void 0 ? void 0 : name.type) !== 'JSXIdentifier') {
1366
+ return null;
1367
+ }
1368
+ if (name.name === 'Fragment') {
1369
+ return enclosing;
1370
+ }
1371
+ return /^[a-z]/.test(name.name) ? name.name : null;
1372
+ };
1373
+ var traverse = function (node, enclosing) {
1295
1374
  if (!node || inserted) {
1296
1375
  return;
1297
1376
  }
1377
+ var tag = elementTagOf(node, enclosing);
1298
1378
  if (Array.isArray(node.children)) {
1299
1379
  var at = node.children.indexOf(provider);
1300
1380
  if (at !== -1) {
1301
- node.children.splice(at + 1, 0, sentinel);
1381
+ node.children.splice(at + 1, 0, buildSentinel(tag));
1302
1382
  inserted = true;
1303
1383
  return;
1304
1384
  }
1305
- node.children.forEach(traverse);
1385
+ node.children.forEach(function (child) { return traverse(child, tag); });
1306
1386
  }
1307
1387
  if (node.body) {
1308
1388
  if (Array.isArray(node.body)) {
1309
- node.body.forEach(traverse);
1389
+ node.body.forEach(function (child) { return traverse(child, tag); });
1310
1390
  }
1311
1391
  else {
1312
- traverse(node.body);
1392
+ traverse(node.body, tag);
1313
1393
  }
1314
1394
  }
1315
1395
  if (node.consequent) {
1316
- traverse(node.consequent);
1396
+ traverse(node.consequent, tag);
1317
1397
  }
1318
1398
  if (node.alternate) {
1319
- traverse(node.alternate);
1399
+ traverse(node.alternate, tag);
1320
1400
  }
1321
1401
  // `{activeTabIndex === 0 && (<div>…<DataProvider/>…</div>)}` — a tab
1322
1402
  // panel, a gated section: the provider sits on the RIGHT of a logical
@@ -1324,22 +1404,22 @@ function insertSentinelAfterProvider(blockStatement, provider, sentinel) {
1324
1404
  // an argument. Left unvisited, the products list under the first tab of
1325
1405
  // a shop was never paginated or seeded, and rendered nothing.
1326
1406
  if (node.left) {
1327
- traverse(node.left);
1407
+ traverse(node.left, tag);
1328
1408
  }
1329
1409
  if (node.right) {
1330
- traverse(node.right);
1410
+ traverse(node.right, tag);
1331
1411
  }
1332
1412
  if (node.test) {
1333
- traverse(node.test);
1413
+ traverse(node.test, tag);
1334
1414
  }
1335
1415
  if (node.expression) {
1336
- traverse(node.expression);
1416
+ traverse(node.expression, tag);
1337
1417
  }
1338
1418
  if (node.argument) {
1339
- traverse(node.argument);
1419
+ traverse(node.argument, tag);
1340
1420
  }
1341
1421
  };
1342
- traverse(blockStatement);
1422
+ traverse(blockStatement, null);
1343
1423
  return inserted;
1344
1424
  }
1345
1425
  function findAllDataProvidersInJSX(blockStatement) {
@@ -1434,61 +1514,6 @@ function findArrayMapperRenderPropInDataProvider(dataProvider) {
1434
1514
  }
1435
1515
  return renderItemAttr.value.expression.params[0].name;
1436
1516
  }
1437
- function findAllSearchInputsInJSX(blockStatement) {
1438
- var results = [];
1439
- var traverse = function (node) {
1440
- var _a, _b, _c, _d, _e, _f;
1441
- if (!node) {
1442
- return;
1443
- }
1444
- 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') {
1445
- var classAttr = (_c = node.openingElement.attributes) === null || _c === void 0 ? void 0 : _c.find(function (attr) { return attr.type === 'JSXAttribute' && attr.name.name === 'className'; });
1446
- 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) || '';
1447
- if (className.includes('search-input')) {
1448
- results.push({ node: node, className: className });
1449
- }
1450
- }
1451
- if (node.children && Array.isArray(node.children)) {
1452
- node.children.forEach(function (c) { return traverse(c); });
1453
- }
1454
- if (node.body) {
1455
- if (Array.isArray(node.body)) {
1456
- node.body.forEach(function (s) { return traverse(s); });
1457
- }
1458
- else {
1459
- traverse(node.body);
1460
- }
1461
- }
1462
- if (node.consequent) {
1463
- traverse(node.consequent);
1464
- }
1465
- if (node.alternate) {
1466
- traverse(node.alternate);
1467
- }
1468
- // `{activeTabIndex === 0 && (<div>…<DataProvider/>…</div>)}` — a tab
1469
- // panel, a gated section: the provider sits on the RIGHT of a logical
1470
- // expression (or in a ternary's `test`), which is not a child, a body or
1471
- // an argument. Left unvisited, the products list under the first tab of
1472
- // a shop was never paginated or seeded, and rendered nothing.
1473
- if (node.left) {
1474
- traverse(node.left);
1475
- }
1476
- if (node.right) {
1477
- traverse(node.right);
1478
- }
1479
- if (node.test) {
1480
- traverse(node.test);
1481
- }
1482
- if (node.expression) {
1483
- traverse(node.expression);
1484
- }
1485
- if (node.argument) {
1486
- traverse(node.argument);
1487
- }
1488
- };
1489
- traverse(blockStatement);
1490
- return results;
1491
- }
1492
1517
  /**
1493
1518
  * Every pagination widget in the JSX, paired with the mapper it belongs to.
1494
1519
  *
@@ -1563,34 +1588,97 @@ function findPaginationNodesWithOwners(blockStatement, usageByProviderNode) {
1563
1588
  *
1564
1589
  * An uncached mapper keeps the inline `params={useMemo(...)}` it has always had,
1565
1590
  * byte for byte. A cached one hoists that memo into a component const so the
1566
- * 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.
1567
1594
  *
1568
- * Returns the identifier holding the peeked rows, or `undefined` when this
1569
- * 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.
1570
1597
  */
1571
1598
  function pushParamsAttribute(
1572
1599
  // tslint:disable-next-line:no-any
1573
- dp, usage, paramsProps, memoDeps, cacheDeclarations) {
1600
+ dp, usage, paramsProps, memoDeps, cacheDeclarations, hoistInto) {
1574
1601
  var _a;
1575
1602
  var paramsMemo = types.callExpression(types.identifier('useMemo'), [
1576
1603
  types.arrowFunctionExpression([], types.objectExpression(paramsProps)),
1577
1604
  types.arrayExpression(memoDeps),
1578
1605
  ]);
1579
- // A state-bound sort deliberately suppresses `initialData` (see the comment at
1580
- // the call site): the prefetch ran without sort params, so reusing anything on
1581
- // mount would mask the current sort. The cache peek is `initialData` by
1582
- // 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.
1583
1610
  var cache = ((_a = usage.cache) === null || _a === void 0 ? void 0 : _a.client) && !usage.dynamicSort ? usage.cache : undefined;
1584
1611
  if (!cache) {
1585
- dp.openingElement.attributes.push(types.jsxAttribute(types.jsxIdentifier('params'), types.jsxExpressionContainer(paramsMemo)));
1586
- 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 };
1587
1622
  }
1588
1623
  var hoisted = (0, ast_1.buildCachedParamsDeclarations)({ index: usage.index, paramsMemo: paramsMemo, cache: cache });
1589
1624
  cacheDeclarations.push.apply(cacheDeclarations, hoisted.declarations);
1590
1625
  dp.openingElement.attributes.push(types.jsxAttribute(types.jsxIdentifier('params'), types.jsxExpressionContainer(types.identifier(hoisted.paramsIdentifier))));
1591
- 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);
1650
+ }
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))));
1592
1680
  }
1593
- function updateDataProviderForPaginatedSearch(dp, usage, vars, fileName, cacheDeclarations, localized) {
1681
+ function updateDataProviderForPaginatedSearch(dp, usage, vars, fileName, cacheDeclarations, localized, firstPageDeclarations) {
1594
1682
  var attrs = dp.openingElement.attributes;
1595
1683
  // Remove existing params, key, initialData, fetchData, persistDataDuringLoading
1596
1684
  dp.openingElement.attributes = attrs.filter(function (attr) {
@@ -1623,20 +1711,29 @@ function updateDataProviderForPaginatedSearch(dp, usage, vars, fileName, cacheDe
1623
1711
  if (localized) {
1624
1712
  (0, request_locale_1.appendClientLocaleParam)(paramsProps, memoDeps);
1625
1713
  }
1626
- var cachedInitialData = pushParamsAttribute(dp, usage, paramsProps, memoDeps, cacheDeclarations);
1627
- // Add initialData. Skip the server-prefetched data entirely when a dynamic
1628
- // state-bound sort is active — the prefetch ran without sort parameters, so
1629
- // reusing it would mask the current sort state AND cause DataProvider to skip
1630
- // the first client fetch (its internal guard only skips when initialData is
1631
- // defined on mount). Without that skip, toggling sort correctly triggers a
1632
- // 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.
1633
1723
  //
1634
- // An appending list skips it for a second reason: `initialData` makes
1635
- // DataProvider skip its first fetch, so page 1 would render from the prefetch
1636
- // and never enter the accumulator — appending page 2 would then REPLACE the
1637
- // visible rows with page 2 alone.
1638
- if (!usage.dynamicSort && !usage.infiniteScroll) {
1639
- 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);
1640
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')))));
1641
1738
  }
1642
1739
  // Add key. Sort is intentionally NOT part of the key — a key change would
@@ -1674,7 +1771,7 @@ function updateDataProviderForPaginatedSearch(dp, usage, vars, fileName, cacheDe
1674
1771
  // Add persistDataDuringLoading
1675
1772
  dp.openingElement.attributes.push(types.jsxAttribute(types.jsxIdentifier('persistDataDuringLoading'), types.jsxExpressionContainer(types.booleanLiteral(true))));
1676
1773
  }
1677
- function updateDataProviderForPaginationOnly(dp, usage, vars, fileName, cacheDeclarations, localized) {
1774
+ function updateDataProviderForPaginationOnly(dp, usage, vars, fileName, cacheDeclarations, localized, firstPageDeclarations) {
1678
1775
  var attrs = dp.openingElement.attributes;
1679
1776
  dp.openingElement.attributes = attrs.filter(function (attr) {
1680
1777
  var _a;
@@ -1703,12 +1800,17 @@ function updateDataProviderForPaginationOnly(dp, usage, vars, fileName, cacheDec
1703
1800
  (0, request_locale_1.appendClientLocaleParam)(paramsProps, memoDeps);
1704
1801
  }
1705
1802
  // Add params
1706
- var cachedInitialData = pushParamsAttribute(dp, usage, paramsProps, memoDeps, cacheDeclarations);
1707
- // Add initialData. See paginated+search updater for why we skip prefetch
1708
- // reuse when a dynamic state-bound sort is active, and why an appending list
1709
- // skips it too.
1710
- if (!usage.dynamicSort && !usage.infiniteScroll) {
1711
- 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')))));
1712
1814
  }
1713
1815
  // Add key — sort is intentionally NOT included; see paginated+search updater,
1714
1816
  // which also explains why an appending list keys on nothing at all (there is
@@ -1758,11 +1860,11 @@ function updateDataProviderForSearchOnly(dp, usage, vars, fileName, cacheDeclara
1758
1860
  if (localized) {
1759
1861
  (0, request_locale_1.appendClientLocaleParam)(paramsProps, memoDeps);
1760
1862
  }
1761
- var cachedInitialData = pushParamsAttribute(dp, usage, paramsProps, memoDeps, cacheDeclarations);
1762
- // Add initialData. See paginated+search updater for why we skip prefetch
1763
- // reuse when a dynamic state-bound sort is active.
1764
- if (!usage.dynamicSort) {
1765
- 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')))));
1766
1868
  }
1767
1869
  // Add key — sort is intentionally NOT included; see paginated+search updater.
1768
1870
  dp.openingElement.attributes.push(types.jsxAttribute(types.jsxIdentifier('key'), types.jsxExpressionContainer(types.templateLiteral([
@@ -1773,14 +1875,42 @@ function updateDataProviderForSearchOnly(dp, usage, vars, fileName, cacheDeclara
1773
1875
  dp.openingElement.attributes.push(createFetchDataAttribute(fileName, ((_a = usage.cache) === null || _a === void 0 ? void 0 : _a.client) ? usage.cache : undefined));
1774
1876
  dp.openingElement.attributes.push(types.jsxAttribute(types.jsxIdentifier('persistDataDuringLoading'), types.jsxExpressionContainer(types.booleanLiteral(true))));
1775
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
+ }
1776
1911
  function updateDataProviderForPlain(dp, fileName, usage, localized) {
1777
1912
  var _a, _b, _c, _d, _e, _f;
1778
- // A sort bound to state is the visitor's, which the server cannot know: rows
1779
- // pre-rendered in the table's order would stand in for it, since a seeded
1780
- // provider skips its first fetch — the paginated lists drop them the same way.
1781
- if (usage.dynamicSort) {
1782
- 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'); });
1783
- }
1913
+ seedPlainListWhileSortStarts(dp, usage);
1784
1914
  var attrs = dp.openingElement.attributes;
1785
1915
  var clientCache = ((_a = usage.cache) === null || _a === void 0 ? void 0 : _a.client) ? usage.cache : undefined;
1786
1916
  // Check if fetchData already exists
@@ -1933,21 +2063,18 @@ accumulate) {
1933
2063
  ? (0, ast_1.buildCacheStoreThen)(cache)
1934
2064
  : uncachedTail;
1935
2065
  var networkChain = types.callExpression(types.memberExpression(parsedJson, types.identifier('then')), [responseTail]);
2066
+ // An appending list records the request it now stands on before making it
2067
+ // (see `buildAccumulatingRequestMark`).
1936
2068
  var arrow = cache
1937
2069
  ? types.arrowFunctionExpression([types.identifier('params')], (0, ast_1.buildCachedFetchBody)(networkChain, cache))
1938
- : types.arrowFunctionExpression([types.identifier('params')], networkChain);
2070
+ : accumulate
2071
+ ? types.arrowFunctionExpression([types.identifier('params')], types.blockStatement([
2072
+ (0, infinite_scroll_1.buildAccumulatingRequestMark)((0, infinite_scroll_1.getInfiniteScrollVars)(accumulate.usage.index)),
2073
+ types.returnStatement(networkChain),
2074
+ ]))
2075
+ : types.arrowFunctionExpression([types.identifier('params')], networkChain);
1939
2076
  return types.jsxAttribute(types.jsxIdentifier('fetchData'), types.jsxExpressionContainer(types.callExpression(types.identifier('useCallback'), [arrow, types.arrayExpression([])])));
1940
2077
  }
1941
- function wireSearchInput(inputNode, vars) {
1942
- // Remove existing onChange and value
1943
- 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); });
1944
- // Add onChange
1945
- inputNode.openingElement.attributes.push(types.jsxAttribute(types.jsxIdentifier('onChange'), types.jsxExpressionContainer(types.arrowFunctionExpression([types.identifier('e')], types.callExpression(types.identifier(vars.setSearchQueryVar), [
1946
- types.memberExpression(types.memberExpression(types.identifier('e'), types.identifier('target')), types.identifier('value')),
1947
- ])))));
1948
- // Add value
1949
- inputNode.openingElement.attributes.push(types.jsxAttribute(types.jsxIdentifier('value'), types.jsxExpressionContainer(types.identifier(vars.searchQueryVar))));
1950
- }
1951
2078
  /**
1952
2079
  * The `fetchData` attribute for one paginated usage.
1953
2080
  *
@@ -1963,6 +2090,32 @@ function buildFetchDataForUsage(fileName, usage) {
1963
2090
  }
1964
2091
  return createFetchDataAttribute(fileName, ((_a = usage.cache) === null || _a === void 0 ? void 0 : _a.client) ? usage.cache : undefined);
1965
2092
  }
2093
+ /** `const [ds_N_total, setDs_N_total] = useState(null)` — declared only beside a range control. */
2094
+ function buildTotalDeclaration(vars) {
2095
+ return types.variableDeclaration('const', [
2096
+ types.variableDeclarator(types.arrayPattern([
2097
+ types.identifier(vars.totalStateVar),
2098
+ types.identifier(vars.setTotalStateVar),
2099
+ ]), types.callExpression(types.identifier('useState'), [types.nullLiteral()])),
2100
+ ]);
2101
+ }
2102
+ /**
2103
+ * Fills one `range` text with the live row range of `usage` (see
2104
+ * `range-control.ts`). A mapper that does not page has no count to show, so
2105
+ * its range text is left as drawn.
2106
+ */
2107
+ function wireRangeControlFor(control, usage, vars) {
2108
+ if (!usage.paginated) {
2109
+ return;
2110
+ }
2111
+ var currentPageExpr = getPageAccessors(usage, vars).currentPageExpr;
2112
+ (0, range_control_1.wireRangeControl)(control, {
2113
+ totalVar: vars.totalStateVar,
2114
+ currentPageExpr: currentPageExpr(),
2115
+ perPage: usage.perPage,
2116
+ infiniteScroll: usage.infiniteScroll,
2117
+ });
2118
+ }
1966
2119
  /**
1967
2120
  * Wires the authored Load More button of an appending list.
1968
2121
  *
@@ -2002,6 +2155,11 @@ function wireLoadMoreButton(paginationNode, usage, vars, hasLoadingState) {
2002
2155
  * buttons beside its Load More.
2003
2156
  */
2004
2157
  function wirePaginationWidget(paginationNode, usage, vars, hasLoadingState) {
2158
+ // A range text inside the container takes the container's owner, like the
2159
+ // buttons beside it; one naming its own mapper is wired in STEP 5b instead.
2160
+ (0, pagination_controls_1.findAllPaginationControls)(paginationNode, 'range')
2161
+ .filter(function (control) { return !(0, pagination_controls_1.readJSXAttributeString)(control, pagination_controls_1.PAGINATION_FOR_ATTR); })
2162
+ .forEach(function (control) { return wireRangeControlFor(control, usage, vars); });
2005
2163
  if (usage.infiniteScroll) {
2006
2164
  // Only the Load More button survives; page controls make no sense for a
2007
2165
  // list the visitor never leaves.
@@ -2206,7 +2364,15 @@ function ensureAPIRouteExists(extractedResources, usage, dataSources, transformO
2206
2364
  };
2207
2365
  }
2208
2366
  }
2209
- 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) {
2210
2376
  var _a;
2211
2377
  var getStaticPropsChunk = chunks.find(function (c) { return c.name === 'getStaticProps'; });
2212
2378
  if (!getStaticPropsChunk || getStaticPropsChunk.type !== teleport_types_1.ChunkType.AST) {
@@ -2287,6 +2453,12 @@ function updateGetStaticProps(chunks, registry, dependencies, folderPath, locali
2287
2453
  })),
2288
2454
  ])));
2289
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
+ }
2290
2462
  // Add filters if present
2291
2463
  // Filter out dynamic destinations for getStaticProps (they can't be resolved at build time)
2292
2464
  var staticFilters = staticFiltersOf(usage);
@@ -2302,7 +2474,7 @@ function updateGetStaticProps(chunks, registry, dependencies, folderPath, locali
2302
2474
  var existingFetchIndex = arrayPattern.elements.findIndex(function (el) { return (el === null || el === void 0 ? void 0 : el.type) === 'Identifier' && el.name === vars.propsPrefix; });
2303
2475
  if (existingFetchIndex === -1) {
2304
2476
  arrayPattern.elements.push(types.identifier(vars.propsPrefix));
2305
- 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')), [
2306
2478
  types.arrowFunctionExpression([types.identifier('error')], types.blockStatement([
2307
2479
  types.expressionStatement(types.callExpression(types.memberExpression(types.identifier('console'), types.identifier('error')), [
2308
2480
  types.stringLiteral("Error fetching ".concat(vars.propsPrefix, ":")),
@@ -2405,7 +2577,7 @@ function updateGetStaticProps(chunks, registry, dependencies, folderPath, locali
2405
2577
  countParams.push(types.objectProperty(types.identifier('filters'), (0, filter_utils_1.buildStaticFiltersStringifyCall)(staticFiltersForCount)));
2406
2578
  }
2407
2579
  (0, items_path_1.appendItemsPathParam)(countParams, firstUsage.itemsPath, 'collectionPath');
2408
- 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')), [
2409
2581
  types.arrowFunctionExpression([types.identifier('error')], types.blockStatement([
2410
2582
  types.expressionStatement(types.callExpression(types.memberExpression(types.identifier('console'), types.identifier('error')), [
2411
2583
  types.stringLiteral("Error fetching ".concat(countVarName, ":")),