@teleporthq/teleport-plugin-next-data-source 0.43.66 → 0.43.68

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 (334) hide show
  1. package/__tests__/array-mapper-cache-wiring.test.ts +11 -0
  2. package/__tests__/array-overlap-sql.test.ts +53 -0
  3. package/__tests__/blog-post-page-enrichment.test.ts +214 -0
  4. package/__tests__/browser-row-policy.test.ts +503 -0
  5. package/__tests__/data-source-cache-wrapping.test.ts +18 -2
  6. package/__tests__/data-source-request-locale.test.ts +313 -0
  7. package/__tests__/ecommerce-product-options.test.ts +206 -0
  8. package/__tests__/ecommerce-product-pages.test.ts +82 -0
  9. package/__tests__/ecommerce-product-pre-options.golden.json +505 -0
  10. package/__tests__/ecommerce-product-storefront-contract.test.ts +293 -0
  11. package/__tests__/ecommerce-product-subscriptions.test.ts +121 -0
  12. package/__tests__/fetchers.test.ts +2 -2
  13. package/__tests__/gated-data-provider.test.ts +88 -0
  14. package/__tests__/items-path.test.ts +150 -0
  15. package/__tests__/localized-columns.test.ts +217 -0
  16. package/__tests__/numbered-pagination.test.ts +15 -0
  17. package/__tests__/product-filter-fields.ts +230 -0
  18. package/__tests__/product-filter-sql.fixture.json +7 -0
  19. package/__tests__/product-options-storefront.fixture.json +2510 -0
  20. package/__tests__/product-page-url-fixtures.json +25 -0
  21. package/__tests__/product-price-sort.ts +4 -2
  22. package/__tests__/raw-rows-mode.test.ts +111 -0
  23. package/__tests__/refetch-loading-state.test.ts +23 -0
  24. package/__tests__/rest-api-response-cache.test.ts +434 -0
  25. package/__tests__/search-clear-button.test.ts +122 -0
  26. package/__tests__/table-read-route-protected-tables.test.ts +493 -0
  27. package/dist/cjs/array-overlap-sql.d.ts +21 -0
  28. package/dist/cjs/array-overlap-sql.d.ts.map +1 -0
  29. package/dist/cjs/array-overlap-sql.js +25 -0
  30. package/dist/cjs/array-overlap-sql.js.map +1 -0
  31. package/dist/cjs/content-localization.d.ts +37 -0
  32. package/dist/cjs/content-localization.d.ts.map +1 -0
  33. package/dist/cjs/content-localization.js +29 -0
  34. package/dist/cjs/content-localization.js.map +1 -0
  35. package/dist/cjs/count-effect.d.ts +11 -1
  36. package/dist/cjs/count-effect.d.ts.map +1 -1
  37. package/dist/cjs/count-effect.js +30 -2
  38. package/dist/cjs/count-effect.js.map +1 -1
  39. package/dist/cjs/data-source-fetchers.d.ts.map +1 -1
  40. package/dist/cjs/data-source-fetchers.js +10 -4
  41. package/dist/cjs/data-source-fetchers.js.map +1 -1
  42. package/dist/cjs/fetchers/airtable.d.ts.map +1 -1
  43. package/dist/cjs/fetchers/airtable.js +1 -1
  44. package/dist/cjs/fetchers/airtable.js.map +1 -1
  45. package/dist/cjs/fetchers/clickhouse.d.ts.map +1 -1
  46. package/dist/cjs/fetchers/clickhouse.js +1 -1
  47. package/dist/cjs/fetchers/clickhouse.js.map +1 -1
  48. package/dist/cjs/fetchers/firestore.d.ts.map +1 -1
  49. package/dist/cjs/fetchers/firestore.js +1 -1
  50. package/dist/cjs/fetchers/firestore.js.map +1 -1
  51. package/dist/cjs/fetchers/javascript.d.ts.map +1 -1
  52. package/dist/cjs/fetchers/javascript.js +1 -1
  53. package/dist/cjs/fetchers/javascript.js.map +1 -1
  54. package/dist/cjs/fetchers/mariadb.d.ts.map +1 -1
  55. package/dist/cjs/fetchers/mariadb.js +1 -1
  56. package/dist/cjs/fetchers/mariadb.js.map +1 -1
  57. package/dist/cjs/fetchers/mongodb.d.ts.map +1 -1
  58. package/dist/cjs/fetchers/mongodb.js +1 -1
  59. package/dist/cjs/fetchers/mongodb.js.map +1 -1
  60. package/dist/cjs/fetchers/mysql.d.ts.map +1 -1
  61. package/dist/cjs/fetchers/mysql.js +1 -1
  62. package/dist/cjs/fetchers/mysql.js.map +1 -1
  63. package/dist/cjs/fetchers/postgresql.d.ts +1 -1
  64. package/dist/cjs/fetchers/postgresql.d.ts.map +1 -1
  65. package/dist/cjs/fetchers/postgresql.js +9 -4
  66. package/dist/cjs/fetchers/postgresql.js.map +1 -1
  67. package/dist/cjs/fetchers/raw-query.d.ts +8 -1
  68. package/dist/cjs/fetchers/raw-query.d.ts.map +1 -1
  69. package/dist/cjs/fetchers/raw-query.js +42 -4
  70. package/dist/cjs/fetchers/raw-query.js.map +1 -1
  71. package/dist/cjs/fetchers/redshift.d.ts.map +1 -1
  72. package/dist/cjs/fetchers/redshift.js +1 -1
  73. package/dist/cjs/fetchers/redshift.js.map +1 -1
  74. package/dist/cjs/fetchers/rest-api.d.ts.map +1 -1
  75. package/dist/cjs/fetchers/rest-api.js +7 -2
  76. package/dist/cjs/fetchers/rest-api.js.map +1 -1
  77. package/dist/cjs/fetchers/teleport.d.ts.map +1 -1
  78. package/dist/cjs/fetchers/teleport.js +16 -6
  79. package/dist/cjs/fetchers/teleport.js.map +1 -1
  80. package/dist/cjs/fetchers/turso.d.ts.map +1 -1
  81. package/dist/cjs/fetchers/turso.js +1 -1
  82. package/dist/cjs/fetchers/turso.js.map +1 -1
  83. package/dist/cjs/fetchers/utils/browser-row-policy.d.ts +43 -0
  84. package/dist/cjs/fetchers/utils/browser-row-policy.d.ts.map +1 -0
  85. package/dist/cjs/fetchers/utils/browser-row-policy.js +66 -0
  86. package/dist/cjs/fetchers/utils/browser-row-policy.js.map +1 -0
  87. package/dist/cjs/fetchers/utils/request-sql-guards.d.ts +8 -0
  88. package/dist/cjs/fetchers/utils/request-sql-guards.d.ts.map +1 -0
  89. package/dist/cjs/fetchers/utils/request-sql-guards.js +12 -0
  90. package/dist/cjs/fetchers/utils/request-sql-guards.js.map +1 -0
  91. package/dist/cjs/fetchers/utils/rest-api-response-cache.d.ts +40 -0
  92. package/dist/cjs/fetchers/utils/rest-api-response-cache.d.ts.map +1 -0
  93. package/dist/cjs/fetchers/utils/rest-api-response-cache.js +64 -0
  94. package/dist/cjs/fetchers/utils/rest-api-response-cache.js.map +1 -0
  95. package/dist/cjs/fetchers/utils/table-access-guard.d.ts +23 -0
  96. package/dist/cjs/fetchers/utils/table-access-guard.d.ts.map +1 -0
  97. package/dist/cjs/fetchers/utils/table-access-guard.js +34 -0
  98. package/dist/cjs/fetchers/utils/table-access-guard.js.map +1 -0
  99. package/dist/cjs/index.d.ts +5 -0
  100. package/dist/cjs/index.d.ts.map +1 -1
  101. package/dist/cjs/index.js +60 -8
  102. package/dist/cjs/index.js.map +1 -1
  103. package/dist/cjs/items-path.d.ts +10 -0
  104. package/dist/cjs/items-path.d.ts.map +1 -0
  105. package/dist/cjs/items-path.js +86 -0
  106. package/dist/cjs/items-path.js.map +1 -0
  107. package/dist/cjs/loading-state.d.ts +16 -1
  108. package/dist/cjs/loading-state.d.ts.map +1 -1
  109. package/dist/cjs/loading-state.js +69 -3
  110. package/dist/cjs/loading-state.js.map +1 -1
  111. package/dist/cjs/localized-columns.d.ts +56 -0
  112. package/dist/cjs/localized-columns.d.ts.map +1 -0
  113. package/dist/cjs/localized-columns.js +137 -0
  114. package/dist/cjs/localized-columns.js.map +1 -0
  115. package/dist/cjs/pagination-plugin.d.ts.map +1 -1
  116. package/dist/cjs/pagination-plugin.js +140 -50
  117. package/dist/cjs/pagination-plugin.js.map +1 -1
  118. package/dist/cjs/product-filter-fields.d.ts +87 -0
  119. package/dist/cjs/product-filter-fields.d.ts.map +1 -0
  120. package/dist/cjs/product-filter-fields.js +161 -0
  121. package/dist/cjs/product-filter-fields.js.map +1 -0
  122. package/dist/cjs/request-locale.d.ts +49 -0
  123. package/dist/cjs/request-locale.d.ts.map +1 -0
  124. package/dist/cjs/request-locale.js +130 -0
  125. package/dist/cjs/request-locale.js.map +1 -0
  126. package/dist/cjs/search-clear.d.ts +31 -0
  127. package/dist/cjs/search-clear.d.ts.map +1 -0
  128. package/dist/cjs/search-clear.js +103 -0
  129. package/dist/cjs/search-clear.js.map +1 -0
  130. package/dist/cjs/transformations/blog-adjacent-posts.d.ts +16 -0
  131. package/dist/cjs/transformations/blog-adjacent-posts.d.ts.map +1 -0
  132. package/dist/cjs/transformations/blog-adjacent-posts.js +25 -0
  133. package/dist/cjs/transformations/blog-adjacent-posts.js.map +1 -0
  134. package/dist/cjs/transformations/blog-comments.d.ts +30 -0
  135. package/dist/cjs/transformations/blog-comments.d.ts.map +1 -0
  136. package/dist/cjs/transformations/blog-comments.js +34 -0
  137. package/dist/cjs/transformations/blog-comments.js.map +1 -0
  138. package/dist/cjs/transformations/blog-post.d.ts +2 -0
  139. package/dist/cjs/transformations/blog-post.d.ts.map +1 -1
  140. package/dist/cjs/transformations/blog-post.js +4 -1
  141. package/dist/cjs/transformations/blog-post.js.map +1 -1
  142. package/dist/cjs/transformations/ecommerce-product.d.ts +9 -1
  143. package/dist/cjs/transformations/ecommerce-product.d.ts.map +1 -1
  144. package/dist/cjs/transformations/ecommerce-product.js +28 -1
  145. package/dist/cjs/transformations/ecommerce-product.js.map +1 -1
  146. package/dist/cjs/transformations/heading-anchors.d.ts +25 -0
  147. package/dist/cjs/transformations/heading-anchors.d.ts.map +1 -0
  148. package/dist/cjs/transformations/heading-anchors.js +29 -0
  149. package/dist/cjs/transformations/heading-anchors.js.map +1 -0
  150. package/dist/cjs/transformations/index.d.ts +61 -4
  151. package/dist/cjs/transformations/index.d.ts.map +1 -1
  152. package/dist/cjs/transformations/index.js +69 -19
  153. package/dist/cjs/transformations/index.js.map +1 -1
  154. package/dist/cjs/transformations/shared-utils.d.ts.map +1 -1
  155. package/dist/cjs/transformations/shared-utils.js +1 -1
  156. package/dist/cjs/transformations/shared-utils.js.map +1 -1
  157. package/dist/cjs/tsconfig.tsbuildinfo +1 -1
  158. package/dist/cjs/utils.d.ts +11 -1
  159. package/dist/cjs/utils.d.ts.map +1 -1
  160. package/dist/cjs/utils.js +60 -16
  161. package/dist/cjs/utils.js.map +1 -1
  162. package/dist/esm/array-overlap-sql.d.ts +21 -0
  163. package/dist/esm/array-overlap-sql.d.ts.map +1 -0
  164. package/dist/esm/array-overlap-sql.js +21 -0
  165. package/dist/esm/array-overlap-sql.js.map +1 -0
  166. package/dist/esm/content-localization.d.ts +37 -0
  167. package/dist/esm/content-localization.d.ts.map +1 -0
  168. package/dist/esm/content-localization.js +24 -0
  169. package/dist/esm/content-localization.js.map +1 -0
  170. package/dist/esm/count-effect.d.ts +11 -1
  171. package/dist/esm/count-effect.d.ts.map +1 -1
  172. package/dist/esm/count-effect.js +28 -1
  173. package/dist/esm/count-effect.js.map +1 -1
  174. package/dist/esm/data-source-fetchers.d.ts.map +1 -1
  175. package/dist/esm/data-source-fetchers.js +11 -5
  176. package/dist/esm/data-source-fetchers.js.map +1 -1
  177. package/dist/esm/fetchers/airtable.d.ts.map +1 -1
  178. package/dist/esm/fetchers/airtable.js +1 -1
  179. package/dist/esm/fetchers/airtable.js.map +1 -1
  180. package/dist/esm/fetchers/clickhouse.d.ts.map +1 -1
  181. package/dist/esm/fetchers/clickhouse.js +1 -1
  182. package/dist/esm/fetchers/clickhouse.js.map +1 -1
  183. package/dist/esm/fetchers/firestore.d.ts.map +1 -1
  184. package/dist/esm/fetchers/firestore.js +1 -1
  185. package/dist/esm/fetchers/firestore.js.map +1 -1
  186. package/dist/esm/fetchers/javascript.d.ts.map +1 -1
  187. package/dist/esm/fetchers/javascript.js +1 -1
  188. package/dist/esm/fetchers/javascript.js.map +1 -1
  189. package/dist/esm/fetchers/mariadb.d.ts.map +1 -1
  190. package/dist/esm/fetchers/mariadb.js +1 -1
  191. package/dist/esm/fetchers/mariadb.js.map +1 -1
  192. package/dist/esm/fetchers/mongodb.d.ts.map +1 -1
  193. package/dist/esm/fetchers/mongodb.js +1 -1
  194. package/dist/esm/fetchers/mongodb.js.map +1 -1
  195. package/dist/esm/fetchers/mysql.d.ts.map +1 -1
  196. package/dist/esm/fetchers/mysql.js +1 -1
  197. package/dist/esm/fetchers/mysql.js.map +1 -1
  198. package/dist/esm/fetchers/postgresql.d.ts +1 -1
  199. package/dist/esm/fetchers/postgresql.d.ts.map +1 -1
  200. package/dist/esm/fetchers/postgresql.js +9 -4
  201. package/dist/esm/fetchers/postgresql.js.map +1 -1
  202. package/dist/esm/fetchers/raw-query.d.ts +8 -1
  203. package/dist/esm/fetchers/raw-query.d.ts.map +1 -1
  204. package/dist/esm/fetchers/raw-query.js +42 -4
  205. package/dist/esm/fetchers/raw-query.js.map +1 -1
  206. package/dist/esm/fetchers/redshift.d.ts.map +1 -1
  207. package/dist/esm/fetchers/redshift.js +1 -1
  208. package/dist/esm/fetchers/redshift.js.map +1 -1
  209. package/dist/esm/fetchers/rest-api.d.ts.map +1 -1
  210. package/dist/esm/fetchers/rest-api.js +7 -2
  211. package/dist/esm/fetchers/rest-api.js.map +1 -1
  212. package/dist/esm/fetchers/teleport.d.ts.map +1 -1
  213. package/dist/esm/fetchers/teleport.js +16 -6
  214. package/dist/esm/fetchers/teleport.js.map +1 -1
  215. package/dist/esm/fetchers/turso.d.ts.map +1 -1
  216. package/dist/esm/fetchers/turso.js +1 -1
  217. package/dist/esm/fetchers/turso.js.map +1 -1
  218. package/dist/esm/fetchers/utils/browser-row-policy.d.ts +43 -0
  219. package/dist/esm/fetchers/utils/browser-row-policy.d.ts.map +1 -0
  220. package/dist/esm/fetchers/utils/browser-row-policy.js +60 -0
  221. package/dist/esm/fetchers/utils/browser-row-policy.js.map +1 -0
  222. package/dist/esm/fetchers/utils/request-sql-guards.d.ts +8 -0
  223. package/dist/esm/fetchers/utils/request-sql-guards.d.ts.map +1 -0
  224. package/dist/esm/fetchers/utils/request-sql-guards.js +8 -0
  225. package/dist/esm/fetchers/utils/request-sql-guards.js.map +1 -0
  226. package/dist/esm/fetchers/utils/rest-api-response-cache.d.ts +40 -0
  227. package/dist/esm/fetchers/utils/rest-api-response-cache.d.ts.map +1 -0
  228. package/dist/esm/fetchers/utils/rest-api-response-cache.js +59 -0
  229. package/dist/esm/fetchers/utils/rest-api-response-cache.js.map +1 -0
  230. package/dist/esm/fetchers/utils/table-access-guard.d.ts +23 -0
  231. package/dist/esm/fetchers/utils/table-access-guard.d.ts.map +1 -0
  232. package/dist/esm/fetchers/utils/table-access-guard.js +29 -0
  233. package/dist/esm/fetchers/utils/table-access-guard.js.map +1 -0
  234. package/dist/esm/index.d.ts +5 -0
  235. package/dist/esm/index.d.ts.map +1 -1
  236. package/dist/esm/index.js +55 -7
  237. package/dist/esm/index.js.map +1 -1
  238. package/dist/esm/items-path.d.ts +10 -0
  239. package/dist/esm/items-path.d.ts.map +1 -0
  240. package/dist/esm/items-path.js +58 -0
  241. package/dist/esm/items-path.js.map +1 -0
  242. package/dist/esm/loading-state.d.ts +16 -1
  243. package/dist/esm/loading-state.d.ts.map +1 -1
  244. package/dist/esm/loading-state.js +69 -3
  245. package/dist/esm/loading-state.js.map +1 -1
  246. package/dist/esm/localized-columns.d.ts +56 -0
  247. package/dist/esm/localized-columns.d.ts.map +1 -0
  248. package/dist/esm/localized-columns.js +132 -0
  249. package/dist/esm/localized-columns.js.map +1 -0
  250. package/dist/esm/pagination-plugin.d.ts.map +1 -1
  251. package/dist/esm/pagination-plugin.js +141 -51
  252. package/dist/esm/pagination-plugin.js.map +1 -1
  253. package/dist/esm/product-filter-fields.d.ts +87 -0
  254. package/dist/esm/product-filter-fields.d.ts.map +1 -0
  255. package/dist/esm/product-filter-fields.js +154 -0
  256. package/dist/esm/product-filter-fields.js.map +1 -0
  257. package/dist/esm/request-locale.d.ts +49 -0
  258. package/dist/esm/request-locale.d.ts.map +1 -0
  259. package/dist/esm/request-locale.js +98 -0
  260. package/dist/esm/request-locale.js.map +1 -0
  261. package/dist/esm/search-clear.d.ts +31 -0
  262. package/dist/esm/search-clear.d.ts.map +1 -0
  263. package/dist/esm/search-clear.js +74 -0
  264. package/dist/esm/search-clear.js.map +1 -0
  265. package/dist/esm/transformations/blog-adjacent-posts.d.ts +16 -0
  266. package/dist/esm/transformations/blog-adjacent-posts.d.ts.map +1 -0
  267. package/dist/esm/transformations/blog-adjacent-posts.js +21 -0
  268. package/dist/esm/transformations/blog-adjacent-posts.js.map +1 -0
  269. package/dist/esm/transformations/blog-comments.d.ts +30 -0
  270. package/dist/esm/transformations/blog-comments.d.ts.map +1 -0
  271. package/dist/esm/transformations/blog-comments.js +30 -0
  272. package/dist/esm/transformations/blog-comments.js.map +1 -0
  273. package/dist/esm/transformations/blog-post.d.ts +2 -0
  274. package/dist/esm/transformations/blog-post.d.ts.map +1 -1
  275. package/dist/esm/transformations/blog-post.js +4 -1
  276. package/dist/esm/transformations/blog-post.js.map +1 -1
  277. package/dist/esm/transformations/ecommerce-product.d.ts +9 -1
  278. package/dist/esm/transformations/ecommerce-product.d.ts.map +1 -1
  279. package/dist/esm/transformations/ecommerce-product.js +29 -2
  280. package/dist/esm/transformations/ecommerce-product.js.map +1 -1
  281. package/dist/esm/transformations/heading-anchors.d.ts +25 -0
  282. package/dist/esm/transformations/heading-anchors.d.ts.map +1 -0
  283. package/dist/esm/transformations/heading-anchors.js +25 -0
  284. package/dist/esm/transformations/heading-anchors.js.map +1 -0
  285. package/dist/esm/transformations/index.d.ts +61 -4
  286. package/dist/esm/transformations/index.d.ts.map +1 -1
  287. package/dist/esm/transformations/index.js +69 -19
  288. package/dist/esm/transformations/index.js.map +1 -1
  289. package/dist/esm/transformations/shared-utils.d.ts.map +1 -1
  290. package/dist/esm/transformations/shared-utils.js +1 -1
  291. package/dist/esm/transformations/shared-utils.js.map +1 -1
  292. package/dist/esm/tsconfig.tsbuildinfo +1 -1
  293. package/dist/esm/utils.d.ts +11 -1
  294. package/dist/esm/utils.d.ts.map +1 -1
  295. package/dist/esm/utils.js +60 -16
  296. package/dist/esm/utils.js.map +1 -1
  297. package/package.json +5 -5
  298. package/src/array-overlap-sql.ts +41 -0
  299. package/src/content-localization.ts +53 -0
  300. package/src/count-effect.ts +53 -2
  301. package/src/data-source-fetchers.ts +41 -5
  302. package/src/fetchers/airtable.ts +4 -2
  303. package/src/fetchers/clickhouse.ts +4 -2
  304. package/src/fetchers/firestore.ts +4 -2
  305. package/src/fetchers/javascript.ts +9 -1
  306. package/src/fetchers/mariadb.ts +4 -2
  307. package/src/fetchers/mongodb.ts +4 -2
  308. package/src/fetchers/mysql.ts +4 -2
  309. package/src/fetchers/postgresql.ts +53 -12
  310. package/src/fetchers/raw-query.ts +60 -5
  311. package/src/fetchers/redshift.ts +4 -2
  312. package/src/fetchers/rest-api.ts +27 -5
  313. package/src/fetchers/teleport.ts +85 -22
  314. package/src/fetchers/turso.ts +4 -2
  315. package/src/fetchers/utils/browser-row-policy.ts +212 -0
  316. package/src/fetchers/utils/request-sql-guards.ts +25 -0
  317. package/src/fetchers/utils/rest-api-response-cache.ts +188 -0
  318. package/src/fetchers/utils/table-access-guard.ts +39 -0
  319. package/src/index.ts +79 -11
  320. package/src/items-path.ts +74 -0
  321. package/src/loading-state.ts +134 -3
  322. package/src/localized-columns.ts +146 -0
  323. package/src/pagination-plugin.ts +206 -60
  324. package/src/product-filter-fields.ts +173 -0
  325. package/src/request-locale.ts +113 -0
  326. package/src/search-clear.ts +102 -0
  327. package/src/transformations/blog-adjacent-posts.ts +74 -0
  328. package/src/transformations/blog-comments.ts +132 -0
  329. package/src/transformations/blog-post.ts +38 -5
  330. package/src/transformations/ecommerce-product.ts +568 -5
  331. package/src/transformations/heading-anchors.ts +85 -0
  332. package/src/transformations/index.ts +191 -23
  333. package/src/transformations/shared-utils.ts +6 -0
  334. package/src/utils.ts +109 -19
