@teleporthq/teleport-plugin-next-data-source 0.43.62 → 0.43.64
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__/blog-post-categories.test.ts +131 -0
- package/__tests__/custom-page-transform.test.ts +119 -0
- package/__tests__/ecommerce-product-ratings.test.ts +155 -0
- package/__tests__/ecommerce-product-storefront-contract.test.ts +147 -0
- package/__tests__/filter-groups.test.ts +174 -0
- package/__tests__/product-reviews-structured-data.test.ts +196 -0
- package/dist/cjs/data-source-fetchers.d.ts +3 -3
- package/dist/cjs/data-source-fetchers.d.ts.map +1 -1
- 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/csv-file.d.ts.map +1 -1
- package/dist/cjs/fetchers/csv-file.js +3 -3
- package/dist/cjs/fetchers/csv-file.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/google-sheets.d.ts.map +1 -1
- package/dist/cjs/fetchers/google-sheets.js +1 -1
- package/dist/cjs/fetchers/google-sheets.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.map +1 -1
- package/dist/cjs/fetchers/postgresql.js +1 -1
- package/dist/cjs/fetchers/postgresql.js.map +1 -1
- package/dist/cjs/fetchers/redis.d.ts.map +1 -1
- package/dist/cjs/fetchers/redis.js +1 -1
- package/dist/cjs/fetchers/redis.js.map +1 -1
- package/dist/cjs/fetchers/redshift.d.ts.map +1 -1
- package/dist/cjs/fetchers/redshift.js +2 -2
- 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 +2 -2
- package/dist/cjs/fetchers/rest-api.js.map +1 -1
- package/dist/cjs/fetchers/supabase.d.ts.map +1 -1
- package/dist/cjs/fetchers/supabase.js +1 -1
- package/dist/cjs/fetchers/supabase.js.map +1 -1
- package/dist/cjs/fetchers/teleport.d.ts +2 -2
- package/dist/cjs/fetchers/teleport.d.ts.map +1 -1
- package/dist/cjs/fetchers/teleport.js +1 -1
- 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/index.d.ts +1 -1
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/pagination-plugin.d.ts.map +1 -1
- package/dist/cjs/pagination-plugin.js.map +1 -1
- package/dist/cjs/transformations/blog-post.d.ts +7 -1
- package/dist/cjs/transformations/blog-post.d.ts.map +1 -1
- package/dist/cjs/transformations/blog-post.js +4 -2
- package/dist/cjs/transformations/blog-post.js.map +1 -1
- package/dist/cjs/transformations/category-taxonomy.d.ts +33 -0
- package/dist/cjs/transformations/category-taxonomy.d.ts.map +1 -0
- package/dist/cjs/transformations/category-taxonomy.js +42 -0
- package/dist/cjs/transformations/category-taxonomy.js.map +1 -0
- package/dist/cjs/transformations/custom-page.d.ts +18 -0
- package/dist/cjs/transformations/custom-page.d.ts.map +1 -0
- package/dist/cjs/transformations/custom-page.js +24 -0
- package/dist/cjs/transformations/custom-page.js.map +1 -0
- package/dist/cjs/transformations/ecommerce-product.d.ts.map +1 -1
- package/dist/cjs/transformations/ecommerce-product.js +2 -35
- package/dist/cjs/transformations/ecommerce-product.js.map +1 -1
- package/dist/cjs/transformations/index.d.ts +23 -4
- package/dist/cjs/transformations/index.d.ts.map +1 -1
- package/dist/cjs/transformations/index.js +38 -8
- 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 +35 -3
- package/dist/cjs/utils.d.ts.map +1 -1
- package/dist/cjs/utils.js +116 -18
- package/dist/cjs/utils.js.map +1 -1
- package/dist/esm/data-source-fetchers.d.ts +3 -3
- package/dist/esm/data-source-fetchers.d.ts.map +1 -1
- 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 +2 -2
- 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 +2 -2
- package/dist/esm/fetchers/clickhouse.js.map +1 -1
- package/dist/esm/fetchers/csv-file.d.ts.map +1 -1
- package/dist/esm/fetchers/csv-file.js +4 -4
- package/dist/esm/fetchers/csv-file.js.map +1 -1
- package/dist/esm/fetchers/firestore.d.ts.map +1 -1
- package/dist/esm/fetchers/firestore.js +2 -2
- package/dist/esm/fetchers/firestore.js.map +1 -1
- package/dist/esm/fetchers/google-sheets.d.ts.map +1 -1
- package/dist/esm/fetchers/google-sheets.js +2 -2
- package/dist/esm/fetchers/google-sheets.js.map +1 -1
- package/dist/esm/fetchers/javascript.d.ts.map +1 -1
- package/dist/esm/fetchers/javascript.js +2 -2
- 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 +2 -2
- 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 +2 -2
- 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 +2 -2
- package/dist/esm/fetchers/mysql.js.map +1 -1
- package/dist/esm/fetchers/postgresql.d.ts.map +1 -1
- package/dist/esm/fetchers/postgresql.js +2 -2
- package/dist/esm/fetchers/postgresql.js.map +1 -1
- package/dist/esm/fetchers/redis.d.ts.map +1 -1
- package/dist/esm/fetchers/redis.js +2 -2
- package/dist/esm/fetchers/redis.js.map +1 -1
- package/dist/esm/fetchers/redshift.d.ts.map +1 -1
- package/dist/esm/fetchers/redshift.js +3 -3
- 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 +3 -3
- package/dist/esm/fetchers/rest-api.js.map +1 -1
- package/dist/esm/fetchers/supabase.d.ts.map +1 -1
- package/dist/esm/fetchers/supabase.js +2 -2
- package/dist/esm/fetchers/supabase.js.map +1 -1
- package/dist/esm/fetchers/teleport.d.ts +2 -2
- package/dist/esm/fetchers/teleport.d.ts.map +1 -1
- package/dist/esm/fetchers/teleport.js +2 -2
- 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 +2 -2
- package/dist/esm/fetchers/turso.js.map +1 -1
- package/dist/esm/index.d.ts +1 -1
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/pagination-plugin.d.ts.map +1 -1
- package/dist/esm/pagination-plugin.js +1 -1
- package/dist/esm/pagination-plugin.js.map +1 -1
- package/dist/esm/transformations/blog-post.d.ts +7 -1
- package/dist/esm/transformations/blog-post.d.ts.map +1 -1
- package/dist/esm/transformations/blog-post.js +4 -2
- package/dist/esm/transformations/blog-post.js.map +1 -1
- package/dist/esm/transformations/category-taxonomy.d.ts +33 -0
- package/dist/esm/transformations/category-taxonomy.d.ts.map +1 -0
- package/dist/esm/transformations/category-taxonomy.js +37 -0
- package/dist/esm/transformations/category-taxonomy.js.map +1 -0
- package/dist/esm/transformations/custom-page.d.ts +18 -0
- package/dist/esm/transformations/custom-page.d.ts.map +1 -0
- package/dist/esm/transformations/custom-page.js +20 -0
- package/dist/esm/transformations/custom-page.js.map +1 -0
- package/dist/esm/transformations/ecommerce-product.d.ts.map +1 -1
- package/dist/esm/transformations/ecommerce-product.js +3 -36
- package/dist/esm/transformations/ecommerce-product.js.map +1 -1
- package/dist/esm/transformations/index.d.ts +23 -4
- package/dist/esm/transformations/index.d.ts.map +1 -1
- package/dist/esm/transformations/index.js +37 -7
- 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 +35 -3
- package/dist/esm/utils.d.ts.map +1 -1
- package/dist/esm/utils.js +112 -17
- package/dist/esm/utils.js.map +1 -1
- package/package.json +5 -5
- package/src/data-source-fetchers.ts +3 -3
- package/src/fetchers/airtable.ts +35 -36
- package/src/fetchers/clickhouse.ts +26 -36
- package/src/fetchers/csv-file.ts +13 -21
- package/src/fetchers/firestore.ts +41 -35
- package/src/fetchers/google-sheets.ts +15 -23
- package/src/fetchers/javascript.ts +11 -21
- package/src/fetchers/mariadb.ts +33 -44
- package/src/fetchers/mongodb.ts +66 -81
- package/src/fetchers/mysql.ts +33 -45
- package/src/fetchers/postgresql.ts +52 -62
- package/src/fetchers/redis.ts +6 -5
- package/src/fetchers/redshift.ts +29 -41
- package/src/fetchers/rest-api.ts +11 -21
- package/src/fetchers/supabase.ts +104 -41
- package/src/fetchers/teleport.ts +55 -66
- package/src/fetchers/turso.ts +27 -39
- package/src/index.ts +1 -1
- package/src/pagination-plugin.ts +3 -6
- package/src/transformations/blog-post.ts +28 -9
- package/src/transformations/category-taxonomy.ts +107 -0
- package/src/transformations/custom-page.ts +83 -0
- package/src/transformations/ecommerce-product.ts +177 -78
- package/src/transformations/index.ts +85 -11
- package/src/transformations/shared-utils.ts +9 -0
- package/src/utils.ts +191 -34
package/src/fetchers/redshift.ts
CHANGED
|
@@ -2,6 +2,7 @@ import {
|
|
|
2
2
|
replaceSecretReference,
|
|
3
3
|
generateDateFormatterCode,
|
|
4
4
|
generateSafeJSONParseCode,
|
|
5
|
+
generateFilterTreeHelpersCode,
|
|
5
6
|
generateSearchEscapeHelpersCode,
|
|
6
7
|
} from '../utils'
|
|
7
8
|
|
|
@@ -56,6 +57,8 @@ const getClient = () => {
|
|
|
56
57
|
|
|
57
58
|
${generateSafeJSONParseCode()}
|
|
58
59
|
|
|
60
|
+
${generateFilterTreeHelpersCode()}
|
|
61
|
+
|
|
59
62
|
${generateSearchEscapeHelpersCode()}
|
|
60
63
|
|
|
61
64
|
${generateDateFormatterCode()}
|
|
@@ -119,54 +122,39 @@ export default async function handler(req, res) {
|
|
|
119
122
|
}
|
|
120
123
|
|
|
121
124
|
if (filters) {
|
|
122
|
-
const
|
|
125
|
+
const filterTree = normalizeFilterTree(safeJSONParse(filters))
|
|
123
126
|
|
|
124
|
-
if (
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
const
|
|
129
|
-
const value = filter.destination
|
|
130
|
-
const operand = filter.operand || '='
|
|
127
|
+
if (filterTree) {
|
|
128
|
+
const buildCondition = (condition) => {
|
|
129
|
+
const field = sanitizeIdentifier(condition.source)
|
|
130
|
+
const value = condition.destination
|
|
131
|
+
const operand = condition.operand
|
|
131
132
|
|
|
132
133
|
if (Array.isArray(value)) {
|
|
133
|
-
if (value.length === 0) return
|
|
134
|
+
if (value.length === 0) return null
|
|
134
135
|
const placeholders = value.map(() => \`$\${paramIndex++}\`)
|
|
135
136
|
queryParams.push(...value)
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
conditions.push(\`\${field} IN (\${placeholders.join(', ')})\`)
|
|
140
|
-
}
|
|
141
|
-
} else {
|
|
142
|
-
if (value === null) {
|
|
143
|
-
if (operand === '=') {
|
|
144
|
-
conditions.push(\`\${field} IS NULL\`)
|
|
145
|
-
} else if (operand === '!=') {
|
|
146
|
-
conditions.push(\`\${field} IS NOT NULL\`)
|
|
147
|
-
}
|
|
148
|
-
} else {
|
|
149
|
-
const validOps = ['=', '!=', '>', '<', '>=', '<=']
|
|
150
|
-
const sqlOperator = validOps.includes(operand) ? operand : '='
|
|
151
|
-
conditions.push(\`\${field} \${sqlOperator} $\${paramIndex}\`)
|
|
152
|
-
queryParams.push(value)
|
|
153
|
-
paramIndex++
|
|
154
|
-
}
|
|
137
|
+
return operand === '!='
|
|
138
|
+
? \`\${field} NOT IN (\${placeholders.join(', ')})\`
|
|
139
|
+
: \`\${field} IN (\${placeholders.join(', ')})\`
|
|
155
140
|
}
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
const placeholders = value.map(() => \`$\${paramIndex++}\`)
|
|
162
|
-
queryParams.push(...value)
|
|
163
|
-
conditions.push(\`\${field} IN (\${placeholders.join(', ')})\`)
|
|
164
|
-
} else {
|
|
165
|
-
conditions.push(\`\${field} = $\${paramIndex}\`)
|
|
166
|
-
queryParams.push(value)
|
|
167
|
-
paramIndex++
|
|
141
|
+
|
|
142
|
+
if (value === null) {
|
|
143
|
+
if (operand === '=') return \`\${field} IS NULL\`
|
|
144
|
+
if (operand === '!=') return \`\${field} IS NOT NULL\`
|
|
145
|
+
return null
|
|
168
146
|
}
|
|
169
|
-
|
|
147
|
+
|
|
148
|
+
const validOps = ['=', '!=', '>', '<', '>=', '<=']
|
|
149
|
+
const sqlOperator = validOps.includes(operand) ? operand : '='
|
|
150
|
+
const clause = \`\${field} \${sqlOperator} $\${paramIndex}\`
|
|
151
|
+
queryParams.push(value)
|
|
152
|
+
paramIndex++
|
|
153
|
+
return clause
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const clause = buildFilterTreeClause(filterTree, buildCondition)
|
|
157
|
+
if (clause) conditions.push(clause)
|
|
170
158
|
}
|
|
171
159
|
}
|
|
172
160
|
|
package/src/fetchers/rest-api.ts
CHANGED
|
@@ -2,6 +2,7 @@ import {
|
|
|
2
2
|
generateDateFormatterCode,
|
|
3
3
|
generateSortFilterHelperCode,
|
|
4
4
|
generateSafeJSONParseCode,
|
|
5
|
+
generateFilterTreeHelpersCode,
|
|
5
6
|
} from '../utils'
|
|
6
7
|
|
|
7
8
|
export const validateRESTAPIConfig = (
|
|
@@ -87,6 +88,8 @@ ${generateDateFormatterCode()}
|
|
|
87
88
|
|
|
88
89
|
${generateSortFilterHelperCode()}
|
|
89
90
|
|
|
91
|
+
${generateFilterTreeHelpersCode()}
|
|
92
|
+
|
|
90
93
|
export default async function handler(req, res) {
|
|
91
94
|
try {
|
|
92
95
|
const { query, queryColumns, limit, page, perPage, sortBy, sortOrder, filters, sorts, offset } = req.query
|
|
@@ -160,17 +163,14 @@ export default async function handler(req, res) {
|
|
|
160
163
|
|
|
161
164
|
if (filters) {
|
|
162
165
|
try {
|
|
163
|
-
const
|
|
166
|
+
const filterTree = normalizeFilterTree(safeJSONParse(filters))
|
|
164
167
|
|
|
165
|
-
if (
|
|
166
|
-
data = data.filter((item) =>
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
const
|
|
171
|
-
const value = getNestedValue(item, field)
|
|
172
|
-
const target = filter.destination
|
|
173
|
-
const operand = filter.operand || '='
|
|
168
|
+
if (filterTree) {
|
|
169
|
+
data = data.filter((item) =>
|
|
170
|
+
evaluateFilterTree(filterTree, (condition) => {
|
|
171
|
+
const value = getNestedValue(item, condition.source)
|
|
172
|
+
const target = condition.destination
|
|
173
|
+
const operand = condition.operand
|
|
174
174
|
|
|
175
175
|
if (Array.isArray(target)) {
|
|
176
176
|
if (operand === '!=') {
|
|
@@ -181,17 +181,7 @@ export default async function handler(req, res) {
|
|
|
181
181
|
|
|
182
182
|
return compareValues(value, target, operand)
|
|
183
183
|
})
|
|
184
|
-
|
|
185
|
-
} else {
|
|
186
|
-
data = data.filter((item) => {
|
|
187
|
-
return Object.entries(parsedFilters).every(([key, value]) => {
|
|
188
|
-
const itemValue = getNestedValue(item, key)
|
|
189
|
-
if (Array.isArray(value)) {
|
|
190
|
-
return value.includes(itemValue)
|
|
191
|
-
}
|
|
192
|
-
return compareValues(itemValue, value, '=')
|
|
193
|
-
})
|
|
194
|
-
})
|
|
184
|
+
)
|
|
195
185
|
}
|
|
196
186
|
} catch (err) {
|
|
197
187
|
console.error('Error parsing filters:', err)
|
package/src/fetchers/supabase.ts
CHANGED
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
generateDateFormatterCode,
|
|
4
4
|
generateSafeJSONParseCode,
|
|
5
5
|
generateSearchEscapeHelpersCode,
|
|
6
|
+
generateFilterTreeHelpersCode,
|
|
6
7
|
} from '../utils'
|
|
7
8
|
|
|
8
9
|
export const validateSupabaseConfig = (
|
|
@@ -127,6 +128,8 @@ const getClient = () => {
|
|
|
127
128
|
|
|
128
129
|
${generateSafeJSONParseCode()}
|
|
129
130
|
|
|
131
|
+
${generateFilterTreeHelpersCode()}
|
|
132
|
+
|
|
130
133
|
${generateSearchEscapeHelpersCode()}
|
|
131
134
|
|
|
132
135
|
// Helper function to process filter values
|
|
@@ -144,12 +147,12 @@ const applyFilters = (queryRef, filters) => {
|
|
|
144
147
|
const parsedFilters = safeJSONParse(filters)
|
|
145
148
|
|
|
146
149
|
if (Array.isArray(parsedFilters)) {
|
|
147
|
-
parsedFilters
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
const field =
|
|
151
|
-
const value =
|
|
152
|
-
const operand =
|
|
150
|
+
const filterTree = normalizeFilterTree(parsedFilters)
|
|
151
|
+
|
|
152
|
+
const applyCondition = (condition) => {
|
|
153
|
+
const field = condition.source
|
|
154
|
+
const value = condition.destination
|
|
155
|
+
const operand = condition.operand
|
|
153
156
|
|
|
154
157
|
if (Array.isArray(value)) {
|
|
155
158
|
const processedValues = value.map(processFilterValue)
|
|
@@ -158,43 +161,103 @@ const applyFilters = (queryRef, filters) => {
|
|
|
158
161
|
} else {
|
|
159
162
|
queryRef = queryRef.in(field, processedValues)
|
|
160
163
|
}
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
// Map operand to Supabase methods
|
|
173
|
-
switch (operand) {
|
|
174
|
-
case '=':
|
|
175
|
-
queryRef = queryRef.eq(field, processedValue)
|
|
176
|
-
break
|
|
177
|
-
case '!=':
|
|
178
|
-
queryRef = queryRef.neq(field, processedValue)
|
|
179
|
-
break
|
|
180
|
-
case '>':
|
|
181
|
-
queryRef = queryRef.gt(field, processedValue)
|
|
182
|
-
break
|
|
183
|
-
case '>=':
|
|
184
|
-
queryRef = queryRef.gte(field, processedValue)
|
|
185
|
-
break
|
|
186
|
-
case '<':
|
|
187
|
-
queryRef = queryRef.lt(field, processedValue)
|
|
188
|
-
break
|
|
189
|
-
case '<=':
|
|
190
|
-
queryRef = queryRef.lte(field, processedValue)
|
|
191
|
-
break
|
|
192
|
-
default:
|
|
193
|
-
queryRef = queryRef.eq(field, processedValue)
|
|
194
|
-
}
|
|
164
|
+
return
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const processedValue = processFilterValue(value)
|
|
168
|
+
|
|
169
|
+
// Handle null values
|
|
170
|
+
if (processedValue === null) {
|
|
171
|
+
if (operand === '=') {
|
|
172
|
+
queryRef = queryRef.is(field, null)
|
|
173
|
+
} else if (operand === '!=') {
|
|
174
|
+
queryRef = queryRef.not(field, 'is', null)
|
|
195
175
|
}
|
|
176
|
+
return
|
|
196
177
|
}
|
|
197
|
-
|
|
178
|
+
|
|
179
|
+
// Map operand to Supabase methods
|
|
180
|
+
switch (operand) {
|
|
181
|
+
case '!=':
|
|
182
|
+
queryRef = queryRef.neq(field, processedValue)
|
|
183
|
+
break
|
|
184
|
+
case '>':
|
|
185
|
+
queryRef = queryRef.gt(field, processedValue)
|
|
186
|
+
break
|
|
187
|
+
case '>=':
|
|
188
|
+
queryRef = queryRef.gte(field, processedValue)
|
|
189
|
+
break
|
|
190
|
+
case '<':
|
|
191
|
+
queryRef = queryRef.lt(field, processedValue)
|
|
192
|
+
break
|
|
193
|
+
case '<=':
|
|
194
|
+
queryRef = queryRef.lte(field, processedValue)
|
|
195
|
+
break
|
|
196
|
+
default:
|
|
197
|
+
queryRef = queryRef.eq(field, processedValue)
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// An OR group cannot be expressed by chaining (chained filters are ANDed),
|
|
202
|
+
// so it is emitted as one PostgREST or=(...) expression instead.
|
|
203
|
+
const POSTGREST_OPERANDS = { '=': 'eq', '!=': 'neq', '>': 'gt', '>=': 'gte', '<': 'lt', '<=': 'lte' }
|
|
204
|
+
|
|
205
|
+
const toExprValue = (value) => {
|
|
206
|
+
if (value === null || value === undefined) return 'null'
|
|
207
|
+
const asString = String(value)
|
|
208
|
+
return /[,.:()"\\s]/.test(asString) ? '"' + asString.replace(/"/g, '\\\\"') + '"' : asString
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
const conditionToExpr = (condition) => {
|
|
212
|
+
const field = condition.source
|
|
213
|
+
const value = condition.destination
|
|
214
|
+
const operand = condition.operand
|
|
215
|
+
|
|
216
|
+
if (Array.isArray(value)) {
|
|
217
|
+
const list = '(' + value.map((entry) => toExprValue(processFilterValue(entry))).join(',') + ')'
|
|
218
|
+
return operand === '!=' ? field + '.not.in.' + list : field + '.in.' + list
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
const processedValue = processFilterValue(value)
|
|
222
|
+
if (processedValue === null) {
|
|
223
|
+
return operand === '!=' ? field + '.not.is.null' : field + '.is.null'
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
return field + '.' + (POSTGREST_OPERANDS[operand] || 'eq') + '.' + toExprValue(processedValue)
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
const nodeToExpr = (node) => {
|
|
230
|
+
if (node.type === 'group') {
|
|
231
|
+
const parts = node.children.map(nodeToExpr).filter(Boolean)
|
|
232
|
+
if (parts.length === 0) return null
|
|
233
|
+
if (parts.length === 1) return parts[0]
|
|
234
|
+
return (node.operator === 'or' ? 'or' : 'and') + '(' + parts.join(',') + ')'
|
|
235
|
+
}
|
|
236
|
+
return conditionToExpr(node)
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
const applyNode = (node) => {
|
|
240
|
+
if (node.type !== 'group') {
|
|
241
|
+
applyCondition(node)
|
|
242
|
+
return
|
|
243
|
+
}
|
|
244
|
+
if (node.operator === 'and') {
|
|
245
|
+
node.children.forEach(applyNode)
|
|
246
|
+
return
|
|
247
|
+
}
|
|
248
|
+
if (node.children.length === 1) {
|
|
249
|
+
applyNode(node.children[0])
|
|
250
|
+
return
|
|
251
|
+
}
|
|
252
|
+
const parts = node.children.map(nodeToExpr).filter(Boolean)
|
|
253
|
+
if (parts.length > 0) {
|
|
254
|
+
queryRef = queryRef.or(parts.join(','))
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
if (filterTree) {
|
|
259
|
+
applyNode(filterTree)
|
|
260
|
+
}
|
|
198
261
|
} else {
|
|
199
262
|
// Old format: object with key-value pairs (backward compatibility)
|
|
200
263
|
Object.entries(parsedFilters).forEach(([key, value]) => {
|
package/src/fetchers/teleport.ts
CHANGED
|
@@ -2,13 +2,14 @@ import {
|
|
|
2
2
|
replaceSecretReference,
|
|
3
3
|
generateDateFormatterCode,
|
|
4
4
|
generateSafeJSONParseCode,
|
|
5
|
+
generateFilterTreeHelpersCode,
|
|
5
6
|
generateSearchEscapeHelpersCode,
|
|
6
7
|
} from '../utils'
|
|
7
8
|
import {
|
|
8
9
|
getTransformationCode,
|
|
9
10
|
getTransformExpression,
|
|
10
11
|
getTransformWrapperCode,
|
|
11
|
-
type
|
|
12
|
+
type EntityTransformOptions,
|
|
12
13
|
} from '../transformations'
|
|
13
14
|
import {
|
|
14
15
|
generateSortFallbackFieldHelper,
|
|
@@ -57,7 +58,7 @@ export const validateTeleportConfig = (
|
|
|
57
58
|
export const generateTeleportFetcher = (
|
|
58
59
|
config: Record<string, unknown>,
|
|
59
60
|
tableName: string,
|
|
60
|
-
transformOptions:
|
|
61
|
+
transformOptions: EntityTransformOptions = {}
|
|
61
62
|
): string => {
|
|
62
63
|
const dbConfig = config as TeleportDBConfig
|
|
63
64
|
const schema = dbConfig.options?.schema
|
|
@@ -146,81 +147,69 @@ const getClient = () => {
|
|
|
146
147
|
|
|
147
148
|
${generateSafeJSONParseCode()}
|
|
148
149
|
|
|
150
|
+
${generateFilterTreeHelpersCode()}
|
|
151
|
+
|
|
149
152
|
${generateSearchEscapeHelpersCode()}
|
|
150
153
|
${getTransformationCode(tableName, transformOptions)}
|
|
151
154
|
${getTransformWrapperCode(tableName)}
|
|
152
155
|
const processFilters = (filters, conditions, queryParams, paramIndex) => {
|
|
153
156
|
if (!filters) return paramIndex
|
|
154
157
|
|
|
155
|
-
const
|
|
158
|
+
const filterTree = normalizeFilterTree(safeJSONParse(filters))
|
|
159
|
+
if (!filterTree) return paramIndex
|
|
156
160
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
// form) avoids the '?' placeholder-token ambiguity some drivers hit.
|
|
171
|
-
conditions.push(\`jsonb_exists_any(NULLIF(\${field}, '')::jsonb, $\${paramIndex}::text[])\`)
|
|
172
|
-
queryParams.push(value.map((entry) => String(entry)))
|
|
173
|
-
paramIndex++
|
|
174
|
-
return
|
|
175
|
-
}
|
|
176
|
-
const placeholders = value.map(() => \`$\${paramIndex++}\`)
|
|
177
|
-
queryParams.push(...value)
|
|
178
|
-
if (operand === '!=') {
|
|
179
|
-
conditions.push(\`\${field} NOT IN (\${placeholders.join(', ')})\`)
|
|
180
|
-
} else {
|
|
181
|
-
conditions.push(\`\${field} IN (\${placeholders.join(', ')})\`)
|
|
182
|
-
}
|
|
183
|
-
} else {
|
|
184
|
-
if (operand === 'array_overlap') {
|
|
185
|
-
if (value === '' || value === null || value === undefined) return
|
|
186
|
-
// A single comma-joined string (the multi-select Category Filter's
|
|
187
|
-
// ?categoryFilter=a,b,c) expands to multiple ids; one id stays one.
|
|
188
|
-
const overlapValues = String(value).split(',').map((entry) => entry.trim()).filter(Boolean)
|
|
189
|
-
if (overlapValues.length === 0) return
|
|
190
|
-
conditions.push(\`jsonb_exists_any(NULLIF(\${field}, '')::jsonb, $\${paramIndex}::text[])\`)
|
|
191
|
-
queryParams.push(overlapValues)
|
|
192
|
-
paramIndex++
|
|
193
|
-
return
|
|
194
|
-
}
|
|
195
|
-
if (value === null) {
|
|
196
|
-
if (operand === '=') {
|
|
197
|
-
conditions.push(\`\${field} IS NULL\`)
|
|
198
|
-
} else if (operand === '!=') {
|
|
199
|
-
conditions.push(\`\${field} IS NOT NULL\`)
|
|
200
|
-
}
|
|
201
|
-
} else {
|
|
202
|
-
const validOps = ['=', '!=', '>', '<', '>=', '<=']
|
|
203
|
-
const sqlOperator = validOps.includes(operand) ? operand : '='
|
|
204
|
-
conditions.push(\`\${field} \${sqlOperator} $\${paramIndex}\`)
|
|
205
|
-
queryParams.push(value)
|
|
206
|
-
paramIndex++
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
})
|
|
210
|
-
} else {
|
|
211
|
-
Object.entries(parsedFilters).forEach(([key, value]) => {
|
|
212
|
-
if (Array.isArray(value)) {
|
|
213
|
-
const placeholders = value.map(() => \`$\${paramIndex++}\`)
|
|
214
|
-
queryParams.push(...value)
|
|
215
|
-
conditions.push(\`\${key} IN (\${placeholders.join(', ')})\`)
|
|
216
|
-
} else {
|
|
217
|
-
conditions.push(\`\${key} = $\${paramIndex}\`)
|
|
218
|
-
queryParams.push(value)
|
|
161
|
+
const buildCondition = (condition) => {
|
|
162
|
+
const field = condition.source
|
|
163
|
+
const value = condition.destination
|
|
164
|
+
const operand = condition.operand
|
|
165
|
+
|
|
166
|
+
if (Array.isArray(value)) {
|
|
167
|
+
if (value.length === 0) return null
|
|
168
|
+
if (operand === 'array_overlap') {
|
|
169
|
+
// Row column is a JSON array (e.g. category_ids). Match when it
|
|
170
|
+
// shares any element with the destination set. jsonb ?| (function
|
|
171
|
+
// form) avoids the '?' placeholder-token ambiguity some drivers hit.
|
|
172
|
+
const clause = \`jsonb_exists_any(NULLIF(\${field}, '')::jsonb, $\${paramIndex}::text[])\`
|
|
173
|
+
queryParams.push(value.map((entry) => String(entry)))
|
|
219
174
|
paramIndex++
|
|
175
|
+
return clause
|
|
220
176
|
}
|
|
221
|
-
|
|
177
|
+
const placeholders = value.map(() => \`$\${paramIndex++}\`)
|
|
178
|
+
queryParams.push(...value)
|
|
179
|
+
return operand === '!='
|
|
180
|
+
? \`\${field} NOT IN (\${placeholders.join(', ')})\`
|
|
181
|
+
: \`\${field} IN (\${placeholders.join(', ')})\`
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
if (operand === 'array_overlap') {
|
|
185
|
+
if (value === '' || value === null || value === undefined) return null
|
|
186
|
+
// A single comma-joined string (the multi-select Category Filter's
|
|
187
|
+
// ?categoryFilter=a,b,c) expands to multiple ids; one id stays one.
|
|
188
|
+
const overlapValues = String(value).split(',').map((entry) => entry.trim()).filter(Boolean)
|
|
189
|
+
if (overlapValues.length === 0) return null
|
|
190
|
+
const clause = \`jsonb_exists_any(NULLIF(\${field}, '')::jsonb, $\${paramIndex}::text[])\`
|
|
191
|
+
queryParams.push(overlapValues)
|
|
192
|
+
paramIndex++
|
|
193
|
+
return clause
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
if (value === null) {
|
|
197
|
+
if (operand === '=') return \`\${field} IS NULL\`
|
|
198
|
+
if (operand === '!=') return \`\${field} IS NOT NULL\`
|
|
199
|
+
return null
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const validOps = ['=', '!=', '>', '<', '>=', '<=']
|
|
203
|
+
const sqlOperator = validOps.includes(operand) ? operand : '='
|
|
204
|
+
const clause = \`\${field} \${sqlOperator} $\${paramIndex}\`
|
|
205
|
+
queryParams.push(value)
|
|
206
|
+
paramIndex++
|
|
207
|
+
return clause
|
|
222
208
|
}
|
|
223
209
|
|
|
210
|
+
const clause = buildFilterTreeClause(filterTree, buildCondition)
|
|
211
|
+
if (clause) conditions.push(clause)
|
|
212
|
+
|
|
224
213
|
return paramIndex
|
|
225
214
|
}
|
|
226
215
|
|
package/src/fetchers/turso.ts
CHANGED
|
@@ -2,6 +2,7 @@ import {
|
|
|
2
2
|
replaceSecretReference,
|
|
3
3
|
generateDateFormatterCode,
|
|
4
4
|
generateSafeJSONParseCode,
|
|
5
|
+
generateFilterTreeHelpersCode,
|
|
5
6
|
generateSearchEscapeHelpersCode,
|
|
6
7
|
} from '../utils'
|
|
7
8
|
|
|
@@ -42,6 +43,8 @@ export const generateTursoFetcher = (
|
|
|
42
43
|
|
|
43
44
|
${generateSafeJSONParseCode()}
|
|
44
45
|
|
|
46
|
+
${generateFilterTreeHelpersCode()}
|
|
47
|
+
|
|
45
48
|
${generateSearchEscapeHelpersCode()}
|
|
46
49
|
|
|
47
50
|
${generateDateFormatterCode()}
|
|
@@ -92,52 +95,37 @@ export default async function handler(req, res) {
|
|
|
92
95
|
}
|
|
93
96
|
|
|
94
97
|
if (filters) {
|
|
95
|
-
const
|
|
98
|
+
const filterTree = normalizeFilterTree(safeJSONParse(filters))
|
|
96
99
|
|
|
97
|
-
if (
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
const
|
|
102
|
-
const value = filter.destination
|
|
103
|
-
const operand = filter.operand || '='
|
|
100
|
+
if (filterTree) {
|
|
101
|
+
const buildCondition = (condition) => {
|
|
102
|
+
const field = sanitizeIdentifier(condition.source)
|
|
103
|
+
const value = condition.destination
|
|
104
|
+
const operand = condition.operand
|
|
104
105
|
|
|
105
106
|
if (Array.isArray(value)) {
|
|
106
|
-
if (value.length === 0) return
|
|
107
|
+
if (value.length === 0) return null
|
|
107
108
|
const placeholders = value.map(() => '?').join(', ')
|
|
108
109
|
queryParams.push(...value)
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
whereClauses.push(\`\${field} IN (\${placeholders})\`)
|
|
113
|
-
}
|
|
114
|
-
} else {
|
|
115
|
-
if (value === null) {
|
|
116
|
-
if (operand === '=') {
|
|
117
|
-
whereClauses.push(\`\${field} IS NULL\`)
|
|
118
|
-
} else if (operand === '!=') {
|
|
119
|
-
whereClauses.push(\`\${field} IS NOT NULL\`)
|
|
120
|
-
}
|
|
121
|
-
} else {
|
|
122
|
-
const validOps = ['=', '!=', '>', '<', '>=', '<=']
|
|
123
|
-
const sqlOperator = validOps.includes(operand) ? operand : '='
|
|
124
|
-
whereClauses.push(\`\${field} \${sqlOperator} ?\`)
|
|
125
|
-
queryParams.push(value)
|
|
126
|
-
}
|
|
110
|
+
return operand === '!='
|
|
111
|
+
? \`\${field} NOT IN (\${placeholders})\`
|
|
112
|
+
: \`\${field} IN (\${placeholders})\`
|
|
127
113
|
}
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
const placeholders = value.map(() => '?').join(', ')
|
|
134
|
-
queryParams.push(...value)
|
|
135
|
-
whereClauses.push(\`\${field} IN (\${placeholders})\`)
|
|
136
|
-
} else {
|
|
137
|
-
whereClauses.push(\`\${field} = ?\`)
|
|
138
|
-
queryParams.push(value)
|
|
114
|
+
|
|
115
|
+
if (value === null) {
|
|
116
|
+
if (operand === '=') return \`\${field} IS NULL\`
|
|
117
|
+
if (operand === '!=') return \`\${field} IS NOT NULL\`
|
|
118
|
+
return null
|
|
139
119
|
}
|
|
140
|
-
|
|
120
|
+
|
|
121
|
+
const validOps = ['=', '!=', '>', '<', '>=', '<=']
|
|
122
|
+
const sqlOperator = validOps.includes(operand) ? operand : '='
|
|
123
|
+
queryParams.push(value)
|
|
124
|
+
return \`\${field} \${sqlOperator} ?\`
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const clause = buildFilterTreeClause(filterTree, buildCondition)
|
|
128
|
+
if (clause) whereClauses.push(clause)
|
|
141
129
|
}
|
|
142
130
|
}
|
|
143
131
|
|
package/src/index.ts
CHANGED
|
@@ -1403,4 +1403,4 @@ export * from './count-fetchers'
|
|
|
1403
1403
|
// tax rate) — every package that emits a data-source fetcher has to derive
|
|
1404
1404
|
// them the same way.
|
|
1405
1405
|
export { buildProductTransformOptions } from './transformations'
|
|
1406
|
-
export type { EcommerceProductTransformOptions } from './transformations'
|
|
1406
|
+
export type { EcommerceProductTransformOptions, EntityTransformOptions } from './transformations'
|
package/src/pagination-plugin.ts
CHANGED
|
@@ -10,10 +10,7 @@ import { ASTStatementOrder, GenericUtils, StringUtils } from '@teleporthq/telepo
|
|
|
10
10
|
import { ASTUtils, URLQueryWriter, URLSearchParamSync } from '@teleporthq/teleport-plugin-common'
|
|
11
11
|
import { generateSafeFileName } from './utils'
|
|
12
12
|
import { generateDataSourceFetcherWithCore } from './data-source-fetchers'
|
|
13
|
-
import {
|
|
14
|
-
buildProductTransformOptions,
|
|
15
|
-
type EcommerceProductTransformOptions,
|
|
16
|
-
} from './transformations'
|
|
13
|
+
import { buildProductTransformOptions, type EntityTransformOptions } from './transformations'
|
|
17
14
|
import { appendSortsParam, DynamicSortAST, extractDynamicSort } from './sort-utils'
|
|
18
15
|
import { appendFiltersParam, pushStateIdsAsDeps, pushPropIdsAsDeps } from './filter-utils'
|
|
19
16
|
import {
|
|
@@ -3223,7 +3220,7 @@ function applyServerCacheToDataSourceModules(
|
|
|
3223
3220
|
registry: StateRegistry,
|
|
3224
3221
|
// tslint:disable-next-line:no-any
|
|
3225
3222
|
dataSources: Record<string, any>,
|
|
3226
|
-
transformOptions:
|
|
3223
|
+
transformOptions: EntityTransformOptions = {},
|
|
3227
3224
|
// Accumulated across every page/component this generation has processed. The
|
|
3228
3225
|
// module is shared project-wide, so merging only the CURRENT page's usages
|
|
3229
3226
|
// would let page ORDER decide the TTL: a page asking for 300s processed after
|
|
@@ -3309,7 +3306,7 @@ function ensureAPIRouteExists(
|
|
|
3309
3306
|
extractedResources: any,
|
|
3310
3307
|
usage: DataSourceUsage,
|
|
3311
3308
|
dataSources: Record<string, any>,
|
|
3312
|
-
transformOptions:
|
|
3309
|
+
transformOptions: EntityTransformOptions = {}
|
|
3313
3310
|
): void {
|
|
3314
3311
|
// Generate file name for the API route
|
|
3315
3312
|
const fileName = generateSafeFileName(
|