@teleporthq/teleport-plugin-next-data-source 0.42.35 → 0.43.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (239) hide show
  1. package/__tests__/ecommerce-product-out-of-stock.test.ts +112 -0
  2. package/__tests__/fetchers.test.ts +0 -42
  3. package/__tests__/filter-utils.test.ts +149 -0
  4. package/__tests__/mocks.ts +0 -12
  5. package/__tests__/utils.test.ts +0 -2
  6. package/dist/cjs/array-mapper-registry.d.ts +2 -0
  7. package/dist/cjs/array-mapper-registry.d.ts.map +1 -1
  8. package/dist/cjs/array-mapper-registry.js +9 -1
  9. package/dist/cjs/array-mapper-registry.js.map +1 -1
  10. package/dist/cjs/count-fetchers.d.ts +2 -2
  11. package/dist/cjs/count-fetchers.d.ts.map +1 -1
  12. package/dist/cjs/count-fetchers.js +5 -5
  13. package/dist/cjs/count-fetchers.js.map +1 -1
  14. package/dist/cjs/data-source-fetchers.d.ts +2 -1
  15. package/dist/cjs/data-source-fetchers.d.ts.map +1 -1
  16. package/dist/cjs/data-source-fetchers.js +11 -9
  17. package/dist/cjs/data-source-fetchers.js.map +1 -1
  18. package/dist/cjs/fetchers/airtable.d.ts.map +1 -1
  19. package/dist/cjs/fetchers/airtable.js +1 -1
  20. package/dist/cjs/fetchers/airtable.js.map +1 -1
  21. package/dist/cjs/fetchers/clickhouse.d.ts.map +1 -1
  22. package/dist/cjs/fetchers/clickhouse.js +1 -1
  23. package/dist/cjs/fetchers/clickhouse.js.map +1 -1
  24. package/dist/cjs/fetchers/csv-file.js +1 -1
  25. package/dist/cjs/fetchers/csv-file.js.map +1 -1
  26. package/dist/cjs/fetchers/firestore.js +1 -1
  27. package/dist/cjs/fetchers/firestore.js.map +1 -1
  28. package/dist/cjs/fetchers/google-sheets.js +1 -1
  29. package/dist/cjs/fetchers/google-sheets.js.map +1 -1
  30. package/dist/cjs/fetchers/index.d.ts +2 -1
  31. package/dist/cjs/fetchers/index.d.ts.map +1 -1
  32. package/dist/cjs/fetchers/index.js +8 -5
  33. package/dist/cjs/fetchers/index.js.map +1 -1
  34. package/dist/cjs/fetchers/javascript.js +1 -1
  35. package/dist/cjs/fetchers/javascript.js.map +1 -1
  36. package/dist/cjs/fetchers/mariadb.d.ts.map +1 -1
  37. package/dist/cjs/fetchers/mariadb.js +3 -3
  38. package/dist/cjs/fetchers/mariadb.js.map +1 -1
  39. package/dist/cjs/fetchers/mongodb.js +1 -1
  40. package/dist/cjs/fetchers/mongodb.js.map +1 -1
  41. package/dist/cjs/fetchers/mysql.d.ts.map +1 -1
  42. package/dist/cjs/fetchers/mysql.js +2 -2
  43. package/dist/cjs/fetchers/mysql.js.map +1 -1
  44. package/dist/cjs/fetchers/postgresql.d.ts.map +1 -1
  45. package/dist/cjs/fetchers/postgresql.js +2 -2
  46. package/dist/cjs/fetchers/postgresql.js.map +1 -1
  47. package/dist/cjs/fetchers/raw-query.d.ts +18 -0
  48. package/dist/cjs/fetchers/raw-query.d.ts.map +1 -0
  49. package/dist/cjs/fetchers/raw-query.js +70 -0
  50. package/dist/cjs/fetchers/raw-query.js.map +1 -0
  51. package/dist/cjs/fetchers/redis.js +1 -1
  52. package/dist/cjs/fetchers/redis.js.map +1 -1
  53. package/dist/cjs/fetchers/redshift.d.ts.map +1 -1
  54. package/dist/cjs/fetchers/redshift.js +2 -2
  55. package/dist/cjs/fetchers/redshift.js.map +1 -1
  56. package/dist/cjs/fetchers/rest-api.js +1 -1
  57. package/dist/cjs/fetchers/rest-api.js.map +1 -1
  58. package/dist/cjs/fetchers/supabase.d.ts.map +1 -1
  59. package/dist/cjs/fetchers/supabase.js +62 -2
  60. package/dist/cjs/fetchers/supabase.js.map +1 -1
  61. package/dist/cjs/fetchers/teleport.d.ts +7 -0
  62. package/dist/cjs/fetchers/teleport.d.ts.map +1 -0
  63. package/dist/cjs/fetchers/teleport.js +63 -0
  64. package/dist/cjs/fetchers/teleport.js.map +1 -0
  65. package/dist/cjs/fetchers/turso.d.ts.map +1 -1
  66. package/dist/cjs/fetchers/turso.js +1 -1
  67. package/dist/cjs/fetchers/turso.js.map +1 -1
  68. package/dist/cjs/filter-utils.d.ts +13 -0
  69. package/dist/cjs/filter-utils.d.ts.map +1 -0
  70. package/dist/cjs/filter-utils.js +95 -0
  71. package/dist/cjs/filter-utils.js.map +1 -0
  72. package/dist/cjs/index.d.ts.map +1 -1
  73. package/dist/cjs/index.js +112 -9
  74. package/dist/cjs/index.js.map +1 -1
  75. package/dist/cjs/pagination-plugin.d.ts.map +1 -1
  76. package/dist/cjs/pagination-plugin.js +389 -128
  77. package/dist/cjs/pagination-plugin.js.map +1 -1
  78. package/dist/cjs/sort-utils.d.ts +10 -0
  79. package/dist/cjs/sort-utils.d.ts.map +1 -0
  80. package/dist/cjs/sort-utils.js +141 -0
  81. package/dist/cjs/sort-utils.js.map +1 -0
  82. package/dist/cjs/transformations/blog-post.d.ts +7 -0
  83. package/dist/cjs/transformations/blog-post.d.ts.map +1 -0
  84. package/dist/cjs/transformations/blog-post.js +13 -0
  85. package/dist/cjs/transformations/blog-post.js.map +1 -0
  86. package/dist/cjs/transformations/ecommerce-product.d.ts +7 -0
  87. package/dist/cjs/transformations/ecommerce-product.d.ts.map +1 -0
  88. package/dist/cjs/transformations/ecommerce-product.js +13 -0
  89. package/dist/cjs/transformations/ecommerce-product.js.map +1 -0
  90. package/dist/cjs/transformations/index.d.ts +26 -0
  91. package/dist/cjs/transformations/index.d.ts.map +1 -0
  92. package/dist/cjs/transformations/index.js +81 -0
  93. package/dist/cjs/transformations/index.js.map +1 -0
  94. package/dist/cjs/transformations/shared-utils.d.ts +7 -0
  95. package/dist/cjs/transformations/shared-utils.d.ts.map +1 -0
  96. package/dist/cjs/transformations/shared-utils.js +13 -0
  97. package/dist/cjs/transformations/shared-utils.js.map +1 -0
  98. package/dist/cjs/tsconfig.tsbuildinfo +1 -1
  99. package/dist/cjs/utils.d.ts +30 -1
  100. package/dist/cjs/utils.d.ts.map +1 -1
  101. package/dist/cjs/utils.js +173 -10
  102. package/dist/cjs/utils.js.map +1 -1
  103. package/dist/esm/array-mapper-registry.d.ts +2 -0
  104. package/dist/esm/array-mapper-registry.d.ts.map +1 -1
  105. package/dist/esm/array-mapper-registry.js +9 -1
  106. package/dist/esm/array-mapper-registry.js.map +1 -1
  107. package/dist/esm/count-fetchers.d.ts +2 -2
  108. package/dist/esm/count-fetchers.d.ts.map +1 -1
  109. package/dist/esm/count-fetchers.js +4 -4
  110. package/dist/esm/count-fetchers.js.map +1 -1
  111. package/dist/esm/data-source-fetchers.d.ts +2 -1
  112. package/dist/esm/data-source-fetchers.d.ts.map +1 -1
  113. package/dist/esm/data-source-fetchers.js +10 -9
  114. package/dist/esm/data-source-fetchers.js.map +1 -1
  115. package/dist/esm/fetchers/airtable.d.ts.map +1 -1
  116. package/dist/esm/fetchers/airtable.js +1 -1
  117. package/dist/esm/fetchers/airtable.js.map +1 -1
  118. package/dist/esm/fetchers/clickhouse.d.ts.map +1 -1
  119. package/dist/esm/fetchers/clickhouse.js +1 -1
  120. package/dist/esm/fetchers/clickhouse.js.map +1 -1
  121. package/dist/esm/fetchers/csv-file.js +1 -1
  122. package/dist/esm/fetchers/csv-file.js.map +1 -1
  123. package/dist/esm/fetchers/firestore.js +1 -1
  124. package/dist/esm/fetchers/firestore.js.map +1 -1
  125. package/dist/esm/fetchers/google-sheets.js +1 -1
  126. package/dist/esm/fetchers/google-sheets.js.map +1 -1
  127. package/dist/esm/fetchers/index.d.ts +2 -1
  128. package/dist/esm/fetchers/index.d.ts.map +1 -1
  129. package/dist/esm/fetchers/index.js +2 -1
  130. package/dist/esm/fetchers/index.js.map +1 -1
  131. package/dist/esm/fetchers/javascript.js +1 -1
  132. package/dist/esm/fetchers/javascript.js.map +1 -1
  133. package/dist/esm/fetchers/mariadb.d.ts.map +1 -1
  134. package/dist/esm/fetchers/mariadb.js +4 -4
  135. package/dist/esm/fetchers/mariadb.js.map +1 -1
  136. package/dist/esm/fetchers/mongodb.js +1 -1
  137. package/dist/esm/fetchers/mongodb.js.map +1 -1
  138. package/dist/esm/fetchers/mysql.d.ts.map +1 -1
  139. package/dist/esm/fetchers/mysql.js +3 -3
  140. package/dist/esm/fetchers/mysql.js.map +1 -1
  141. package/dist/esm/fetchers/postgresql.d.ts.map +1 -1
  142. package/dist/esm/fetchers/postgresql.js +3 -3
  143. package/dist/esm/fetchers/postgresql.js.map +1 -1
  144. package/dist/esm/fetchers/raw-query.d.ts +18 -0
  145. package/dist/esm/fetchers/raw-query.d.ts.map +1 -0
  146. package/dist/esm/fetchers/raw-query.js +65 -0
  147. package/dist/esm/fetchers/raw-query.js.map +1 -0
  148. package/dist/esm/fetchers/redis.js +1 -1
  149. package/dist/esm/fetchers/redis.js.map +1 -1
  150. package/dist/esm/fetchers/redshift.d.ts.map +1 -1
  151. package/dist/esm/fetchers/redshift.js +3 -3
  152. package/dist/esm/fetchers/redshift.js.map +1 -1
  153. package/dist/esm/fetchers/rest-api.js +1 -1
  154. package/dist/esm/fetchers/rest-api.js.map +1 -1
  155. package/dist/esm/fetchers/supabase.d.ts.map +1 -1
  156. package/dist/esm/fetchers/supabase.js +63 -3
  157. package/dist/esm/fetchers/supabase.js.map +1 -1
  158. package/dist/esm/fetchers/teleport.d.ts +7 -0
  159. package/dist/esm/fetchers/teleport.d.ts.map +1 -0
  160. package/dist/esm/fetchers/teleport.js +57 -0
  161. package/dist/esm/fetchers/teleport.js.map +1 -0
  162. package/dist/esm/fetchers/turso.d.ts.map +1 -1
  163. package/dist/esm/fetchers/turso.js +2 -2
  164. package/dist/esm/fetchers/turso.js.map +1 -1
  165. package/dist/esm/filter-utils.d.ts +13 -0
  166. package/dist/esm/filter-utils.d.ts.map +1 -0
  167. package/dist/esm/filter-utils.js +66 -0
  168. package/dist/esm/filter-utils.js.map +1 -0
  169. package/dist/esm/index.d.ts.map +1 -1
  170. package/dist/esm/index.js +113 -10
  171. package/dist/esm/index.js.map +1 -1
  172. package/dist/esm/pagination-plugin.d.ts.map +1 -1
  173. package/dist/esm/pagination-plugin.js +389 -128
  174. package/dist/esm/pagination-plugin.js.map +1 -1
  175. package/dist/esm/sort-utils.d.ts +10 -0
  176. package/dist/esm/sort-utils.d.ts.map +1 -0
  177. package/dist/esm/sort-utils.js +113 -0
  178. package/dist/esm/sort-utils.js.map +1 -0
  179. package/dist/esm/transformations/blog-post.d.ts +7 -0
  180. package/dist/esm/transformations/blog-post.d.ts.map +1 -0
  181. package/dist/esm/transformations/blog-post.js +9 -0
  182. package/dist/esm/transformations/blog-post.js.map +1 -0
  183. package/dist/esm/transformations/ecommerce-product.d.ts +7 -0
  184. package/dist/esm/transformations/ecommerce-product.d.ts.map +1 -0
  185. package/dist/esm/transformations/ecommerce-product.js +9 -0
  186. package/dist/esm/transformations/ecommerce-product.js.map +1 -0
  187. package/dist/esm/transformations/index.d.ts +26 -0
  188. package/dist/esm/transformations/index.d.ts.map +1 -0
  189. package/dist/esm/transformations/index.js +74 -0
  190. package/dist/esm/transformations/index.js.map +1 -0
  191. package/dist/esm/transformations/shared-utils.d.ts +7 -0
  192. package/dist/esm/transformations/shared-utils.d.ts.map +1 -0
  193. package/dist/esm/transformations/shared-utils.js +9 -0
  194. package/dist/esm/transformations/shared-utils.js.map +1 -0
  195. package/dist/esm/tsconfig.tsbuildinfo +1 -1
  196. package/dist/esm/utils.d.ts +30 -1
  197. package/dist/esm/utils.d.ts.map +1 -1
  198. package/dist/esm/utils.js +170 -9
  199. package/dist/esm/utils.js.map +1 -1
  200. package/package.json +6 -5
  201. package/src/array-mapper-registry.ts +13 -0
  202. package/src/count-fetchers.ts +5 -5
  203. package/src/data-source-fetchers.ts +15 -11
  204. package/src/fetchers/airtable.ts +54 -8
  205. package/src/fetchers/clickhouse.ts +25 -19
  206. package/src/fetchers/csv-file.ts +2 -2
  207. package/src/fetchers/firestore.ts +2 -2
  208. package/src/fetchers/google-sheets.ts +2 -2
  209. package/src/fetchers/index.ts +6 -5
  210. package/src/fetchers/javascript.ts +2 -2
  211. package/src/fetchers/mariadb.ts +27 -12
  212. package/src/fetchers/mongodb.ts +2 -2
  213. package/src/fetchers/mysql.ts +27 -12
  214. package/src/fetchers/postgresql.ts +31 -18
  215. package/src/fetchers/raw-query.ts +178 -0
  216. package/src/fetchers/redis.ts +2 -2
  217. package/src/fetchers/redshift.ts +14 -10
  218. package/src/fetchers/rest-api.ts +2 -2
  219. package/src/fetchers/supabase.ts +97 -14
  220. package/src/fetchers/teleport.ts +485 -0
  221. package/src/fetchers/turso.ts +15 -7
  222. package/src/filter-utils.ts +111 -0
  223. package/src/index.ts +146 -6
  224. package/src/pagination-plugin.ts +547 -308
  225. package/src/sort-utils.ts +150 -0
  226. package/src/transformations/blog-post.ts +128 -0
  227. package/src/transformations/ecommerce-product.ts +173 -0
  228. package/src/transformations/index.ts +97 -0
  229. package/src/transformations/shared-utils.ts +271 -0
  230. package/src/utils.ts +227 -11
  231. package/dist/cjs/fetchers/static-collection.d.ts +0 -7
  232. package/dist/cjs/fetchers/static-collection.d.ts.map +0 -1
  233. package/dist/cjs/fetchers/static-collection.js +0 -25
  234. package/dist/cjs/fetchers/static-collection.js.map +0 -1
  235. package/dist/esm/fetchers/static-collection.d.ts +0 -7
  236. package/dist/esm/fetchers/static-collection.d.ts.map +0 -1
  237. package/dist/esm/fetchers/static-collection.js +0 -19
  238. package/dist/esm/fetchers/static-collection.js.map +0 -1
  239. package/src/fetchers/static-collection.ts +0 -231
