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

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
@@ -62,10 +62,220 @@ Object.defineProperty(exports, "__esModule", { value: true });
62
62
  exports.createNextArrayMapperPaginationPlugin = void 0;
63
63
  var teleport_types_1 = require("@teleporthq/teleport-types");
64
64
  var types = __importStar(require("@babel/types"));
65
+ var parser_1 = require("@babel/parser");
65
66
  var teleport_shared_1 = require("@teleporthq/teleport-shared");
66
67
  var teleport_plugin_common_1 = require("@teleporthq/teleport-plugin-common");
67
68
  var utils_1 = require("./utils");
68
69
  var data_source_fetchers_1 = require("./data-source-fetchers");
70
+ var sort_utils_1 = require("./sort-utils");
71
+ var filter_utils_1 = require("./filter-utils");
72
+ // tslint:disable-next-line:no-any
73
+ function parseSearchDefaultValue(raw) {
74
+ if (!raw || typeof raw !== 'object') {
75
+ return undefined;
76
+ }
77
+ if (raw.type === 'static' && typeof raw.content === 'string' && raw.content.length > 0) {
78
+ return { kind: 'static', value: raw.content };
79
+ }
80
+ if ((raw.type === 'expr' || raw.type === 'dynamic') && typeof raw.content === 'string') {
81
+ var src = raw.content.trim();
82
+ if (src.length === 0) {
83
+ return undefined;
84
+ }
85
+ try {
86
+ var ast = (0, parser_1.parseExpression)(src, { sourceType: 'module', plugins: ['jsx'] });
87
+ return { kind: 'expression', ast: ast };
88
+ }
89
+ catch (_a) {
90
+ // Fall back to no seed if the expression is malformed — better to
91
+ // render an empty input than to emit broken code.
92
+ return undefined;
93
+ }
94
+ }
95
+ return undefined;
96
+ }
97
+ function searchDefaultValueInitAST(value) {
98
+ if (!value) {
99
+ return types.stringLiteral('');
100
+ }
101
+ if (value.kind === 'static') {
102
+ return types.stringLiteral(value.value);
103
+ }
104
+ // Clone so multiple `useState(...)` call sites each get an
105
+ // independent AST node — Babel does not support shared references.
106
+ return types.cloneNode(value.ast, /* deep */ true);
107
+ }
108
+ // The UIDL's `filters.content` array can wrap one or more conditions inside
109
+ // a `{ type: 'group', operator: 'and' | 'or', children: [...] }` entry — the
110
+ // GUI builds this shape when the inspector adds a logical group. The data
111
+ // source API endpoint (`processFilters` in the generated data-source module)
112
+ // only knows how to consume a FLAT array of `{ source, destination, operand }`
113
+ // conditions, so we walk groups recursively here and collect their leaf
114
+ // conditions in order. Anything that isn't a group AND isn't a condition is
115
+ // dropped — defensive against partially-built filter entries that would
116
+ // otherwise emit `{ source: '', destination: '', operand: '' }` rows the
117
+ // API ignores anyway.
118
+ //
119
+ // `or`-grouped conditions are flattened just like `and`-grouped ones — the
120
+ // downstream SQL builder always joins flat filters with `AND`, so emitting
121
+ // the conditions side-by-side approximates AND semantics. A future API
122
+ // upgrade that respects group operators would key off the original tree
123
+ // directly; until then, AND-flattening is the closest correct behaviour and
124
+ // matches what the inspector preview shows for the common single-group case
125
+ // the GUI emits today.
126
+ function flattenFilterGroups(filters) {
127
+ if (!Array.isArray(filters)) {
128
+ return [];
129
+ }
130
+ var out = [];
131
+ var walk = function (entry) {
132
+ if (!entry || typeof entry !== 'object') {
133
+ return;
134
+ }
135
+ if (entry.type === 'group' && Array.isArray(entry.children)) {
136
+ for (var _i = 0, _a = entry.children; _i < _a.length; _i++) {
137
+ var child = _a[_i];
138
+ walk(child);
139
+ }
140
+ return;
141
+ }
142
+ // Backwards-compatible: entries without an explicit `type` (legacy flat
143
+ // form) and entries explicitly tagged `condition` are both treated as
144
+ // condition leaves.
145
+ if (entry.type === undefined || entry.type === 'condition') {
146
+ out.push(entry);
147
+ }
148
+ };
149
+ for (var _i = 0, filters_1 = filters; _i < filters_1.length; _i++) {
150
+ var entry = filters_1[_i];
151
+ walk(entry);
152
+ }
153
+ return out;
154
+ }
155
+ // Walks a flat filter list to extract every `urlSearchParams` reference key
156
+ // (e.g. `'categoryFilter'`). Used to (a) inject `const router = useRouter()`
157
+ // once at the top of the component, (b) wire `router.query.<key>` into the
158
+ // `useMemo` dependency array so the client-side fetch reruns whenever the
159
+ // buyer navigates to a URL with a different `?key=value`. Returned in the
160
+ // order keys first appear so the generated dep array stays stable.
161
+ function collectFilterUrlSearchParamKeys(filters) {
162
+ var seen = new Set();
163
+ var out = [];
164
+ for (var _i = 0, filters_2 = filters; _i < filters_2.length; _i++) {
165
+ var f = filters_2[_i];
166
+ var dest = f === null || f === void 0 ? void 0 : f.destination;
167
+ if (!teleport_plugin_common_1.ASTUtils.isUIDLDynamicReference(dest)) {
168
+ continue;
169
+ }
170
+ var content = dest.content;
171
+ if (!content || content.referenceType !== 'urlSearchParams' || !content.id) {
172
+ continue;
173
+ }
174
+ if (seen.has(content.id)) {
175
+ continue;
176
+ }
177
+ seen.add(content.id);
178
+ out.push(content.id);
179
+ }
180
+ return out;
181
+ }
182
+ // Returns true when any filter destination is a `urlSearchParams` dynamic
183
+ // reference — used by the plugin to know it needs to import `useRouter` and
184
+ // emit `const router = useRouter()` at the top of the component. Cheaper to
185
+ // test against the precomputed key list once than to walk all filters on
186
+ // every check.
187
+ function hasUrlSearchParamFilters(usage) {
188
+ return usage.filterUrlSearchParamKeys.length > 0;
189
+ }
190
+ // Appends `router.query.<key>` member expressions to a useMemo deps array so
191
+ // every client-side fetch refires when the buyer navigates between URLs
192
+ // that differ only by `?key=value`. React's shallow-compare semantics treat
193
+ // the member expression as a distinct value per render, so the array stays
194
+ // stable across paints with the same query string and changes the moment
195
+ // the URL does. Skip the bare-identifier dedupe `filterStateIds` uses —
196
+ // member expressions never collide with state identifiers, and the deps
197
+ // array allows duplicates without harm.
198
+ function pushUrlSearchParamMemoDeps(memoDeps, usage) {
199
+ for (var _i = 0, _a = usage.filterUrlSearchParamKeys; _i < _a.length; _i++) {
200
+ var key = _a[_i];
201
+ memoDeps.push(types.memberExpression(types.memberExpression(types.identifier('router'), types.identifier('query')), types.identifier(key)));
202
+ }
203
+ }
204
+ // Builds the AST for `!router.query.<key1> && !router.query.<key2> && ...`,
205
+ // used as a runtime guard around `initialData={props.X}` so the server-
206
+ // prefetched (unfiltered) data is only handed to `DataProvider` when the
207
+ // URL has no active filter. Without this guard, navigating to
208
+ // `/products-list?categoryFilter=Rings` (especially via soft Next.js
209
+ // transitions where the page component remounts with fresh getStaticProps
210
+ // but the buyer's URL filter is still in scope) shows the unfiltered list
211
+ // for the first paint AND keeps it on screen because `DataProvider`'s
212
+ // `passFetchBecauseWeHaveInitialData` ref skips the very first fetch when
213
+ // `initialData !== undefined`. By emitting `undefined` here whenever ANY
214
+ // url-search-param filter is set, the DataProvider's mount-time fetch runs
215
+ // immediately with the filtered params instead of presenting stale data.
216
+ //
217
+ // Returns `null` when the usage has no url-search-param filters at all —
218
+ // callers fall back to the existing `props.X` expression unchanged so
219
+ // non-filtered pages still benefit from the SSR prefetch.
220
+ function buildNoUrlFilterGuard(usage) {
221
+ if (usage.filterUrlSearchParamKeys.length === 0 && usage.filterStateIds.length === 0) {
222
+ return null;
223
+ }
224
+ var guards = [];
225
+ for (var _i = 0, _a = usage.filterUrlSearchParamKeys; _i < _a.length; _i++) {
226
+ var key = _a[_i];
227
+ guards.push(types.unaryExpression('!', types.memberExpression(types.memberExpression(types.identifier('router'), types.identifier('query')), types.identifier(key)), true));
228
+ }
229
+ // State-bound filter destinations (e.g. `selectedCategory`) emit as bare
230
+ // identifiers, so the corresponding guard is `!selectedCategory`. An empty
231
+ // string ('') for the state — which the GUI emits when the user picks the
232
+ // "All Categories" reset option — is falsy and so passes through the guard
233
+ // exactly like a missing URL param: initialData (unfiltered prefetch) wins
234
+ // until the user picks a real value.
235
+ for (var _b = 0, _c = usage.filterStateIds; _b < _c.length; _b++) {
236
+ var id = _c[_b];
237
+ guards.push(types.unaryExpression('!', types.identifier(id), true));
238
+ }
239
+ return guards.reduce(function (acc, next) { return types.logicalExpression('&&', acc, next); });
240
+ }
241
+ // Wraps an existing `initialData` condition with the additional "no URL
242
+ // filter active" guard so server-prefetched data is only handed to
243
+ // DataProvider when both (a) the original guard (page === 1, no search
244
+ // query, etc.) AND (b) every relevant `router.query.<key>` is falsy. See
245
+ // `buildNoUrlFilterGuard` for the rationale on why bare-identity feature
246
+ // detection is safer than the `dynamicSort`-style "always undefined" path.
247
+ function wrapInitialDataWithUrlFilterGuard(baseCondition, usage) {
248
+ var noFilterGuard = buildNoUrlFilterGuard(usage);
249
+ if (!noFilterGuard) {
250
+ return baseCondition;
251
+ }
252
+ return types.logicalExpression('&&', baseCondition, noFilterGuard);
253
+ }
254
+ // Builds the destination AST for a single filter entry. Replaces the bare
255
+ // `ASTUtils.convertFilterDestinationToExpression(filter.destination)` call
256
+ // at every emit site so `urlSearchParams` references resolve to
257
+ // `router?.query?.<key>` instead of falling through to a bare identifier
258
+ // (which the existing helper would emit, leaving the client-side fetch
259
+ // referencing an undeclared symbol).
260
+ //
261
+ // State and prop references delegate back to the shared helper so the
262
+ // existing inspector behaviour (state-bound filter destinations) keeps
263
+ // working unchanged. `router?.query?.<key>` is the same shape the
264
+ // `createNextUrlSearchParamsPlugin`-driven `dynamicReferencePrefixMap`
265
+ // emits for page-level navlink reads.
266
+ function buildFilterDestinationExpression(destination) {
267
+ if (teleport_plugin_common_1.ASTUtils.isUIDLDynamicReference(destination)) {
268
+ var content = destination.content;
269
+ if ((content === null || content === void 0 ? void 0 : content.referenceType) === 'urlSearchParams' && (content === null || content === void 0 ? void 0 : content.id)) {
270
+ // router?.query?.<id> — the optional-chain survives the first paint
271
+ // where Next.js's `useRouter()` returns `null` during static export
272
+ // hydration, so the fetch doesn't crash with "Cannot read properties
273
+ // of null" before the router is ready.
274
+ return types.optionalMemberExpression(types.optionalMemberExpression(types.identifier('router'), types.identifier('query'), false, true), types.identifier(content.id), false, true);
275
+ }
276
+ }
277
+ return teleport_plugin_common_1.ASTUtils.convertFilterDestinationToExpression(destination);
278
+ }
69
279
  // Scan UIDL to find all data source usages and build a registry
