@teleporthq/teleport-plugin-next-data-source 0.40.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (240) hide show
  1. package/ARRAY_MAPPER_PAGINATION.md +1128 -0
  2. package/LICENSE +21 -0
  3. package/README.md +40 -0
  4. package/SEARCH_IMPLEMENTATION_SUMMARY.md +983 -0
  5. package/__tests__/fetchers.test.ts +545 -0
  6. package/__tests__/integration.test.ts +561 -0
  7. package/__tests__/mocks.ts +241 -0
  8. package/__tests__/pagination.test.ts +31 -0
  9. package/__tests__/plugin.test.ts +577 -0
  10. package/__tests__/utils.test.ts +430 -0
  11. package/__tests__/validation.test.ts +348 -0
  12. package/dist/cjs/array-mapper-pagination.d.ts +32 -0
  13. package/dist/cjs/array-mapper-pagination.d.ts.map +1 -0
  14. package/dist/cjs/array-mapper-pagination.js +77 -0
  15. package/dist/cjs/array-mapper-pagination.js.map +1 -0
  16. package/dist/cjs/count-fetchers.d.ts +12 -0
  17. package/dist/cjs/count-fetchers.d.ts.map +1 -0
  18. package/dist/cjs/count-fetchers.js +46 -0
  19. package/dist/cjs/count-fetchers.js.map +1 -0
  20. package/dist/cjs/data-source-fetchers.d.ts +14 -0
  21. package/dist/cjs/data-source-fetchers.d.ts.map +1 -0
  22. package/dist/cjs/data-source-fetchers.js +185 -0
  23. package/dist/cjs/data-source-fetchers.js.map +1 -0
  24. package/dist/cjs/fetchers/airtable.d.ts +6 -0
  25. package/dist/cjs/fetchers/airtable.d.ts.map +1 -0
  26. package/dist/cjs/fetchers/airtable.js +27 -0
  27. package/dist/cjs/fetchers/airtable.js.map +1 -0
  28. package/dist/cjs/fetchers/clickhouse.d.ts +6 -0
  29. package/dist/cjs/fetchers/clickhouse.d.ts.map +1 -0
  30. package/dist/cjs/fetchers/clickhouse.js +29 -0
  31. package/dist/cjs/fetchers/clickhouse.js.map +1 -0
  32. package/dist/cjs/fetchers/csv-file.d.ts +7 -0
  33. package/dist/cjs/fetchers/csv-file.d.ts.map +1 -0
  34. package/dist/cjs/fetchers/csv-file.js +36 -0
  35. package/dist/cjs/fetchers/csv-file.js.map +1 -0
  36. package/dist/cjs/fetchers/firestore.d.ts +6 -0
  37. package/dist/cjs/fetchers/firestore.d.ts.map +1 -0
  38. package/dist/cjs/fetchers/firestore.js +35 -0
  39. package/dist/cjs/fetchers/firestore.js.map +1 -0
  40. package/dist/cjs/fetchers/google-sheets.d.ts +6 -0
  41. package/dist/cjs/fetchers/google-sheets.d.ts.map +1 -0
  42. package/dist/cjs/fetchers/google-sheets.js +30 -0
  43. package/dist/cjs/fetchers/google-sheets.js.map +1 -0
  44. package/dist/cjs/fetchers/index.d.ts +17 -0
  45. package/dist/cjs/fetchers/index.d.ts.map +1 -0
  46. package/dist/cjs/fetchers/index.js +56 -0
  47. package/dist/cjs/fetchers/index.js.map +1 -0
  48. package/dist/cjs/fetchers/javascript.d.ts +7 -0
  49. package/dist/cjs/fetchers/javascript.d.ts.map +1 -0
  50. package/dist/cjs/fetchers/javascript.js +40 -0
  51. package/dist/cjs/fetchers/javascript.js.map +1 -0
  52. package/dist/cjs/fetchers/mariadb.d.ts +3 -0
  53. package/dist/cjs/fetchers/mariadb.d.ts.map +1 -0
  54. package/dist/cjs/fetchers/mariadb.js +23 -0
  55. package/dist/cjs/fetchers/mariadb.js.map +1 -0
  56. package/dist/cjs/fetchers/mongodb.d.ts +7 -0
  57. package/dist/cjs/fetchers/mongodb.d.ts.map +1 -0
  58. package/dist/cjs/fetchers/mongodb.js +52 -0
  59. package/dist/cjs/fetchers/mongodb.js.map +1 -0
  60. package/dist/cjs/fetchers/mysql.d.ts +3 -0
  61. package/dist/cjs/fetchers/mysql.d.ts.map +1 -0
  62. package/dist/cjs/fetchers/mysql.js +30 -0
  63. package/dist/cjs/fetchers/mysql.js.map +1 -0
  64. package/dist/cjs/fetchers/postgresql.d.ts +3 -0
  65. package/dist/cjs/fetchers/postgresql.d.ts.map +1 -0
  66. package/dist/cjs/fetchers/postgresql.js +25 -0
  67. package/dist/cjs/fetchers/postgresql.js.map +1 -0
  68. package/dist/cjs/fetchers/redis.d.ts +6 -0
  69. package/dist/cjs/fetchers/redis.d.ts.map +1 -0
  70. package/dist/cjs/fetchers/redis.js +46 -0
  71. package/dist/cjs/fetchers/redis.js.map +1 -0
  72. package/dist/cjs/fetchers/redshift.d.ts +2 -0
  73. package/dist/cjs/fetchers/redshift.d.ts.map +1 -0
  74. package/dist/cjs/fetchers/redshift.js +24 -0
  75. package/dist/cjs/fetchers/redshift.js.map +1 -0
  76. package/dist/cjs/fetchers/rest-api.d.ts +6 -0
  77. package/dist/cjs/fetchers/rest-api.d.ts.map +1 -0
  78. package/dist/cjs/fetchers/rest-api.js +58 -0
  79. package/dist/cjs/fetchers/rest-api.js.map +1 -0
  80. package/dist/cjs/fetchers/static-collection.d.ts +7 -0
  81. package/dist/cjs/fetchers/static-collection.d.ts.map +1 -0
  82. package/dist/cjs/fetchers/static-collection.js +24 -0
  83. package/dist/cjs/fetchers/static-collection.js.map +1 -0
  84. package/dist/cjs/fetchers/supabase.d.ts +7 -0
  85. package/dist/cjs/fetchers/supabase.d.ts.map +1 -0
  86. package/dist/cjs/fetchers/supabase.js +42 -0
  87. package/dist/cjs/fetchers/supabase.js.map +1 -0
  88. package/dist/cjs/fetchers/turso.d.ts +6 -0
  89. package/dist/cjs/fetchers/turso.d.ts.map +1 -0
  90. package/dist/cjs/fetchers/turso.js +25 -0
  91. package/dist/cjs/fetchers/turso.js.map +1 -0
  92. package/dist/cjs/index.d.ts +9 -0
  93. package/dist/cjs/index.d.ts.map +1 -0
  94. package/dist/cjs/index.js +325 -0
  95. package/dist/cjs/index.js.map +1 -0
  96. package/dist/cjs/pagination-plugin.d.ts +5 -0
  97. package/dist/cjs/pagination-plugin.d.ts.map +1 -0
  98. package/dist/cjs/pagination-plugin.js +1484 -0
  99. package/dist/cjs/pagination-plugin.js.map +1 -0
  100. package/dist/cjs/pagination-with-count.d.ts +6 -0
  101. package/dist/cjs/pagination-with-count.d.ts.map +1 -0
  102. package/dist/cjs/pagination-with-count.js +63 -0
  103. package/dist/cjs/pagination-with-count.js.map +1 -0
  104. package/dist/cjs/tsconfig.tsbuildinfo +1 -0
  105. package/dist/cjs/utils.d.ts +31 -0
  106. package/dist/cjs/utils.d.ts.map +1 -0
  107. package/dist/cjs/utils.js +763 -0
  108. package/dist/cjs/utils.js.map +1 -0
  109. package/dist/cjs/validation.d.ts +5 -0
  110. package/dist/cjs/validation.d.ts.map +1 -0
  111. package/dist/cjs/validation.js +29 -0
  112. package/dist/cjs/validation.js.map +1 -0
  113. package/dist/esm/array-mapper-pagination.d.ts +32 -0
  114. package/dist/esm/array-mapper-pagination.d.ts.map +1 -0
  115. package/dist/esm/array-mapper-pagination.js +72 -0
  116. package/dist/esm/array-mapper-pagination.js.map +1 -0
  117. package/dist/esm/count-fetchers.d.ts +12 -0
  118. package/dist/esm/count-fetchers.d.ts.map +1 -0
  119. package/dist/esm/count-fetchers.js +35 -0
  120. package/dist/esm/count-fetchers.js.map +1 -0
  121. package/dist/esm/data-source-fetchers.d.ts +14 -0
  122. package/dist/esm/data-source-fetchers.d.ts.map +1 -0
  123. package/dist/esm/data-source-fetchers.js +179 -0
  124. package/dist/esm/data-source-fetchers.js.map +1 -0
  125. package/dist/esm/fetchers/airtable.d.ts +6 -0
  126. package/dist/esm/fetchers/airtable.d.ts.map +1 -0
  127. package/dist/esm/fetchers/airtable.js +22 -0
  128. package/dist/esm/fetchers/airtable.js.map +1 -0
  129. package/dist/esm/fetchers/clickhouse.d.ts +6 -0
  130. package/dist/esm/fetchers/clickhouse.d.ts.map +1 -0
  131. package/dist/esm/fetchers/clickhouse.js +24 -0
  132. package/dist/esm/fetchers/clickhouse.js.map +1 -0
  133. package/dist/esm/fetchers/csv-file.d.ts +7 -0
  134. package/dist/esm/fetchers/csv-file.d.ts.map +1 -0
  135. package/dist/esm/fetchers/csv-file.js +30 -0
  136. package/dist/esm/fetchers/csv-file.js.map +1 -0
  137. package/dist/esm/fetchers/firestore.d.ts +6 -0
  138. package/dist/esm/fetchers/firestore.d.ts.map +1 -0
  139. package/dist/esm/fetchers/firestore.js +30 -0
  140. package/dist/esm/fetchers/firestore.js.map +1 -0
  141. package/dist/esm/fetchers/google-sheets.d.ts +6 -0
  142. package/dist/esm/fetchers/google-sheets.d.ts.map +1 -0
  143. package/dist/esm/fetchers/google-sheets.js +25 -0
  144. package/dist/esm/fetchers/google-sheets.js.map +1 -0
  145. package/dist/esm/fetchers/index.d.ts +17 -0
  146. package/dist/esm/fetchers/index.d.ts.map +1 -0
  147. package/dist/esm/fetchers/index.js +17 -0
  148. package/dist/esm/fetchers/index.js.map +1 -0
  149. package/dist/esm/fetchers/javascript.d.ts +7 -0
  150. package/dist/esm/fetchers/javascript.d.ts.map +1 -0
  151. package/dist/esm/fetchers/javascript.js +34 -0
  152. package/dist/esm/fetchers/javascript.js.map +1 -0
  153. package/dist/esm/fetchers/mariadb.d.ts +3 -0
  154. package/dist/esm/fetchers/mariadb.d.ts.map +1 -0
  155. package/dist/esm/fetchers/mariadb.js +18 -0
  156. package/dist/esm/fetchers/mariadb.js.map +1 -0
  157. package/dist/esm/fetchers/mongodb.d.ts +7 -0
  158. package/dist/esm/fetchers/mongodb.d.ts.map +1 -0
  159. package/dist/esm/fetchers/mongodb.js +46 -0
  160. package/dist/esm/fetchers/mongodb.js.map +1 -0
  161. package/dist/esm/fetchers/mysql.d.ts +3 -0
  162. package/dist/esm/fetchers/mysql.d.ts.map +1 -0
  163. package/dist/esm/fetchers/mysql.js +25 -0
  164. package/dist/esm/fetchers/mysql.js.map +1 -0
  165. package/dist/esm/fetchers/postgresql.d.ts +3 -0
  166. package/dist/esm/fetchers/postgresql.d.ts.map +1 -0
  167. package/dist/esm/fetchers/postgresql.js +20 -0
  168. package/dist/esm/fetchers/postgresql.js.map +1 -0
  169. package/dist/esm/fetchers/redis.d.ts +6 -0
  170. package/dist/esm/fetchers/redis.d.ts.map +1 -0
  171. package/dist/esm/fetchers/redis.js +41 -0
  172. package/dist/esm/fetchers/redis.js.map +1 -0
  173. package/dist/esm/fetchers/redshift.d.ts +2 -0
  174. package/dist/esm/fetchers/redshift.d.ts.map +1 -0
  175. package/dist/esm/fetchers/redshift.js +20 -0
  176. package/dist/esm/fetchers/redshift.js.map +1 -0
  177. package/dist/esm/fetchers/rest-api.d.ts +6 -0
  178. package/dist/esm/fetchers/rest-api.d.ts.map +1 -0
  179. package/dist/esm/fetchers/rest-api.js +53 -0
  180. package/dist/esm/fetchers/rest-api.js.map +1 -0
  181. package/dist/esm/fetchers/static-collection.d.ts +7 -0
  182. package/dist/esm/fetchers/static-collection.d.ts.map +1 -0
  183. package/dist/esm/fetchers/static-collection.js +18 -0
  184. package/dist/esm/fetchers/static-collection.js.map +1 -0
  185. package/dist/esm/fetchers/supabase.d.ts +7 -0
  186. package/dist/esm/fetchers/supabase.d.ts.map +1 -0
  187. package/dist/esm/fetchers/supabase.js +36 -0
  188. package/dist/esm/fetchers/supabase.js.map +1 -0
  189. package/dist/esm/fetchers/turso.d.ts +6 -0
  190. package/dist/esm/fetchers/turso.d.ts.map +1 -0
  191. package/dist/esm/fetchers/turso.js +20 -0
  192. package/dist/esm/fetchers/turso.js.map +1 -0
  193. package/dist/esm/index.d.ts +9 -0
  194. package/dist/esm/index.d.ts.map +1 -0
  195. package/dist/esm/index.js +306 -0
  196. package/dist/esm/index.js.map +1 -0
  197. package/dist/esm/pagination-plugin.d.ts +5 -0
  198. package/dist/esm/pagination-plugin.d.ts.map +1 -0
  199. package/dist/esm/pagination-plugin.js +1457 -0
  200. package/dist/esm/pagination-plugin.js.map +1 -0
  201. package/dist/esm/pagination-with-count.d.ts +6 -0
  202. package/dist/esm/pagination-with-count.d.ts.map +1 -0
  203. package/dist/esm/pagination-with-count.js +34 -0
  204. package/dist/esm/pagination-with-count.js.map +1 -0
  205. package/dist/esm/tsconfig.tsbuildinfo +1 -0
  206. package/dist/esm/utils.d.ts +31 -0
  207. package/dist/esm/utils.d.ts.map +1 -0
  208. package/dist/esm/utils.js +722 -0
  209. package/dist/esm/utils.js.map +1 -0
  210. package/dist/esm/validation.d.ts +5 -0
  211. package/dist/esm/validation.d.ts.map +1 -0
  212. package/dist/esm/validation.js +25 -0
  213. package/dist/esm/validation.js.map +1 -0
  214. package/package.json +33 -0
  215. package/src/array-mapper-pagination.ts +113 -0
  216. package/src/count-fetchers.ts +99 -0
  217. package/src/data-source-fetchers.ts +313 -0
  218. package/src/fetchers/airtable.ts +153 -0
  219. package/src/fetchers/clickhouse.ts +127 -0
  220. package/src/fetchers/csv-file.ts +163 -0
  221. package/src/fetchers/firestore.ts +138 -0
  222. package/src/fetchers/google-sheets.ts +189 -0
  223. package/src/fetchers/index.ts +32 -0
  224. package/src/fetchers/javascript.ts +150 -0
  225. package/src/fetchers/mariadb.ts +230 -0
  226. package/src/fetchers/mongodb.ts +239 -0
  227. package/src/fetchers/mysql.ts +237 -0
  228. package/src/fetchers/postgresql.ts +247 -0
  229. package/src/fetchers/redis.ts +152 -0
  230. package/src/fetchers/redshift.ts +138 -0
  231. package/src/fetchers/rest-api.ts +148 -0
  232. package/src/fetchers/static-collection.ts +149 -0
  233. package/src/fetchers/supabase.ts +246 -0
  234. package/src/fetchers/turso.ts +131 -0
  235. package/src/index.ts +352 -0
  236. package/src/pagination-plugin.ts +2335 -0
  237. package/src/pagination-with-count.ts +89 -0
  238. package/src/utils.ts +1013 -0
  239. package/src/validation.ts +32 -0
  240. package/tsconfig.json +9 -0