@@ -13,6 +13,7 @@ import { generateDataSourceFetcherWithCore } from './data-source-fetchers'
13
13
  import { buildProductTransformOptions, type EntityTransformOptions } from './transformations'
14
14
  import { appendSortsParam, DynamicSortAST, extractDynamicSort } from './sort-utils'
15
15
  import { appendFiltersParam, pushStateIdsAsDeps, pushPropIdsAsDeps } from './filter-utils'
16
+ import { appendItemsPathParam, extractItemsPath } from './items-path'
16
17
  import {
17
18
  applyLoadingStateToDataProvider,
18
19
  buildLoadingStateDeclarations,
@@ -34,7 +35,12 @@ import {
34
35
  setJSXExpressionAttribute,
35
36
  type PaginationControlKind,
36
37
  } from './pagination-controls'
37
- import { buildCountFetchEffect } from './count-effect'
38
+ import { buildCountFetchEffect, buildCountSeqDeclaration } from './count-effect'
39
+ import {
40
+ findAllSearchClearButtonsInJSX,
41
+ findSiblingSearchInput,
42
+ wireSearchClearButton,
43
+ } from './search-clear'
38
44
  import {
39
45
  buildPageTokensDeclaration,
40
46
  wireNumberedPagination,
@@ -54,6 +60,14 @@ import {
54
60
  type PageResetDeps,
55
61
  } from './page-reset'
56
62
  import { buildPageUrlRefDeclaration, buildPageUrlSyncEffect } from './page-url-sync'
63
+ import { isLocalizedProject } from './content-localization'
64
+ import {
65
+ appendClientLocaleParam,
66
+ buildClientLocaleExpression,
67
+ buildServerLocaleParam,
68
+ ensureRouterDeclaration,
69
+ hasLocaleParam,
70
+ } from './request-locale'
57
71
 
58
72
  // ----- searchDefaultValue support -----
59
73
  //
@@ -242,6 +256,9 @@ interface DataSourceUsage {
242
256
  // `URLSearchParamSync` builders. Page 1 is written as the ABSENCE of the key.
243
257
  // Cleared when `infiniteScroll` is set.
244
258
  pageUrlParamKey?: string
259
+ // Path to the rows inside a wrapped REST/JavaScript payload, when the list is
260
+ // bound to one (`data?.templates` -> `['templates']`). See `items-path.ts`.
261
+ itemsPath?: string[]
245
262
  // Computed category
246
263
  category: 'paginated+search' | 'paginated-only' | 'search-only' | 'plain'
247
264
  }
@@ -643,6 +660,11 @@ function buildStateRegistry(uidlNode: any): StateRegistry {
643
660
  infiniteScroll,
644
661
  infiniteScrollLoadMore: infiniteScroll && !!content.infiniteScrollLoadMore,
645
662
  pageUrlParamKey,
663
+ itemsPath: extractItemsPath(
664
+ content.source,
665
+ parentDataSource.identifier,
666
+ parentDataSource.resourceDef.dataSourceType || ''
667
+ ),
646
668
  category: 'plain',
647
669
  }
648
670
 
@@ -723,6 +745,8 @@ function getStateVarsForUsage(usage: DataSourceUsage): {
723
745
  combinedStateVar: string
724
746
  setCombinedStateVar: string
725
747
  skipDebounceRefVar: string
748
+ /** Ref numbering the count requests, so an overtaken count response is dropped. */
749
+ countSeqRefVar: string
726
750
  propsPrefix: string
727
751
  } {
728
752
  const idx = usage.index
@@ -739,6 +763,7 @@ function getStateVarsForUsage(usage: DataSourceUsage): {
739
763
  combinedStateVar: `ds_${idx}_state`,
740
764
  setCombinedStateVar: `setDs_${idx}_state`,
741
765
  skipDebounceRefVar: `ds_${idx}_skipDebounce`,
766
+ countSeqRefVar: `ds_${idx}_countSeq`,
742
767
  propsPrefix: `${usage.dataSourceIdentifier}_ds_${idx}`,
743
768
  }
744
769
  }
@@ -1110,6 +1135,10 @@ export const createNextArrayMapperPaginationPlugin: ComponentPluginFactory<{}> =
1110
1135
  const getStaticPropsChunk = chunks.find((chunk) => chunk.name === 'getStaticProps')
1111
1136
  const isPage = !!getStaticPropsChunk
1112
1137
 
1138
+ // In a multi-language project every fetch says which language it is for —
1139
+ // see `request-locale.ts`. Decided once for the whole component.
1140
+ const localized = isLocalizedProject(options)
1141
+
1113
1142
  // Add React dependencies
1114
1143
  if (!dependencies.useState) {
1115
1144
  dependencies.useState = {
@@ -1390,6 +1419,7 @@ export const createNextArrayMapperPaginationPlugin: ComponentPluginFactory<{}> =
1390
1419
  }
1391
1420
  // Add filters to count fetch params if present
1392
1421
  appendFiltersParam(urlParams, usage.filters, buildFilterDestinationExpression)
1422
+ appendItemsPathParam(urlParams, usage.itemsPath, 'collectionPath')
1393
1423
 
1394
1424
  const countEffectDeps: types.Expression[] = [
1395
1425
  types.memberExpression(
@@ -1408,6 +1438,7 @@ export const createNextArrayMapperPaginationPlugin: ComponentPluginFactory<{}> =
1408
1438
  pushPropIdsAsDeps(countEffectDeps, countEffectSeen, usage.filterPropIds)
1409
1439
  // Same goes for URL-driven filters (already documented above).
1410
1440
  pushUrlSearchParamMemoDeps(countEffectDeps, usage)
1441
+ stateDeclarations.push(buildCountSeqDeclaration(vars.countSeqRefVar))
1411
1442
  effectStatements.push(
1412
1443
  buildCountFetchEffect({
1413
1444
  fileName,
@@ -1415,6 +1446,7 @@ export const createNextArrayMapperPaginationPlugin: ComponentPluginFactory<{}> =
1415
1446
  setMaxPagesVar: vars.setMaxPagesStateVar,
1416
1447
  urlParams,
1417
1448
  deps: countEffectDeps,
1449
+ countSeqRefVar: vars.countSeqRefVar,
1418
1450
  })
1419
1451
  )
1420
1452
 
@@ -1504,6 +1536,7 @@ export const createNextArrayMapperPaginationPlugin: ComponentPluginFactory<{}> =
1504
1536
  )
1505
1537
  }
1506
1538
  appendFiltersParam(countUrlParams, usage.filters, buildFilterDestinationExpression)
1539
+ appendItemsPathParam(countUrlParams, usage.itemsPath, 'collectionPath')
1507
1540
 
1508
1541
  // Default to mount-only; refresh when ANY filter destination
1509
1542
  // changes — state-bound (e.g. `selectedCategory`) and URL-driven —
@@ -1515,6 +1548,7 @@ export const createNextArrayMapperPaginationPlugin: ComponentPluginFactory<{}> =
1515
1548
  pushPropIdsAsDeps(countDeps, countDepsSeen, usage.filterPropIds)
1516
1549
  pushUrlSearchParamMemoDeps(countDeps, usage)
1517
1550
 
1551
+ stateDeclarations.push(buildCountSeqDeclaration(vars.countSeqRefVar))
1518
1552
  effectStatements.push(
1519
1553
  buildCountFetchEffect({
1520
1554
  fileName,
@@ -1522,6 +1556,7 @@ export const createNextArrayMapperPaginationPlugin: ComponentPluginFactory<{}> =
1522
1556
  setMaxPagesVar: vars.setMaxPagesStateVar,
1523
1557
  urlParams: countUrlParams,
1524
1558
  deps: countDeps,
1559
+ countSeqRefVar: vars.countSeqRefVar,
1525
1560
  })
1526
1561
  )
1527
1562
  }
@@ -1658,43 +1693,16 @@ export const createNextArrayMapperPaginationPlugin: ComponentPluginFactory<{}> =
1658
1693
  // `router.query` / `router.isReady` and call `router.replace`. Gated on the
1659
1694
  // same predicate as the effects themselves so router is only injected when
1660
1695
  // it is actually used.
1661
- const needsUseRouter = registry.usages.some(
1662
- (u) => hasUrlSearchParamFilters(u) || usageHasSearchUrlSync(u) || usageHasPageUrlSync(u)
1663
- )
1664
- if (needsUseRouter) {
1665
- if (!dependencies.useRouter) {
1666
- // Match the shape the sibling Next.js plugins use (i18n locale mapper,
1667
- // search-params plugin) so the deduped import line is identical and
1668
- // the dependency-resolver merges instead of emitting a second one.
1669
- dependencies.useRouter = {
1670
- type: 'library',
1671
- path: 'next/router',
1672
- version: '^12.1.10',
1673
- meta: { namedImport: true },
1674
- }
1675
- }
1676
- const hasRouterDecl = blockStatement.body.some(
1677
- (statement) =>
1678
- statement.type === 'VariableDeclaration' &&
1679
- statement.declarations.some(
1680
- (decl) =>
1681
- decl.id.type === 'Identifier' &&
1682
- decl.id.name === 'router' &&
1683
- decl.init?.type === 'CallExpression' &&
1684
- decl.init.callee.type === 'Identifier' &&
1685
- decl.init.callee.name === 'useRouter'
1686
- )
1696
+ // A localized project reads `router.locale` into every wired provider's
1697
+ // params (each of them fetches client-side), so the router is needed as
1698
+ // soon as there is anything to wire.
1699
+ const needsUseRouter =
1700
+ localized ||
1701
+ registry.usages.some(
1702
+ (u) => hasUrlSearchParamFilters(u) || usageHasSearchUrlSync(u) || usageHasPageUrlSync(u)
1687
1703
  )
1688
- if (!hasRouterDecl) {
1689
- blockStatement.body.unshift(
1690
- types.variableDeclaration('const', [
1691
- types.variableDeclarator(
1692
- types.identifier('router'),
1693
- types.callExpression(types.identifier('useRouter'), [])
1694
- ),
1695
- ])
1696
- )
1697
- }
1704
+ if (needsUseRouter) {
1705
+ ensureRouterDeclaration(blockStatement.body, dependencies)
1698
1706
  }
1699
1707
 
1700
1708
  // Both the search write-back and the page ⇄ URL effect write the URL through
@@ -1766,13 +1774,20 @@ export const createNextArrayMapperPaginationPlugin: ComponentPluginFactory<{}> =
1766
1774
 
1767
1775
  // Update DataProvider based on category
1768
1776
  if (usage.category === 'paginated+search') {
1769
- updateDataProviderForPaginatedSearch(dp, usage, vars, fileName, cacheDeclarations)
1777
+ updateDataProviderForPaginatedSearch(
1778
+ dp,
1779
+ usage,
1780
+ vars,
1781
+ fileName,
1782
+ cacheDeclarations,
1783
+ localized
1784
+ )
1770
1785
  } else if (usage.category === 'paginated-only') {
1771
- updateDataProviderForPaginationOnly(dp, usage, vars, fileName, cacheDeclarations)
1786
+ updateDataProviderForPaginationOnly(dp, usage, vars, fileName, cacheDeclarations, localized)
1772
1787
  } else if (usage.category === 'search-only') {
1773
- updateDataProviderForSearchOnly(dp, usage, vars, fileName, cacheDeclarations)
1788
+ updateDataProviderForSearchOnly(dp, usage, vars, fileName, cacheDeclarations, localized)
1774
1789
  } else if (usage.category === 'plain') {
1775
- updateDataProviderForPlain(dp, fileName, usage)
1790
+ updateDataProviderForPlain(dp, fileName, usage, localized)
1776
1791
  }
1777
1792
 
1778
1793
  // Show the array mapper's loading state while a category/sort/search
@@ -1871,6 +1886,7 @@ export const createNextArrayMapperPaginationPlugin: ComponentPluginFactory<{}> =
1871
1886
  const searchEnabledUsages = registry.usages.filter((u) => u.searchEnabled)
1872
1887
 
1873
1888
  // Match by order - search input 0 -> searchEnabledUsages[0], etc.
1889
+ const usageBySearchInput = new Map<any, DataSourceUsage>()
1874
1890
  searchInputs.forEach((input, idx) => {
1875
1891
  if (idx >= searchEnabledUsages.length) {
1876
1892
  return
@@ -1879,6 +1895,19 @@ export const createNextArrayMapperPaginationPlugin: ComponentPluginFactory<{}> =
1879
1895
  const usage = searchEnabledUsages[idx]
1880
1896
  const vars = getStateVarsForUsage(usage)
1881
1897
  wireSearchInput(input.node, vars)
1898
+ usageBySearchInput.set(input.node, usage)
1899
+ })
1900
+
1901
+ // STEP 4.5: Wire the clear buttons, each to the input it shares a parent with.
1902
+ findAllSearchClearButtonsInJSX(blockStatement).forEach((button) => {
1903
+ const input = findSiblingSearchInput(
1904
+ button,
1905
+ searchInputs.map((entry) => entry.node)
1906
+ )
1907
+ const usage = input ? usageBySearchInput.get(input) : undefined
1908
+ if (usage) {
1909
+ wireSearchClearButton(button, getStateVarsForUsage(usage))
1910
+ }
1882
1911
  })
1883
1912
 
1884
1913
  // STEP 5: Wire pagination widgets
@@ -1908,7 +1937,13 @@ export const createNextArrayMapperPaginationPlugin: ComponentPluginFactory<{}> =
1908
1937
 
1909
1938
  // STEP 6: Update getStaticProps if this is a page
1910
1939
  if (isPage) {
1911
- updateGetStaticProps(chunks, registry, dependencies, uidl.outputOptions?.folderPath)
1940
+ updateGetStaticProps(
1941
+ chunks,
1942
+ registry,
1943
+ dependencies,
1944
+ uidl.outputOptions?.folderPath,
1945
+ localized
1946
+ )
1912
1947
  }
1913
1948
 
1914
1949
  return structure
@@ -1964,6 +1999,20 @@ function insertSentinelAfterProvider(
1964
1999
  if (node.alternate) {
1965
2000
  traverse(node.alternate)
1966
2001
  }
2002
+ // `{activeTabIndex === 0 && (<div>…<DataProvider/>…</div>)}` — a tab
2003
+ // panel, a gated section: the provider sits on the RIGHT of a logical
2004
+ // expression (or in a ternary's `test`), which is not a child, a body or
2005
+ // an argument. Left unvisited, the products list under the first tab of
2006
+ // a shop was never paginated or seeded, and rendered nothing.
2007
+ if (node.left) {
2008
+ traverse(node.left)
2009
+ }
2010
+ if (node.right) {
2011
+ traverse(node.right)
2012
+ }
2013
+ if (node.test) {
2014
+ traverse(node.test)
2015
+ }
1967
2016
  if (node.expression) {
1968
2017
  traverse(node.expression)
1969
2018
  }
@@ -2007,6 +2056,20 @@ function findAllDataProvidersInJSX(blockStatement: types.BlockStatement): any[]
2007
2056
  if (node.alternate) {
2008
2057
  traverse(node.alternate)
2009
2058
  }
2059
+ // `{activeTabIndex === 0 && (<div>…<DataProvider/>…</div>)}` — a tab
2060
+ // panel, a gated section: the provider sits on the RIGHT of a logical
2061
+ // expression (or in a ternary's `test`), which is not a child, a body or
2062
+ // an argument. Left unvisited, the products list under the first tab of
2063
+ // a shop was never paginated or seeded, and rendered nothing.
2064
+ if (node.left) {
2065
+ traverse(node.left)
2066
+ }
2067
+ if (node.right) {
2068
+ traverse(node.right)
2069
+ }
2070
+ if (node.test) {
2071
+ traverse(node.test)
2072
+ }
2010
2073
 
2011
2074
  if (node.expression) {
2012
2075
  traverse(node.expression)
@@ -2110,6 +2173,20 @@ function findAllSearchInputsInJSX(
2110
2173
  if (node.alternate) {
2111
2174
  traverse(node.alternate)
2112
2175
  }
2176
+ // `{activeTabIndex === 0 && (<div>…<DataProvider/>…</div>)}` — a tab
2177
+ // panel, a gated section: the provider sits on the RIGHT of a logical
2178
+ // expression (or in a ternary's `test`), which is not a child, a body or
2179
+ // an argument. Left unvisited, the products list under the first tab of
2180
+ // a shop was never paginated or seeded, and rendered nothing.
2181
+ if (node.left) {
2182
+ traverse(node.left)
2183
+ }
2184
+ if (node.right) {
2185
+ traverse(node.right)
2186
+ }
2187
+ if (node.test) {
2188
+ traverse(node.test)
2189
+ }
2113
2190
  if (node.expression) {
2114
2191
  traverse(node.expression)
2115
2192
  }
@@ -2173,6 +2250,20 @@ function findPaginationNodesWithOwners(
2173
2250
  if (node.alternate) {
2174
2251
  traverse(node.alternate)
2175
2252
  }
2253
+ // `{activeTabIndex === 0 && (<div>…<DataProvider/>…</div>)}` — a tab
2254
+ // panel, a gated section: the provider sits on the RIGHT of a logical
2255
+ // expression (or in a ternary's `test`), which is not a child, a body or
2256
+ // an argument. Left unvisited, the products list under the first tab of
2257
+ // a shop was never paginated or seeded, and rendered nothing.
2258
+ if (node.left) {
2259
+ traverse(node.left)
2260
+ }
2261
+ if (node.right) {
2262
+ traverse(node.right)
2263
+ }
2264
+ if (node.test) {
2265
+ traverse(node.test)
2266
+ }
2176
2267
  if (node.expression) {
2177
2268
  traverse(node.expression)
2178
2269
  }
@@ -2239,7 +2330,8 @@ function updateDataProviderForPaginatedSearch(
2239
2330
  usage: DataSourceUsage,
2240
2331
  vars: ReturnType<typeof getStateVarsForUsage>,
2241
2332
  fileName: string,
2242
- cacheDeclarations: types.Statement[]
2333
+ cacheDeclarations: types.Statement[],
2334
+ localized: boolean
2243
2335
  ): void {
2244
2336
  const attrs = dp.openingElement.attributes
2245
2337
 
@@ -2283,6 +2375,7 @@ function updateDataProviderForPaginatedSearch(
2283
2375
 
2284
2376
  // Add filters if present
2285
2377
  appendFiltersParam(paramsProps, usage.filters, buildFilterDestinationExpression)
2378
+ appendItemsPathParam(paramsProps, usage.itemsPath, 'itemsPath')
2286
2379
 
2287
2380
  // Build useMemo dependencies including filter state IDs and dynamic sort state IDs
2288
2381
  const memoDeps: types.Expression[] = [types.identifier(vars.combinedStateVar)]
@@ -2293,6 +2386,9 @@ function updateDataProviderForPaginatedSearch(
2293
2386
  pushStateIdsAsDeps(memoDeps, seenDeps, usage.dynamicSort.depStateIds)
2294
2387
  }
2295
2388
  pushUrlSearchParamMemoDeps(memoDeps, usage)
2389
+ if (localized) {
2390
+ appendClientLocaleParam(paramsProps, memoDeps)
2391
+ }
2296
2392
 
2297
2393
  const cachedInitialData = pushParamsAttribute(dp, usage, paramsProps, memoDeps, cacheDeclarations)
2298
2394
 
@@ -2416,7 +2512,8 @@ function updateDataProviderForPaginationOnly(
2416
2512
  usage: DataSourceUsage,
2417
2513
  vars: ReturnType<typeof getStateVarsForUsage>,
2418
2514
  fileName: string,
2419
- cacheDeclarations: types.Statement[]
2515
+ cacheDeclarations: types.Statement[],
2516
+ localized: boolean
2420
2517
  ): void {
2421
2518
  const attrs = dp.openingElement.attributes
2422
2519
 
@@ -2438,6 +2535,7 @@ function updateDataProviderForPaginationOnly(
2438
2535
 
2439
2536
  // Add filters if present
2440
2537
  appendFiltersParam(paramsProps, usage.filters, buildFilterDestinationExpression)
2538
+ appendItemsPathParam(paramsProps, usage.itemsPath, 'itemsPath')
2441
2539
 
2442
2540
  // Build useMemo dependencies including filter state IDs and dynamic sort state IDs
2443
2541
  const memoDeps: types.Expression[] = [types.identifier(vars.pageStateVar)]
@@ -2448,6 +2546,9 @@ function updateDataProviderForPaginationOnly(
2448
2546
  pushStateIdsAsDeps(memoDeps, seenDeps, usage.dynamicSort.depStateIds)
2449
2547
  }
2450
2548
  pushUrlSearchParamMemoDeps(memoDeps, usage)
2549
+ if (localized) {
2550
+ appendClientLocaleParam(paramsProps, memoDeps)
2551
+ }
2451
2552
 
2452
2553
  // Add params
2453
2554
  const cachedInitialData = pushParamsAttribute(dp, usage, paramsProps, memoDeps, cacheDeclarations)
@@ -2522,7 +2623,8 @@ function updateDataProviderForSearchOnly(
2522
2623
  usage: DataSourceUsage,
2523
2624
  vars: ReturnType<typeof getStateVarsForUsage>,
2524
2625
  fileName: string,
2525
- cacheDeclarations: types.Statement[]
2626
+ cacheDeclarations: types.Statement[],
2627
+ localized: boolean
2526
2628
  ): void {
2527
2629
  const attrs = dp.openingElement.attributes
2528
2630
 
@@ -2554,6 +2656,7 @@ function updateDataProviderForSearchOnly(
2554
2656
 
2555
2657
  // Add filters if present
2556
2658
  appendFiltersParam(paramsProps, usage.filters, buildFilterDestinationExpression)
2659
+ appendItemsPathParam(paramsProps, usage.itemsPath, 'itemsPath')
2557
2660
 
2558
2661
  // Build useMemo dependencies including filter state IDs and dynamic sort state IDs
2559
2662
  const memoDeps: types.Expression[] = [types.identifier(vars.debouncedSearchQueryVar)]
@@ -2564,6 +2667,9 @@ function updateDataProviderForSearchOnly(
2564
2667
  pushStateIdsAsDeps(memoDeps, seenDeps, usage.dynamicSort.depStateIds)
2565
2668
  }
2566
2669
  pushUrlSearchParamMemoDeps(memoDeps, usage)
2670
+ if (localized) {
2671
+ appendClientLocaleParam(paramsProps, memoDeps)
2672
+ }
2567
2673
 
2568
2674
  const cachedInitialData = pushParamsAttribute(dp, usage, paramsProps, memoDeps, cacheDeclarations)
2569
2675
 
@@ -2621,7 +2727,12 @@ function updateDataProviderForSearchOnly(
2621
2727
  )
2622
2728
  }
2623
2729
 
2624
- function updateDataProviderForPlain(dp: any, fileName: string, usage: DataSourceUsage): void {
2730
+ function updateDataProviderForPlain(
2731
+ dp: any,
2732
+ fileName: string,
2733
+ usage: DataSourceUsage,
2734
+ localized: boolean
2735
+ ): void {
2625
2736
  const attrs = dp.openingElement.attributes
2626
2737
 
2627
2738
  const clientCache = usage.cache?.client ? usage.cache : undefined
@@ -2658,13 +2769,24 @@ function updateDataProviderForPlain(dp: any, fileName: string, usage: DataSource
2658
2769
  )
2659
2770
  }
2660
2771
 
2661
- // Find the params attribute
2662
- const paramsAttrIndex = attrs.findIndex(
2772
+ // Find the params attribute. A plain list may have none at all — but the
2773
+ // fetch it was just given sends `params` regardless, and in a localized
2774
+ // project those have to carry the locale, so the attribute is created.
2775
+ let paramsAttrIndex = attrs.findIndex(
2663
2776
  (attr: any) => attr.type === 'JSXAttribute' && attr.name.name === 'params'
2664
2777
  )
2665
2778
 
2666
2779
  if (paramsAttrIndex === -1) {
2667
- return
2780
+ if (!localized) {
2781
+ return
2782
+ }
2783
+ attrs.push(
2784
+ types.jsxAttribute(
2785
+ types.jsxIdentifier('params'),
2786
+ types.jsxExpressionContainer(types.objectExpression([]))
2787
+ )
2788
+ )
2789
+ paramsAttrIndex = attrs.length - 1
2668
2790
  }
2669
2791
 
2670
2792
  const paramsAttr = attrs[paramsAttrIndex] as types.JSXAttribute
@@ -2695,6 +2817,9 @@ function updateDataProviderForPlain(dp: any, fileName: string, usage: DataSource
2695
2817
  pushStateIdsAsDeps(memoDeps, memoSeen, usage.filterStateIds)
2696
2818
  pushPropIdsAsDeps(memoDeps, memoSeen, usage.filterPropIds)
2697
2819
  pushUrlSearchParamMemoDeps(memoDeps, usage)
2820
+ if (localized && paramsExpression.type === 'ObjectExpression') {
2821
+ appendClientLocaleParam(paramsExpression.properties, memoDeps)
2822
+ }
2698
2823
 
2699
2824
  // Wrap params in useMemo with filter state dependencies
2700
2825
  const memoizedParams = types.callExpression(types.identifier('useMemo'), [
@@ -2743,11 +2868,18 @@ function stabilizeDataProviderWithoutRepeater(dp: any): void {
2743
2868
  return
2744
2869
  }
2745
2870
 
2746
- // Wrap params in useMemo with empty dependencies array
2747
- // This ensures the object reference stays stable across re-renders
2871
+ // Wrap params in useMemo with an empty dependencies array so the object
2872
+ // reference stays stable across re-renders. The one thing that may vary is
2873
+ // the locale a client-fetched item carries (put there by the data-source
2874
+ // plugin): a locale switch must rebuild the params, or the item would keep
2875
+ // its previous language.
2876
+ const memoDeps: types.Expression[] = []
2877
+ if (paramsExpression.type === 'ObjectExpression' && hasLocaleParam(paramsExpression.properties)) {
2878
+ memoDeps.push(buildClientLocaleExpression())
2879
+ }
2748
2880
  const memoizedParams = types.callExpression(types.identifier('useMemo'), [
2749
2881
  types.arrowFunctionExpression([], paramsExpression),
2750
- types.arrayExpression([]),
2882
+ types.arrayExpression(memoDeps),
2751
2883
  ])
2752
2884
 
2753
2885
  // Replace the params attribute
@@ -3377,7 +3509,8 @@ function updateGetStaticProps(
3377
3509
  chunks: any[],
3378
3510
  registry: StateRegistry,
3379
3511
  dependencies: Record<string, any>,
3380
- folderPath?: string[]
3512
+ folderPath: string[] | undefined,
3513
+ localized: boolean
3381
3514
  ): void {
3382
3515
  const getStaticPropsChunk = chunks.find((c) => c.name === 'getStaticProps')
3383
3516
  if (!getStaticPropsChunk || getStaticPropsChunk.type !== ChunkType.AST) {
@@ -3549,6 +3682,13 @@ function updateGetStaticProps(
3549
3682
  )
3550
3683
  }
3551
3684
 
3685
+ appendItemsPathParam(fetchParams, usage.itemsPath, 'itemsPath')
3686
+
3687
+ // The prefetch is per locale — getStaticProps runs once per language.
3688
+ if (localized) {
3689
+ fetchParams.push(buildServerLocaleParam())
3690
+ }
3691
+
3552
3692
  // Check if this fetch already exists
3553
3693
  const existingFetchIndex = arrayPattern.elements.findIndex(
3554
3694
  (el: any) => el?.type === 'Identifier' && el.name === vars.propsPrefix
@@ -3625,8 +3765,10 @@ function updateGetStaticProps(
3625
3765
  const staticFiltersForCount = (usage.filters || []).filter(
3626
3766
  (f: any) => !ASTUtils.isUIDLDynamicReference(f.destination)
3627
3767
  )
3628
- const hasStaticFilters = staticFiltersForCount.length > 0
3629
- const countVarName = hasStaticFilters
3768
+ // A count scoped to an inner array is a different number from the
3769
+ // whole-payload one, so it gets its own variable just like a filtered one.
3770
+ const hasScopedCount = staticFiltersForCount.length > 0 || !!usage.itemsPath
3771
+ const countVarName = hasScopedCount
3630
3772
  ? `${usage.dataSourceIdentifier}_ds_${usage.index}_count`
3631
3773
  : `${usage.dataSourceIdentifier}_count`
3632
3774
 
@@ -3698,7 +3840,7 @@ function updateGetStaticProps(
3698
3840
  // Group usages by their filters (stringify for comparison)
3699
3841
  const usagesByFilters = new Map<string, DataSourceUsage[]>()
3700
3842
  for (const usage of usagesForDataSource) {
3701
- const filtersKey = JSON.stringify(usage.filters || [])
3843
+ const filtersKey = JSON.stringify([usage.filters || [], usage.itemsPath || []])
3702
3844
  const existing = usagesByFilters.get(filtersKey) || []
3703
3845
  existing.push(usage)
3704
3846
  usagesByFilters.set(filtersKey, existing)
@@ -3713,10 +3855,12 @@ function updateGetStaticProps(
3713
3855
  )
3714
3856
  const hasStaticFilters = staticFiltersForCount.length > 0
3715
3857
 
3716
- // Create unique count variable name based on filters
3717
- const countVarName = hasStaticFilters
3718
- ? `${firstUsage.dataSourceIdentifier}_ds_${firstUsage.index}_count`
3719
- : `${firstUsage.dataSourceIdentifier}_count`
3858
+ // Create unique count variable name based on filters (and inner-array
3859
+ // scope — must match the naming at the maxPages site above)
3860
+ const countVarName =
3861
+ hasStaticFilters || firstUsage.itemsPath
3862
+ ? `${firstUsage.dataSourceIdentifier}_ds_${firstUsage.index}_count`
3863
+ : `${firstUsage.dataSourceIdentifier}_count`
3720
3864
 
3721
3865
  // Check if this count was already processed
3722
3866
  const countKey = `${key}:${filtersKey}`
@@ -3768,6 +3912,8 @@ function updateGetStaticProps(
3768
3912
  )
3769
3913
  }
3770
3914
 
3915
+ appendItemsPathParam(countParams, firstUsage.itemsPath, 'collectionPath')
3916
+
3771
3917
  fetchesArray.elements.push(
3772
3918
  types.callExpression(
3773
3919
  types.memberExpression(