@teleporthq/teleport-plugin-next-data-source 0.42.35 → 0.43.0
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__/ecommerce-product-out-of-stock.test.ts +112 -0
- package/__tests__/fetchers.test.ts +0 -42
- package/__tests__/filter-utils.test.ts +149 -0
- package/__tests__/mocks.ts +0 -12
- package/__tests__/utils.test.ts +0 -2
- package/dist/cjs/array-mapper-registry.d.ts +2 -0
- package/dist/cjs/array-mapper-registry.d.ts.map +1 -1
- package/dist/cjs/array-mapper-registry.js +9 -1
- package/dist/cjs/array-mapper-registry.js.map +1 -1
- package/dist/cjs/count-fetchers.d.ts +2 -2
- package/dist/cjs/count-fetchers.d.ts.map +1 -1
- package/dist/cjs/count-fetchers.js +5 -5
- package/dist/cjs/count-fetchers.js.map +1 -1
- package/dist/cjs/data-source-fetchers.d.ts +2 -1
- package/dist/cjs/data-source-fetchers.d.ts.map +1 -1
- package/dist/cjs/data-source-fetchers.js +11 -9
- 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.js +1 -1
- package/dist/cjs/fetchers/csv-file.js.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.js +1 -1
- package/dist/cjs/fetchers/google-sheets.js.map +1 -1
- package/dist/cjs/fetchers/index.d.ts +2 -1
- package/dist/cjs/fetchers/index.d.ts.map +1 -1
- package/dist/cjs/fetchers/index.js +8 -5
- package/dist/cjs/fetchers/index.js.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 +3 -3
- package/dist/cjs/fetchers/mariadb.js.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 +2 -2
- 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 +2 -2
- package/dist/cjs/fetchers/postgresql.js.map +1 -1
- package/dist/cjs/fetchers/raw-query.d.ts +18 -0
- package/dist/cjs/fetchers/raw-query.d.ts.map +1 -0
- package/dist/cjs/fetchers/raw-query.js +70 -0
- package/dist/cjs/fetchers/raw-query.js.map +1 -0
- 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.js +1 -1
- 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 +62 -2
- package/dist/cjs/fetchers/supabase.js.map +1 -1
- package/dist/cjs/fetchers/teleport.d.ts +7 -0
- package/dist/cjs/fetchers/teleport.d.ts.map +1 -0
- package/dist/cjs/fetchers/teleport.js +63 -0
- package/dist/cjs/fetchers/teleport.js.map +1 -0
- 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/filter-utils.d.ts +13 -0
- package/dist/cjs/filter-utils.d.ts.map +1 -0
- package/dist/cjs/filter-utils.js +95 -0
- package/dist/cjs/filter-utils.js.map +1 -0
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js +112 -9
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/pagination-plugin.d.ts.map +1 -1
- package/dist/cjs/pagination-plugin.js +389 -128
- package/dist/cjs/pagination-plugin.js.map +1 -1
- package/dist/cjs/sort-utils.d.ts +10 -0
- package/dist/cjs/sort-utils.d.ts.map +1 -0
- package/dist/cjs/sort-utils.js +141 -0
- package/dist/cjs/sort-utils.js.map +1 -0
- package/dist/cjs/transformations/blog-post.d.ts +7 -0
- package/dist/cjs/transformations/blog-post.d.ts.map +1 -0
- package/dist/cjs/transformations/blog-post.js +13 -0
- package/dist/cjs/transformations/blog-post.js.map +1 -0
- package/dist/cjs/transformations/ecommerce-product.d.ts +7 -0
- package/dist/cjs/transformations/ecommerce-product.d.ts.map +1 -0
- package/dist/cjs/transformations/ecommerce-product.js +13 -0
- package/dist/cjs/transformations/ecommerce-product.js.map +1 -0
- package/dist/cjs/transformations/index.d.ts +26 -0
- package/dist/cjs/transformations/index.d.ts.map +1 -0
- package/dist/cjs/transformations/index.js +81 -0
- package/dist/cjs/transformations/index.js.map +1 -0
- package/dist/cjs/transformations/shared-utils.d.ts +7 -0
- package/dist/cjs/transformations/shared-utils.d.ts.map +1 -0
- package/dist/cjs/transformations/shared-utils.js +13 -0
- package/dist/cjs/transformations/shared-utils.js.map +1 -0
- package/dist/cjs/tsconfig.tsbuildinfo +1 -1
- package/dist/cjs/utils.d.ts +30 -1
- package/dist/cjs/utils.d.ts.map +1 -1
- package/dist/cjs/utils.js +173 -10
- package/dist/cjs/utils.js.map +1 -1
- package/dist/esm/array-mapper-registry.d.ts +2 -0
- package/dist/esm/array-mapper-registry.d.ts.map +1 -1
- package/dist/esm/array-mapper-registry.js +9 -1
- package/dist/esm/array-mapper-registry.js.map +1 -1
- package/dist/esm/count-fetchers.d.ts +2 -2
- package/dist/esm/count-fetchers.d.ts.map +1 -1
- package/dist/esm/count-fetchers.js +4 -4
- package/dist/esm/count-fetchers.js.map +1 -1
- package/dist/esm/data-source-fetchers.d.ts +2 -1
- package/dist/esm/data-source-fetchers.d.ts.map +1 -1
- package/dist/esm/data-source-fetchers.js +10 -9
- 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/csv-file.js +1 -1
- package/dist/esm/fetchers/csv-file.js.map +1 -1
- package/dist/esm/fetchers/firestore.js +1 -1
- package/dist/esm/fetchers/firestore.js.map +1 -1
- package/dist/esm/fetchers/google-sheets.js +1 -1
- package/dist/esm/fetchers/google-sheets.js.map +1 -1
- package/dist/esm/fetchers/index.d.ts +2 -1
- package/dist/esm/fetchers/index.d.ts.map +1 -1
- package/dist/esm/fetchers/index.js +2 -1
- package/dist/esm/fetchers/index.js.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 +4 -4
- package/dist/esm/fetchers/mariadb.js.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 +3 -3
- 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 +3 -3
- package/dist/esm/fetchers/postgresql.js.map +1 -1
- package/dist/esm/fetchers/raw-query.d.ts +18 -0
- package/dist/esm/fetchers/raw-query.d.ts.map +1 -0
- package/dist/esm/fetchers/raw-query.js +65 -0
- package/dist/esm/fetchers/raw-query.js.map +1 -0
- package/dist/esm/fetchers/redis.js +1 -1
- 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.js +1 -1
- 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 +63 -3
- package/dist/esm/fetchers/supabase.js.map +1 -1
- package/dist/esm/fetchers/teleport.d.ts +7 -0
- package/dist/esm/fetchers/teleport.d.ts.map +1 -0
- package/dist/esm/fetchers/teleport.js +57 -0
- package/dist/esm/fetchers/teleport.js.map +1 -0
- 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/filter-utils.d.ts +13 -0
- package/dist/esm/filter-utils.d.ts.map +1 -0
- package/dist/esm/filter-utils.js +66 -0
- package/dist/esm/filter-utils.js.map +1 -0
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +113 -10
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/pagination-plugin.d.ts.map +1 -1
- package/dist/esm/pagination-plugin.js +389 -128
- package/dist/esm/pagination-plugin.js.map +1 -1
- package/dist/esm/sort-utils.d.ts +10 -0
- package/dist/esm/sort-utils.d.ts.map +1 -0
- package/dist/esm/sort-utils.js +113 -0
- package/dist/esm/sort-utils.js.map +1 -0
- package/dist/esm/transformations/blog-post.d.ts +7 -0
- package/dist/esm/transformations/blog-post.d.ts.map +1 -0
- package/dist/esm/transformations/blog-post.js +9 -0
- package/dist/esm/transformations/blog-post.js.map +1 -0
- package/dist/esm/transformations/ecommerce-product.d.ts +7 -0
- package/dist/esm/transformations/ecommerce-product.d.ts.map +1 -0
- package/dist/esm/transformations/ecommerce-product.js +9 -0
- package/dist/esm/transformations/ecommerce-product.js.map +1 -0
- package/dist/esm/transformations/index.d.ts +26 -0
- package/dist/esm/transformations/index.d.ts.map +1 -0
- package/dist/esm/transformations/index.js +74 -0
- package/dist/esm/transformations/index.js.map +1 -0
- package/dist/esm/transformations/shared-utils.d.ts +7 -0
- package/dist/esm/transformations/shared-utils.d.ts.map +1 -0
- package/dist/esm/transformations/shared-utils.js +9 -0
- package/dist/esm/transformations/shared-utils.js.map +1 -0
- package/dist/esm/tsconfig.tsbuildinfo +1 -1
- package/dist/esm/utils.d.ts +30 -1
- package/dist/esm/utils.d.ts.map +1 -1
- package/dist/esm/utils.js +170 -9
- package/dist/esm/utils.js.map +1 -1
- package/package.json +6 -5
- package/src/array-mapper-registry.ts +13 -0
- package/src/count-fetchers.ts +5 -5
- package/src/data-source-fetchers.ts +15 -11
- package/src/fetchers/airtable.ts +54 -8
- package/src/fetchers/clickhouse.ts +25 -19
- package/src/fetchers/csv-file.ts +2 -2
- package/src/fetchers/firestore.ts +2 -2
- package/src/fetchers/google-sheets.ts +2 -2
- package/src/fetchers/index.ts +6 -5
- package/src/fetchers/javascript.ts +2 -2
- package/src/fetchers/mariadb.ts +27 -12
- package/src/fetchers/mongodb.ts +2 -2
- package/src/fetchers/mysql.ts +27 -12
- package/src/fetchers/postgresql.ts +31 -18
- package/src/fetchers/raw-query.ts +178 -0
- package/src/fetchers/redis.ts +2 -2
- package/src/fetchers/redshift.ts +14 -10
- package/src/fetchers/rest-api.ts +2 -2
- package/src/fetchers/supabase.ts +97 -14
- package/src/fetchers/teleport.ts +485 -0
- package/src/fetchers/turso.ts +15 -7
- package/src/filter-utils.ts +111 -0
- package/src/index.ts +146 -6
- package/src/pagination-plugin.ts +547 -308
- package/src/sort-utils.ts +150 -0
- package/src/transformations/blog-post.ts +128 -0
- package/src/transformations/ecommerce-product.ts +173 -0
- package/src/transformations/index.ts +97 -0
- package/src/transformations/shared-utils.ts +271 -0
- package/src/utils.ts +227 -11
- package/dist/cjs/fetchers/static-collection.d.ts +0 -7
- package/dist/cjs/fetchers/static-collection.d.ts.map +0 -1
- package/dist/cjs/fetchers/static-collection.js +0 -25
- package/dist/cjs/fetchers/static-collection.js.map +0 -1
- package/dist/esm/fetchers/static-collection.d.ts +0 -7
- package/dist/esm/fetchers/static-collection.d.ts.map +0 -1
- package/dist/esm/fetchers/static-collection.js +0 -19
- package/dist/esm/fetchers/static-collection.js.map +0 -1
- package/src/fetchers/static-collection.ts +0 -231
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import * as types from '@babel/types'
|
|
2
|
+
import { ASTUtils } from '@teleporthq/teleport-plugin-common'
|
|
3
|
+
import { UIDLExpressionValue, UIDLStaticValue } from '@teleporthq/teleport-types'
|
|
4
|
+
|
|
5
|
+
export interface DynamicSortAST {
|
|
6
|
+
field: types.Expression
|
|
7
|
+
order: types.Expression
|
|
8
|
+
depStateIds: string[]
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const SKIP_IDENTIFIERS = new Set([
|
|
12
|
+
'undefined',
|
|
13
|
+
'NaN',
|
|
14
|
+
'Infinity',
|
|
15
|
+
'globalThis',
|
|
16
|
+
'window',
|
|
17
|
+
'document',
|
|
18
|
+
'console',
|
|
19
|
+
])
|
|
20
|
+
|
|
21
|
+
const collectIdentifiers = (node: types.Node | null | undefined, acc: Set<string>): void => {
|
|
22
|
+
if (!node || typeof node !== 'object') {
|
|
23
|
+
return
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (node.type === 'Identifier') {
|
|
27
|
+
if (!SKIP_IDENTIFIERS.has(node.name)) {
|
|
28
|
+
acc.add(node.name)
|
|
29
|
+
}
|
|
30
|
+
return
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (node.type === 'MemberExpression') {
|
|
34
|
+
collectIdentifiers(node.object, acc)
|
|
35
|
+
if (node.computed) {
|
|
36
|
+
collectIdentifiers(node.property, acc)
|
|
37
|
+
}
|
|
38
|
+
return
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// tslint:disable-next-line:no-any
|
|
42
|
+
for (const key of Object.keys(node as any)) {
|
|
43
|
+
// tslint:disable-next-line:no-any
|
|
44
|
+
const child = (node as any)[key]
|
|
45
|
+
if (Array.isArray(child)) {
|
|
46
|
+
child.forEach((c) => collectIdentifiers(c, acc))
|
|
47
|
+
} else if (child && typeof child === 'object' && typeof child.type === 'string') {
|
|
48
|
+
collectIdentifiers(child, acc)
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const toExpressionAndDeps = (
|
|
54
|
+
value: UIDLStaticValue | UIDLExpressionValue
|
|
55
|
+
): { expr: types.Expression; deps: string[] } => {
|
|
56
|
+
if (value.type === 'static') {
|
|
57
|
+
return { expr: types.stringLiteral(String(value.content ?? '')), deps: [] }
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const parsed = ASTUtils.getExpressionFromUIDLExpressionNode(value)
|
|
61
|
+
const deps = new Set<string>()
|
|
62
|
+
collectIdentifiers(parsed, deps)
|
|
63
|
+
return { expr: parsed, deps: Array.from(deps) }
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export const extractDynamicSort = (
|
|
67
|
+
sort: UIDLStaticValue | UIDLExpressionValue | undefined,
|
|
68
|
+
sortDirection: UIDLStaticValue | UIDLExpressionValue | undefined
|
|
69
|
+
): DynamicSortAST | undefined => {
|
|
70
|
+
if (!sort) {
|
|
71
|
+
return undefined
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const field = toExpressionAndDeps(sort)
|
|
75
|
+
|
|
76
|
+
const order: { expr: types.Expression; deps: string[] } = sortDirection
|
|
77
|
+
? toExpressionAndDeps(sortDirection)
|
|
78
|
+
: { expr: types.stringLiteral('asc'), deps: [] }
|
|
79
|
+
|
|
80
|
+
const depSet = new Set<string>([...field.deps, ...order.deps])
|
|
81
|
+
|
|
82
|
+
return {
|
|
83
|
+
field: field.expr,
|
|
84
|
+
order: order.expr,
|
|
85
|
+
depStateIds: Array.from(depSet),
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Build the AST for `sorts: JSON.stringify([{ field: <fieldExpr>, order: <orderExpr> }])`
|
|
90
|
+
const buildDynamicSortsProperty = (dynamicSort: DynamicSortAST): types.ObjectProperty => {
|
|
91
|
+
return types.objectProperty(
|
|
92
|
+
types.identifier('sorts'),
|
|
93
|
+
types.callExpression(
|
|
94
|
+
types.memberExpression(types.identifier('JSON'), types.identifier('stringify')),
|
|
95
|
+
[
|
|
96
|
+
types.arrayExpression([
|
|
97
|
+
types.objectExpression([
|
|
98
|
+
types.objectProperty(types.identifier('field'), types.cloneNode(dynamicSort.field)),
|
|
99
|
+
types.objectProperty(types.identifier('order'), types.cloneNode(dynamicSort.order)),
|
|
100
|
+
]),
|
|
101
|
+
]),
|
|
102
|
+
]
|
|
103
|
+
)
|
|
104
|
+
)
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Build the AST for the legacy static sorts array form.
|
|
108
|
+
// tslint:disable-next-line:no-any
|
|
109
|
+
const buildLegacySortsProperty = (sorts: any[]): types.ObjectProperty => {
|
|
110
|
+
return types.objectProperty(
|
|
111
|
+
types.identifier('sorts'),
|
|
112
|
+
types.callExpression(
|
|
113
|
+
types.memberExpression(types.identifier('JSON'), types.identifier('stringify')),
|
|
114
|
+
[
|
|
115
|
+
types.arrayExpression(
|
|
116
|
+
// tslint:disable-next-line:no-any
|
|
117
|
+
sorts.map((sort: any) =>
|
|
118
|
+
types.objectExpression([
|
|
119
|
+
types.objectProperty(
|
|
120
|
+
types.identifier('field'),
|
|
121
|
+
types.stringLiteral(sort.field || '')
|
|
122
|
+
),
|
|
123
|
+
types.objectProperty(
|
|
124
|
+
types.identifier('order'),
|
|
125
|
+
types.stringLiteral(sort.order || '')
|
|
126
|
+
),
|
|
127
|
+
])
|
|
128
|
+
)
|
|
129
|
+
),
|
|
130
|
+
]
|
|
131
|
+
)
|
|
132
|
+
)
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Push the `sorts` param to paramsProps. Legacy static-array form wins when present.
|
|
136
|
+
export const appendSortsParam = (
|
|
137
|
+
paramsProps: types.ObjectProperty[],
|
|
138
|
+
// tslint:disable-next-line:no-any
|
|
139
|
+
legacySorts: any[] | undefined,
|
|
140
|
+
dynamicSort: DynamicSortAST | undefined
|
|
141
|
+
): void => {
|
|
142
|
+
if (legacySorts && legacySorts.length > 0) {
|
|
143
|
+
paramsProps.push(buildLegacySortsProperty(legacySorts))
|
|
144
|
+
return
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
if (dynamicSort) {
|
|
148
|
+
paramsProps.push(buildDynamicSortsProperty(dynamicSort))
|
|
149
|
+
}
|
|
150
|
+
}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generates JavaScript code for blog post data transformation.
|
|
3
|
+
* Transforms raw snake_case database records into the camelCase shape
|
|
4
|
+
* that UIDL components expect.
|
|
5
|
+
*/
|
|
6
|
+
export const generateBlogPostTransformationCode = (): string => {
|
|
7
|
+
return `
|
|
8
|
+
// ============================================================
|
|
9
|
+
// Blog Post Transformation
|
|
10
|
+
// ============================================================
|
|
11
|
+
|
|
12
|
+
function buildBlogPost(record, options) {
|
|
13
|
+
if (!record || typeof record !== 'object') return record
|
|
14
|
+
options = options || {}
|
|
15
|
+
var currentLang = options.currentLanguage || null
|
|
16
|
+
var mainLang = options.mainLanguage || null
|
|
17
|
+
var assetMap = options.assetMap || {}
|
|
18
|
+
|
|
19
|
+
var id = record.id !== undefined && record.id !== null ? record.id : null
|
|
20
|
+
|
|
21
|
+
// i18n-resolved text fields
|
|
22
|
+
var title = resolveI18nField(record, 'title', 'title', currentLang, mainLang) || ''
|
|
23
|
+
var slug = resolveI18nField(record, 'slug', 'slug', currentLang, mainLang) || ''
|
|
24
|
+
var content = resolveI18nField(record, 'content', 'content', currentLang, mainLang) || ''
|
|
25
|
+
var excerpt = resolveI18nField(record, 'excerpt', 'excerpt', currentLang, mainLang) || ''
|
|
26
|
+
var category = resolveI18nField(record, 'category', 'category', currentLang, mainLang) || null
|
|
27
|
+
var metaTitle = resolveI18nField(record, 'meta_title', 'metaTitle', currentLang, mainLang) || null
|
|
28
|
+
var metaDescription = resolveI18nField(record, 'meta_description', 'metaDescription', currentLang, mainLang) || null
|
|
29
|
+
var featuredImageAlt = resolveI18nField(record, 'featured_image_alt', 'featuredImageAlt', currentLang, mainLang) || null
|
|
30
|
+
|
|
31
|
+
// Status
|
|
32
|
+
var status = record.status || 'draft'
|
|
33
|
+
|
|
34
|
+
// Asset fields - resolve IDs to URLs
|
|
35
|
+
var rawFeaturedImage = pickFirst(record.featured_image_url, record.featuredImageUrl)
|
|
36
|
+
var TELEPORT_DEFAULT_FEATURED_IMG = 'https://play.teleporthq.io/static/svg/default-img.svg'
|
|
37
|
+
var featuredImageUrl = resolveAssetUrl(rawFeaturedImage, assetMap)
|
|
38
|
+
if (
|
|
39
|
+
!featuredImageUrl ||
|
|
40
|
+
rawFeaturedImage === TELEPORT_DEFAULT_FEATURED_IMG ||
|
|
41
|
+
featuredImageUrl === TELEPORT_DEFAULT_FEATURED_IMG
|
|
42
|
+
) {
|
|
43
|
+
featuredImageUrl =
|
|
44
|
+
'data:image/svg+xml;charset=utf-8,' +
|
|
45
|
+
encodeURIComponent(
|
|
46
|
+
'<svg xmlns="http://www.w3.org/2000/svg" width="400" height="240" viewBox="0 0 400 240"><rect fill="#e5e7eb" width="400" height="240"/><text x="50%" y="50%" dominant-baseline="middle" text-anchor="middle" fill="#9ca3af" font-family="system-ui,sans-serif" font-size="13">No image</text></svg>'
|
|
47
|
+
)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
var rawAuthorAvatar = pickFirst(record.author_avatar_url, record.authorAvatarUrl)
|
|
51
|
+
var authorAvatarUrl = resolveAssetUrl(rawAuthorAvatar, assetMap)
|
|
52
|
+
|
|
53
|
+
// Simple pass-through fields
|
|
54
|
+
var authorName = pickFirst(record.author_name, record.authorName)
|
|
55
|
+
var authorEmail = pickFirst(record.author_email, record.authorEmail)
|
|
56
|
+
var readingTimeMinutes = safeNumber(pickFirst(record.reading_time_minutes, record.readingTimeMinutes), null)
|
|
57
|
+
|
|
58
|
+
// Tags - i18n-resolved then parsed as JSON array
|
|
59
|
+
var rawTags = resolveI18nField(record, 'tags', 'tags', currentLang, mainLang)
|
|
60
|
+
var tags = parseJsonArray(rawTags)
|
|
61
|
+
|
|
62
|
+
// Gallery images - parse JSON array then resolve each asset URL
|
|
63
|
+
var rawGalleryImages = pickFirst(record.gallery_images, record.galleryImages)
|
|
64
|
+
var parsedGalleryImages = parseJsonArray(rawGalleryImages)
|
|
65
|
+
var galleryImages = resolveAssetUrls(parsedGalleryImages, assetMap)
|
|
66
|
+
|
|
67
|
+
// Computed: allImages = [featuredImageUrl, ...galleryImages] (skip null featured)
|
|
68
|
+
var allImages = []
|
|
69
|
+
if (featuredImageUrl) allImages.push(featuredImageUrl)
|
|
70
|
+
allImages = allImages.concat(galleryImages)
|
|
71
|
+
|
|
72
|
+
// Boolean fields
|
|
73
|
+
var isFeatured = coerceBoolean(pickFirst(record.is_featured, record.isFeatured), false)
|
|
74
|
+
var allowComments = record.allow_comments !== undefined
|
|
75
|
+
? coerceBoolean(record.allow_comments, true)
|
|
76
|
+
: record.allowComments !== undefined
|
|
77
|
+
? coerceBoolean(record.allowComments, true)
|
|
78
|
+
: true
|
|
79
|
+
|
|
80
|
+
// Timestamps
|
|
81
|
+
var rawPublishedAt = pickFirst(record.published_at, record.publishedAt)
|
|
82
|
+
var publishedAt = rawPublishedAt != null ? normalizeTimestamp(rawPublishedAt) : null
|
|
83
|
+
|
|
84
|
+
var rawCreatedAt = pickFirst(record.created_at, record.createdAt, record.created)
|
|
85
|
+
var createdAt = normalizeTimestamp(rawCreatedAt)
|
|
86
|
+
|
|
87
|
+
var rawUpdatedAt = pickFirst(record.updated_at, record.updatedAt, record.updated)
|
|
88
|
+
var updatedAt = rawUpdatedAt != null ? normalizeTimestamp(rawUpdatedAt) : createdAt
|
|
89
|
+
|
|
90
|
+
// Computed: created = Unix seconds from createdAt milliseconds
|
|
91
|
+
var created = Math.floor(createdAt / 1000)
|
|
92
|
+
|
|
93
|
+
return {
|
|
94
|
+
id: id,
|
|
95
|
+
title: title,
|
|
96
|
+
slug: slug,
|
|
97
|
+
content: content,
|
|
98
|
+
excerpt: excerpt,
|
|
99
|
+
status: status,
|
|
100
|
+
category: category,
|
|
101
|
+
tags: tags,
|
|
102
|
+
featuredImageUrl: featuredImageUrl,
|
|
103
|
+
featuredImageAlt: featuredImageAlt,
|
|
104
|
+
galleryImages: galleryImages,
|
|
105
|
+
allImages: allImages,
|
|
106
|
+
authorName: authorName,
|
|
107
|
+
authorEmail: authorEmail,
|
|
108
|
+
author_name: authorName,
|
|
109
|
+
author_email: authorEmail,
|
|
110
|
+
authorAvatarUrl: authorAvatarUrl,
|
|
111
|
+
metaTitle: metaTitle,
|
|
112
|
+
metaDescription: metaDescription,
|
|
113
|
+
readingTimeMinutes: readingTimeMinutes,
|
|
114
|
+
isFeatured: isFeatured,
|
|
115
|
+
allowComments: allowComments,
|
|
116
|
+
publishedAt: publishedAt,
|
|
117
|
+
createdAt: createdAt,
|
|
118
|
+
updatedAt: updatedAt,
|
|
119
|
+
created: created,
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function transformBlogPosts(records, options) {
|
|
124
|
+
if (!Array.isArray(records)) return []
|
|
125
|
+
return records.map(function(record) { return buildBlogPost(record, options) })
|
|
126
|
+
}
|
|
127
|
+
`
|
|
128
|
+
}
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generates JavaScript code for e-commerce product data transformation.
|
|
3
|
+
* Transforms raw snake_case database records into the camelCase shape
|
|
4
|
+
* that UIDL components expect.
|
|
5
|
+
*/
|
|
6
|
+
export const generateEcommerceProductTransformationCode = (): string => {
|
|
7
|
+
return `
|
|
8
|
+
// ============================================================
|
|
9
|
+
// E-Commerce Product Transformation
|
|
10
|
+
// ============================================================
|
|
11
|
+
|
|
12
|
+
function buildEcommerceProduct(record, options) {
|
|
13
|
+
if (!record || typeof record !== 'object') return record
|
|
14
|
+
options = options || {}
|
|
15
|
+
var currentLang = options.currentLanguage || null
|
|
16
|
+
var mainLang = options.mainLanguage || null
|
|
17
|
+
var assetMap = options.assetMap || {}
|
|
18
|
+
|
|
19
|
+
var id = record.id !== undefined && record.id !== null ? record.id : null
|
|
20
|
+
|
|
21
|
+
// i18n-resolved text fields
|
|
22
|
+
var name = resolveI18nField(record, 'name', 'name', currentLang, mainLang) || ''
|
|
23
|
+
var slug = resolveI18nField(record, 'slug', 'slug', currentLang, mainLang) || ''
|
|
24
|
+
var description = resolveI18nField(record, 'description', 'description', currentLang, mainLang) || ''
|
|
25
|
+
var category = resolveI18nField(record, 'category', 'category', currentLang, mainLang) || null
|
|
26
|
+
var tagsRaw = resolveI18nField(record, 'tags', 'tags', currentLang, mainLang)
|
|
27
|
+
var imageAlt = resolveI18nField(record, 'image_alt', 'imageAlt', currentLang, mainLang) || null
|
|
28
|
+
|
|
29
|
+
// Computed: plain-text description (HTML stripped)
|
|
30
|
+
var descriptionText = stripHtmlTags(description)
|
|
31
|
+
|
|
32
|
+
// Numeric fields (with NaN protection)
|
|
33
|
+
var price = safeNumber(record.price, 0)
|
|
34
|
+
var quantity = safeNumber(record.quantity, null)
|
|
35
|
+
|
|
36
|
+
// Currency
|
|
37
|
+
var currency = (record.currency || 'USD').toUpperCase()
|
|
38
|
+
var rawCurrencySymbol = pickFirst(record.currency_symbol, record.currencySymbol)
|
|
39
|
+
var currencySymbol = rawCurrencySymbol || getCurrencySymbol(currency)
|
|
40
|
+
|
|
41
|
+
// Status and active (normalize case — DB / forms may send "Active", "Inactive", etc.)
|
|
42
|
+
var statusRaw = record.status
|
|
43
|
+
if (statusRaw == null || statusRaw === '') {
|
|
44
|
+
statusRaw = record.active ? 'active' : 'inactive'
|
|
45
|
+
}
|
|
46
|
+
var status =
|
|
47
|
+
typeof statusRaw === 'string'
|
|
48
|
+
? statusRaw.trim().toLowerCase()
|
|
49
|
+
: String(statusRaw).toLowerCase()
|
|
50
|
+
var active = status === 'active'
|
|
51
|
+
|
|
52
|
+
// Payment fields
|
|
53
|
+
var paymentType = pickFirst(record.payment_type, record.paymentType) || 'one_time'
|
|
54
|
+
var recurringInterval = pickFirst(record.recurring_interval, record.recurringInterval) || null
|
|
55
|
+
var rawRecurringCount = pickFirst(record.recurring_interval_count, record.recurringIntervalCount)
|
|
56
|
+
var recurringIntervalCount = safeNumber(rawRecurringCount, null)
|
|
57
|
+
|
|
58
|
+
// Physical product fields
|
|
59
|
+
var sku = record.sku || null
|
|
60
|
+
var weight = safeNumber(record.weight, null)
|
|
61
|
+
var weightUnit = pickFirst(record.weight_unit, record.weightUnit) || null
|
|
62
|
+
var dimensions = record.dimensions || null
|
|
63
|
+
|
|
64
|
+
// Tags - parse as JSON array (handles JSON strings, arrays, comma-separated)
|
|
65
|
+
var tags = parseJsonArray(tagsRaw)
|
|
66
|
+
|
|
67
|
+
// Provider fields
|
|
68
|
+
var providerType = pickFirst(record.provider_type, record.providerType) || null
|
|
69
|
+
var providerProductId = pickFirst(record.provider_product_id, record.providerProductId) || null
|
|
70
|
+
var defaultPrice = pickFirst(record.default_price, record.defaultPrice) || null
|
|
71
|
+
|
|
72
|
+
// Metadata
|
|
73
|
+
var metadata = parseJsonObject(record.metadata)
|
|
74
|
+
|
|
75
|
+
// -------------------------------------------------------
|
|
76
|
+
// Image resolution (complex multi-step process)
|
|
77
|
+
// -------------------------------------------------------
|
|
78
|
+
|
|
79
|
+
// Step 1: Get raw image values
|
|
80
|
+
var rawImageUrl = pickFirst(record.image_url, record.mainImage, record.imageUrl)
|
|
81
|
+
var rawGalleryImages = pickFirst(record.gallery_images, record.galleryImages)
|
|
82
|
+
var legacyImages = Array.isArray(record.images)
|
|
83
|
+
? record.images.filter(function(v) { return typeof v === 'string' })
|
|
84
|
+
: []
|
|
85
|
+
|
|
86
|
+
// Step 2: Parse gallery images from JSON
|
|
87
|
+
var parsedGalleryImages = parseJsonArray(rawGalleryImages)
|
|
88
|
+
|
|
89
|
+
// Step 3: Build combined raw images array
|
|
90
|
+
var allRawImages = []
|
|
91
|
+
if (rawImageUrl) allRawImages.push(rawImageUrl)
|
|
92
|
+
allRawImages = allRawImages.concat(parsedGalleryImages).concat(legacyImages)
|
|
93
|
+
|
|
94
|
+
// Step 4: Resolve all asset URLs first, then deduplicate
|
|
95
|
+
var resolvedAll = resolveAssetUrls(allRawImages, assetMap)
|
|
96
|
+
var resolvedImages = deduplicateStrings(resolvedAll)
|
|
97
|
+
|
|
98
|
+
// Step 5: Determine mainImage
|
|
99
|
+
var mainImage = rawImageUrl
|
|
100
|
+
? resolveAssetUrl(rawImageUrl, assetMap)
|
|
101
|
+
: (resolvedImages.length > 0 ? resolvedImages[0] : null)
|
|
102
|
+
|
|
103
|
+
// Step 6: galleryImages = resolvedImages without mainImage
|
|
104
|
+
var galleryImages = mainImage
|
|
105
|
+
? resolvedImages.filter(function(url) { return url !== mainImage })
|
|
106
|
+
: resolvedImages.slice()
|
|
107
|
+
|
|
108
|
+
// Step 7: images = full resolved deduplicated array
|
|
109
|
+
var images = resolvedImages
|
|
110
|
+
|
|
111
|
+
// Timestamps
|
|
112
|
+
var rawCreatedAt = pickFirst(record.created_at, record.createdAt, record.created)
|
|
113
|
+
var createdAt = normalizeTimestamp(rawCreatedAt)
|
|
114
|
+
|
|
115
|
+
var rawUpdatedAt = pickFirst(record.updated_at, record.updatedAt, record.updated)
|
|
116
|
+
var updatedAt = rawUpdatedAt != null ? normalizeTimestamp(rawUpdatedAt) : createdAt
|
|
117
|
+
|
|
118
|
+
// Computed: created = Unix seconds
|
|
119
|
+
var created = Math.floor(createdAt / 1000)
|
|
120
|
+
|
|
121
|
+
// Computed: outOfStock (emitted as a STRING — 'true' / 'false' — so it
|
|
122
|
+
// matches the strict equality check the AI generates on product
|
|
123
|
+
// cards/details pages: \`ecommerceProduct?.outOfStock === 'true'\`.
|
|
124
|
+
// A boolean here would silently fail that comparison (boolean !==
|
|
125
|
+
// string), leaving the Add to Cart button visible on out-of-stock
|
|
126
|
+
// products. NULL/NaN quantity → 'false' (treated as "unlimited
|
|
127
|
+
// stock", same as the upstream cart-availability rewriter).
|
|
128
|
+
var outOfStock = (quantity !== null && !isNaN(quantity) && quantity <= 0) ? 'true' : 'false'
|
|
129
|
+
|
|
130
|
+
return {
|
|
131
|
+
id: id,
|
|
132
|
+
name: name,
|
|
133
|
+
slug: slug,
|
|
134
|
+
description: description,
|
|
135
|
+
descriptionText: descriptionText,
|
|
136
|
+
price: price,
|
|
137
|
+
currency: currency,
|
|
138
|
+
currencySymbol: currencySymbol,
|
|
139
|
+
status: status,
|
|
140
|
+
active: active,
|
|
141
|
+
quantity: quantity,
|
|
142
|
+
paymentType: paymentType,
|
|
143
|
+
recurringInterval: recurringInterval,
|
|
144
|
+
recurringIntervalCount: recurringIntervalCount,
|
|
145
|
+
sku: sku,
|
|
146
|
+
weight: weight,
|
|
147
|
+
weightUnit: weightUnit,
|
|
148
|
+
dimensions: dimensions,
|
|
149
|
+
category: category,
|
|
150
|
+
tags: tags,
|
|
151
|
+
imageAlt: imageAlt,
|
|
152
|
+
providerType: providerType,
|
|
153
|
+
providerProductId: providerProductId,
|
|
154
|
+
default_price: defaultPrice,
|
|
155
|
+
metadata: metadata,
|
|
156
|
+
mainImage: mainImage,
|
|
157
|
+
imageUrl: mainImage,
|
|
158
|
+
image_url: mainImage,
|
|
159
|
+
galleryImages: galleryImages,
|
|
160
|
+
images: images,
|
|
161
|
+
createdAt: createdAt,
|
|
162
|
+
updatedAt: updatedAt,
|
|
163
|
+
created: created,
|
|
164
|
+
outOfStock: outOfStock,
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function transformEcommerceProducts(records, options) {
|
|
169
|
+
if (!Array.isArray(records)) return []
|
|
170
|
+
return records.map(function(record) { return buildEcommerceProduct(record, options) })
|
|
171
|
+
}
|
|
172
|
+
`
|
|
173
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { generateSharedTransformationCode } from './shared-utils'
|
|
2
|
+
import { generateBlogPostTransformationCode } from './blog-post'
|
|
3
|
+
import { generateEcommerceProductTransformationCode } from './ecommerce-product'
|
|
4
|
+
|
|
5
|
+
export type TransformationType = 'blog-post' | 'ecommerce-product' | null
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Detects which transformation type to apply based on the table name.
|
|
9
|
+
* Returns null if no transformation is needed.
|
|
10
|
+
*/
|
|
11
|
+
export const detectTransformationType = (tableName: string): TransformationType => {
|
|
12
|
+
if (!tableName) {
|
|
13
|
+
return null
|
|
14
|
+
}
|
|
15
|
+
const lower = tableName.toLowerCase()
|
|
16
|
+
if (lower.includes('blog_posts') || lower.includes('blog-posts')) {
|
|
17
|
+
return 'blog-post'
|
|
18
|
+
}
|
|
19
|
+
if (lower.includes('products')) {
|
|
20
|
+
return 'ecommerce-product'
|
|
21
|
+
}
|
|
22
|
+
return null
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Returns the full transformation code (shared utils + specific transformer)
|
|
27
|
+
* to be included in the generated data source fetcher file.
|
|
28
|
+
* Returns empty string if no transformation is needed for this table.
|
|
29
|
+
*/
|
|
30
|
+
export const getTransformationCode = (tableName: string): string => {
|
|
31
|
+
const type = detectTransformationType(tableName)
|
|
32
|
+
if (!type) {
|
|
33
|
+
return ''
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const shared = generateSharedTransformationCode()
|
|
37
|
+
|
|
38
|
+
switch (type) {
|
|
39
|
+
case 'blog-post':
|
|
40
|
+
return shared + generateBlogPostTransformationCode()
|
|
41
|
+
case 'ecommerce-product':
|
|
42
|
+
return shared + generateEcommerceProductTransformationCode()
|
|
43
|
+
default:
|
|
44
|
+
return ''
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Returns the JavaScript expression that transforms the data array.
|
|
50
|
+
* This expression assumes the data array variable is called `safeData`,
|
|
51
|
+
* that `getClient` is available for asset resolution, and that `req` is in scope.
|
|
52
|
+
* Returns null if no transformation is needed.
|
|
53
|
+
*/
|
|
54
|
+
export const getTransformExpression = (tableName: string): string | null => {
|
|
55
|
+
const type = detectTransformationType(tableName)
|
|
56
|
+
if (!type) {
|
|
57
|
+
return null
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
switch (type) {
|
|
61
|
+
case 'blog-post':
|
|
62
|
+
return 'await transformRecords(safeData, getClient, req.query)'
|
|
63
|
+
case 'ecommerce-product':
|
|
64
|
+
return 'await transformRecords(safeData, getClient, req.query)'
|
|
65
|
+
default:
|
|
66
|
+
return null
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Returns the transform wrapper function code that handles asset map loading
|
|
72
|
+
* and calls the appropriate transformer.
|
|
73
|
+
* Returns empty string if no transformation is needed.
|
|
74
|
+
*/
|
|
75
|
+
export const getTransformWrapperCode = (tableName: string): string => {
|
|
76
|
+
const type = detectTransformationType(tableName)
|
|
77
|
+
if (!type) {
|
|
78
|
+
return ''
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const transformFn = type === 'blog-post' ? 'transformBlogPosts' : 'transformEcommerceProducts'
|
|
82
|
+
|
|
83
|
+
return `
|
|
84
|
+
async function transformRecords(records, getClientFn, reqQuery) {
|
|
85
|
+
var assetMap = {}
|
|
86
|
+
try {
|
|
87
|
+
assetMap = await getAssetMap(getClientFn)
|
|
88
|
+
} catch (e) {
|
|
89
|
+
// Asset resolution is best-effort; continue without it
|
|
90
|
+
}
|
|
91
|
+
var currentLanguage = (reqQuery && reqQuery.lang) || null
|
|
92
|
+
var mainLanguage = (reqQuery && reqQuery.mainLang) || null
|
|
93
|
+
var options = { assetMap: assetMap, currentLanguage: currentLanguage, mainLanguage: mainLanguage }
|
|
94
|
+
return ${transformFn}(records, options)
|
|
95
|
+
}
|
|
96
|
+
`
|
|
97
|
+
}
|