@@ -36,10 +36,220 @@ var __generator = (this && this.__generator) || function (thisArg, body) {
36
36
  };
37
37
  import { ChunkType, FileType, } from '@teleporthq/teleport-types';
38
38
  import * as types from '@babel/types';
39
+ import { parseExpression } from '@babel/parser';
39
40
  import { StringUtils } from '@teleporthq/teleport-shared';
40
41
  import { ASTUtils } from '@teleporthq/teleport-plugin-common';
41
42
  import { generateSafeFileName } from './utils';
42
43
  import { generateDataSourceFetcherWithCore } from './data-source-fetchers';
44
+ import { appendSortsParam, extractDynamicSort } from './sort-utils';
45
+ import { appendFiltersParam, pushStateIdsAsDeps } from './filter-utils';
46
+ // tslint:disable-next-line:no-any
47
+ function parseSearchDefaultValue(raw) {
48
+ if (!raw || typeof raw !== 'object') {
49
+ return undefined;
50
+ }
51
+ if (raw.type === 'static' && typeof raw.content === 'string' && raw.content.length > 0) {
52
+ return { kind: 'static', value: raw.content };
53
+ }
54
+ if ((raw.type === 'expr' || raw.type === 'dynamic') && typeof raw.content === 'string') {
55
+ var src = raw.content.trim();
56
+ if (src.length === 0) {
57
+ return undefined;
58
+ }
59
+ try {
60
+ var ast = parseExpression(src, { sourceType: 'module', plugins: ['jsx'] });
61
+ return { kind: 'expression', ast: ast };
62
+ }
63
+ catch (_a) {
64
+ // Fall back to no seed if the expression is malformed — better to
65
+ // render an empty input than to emit broken code.
66
+ return undefined;
67
+ }
68
+ }
69
+ return undefined;
70
+ }
71
+ function searchDefaultValueInitAST(value) {
72
+ if (!value) {
73
+ return types.stringLiteral('');
74
+ }
75
+ if (value.kind === 'static') {
76
+ return types.stringLiteral(value.value);
77
+ }
78
+ // Clone so multiple `useState(...)` call sites each get an
79
+ // independent AST node — Babel does not support shared references.
80
+ return types.cloneNode(value.ast, /* deep */ true);
81
+ }
82
+ // The UIDL's `filters.content` array can wrap one or more conditions inside
83
+ // a `{ type: 'group', operator: 'and' | 'or', children: [...] }` entry — the
84
+ // GUI builds this shape when the inspector adds a logical group. The data
85
+ // source API endpoint (`processFilters` in the generated data-source module)
86
+ // only knows how to consume a FLAT array of `{ source, destination, operand }`
87
+ // conditions, so we walk groups recursively here and collect their leaf
88
+ // conditions in order. Anything that isn't a group AND isn't a condition is
89
+ // dropped — defensive against partially-built filter entries that would
90
+ // otherwise emit `{ source: '', destination: '', operand: '' }` rows the
91
+ // API ignores anyway.
92
+ //
93
+ // `or`-grouped conditions are flattened just like `and`-grouped ones — the
94
+ // downstream SQL builder always joins flat filters with `AND`, so emitting
95
+ // the conditions side-by-side approximates AND semantics. A future API
96
+ // upgrade that respects group operators would key off the original tree
97
+ // directly; until then, AND-flattening is the closest correct behaviour and
98
+ // matches what the inspector preview shows for the common single-group case
99
+ // the GUI emits today.
100
+ function flattenFilterGroups(filters) {
101
+ if (!Array.isArray(filters)) {
102
+ return [];
103
+ }
104
+ var out = [];
105
+ var walk = function (entry) {
106
+ if (!entry || typeof entry !== 'object') {
107
+ return;
108
+ }
109
+ if (entry.type === 'group' && Array.isArray(entry.children)) {
110
+ for (var _i = 0, _a = entry.children; _i < _a.length; _i++) {
111
+ var child = _a[_i];
112
+ walk(child);
113
+ }
114
+ return;
115
+ }
116
+ // Backwards-compatible: entries without an explicit `type` (legacy flat
117
+ // form) and entries explicitly tagged `condition` are both treated as
118
+ // condition leaves.
119
+ if (entry.type === undefined || entry.type === 'condition') {
120
+ out.push(entry);
121
+ }
122
+ };
123
+ for (var _i = 0, filters_1 = filters; _i < filters_1.length; _i++) {
124
+ var entry = filters_1[_i];
125
+ walk(entry);
126
+ }
127
+ return out;
128
+ }
129
+ // Walks a flat filter list to extract every `urlSearchParams` reference key
130
+ // (e.g. `'categoryFilter'`). Used to (a) inject `const router = useRouter()`
131
+ // once at the top of the component, (b) wire `router.query.<key>` into the
132
+ // `useMemo` dependency array so the client-side fetch reruns whenever the
133
+ // buyer navigates to a URL with a different `?key=value`. Returned in the
134
+ // order keys first appear so the generated dep array stays stable.
135
+ function collectFilterUrlSearchParamKeys(filters) {
136
+ var seen = new Set();
137
+ var out = [];
138
+ for (var _i = 0, filters_2 = filters; _i < filters_2.length; _i++) {
139
+ var f = filters_2[_i];
140
+ var dest = f === null || f === void 0 ? void 0 : f.destination;
141
+ if (!ASTUtils.isUIDLDynamicReference(dest)) {
142
+ continue;
143
+ }
144
+ var content = dest.content;
145
+ if (!content || content.referenceType !== 'urlSearchParams' || !content.id) {
146
+ continue;
147
+ }
148
+ if (seen.has(content.id)) {
149
+ continue;
150
+ }
151
+ seen.add(content.id);
152
+ out.push(content.id);
153
+ }
154
+ return out;
155
+ }
156
+ // Returns true when any filter destination is a `urlSearchParams` dynamic
157
+ // reference — used by the plugin to know it needs to import `useRouter` and
158
+ // emit `const router = useRouter()` at the top of the component. Cheaper to
159
+ // test against the precomputed key list once than to walk all filters on
160
+ // every check.
161
+ function hasUrlSearchParamFilters(usage) {
162
+ return usage.filterUrlSearchParamKeys.length > 0;
163
+ }
164
+ // Appends `router.query.<key>` member expressions to a useMemo deps array so
165
+ // every client-side fetch refires when the buyer navigates between URLs
166
+ // that differ only by `?key=value`. React's shallow-compare semantics treat
167
+ // the member expression as a distinct value per render, so the array stays
168
+ // stable across paints with the same query string and changes the moment
169
+ // the URL does. Skip the bare-identifier dedupe `filterStateIds` uses —
170
+ // member expressions never collide with state identifiers, and the deps
171
+ // array allows duplicates without harm.
172
+ function pushUrlSearchParamMemoDeps(memoDeps, usage) {
173
+ for (var _i = 0, _a = usage.filterUrlSearchParamKeys; _i < _a.length; _i++) {
174
+ var key = _a[_i];
175
+ memoDeps.push(types.memberExpression(types.memberExpression(types.identifier('router'), types.identifier('query')), types.identifier(key)));
176
+ }
177
+ }
178
+ // Builds the AST for `!router.query.<key1> && !router.query.<key2> && ...`,
179
+ // used as a runtime guard around `initialData={props.X}` so the server-
180
+ // prefetched (unfiltered) data is only handed to `DataProvider` when the
181
+ // URL has no active filter. Without this guard, navigating to
182
+ // `/products-list?categoryFilter=Rings` (especially via soft Next.js
183
+ // transitions where the page component remounts with fresh getStaticProps
184
+ // but the buyer's URL filter is still in scope) shows the unfiltered list
185
+ // for the first paint AND keeps it on screen because `DataProvider`'s
186
+ // `passFetchBecauseWeHaveInitialData` ref skips the very first fetch when
187
+ // `initialData !== undefined`. By emitting `undefined` here whenever ANY
188
+ // url-search-param filter is set, the DataProvider's mount-time fetch runs
189
+ // immediately with the filtered params instead of presenting stale data.
190
+ //
191
+ // Returns `null` when the usage has no url-search-param filters at all —
192
+ // callers fall back to the existing `props.X` expression unchanged so
193
+ // non-filtered pages still benefit from the SSR prefetch.
194
+ function buildNoUrlFilterGuard(usage) {
195
+ if (usage.filterUrlSearchParamKeys.length === 0 && usage.filterStateIds.length === 0) {
196
+ return null;
197
+ }
198
+ var guards = [];
199
+ for (var _i = 0, _a = usage.filterUrlSearchParamKeys; _i < _a.length; _i++) {
200
+ var key = _a[_i];
201
+ guards.push(types.unaryExpression('!', types.memberExpression(types.memberExpression(types.identifier('router'), types.identifier('query')), types.identifier(key)), true));
202
+ }
203
+ // State-bound filter destinations (e.g. `selectedCategory`) emit as bare
204
+ // identifiers, so the corresponding guard is `!selectedCategory`. An empty
205
+ // string ('') for the state — which the GUI emits when the user picks the
206
+ // "All Categories" reset option — is falsy and so passes through the guard
207
+ // exactly like a missing URL param: initialData (unfiltered prefetch) wins
208
+ // until the user picks a real value.
209
+ for (var _b = 0, _c = usage.filterStateIds; _b < _c.length; _b++) {
210
+ var id = _c[_b];
211
+ guards.push(types.unaryExpression('!', types.identifier(id), true));
212
+ }
213
+ return guards.reduce(function (acc, next) { return types.logicalExpression('&&', acc, next); });
214
+ }
215
+ // Wraps an existing `initialData` condition with the additional "no URL
216
+ // filter active" guard so server-prefetched data is only handed to
217
+ // DataProvider when both (a) the original guard (page === 1, no search
218
+ // query, etc.) AND (b) every relevant `router.query.<key>` is falsy. See
219
+ // `buildNoUrlFilterGuard` for the rationale on why bare-identity feature
220
+ // detection is safer than the `dynamicSort`-style "always undefined" path.
221
+ function wrapInitialDataWithUrlFilterGuard(baseCondition, usage) {
222
+ var noFilterGuard = buildNoUrlFilterGuard(usage);
223
+ if (!noFilterGuard) {
224
+ return baseCondition;
225
+ }
226
+ return types.logicalExpression('&&', baseCondition, noFilterGuard);
227
+ }
228
+ // Builds the destination AST for a single filter entry. Replaces the bare
229
+ // `ASTUtils.convertFilterDestinationToExpression(filter.destination)` call
230
+ // at every emit site so `urlSearchParams` references resolve to
231
+ // `router?.query?.<key>` instead of falling through to a bare identifier
232
+ // (which the existing helper would emit, leaving the client-side fetch
233
+ // referencing an undeclared symbol).
234
+ //
235
+ // State and prop references delegate back to the shared helper so the
236
+ // existing inspector behaviour (state-bound filter destinations) keeps
237
+ // working unchanged. `router?.query?.<key>` is the same shape the
238
+ // `createNextUrlSearchParamsPlugin`-driven `dynamicReferencePrefixMap`
239
+ // emits for page-level navlink reads.
240
+ function buildFilterDestinationExpression(destination) {
241
+ if (ASTUtils.isUIDLDynamicReference(destination)) {
242
+ var content = destination.content;
243
+ if ((content === null || content === void 0 ? void 0 : content.referenceType) === 'urlSearchParams' && (content === null || content === void 0 ? void 0 : content.id)) {
244
+ // router?.query?.<id> — the optional-chain survives the first paint
245
+ // where Next.js's `useRouter()` returns `null` during static export
246
+ // hydration, so the fetch doesn't crash with "Cannot read properties
247
+ // of null" before the router is ready.
248
+ return types.optionalMemberExpression(types.optionalMemberExpression(types.identifier('router'), types.identifier('query'), false, true), types.identifier(content.id), false, true);
249
+ }
250
+ }
251
+ return ASTUtils.convertFilterDestinationToExpression(destination);
252
+ }
43
253
  // Scan UIDL to find all data source usages and build a registry
