@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
@@ -285,6 +285,17 @@ describe('array-mapper cache — client wiring', () => {
285
285
  expect(hitReturn).toBeLessThan(raise)
286
286
  })
287
287
 
288
+ it('counts a cache hit as the newest settled result', async () => {
289
+ const { code } = await runPlugin({ cache: CACHE })
290
+
291
+ // A hit answers synchronously with THIS request's rows; without advancing
292
+ // the sequence, an older network request still in flight would resolve
293
+ // after it and paint over it.
294
+ expect(code).toMatch(
295
+ /if \(__tqHit !== undefined\) \{\s*\+\+ds_0_fetchSeq\.current;?\s*ds_0_latestData\.current = __tqHit;?\s*return Promise\.resolve\(__tqHit\)/
296
+ )
297
+ })
298
+
288
299
  it('still tracks in-flight fetches on a cache MISS', async () => {
289
300
  const { code } = await runPlugin({ cache: CACHE })
290
301
 
@@ -0,0 +1,53 @@
1
+ import { generateArrayOverlapSqlCode } from '../src/array-overlap-sql'
2
+ import { generateLocalizedColumnsHelper } from '../src/localized-columns'
3
+
4
+ /**
5
+ * An `array_overlap` filter over a list column. The emitted SQL was run
6
+ * against PostgreSQL over every form a list cell takes (JSON array, a
7
+ * hand-typed comma list, a native array literal, blank, padded); these pin the
8
+ * shape that run proved.
9
+ */
10
+
11
+ type OverlapSql = (columns: string[], param: string) => string
12
+
13
+ const arrayOverlapSql = new Function(
14
+ generateArrayOverlapSqlCode() + '\nreturn arrayOverlapSql'
15
+ )() as OverlapSql
16
+
17
+ describe('arrayOverlapSql', () => {
18
+ it('reads a JSON array, a comma list and an array literal as one text[]', () => {
19
+ const sql = arrayOverlapSql(['tags'], '$1')
20
+ expect(sql).toContain("LIKE '[%' THEN ARRAY(SELECT jsonb_array_elements_text(")
21
+ expect(sql).toContain("LIKE '{%' THEN")
22
+ expect(sql).toContain("regexp_split_to_array(NULLIF(BTRIM(COALESCE(tags::text, '')), ''),")
23
+ expect(sql).toMatch(/&& \$1::text\[\]$/)
24
+ })
25
+
26
+ it('never casts a comma list to jsonb', () => {
27
+ // The old `NULLIF(tags, '')::jsonb` threw on `react, css` and failed the
28
+ // whole listing query.
29
+ expect(arrayOverlapSql(['tags'], '$1')).not.toContain("NULLIF(tags, '')::jsonb")
30
+ })
31
+
32
+ it('matches any of a translatable list and its language copies', () => {
33
+ const sql = arrayOverlapSql(['"tags"', '"es_tags"'], '$2')
34
+ expect(sql.startsWith('(')).toBe(true)
35
+ expect(sql.split(' OR ')).toHaveLength(2)
36
+ expect(sql.match(/&& \$2::text\[\]/g)).toHaveLength(2)
37
+ })
38
+ })
39
+
40
+ describe('localizedColumnList', () => {
41
+ const localizedColumnList = new Function(
42
+ generateLocalizedColumnsHelper({ tags: { es: 'es_tags', fr: 'fr_tags' } }) +
43
+ '\nreturn localizedColumnList'
44
+ )() as (field: string) => string[] | null
45
+
46
+ it('names a translatable column with every copy the schema has', () => {
47
+ expect(localizedColumnList('tags')).toEqual(['"tags"', '"es_tags"', '"fr_tags"'])
48
+ })
49
+
50
+ it('answers null for a column with no copies', () => {
51
+ expect(localizedColumnList('category_filter_ids')).toBeNull()
52
+ })
53
+ })
@@ -0,0 +1,214 @@
1
+ import { generateBlogPostTransformationCode } from '../src/transformations/blog-post'
2
+ import { generateSharedTransformationCode } from '../src/transformations/shared-utils'
3
+ import { buildProductTransformOptions, getTransformWrapperCode } from '../src/transformations'
4
+
5
+ /**
6
+ * What a blog post DETAILS page carries beyond the row: the author's bio and
7
+ * initials, its approved comments, its previous / next neighbours, and — when
8
+ * the blog asks for it — anchors on its content headings.
9
+ *
10
+ * These evaluate the EMITTED code, the way the runtime does.
11
+ */
12
+
13
+ type Build = (record: unknown, options?: Record<string, unknown>) => Record<string, unknown>
14
+
15
+ const evalExport = <T>(code: string, exportName: string): T =>
16
+ new Function(generateSharedTransformationCode() + '\n' + code + `\nreturn ${exportName};`)() as T
17
+
18
+ const buildWith = (options: { headingAnchors?: boolean } = {}): Build =>
19
+ evalExport<Build>(generateBlogPostTransformationCode(options), 'buildBlogPost')
20
+
21
+ const buildBlogPost = buildWith()
22
+
23
+ const post = (id: string, overrides: Record<string, unknown> = {}) => ({
24
+ id,
25
+ title: `Post ${id}`,
26
+ slug: `post-${id}`,
27
+ status: 'published',
28
+ ...overrides,
29
+ })
30
+
31
+ describe('blog post transform — author box fields', () => {
32
+ it('resolves the bio in the request language, falling back to the main one', () => {
33
+ const record = post('a', {
34
+ author_bio: 'Writes about tea.',
35
+ es_author_bio: 'Escribe sobre té.',
36
+ })
37
+ expect(buildBlogPost(record).authorBio).toBe('Writes about tea.')
38
+ expect(buildBlogPost(record, { currentLanguage: 'es', mainLanguage: 'en' }).authorBio).toBe(
39
+ 'Escribe sobre té.'
40
+ )
41
+ expect(
42
+ buildBlogPost(post('b', { author_bio: 'Only English.' }), {
43
+ currentLanguage: 'es',
44
+ mainLanguage: 'en',
45
+ }).authorBio
46
+ ).toBe('Only English.')
47
+ })
48
+
49
+ it('reads a missing bio as null', () => {
50
+ expect(buildBlogPost(post('a')).authorBio).toBeNull()
51
+ })
52
+
53
+ it('derives initials from the first and last word of the author name', () => {
54
+ expect(buildBlogPost(post('a', { author_name: 'ada king lovelace' })).authorInitials).toBe(
55
+ 'AL'
56
+ )
57
+ expect(buildBlogPost(post('a', { author_name: 'Plato' })).authorInitials).toBe('P')
58
+ expect(buildBlogPost(post('a')).authorInitials).toBe('')
59
+ })
60
+ })
61
+
62
+ describe('blog post transform — comments', () => {
63
+ const comment = (id: string, overrides: Record<string, unknown> = {}) => ({
64
+ id,
65
+ parent_id: null as string | null,
66
+ author_name: `Reader ${id}`,
67
+ content: `Comment ${id}`,
68
+ is_author_reply: false,
69
+ created_at: '2026-09-01T10:00:00.000Z',
70
+ ...overrides,
71
+ })
72
+
73
+ it('emits empty comments on a fetch that did not ask for them', () => {
74
+ const built = buildBlogPost(post('a'))
75
+ expect(built.comments).toEqual([])
76
+ expect(built.commentsCount).toBe(0)
77
+ })
78
+
79
+ it('nests replies under the comment they answer, in the order given', () => {
80
+ const built = buildBlogPost(post('a'), {
81
+ commentsByPostId: {
82
+ a: {
83
+ count: 4,
84
+ rows: [
85
+ comment('c1', { author_name: 'Grace Hopper' }),
86
+ comment('c2'),
87
+ comment('r1', { parent_id: 'c1', is_author_reply: true }),
88
+ comment('r2', { parent_id: 'c1' }),
89
+ ],
90
+ },
91
+ },
92
+ })
93
+
94
+ const comments = built.comments as Array<Record<string, unknown>>
95
+ expect(comments.map((entry) => entry.id)).toEqual(['c1', 'c2'])
96
+ expect(comments[0]).toMatchObject({
97
+ authorName: 'Grace Hopper',
98
+ authorInitials: 'GH',
99
+ content: 'Comment c1',
100
+ isAuthorReply: false,
101
+ createdAt: Date.parse('2026-09-01T10:00:00.000Z'),
102
+ })
103
+ const replies = comments[0].replies as Array<Record<string, unknown>>
104
+ expect(replies.map((entry) => entry.id)).toEqual(['r1', 'r2'])
105
+ expect(replies[0].isAuthorReply).toBe(true)
106
+ expect(replies[0]).not.toHaveProperty('replies')
107
+ expect(comments[1].replies).toEqual([])
108
+ expect(built.commentsCount).toBe(4)
109
+ })
110
+
111
+ it('drops a reply whose comment is not among the rows', () => {
112
+ const built = buildBlogPost(post('a'), {
113
+ commentsByPostId: { a: { count: 1, rows: [comment('r1', { parent_id: 'gone' })] } },
114
+ })
115
+ expect(built.comments).toEqual([])
116
+ })
117
+
118
+ it('never hands the related or adjacent cards comments of their own', () => {
119
+ const built = buildBlogPost(post('a', { related_post_ids: '["b"]' }), {
120
+ relatedPostsById: { b: post('b') },
121
+ commentsByPostId: {
122
+ a: { count: 1, rows: [comment('c1')] },
123
+ b: { count: 1, rows: [comment('c9')] },
124
+ },
125
+ })
126
+ const related = built.relatedPosts as Array<Record<string, unknown>>
127
+ expect(related[0].comments).toEqual([])
128
+ })
129
+ })
130
+
131
+ describe('blog post transform — previous / next', () => {
132
+ it('emits 0-or-1-item lists of transformed posts', () => {
133
+ const built = buildBlogPost(post('b'), {
134
+ adjacentPostsById: { b: { previous: post('a', { title: 'Older' }), next: null } },
135
+ })
136
+ const previous = built.previousPosts as Array<Record<string, unknown>>
137
+ expect(previous).toHaveLength(1)
138
+ expect(previous[0]).toMatchObject({ id: 'a', title: 'Older', slug: 'post-a' })
139
+ expect(built.nextPosts).toEqual([])
140
+ })
141
+
142
+ it('emits empty lists when nothing was looked up', () => {
143
+ const built = buildBlogPost(post('b'))
144
+ expect(built.previousPosts).toEqual([])
145
+ expect(built.nextPosts).toEqual([])
146
+ })
147
+ })
148
+
149
+ describe('blog post transform — heading anchors', () => {
150
+ it('leaves content alone unless the blog asks for anchors', () => {
151
+ const content = '<h2>Setup</h2><p>Text</p>'
152
+ expect(buildBlogPost(post('a', { content })).content).toBe(content)
153
+ })
154
+
155
+ it('gives every h2-h6 a unique id and a hidden # link, keeping ids it already has', () => {
156
+ const built = buildWith({ headingAnchors: true })(
157
+ post('a', {
158
+ content:
159
+ '<h1>Title</h1><h2>Getting Started</h2><h3 class="x" id="custom">Café &amp; Crème</h3>' +
160
+ '<h2>Getting started</h2><h4><br></h4><h2 id="getting-started-2">Clash</h2>',
161
+ })
162
+ )
163
+ expect(built.content).toBe(
164
+ '<h1>Title</h1>' +
165
+ '<h2 id="getting-started">Getting Started<a class="tq-heading-anchor" href="#getting-started" aria-hidden="true" tabindex="-1">#</a></h2>' +
166
+ '<h3 class="x" id="custom">Café &amp; Crème<a class="tq-heading-anchor" href="#custom" aria-hidden="true" tabindex="-1">#</a></h3>' +
167
+ '<h2 id="getting-started-3">Getting started<a class="tq-heading-anchor" href="#getting-started-3" aria-hidden="true" tabindex="-1">#</a></h2>' +
168
+ '<h4><br></h4>' +
169
+ '<h2 id="getting-started-2">Clash<a class="tq-heading-anchor" href="#getting-started-2" aria-hidden="true" tabindex="-1">#</a></h2>'
170
+ )
171
+ })
172
+
173
+ it('slugs accented and non-Latin text instead of dropping it', () => {
174
+ const built = buildWith({ headingAnchors: true })(
175
+ post('a', { content: '<h2>Crème brûlée</h2><h2>Привет мир</h2><h2>!!!</h2>' })
176
+ )
177
+ expect(built.content).toContain('id="creme-brulee"')
178
+ expect(built.content).toContain('id="привет-мир"')
179
+ expect(built.content).toContain('id="section"')
180
+ })
181
+
182
+ it('is idempotent', () => {
183
+ const build = buildWith({ headingAnchors: true })
184
+ const once = build(post('a', { content: '<h2>Setup</h2>' })).content as string
185
+ expect(build(post('a', { content: once })).content).toBe(once)
186
+ })
187
+ })
188
+
189
+ describe('transform wrapper — post page lookups', () => {
190
+ it('asks for the neighbours on every blog fetch, and for comments only when the blog takes them', () => {
191
+ const withoutComments = getTransformWrapperCode('teleport_blog_posts')
192
+ expect(withoutComments).toContain('getAdjacentPostsMap(getClientFn, records)')
193
+ expect(withoutComments).not.toContain('getBlogCommentsMap(getClientFn, records)')
194
+ expect(withoutComments).toContain('commentsByPostId: commentsByPostId')
195
+
196
+ const withComments = getTransformWrapperCode('teleport_blog_posts', { blogComments: true })
197
+ expect(withComments).toContain('getBlogCommentsMap(getClientFn, records)')
198
+ })
199
+
200
+ it('never asks for them on another table', () => {
201
+ const products = getTransformWrapperCode('teleport_products', { blogComments: true })
202
+ expect(products).not.toContain('getAdjacentPostsMap')
203
+ expect(products).not.toContain('getBlogCommentsMap')
204
+ })
205
+
206
+ it('reads both switches off the blog settings', () => {
207
+ const options = buildProductTransformOptions({
208
+ blogSettings: { categories: [], headingAnchors: true, comments: true },
209
+ })
210
+ expect(options.blogHeadingAnchors).toBe(true)
211
+ expect(options.blogComments).toBe(true)
212
+ expect(buildProductTransformOptions({}).blogComments).toBe(false)
213
+ })
214
+ })