70
280
  function buildStateRegistry(uidlNode) {
71
281
  var usages = [];
@@ -112,20 +322,48 @@ function buildStateRegistry(uidlNode) {
112
322
  if ((_h = (_g = parentDataSource.resourceParams) === null || _g === void 0 ? void 0 : _g.queryColumns) === null || _h === void 0 ? void 0 : _h.content) {
113
323
  queryColumns = parentDataSource.resourceParams.queryColumns.content;
114
324
  }
115
- // Extract sorts from parent's resource params
325
+ // Extract sorts from parent's resource params (legacy static array form)
116
326
  var sorts = [];
117
327
  if ((_k = (_j = parentDataSource.resourceParams) === null || _j === void 0 ? void 0 : _j.sorts) === null || _k === void 0 ? void 0 : _k.content) {
118
328
  sorts = parentDataSource.resourceParams.sorts.content;
119
329
  }
120
- // Extract filters from parent's resource params
330
+ // If legacy sorts aren't set, fall back to the new dynamic single-column
331
+ // sort fields on the cms-list-repeater (used by admin-panel listing pages).
332
+ var dynamicSort = void 0;
333
+ if ((!sorts || sorts.length === 0) && content.sort) {
334
+ dynamicSort = (0, sort_utils_1.extractDynamicSort)(content.sort, content.sortDirection);
335
+ }
336
+ // Extract filters from parent's resource params. The inspector wraps
337
+ // every condition in a `{ type: 'group' }` envelope (single-group or
338
+ // nested), so flatten to leaf conditions before the downstream emit
339
+ // sites consume `.source` / `.destination` / `.operand` directly —
340
+ // they expect a flat condition array. See `flattenFilterGroups`'s
341
+ // header comment for why AND-flattening is the correct fallback for
342
+ // the API endpoint's flat-condition contract.
121
343
  var filters = [];
122
344
  if ((_m = (_l = parentDataSource.resourceParams) === null || _l === void 0 ? void 0 : _l.filters) === null || _m === void 0 ? void 0 : _m.content) {
123
- filters = parentDataSource.resourceParams.filters.content;
345
+ filters = flattenFilterGroups(parentDataSource.resourceParams.filters.content);
346
+ }
347
+ // Split dynamic destination keys by reference type. `state`/`prop`
348
+ // refs resolve to bare identifiers (so they're useMemo deps as-is);
349
+ // `urlSearchParams` refs resolve to `router.query.<key>` and need a
350
+ // `useRouter()` declaration injected separately.
351
+ var filterStateIds = [];
352
+ for (var _i = 0, filters_3 = filters; _i < filters_3.length; _i++) {
353
+ var f = filters_3[_i];
354
+ if (!teleport_plugin_common_1.ASTUtils.isUIDLDynamicReference(f.destination)) {
355
+ continue;
356
+ }
357
+ var destinationContent = f.destination.content;
358
+ if (!destinationContent || !destinationContent.id) {
359
+ continue;
360
+ }
361
+ if (destinationContent.referenceType === 'urlSearchParams') {
362
+ continue;
363
+ }
364
+ filterStateIds.push(destinationContent.id);
124
365
  }
125
- // Extract state IDs from dynamic filter destinations
126
- var filterStateIds = filters
127
- .filter(function (f) { return teleport_plugin_common_1.ASTUtils.isUIDLDynamicReference(f.destination); })
128
- .map(function (f) { return f.destination.content.id; });
366
+ var filterUrlSearchParamKeys = collectFilterUrlSearchParamKeys(filters);
129
367
  // Extract limit from parent's resource params (for plain array mappers)
130
368
  var limit = 0;
131
369
  if ((_p = (_o = parentDataSource.resourceParams) === null || _o === void 0 ? void 0 : _o.limit) === null || _p === void 0 ? void 0 : _p.content) {
@@ -134,6 +372,7 @@ function buildStateRegistry(uidlNode) {
134
372
  // For paginated mappers, use perPage from cms-list-repeater
135
373
  // For plain mappers, use limit from data-source-list resource params
136
374
  var effectivePerPage = content.paginated ? content.perPage : limit || content.perPage;
375
+ var searchDefaultValue = parseSearchDefaultValue(content.searchDefaultValue);
137
376
  var usage = {
138
377
  index: index++,
139
378
  dataSourceIdentifier: parentDataSource.identifier,
@@ -147,10 +386,13 @@ function buildStateRegistry(uidlNode) {
147
386
  perPage: effectivePerPage,
148
387
  searchEnabled: !!content.searchEnabled,
149
388
  searchDebounce: content.searchDebounce || 300,
389
+ searchDefaultValue: searchDefaultValue,
150
390
  queryColumns: queryColumns,
151
391
  sorts: sorts,
392
+ dynamicSort: dynamicSort,
152
393
  filters: filters,
153
394
  filterStateIds: filterStateIds,
395
+ filterUrlSearchParamKeys: filterUrlSearchParamKeys,
154
396
  category: 'plain',
155
397
  };
156
398
  // Determine category
@@ -179,8 +421,8 @@ function buildStateRegistry(uidlNode) {
179
421
  }
180
422
  // Recurse into children
181
423
  if (((_r = node.content) === null || _r === void 0 ? void 0 : _r.children) && Array.isArray(node.content.children)) {
182
- for (var _i = 0, _u = node.content.children; _i < _u.length; _i++) {
183
- var child = _u[_i];
424
+ for (var _u = 0, _v = node.content.children; _u < _v.length; _u++) {
425
+ var child = _v[_u];
184
426
  traverse(child, parentDataSource);
185
427
  }
186
428
  }
@@ -205,8 +447,8 @@ function buildStateRegistry(uidlNode) {
205
447
  }
206
448
  }
207
449
  if (Array.isArray(node.children)) {
208
- for (var _v = 0, _w = node.children; _v < _w.length; _v++) {
209
- var child = _w[_v];
450
+ for (var _w = 0, _x = node.children; _w < _x.length; _w++) {
451
+ var child = _x[_w];
210
452
  traverse(child, parentDataSource);
211
453
  }
212
454
  }
@@ -236,8 +478,9 @@ function getStateVarsForUsage(usage) {
236
478
  // ==================== MAIN PLUGIN ====================
237
479
  var createNextArrayMapperPaginationPlugin = function () {
238
480
  var paginationPlugin = function (structure) { return __awaiter(void 0, void 0, void 0, function () {
239
- 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;
240
- return __generator(this, function (_a) {
481
+ 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;
482
+ var _a;
483
+ return __generator(this, function (_b) {
241
484
  uidl = structure.uidl, chunks = structure.chunks, dependencies = structure.dependencies, options = structure.options;
242
485
  componentChunk = chunks.find(function (chunk) { return chunk.name === 'jsx-component'; });
243
486
  if (!componentChunk || componentChunk.type !== teleport_types_1.ChunkType.AST) {
@@ -347,16 +590,19 @@ var createNextArrayMapperPaginationPlugin = function () {
347
590
  ]), types.callExpression(types.identifier('useState'), [
348
591
  types.objectExpression([
349
592
  types.objectProperty(types.identifier('page'), types.numericLiteral(1)),
350
- types.objectProperty(types.identifier('debouncedQuery'), types.stringLiteral('')),
593
+ types.objectProperty(types.identifier('debouncedQuery'), searchDefaultValueInitAST(usage.searchDefaultValue)),
351
594
  ]),
352
595
  ])),
353
596
  ]));
354
- // Immediate search query state
597
+ // Immediate search query state — seeded with `searchDefaultValue`
598
+ // when provided so the input is pre-filled on mount.
355
599
  stateDeclarations.push(types.variableDeclaration('const', [
356
600
  types.variableDeclarator(types.arrayPattern([
357
601
  types.identifier(vars.searchQueryVar),
358
602
  types.identifier(vars.setSearchQueryVar),
359
- ]), types.callExpression(types.identifier('useState'), [types.stringLiteral('')])),
603
+ ]), types.callExpression(types.identifier('useState'), [
604
+ searchDefaultValueInitAST(usage.searchDefaultValue),
605
+ ])),
360
606
  ]));
361
607
  // Debounce effect
362
608
  effectStatements.push(types.expressionStatement(types.callExpression(types.identifier('useEffect'), [
@@ -404,17 +650,7 @@ var createNextArrayMapperPaginationPlugin = function () {
404
650
  ])));
405
651
  }
406
652
  // Add filters to count fetch params if present
407
- if (usage.filters && usage.filters.length > 0) {
408
- urlParams.push(types.objectProperty(types.identifier('filters'), types.callExpression(types.memberExpression(types.identifier('JSON'), types.identifier('stringify')), [
409
- types.arrayExpression(usage.filters.map(function (filter) {
410
- return types.objectExpression([
411
- types.objectProperty(types.identifier('source'), types.stringLiteral(filter.source || '')),
412
- types.objectProperty(types.identifier('destination'), teleport_plugin_common_1.ASTUtils.convertFilterDestinationToExpression(filter.destination)),
413
- types.objectProperty(types.identifier('operand'), types.stringLiteral(filter.operand || '')),
414
- ]);
415
- })),
416
- ])));
417
- }
653
+ (0, filter_utils_1.appendFiltersParam)(urlParams, usage.filters, buildFilterDestinationExpression);
418
654
  // Build the count fetch effect body
419
655
  var countFetchEffectBody = [];
420
656
  // Only add skip-on-mount check for pages (where we have server-side count)
@@ -450,11 +686,21 @@ var createNextArrayMapperPaginationPlugin = function () {
450
686
  ])),
451
687
  ])),
452
688
  ])));
