@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
@@ -10,6 +10,10 @@ import {
10
10
  generateSortFieldSqlHelper,
11
11
  generateSortTiebreakSql,
12
12
  } from '../product-price-sort'
13
+ import { generateProductFilterClauseHelper } from '../product-filter-fields'
14
+ import { generateReadGuardCall, generateTableAccessPreamble } from './utils/table-access-guard'
15
+ import { generateRequestSqlGuardsCode } from './utils/request-sql-guards'
16
+ import { generateArrayOverlapSqlCode } from '../array-overlap-sql'
13
17
 
14
18
  interface PostgreSQLConfig {
15
19
  connectionString?: string
@@ -26,7 +30,8 @@ interface PostgreSQLConfig {
26
30
 
27
31
  export const generatePostgreSQLFetcher = (
28
32
  config: Record<string, unknown>,
29
- tableName: string
33
+ tableName: string,
34
+ trustedReaderRoles: ReadonlyArray<string> = []
30
35
  ): string => {
31
36
  const pgConfig = config as PostgreSQLConfig
32
37
  const schema = pgConfig.options?.schema
@@ -57,6 +62,8 @@ export const generatePostgreSQLFetcher = (
57
62
 
58
63
  return `import { Client } from 'pg'
59
64
 
65
+ ${generateTableAccessPreamble(trustedReaderRoles)}
66
+
60
67
  const getClient = () => {
61
68
  return new Client(${clientConfig})
62
69
  }
@@ -66,9 +73,11 @@ ${generateSafeJSONParseCode()}
66
73
  ${generateFilterTreeHelpersCode()}
67
74
 
68
75
  ${generateSearchEscapeHelpersCode()}
76
+ ${generateArrayOverlapSqlCode()}
69
77
 
70
78
  // Builds one SQL clause for the whole filter tree, so an OR group nested in
71
79
  // the root AND keeps its meaning instead of being flattened into ANDs.
80
+ ${generateRequestSqlGuardsCode()}
72
81
  const processFilters = (filters, conditions, queryParams, paramIndex) => {
73
82
  if (!filters) return paramIndex
74
83
 
@@ -77,13 +86,26 @@ const processFilters = (filters, conditions, queryParams, paramIndex) => {
77
86
 
78
87
  const buildCondition = (condition) => {
79
88
  const field = condition.source
80
- const value = condition.destination
81
- const operand = condition.operand
89
+ const normalizedCondition = normalizeInOperand(condition.operand, condition.destination)
90
+ if (normalizedCondition === null) return null
91
+ const value = normalizedCondition.value
92
+ const operand = normalizedCondition.operand
93
+
94
+ // The products table's virtual fields (effective price, rating bucket,
95
+ // on-sale flag, variant axes) are whole clauses, not columns - so they are
96
+ // answered before any branch below can interpolate the field name raw.
97
+ const productClause = productFilterClause(field, operand, value, (param) => {
98
+ queryParams.push(param)
99
+ return '$' + paramIndex++
100
+ })
101
+ if (productClause !== null) return productClause
102
+ assertRequestColumn(field, 'filter field')
82
103
 
83
104
  if (Array.isArray(value)) {
84
105
  if (value.length === 0) return null
85
106
  if (operand === 'array_overlap') {
86
- const clause = \`jsonb_exists_any(NULLIF(\${field}, '')::jsonb, $\${paramIndex}::text[])\`
107
+ // The row's list (see array-overlap-sql.ts) shares any value with the set.
108
+ const clause = arrayOverlapSql([field], '$' + paramIndex)
87
109
  queryParams.push(value.map((entry) => String(entry)))
88
110
  paramIndex++
89
111
  return clause
@@ -101,7 +123,7 @@ const processFilters = (filters, conditions, queryParams, paramIndex) => {
101
123
  // ?categoryFilter=a,b,c) expands to multiple ids; one id stays one.
102
124
  const overlapValues = String(value).split(',').map((entry) => entry.trim()).filter(Boolean)
103
125
  if (overlapValues.length === 0) return null
104
- const clause = \`jsonb_exists_any(NULLIF(\${field}, '')::jsonb, $\${paramIndex}::text[])\`
126
+ const clause = arrayOverlapSql([field], '$' + paramIndex)
105
127
  queryParams.push(overlapValues)
106
128
  paramIndex++
107
129
  return clause
@@ -133,7 +155,10 @@ ${generateSortFieldSqlHelper(tableName)}
133
155
 
134
156
  ${generateSortFallbackFieldHelper(tableName)}
135
157
 
158
+ ${generateProductFilterClauseHelper(tableName)}
159
+
136
160
  export default async function handler(req, res) {
161
+ ${generateReadGuardCall(tableName, null)}
137
162
  const client = getClient()
138
163
 
139
164
  try {
@@ -141,6 +166,8 @@ export default async function handler(req, res) {
141
166
  ${schema ? `await client.query('SET search_path TO ${schema}')` : ''}
142
167
 
143
168
  const { query, queryColumns, limit, page, perPage, sortBy, sortOrder, filters, sorts, offset } = req.query
169
+ // A visitor never filters, sorts or searches by a column it is not served.
170
+ if (__rowPolicy) __brpAssertVisibleFields(__rowPolicy, req.query)
144
171
 
145
172
  const conditions = []
146
173
  const queryParams = []
@@ -175,6 +202,7 @@ export default async function handler(req, res) {
175
202
  // Continue without search if we can't get columns
176
203
  }
177
204
  }
205
+ if (__rowPolicy) columns = __brpVisibleColumns(__rowPolicy, columns)
178
206
 
179
207
  if (columns.length > 0) {
180
208
  const pattern = '%' + escapeLikePattern(query) + '%'
@@ -190,6 +218,7 @@ export default async function handler(req, res) {
190
218
 
191
219
  // Apply filters using helper function
192
220
  paramIndex = processFilters(filters, conditions, queryParams, paramIndex)
221
+ if (__rowPolicy) conditions.push('(' + __rowPolicy.predicate + ')')
193
222
 
194
223
  let sql = \`SELECT * FROM "${tableName}"\`
195
224
 
@@ -205,6 +234,7 @@ export default async function handler(req, res) {
205
234
  const parsedSorts = safeJSONParse(sorts)
206
235
  if (Array.isArray(parsedSorts) && parsedSorts.length > 0) {
207
236
  const valid = parsedSorts.filter((sort) => sort && sort.field)
237
+ valid.forEach((sort) => assertRequestColumn(sort.field, 'sort field'))
208
238
  const orderOf = (sort) => (sort.order || '').toUpperCase().startsWith('DESC') ? 'DESC' : 'ASC'
209
239
  const orderClauses = valid.map((sort) => \`\${sortFieldSql(sort.field)} \${orderOf(sort)}\`)
210
240
  const plainClauses = valid.map((sort) => \`\${sortFallbackField(sort.field)} \${orderOf(sort)}\`)
@@ -222,16 +252,17 @@ export default async function handler(req, res) {
222
252
  }
223
253
  }
224
254
  } else if (sortBy) {
255
+ assertRequestColumn(sortBy, 'sort field')
225
256
  orderBySql = \` ORDER BY \${sortBy} \${(sortOrder || '').toUpperCase().startsWith('DESC') ? 'DESC' : 'ASC'}\`
226
257
  plainOrderBySql = orderBySql
227
258
  }
228
259
  const usedDiscountAwareSort = orderBySql !== plainOrderBySql
229
260
 
230
- const limitValue = limit || perPage
261
+ const limitValue = requestRowCount(limit || perPage)
231
262
  const offsetValue = offset !== undefined ? parseInt(offset) : (page && perPage ? (parseInt(page) - 1) * parseInt(perPage) : undefined)
232
263
 
233
264
  let sqlTail = ''
234
- if (limitValue) {
265
+ if (limitValue !== undefined) {
235
266
  sqlTail += \` LIMIT \${limitValue}\`
236
267
  }
237
268
 
@@ -265,14 +296,20 @@ export default async function handler(req, res) {
265
296
 
266
297
  return res.status(200).json({
267
298
  success: true,
268
- data: safeData,
299
+ data: __taWithoutCredentials(
300
+ __rowPolicy ? __brpStripHidden(__rowPolicy, safeData) : safeData,
301
+ ${JSON.stringify(tableName)}
302
+ ),
269
303
  timestamp: Date.now()
270
304
  })
271
305
  } catch (error) {
272
306
  console.error('PostgreSQL fetch error:', error)
273
- return res.status(500).json({
307
+ // A database error names tables, columns and values: it stays in the server
308
+ // log. Only an error this route raised for the caller (it carries a status)
309
+ // is repeated to it.
310
+ return res.status(error.status || 500).json({
274
311
  success: false,
275
- error: error.message || 'Failed to fetch data',
312
+ error: error.status ? error.message : 'Failed to fetch data',
276
313
  timestamp: Date.now()
277
314
  })
278
315
  } finally {
@@ -297,11 +334,13 @@ export const generatePostgreSQLCountFetcher = (
297
334
 
298
335
  return `
299
336
  async function getCount(req, res) {
337
+ ${generateReadGuardCall(tableName, null)}
300
338
  const client = getClient()
301
339
 
302
340
  try {
303
341
  await client.connect()
304
342
  const { query, queryColumns, filters } = req.query
343
+ if (__rowPolicy) __brpAssertVisibleFields(__rowPolicy, req.query)
305
344
  const conditions = []
306
345
  const queryParams = []
307
346
  let paramIndex = 1
@@ -336,6 +375,7 @@ async function getCount(req, res) {
336
375
  // Continue without search if we can't get columns
337
376
  }
338
377
  }
378
+ if (__rowPolicy) columns = __brpVisibleColumns(__rowPolicy, columns)
339
379
 
340
380
  if (columns.length > 0) {
341
381
  const pattern = '%' + escapeLikePattern(query) + '%'
@@ -353,6 +393,7 @@ async function getCount(req, res) {
353
393
 
354
394
  // Apply filters using helper function
355
395
  paramIndex = processFilters(filters, conditions, queryParams, paramIndex)
396
+ if (__rowPolicy) conditions.push('(' + __rowPolicy.predicate + ')')
356
397
 
357
398
  let countSql = \`SELECT COUNT(*) FROM "${tableName}"\`
358
399
  if (conditions.length > 0) {
@@ -369,9 +410,9 @@ async function getCount(req, res) {
369
410
  })
370
411
  } catch (error) {
371
412
  console.error('Error getting count:', error)
372
- return res.status(500).json({
413
+ return res.status(error.status || 500).json({
373
414
  success: false,
374
- error: error.message || 'Failed to get count',
415
+ error: error.status ? error.message : 'Failed to get count',
375
416
  timestamp: Date.now()
376
417
  })
377
418
  } finally {
@@ -1,3 +1,4 @@
1
+ import { SessionCookieResolver } from '@teleporthq/teleport-shared'
1
2
  import { replaceSecretReference } from '../utils'
2
3
 
3
4
  interface PostgreSQLConfig {
@@ -13,9 +14,28 @@ interface PostgreSQLConfig {
13
14
 
14
15
  const FORBIDDEN_KEYWORDS = ['CREATE', 'ALTER', 'DROP', 'TRUNCATE', 'RENAME', 'GRANT', 'REVOKE']
15
16
 
17
+ /** The param a `{{Current User.<field>}}` token becomes: `currentUser` + the field, capitalised. */
18
+ const CURRENT_USER_PARAM_PREFIX = 'currentUser'
19
+
20
+ const isCurrentUserParam = (param: string): boolean =>
21
+ param.length > CURRENT_USER_PARAM_PREFIX.length && param.indexOf(CURRENT_USER_PARAM_PREFIX) === 0
22
+
23
+ /** The user field a `currentUser<Field>` param stands for (`currentUserId` → `id`). */
24
+ const currentUserField = (param: string): string => {
25
+ const rest = param.slice(CURRENT_USER_PARAM_PREFIX.length)
26
+ return rest.charAt(0).toLowerCase() + rest.slice(1)
27
+ }
28
+
16
29
  /**
17
30
  * Generates an API route handler that executes a parameterized raw SQL query.
18
- * Supports {{Current User.*}} substitution via query params.
31
+ * Supports {{Current User.*}} substitution — bound to the SESSION's user.
32
+ *
33
+ * ⛔ A `{{Current User.<field>}}` value is read from the signed-in session on
34
+ * the server, never from the request: the query string is whatever the caller
35
+ * wrote, so a route that bound `?currentUserId=` served any user's rows (their
36
+ * favourites, their orders) to anyone who named that user's id. A caller with
37
+ * no session gets no rows, exactly as when the value is missing. Other params
38
+ * (`currentPageEntityId`, the details page's route param) stay request values.
19
39
  *
20
40
  * @param config - PostgreSQL connection config
21
41
  * @param query - Raw SQL query string with {{Current User.*}} patterns already replaced to $N placeholders
@@ -27,9 +47,43 @@ export const generateRawQueryFetcher = (
27
47
  paramFields: string[]
28
48
  ): string => {
29
49
  const pgConfig = config as PostgreSQLConfig
50
+ const userParams = paramFields.filter(isCurrentUserParam)
51
+ const requestParams = paramFields.filter((param) => !isCurrentUserParam(param))
52
+
53
+ const paramDestructure = [
54
+ requestParams.length > 0 ? `const { ${requestParams.join(', ')} } = req.query` : '',
55
+ userParams.length > 0 ? 'const __sessionUser = await sessionUser(req)' : '',
56
+ ...userParams.map(
57
+ (param) =>
58
+ `const ${param} = sessionUserField(__sessionUser, ${JSON.stringify(
59
+ currentUserField(param)
60
+ )})`
61
+ ),
62
+ ]
63
+ .filter(Boolean)
64
+ .join('\n ')
65
+
66
+ const sessionHelpers =
67
+ userParams.length > 0
68
+ ? `${SessionCookieResolver.generateCommonJsSessionTokenResolverCode()}
69
+ // The signed-in user this request's session names, or null.
70
+ async function sessionUser(req) {
71
+ try {
72
+ return await __tqSessionToken(req)
73
+ } catch (e) {
74
+ return null
75
+ }
76
+ }
30
77
 
31
- const paramDestructure =
32
- paramFields.length > 0 ? `const { ${paramFields.join(', ')} } = req.query` : ''
78
+ // One field of that user, as the session carries it (the token is seeded from
79
+ // the users row), or undefined. The id is the token's subject when not copied.
80
+ function sessionUserField(user, field) {
81
+ if (!user) return undefined
82
+ var value = field === 'id' && user.id == null ? user.sub : user[field]
83
+ return value == null || value === '' ? undefined : value
84
+ }
85
+ `
86
+ : ''
33
87
 
34
88
  const paramValidation =
35
89
  paramFields.length > 0
@@ -58,7 +112,7 @@ export const generateRawQueryFetcher = (
58
112
  return `import { Client } from 'pg'
59
113
 
60
114
  const FORBIDDEN_KEYWORDS = ${JSON.stringify(FORBIDDEN_KEYWORDS)}
61
-
115
+ ${sessionHelpers}
62
116
  const getClient = () => {
63
117
  const connStr = process.env.TELEPORT_DB_CONNECTION_STRING
64
118
  if (connStr) {
@@ -122,10 +176,11 @@ export default async function handler(req, res) {
122
176
  await client.end()
123
177
  }
124
178
  } catch (error) {
179
+ // A database error names tables, columns and values: server log only.
125
180
  console.error('Raw query fetch error:', error)
126
181
  return res.status(500).json({
127
182
  success: false,
128
- error: error.message || 'Failed to fetch data',
183
+ error: 'Failed to fetch data',
129
184
  timestamp: Date.now()
130
185
  })
131
186
  }
@@ -127,8 +127,10 @@ export default async function handler(req, res) {
127
127
  if (filterTree) {
128
128
  const buildCondition = (condition) => {
129
129
  const field = sanitizeIdentifier(condition.source)
130
- const value = condition.destination
131
- const operand = condition.operand
130
+ const normalizedCondition = normalizeInOperand(condition.operand, condition.destination)
131
+ if (normalizedCondition === null) return null
132
+ const value = normalizedCondition.value
133
+ const operand = normalizedCondition.operand
132
134
 
133
135
  if (Array.isArray(value)) {
134
136
  if (value.length === 0) return null
@@ -5,6 +5,11 @@ import {
5
5
  generateSafeJSONParseCode,
6
6
  generateFilterTreeHelpersCode,
7
7
  } from '../utils'
8
+ import {
9
+ RESTAPICacheConfig,
10
+ generateRESTAPIResponseCacheCode,
11
+ resolveRESTAPICacheTTLSeconds,
12
+ } from './utils/rest-api-response-cache'
8
13
 
9
14
  export const validateRESTAPIConfig = (
10
15
  config: Record<string, unknown>
@@ -76,11 +81,16 @@ interface RESTAPIConfig {
76
81
  authorization?: Authorization
77
82
  bodyType?: string
78
83
  body?: string
84
+ cache?: RESTAPICacheConfig
79
85
  }
80
86
 
81
87
  export const generateRESTAPIFetcher = (config: Record<string, unknown>): string => {
82
88
  const restConfig = config as RESTAPIConfig
83
89
  const authCode = generateAuthCode(restConfig.authorization || {})
90
+ // Every cache-only fragment below is empty when caching is off, so an
91
+ // uncached data source keeps generating byte-identical output.
92
+ const cacheTTLSeconds = resolveRESTAPICacheTTLSeconds(restConfig.cache)
93
+ const cached = cacheTTLSeconds !== undefined
84
94
 
85
95
  return `import fetch from 'node-fetch'
86
96
 
@@ -92,11 +102,13 @@ ${generateSortFilterHelperCode()}
92
102
 
93
103
  ${generateFilterTreeHelpersCode()}
94
104
 
95
- ${generateCollectionPathHelperCode()}
105
+ ${generateCollectionPathHelperCode()}${
106
+ cached ? `\n\n${generateRESTAPIResponseCacheCode(cacheTTLSeconds)}` : ''
107
+ }
96
108
 
97
109
  export default async function handler(req, res) {
98
110
  try {
99
- const { query, queryColumns, limit, page, perPage, sortBy, sortOrder, filters, sorts, offset, collectionPath } = req.query
111
+ const { query, queryColumns, limit, page, perPage, sortBy, sortOrder, filters, sorts, offset, collectionPath, itemsPath } = req.query
100
112
 
101
113
  const url = ${JSON.stringify(restConfig.url)}
102
114
  const method = ${JSON.stringify(restConfig.method || 'GET')}
@@ -134,7 +146,7 @@ export default async function handler(req, res) {
134
146
  : ''
135
147
  }
136
148
 
137
- const response = await fetch(url, options)
149
+ const response = await ${cached ? 'fetchRestApiWithCache' : 'fetch'}(url, options)
138
150
 
139
151
  if (!response.ok) {
140
152
  return res.status(response.status).json({
@@ -144,9 +156,13 @@ export default async function handler(req, res) {
144
156
  })
145
157
  }
146
158
 
147
- let data = await response.json()
159
+ let data = ${cached ? 'JSON.parse(response.bodyText)' : 'await response.json()'}
148
160
 
149
161
  data = resolveCollectionPath(data, collectionPath)
162
+ const itemsEnvelope = openItemsEnvelope(data, itemsPath)
163
+ if (itemsEnvelope) {
164
+ data = itemsEnvelope.items
165
+ }
150
166
 
151
167
  if (Array.isArray(data)) {
152
168
  if (query && query.trim()) {
@@ -278,9 +294,15 @@ export default async function handler(req, res) {
278
294
  }
279
295
  }
280
296
 
297
+ if (itemsEnvelope) {
298
+ data = itemsEnvelope.close(data)
299
+ }
300
+
281
301
  const safeData = JSON.parse(JSON.stringify(data, dateReplacer))
282
302
 
283
- return res.status(200).json({
303
+ ${
304
+ cached ? 'applyRestApiCacheHeaders(req, res, response.fetchedAt)\n \n ' : ''
305
+ }return res.status(200).json({
284
306
  success: true,
285
307
  data: safeData,
286
308
  timestamp: Date.now()