44
254
  function buildStateRegistry(uidlNode) {
45
255
  var usages = [];
@@ -86,20 +296,48 @@ function buildStateRegistry(uidlNode) {
86
296
  if ((_h = (_g = parentDataSource.resourceParams) === null || _g === void 0 ? void 0 : _g.queryColumns) === null || _h === void 0 ? void 0 : _h.content) {
87
297
  queryColumns = parentDataSource.resourceParams.queryColumns.content;
88
298
  }
89
- // Extract sorts from parent's resource params
299
+ // Extract sorts from parent's resource params (legacy static array form)
90
300
  var sorts = [];
91
301
  if ((_k = (_j = parentDataSource.resourceParams) === null || _j === void 0 ? void 0 : _j.sorts) === null || _k === void 0 ? void 0 : _k.content) {
92
302
  sorts = parentDataSource.resourceParams.sorts.content;
93
303
  }
94
- // Extract filters from parent's resource params
304
+ // If legacy sorts aren't set, fall back to the new dynamic single-column
305
+ // sort fields on the cms-list-repeater (used by admin-panel listing pages).
306
+ var dynamicSort = void 0;
307
+ if ((!sorts || sorts.length === 0) && content.sort) {
308
+ dynamicSort = extractDynamicSort(content.sort, content.sortDirection);
309
+ }
310
+ // Extract filters from parent's resource params. The inspector wraps
311
+ // every condition in a `{ type: 'group' }` envelope (single-group or
312
+ // nested), so flatten to leaf conditions before the downstream emit
313
+ // sites consume `.source` / `.destination` / `.operand` directly —
314
+ // they expect a flat condition array. See `flattenFilterGroups`'s
315
+ // header comment for why AND-flattening is the correct fallback for
316
+ // the API endpoint's flat-condition contract.
95
317
  var filters = [];
96
318
  if ((_m = (_l = parentDataSource.resourceParams) === null || _l === void 0 ? void 0 : _l.filters) === null || _m === void 0 ? void 0 : _m.content) {
97
- filters = parentDataSource.resourceParams.filters.content;
319
+ filters = flattenFilterGroups(parentDataSource.resourceParams.filters.content);
320
+ }
321
+ // Split dynamic destination keys by reference type. `state`/`prop`
322
+ // refs resolve to bare identifiers (so they're useMemo deps as-is);
323
+ // `urlSearchParams` refs resolve to `router.query.<key>` and need a
324
+ // `useRouter()` declaration injected separately.
325
+ var filterStateIds = [];
326
+ for (var _i = 0, filters_3 = filters; _i < filters_3.length; _i++) {
327
+ var f = filters_3[_i];
328
+ if (!ASTUtils.isUIDLDynamicReference(f.destination)) {
329
+ continue;
330
+ }
331
+ var destinationContent = f.destination.content;
332
+ if (!destinationContent || !destinationContent.id) {
333
+ continue;
334
+ }
335
+ if (destinationContent.referenceType === 'urlSearchParams') {
336
+ continue;
337
+ }
338
+ filterStateIds.push(destinationContent.id);
98
339
  }
99
- // Extract state IDs from dynamic filter destinations
100
- var filterStateIds = filters
101
- .filter(function (f) { return ASTUtils.isUIDLDynamicReference(f.destination); })
102
- .map(function (f) { return f.destination.content.id; });
340
+ var filterUrlSearchParamKeys = collectFilterUrlSearchParamKeys(filters);
103
341
  // Extract limit from parent's resource params (for plain array mappers)
104
342
  var limit = 0;
105
343
  if ((_p = (_o = parentDataSource.resourceParams) === null || _o === void 0 ? void 0 : _o.limit) === null || _p === void 0 ? void 0 : _p.content) {
@@ -108,6 +346,7 @@ function buildStateRegistry(uidlNode) {
108
346
  // For paginated mappers, use perPage from cms-list-repeater
109
347
  // For plain mappers, use limit from data-source-list resource params
110
348
  var effectivePerPage = content.paginated ? content.perPage : limit || content.perPage;
349
+ var searchDefaultValue = parseSearchDefaultValue(content.searchDefaultValue);
111
350
  var usage = {
112
351
  index: index++,
113
352
  dataSourceIdentifier: parentDataSource.identifier,
@@ -121,10 +360,13 @@ function buildStateRegistry(uidlNode) {
121
360
  perPage: effectivePerPage,
122
361
  searchEnabled: !!content.searchEnabled,
123
362
  searchDebounce: content.searchDebounce || 300,
363
+ searchDefaultValue: searchDefaultValue,
124
364
  queryColumns: queryColumns,
125
365
  sorts: sorts,
366
+ dynamicSort: dynamicSort,
126
367
  filters: filters,
127
368
  filterStateIds: filterStateIds,
369
+ filterUrlSearchParamKeys: filterUrlSearchParamKeys,
128
370
  category: 'plain',
129
371
  };
130
372
  // Determine category
@@ -153,8 +395,8 @@ function buildStateRegistry(uidlNode) {
153
395
  }
154
396
  // Recurse into children
155
397
  if (((_r = node.content) === null || _r === void 0 ? void 0 : _r.children) && Array.isArray(node.content.children)) {
156
- for (var _i = 0, _u = node.content.children; _i < _u.length; _i++) {
157
- var child = _u[_i];
398
+ for (var _u = 0, _v = node.content.children; _u < _v.length; _u++) {
399
+ var child = _v[_u];
158
400
  traverse(child, parentDataSource);
159
401
  }
160
402
  }
@@ -179,8 +421,8 @@ function buildStateRegistry(uidlNode) {
179
421
  }
180
422
  }
181
423
  if (Array.isArray(node.children)) {
182
- for (var _v = 0, _w = node.children; _v < _w.length; _v++) {
183
- var child = _w[_v];
424
+ for (var _w = 0, _x = node.children; _w < _x.length; _w++) {
425
+ var child = _x[_w];
184
426
  traverse(child, parentDataSource);
185
427
  }
186
428
  }
@@ -210,8 +452,9 @@ function getStateVarsForUsage(usage) {
210
452
  // ==================== MAIN PLUGIN ====================
211
453
  export var createNextArrayMapperPaginationPlugin = function () {
212
454
  var paginationPlugin = function (structure) { return __awaiter(void 0, void 0, void 0, function () {
213
- var uidl, chunks, dependencies, options, componentChunk, variableDeclaration, declarator, arrowFunction, blockStatement, registry, getStaticPropsChunk, isPage, stateDeclarations, effectStatements, returnIndex, insertIndex, dataProviders, dataProvidersWithRepeaters, usageIndexByDataSourceId, dataProvidersWithoutRepeaters, searchInputs, searchEnabledUsages, paginationNodes, paginatedUsages;
214
- return __generator(this, function (_a) {
455
+ var uidl, chunks, dependencies, options, componentChunk, variableDeclaration, declarator, arrowFunction, blockStatement, registry, getStaticPropsChunk, isPage, stateDeclarations, effectStatements, needsUseRouter, hasRouterDecl, returnIndex, insertIndex, dataProviders, dataProvidersWithRepeaters, usageIndexByDataSourceId, dataProvidersWithoutRepeaters, searchInputs, searchEnabledUsages, paginationNodes, paginatedUsages;
456
+ var _a;
457
+ return __generator(this, function (_b) {
215
458
  uidl = structure.uidl, chunks = structure.chunks, dependencies = structure.dependencies, options = structure.options;
216
459
  componentChunk = chunks.find(function (chunk) { return chunk.name === 'jsx-component'; });
217
460
  if (!componentChunk || componentChunk.type !== ChunkType.AST) {
@@ -321,16 +564,19 @@ export var createNextArrayMapperPaginationPlugin = function () {
321
564
  ]), types.callExpression(types.identifier('useState'), [
322
565
  types.objectExpression([
323
566
  types.objectProperty(types.identifier('page'), types.numericLiteral(1)),
324
- types.objectProperty(types.identifier('debouncedQuery'), types.stringLiteral('')),
567
+ types.objectProperty(types.identifier('debouncedQuery'), searchDefaultValueInitAST(usage.searchDefaultValue)),
325
568
  ]),
326
569
  ])),
327
570
  ]));
328
- // Immediate search query state
571
+ // Immediate search query state — seeded with `searchDefaultValue`
572
+ // when provided so the input is pre-filled on mount.
329
573
  stateDeclarations.push(types.variableDeclaration('const', [
330
574
  types.variableDeclarator(types.arrayPattern([
331
575
  types.identifier(vars.searchQueryVar),
332
576
  types.identifier(vars.setSearchQueryVar),
333
- ]), types.callExpression(types.identifier('useState'), [types.stringLiteral('')])),
577
+ ]), types.callExpression(types.identifier('useState'), [
578
+ searchDefaultValueInitAST(usage.searchDefaultValue),
579
+ ])),
334
580
  ]));
335
581
  // Debounce effect
336
582
  effectStatements.push(types.expressionStatement(types.callExpression(types.identifier('useEffect'), [
@@ -378,17 +624,7 @@ export var createNextArrayMapperPaginationPlugin = function () {
378
624
  ])));
379
625
  }
380
626
  // Add filters to count fetch params if present
381
- if (usage.filters && usage.filters.length > 0) {
382
- urlParams.push(types.objectProperty(types.identifier('filters'), types.callExpression(types.memberExpression(types.identifier('JSON'), types.identifier('stringify')), [
383
- types.arrayExpression(usage.filters.map(function (filter) {
384
- return types.objectExpression([
385
- types.objectProperty(types.identifier('source'), types.stringLiteral(filter.source || '')),
386
- types.objectProperty(types.identifier('destination'), ASTUtils.convertFilterDestinationToExpression(filter.destination)),
387
- types.objectProperty(types.identifier('operand'), types.stringLiteral(filter.operand || '')),
388
- ]);
389
- })),
390
- ])));
391
- }
627
+ appendFiltersParam(urlParams, usage.filters, buildFilterDestinationExpression);
392
628
  // Build the count fetch effect body
393
629
  var countFetchEffectBody = [];
394
630
  // Only add skip-on-mount check for pages (where we have server-side count)
@@ -424,11 +660,21 @@ export var createNextArrayMapperPaginationPlugin = function () {
424
660
  ])),
425
661
  ])),
426
662
  ])));