689
+ var countEffectDeps = [
690
+ types.memberExpression(types.identifier(vars.combinedStateVar), types.identifier('debouncedQuery')),
691
+ ];
692
+ // Refresh the count whenever a state-bound filter destination changes
693
+ // (e.g. user picks a category) so pagination tracks the filtered
694
+ // result-set, not the mount-time unfiltered total. Without these
695
+ // deps, ds_0_maxPages stays at the original count and the "Next"
696
+ // button stays enabled past the actual last page of the filtered
697
+ // results — letting the user click into empty pages.
698
+ (0, filter_utils_1.pushStateIdsAsDeps)(countEffectDeps, new Set(), usage.filterStateIds);
699
+ // Same goes for URL-driven filters (already documented above).
700
+ pushUrlSearchParamMemoDeps(countEffectDeps, usage);
453
701
  effectStatements.push(types.expressionStatement(types.callExpression(types.identifier('useEffect'), [
454
702
  types.arrowFunctionExpression([], types.blockStatement(countFetchEffectBody)),
455
- types.arrayExpression([
456
- types.memberExpression(types.identifier(vars.combinedStateVar), types.identifier('debouncedQuery')),
457
- ]),
703
+ types.arrayExpression(countEffectDeps),
458
704
  ])));
459
705
  }