@@ -0,0 +1,149 @@
1
+ export const validateStaticCollectionConfig = (
2
+ config: Record<string, unknown>
3
+ ): { isValid: boolean; error?: string } => {
4
+ if (!config || typeof config !== 'object') {
5
+ return { isValid: false, error: 'Config must be a valid object' }
6
+ }
7
+
8
+ if (!config.data || !Array.isArray(config.data)) {
9
+ return { isValid: false, error: 'Data must be an array' }
10
+ }
11
+
12
+ return { isValid: true }
13
+ }
14
+
15
+ interface StaticCollectionConfig {
16
+ data?: unknown[]
17
+ }
18
+
19
+ export const generateStaticCollectionFetcher = (config: Record<string, unknown>): string => {
20
+ const staticConfig = config as StaticCollectionConfig
21
+ return `const data = ${JSON.stringify(staticConfig.data || [])}
22
+
23
+ export default async function handler(req, res) {
24
+ try {
25
+ const { query, queryColumns, limit, page, perPage, sortBy, sortOrder, filters, offset: offsetParam } = req.query
26
+
27
+ let filteredData = [...data]
28
+
29
+ if (query) {
30
+ const searchQuery = query.toLowerCase()
31
+
32
+ if (queryColumns) {
33
+ const columns = JSON.parse(queryColumns)
34
+ filteredData = filteredData.filter((item) => {
35
+ return columns.some((col) => {
36
+ const value = item[col]
37
+ return value && String(value).toLowerCase().includes(searchQuery)
38
+ })
39
+ })
40
+ } else {
41
+ filteredData = filteredData.filter((item) => {
42
+ try {
43
+ const stringified = JSON.stringify(item).toLowerCase()
44
+ return stringified.includes(searchQuery)
45
+ } catch {
46
+ return false
47
+ }
48
+ })
49
+ }
50
+ }
51
+
52
+ if (filters) {
53
+ const parsedFilters = JSON.parse(filters)
54
+ filteredData = filteredData.filter((item) => {
55
+ return Object.entries(parsedFilters).every(([key, value]) => {
56
+ if (Array.isArray(value)) {
57
+ return value.includes(item[key])
58
+ }
59
+ return item[key] === value
60
+ })
61
+ })
62
+ }
63
+
64
+ if (sortBy) {
65
+ filteredData.sort((a, b) => {
66
+ const aVal = a[sortBy]
67
+ const bVal = b[sortBy]
68
+ const sortOrderValue = sortOrder?.toLowerCase() === 'desc' ? -1 : 1
69
+ if (aVal < bVal) return -sortOrderValue
70
+ if (aVal > bVal) return sortOrderValue
71
+ return 0
72
+ })
73
+ }
74
+
75
+ const limitValue = limit || perPage
76
+ const offsetValue = offsetParam !== undefined ? parseInt(offsetParam) : (page && perPage ? (parseInt(page) - 1) * parseInt(perPage) : 0)
77
+
78
+ if (limitValue) {
79
+ filteredData = filteredData.slice(offsetValue, offsetValue + parseInt(limitValue))
80
+ }
81
+
82
+ const safeData = JSON.parse(JSON.stringify(filteredData))
83
+
84
+ return res.status(200).json({
85
+ success: true,
86
+ data: safeData,
87
+ timestamp: Date.now()
88
+ })
89
+ } catch (error) {
90
+ console.error('Static collection fetch error:', error)
91
+ return res.status(500).json({
92
+ success: false,
93
+ error: error.message || 'Failed to fetch data',
94
+ timestamp: Date.now()
95
+ })
96
+ }
97
+ }
98
+ `
99
+ }
100
+
101
+ // tslint:disable-next-line:variable-name
102
+ export const generateStaticCollectionCountFetcher = (_config: any): string => {
103
+ return `
104
+ async function getCount(req, res) {
105
+ try {
106
+ const { query, queryColumns, filters } = req.query
107
+ const fakeReq = { query: { query, queryColumns, filters }, method: 'GET' }
108
+ let result = null
109
+ let statusCode = 200
110
+
111
+ const fakeRes = {
112
+ status: (code) => {
113
+ statusCode = code
114
+ return fakeRes
115
+ },
116
+ json: (data) => {
117
+ result = data
118
+ return fakeRes
119
+ },
120
+ }
121
+
122
+ await handler(fakeReq, fakeRes)
123
+
124
+ if (statusCode !== 200 || !result || !result.success) {
125
+ return res.status(500).json({
126
+ success: false,
127
+ error: 'Failed to get data for counting',
128
+ timestamp: Date.now()
129
+ })
130
+ }
131
+
132
+ const count = Array.isArray(result.data) ? result.data.length : 0
133
+
134
+ return res.status(200).json({
135
+ success: true,
136
+ count: count,
137
+ timestamp: Date.now()
138
+ })
139
+ } catch (error) {
140
+ console.error('Error getting count:', error)
141
+ return res.status(500).json({
142
+ success: false,
143
+ error: error.message || 'Failed to get count',
144
+ timestamp: Date.now()
145
+ })
146
+ }
147
+ }
148
+ `
149
+ }
@@ -0,0 +1,246 @@
1
+ import { replaceSecretReference } from '../utils'
2
+
3
+ export const validateSupabaseConfig = (
4
+ config: Record<string, unknown>
5
+ ): { isValid: boolean; error?: string } => {
6
+ if (!config || typeof config !== 'object') {
7
+ return { isValid: false, error: 'Config must be a valid object' }
8
+ }
9
+
10
+ if (!config.supabaseUrl || typeof config.supabaseUrl !== 'string') {
11
+ return { isValid: false, error: 'Supabase URL is required' }
12
+ }
13
+
14
+ try {
15
+ const url = new URL(config.supabaseUrl)
16
+ if (!url.hostname.endsWith('.supabase.co') && !url.hostname.endsWith('.supabase.in')) {
17
+ console.warn('[Data Source] Warning: Supabase URL does not match expected format')
18
+ }
19
+ } catch {
20
+ return { isValid: false, error: 'Invalid Supabase URL format' }
21
+ }
22
+
23
+ if (!config.serviceRoleKey && !config.publicApiKey) {
24
+ return {
25
+ isValid: false,
26
+ error: 'Supabase API key (serviceRoleKey or publicApiKey) is required',
27
+ }
28
+ }
29
+
30
+ return { isValid: true }
31
+ }
32
+
33
+ interface SupabaseConfig {
34
+ url?: string
35
+ anonKey?: string
36
+ supabaseUrl?: string
37
+ serviceRoleKey?: string
38
+ publicApiKey?: string
39
+ }
40
+
41
+ export const generateSupabaseFetcher = (
42
+ config: Record<string, unknown>,
43
+ tableName: string
44
+ ): string => {
45
+ const supabaseConfig = config as SupabaseConfig
46
+ const supabaseUrl = supabaseConfig.supabaseUrl
47
+ const apiKey = supabaseConfig.serviceRoleKey || supabaseConfig.publicApiKey
48
+
49
+ return `import { createClient } from '@supabase/supabase-js'
50
+
51
+ let client = null
52
+
53
+ const getClient = () => {
54
+ if (client) return client
55
+
56
+ client = createClient(
57
+ ${JSON.stringify(supabaseUrl)},
58
+ ${replaceSecretReference(apiKey)}
59
+ )
60
+
61
+ return client
62
+ }
63
+
64
+ export default async function handler(req, res) {
65
+ try {
66
+ const client = getClient()
67
+ const { query, queryColumns, select, limit, page, perPage, sortBy, sortOrder, filters, offset } = req.query
68
+
69
+ let queryRef = client.from('${tableName}').select(select || '*')
70
+
71
+ if (query) {
72
+ let columns = []
73
+
74
+ if (queryColumns) {
75
+ // Use specified columns
76
+ columns = JSON.parse(queryColumns)
77
+ } else {
78
+ // Fallback: Get all column names from a sample row
79
+ try {
80
+ const { data: sampleData, error: sampleError } = await client.from('${tableName}').select('*').limit(1).single()
81
+ if (sampleError) {
82
+ throw sampleError
83
+ }
84
+ if (sampleData) {
85
+ columns = Object.keys(sampleData)
86
+ }
87
+ } catch (schemaError) {
88
+ console.warn('Failed to fetch sample row for column names:', schemaError.message)
89
+ // Continue without search if we can't get columns
90
+ }
91
+ }
92
+
93
+ if (columns.length > 0) {
94
+ const searchPattern = \`%\${query}%\`
95
+ const orConditions = columns.map((col) => \`\${col}.ilike.\${searchPattern}\`).join(',')
96
+ queryRef = queryRef.or(orConditions)
97
+ }
98
+ }
99
+
100
+ if (filters) {
101
+ const parsedFilters = JSON.parse(filters)
102
+ Object.entries(parsedFilters).forEach(([key, value]) => {
103
+ if (Array.isArray(value)) {
104
+ const processedValues = value.map((v) => {
105
+ if (typeof v === 'string' && !isNaN(Number(v))) {
106
+ return Number(v)
107
+ }
108
+ return v
109
+ })
110
+ queryRef = queryRef.in(key, processedValues)
111
+ } else if (typeof value === 'object' && value !== null) {
112
+ const operator = Object.keys(value)[0]
113
+ let operatorValue = value[operator]
114
+ if (typeof operatorValue === 'string' && !isNaN(Number(operatorValue))) {
115
+ operatorValue = Number(operatorValue)
116
+ }
117
+ switch (operator) {
118
+ case 'eq': queryRef = queryRef.eq(key, operatorValue); break
119
+ case 'neq': queryRef = queryRef.neq(key, operatorValue); break
120
+ case 'gt': queryRef = queryRef.gt(key, operatorValue); break
121
+ case 'gte': queryRef = queryRef.gte(key, operatorValue); break
122
+ case 'lt': queryRef = queryRef.lt(key, operatorValue); break
123
+ case 'lte': queryRef = queryRef.lte(key, operatorValue); break
124
+ case 'like': queryRef = queryRef.like(key, operatorValue); break
125
+ case 'ilike': queryRef = queryRef.ilike(key, operatorValue); break
126
+ case 'in': queryRef = queryRef.in(key, operatorValue); break
127
+ default: queryRef = queryRef.eq(key, operatorValue)
128
+ }
129
+ } else {
130
+ let processedValue = value
131
+ if (typeof value === 'string' && !isNaN(Number(value))) {
132
+ processedValue = Number(value)
133
+ }
134
+ queryRef = queryRef.eq(key, processedValue)
135
+ }
136
+ })
137
+ }
138
+
139
+ if (sortBy) {
140
+ queryRef = queryRef.order(sortBy, { ascending: sortOrder !== 'desc' })
141
+ }
142
+
143
+ const limitValue = limit || perPage
144
+ const offsetValue = offset !== undefined ? parseInt(offset) : (page && perPage ? (parseInt(page) - 1) * parseInt(perPage) : undefined)
145
+
146
+ if (offsetValue !== undefined && limitValue) {
147
+ queryRef = queryRef.range(offsetValue, offsetValue + parseInt(limitValue) - 1)
148
+ } else if (limitValue) {
149
+ queryRef = queryRef.limit(parseInt(limitValue))
150
+ }
151
+
152
+ const { data, error } = await queryRef
153
+
154
+ if (error) {
155
+ return res.status(500).json({
156
+ success: false,
157
+ error: error.message,
158
+ timestamp: Date.now()
159
+ })
160
+ }
161
+
162
+ const safeData = JSON.parse(JSON.stringify(data))
163
+
164
+ return res.status(200).json({
165
+ success: true,
166
+ data: safeData,
167
+ timestamp: Date.now()
168
+ })
169
+ } catch (error) {
170
+ console.error('Supabase fetch error:', error)
171
+ return res.status(500).json({
172
+ success: false,
173
+ error: error.message || 'Failed to fetch data',
174
+ timestamp: Date.now()
175
+ })
176
+ }
177
+ }
178
+ `
179
+ }
180
+
181
+ // tslint:disable-next-line:variable-name
182
+ export const generateSupabaseCountFetcher = (_config: any, tableName: string): string => {
183
+ return `
184
+ async function getCount(req, res) {
185
+ const supabase = getClient()
186
+
187
+ try {
188
+ const { query, queryColumns, filters } = req.query
189
+ let countQuery = supabase.from('${tableName}').select('*', { count: 'exact', head: true })
190
+
191
+ if (query) {
192
+ let columns = []
193
+
194
+ if (queryColumns) {
195
+ // Use specified columns
196
+ columns = typeof queryColumns === 'string' ? JSON.parse(queryColumns) : (Array.isArray(queryColumns) ? queryColumns : [queryColumns])
197
+ } else {
198
+ // Fallback: Get all column names from a sample row
199
+ try {
200
+ const { data: sampleData, error: sampleError } = await supabase.from('${tableName}').select('*').limit(1).single()
201
+ if (sampleError) {
202
+ throw sampleError
203
+ }
204
+ if (sampleData) {
205
+ columns = Object.keys(sampleData)
206
+ }
207
+ } catch (schemaError) {
208
+ console.warn('Failed to fetch sample row for column names:', schemaError.message)
209
+ // Continue without search if we can't get columns
210
+ }
211
+ }
212
+
213
+ if (columns.length > 0) {
214
+ const searchPattern = \`%\${query}%\`
215
+ const orConditions = columns.map((col) => \`\${col}.ilike.\${searchPattern}\`).join(',')
216
+ countQuery = countQuery.or(orConditions)
217
+ }
218
+ }
219
+
220
+ if (filters) {
221
+ const parsedFilters = JSON.parse(filters)
222
+ for (const filter of parsedFilters) {
223
+ countQuery = countQuery.eq(filter.column, filter.value)
224
+ }
225
+ }
226
+
227
+ const { count, error } = await countQuery
228
+
229
+ if (error) throw error
230
+
231
+ return res.status(200).json({
232
+ success: true,
233
+ count: count || 0,
234
+ timestamp: Date.now()
235
+ })
236
+ } catch (error) {
237
+ console.error('Error getting count:', error)
238
+ return res.status(500).json({
239
+ success: false,
240
+ error: error.message || 'Failed to get count',
241
+ timestamp: Date.now()
242
+ })
243
+ }
244
+ }
245
+ `
246
+ }
@@ -0,0 +1,131 @@
1
+ import { replaceSecretReference } from '../utils'
2
+
3
+ export const validateTursoConfig = (
4
+ config: Record<string, unknown>
5
+ ): { isValid: boolean; error?: string } => {
6
+ if (!config || typeof config !== 'object') {
7
+ return { isValid: false, error: 'Config must be a valid object' }
8
+ }
9
+
10
+ if (!config.databaseUrl || typeof config.databaseUrl !== 'string') {
11
+ return { isValid: false, error: 'Turso database URL is required' }
12
+ }
13
+
14
+ if (!config.token || typeof config.token !== 'string') {
15
+ return { isValid: false, error: 'Turso authentication token is required' }
16
+ }
17
+
18
+ return { isValid: true }
19
+ }
20
+
21
+ interface TursoConfig {
22
+ url?: string
23
+ authToken?: string
24
+ databaseUrl?: string
25
+ token?: string
26
+ }
27
+
28
+ export const generateTursoFetcher = (
29
+ config: Record<string, unknown>,
30
+ tableName: string
31
+ ): string => {
32
+ const tursoConfig = config as TursoConfig
33
+ const databaseUrl = tursoConfig.databaseUrl
34
+ const token = tursoConfig.token
35
+
36
+ return `import { createClient } from '@libsql/client'
37
+
38
+ export default async function handler(req, res) {
39
+ let client = null
40
+ try {
41
+ client = createClient({
42
+ url: ${JSON.stringify(databaseUrl)},
43
+ authToken: ${replaceSecretReference(token)}
44
+ })
45
+
46
+ const { query, queryColumns, limit, page, perPage, sortBy, sortOrder, filters, offset } = req.query
47
+
48
+ let sql = \`SELECT * FROM ${tableName}\`
49
+ const whereClauses = []
50
+ const queryParams = []
51
+
52
+ if (query && queryColumns) {
53
+ const columns = JSON.parse(queryColumns)
54
+ const searchConditions = columns.map((col) => \`\${col} LIKE ?\`)
55
+ whereClauses.push(\`(\${searchConditions.join(' OR ')})\`)
56
+ columns.forEach(() => {
57
+ queryParams.push(\`%\${query}%\`)
58
+ })
59
+ }
60
+
61
+ if (filters) {
62
+ const parsedFilters = JSON.parse(filters)
63
+ Object.entries(parsedFilters).forEach(([key, value]) => {
64
+ if (Array.isArray(value)) {
65
+ const placeholders = value.map(() => '?').join(', ')
66
+ queryParams.push(...value)
67
+ whereClauses.push(\`\${key} IN (\${placeholders})\`)
68
+ } else {
69
+ whereClauses.push(\`\${key} = ?\`)
70
+ queryParams.push(value)
71
+ }
72
+ })
73
+ }
74
+
75
+ if (whereClauses.length > 0) {
76
+ sql += \` WHERE \${whereClauses.join(' AND ')}\`
77
+ }
78
+
79
+ if (sortBy) {
80
+ const sortOrderValue = sortOrder?.toUpperCase() === 'DESC' ? 'DESC' : 'ASC'
81
+ sql += \` ORDER BY \${sortBy} \${sortOrderValue}\`
82
+ }
83
+
84
+ const limitValue = limit || perPage
85
+ const offsetValue = offset !== undefined ? parseInt(offset) : (page && perPage ? (parseInt(page) - 1) * parseInt(perPage) : undefined)
86
+
87
+ if (limitValue) {
88
+ sql += \` LIMIT ?\`
89
+ queryParams.push(parseInt(limitValue))
90
+ }
91
+
92
+ if (offsetValue !== undefined) {
93
+ sql += \` OFFSET ?\`
94
+ queryParams.push(offsetValue)
95
+ }
96
+
97
+ const result = await client.execute({
98
+ sql,
99
+ args: queryParams
100
+ })
101
+
102
+ const data = result.rows.map((row) => {
103
+ const obj = {}
104
+ result.columns.forEach((col, idx) => {
105
+ obj[col] = row[col]
106
+ })
107
+ return obj
108
+ })
109
+
110
+ const safeData = JSON.parse(JSON.stringify(data))
111
+
112
+ return res.status(200).json({
113
+ success: true,
114
+ data: safeData,
115
+ timestamp: Date.now()
116
+ })
117
+ } catch (error) {
118
+ console.error('Turso fetch error:', error)
119
+ return res.status(500).json({
120
+ success: false,
121
+ error: error.message || 'Failed to fetch data',
122
+ timestamp: Date.now()
123
+ })
124
+ } finally {
125
+ if (client) {
126
+ client.close()
127
+ }
128
+ }
129
+ }
130
+ `
131
+ }