@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,430 @@
1
+ import {
2
+ sanitizeFileName,
3
+ isNonEmptyString,
4
+ validateResourceDefinition,
5
+ validateNodeContent,
6
+ validateDataSourceConfig,
7
+ generateSafeFileName,
8
+ isEmbeddedDataSource,
9
+ replaceSecretReference,
10
+ sanitizeNumericParam,
11
+ sanitizePort,
12
+ isValidUrl,
13
+ sanitizeIdentifier,
14
+ } from '../src/utils'
15
+
16
+ describe('sanitizeFileName', () => {
17
+ it('removes path traversal attempts', () => {
18
+ expect(sanitizeFileName('../../../etc/passwd')).toBe('etc-passwd')
19
+ expect(sanitizeFileName('..\\..\\windows')).toBe('windows')
20
+ })
21
+
22
+ it('removes invalid filename characters', () => {
23
+ expect(sanitizeFileName('file<>:"|?*name')).toBe('filename')
24
+ expect(sanitizeFileName('test\x00\x1Ffile')).toBe('testfile')
25
+ })
26
+
27
+ it('replaces spaces with dashes', () => {
28
+ expect(sanitizeFileName('my file name')).toBe('my-file-name')
29
+ expect(sanitizeFileName('multiple spaces')).toBe('multiple-spaces')
30
+ })
31
+
32
+ it('removes leading/trailing dashes and dots', () => {
33
+ expect(sanitizeFileName('---file---')).toBe('file')
34
+ expect(sanitizeFileName('...file...')).toBe('file')
35
+ expect(sanitizeFileName('_-file-_')).toBe('file')
36
+ })
37
+
38
+ it('limits length to 200 characters', () => {
39
+ const longString = 'a'.repeat(300)
40
+ expect(sanitizeFileName(longString).length).toBe(200)
41
+ })
42
+
43
+ it('returns "unknown" for invalid input', () => {
44
+ expect(sanitizeFileName('')).toBe('unknown')
45
+ expect(sanitizeFileName(null as any)).toBe('unknown')
46
+ expect(sanitizeFileName(undefined as any)).toBe('unknown')
47
+ expect(sanitizeFileName(123 as any)).toBe('unknown')
48
+ })
49
+
50
+ it('handles normal filenames correctly', () => {
51
+ expect(sanitizeFileName('my-file-name')).toBe('my-file-name')
52
+ expect(sanitizeFileName('postgresql-users-12345678')).toBe('postgresql-users-12345678')
53
+ })
54
+ })
55
+
56
+ describe('isNonEmptyString', () => {
57
+ it('returns true for non-empty strings', () => {
58
+ expect(isNonEmptyString('hello')).toBe(true)
59
+ expect(isNonEmptyString('test')).toBe(true)
60
+ })
61
+
62
+ it('returns false for empty or whitespace strings', () => {
63
+ expect(isNonEmptyString('')).toBe(false)
64
+ expect(isNonEmptyString(' ')).toBe(false)
65
+ expect(isNonEmptyString('\t\n')).toBe(false)
66
+ })
67
+
68
+ it('returns false for non-string values', () => {
69
+ expect(isNonEmptyString(null)).toBe(false)
70
+ expect(isNonEmptyString(undefined)).toBe(false)
71
+ expect(isNonEmptyString(123)).toBe(false)
72
+ expect(isNonEmptyString({})).toBe(false)
73
+ })
74
+ })
75
+
76
+ describe('validateResourceDefinition', () => {
77
+ it('validates a correct resource definition', () => {
78
+ const result = validateResourceDefinition({
79
+ dataSourceId: 'ds-123',
80
+ tableName: 'users',
81
+ dataSourceType: 'postgresql',
82
+ })
83
+ expect(result.isValid).toBe(true)
84
+ })
85
+
86
+ it('rejects missing or invalid input', () => {
87
+ expect(validateResourceDefinition(null).isValid).toBe(false)
88
+ expect(validateResourceDefinition(undefined).isValid).toBe(false)
89
+ expect(validateResourceDefinition('string').isValid).toBe(false)
90
+ })
91
+
92
+ it('rejects missing dataSourceId', () => {
93
+ const result = validateResourceDefinition({
94
+ tableName: 'users',
95
+ dataSourceType: 'postgresql',
96
+ })
97
+ expect(result.isValid).toBe(false)
98
+ expect(result.error).toContain('Data source ID')
99
+ })
100
+
101
+ it('rejects missing dataSourceType', () => {
102
+ const result = validateResourceDefinition({
103
+ dataSourceId: 'ds-123',
104
+ tableName: 'users',
105
+ })
106
+ expect(result.isValid).toBe(false)
107
+ expect(result.error).toContain('Data source type')
108
+ })
109
+
110
+ it('rejects invalid dataSourceType', () => {
111
+ const result = validateResourceDefinition({
112
+ dataSourceId: 'ds-123',
113
+ tableName: 'users',
114
+ dataSourceType: 'invalid-type',
115
+ })
116
+ expect(result.isValid).toBe(false)
117
+ expect(result.error).toContain('Invalid data source type')
118
+ })
119
+
120
+ it('accepts all valid data source types', () => {
121
+ const validTypes = [
122
+ 'rest-api',
123
+ 'postgresql',
124
+ 'mysql',
125
+ 'mariadb',
126
+ 'amazon-redshift',
127
+ 'mongodb',
128
+ 'cockroachdb',
129
+ 'tidb',
130
+ 'redis',
131
+ 'firestore',
132
+ 'clickhouse',
133
+ 'airtable',
134
+ 'supabase',
135
+ 'turso',
136
+ 'javascript',
137
+ 'google-sheets',
138
+ 'csv-file',
139
+ 'static-collection',
140
+ ]
141
+
142
+ validTypes.forEach((type) => {
143
+ const result = validateResourceDefinition({
144
+ dataSourceId: 'ds-123',
145
+ dataSourceType: type,
146
+ })
147
+ expect(result.isValid).toBe(true)
148
+ })
149
+ })
150
+
151
+ it('allows missing tableName', () => {
152
+ const result = validateResourceDefinition({
153
+ dataSourceId: 'ds-123',
154
+ dataSourceType: 'rest-api',
155
+ })
156
+ expect(result.isValid).toBe(true)
157
+ })
158
+
159
+ it('rejects invalid tableName when provided', () => {
160
+ const result = validateResourceDefinition({
161
+ dataSourceId: 'ds-123',
162
+ dataSourceType: 'postgresql',
163
+ tableName: '',
164
+ })
165
+ expect(result.isValid).toBe(false)
166
+ expect(result.error).toContain('Table name')
167
+ })
168
+ })
169
+
170
+ describe('validateNodeContent', () => {
171
+ it('validates correct node content', () => {
172
+ const result = validateNodeContent({
173
+ resourceDefinition: {
174
+ dataSourceId: 'ds-123',
175
+ dataSourceType: 'postgresql',
176
+ },
177
+ })
178
+ expect(result.isValid).toBe(true)
179
+ })
180
+
181
+ it('rejects missing or invalid input', () => {
182
+ expect(validateNodeContent(null).isValid).toBe(false)
183
+ expect(validateNodeContent(undefined).isValid).toBe(false)
184
+ })
185
+
186
+ it('rejects missing resourceDefinition', () => {
187
+ const result = validateNodeContent({})
188
+ expect(result.isValid).toBe(false)
189
+ expect(result.error).toContain('Resource definition')
190
+ })
191
+ })
192
+
193
+ describe('validateDataSourceConfig', () => {
194
+ it('validates correct data source config', () => {
195
+ const result = validateDataSourceConfig({
196
+ id: 'ds-123',
197
+ name: 'Test DS',
198
+ type: 'postgresql',
199
+ config: { host: 'localhost' },
200
+ } as any)
201
+ expect(result.isValid).toBe(true)
202
+ })
203
+
204
+ it('rejects missing or invalid input', () => {
205
+ expect(validateDataSourceConfig(null as any).isValid).toBe(false)
206
+ expect(validateDataSourceConfig(undefined as any).isValid).toBe(false)
207
+ })
208
+
209
+ it('rejects missing id', () => {
210
+ const result = validateDataSourceConfig({
211
+ type: 'postgresql',
212
+ config: {},
213
+ } as any)
214
+ expect(result.isValid).toBe(false)
215
+ expect(result.error).toContain('Data source ID')
216
+ })
217
+
218
+ it('rejects missing type', () => {
219
+ const result = validateDataSourceConfig({
220
+ id: 'ds-123',
221
+ config: {},
222
+ } as any)
223
+ expect(result.isValid).toBe(false)
224
+ expect(result.error).toContain('Data source type')
225
+ })
226
+
227
+ it('rejects missing or invalid config', () => {
228
+ const result = validateDataSourceConfig({
229
+ id: 'ds-123',
230
+ type: 'postgresql',
231
+ } as any)
232
+ expect(result.isValid).toBe(false)
233
+ expect(result.error).toContain('config')
234
+ })
235
+ })
236
+
237
+ describe('generateSafeFileName', () => {
238
+ it('generates safe filenames from components', () => {
239
+ const result = generateSafeFileName('postgresql', 'users', 'abcd1234-efgh-5678')
240
+ expect(result).toContain('postgresql')
241
+ expect(result).toContain('users')
242
+ expect(result).toContain('abcd1234')
243
+ })
244
+
245
+ it('handles missing tableName', () => {
246
+ const result = generateSafeFileName('rest-api', '', 'ds-12345678')
247
+ expect(result).toContain('data')
248
+ expect(result).toContain('rest-api')
249
+ })
250
+
251
+ it('converts to dash case', () => {
252
+ const result = generateSafeFileName('PostgreSQL', 'UsersTable', 'DS123')
253
+ expect(result).toMatch(/^[a-z0-9-]+$/)
254
+ })
255
+
256
+ it('limits ID to 8 characters', () => {
257
+ const result = generateSafeFileName('postgresql', 'users', 'verylongidentifier123456')
258
+ expect(result).toContain('verylong')
259
+ })
260
+ })
261
+
262
+ describe('isEmbeddedDataSource', () => {
263
+ it('identifies embedded data sources', () => {
264
+ expect(isEmbeddedDataSource('javascript')).toBe(true)
265
+ expect(isEmbeddedDataSource('csv-file')).toBe(true)
266
+ expect(isEmbeddedDataSource('static-collection')).toBe(true)
267
+ })
268
+
269
+ it('identifies non-embedded data sources', () => {
270
+ expect(isEmbeddedDataSource('postgresql')).toBe(false)
271
+ expect(isEmbeddedDataSource('rest-api')).toBe(false)
272
+ expect(isEmbeddedDataSource('mongodb')).toBe(false)
273
+ })
274
+ })
275
+
276
+ describe('replaceSecretReference', () => {
277
+ describe('with templateLiteral = false (default)', () => {
278
+ it('replaces secret references with process.env', () => {
279
+ const result = replaceSecretReference('teleporthq.secrets.API_KEY')
280
+ expect(result).toBe('process.env.API_KEY')
281
+ })
282
+
283
+ it('validates environment variable names', () => {
284
+ const result = replaceSecretReference('teleporthq.secrets.VALID_ENV_VAR_123')
285
+ expect(result).toBe('process.env.VALID_ENV_VAR_123')
286
+ })
287
+
288
+ it('handles invalid env var names as regular strings', () => {
289
+ const result = replaceSecretReference('teleporthq.secrets.123-invalid')
290
+ expect(result).toBe('"teleporthq.secrets.123-invalid"')
291
+ })
292
+
293
+ it('stringifies regular strings', () => {
294
+ expect(replaceSecretReference('regular-string')).toBe('"regular-string"')
295
+ })
296
+
297
+ it('handles null', () => {
298
+ expect(replaceSecretReference(null)).toBe('null')
299
+ })
300
+
301
+ it('handles undefined', () => {
302
+ expect(replaceSecretReference(undefined)).toBe('undefined')
303
+ })
304
+
305
+ it('stringifies non-string values', () => {
306
+ expect(replaceSecretReference(123)).toBe('123')
307
+ expect(replaceSecretReference(true)).toBe('true')
308
+ expect(replaceSecretReference({ key: 'value' })).toBe('{"key":"value"}')
309
+ })
310
+ })
311
+
312
+ describe('with templateLiteral = true', () => {
313
+ it('replaces secret references for template literals', () => {
314
+ const result = replaceSecretReference('teleporthq.secrets.API_KEY', { templateLiteral: true })
315
+ expect(result).toBe(`\${process.env.API_KEY}`)
316
+ })
317
+
318
+ it('still stringifies regular strings', () => {
319
+ const result = replaceSecretReference('regular-string', { templateLiteral: true })
320
+ expect(result).toBe('"regular-string"')
321
+ })
322
+ })
323
+ })
324
+
325
+ describe('sanitizeNumericParam', () => {
326
+ it('returns valid numbers', () => {
327
+ expect(sanitizeNumericParam(10)).toBe(10)
328
+ expect(sanitizeNumericParam(0)).toBe(0)
329
+ expect(sanitizeNumericParam(100.5)).toBe(100)
330
+ })
331
+
332
+ it('floors decimal numbers', () => {
333
+ expect(sanitizeNumericParam(10.9)).toBe(10)
334
+ expect(sanitizeNumericParam(5.1)).toBe(5)
335
+ })
336
+
337
+ it('returns 0 for negative numbers', () => {
338
+ expect(sanitizeNumericParam(-5)).toBe(0)
339
+ expect(sanitizeNumericParam(-100)).toBe(0)
340
+ })
341
+
342
+ it('parses string numbers', () => {
343
+ expect(sanitizeNumericParam('10')).toBe(10)
344
+ expect(sanitizeNumericParam('100')).toBe(100)
345
+ })
346
+
347
+ it('returns default for invalid values', () => {
348
+ expect(sanitizeNumericParam('invalid')).toBe(0)
349
+ expect(sanitizeNumericParam(NaN)).toBe(0)
350
+ expect(sanitizeNumericParam(Infinity)).toBe(0)
351
+ expect(sanitizeNumericParam(null)).toBe(0)
352
+ })
353
+
354
+ it('uses custom default value', () => {
355
+ expect(sanitizeNumericParam('invalid', 42)).toBe(42)
356
+ expect(sanitizeNumericParam(null, 100)).toBe(100)
357
+ })
358
+ })
359
+
360
+ describe('sanitizePort', () => {
361
+ it('returns valid ports', () => {
362
+ expect(sanitizePort(3000, 8080)).toBe(3000)
363
+ expect(sanitizePort(65535, 8080)).toBe(65535)
364
+ expect(sanitizePort(1, 8080)).toBe(1)
365
+ })
366
+
367
+ it('returns default for invalid port ranges', () => {
368
+ expect(sanitizePort(0, 8080)).toBe(8080)
369
+ expect(sanitizePort(65536, 8080)).toBe(8080)
370
+ expect(sanitizePort(-100, 8080)).toBe(8080)
371
+ })
372
+
373
+ it('parses string ports', () => {
374
+ expect(sanitizePort('3000', 8080)).toBe(3000)
375
+ expect(sanitizePort('5432', 5432)).toBe(5432)
376
+ })
377
+
378
+ it('returns default for invalid strings', () => {
379
+ expect(sanitizePort('invalid', 8080)).toBe(8080)
380
+ })
381
+ })
382
+
383
+ describe('isValidUrl', () => {
384
+ it('validates HTTP URLs', () => {
385
+ expect(isValidUrl('http://example.com')).toBe(true)
386
+ expect(isValidUrl('https://api.example.com/data')).toBe(true)
387
+ })
388
+
389
+ it('validates database URLs', () => {
390
+ expect(isValidUrl('postgresql://localhost:5432/db')).toBe(true)
391
+ expect(isValidUrl('mysql://localhost:3306/db')).toBe(true)
392
+ expect(isValidUrl('mongodb://localhost:27017/db')).toBe(true)
393
+ expect(isValidUrl('redis://localhost:6379')).toBe(true)
394
+ })
395
+
396
+ it('rejects invalid protocols', () => {
397
+ expect(isValidUrl('ftp://example.com')).toBe(false)
398
+ expect(isValidUrl('file:///path/to/file')).toBe(false)
399
+ })
400
+
401
+ it('rejects invalid URLs', () => {
402
+ expect(isValidUrl('not-a-url')).toBe(false)
403
+ expect(isValidUrl('')).toBe(false)
404
+ expect(isValidUrl(null)).toBe(false)
405
+ expect(isValidUrl(123)).toBe(false)
406
+ })
407
+ })
408
+
409
+ describe('sanitizeIdentifier', () => {
410
+ it('keeps safe characters', () => {
411
+ expect(sanitizeIdentifier('safe_identifier-123')).toBe('safe_identifier-123')
412
+ expect(sanitizeIdentifier('validName')).toBe('validName')
413
+ })
414
+
415
+ it('removes dangerous characters', () => {
416
+ expect(sanitizeIdentifier('unsafe<script>')).toBe('unsafescript')
417
+ expect(sanitizeIdentifier('path/../../../etc')).toBe('pathetc')
418
+ })
419
+
420
+ it('limits length to 64 characters', () => {
421
+ const longString = 'a'.repeat(100)
422
+ expect(sanitizeIdentifier(longString).length).toBe(64)
423
+ })
424
+
425
+ it('returns empty string for invalid input', () => {
426
+ expect(sanitizeIdentifier('')).toBe('')
427
+ expect(sanitizeIdentifier(null)).toBe('')
428
+ expect(sanitizeIdentifier(undefined)).toBe('')
429
+ })
430
+ })