663
+ var countEffectDeps = [
664
+ types.memberExpression(types.identifier(vars.combinedStateVar), types.identifier('debouncedQuery')),
665
+ ];
666
+ // Refresh the count whenever a state-bound filter destination changes
667
+ // (e.g. user picks a category) so pagination tracks the filtered
668
+ // result-set, not the mount-time unfiltered total. Without these
669
+ // deps, ds_0_maxPages stays at the original count and the "Next"
670
+ // button stays enabled past the actual last page of the filtered
671
+ // results — letting the user click into empty pages.
672
+ pushStateIdsAsDeps(countEffectDeps, new Set(), usage.filterStateIds);
673
+ // Same goes for URL-driven filters (already documented above).
674
+ pushUrlSearchParamMemoDeps(countEffectDeps, usage);
427
675
  effectStatements.push(types.expressionStatement(types.callExpression(types.identifier('useEffect'), [
428
676
  types.arrowFunctionExpression([], types.blockStatement(countFetchEffectBody)),
429
- types.arrayExpression([
430
- types.memberExpression(types.identifier(vars.combinedStateVar), types.identifier('debouncedQuery')),
431
- ]),
677
+ types.arrayExpression(countEffectDeps),
432
678
  ])));
433
679
  }
434
680
  else if (usage.category === 'paginated-only') {
@@ -470,7 +716,16 @@ export var createNextArrayMapperPaginationPlugin = function () {
470
716
  ])),