460
706
  else if (usage.category === 'paginated-only') {
@@ -496,7 +742,16 @@ var createNextArrayMapperPaginationPlugin = function () {
496
742
  ])),
497
743
  ])),
498
744
  ])),
499
- types.arrayExpression([]), // Empty dependency array - fetch on mount only
745
+ // Default to mount-only; refresh when ANY filter destination
746
+ // changes — state-bound (e.g. `selectedCategory`) and
747
+ // URL-driven — so the pagination control reflects the current
748
+ // filtered count instead of the unfiltered mount-time total.
749
+ types.arrayExpression((function () {
750
+ var deps = [];
751
+ (0, filter_utils_1.pushStateIdsAsDeps)(deps, new Set(), usage.filterStateIds);
752
+ pushUrlSearchParamMemoDeps(deps, usage);
753
+ return deps;
754
+ })()),
500
755
  ])));
501
756
  }
502
757
  }
@@ -509,13 +764,17 @@ var createNextArrayMapperPaginationPlugin = function () {
509
764
  types.variableDeclarator(types.arrayPattern([
510
765
  types.identifier(vars.debouncedSearchQueryVar),
511
766
  types.identifier(vars.setDebouncedSearchQueryVar),
512
- ]), types.callExpression(types.identifier('useState'), [types.stringLiteral('')])),
767
+ ]), types.callExpression(types.identifier('useState'), [
768
+ searchDefaultValueInitAST(usage.searchDefaultValue),
769
+ ])),
513
770
  ]));
