@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.
- package/__tests__/array-mapper-cache-wiring.test.ts +11 -0
- package/__tests__/array-overlap-sql.test.ts +53 -0
- package/__tests__/blog-post-page-enrichment.test.ts +214 -0
- package/__tests__/browser-row-policy.test.ts +503 -0
- package/__tests__/data-source-cache-wrapping.test.ts +18 -2
- package/__tests__/data-source-request-locale.test.ts +313 -0
- package/__tests__/ecommerce-product-options.test.ts +206 -0
- package/__tests__/ecommerce-product-pages.test.ts +82 -0
- package/__tests__/ecommerce-product-pre-options.golden.json +505 -0
- package/__tests__/ecommerce-product-storefront-contract.test.ts +293 -0
- package/__tests__/ecommerce-product-subscriptions.test.ts +121 -0
- package/__tests__/fetchers.test.ts +2 -2
- package/__tests__/gated-data-provider.test.ts +88 -0
- package/__tests__/items-path.test.ts +150 -0
- package/__tests__/localized-columns.test.ts +217 -0
- package/__tests__/numbered-pagination.test.ts +15 -0
- package/__tests__/product-filter-fields.ts +230 -0
- package/__tests__/product-filter-sql.fixture.json +7 -0
- package/__tests__/product-options-storefront.fixture.json +2510 -0
- package/__tests__/product-page-url-fixtures.json +25 -0
- package/__tests__/product-price-sort.ts +4 -2
- package/__tests__/raw-rows-mode.test.ts +111 -0
- package/__tests__/refetch-loading-state.test.ts +23 -0
- package/__tests__/rest-api-response-cache.test.ts +434 -0
- package/__tests__/search-clear-button.test.ts +122 -0
- package/__tests__/table-read-route-protected-tables.test.ts +493 -0
- package/dist/cjs/array-overlap-sql.d.ts +21 -0
- package/dist/cjs/array-overlap-sql.d.ts.map +1 -0
- package/dist/cjs/array-overlap-sql.js +25 -0
- package/dist/cjs/array-overlap-sql.js.map +1 -0
- package/dist/cjs/content-localization.d.ts +37 -0
- package/dist/cjs/content-localization.d.ts.map +1 -0
- package/dist/cjs/content-localization.js +29 -0
- package/dist/cjs/content-localization.js.map +1 -0
- package/dist/cjs/count-effect.d.ts +11 -1
- package/dist/cjs/count-effect.d.ts.map +1 -1
- package/dist/cjs/count-effect.js +30 -2
- package/dist/cjs/count-effect.js.map +1 -1
- package/dist/cjs/data-source-fetchers.d.ts.map +1 -1
- package/dist/cjs/data-source-fetchers.js +10 -4
- package/dist/cjs/data-source-fetchers.js.map +1 -1
- package/dist/cjs/fetchers/airtable.d.ts.map +1 -1
- package/dist/cjs/fetchers/airtable.js +1 -1
- package/dist/cjs/fetchers/airtable.js.map +1 -1
- package/dist/cjs/fetchers/clickhouse.d.ts.map +1 -1
- package/dist/cjs/fetchers/clickhouse.js +1 -1
- package/dist/cjs/fetchers/clickhouse.js.map +1 -1
- package/dist/cjs/fetchers/firestore.d.ts.map +1 -1
- package/dist/cjs/fetchers/firestore.js +1 -1
- package/dist/cjs/fetchers/firestore.js.map +1 -1
- package/dist/cjs/fetchers/javascript.d.ts.map +1 -1
- package/dist/cjs/fetchers/javascript.js +1 -1
- package/dist/cjs/fetchers/javascript.js.map +1 -1
- package/dist/cjs/fetchers/mariadb.d.ts.map +1 -1
- package/dist/cjs/fetchers/mariadb.js +1 -1
- package/dist/cjs/fetchers/mariadb.js.map +1 -1
- package/dist/cjs/fetchers/mongodb.d.ts.map +1 -1
- package/dist/cjs/fetchers/mongodb.js +1 -1
- package/dist/cjs/fetchers/mongodb.js.map +1 -1
- package/dist/cjs/fetchers/mysql.d.ts.map +1 -1
- package/dist/cjs/fetchers/mysql.js +1 -1
- package/dist/cjs/fetchers/mysql.js.map +1 -1
- package/dist/cjs/fetchers/postgresql.d.ts +1 -1
- package/dist/cjs/fetchers/postgresql.d.ts.map +1 -1
- package/dist/cjs/fetchers/postgresql.js +9 -4
- package/dist/cjs/fetchers/postgresql.js.map +1 -1
- package/dist/cjs/fetchers/raw-query.d.ts +8 -1
- package/dist/cjs/fetchers/raw-query.d.ts.map +1 -1
- package/dist/cjs/fetchers/raw-query.js +42 -4
- package/dist/cjs/fetchers/raw-query.js.map +1 -1
- package/dist/cjs/fetchers/redshift.d.ts.map +1 -1
- package/dist/cjs/fetchers/redshift.js +1 -1
- package/dist/cjs/fetchers/redshift.js.map +1 -1
- package/dist/cjs/fetchers/rest-api.d.ts.map +1 -1
- package/dist/cjs/fetchers/rest-api.js +7 -2
- package/dist/cjs/fetchers/rest-api.js.map +1 -1
- package/dist/cjs/fetchers/teleport.d.ts.map +1 -1
- package/dist/cjs/fetchers/teleport.js +16 -6
- package/dist/cjs/fetchers/teleport.js.map +1 -1
- package/dist/cjs/fetchers/turso.d.ts.map +1 -1
- package/dist/cjs/fetchers/turso.js +1 -1
- package/dist/cjs/fetchers/turso.js.map +1 -1
- package/dist/cjs/fetchers/utils/browser-row-policy.d.ts +43 -0
- package/dist/cjs/fetchers/utils/browser-row-policy.d.ts.map +1 -0
- package/dist/cjs/fetchers/utils/browser-row-policy.js +66 -0
- package/dist/cjs/fetchers/utils/browser-row-policy.js.map +1 -0
- package/dist/cjs/fetchers/utils/request-sql-guards.d.ts +8 -0
- package/dist/cjs/fetchers/utils/request-sql-guards.d.ts.map +1 -0
- package/dist/cjs/fetchers/utils/request-sql-guards.js +12 -0
- package/dist/cjs/fetchers/utils/request-sql-guards.js.map +1 -0
- package/dist/cjs/fetchers/utils/rest-api-response-cache.d.ts +40 -0
- package/dist/cjs/fetchers/utils/rest-api-response-cache.d.ts.map +1 -0
- package/dist/cjs/fetchers/utils/rest-api-response-cache.js +64 -0
- package/dist/cjs/fetchers/utils/rest-api-response-cache.js.map +1 -0
- package/dist/cjs/fetchers/utils/table-access-guard.d.ts +23 -0
- package/dist/cjs/fetchers/utils/table-access-guard.d.ts.map +1 -0
- package/dist/cjs/fetchers/utils/table-access-guard.js +34 -0
- package/dist/cjs/fetchers/utils/table-access-guard.js.map +1 -0
- package/dist/cjs/index.d.ts +5 -0
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js +60 -8
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/items-path.d.ts +10 -0
- package/dist/cjs/items-path.d.ts.map +1 -0
- package/dist/cjs/items-path.js +86 -0
- package/dist/cjs/items-path.js.map +1 -0
- package/dist/cjs/loading-state.d.ts +16 -1
- package/dist/cjs/loading-state.d.ts.map +1 -1
- package/dist/cjs/loading-state.js +69 -3
- package/dist/cjs/loading-state.js.map +1 -1
- package/dist/cjs/localized-columns.d.ts +56 -0
- package/dist/cjs/localized-columns.d.ts.map +1 -0
- package/dist/cjs/localized-columns.js +137 -0
- package/dist/cjs/localized-columns.js.map +1 -0
- package/dist/cjs/pagination-plugin.d.ts.map +1 -1
- package/dist/cjs/pagination-plugin.js +140 -50
- package/dist/cjs/pagination-plugin.js.map +1 -1
- package/dist/cjs/product-filter-fields.d.ts +87 -0
- package/dist/cjs/product-filter-fields.d.ts.map +1 -0
- package/dist/cjs/product-filter-fields.js +161 -0
- package/dist/cjs/product-filter-fields.js.map +1 -0
- package/dist/cjs/request-locale.d.ts +49 -0
- package/dist/cjs/request-locale.d.ts.map +1 -0
- package/dist/cjs/request-locale.js +130 -0
- package/dist/cjs/request-locale.js.map +1 -0
- package/dist/cjs/search-clear.d.ts +31 -0
- package/dist/cjs/search-clear.d.ts.map +1 -0
- package/dist/cjs/search-clear.js +103 -0
- package/dist/cjs/search-clear.js.map +1 -0
- package/dist/cjs/transformations/blog-adjacent-posts.d.ts +16 -0
- package/dist/cjs/transformations/blog-adjacent-posts.d.ts.map +1 -0
- package/dist/cjs/transformations/blog-adjacent-posts.js +25 -0
- package/dist/cjs/transformations/blog-adjacent-posts.js.map +1 -0
- package/dist/cjs/transformations/blog-comments.d.ts +30 -0
- package/dist/cjs/transformations/blog-comments.d.ts.map +1 -0
- package/dist/cjs/transformations/blog-comments.js +34 -0
- package/dist/cjs/transformations/blog-comments.js.map +1 -0
- package/dist/cjs/transformations/blog-post.d.ts +2 -0
- package/dist/cjs/transformations/blog-post.d.ts.map +1 -1
- package/dist/cjs/transformations/blog-post.js +4 -1
- package/dist/cjs/transformations/blog-post.js.map +1 -1
- package/dist/cjs/transformations/ecommerce-product.d.ts +9 -1
- package/dist/cjs/transformations/ecommerce-product.d.ts.map +1 -1
- package/dist/cjs/transformations/ecommerce-product.js +28 -1
- package/dist/cjs/transformations/ecommerce-product.js.map +1 -1
- package/dist/cjs/transformations/heading-anchors.d.ts +25 -0
- package/dist/cjs/transformations/heading-anchors.d.ts.map +1 -0
- package/dist/cjs/transformations/heading-anchors.js +29 -0
- package/dist/cjs/transformations/heading-anchors.js.map +1 -0
- package/dist/cjs/transformations/index.d.ts +61 -4
- package/dist/cjs/transformations/index.d.ts.map +1 -1
- package/dist/cjs/transformations/index.js +69 -19
- package/dist/cjs/transformations/index.js.map +1 -1
- package/dist/cjs/transformations/shared-utils.d.ts.map +1 -1
- package/dist/cjs/transformations/shared-utils.js +1 -1
- package/dist/cjs/transformations/shared-utils.js.map +1 -1
- package/dist/cjs/tsconfig.tsbuildinfo +1 -1
- package/dist/cjs/utils.d.ts +11 -1
- package/dist/cjs/utils.d.ts.map +1 -1
- package/dist/cjs/utils.js +60 -16
- package/dist/cjs/utils.js.map +1 -1
- package/dist/esm/array-overlap-sql.d.ts +21 -0
- package/dist/esm/array-overlap-sql.d.ts.map +1 -0
- package/dist/esm/array-overlap-sql.js +21 -0
- package/dist/esm/array-overlap-sql.js.map +1 -0
- package/dist/esm/content-localization.d.ts +37 -0
- package/dist/esm/content-localization.d.ts.map +1 -0
- package/dist/esm/content-localization.js +24 -0
- package/dist/esm/content-localization.js.map +1 -0
- package/dist/esm/count-effect.d.ts +11 -1
- package/dist/esm/count-effect.d.ts.map +1 -1
- package/dist/esm/count-effect.js +28 -1
- package/dist/esm/count-effect.js.map +1 -1
- package/dist/esm/data-source-fetchers.d.ts.map +1 -1
- package/dist/esm/data-source-fetchers.js +11 -5
- package/dist/esm/data-source-fetchers.js.map +1 -1
- package/dist/esm/fetchers/airtable.d.ts.map +1 -1
- package/dist/esm/fetchers/airtable.js +1 -1
- package/dist/esm/fetchers/airtable.js.map +1 -1
- package/dist/esm/fetchers/clickhouse.d.ts.map +1 -1
- package/dist/esm/fetchers/clickhouse.js +1 -1
- package/dist/esm/fetchers/clickhouse.js.map +1 -1
- package/dist/esm/fetchers/firestore.d.ts.map +1 -1
- package/dist/esm/fetchers/firestore.js +1 -1
- package/dist/esm/fetchers/firestore.js.map +1 -1
- package/dist/esm/fetchers/javascript.d.ts.map +1 -1
- package/dist/esm/fetchers/javascript.js +1 -1
- package/dist/esm/fetchers/javascript.js.map +1 -1
- package/dist/esm/fetchers/mariadb.d.ts.map +1 -1
- package/dist/esm/fetchers/mariadb.js +1 -1
- package/dist/esm/fetchers/mariadb.js.map +1 -1
- package/dist/esm/fetchers/mongodb.d.ts.map +1 -1
- package/dist/esm/fetchers/mongodb.js +1 -1
- package/dist/esm/fetchers/mongodb.js.map +1 -1
- package/dist/esm/fetchers/mysql.d.ts.map +1 -1
- package/dist/esm/fetchers/mysql.js +1 -1
- package/dist/esm/fetchers/mysql.js.map +1 -1
- package/dist/esm/fetchers/postgresql.d.ts +1 -1
- package/dist/esm/fetchers/postgresql.d.ts.map +1 -1
- package/dist/esm/fetchers/postgresql.js +9 -4
- package/dist/esm/fetchers/postgresql.js.map +1 -1
- package/dist/esm/fetchers/raw-query.d.ts +8 -1
- package/dist/esm/fetchers/raw-query.d.ts.map +1 -1
- package/dist/esm/fetchers/raw-query.js +42 -4
- package/dist/esm/fetchers/raw-query.js.map +1 -1
- package/dist/esm/fetchers/redshift.d.ts.map +1 -1
- package/dist/esm/fetchers/redshift.js +1 -1
- package/dist/esm/fetchers/redshift.js.map +1 -1
- package/dist/esm/fetchers/rest-api.d.ts.map +1 -1
- package/dist/esm/fetchers/rest-api.js +7 -2
- package/dist/esm/fetchers/rest-api.js.map +1 -1
- package/dist/esm/fetchers/teleport.d.ts.map +1 -1
- package/dist/esm/fetchers/teleport.js +16 -6
- package/dist/esm/fetchers/teleport.js.map +1 -1
- package/dist/esm/fetchers/turso.d.ts.map +1 -1
- package/dist/esm/fetchers/turso.js +1 -1
- package/dist/esm/fetchers/turso.js.map +1 -1
- package/dist/esm/fetchers/utils/browser-row-policy.d.ts +43 -0
- package/dist/esm/fetchers/utils/browser-row-policy.d.ts.map +1 -0
- package/dist/esm/fetchers/utils/browser-row-policy.js +60 -0
- package/dist/esm/fetchers/utils/browser-row-policy.js.map +1 -0
- package/dist/esm/fetchers/utils/request-sql-guards.d.ts +8 -0
- package/dist/esm/fetchers/utils/request-sql-guards.d.ts.map +1 -0
- package/dist/esm/fetchers/utils/request-sql-guards.js +8 -0
- package/dist/esm/fetchers/utils/request-sql-guards.js.map +1 -0
- package/dist/esm/fetchers/utils/rest-api-response-cache.d.ts +40 -0
- package/dist/esm/fetchers/utils/rest-api-response-cache.d.ts.map +1 -0
- package/dist/esm/fetchers/utils/rest-api-response-cache.js +59 -0
- package/dist/esm/fetchers/utils/rest-api-response-cache.js.map +1 -0
- package/dist/esm/fetchers/utils/table-access-guard.d.ts +23 -0
- package/dist/esm/fetchers/utils/table-access-guard.d.ts.map +1 -0
- package/dist/esm/fetchers/utils/table-access-guard.js +29 -0
- package/dist/esm/fetchers/utils/table-access-guard.js.map +1 -0
- package/dist/esm/index.d.ts +5 -0
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +55 -7
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/items-path.d.ts +10 -0
- package/dist/esm/items-path.d.ts.map +1 -0
- package/dist/esm/items-path.js +58 -0
- package/dist/esm/items-path.js.map +1 -0
- package/dist/esm/loading-state.d.ts +16 -1
- package/dist/esm/loading-state.d.ts.map +1 -1
- package/dist/esm/loading-state.js +69 -3
- package/dist/esm/loading-state.js.map +1 -1
- package/dist/esm/localized-columns.d.ts +56 -0
- package/dist/esm/localized-columns.d.ts.map +1 -0
- package/dist/esm/localized-columns.js +132 -0
- package/dist/esm/localized-columns.js.map +1 -0
- package/dist/esm/pagination-plugin.d.ts.map +1 -1
- package/dist/esm/pagination-plugin.js +141 -51
- package/dist/esm/pagination-plugin.js.map +1 -1
- package/dist/esm/product-filter-fields.d.ts +87 -0
- package/dist/esm/product-filter-fields.d.ts.map +1 -0
- package/dist/esm/product-filter-fields.js +154 -0
- package/dist/esm/product-filter-fields.js.map +1 -0
- package/dist/esm/request-locale.d.ts +49 -0
- package/dist/esm/request-locale.d.ts.map +1 -0
- package/dist/esm/request-locale.js +98 -0
- package/dist/esm/request-locale.js.map +1 -0
- package/dist/esm/search-clear.d.ts +31 -0
- package/dist/esm/search-clear.d.ts.map +1 -0
- package/dist/esm/search-clear.js +74 -0
- package/dist/esm/search-clear.js.map +1 -0
- package/dist/esm/transformations/blog-adjacent-posts.d.ts +16 -0
- package/dist/esm/transformations/blog-adjacent-posts.d.ts.map +1 -0
- package/dist/esm/transformations/blog-adjacent-posts.js +21 -0
- package/dist/esm/transformations/blog-adjacent-posts.js.map +1 -0
- package/dist/esm/transformations/blog-comments.d.ts +30 -0
- package/dist/esm/transformations/blog-comments.d.ts.map +1 -0
- package/dist/esm/transformations/blog-comments.js +30 -0
- package/dist/esm/transformations/blog-comments.js.map +1 -0
- package/dist/esm/transformations/blog-post.d.ts +2 -0
- package/dist/esm/transformations/blog-post.d.ts.map +1 -1
- package/dist/esm/transformations/blog-post.js +4 -1
- package/dist/esm/transformations/blog-post.js.map +1 -1
- package/dist/esm/transformations/ecommerce-product.d.ts +9 -1
- package/dist/esm/transformations/ecommerce-product.d.ts.map +1 -1
- package/dist/esm/transformations/ecommerce-product.js +29 -2
- package/dist/esm/transformations/ecommerce-product.js.map +1 -1
- package/dist/esm/transformations/heading-anchors.d.ts +25 -0
- package/dist/esm/transformations/heading-anchors.d.ts.map +1 -0
- package/dist/esm/transformations/heading-anchors.js +25 -0
- package/dist/esm/transformations/heading-anchors.js.map +1 -0
- package/dist/esm/transformations/index.d.ts +61 -4
- package/dist/esm/transformations/index.d.ts.map +1 -1
- package/dist/esm/transformations/index.js +69 -19
- package/dist/esm/transformations/index.js.map +1 -1
- package/dist/esm/transformations/shared-utils.d.ts.map +1 -1
- package/dist/esm/transformations/shared-utils.js +1 -1
- package/dist/esm/transformations/shared-utils.js.map +1 -1
- package/dist/esm/tsconfig.tsbuildinfo +1 -1
- package/dist/esm/utils.d.ts +11 -1
- package/dist/esm/utils.d.ts.map +1 -1
- package/dist/esm/utils.js +60 -16
- package/dist/esm/utils.js.map +1 -1
- package/package.json +5 -5
- package/src/array-overlap-sql.ts +41 -0
- package/src/content-localization.ts +53 -0
- package/src/count-effect.ts +53 -2
- package/src/data-source-fetchers.ts +41 -5
- package/src/fetchers/airtable.ts +4 -2
- package/src/fetchers/clickhouse.ts +4 -2
- package/src/fetchers/firestore.ts +4 -2
- package/src/fetchers/javascript.ts +9 -1
- package/src/fetchers/mariadb.ts +4 -2
- package/src/fetchers/mongodb.ts +4 -2
- package/src/fetchers/mysql.ts +4 -2
- package/src/fetchers/postgresql.ts +53 -12
- package/src/fetchers/raw-query.ts +60 -5
- package/src/fetchers/redshift.ts +4 -2
- package/src/fetchers/rest-api.ts +27 -5
- package/src/fetchers/teleport.ts +85 -22
- package/src/fetchers/turso.ts +4 -2
- package/src/fetchers/utils/browser-row-policy.ts +212 -0
- package/src/fetchers/utils/request-sql-guards.ts +25 -0
- package/src/fetchers/utils/rest-api-response-cache.ts +188 -0
- package/src/fetchers/utils/table-access-guard.ts +39 -0
- package/src/index.ts +79 -11
- package/src/items-path.ts +74 -0
- package/src/loading-state.ts +134 -3
- package/src/localized-columns.ts +146 -0
- package/src/pagination-plugin.ts +206 -60
- package/src/product-filter-fields.ts +173 -0
- package/src/request-locale.ts +113 -0
- package/src/search-clear.ts +102 -0
- package/src/transformations/blog-adjacent-posts.ts +74 -0
- package/src/transformations/blog-comments.ts +132 -0
- package/src/transformations/blog-post.ts +38 -5
- package/src/transformations/ecommerce-product.ts +568 -5
- package/src/transformations/heading-anchors.ts +85 -0
- package/src/transformations/index.ts +191 -23
- package/src/transformations/shared-utils.ts +6 -0
- 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é & 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é & 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
|
+
})
|