471
717
  ])),
472
718
  ])),
473
- types.arrayExpression([]), // Empty dependency array - fetch on mount only
719
+ // Default to mount-only; refresh when ANY filter destination
720
+ // changes — state-bound (e.g. `selectedCategory`) and
721
+ // URL-driven — so the pagination control reflects the current
722
+ // filtered count instead of the unfiltered mount-time total.
723
+ types.arrayExpression((function () {
724
+ var deps = [];
725
+ pushStateIdsAsDeps(deps, new Set(), usage.filterStateIds);
726
+ pushUrlSearchParamMemoDeps(deps, usage);
727
+ return deps;
728
+ })()),
474
729
  ])));
475
730
  }
476
731
  }
@@ -483,13 +738,17 @@ export var createNextArrayMapperPaginationPlugin = function () {
483
738
  types.variableDeclarator(types.arrayPattern([
484
739
  types.identifier(vars.debouncedSearchQueryVar),
485
740
  types.identifier(vars.setDebouncedSearchQueryVar),
486
- ]), types.callExpression(types.identifier('useState'), [types.stringLiteral('')])),
741
+ ]), types.callExpression(types.identifier('useState'), [
742
+ searchDefaultValueInitAST(usage.searchDefaultValue),
743
+ ])),
487
744
  ]));