514
771
  stateDeclarations.push(types.variableDeclaration('const', [
515
772
  types.variableDeclarator(types.arrayPattern([
516
773
  types.identifier(vars.searchQueryVar),
517
774
  types.identifier(vars.setSearchQueryVar),
518
- ]), types.callExpression(types.identifier('useState'), [types.stringLiteral('')])),
775
+ ]), types.callExpression(types.identifier('useState'), [
776
+ searchDefaultValueInitAST(usage.searchDefaultValue),
777
+ ])),
519
778
  ]));
520
779
  // Debounce effect
521
780
  effectStatements.push(types.expressionStatement(types.callExpression(types.identifier('useEffect'), [
@@ -542,6 +801,36 @@ var createNextArrayMapperPaginationPlugin = function () {
542
801
  });
543
802
  // Insert state declarations at the beginning
544
803
  stateDeclarations.reverse().forEach(function (s) { return blockStatement.body.unshift(s); });
804
+ needsUseRouter = registry.usages.some(function (u) { return hasUrlSearchParamFilters(u); });
805
+ if (needsUseRouter) {
806
+ if (!dependencies.useRouter) {
807
+ // Match the shape the sibling Next.js plugins use (i18n locale mapper,
808
+ // search-params plugin) so the deduped import line is identical and
809
+ // the dependency-resolver merges instead of emitting a second one.
810
+ dependencies.useRouter = {
811
+ type: 'library',
812
+ path: 'next/router',
813
+ version: '^12.1.10',
814
+ meta: { namedImport: true },
815
+ };
816
+ }
817
+ hasRouterDecl = blockStatement.body.some(function (statement) {
818
+ return statement.type === 'VariableDeclaration' &&
819
+ statement.declarations.some(function (decl) {
820
+ var _a;
821
+ return decl.id.type === 'Identifier' &&
822
+ decl.id.name === 'router' &&
823
+ ((_a = decl.init) === null || _a === void 0 ? void 0 : _a.type) === 'CallExpression' &&
824
+ decl.init.callee.type === 'Identifier' &&
825
+ decl.init.callee.name === 'useRouter';
826
+ });
827
+ });
828
+ if (!hasRouterDecl) {
829
+ blockStatement.body.unshift(types.variableDeclaration('const', [
830
+ types.variableDeclarator(types.identifier('router'), types.callExpression(types.identifier('useRouter'), [])),
831
+ ]));
832
+ }
833
+ }
545
834
  returnIndex = blockStatement.body.findIndex(function (s) { return s.type === 'ReturnStatement'; });
546
835
  insertIndex = returnIndex !== -1 ? returnIndex : blockStatement.body.length;
547
836
  effectStatements.reverse().forEach(function (e) { return blockStatement.body.splice(insertIndex, 0, e); });
@@ -615,7 +904,7 @@ var createNextArrayMapperPaginationPlugin = function () {
615
904
  });
616
905
  // STEP 6: Update getStaticProps if this is a page
617
906
  if (isPage) {
618
- updateGetStaticProps(chunks, registry, dependencies);
907
+ updateGetStaticProps(chunks, registry, dependencies, (_a = uidl.outputOptions) === null || _a === void 0 ? void 0 : _a.folderPath);
619
908
  }
620
909
  return [2 /*return*/, structure];
621
910
  });