488
745
  stateDeclarations.push(types.variableDeclaration('const', [
489
746
  types.variableDeclarator(types.arrayPattern([
490
747
  types.identifier(vars.searchQueryVar),
491
748
  types.identifier(vars.setSearchQueryVar),
492
- ]), types.callExpression(types.identifier('useState'), [types.stringLiteral('')])),
749
+ ]), types.callExpression(types.identifier('useState'), [
750
+ searchDefaultValueInitAST(usage.searchDefaultValue),
751
+ ])),
493
752
  ]));
494
753
  // Debounce effect
495
754
  effectStatements.push(types.expressionStatement(types.callExpression(types.identifier('useEffect'), [
@@ -516,6 +775,36 @@ export var createNextArrayMapperPaginationPlugin = function () {
516
775
  });
517
776
  // Insert state declarations at the beginning
518
777
  stateDeclarations.reverse().forEach(function (s) { return blockStatement.body.unshift(s); });
778
+ needsUseRouter = registry.usages.some(function (u) { return hasUrlSearchParamFilters(u); });
779
+ if (needsUseRouter) {
780
+ if (!dependencies.useRouter) {
781
+ // Match the shape the sibling Next.js plugins use (i18n locale mapper,
782
+ // search-params plugin) so the deduped import line is identical and
783
+ // the dependency-resolver merges instead of emitting a second one.
784
+ dependencies.useRouter = {
785
+ type: 'library',
786
+ path: 'next/router',
787
+ version: '^12.1.10',
788
+ meta: { namedImport: true },
789
+ };
790
+ }
791
+ hasRouterDecl = blockStatement.body.some(function (statement) {
792
+ return statement.type === 'VariableDeclaration' &&
793
+ statement.declarations.some(function (decl) {
794
+ var _a;
795
+ return decl.id.type === 'Identifier' &&
796
+ decl.id.name === 'router' &&
797
+ ((_a = decl.init) === null || _a === void 0 ? void 0 : _a.type) === 'CallExpression' &&
798
+ decl.init.callee.type === 'Identifier' &&
799
+ decl.init.callee.name === 'useRouter';
800
+ });
801
+ });
802
+ if (!hasRouterDecl) {
803
+ blockStatement.body.unshift(types.variableDeclaration('const', [
804
+ types.variableDeclarator(types.identifier('router'), types.callExpression(types.identifier('useRouter'), [])),
805
+ ]));
806
+ }
807
+ }
519
808
  returnIndex = blockStatement.body.findIndex(function (s) { return s.type === 'ReturnStatement'; });
520
809
  insertIndex = returnIndex !== -1 ? returnIndex : blockStatement.body.length;
521
810
  effectStatements.reverse().forEach(function (e) { return blockStatement.body.splice(insertIndex, 0, e); });
@@ -589,7 +878,7 @@ export var createNextArrayMapperPaginationPlugin = function () {
589
878
  });
590
879
  // STEP 6: Update getStaticProps if this is a page
591
880
  if (isPage) {
592
- updateGetStaticProps(chunks, registry, dependencies);
881
+ updateGetStaticProps(chunks, registry, dependencies, (_a = uidl.outputOptions) === null || _a === void 0 ? void 0 : _a.folderPath);
593
882
  }
594
883
  return [2 /*return*/, structure];
595
884
  });
@@ -773,42 +1062,37 @@ function updateDataProviderForPaginatedSearch(dp, usage, vars, fileName) {
773
1062
  if (usage.queryColumns.length > 0) {
774
1063
  paramsProps.push(types.objectProperty(types.identifier('queryColumns'), types.callExpression(types.memberExpression(types.identifier('JSON'), types.identifier('stringify')), [types.arrayExpression(usage.queryColumns.map(function (c) { return types.stringLiteral(c); }))])));
775
1064
  }
776
- // Add sorts if present
777
- if (usage.sorts && usage.sorts.length > 0) {
778
- paramsProps.push(types.objectProperty(types.identifier('sorts'), types.callExpression(types.memberExpression(types.identifier('JSON'), types.identifier('stringify')), [
779
- types.arrayExpression(usage.sorts.map(function (sort) {
780
- return types.objectExpression([
781
- types.objectProperty(types.identifier('field'), types.stringLiteral(sort.field || '')),
782
- types.objectProperty(types.identifier('order'), types.stringLiteral(sort.order || '')),
783
- ]);
784
- })),
785
- ])));
786
- }
1065
+ // Add sorts if present (legacy static array wins; otherwise dynamic state-bound sort)
1066
+ appendSortsParam(paramsProps, usage.sorts, usage.dynamicSort);
787
1067
  // Add filters if present
788
- if (usage.filters && usage.filters.length > 0) {
789
- paramsProps.push(types.objectProperty(types.identifier('filters'), types.callExpression(types.memberExpression(types.identifier('JSON'), types.identifier('stringify')), [
790
- types.arrayExpression(usage.filters.map(function (filter) {
791
- return types.objectExpression([
792
- types.objectProperty(types.identifier('source'), types.stringLiteral(filter.source || '')),
793
- types.objectProperty(types.identifier('destination'), ASTUtils.convertFilterDestinationToExpression(filter.destination)),
794
- types.objectProperty(types.identifier('operand'), types.stringLiteral(filter.operand || '')),
795
- ]);
796
- })),
797
- ])));
798
- }
799
- // Build useMemo dependencies including filter state IDs
1068
+ appendFiltersParam(paramsProps, usage.filters, buildFilterDestinationExpression);
1069
+ // Build useMemo dependencies including filter state IDs and dynamic sort state IDs
800
1070
  var memoDeps = [types.identifier(vars.combinedStateVar)];
801
- usage.filterStateIds.forEach(function (stateId) {
802
- memoDeps.push(types.identifier(stateId));
803
- });
1071
+ var seenDeps = new Set([vars.combinedStateVar]);
1072
+ pushStateIdsAsDeps(memoDeps, seenDeps, usage.filterStateIds);
1073
+ if (usage.dynamicSort) {
1074
+ pushStateIdsAsDeps(memoDeps, seenDeps, usage.dynamicSort.depStateIds);
1075
+ }
1076
+ pushUrlSearchParamMemoDeps(memoDeps, usage);
804
1077
  dp.openingElement.attributes.push(types.jsxAttribute(types.jsxIdentifier('params'), types.jsxExpressionContainer(types.callExpression(types.identifier('useMemo'), [
805
1078
  types.arrowFunctionExpression([], types.objectExpression(paramsProps)),
806
1079
  types.arrayExpression(memoDeps),
807
1080
  ]))));
808
- // Add initialData
809
- var initialDataCondition = 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));
810
- 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), types.identifier('undefined')))));
811
- // Add key
1081
+ // Add initialData. Skip the server-prefetched data entirely when a dynamic
1082
+ // state-bound sort is active the prefetch ran without sort parameters, so
1083
+ // reusing it would mask the current sort state AND cause DataProvider to skip
1084
+ // the first client fetch (its internal guard only skips when initialData is
1085
+ // defined on mount). Without that skip, toggling sort correctly triggers a
1086
+ // refetch via the useMemo params dependency chain.
1087
+ if (!usage.dynamicSort) {
1088
+ 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);
1089
+ 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), types.identifier('undefined')))));
1090
+ }
1091
+ // Add key. Sort is intentionally NOT part of the key — a key change would
1092
+ // remount the DataProvider, and a fresh mount re-arms the internal
1093
+ // "skip-first-fetch-when-we-have-initialData" guard, which would prevent the
1094
+ // new sort params from reaching the fetcher. Leaving sort out of the key
1095
+ // lets the useMemo params identity change alone drive refetch.
812
1096
  dp.openingElement.attributes.push(types.jsxAttribute(types.jsxIdentifier('key'), types.jsxExpressionContainer(types.templateLiteral([
813
1097
  types.templateElement({
814
1098
  raw: "".concat(usage.dataSourceIdentifier, "-"),
@@ -836,42 +1120,29 @@ function updateDataProviderForPaginationOnly(dp, usage, vars, fileName) {
836
1120
  types.objectProperty(types.identifier('page'), types.identifier(vars.pageStateVar)),
837
1121
  types.objectProperty(types.identifier('perPage'), types.numericLiteral(usage.perPage)),
838
1122
  ];
839
- // Add sorts if present
840
- if (usage.sorts && usage.sorts.length > 0) {
841
- paramsProps.push(types.objectProperty(types.identifier('sorts'), types.callExpression(types.memberExpression(types.identifier('JSON'), types.identifier('stringify')), [
842
- types.arrayExpression(usage.sorts.map(function (sort) {
843
- return types.objectExpression([
844
- types.objectProperty(types.identifier('field'), types.stringLiteral(sort.field || '')),
845
- types.objectProperty(types.identifier('order'), types.stringLiteral(sort.order || '')),
846
- ]);
847
- })),
848
- ])));
849
- }
1123
+ // Add sorts if present (legacy static array wins; otherwise dynamic state-bound sort)
1124
+ appendSortsParam(paramsProps, usage.sorts, usage.dynamicSort);
850
1125
  // Add filters if present
851
- if (usage.filters && usage.filters.length > 0) {
852
- paramsProps.push(types.objectProperty(types.identifier('filters'), types.callExpression(types.memberExpression(types.identifier('JSON'), types.identifier('stringify')), [
853
- types.arrayExpression(usage.filters.map(function (filter) {
854
- return types.objectExpression([
855
- types.objectProperty(types.identifier('source'), types.stringLiteral(filter.source || '')),
856
- types.objectProperty(types.identifier('destination'), ASTUtils.convertFilterDestinationToExpression(filter.destination)),
857
- types.objectProperty(types.identifier('operand'), types.stringLiteral(filter.operand || '')),
858
- ]);
859
- })),
860
- ])));
861
- }
862
- // Build useMemo dependencies including filter state IDs
1126
+ appendFiltersParam(paramsProps, usage.filters, buildFilterDestinationExpression);
1127
+ // Build useMemo dependencies including filter state IDs and dynamic sort state IDs
863
1128
  var memoDeps = [types.identifier(vars.pageStateVar)];
864
- usage.filterStateIds.forEach(function (stateId) {
865
- memoDeps.push(types.identifier(stateId));
866
- });
1129
+ var seenDeps = new Set([vars.pageStateVar]);
1130
+ pushStateIdsAsDeps(memoDeps, seenDeps, usage.filterStateIds);
1131
+ if (usage.dynamicSort) {
1132
+ pushStateIdsAsDeps(memoDeps, seenDeps, usage.dynamicSort.depStateIds);
1133
+ }
1134
+ pushUrlSearchParamMemoDeps(memoDeps, usage);
867
1135
  // Add params
868
1136
  dp.openingElement.attributes.push(types.jsxAttribute(types.jsxIdentifier('params'), types.jsxExpressionContainer(types.callExpression(types.identifier('useMemo'), [
869
1137
  types.arrowFunctionExpression([], types.objectExpression(paramsProps)),
870
1138
  types.arrayExpression(memoDeps),
871
1139
  ]))));
872
- // Add initialData
873
- dp.openingElement.attributes.push(types.jsxAttribute(types.jsxIdentifier('initialData'), types.jsxExpressionContainer(types.conditionalExpression(types.binaryExpression('===', types.identifier(vars.pageStateVar), types.numericLiteral(1)), types.optionalMemberExpression(types.identifier('props'), types.identifier(vars.propsPrefix), false, true), types.identifier('undefined')))));
874
- // Add key
1140
+ // Add initialData. See paginated+search updater for why we skip prefetch
1141
+ // reuse when a dynamic state-bound sort is active.
1142
+ if (!usage.dynamicSort) {
1143
+ 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), types.identifier('undefined')))));
1144
+ }
1145
+ // Add key — sort is intentionally NOT included; see paginated+search updater.
875
1146
  dp.openingElement.attributes.push(types.jsxAttribute(types.jsxIdentifier('key'), types.jsxExpressionContainer(types.templateLiteral([
876
1147
  types.templateElement({
877
1148
  raw: "".concat(usage.dataSourceIdentifier, "-page-"),
@@ -896,41 +1167,28 @@ function updateDataProviderForSearchOnly(dp, usage, vars, fileName) {
896
1167
  if (usage.queryColumns.length > 0) {
897
1168
  paramsProps.push(types.objectProperty(types.identifier('queryColumns'), types.callExpression(types.memberExpression(types.identifier('JSON'), types.identifier('stringify')), [types.arrayExpression(usage.queryColumns.map(function (c) { return types.stringLiteral(c); }))])));
898
1169
  }
899
- // Add sorts if present
900
- if (usage.sorts && usage.sorts.length > 0) {
901
- paramsProps.push(types.objectProperty(types.identifier('sorts'), types.callExpression(types.memberExpression(types.identifier('JSON'), types.identifier('stringify')), [
902
- types.arrayExpression(usage.sorts.map(function (sort) {
903
- return types.objectExpression([
904
- types.objectProperty(types.identifier('field'), types.stringLiteral(sort.field || '')),
905
- types.objectProperty(types.identifier('order'), types.stringLiteral(sort.order || '')),
906
- ]);
907
- })),
908
- ])));
909
- }
1170
+ // Add sorts if present (legacy static array wins; otherwise dynamic state-bound sort)
1171
+ appendSortsParam(paramsProps, usage.sorts, usage.dynamicSort);
910
1172
  // Add filters if present
911
- if (usage.filters && usage.filters.length > 0) {
912
- paramsProps.push(types.objectProperty(types.identifier('filters'), types.callExpression(types.memberExpression(types.identifier('JSON'), types.identifier('stringify')), [
913
- types.arrayExpression(usage.filters.map(function (filter) {
914
- return types.objectExpression([
915
- types.objectProperty(types.identifier('source'), types.stringLiteral(filter.source || '')),
916
- types.objectProperty(types.identifier('destination'), ASTUtils.convertFilterDestinationToExpression(filter.destination)),
917
- types.objectProperty(types.identifier('operand'), types.stringLiteral(filter.operand || '')),
918
- ]);
919
- })),
920
- ])));
921
- }
922
- // Build useMemo dependencies including filter state IDs
1173
+ appendFiltersParam(paramsProps, usage.filters, buildFilterDestinationExpression);
1174
+ // Build useMemo dependencies including filter state IDs and dynamic sort state IDs
923
1175
  var memoDeps = [types.identifier(vars.debouncedSearchQueryVar)];
924
- usage.filterStateIds.forEach(function (stateId) {
925
- memoDeps.push(types.identifier(stateId));
926
- });
1176
+ var seenDeps = new Set([vars.debouncedSearchQueryVar]);
1177
+ pushStateIdsAsDeps(memoDeps, seenDeps, usage.filterStateIds);
1178
+ if (usage.dynamicSort) {
1179
+ pushStateIdsAsDeps(memoDeps, seenDeps, usage.dynamicSort.depStateIds);
1180
+ }
1181
+ pushUrlSearchParamMemoDeps(memoDeps, usage);
927
1182
  dp.openingElement.attributes.push(types.jsxAttribute(types.jsxIdentifier('params'), types.jsxExpressionContainer(types.callExpression(types.identifier('useMemo'), [
928
1183
  types.arrowFunctionExpression([], types.objectExpression(paramsProps)),
929
1184
  types.arrayExpression(memoDeps),
930
1185
  ]))));
931
- // Add initialData
932
- dp.openingElement.attributes.push(types.jsxAttribute(types.jsxIdentifier('initialData'), types.jsxExpressionContainer(types.conditionalExpression(types.unaryExpression('!', types.identifier(vars.debouncedSearchQueryVar), true), types.optionalMemberExpression(types.identifier('props'), types.identifier(vars.propsPrefix), false, true), types.identifier('undefined')))));
933
- // Add key
1186
+ // Add initialData. See paginated+search updater for why we skip prefetch
1187
+ // reuse when a dynamic state-bound sort is active.
1188
+ if (!usage.dynamicSort) {
1189
+ 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), types.identifier('undefined')))));
1190
+ }
1191
+ // Add key — sort is intentionally NOT included; see paginated+search updater.
934
1192
  dp.openingElement.attributes.push(types.jsxAttribute(types.jsxIdentifier('key'), types.jsxExpressionContainer(types.templateLiteral([
935
1193
  types.templateElement({ raw: 'search-', cooked: 'search-' }),
936
1194
  types.templateElement({ raw: '', cooked: '' }),
@@ -976,9 +1234,8 @@ function updateDataProviderForPlain(dp, fileName, usage) {
976
1234
  }
977
1235
  // Build useMemo dependencies including filter state IDs
978
1236
  var memoDeps = [];
979
- usage.filterStateIds.forEach(function (stateId) {
980
- memoDeps.push(types.identifier(stateId));
981
- });
1237
+ pushStateIdsAsDeps(memoDeps, new Set(), usage.filterStateIds);
1238
+ pushUrlSearchParamMemoDeps(memoDeps, usage);
982
1239
  // Wrap params in useMemo with filter state dependencies
983
1240
  var memoizedParams = types.callExpression(types.identifier('useMemo'), [
984
1241
  types.arrowFunctionExpression([], paramsExpression),
@@ -1198,7 +1455,7 @@ function ensureAPIRouteExists(extractedResources, usage, dataSources) {
1198
1455
  };
1199
1456
  }
1200
1457
  }
1201
- function updateGetStaticProps(chunks, registry, dependencies) {
1458
+ function updateGetStaticProps(chunks, registry, dependencies, folderPath) {
1202
1459
  var _a;
1203
1460
  var getStaticPropsChunk = chunks.find(function (c) { return c.name === 'getStaticProps'; });
1204
1461
  if (!getStaticPropsChunk || getStaticPropsChunk.type !== ChunkType.AST) {
@@ -1308,9 +1565,11 @@ function updateGetStaticProps(chunks, registry, dependencies) {
1308
1565
  ]));
1309
1566
  // Add import dependency for the fetcher
1310
1567
  if (!dependencies[fetcherImportName]) {
1568
+ var depth = (folderPath ? folderPath.length : 0) + 1;
1569
+ var relativePrefix = '../'.repeat(depth);
1311
1570
  dependencies[fetcherImportName] = {
1312
1571
  type: 'local',
1313
- path: "../utils/data-sources/".concat(fileName),
1572
+ path: "".concat(relativePrefix, "utils/data-sources/").concat(fileName),
1314
1573
  };
1315
1574
  }
1316
1575
  // Add to props
@@ -1421,9 +1680,11 @@ function updateGetStaticProps(chunks, registry, dependencies) {
1421
1680
  }
1422
1681
  // Add import dependency for the fetcher
1423
1682
  if (!dependencies[fetcherImportName]) {
1683
+ var depth = (folderPath ? folderPath.length : 0) + 1;
1684
+ var relativePrefix = '../'.repeat(depth);
1424
1685
  dependencies[fetcherImportName] = {
1425
1686
  type: 'local',
1426
- path: "../utils/data-sources/".concat(fileName),
1687
+ path: "".concat(relativePrefix, "utils/data-sources/").concat(fileName),
1427
1688
  };
1428
1689
  }
1429
1690
  }