@@ -800,42 +1089,37 @@ function updateDataProviderForPaginatedSearch(dp, usage, vars, fileName) {
800
1089
  if (usage.queryColumns.length > 0) {
801
1090
  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); }))])));
802
1091
  }
803
- // Add sorts if present
804
- if (usage.sorts && usage.sorts.length > 0) {
805
- paramsProps.push(types.objectProperty(types.identifier('sorts'), types.callExpression(types.memberExpression(types.identifier('JSON'), types.identifier('stringify')), [
806
- types.arrayExpression(usage.sorts.map(function (sort) {
807
- return types.objectExpression([
808
- types.objectProperty(types.identifier('field'), types.stringLiteral(sort.field || '')),
809
- types.objectProperty(types.identifier('order'), types.stringLiteral(sort.order || '')),
810
- ]);
811
- })),
812
- ])));
813
- }
1092
+ // Add sorts if present (legacy static array wins; otherwise dynamic state-bound sort)
1093
+ (0, sort_utils_1.appendSortsParam)(paramsProps, usage.sorts, usage.dynamicSort);
814
1094
  // Add filters if present
815
- if (usage.filters && usage.filters.length > 0) {
816
- paramsProps.push(types.objectProperty(types.identifier('filters'), types.callExpression(types.memberExpression(types.identifier('JSON'), types.identifier('stringify')), [
817
- types.arrayExpression(usage.filters.map(function (filter) {
818
- return types.objectExpression([
819
- types.objectProperty(types.identifier('source'), types.stringLiteral(filter.source || '')),
820
- types.objectProperty(types.identifier('destination'), teleport_plugin_common_1.ASTUtils.convertFilterDestinationToExpression(filter.destination)),
821
- types.objectProperty(types.identifier('operand'), types.stringLiteral(filter.operand || '')),
822
- ]);
823
- })),
824
- ])));
825
- }
826
- // Build useMemo dependencies including filter state IDs
1095
+ (0, filter_utils_1.appendFiltersParam)(paramsProps, usage.filters, buildFilterDestinationExpression);
1096
+ // Build useMemo dependencies including filter state IDs and dynamic sort state IDs
827
1097
  var memoDeps = [types.identifier(vars.combinedStateVar)];
828
- usage.filterStateIds.forEach(function (stateId) {
829
- memoDeps.push(types.identifier(stateId));
830
- });
1098
+ var seenDeps = new Set([vars.combinedStateVar]);
1099
+ (0, filter_utils_1.pushStateIdsAsDeps)(memoDeps, seenDeps, usage.filterStateIds);
1100
+ if (usage.dynamicSort) {
1101
+ (0, filter_utils_1.pushStateIdsAsDeps)(memoDeps, seenDeps, usage.dynamicSort.depStateIds);
1102
+ }
1103
+ pushUrlSearchParamMemoDeps(memoDeps, usage);
831
1104
  dp.openingElement.attributes.push(types.jsxAttribute(types.jsxIdentifier('params'), types.jsxExpressionContainer(types.callExpression(types.identifier('useMemo'), [
832
1105
  types.arrowFunctionExpression([], types.objectExpression(paramsProps)),
833
1106
  types.arrayExpression(memoDeps),
834
1107
  ]))));
835
- // Add initialData
836
- 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));
837
- 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')))));
838
- // Add key
1108
+ // Add initialData. Skip the server-prefetched data entirely when a dynamic
1109
+ // state-bound sort is active the prefetch ran without sort parameters, so
1110
+ // reusing it would mask the current sort state AND cause DataProvider to skip
1111
+ // the first client fetch (its internal guard only skips when initialData is
1112
+ // defined on mount). Without that skip, toggling sort correctly triggers a
1113
+ // refetch via the useMemo params dependency chain.
1114
+ if (!usage.dynamicSort) {
1115
+ 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);
1116
+ 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')))));
1117
+ }
1118
+ // Add key. Sort is intentionally NOT part of the key — a key change would
1119
+ // remount the DataProvider, and a fresh mount re-arms the internal
1120
+ // "skip-first-fetch-when-we-have-initialData" guard, which would prevent the
1121
+ // new sort params from reaching the fetcher. Leaving sort out of the key
1122
+ // lets the useMemo params identity change alone drive refetch.
839
1123
  dp.openingElement.attributes.push(types.jsxAttribute(types.jsxIdentifier('key'), types.jsxExpressionContainer(types.templateLiteral([
840
1124
  types.templateElement({
841
1125
  raw: "".concat(usage.dataSourceIdentifier, "-"),
@@ -863,42 +1147,29 @@ function updateDataProviderForPaginationOnly(dp, usage, vars, fileName) {
863
1147
  types.objectProperty(types.identifier('page'), types.identifier(vars.pageStateVar)),
864
1148
  types.objectProperty(types.identifier('perPage'), types.numericLiteral(usage.perPage)),
865
1149
  ];
866
- // Add sorts if present
867
- if (usage.sorts && usage.sorts.length > 0) {
868
- paramsProps.push(types.objectProperty(types.identifier('sorts'), types.callExpression(types.memberExpression(types.identifier('JSON'), types.identifier('stringify')), [
869
- types.arrayExpression(usage.sorts.map(function (sort) {
870
- return types.objectExpression([
871
- types.objectProperty(types.identifier('field'), types.stringLiteral(sort.field || '')),
872
- types.objectProperty(types.identifier('order'), types.stringLiteral(sort.order || '')),
873
- ]);
874
- })),
875
- ])));
876
- }
1150
+ // Add sorts if present (legacy static array wins; otherwise dynamic state-bound sort)
1151
+ (0, sort_utils_1.appendSortsParam)(paramsProps, usage.sorts, usage.dynamicSort);
877
1152
  // Add filters if present
878
- if (usage.filters && usage.filters.length > 0) {
879
- paramsProps.push(types.objectProperty(types.identifier('filters'), types.callExpression(types.memberExpression(types.identifier('JSON'), types.identifier('stringify')), [
880
- types.arrayExpression(usage.filters.map(function (filter) {
881
- return types.objectExpression([
882
- types.objectProperty(types.identifier('source'), types.stringLiteral(filter.source || '')),
883
- types.objectProperty(types.identifier('destination'), teleport_plugin_common_1.ASTUtils.convertFilterDestinationToExpression(filter.destination)),
884
- types.objectProperty(types.identifier('operand'), types.stringLiteral(filter.operand || '')),
885
- ]);
886
- })),
887
- ])));
888
- }
889
- // Build useMemo dependencies including filter state IDs
1153
+ (0, filter_utils_1.appendFiltersParam)(paramsProps, usage.filters, buildFilterDestinationExpression);
1154
+ // Build useMemo dependencies including filter state IDs and dynamic sort state IDs
890
1155
  var memoDeps = [types.identifier(vars.pageStateVar)];
891
- usage.filterStateIds.forEach(function (stateId) {
892
- memoDeps.push(types.identifier(stateId));
893
- });
1156
+ var seenDeps = new Set([vars.pageStateVar]);
1157
+ (0, filter_utils_1.pushStateIdsAsDeps)(memoDeps, seenDeps, usage.filterStateIds);
1158
+ if (usage.dynamicSort) {
1159
+ (0, filter_utils_1.pushStateIdsAsDeps)(memoDeps, seenDeps, usage.dynamicSort.depStateIds);
1160
+ }
1161
+ pushUrlSearchParamMemoDeps(memoDeps, usage);
894
1162
  // Add params
895
1163
  dp.openingElement.attributes.push(types.jsxAttribute(types.jsxIdentifier('params'), types.jsxExpressionContainer(types.callExpression(types.identifier('useMemo'), [
896
1164
  types.arrowFunctionExpression([], types.objectExpression(paramsProps)),
897
1165
  types.arrayExpression(memoDeps),
898
1166
  ]))));
899
- // Add initialData
900
- 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')))));
901
- // Add key
1167
+ // Add initialData. See paginated+search updater for why we skip prefetch
1168
+ // reuse when a dynamic state-bound sort is active.
1169
+ if (!usage.dynamicSort) {
1170
+ 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')))));
1171
+ }
1172
+ // Add key — sort is intentionally NOT included; see paginated+search updater.
902
1173
  dp.openingElement.attributes.push(types.jsxAttribute(types.jsxIdentifier('key'), types.jsxExpressionContainer(types.templateLiteral([
903
1174
  types.templateElement({
904
1175
  raw: "".concat(usage.dataSourceIdentifier, "-page-"),
@@ -923,41 +1194,28 @@ function updateDataProviderForSearchOnly(dp, usage, vars, fileName) {
923
1194
  if (usage.queryColumns.length > 0) {
924
1195
  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); }))])));
925
1196
  }
926
- // Add sorts if present
927
- if (usage.sorts && usage.sorts.length > 0) {
928
- paramsProps.push(types.objectProperty(types.identifier('sorts'), types.callExpression(types.memberExpression(types.identifier('JSON'), types.identifier('stringify')), [
929
- types.arrayExpression(usage.sorts.map(function (sort) {
930
- return types.objectExpression([
931
- types.objectProperty(types.identifier('field'), types.stringLiteral(sort.field || '')),
932
- types.objectProperty(types.identifier('order'), types.stringLiteral(sort.order || '')),
933
- ]);
934
- })),
935
- ])));
936
- }
1197
+ // Add sorts if present (legacy static array wins; otherwise dynamic state-bound sort)
1198
+ (0, sort_utils_1.appendSortsParam)(paramsProps, usage.sorts, usage.dynamicSort);
937
1199
  // Add filters if present
938
- if (usage.filters && usage.filters.length > 0) {
939
- paramsProps.push(types.objectProperty(types.identifier('filters'), types.callExpression(types.memberExpression(types.identifier('JSON'), types.identifier('stringify')), [
940
- types.arrayExpression(usage.filters.map(function (filter) {
941
- return types.objectExpression([
942
- types.objectProperty(types.identifier('source'), types.stringLiteral(filter.source || '')),
943
- types.objectProperty(types.identifier('destination'), teleport_plugin_common_1.ASTUtils.convertFilterDestinationToExpression(filter.destination)),
944
- types.objectProperty(types.identifier('operand'), types.stringLiteral(filter.operand || '')),
945
- ]);
946
- })),
947
- ])));
948
- }
949
- // Build useMemo dependencies including filter state IDs
1200
+ (0, filter_utils_1.appendFiltersParam)(paramsProps, usage.filters, buildFilterDestinationExpression);
1201
+ // Build useMemo dependencies including filter state IDs and dynamic sort state IDs
950
1202
  var memoDeps = [types.identifier(vars.debouncedSearchQueryVar)];
951
- usage.filterStateIds.forEach(function (stateId) {
952
- memoDeps.push(types.identifier(stateId));
953
- });
1203
+ var seenDeps = new Set([vars.debouncedSearchQueryVar]);
1204
+ (0, filter_utils_1.pushStateIdsAsDeps)(memoDeps, seenDeps, usage.filterStateIds);
1205
+ if (usage.dynamicSort) {
1206
+ (0, filter_utils_1.pushStateIdsAsDeps)(memoDeps, seenDeps, usage.dynamicSort.depStateIds);
1207
+ }
1208
+ pushUrlSearchParamMemoDeps(memoDeps, usage);
954
1209
  dp.openingElement.attributes.push(types.jsxAttribute(types.jsxIdentifier('params'), types.jsxExpressionContainer(types.callExpression(types.identifier('useMemo'), [
955
1210
  types.arrowFunctionExpression([], types.objectExpression(paramsProps)),
956
1211
  types.arrayExpression(memoDeps),
957
1212
  ]))));
958
- // Add initialData
959
- 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')))));
960
- // Add key
1213
+ // Add initialData. See paginated+search updater for why we skip prefetch
1214
+ // reuse when a dynamic state-bound sort is active.
1215
+ if (!usage.dynamicSort) {
1216
+ 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')))));
1217
+ }
1218
+ // Add key — sort is intentionally NOT included; see paginated+search updater.
961
1219
  dp.openingElement.attributes.push(types.jsxAttribute(types.jsxIdentifier('key'), types.jsxExpressionContainer(types.templateLiteral([
962
1220
  types.templateElement({ raw: 'search-', cooked: 'search-' }),
963
1221
  types.templateElement({ raw: '', cooked: '' }),
@@ -1003,9 +1261,8 @@ function updateDataProviderForPlain(dp, fileName, usage) {
1003
1261
  }
1004
1262
  // Build useMemo dependencies including filter state IDs
1005
1263
  var memoDeps = [];
1006
- usage.filterStateIds.forEach(function (stateId) {
1007
- memoDeps.push(types.identifier(stateId));
1008
- });
1264
+ (0, filter_utils_1.pushStateIdsAsDeps)(memoDeps, new Set(), usage.filterStateIds);
1265
+ pushUrlSearchParamMemoDeps(memoDeps, usage);
1009
1266
  // Wrap params in useMemo with filter state dependencies
1010
1267
  var memoizedParams = types.callExpression(types.identifier('useMemo'), [
1011
1268
  types.arrowFunctionExpression([], paramsExpression),
@@ -1225,7 +1482,7 @@ function ensureAPIRouteExists(extractedResources, usage, dataSources) {
1225
1482
  };
1226
1483
  }
1227
1484
  }
1228
- function updateGetStaticProps(chunks, registry, dependencies) {
1485
+ function updateGetStaticProps(chunks, registry, dependencies, folderPath) {
1229
1486
  var _a;
1230
1487
  var getStaticPropsChunk = chunks.find(function (c) { return c.name === 'getStaticProps'; });
1231
1488
  if (!getStaticPropsChunk || getStaticPropsChunk.type !== teleport_types_1.ChunkType.AST) {
@@ -1335,9 +1592,11 @@ function updateGetStaticProps(chunks, registry, dependencies) {
1335
1592
  ]));
1336
1593
  // Add import dependency for the fetcher
1337
1594
  if (!dependencies[fetcherImportName]) {
1595
+ var depth = (folderPath ? folderPath.length : 0) + 1;
1596
+ var relativePrefix = '../'.repeat(depth);
1338
1597
  dependencies[fetcherImportName] = {
1339
1598
  type: 'local',
1340
- path: "../utils/data-sources/".concat(fileName),
1599
+ path: "".concat(relativePrefix, "utils/data-sources/").concat(fileName),
1341
1600
  };
1342
1601
  }
1343
1602
  // Add to props
@@ -1448,9 +1707,11 @@ function updateGetStaticProps(chunks, registry, dependencies) {
1448
1707
  }
1449
1708
  // Add import dependency for the fetcher
1450
1709
  if (!dependencies[fetcherImportName]) {
1710
+ var depth = (folderPath ? folderPath.length : 0) + 1;
1711
+ var relativePrefix = '../'.repeat(depth);
1451
1712
  dependencies[fetcherImportName] = {
1452
1713
  type: 'local',
1453
- path: "../utils/data-sources/".concat(fileName),
1714
+ path: "".concat(relativePrefix, "utils/data-sources/").concat(fileName),
1454
1715
  };
1455
1716
  }
1456
1717
  }