insforge 0.3.1

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 (395) hide show
  1. package/.dockerignore +58 -0
  2. package/.env.example +49 -0
  3. package/.github/ISSUE_TEMPLATE/bug_report.yml +83 -0
  4. package/.github/ISSUE_TEMPLATE/config.yml +11 -0
  5. package/.github/ISSUE_TEMPLATE/feature_request.yml +79 -0
  6. package/.github/copilot-instructions.md +147 -0
  7. package/.github/workflows/build-image.yml +65 -0
  8. package/.github/workflows/ci-premerge-check.yml +24 -0
  9. package/.github/workflows/deploy-aws.yml +130 -0
  10. package/.github/workflows/lint-and-format.yml +33 -0
  11. package/.prettierignore +65 -0
  12. package/.prettierrc +9 -0
  13. package/CHANGELOG.md +3 -0
  14. package/CONTRIBUTING.md +126 -0
  15. package/Dockerfile +27 -0
  16. package/GITHUB_OAUTH_SETUP.md +49 -0
  17. package/GOOGLE_OAUTH_SETUP.md +148 -0
  18. package/LICENSE +201 -0
  19. package/README.md +134 -0
  20. package/assets/Dark.svg +23 -0
  21. package/assets/archDiagram.png +0 -0
  22. package/assets/banner.png +0 -0
  23. package/assets/mcpInstallv2.png +0 -0
  24. package/assets/sampleResponse.png +0 -0
  25. package/assets/signin.png +0 -0
  26. package/assets/userflow.png +0 -0
  27. package/backend/migrations/000_create-base-tables.sql +142 -0
  28. package/backend/migrations/001_create-helper-functions.sql +41 -0
  29. package/backend/migrations/002_rename-auth-tables.sql +30 -0
  30. package/backend/migrations/003_create-users-table.sql +56 -0
  31. package/backend/migrations/004_add-reload-postgrest-func.sql +24 -0
  32. package/backend/migrations/005_enable-project-admin-modify-users.sql +30 -0
  33. package/backend/migrations/006_modify-ai-usage-table.sql +25 -0
  34. package/backend/migrations/007_drop-metadata-table.sql +2 -0
  35. package/backend/migrations/008_add-system-tables.sql +77 -0
  36. package/backend/migrations/009_add-function-secrets.sql +24 -0
  37. package/backend/migrations/010_modify-ai-config-modalities.sql +93 -0
  38. package/backend/migrations/011_refactor-secrets-table.sql +15 -0
  39. package/backend/migrations/012_add-storage-uploaded-by.sql +8 -0
  40. package/backend/package.json +75 -0
  41. package/backend/src/api/middleware/auth.ts +240 -0
  42. package/backend/src/api/middleware/error.ts +231 -0
  43. package/backend/src/api/middleware/upload.ts +59 -0
  44. package/backend/src/api/routes/agent.ts +29 -0
  45. package/backend/src/api/routes/ai.ts +472 -0
  46. package/backend/src/api/routes/auth.oauth.ts +482 -0
  47. package/backend/src/api/routes/auth.ts +386 -0
  48. package/backend/src/api/routes/database.advance.ts +275 -0
  49. package/backend/src/api/routes/database.records.ts +246 -0
  50. package/backend/src/api/routes/database.tables.ts +161 -0
  51. package/backend/src/api/routes/docs.ts +66 -0
  52. package/backend/src/api/routes/functions.ts +183 -0
  53. package/backend/src/api/routes/logs.ts +150 -0
  54. package/backend/src/api/routes/metadata.ts +160 -0
  55. package/backend/src/api/routes/openapi.ts +82 -0
  56. package/backend/src/api/routes/secrets.ts +199 -0
  57. package/backend/src/api/routes/storage.ts +547 -0
  58. package/backend/src/api/routes/usage.ts +96 -0
  59. package/backend/src/core/ai/chat.ts +207 -0
  60. package/backend/src/core/ai/client.ts +242 -0
  61. package/backend/src/core/ai/config.ts +187 -0
  62. package/backend/src/core/ai/image.ts +156 -0
  63. package/backend/src/core/ai/model.ts +117 -0
  64. package/backend/src/core/ai/usage.ts +290 -0
  65. package/backend/src/core/auth/auth.ts +781 -0
  66. package/backend/src/core/auth/oauth.ts +398 -0
  67. package/backend/src/core/database/advance.ts +1074 -0
  68. package/backend/src/core/database/manager.ts +178 -0
  69. package/backend/src/core/database/table.ts +772 -0
  70. package/backend/src/core/documentation/agent.ts +689 -0
  71. package/backend/src/core/documentation/openapi.ts +856 -0
  72. package/backend/src/core/functions/functions.ts +310 -0
  73. package/backend/src/core/logs/analytics.ts +76 -0
  74. package/backend/src/core/logs/audit.ts +255 -0
  75. package/backend/src/core/logs/providers/base.provider.ts +83 -0
  76. package/backend/src/core/logs/providers/cloudwatch.provider.ts +510 -0
  77. package/backend/src/core/logs/providers/localdb.provider.ts +246 -0
  78. package/backend/src/core/secrets/encryption.ts +58 -0
  79. package/backend/src/core/secrets/secrets.ts +410 -0
  80. package/backend/src/core/socket/socket.ts +388 -0
  81. package/backend/src/core/socket/types.ts +79 -0
  82. package/backend/src/core/storage/storage.ts +923 -0
  83. package/backend/src/server.ts +288 -0
  84. package/backend/src/types/ai.ts +46 -0
  85. package/backend/src/types/auth.ts +90 -0
  86. package/backend/src/types/database.ts +136 -0
  87. package/backend/src/types/error-constants.ts +86 -0
  88. package/backend/src/types/logs.ts +47 -0
  89. package/backend/src/types/profile.ts +55 -0
  90. package/backend/src/types/storage.ts +23 -0
  91. package/backend/src/utils/cloud-token.ts +39 -0
  92. package/backend/src/utils/constants.ts +1 -0
  93. package/backend/src/utils/environment.ts +35 -0
  94. package/backend/src/utils/helpers.ts +49 -0
  95. package/backend/src/utils/logger.ts +13 -0
  96. package/backend/src/utils/response.ts +62 -0
  97. package/backend/src/utils/seed.ts +205 -0
  98. package/backend/src/utils/sql-parser.ts +63 -0
  99. package/backend/src/utils/uuid.ts +9 -0
  100. package/backend/src/utils/validations.ts +129 -0
  101. package/backend/tests/README.md +134 -0
  102. package/backend/tests/cleanup-all-test-data.sh +231 -0
  103. package/backend/tests/cloud/test-s3-multitenant.sh +132 -0
  104. package/backend/tests/local/comprehensive-curl-tests.sh +156 -0
  105. package/backend/tests/local/test-auth-router.sh +144 -0
  106. package/backend/tests/local/test-database-router.sh +222 -0
  107. package/backend/tests/local/test-e2e.sh +241 -0
  108. package/backend/tests/local/test-fk-errors.sh +97 -0
  109. package/backend/tests/local/test-id-field.sh +201 -0
  110. package/backend/tests/local/test-public-bucket.sh +265 -0
  111. package/backend/tests/local/test-secrets.sh +248 -0
  112. package/backend/tests/local/test-serverless-functions.sh.disabled +325 -0
  113. package/backend/tests/local/test-traditional-rest.sh +209 -0
  114. package/backend/tests/manual/README.md +51 -0
  115. package/backend/tests/manual/create-large-table-simple.sql +11 -0
  116. package/backend/tests/manual/seed-large-table.sql +101 -0
  117. package/backend/tests/manual/setup-large-table-extras.sql +34 -0
  118. package/backend/tests/manual/test-better-auth.sh +303 -0
  119. package/backend/tests/manual/test-bulk-upsert.sh +410 -0
  120. package/backend/tests/manual/test-database-advance.sh +297 -0
  121. package/backend/tests/manual/test-postgrest-stability.sh +192 -0
  122. package/backend/tests/manual/test-rawsql-export-import.sh +412 -0
  123. package/backend/tests/manual/test-universal-storage.sh +264 -0
  124. package/backend/tests/manual/test-users.sql +18 -0
  125. package/backend/tests/run-all-tests.sh +140 -0
  126. package/backend/tests/setup.ts +22 -0
  127. package/backend/tests/test-config.sh +303 -0
  128. package/backend/tsconfig.json +23 -0
  129. package/backend/tsup.config.ts +18 -0
  130. package/backend/vitest.config.ts +22 -0
  131. package/docker-compose.prod.yml +145 -0
  132. package/docker-compose.yml +167 -0
  133. package/docker-init/db/db-init.sql +125 -0
  134. package/docker-init/db/jwt.sql +5 -0
  135. package/docker-init/db/logs.sql +9 -0
  136. package/docker-init/db/postgresql.conf +17 -0
  137. package/docs/deprecated/insforge-auth-api.md +215 -0
  138. package/docs/deprecated/insforge-auth-sdk.md +100 -0
  139. package/docs/deprecated/insforge-db-api.md +359 -0
  140. package/docs/deprecated/insforge-db-sdk.md +140 -0
  141. package/docs/deprecated/insforge-debug-sdk.md +157 -0
  142. package/docs/deprecated/insforge-debug.md +65 -0
  143. package/docs/deprecated/insforge-instructions.md +124 -0
  144. package/docs/deprecated/insforge-project.md +118 -0
  145. package/docs/deprecated/insforge-storage-api.md +279 -0
  146. package/docs/deprecated/insforge-storage-sdk.md +159 -0
  147. package/docs/insforge-instructions-sdk.md +407 -0
  148. package/eslint.config.js +317 -0
  149. package/examples/oauth/frontend-oauth-example.html +251 -0
  150. package/examples/response-examples.md +444 -0
  151. package/frontend/README.md +112 -0
  152. package/frontend/components.json +17 -0
  153. package/frontend/index.html +13 -0
  154. package/frontend/package.json +63 -0
  155. package/frontend/public/favicon.ico +0 -0
  156. package/frontend/src/App.tsx +106 -0
  157. package/frontend/src/assets/icons/checkbox_checked.svg +6 -0
  158. package/frontend/src/assets/icons/checkbox_undetermined.svg +6 -0
  159. package/frontend/src/assets/icons/checked.svg +3 -0
  160. package/frontend/src/assets/icons/error.svg +3 -0
  161. package/frontend/src/assets/icons/pencil.svg +4 -0
  162. package/frontend/src/assets/icons/refresh.svg +4 -0
  163. package/frontend/src/assets/icons/step_active.svg +3 -0
  164. package/frontend/src/assets/icons/step_inactive.svg +11 -0
  165. package/frontend/src/assets/icons/warning.svg +3 -0
  166. package/frontend/src/assets/logos/amazon.svg +1 -0
  167. package/frontend/src/assets/logos/claude_code.svg +3 -0
  168. package/frontend/src/assets/logos/cline.svg +6 -0
  169. package/frontend/src/assets/logos/cursor.svg +20 -0
  170. package/frontend/src/assets/logos/discord.svg +9 -0
  171. package/frontend/src/assets/logos/gemini.svg +19 -0
  172. package/frontend/src/assets/logos/github.svg +5 -0
  173. package/frontend/src/assets/logos/google.svg +13 -0
  174. package/frontend/src/assets/logos/grok.svg +10 -0
  175. package/frontend/src/assets/logos/insforge_dark.svg +15 -0
  176. package/frontend/src/assets/logos/insforge_light.svg +15 -0
  177. package/frontend/src/assets/logos/openai.svg +10 -0
  178. package/frontend/src/assets/logos/roo_code.svg +9 -0
  179. package/frontend/src/assets/logos/trae.svg +3 -0
  180. package/frontend/src/assets/logos/windsurf.svg +10 -0
  181. package/frontend/src/components/ButtonWithLoading.tsx +27 -0
  182. package/frontend/src/components/Checkbox.tsx +61 -0
  183. package/frontend/src/components/CodeBlock.tsx +32 -0
  184. package/frontend/src/components/ConfirmDialog.tsx +96 -0
  185. package/frontend/src/components/CopyButton.tsx +69 -0
  186. package/frontend/src/components/DeleteActionButton.tsx +42 -0
  187. package/frontend/src/components/EmptyState.tsx +41 -0
  188. package/frontend/src/components/ErrorState.tsx +35 -0
  189. package/frontend/src/components/FeatureSidebar.tsx +126 -0
  190. package/frontend/src/components/FeatureSidebarItem.tsx +101 -0
  191. package/frontend/src/components/JsonHighlight.tsx +61 -0
  192. package/frontend/src/components/LoadingState.tsx +16 -0
  193. package/frontend/src/components/PaginationControls.tsx +54 -0
  194. package/frontend/src/components/PromptDialog.tsx +68 -0
  195. package/frontend/src/components/SearchInput.tsx +90 -0
  196. package/frontend/src/components/SelectionClearButton.tsx +26 -0
  197. package/frontend/src/components/Stepper.tsx +139 -0
  198. package/frontend/src/components/ThemeToggle.tsx +58 -0
  199. package/frontend/src/components/TypeBadge.tsx +20 -0
  200. package/frontend/src/components/datagrid/DataGrid.tsx +264 -0
  201. package/frontend/src/components/datagrid/DefaultCellRenderer.tsx +114 -0
  202. package/frontend/src/components/datagrid/IdCell.tsx +44 -0
  203. package/frontend/src/components/datagrid/SortableHeader.tsx +74 -0
  204. package/frontend/src/components/datagrid/cell-editors/BooleanCellEditor.tsx +54 -0
  205. package/frontend/src/components/datagrid/cell-editors/DateCellEditor.tsx +483 -0
  206. package/frontend/src/components/datagrid/cell-editors/JsonCellEditor.tsx +362 -0
  207. package/frontend/src/components/datagrid/cell-editors/TextCellEditor.tsx +38 -0
  208. package/frontend/src/components/datagrid/cell-editors/index.ts +14 -0
  209. package/frontend/src/components/datagrid/cell-editors/types.ts +43 -0
  210. package/frontend/src/components/datagrid/datagridTypes.tsx +72 -0
  211. package/frontend/src/components/datagrid/index.tsx +20 -0
  212. package/frontend/src/components/index.ts +39 -0
  213. package/frontend/src/components/layout/AppHeader.tsx +146 -0
  214. package/frontend/src/components/layout/AppSidebar.tsx +190 -0
  215. package/frontend/src/components/layout/CloudLayout.tsx +95 -0
  216. package/frontend/src/components/layout/Layout.tsx +43 -0
  217. package/frontend/src/components/radix/Alert.tsx +45 -0
  218. package/frontend/src/components/radix/AlertDialog.tsx +115 -0
  219. package/frontend/src/components/radix/Avatar.tsx +45 -0
  220. package/frontend/src/components/radix/Badge.tsx +33 -0
  221. package/frontend/src/components/radix/Button.tsx +50 -0
  222. package/frontend/src/components/radix/Card.tsx +58 -0
  223. package/frontend/src/components/radix/Dialog.tsx +98 -0
  224. package/frontend/src/components/radix/DropdownMenu.tsx +185 -0
  225. package/frontend/src/components/radix/Form.tsx +167 -0
  226. package/frontend/src/components/radix/Input.tsx +22 -0
  227. package/frontend/src/components/radix/Label.tsx +19 -0
  228. package/frontend/src/components/radix/Popover.tsx +29 -0
  229. package/frontend/src/components/radix/ScrollArea.tsx +44 -0
  230. package/frontend/src/components/radix/Select.tsx +151 -0
  231. package/frontend/src/components/radix/Separator.tsx +26 -0
  232. package/frontend/src/components/radix/Sheet.tsx +119 -0
  233. package/frontend/src/components/radix/Skeleton.tsx +7 -0
  234. package/frontend/src/components/radix/Switch.tsx +29 -0
  235. package/frontend/src/components/radix/Tabs.tsx +50 -0
  236. package/frontend/src/components/radix/Textarea.tsx +21 -0
  237. package/frontend/src/components/radix/Tooltip.tsx +28 -0
  238. package/frontend/src/features/ai/components/AIConfigCard.tsx +154 -0
  239. package/frontend/src/features/ai/components/AIConfigDialog.tsx +76 -0
  240. package/frontend/src/features/ai/components/AIConfigForm.tsx +222 -0
  241. package/frontend/src/features/ai/components/AIEmptyState.tsx +18 -0
  242. package/frontend/src/features/ai/components/fields/ModalityField.tsx +87 -0
  243. package/frontend/src/features/ai/components/fields/ModelSelectionField.tsx +134 -0
  244. package/frontend/src/features/ai/components/fields/SystemPromptField.tsx +33 -0
  245. package/frontend/src/features/ai/helpers.ts +155 -0
  246. package/frontend/src/features/ai/hooks/useAIConfigs.ts +221 -0
  247. package/frontend/src/features/ai/hooks/useAIUsage.ts +77 -0
  248. package/frontend/src/features/ai/page/AIPage.tsx +178 -0
  249. package/frontend/src/features/ai/services/ai.service.ts +148 -0
  250. package/frontend/src/features/auth/components/AddOAuthDialog.tsx +106 -0
  251. package/frontend/src/features/auth/components/AuthMethodTab.tsx +238 -0
  252. package/frontend/src/features/auth/components/OAuthConfigDialog.tsx +303 -0
  253. package/frontend/src/features/auth/components/OAuthEmptyState.tsx +15 -0
  254. package/frontend/src/features/auth/components/UserFormDialog.tsx +248 -0
  255. package/frontend/src/features/auth/components/UsersDataGrid.tsx +183 -0
  256. package/frontend/src/features/auth/components/UsersTab.tsx +114 -0
  257. package/frontend/src/features/auth/hooks/useOAuthConfig.ts +129 -0
  258. package/frontend/src/features/auth/hooks/useUsers.ts +57 -0
  259. package/frontend/src/features/auth/index.ts +9 -0
  260. package/frontend/src/features/auth/page/AuthenticationPage.tsx +169 -0
  261. package/frontend/src/features/auth/services/auth.service.ts +112 -0
  262. package/frontend/src/features/auth/services/oauth.service.ts +49 -0
  263. package/frontend/src/features/dashboard/page/DashboardPage.tsx +194 -0
  264. package/frontend/src/features/database/components/ColumnTypeSelect.tsx +64 -0
  265. package/frontend/src/features/database/components/DatabaseDataGrid.tsx +282 -0
  266. package/frontend/src/features/database/components/ForeignKeyCell.tsx +187 -0
  267. package/frontend/src/features/database/components/ForeignKeyPopover.tsx +378 -0
  268. package/frontend/src/features/database/components/LinkRecordModal.tsx +288 -0
  269. package/frontend/src/features/database/components/RecordFormDialog.tsx +164 -0
  270. package/frontend/src/features/database/components/RecordFormField.tsx +568 -0
  271. package/frontend/src/features/database/components/TableEmptyState.tsx +21 -0
  272. package/frontend/src/features/database/components/TableForm.tsx +656 -0
  273. package/frontend/src/features/database/components/TableFormColumn.tsx +137 -0
  274. package/frontend/src/features/database/components/TableListSkeleton.tsx +9 -0
  275. package/frontend/src/features/database/components/TableSidebar.tsx +47 -0
  276. package/frontend/src/features/database/constants.ts +26 -0
  277. package/frontend/src/features/database/helpers.ts +125 -0
  278. package/frontend/src/features/database/hooks/UseLinkModal.tsx +78 -0
  279. package/frontend/src/features/database/index.ts +12 -0
  280. package/frontend/src/features/database/page/DatabasePage.tsx +626 -0
  281. package/frontend/src/features/database/schema.ts +25 -0
  282. package/frontend/src/features/database/services/database.service.ts +216 -0
  283. package/frontend/src/features/functions/components/FunctionEmptyState.tsx +15 -0
  284. package/frontend/src/features/functions/components/FunctionRow.tsx +71 -0
  285. package/frontend/src/features/functions/components/FunctionViewer.tsx +46 -0
  286. package/frontend/src/features/functions/components/FunctionsContent.tsx +88 -0
  287. package/frontend/src/features/functions/components/FunctionsSidebar.tsx +56 -0
  288. package/frontend/src/features/functions/components/SecretEmptyState.tsx +23 -0
  289. package/frontend/src/features/functions/components/SecretRow.tsx +68 -0
  290. package/frontend/src/features/functions/components/SecretsContent.tsx +120 -0
  291. package/frontend/src/features/functions/hooks/useFunctions.ts +106 -0
  292. package/frontend/src/features/functions/page/FunctionsPage.tsx +28 -0
  293. package/frontend/src/features/functions/services/functions.service.ts +48 -0
  294. package/frontend/src/features/login/components/AuthErrorBoundary.tsx +87 -0
  295. package/frontend/src/features/login/components/PrivateRoute.tsx +24 -0
  296. package/frontend/src/features/login/page/CloudLoginPage.tsx +93 -0
  297. package/frontend/src/features/login/page/LoginPage.tsx +174 -0
  298. package/frontend/src/features/logs/components/AnalyticsLogsTable.tsx +313 -0
  299. package/frontend/src/features/logs/components/LogsTable.tsx +199 -0
  300. package/frontend/src/features/logs/hooks/useAuditLogs.ts +39 -0
  301. package/frontend/src/features/logs/index.ts +5 -0
  302. package/frontend/src/features/logs/page/AnalyticsLogsPage.tsx +530 -0
  303. package/frontend/src/features/logs/page/AuditsPage.tsx +192 -0
  304. package/frontend/src/features/logs/services/log.service.ts +171 -0
  305. package/frontend/src/features/metadata/hooks/useMetadata.ts +53 -0
  306. package/frontend/src/features/metadata/index.ts +0 -0
  307. package/frontend/src/features/metadata/page/MetadataPage.tsx +136 -0
  308. package/frontend/src/features/metadata/services/metadata.service.ts +17 -0
  309. package/frontend/src/features/onboard/components/CompletionCard.tsx +41 -0
  310. package/frontend/src/features/onboard/components/OnboardButton.tsx +84 -0
  311. package/frontend/src/features/onboard/components/StepContent.tsx +91 -0
  312. package/frontend/src/features/onboard/components/TestConnectionStep.tsx +53 -0
  313. package/frontend/src/features/onboard/components/mcp/CursorDeeplinkGenerator.tsx +35 -0
  314. package/frontend/src/features/onboard/components/mcp/McpInstallation.tsx +144 -0
  315. package/frontend/src/features/onboard/components/mcp/index.ts +4 -0
  316. package/frontend/src/features/onboard/components/mcp/mcp-helper.tsx +98 -0
  317. package/frontend/src/features/onboard/index.ts +3 -0
  318. package/frontend/src/features/onboard/page/OnBoardPage.tsx +104 -0
  319. package/frontend/src/features/onboard/types.ts +8 -0
  320. package/frontend/src/features/secrets/hooks/useSecrets.ts +139 -0
  321. package/frontend/src/features/secrets/services/secrets.service.ts +57 -0
  322. package/frontend/src/features/storage/components/BucketEmptyState.tsx +19 -0
  323. package/frontend/src/features/storage/components/BucketFormDialog.tsx +194 -0
  324. package/frontend/src/features/storage/components/BucketListSkeleton.tsx +17 -0
  325. package/frontend/src/features/storage/components/FilePreviewDialog.tsx +287 -0
  326. package/frontend/src/features/storage/components/StorageDataGrid.tsx +239 -0
  327. package/frontend/src/features/storage/components/StorageManager.tsx +236 -0
  328. package/frontend/src/features/storage/components/StorageSidebar.tsx +44 -0
  329. package/frontend/src/features/storage/components/UploadToast.tsx +46 -0
  330. package/frontend/src/features/storage/index.ts +3 -0
  331. package/frontend/src/features/storage/page/StoragePage.tsx +553 -0
  332. package/frontend/src/features/storage/services/storage.service.ts +144 -0
  333. package/frontend/src/features/visualizer/components/AuthNode.tsx +107 -0
  334. package/frontend/src/features/visualizer/components/BucketNode.tsx +34 -0
  335. package/frontend/src/features/visualizer/components/SchemaVisualizer.tsx +359 -0
  336. package/frontend/src/features/visualizer/components/TableNode.tsx +152 -0
  337. package/frontend/src/features/visualizer/components/VisualizerSkeleton.tsx +24 -0
  338. package/frontend/src/features/visualizer/components/index.ts +5 -0
  339. package/frontend/src/features/visualizer/page/VisualizerPage.tsx +127 -0
  340. package/frontend/src/index.css +248 -0
  341. package/frontend/src/lib/api/client.ts +163 -0
  342. package/frontend/src/lib/contexts/AuthContext.tsx +157 -0
  343. package/frontend/src/lib/contexts/OnboardStepContext.tsx +68 -0
  344. package/frontend/src/lib/contexts/SocketContext.tsx +303 -0
  345. package/frontend/src/lib/contexts/ThemeContext.tsx +125 -0
  346. package/frontend/src/lib/hooks/useAuth.ts +4 -0
  347. package/frontend/src/lib/hooks/useConfirm.ts +55 -0
  348. package/frontend/src/lib/hooks/useInterval.ts +27 -0
  349. package/frontend/src/lib/hooks/useMediaQuery.ts +59 -0
  350. package/frontend/src/lib/hooks/useOnboardingCompletion.ts +29 -0
  351. package/frontend/src/lib/hooks/usePagination.ts +27 -0
  352. package/frontend/src/lib/hooks/useTimeout.ts +27 -0
  353. package/frontend/src/lib/hooks/useToast.tsx +229 -0
  354. package/frontend/src/lib/utils/constants.ts +38 -0
  355. package/frontend/src/lib/utils/utils.ts +165 -0
  356. package/frontend/src/lib/utils/validation-schemas.ts +126 -0
  357. package/frontend/src/main.tsx +16 -0
  358. package/frontend/src/rdg.css +194 -0
  359. package/frontend/src/vite-env.d.ts +12 -0
  360. package/frontend/tailwind.config.js +97 -0
  361. package/frontend/tsconfig.json +26 -0
  362. package/frontend/tsconfig.node.json +10 -0
  363. package/frontend/vite.config.ts +37 -0
  364. package/frontend/vitest.config.ts +36 -0
  365. package/functions/deno.json +25 -0
  366. package/functions/server.ts +290 -0
  367. package/functions/worker-template.js +126 -0
  368. package/openapi/ai.yaml +689 -0
  369. package/openapi/auth.yaml +563 -0
  370. package/openapi/functions.yaml +476 -0
  371. package/openapi/health.yaml +30 -0
  372. package/openapi/logs.yaml +224 -0
  373. package/openapi/metadata.yaml +178 -0
  374. package/openapi/records.yaml +382 -0
  375. package/openapi/secrets.yaml +371 -0
  376. package/openapi/storage.yaml +876 -0
  377. package/openapi/tables.yaml +464 -0
  378. package/package.json +88 -0
  379. package/shared-schemas/package.json +31 -0
  380. package/shared-schemas/src/ai-api.schema.ts +167 -0
  381. package/shared-schemas/src/ai.schema.ts +54 -0
  382. package/shared-schemas/src/auth-api.schema.ts +193 -0
  383. package/shared-schemas/src/auth.schema.ts +94 -0
  384. package/shared-schemas/src/database-api.schema.ts +259 -0
  385. package/shared-schemas/src/database.schema.ts +69 -0
  386. package/shared-schemas/src/functions-api.schema.ts +25 -0
  387. package/shared-schemas/src/functions.schema.ts +16 -0
  388. package/shared-schemas/src/index.ts +13 -0
  389. package/shared-schemas/src/logs-api.schema.ts +49 -0
  390. package/shared-schemas/src/logs.schema.ts +14 -0
  391. package/shared-schemas/src/metadata.schema.ts +56 -0
  392. package/shared-schemas/src/storage-api.schema.ts +65 -0
  393. package/shared-schemas/src/storage.schema.ts +19 -0
  394. package/shared-schemas/tsconfig.json +21 -0
  395. package/tsconfig.json +8 -0
@@ -0,0 +1,107 @@
1
+ import { Lock, FormInput, Users } from 'lucide-react';
2
+ import GoogleIcon from '@/assets/logos/google.svg';
3
+ import GithubIcon from '@/assets/logos/github.svg';
4
+ import { AuthMetadataSchema } from '@insforge/shared-schemas';
5
+ import { cn } from '@/lib/utils/utils';
6
+ import { useOAuthConfig } from '@/features/auth/hooks/useOAuthConfig';
7
+
8
+ interface AuthNodeProps {
9
+ data: {
10
+ authMetadata: AuthMetadataSchema;
11
+ userCount?: number;
12
+ };
13
+ }
14
+
15
+ export function AuthNode({ data }: AuthNodeProps) {
16
+ const { authMetadata, userCount } = data;
17
+ const { isProviderConfigured } = useOAuthConfig();
18
+
19
+ const enabledCount = authMetadata.oauths.length;
20
+
21
+ return (
22
+ <div className="bg-neutral-900 rounded-lg border border-[#363636] min-w-[280px]">
23
+ {/* Auth Header */}
24
+ <div className="flex items-center justify-between p-2 border-b border-neutral-800">
25
+ <div className="flex items-center gap-2">
26
+ <div className="flex items-center justify-center w-11 h-11 bg-lime-300 rounded p-1.5">
27
+ <Lock className="w-5 h-5 text-neutral-900" />
28
+ </div>
29
+ <div className="flex-1">
30
+ <h3 className="text-sm font-medium text-white">Authentication</h3>
31
+ <p className="text-xs text-neutral-300">
32
+ {enabledCount} provider{enabledCount !== 1 ? 's' : ''} enabled
33
+ </p>
34
+ </div>
35
+ </div>
36
+ {/* <div className="p-1.5">
37
+ <ExternalLink className="w-4 h-4 text-neutral-400" />
38
+ </div> */}
39
+ </div>
40
+
41
+ {/* Auth Providers */}
42
+ <div className="p-2 space-y-2 border-b border-neutral-800">
43
+ {/* Email/Password */}
44
+ <div className="flex items-center justify-between p-2.5 bg-neutral-800 rounded">
45
+ <div className="flex items-center gap-2.5">
46
+ <FormInput className="w-5 h-5 text-neutral-300" />
47
+ <span className="text-sm text-neutral-300">Email/Password</span>
48
+ </div>
49
+ <div className="px-1.5 py-0.5 bg-lime-200 rounded flex items-center">
50
+ <span className="text-xs font-medium text-lime-900">Enabled</span>
51
+ </div>
52
+ </div>
53
+
54
+ {/* Google OAuth */}
55
+ <div className="flex items-center justify-between p-2.5 bg-neutral-800 rounded">
56
+ <div className="flex items-center gap-2.5">
57
+ <img src={GoogleIcon} alt="google" className="h-5 w-5" />
58
+ <span className="text-sm text-neutral-300">Google OAuth</span>
59
+ </div>
60
+ <div
61
+ className={cn(
62
+ 'px-1.5 py-0.5 rounded flex items-center',
63
+ isProviderConfigured('google')
64
+ ? 'bg-lime-200 text-lime-900'
65
+ : 'bg-neutral-700 text-neutral-300'
66
+ )}
67
+ >
68
+ <span className="text-xs font-medium">
69
+ {isProviderConfigured('google') ? 'Enabled' : 'Disabled'}
70
+ </span>
71
+ </div>
72
+ </div>
73
+
74
+ {/* GitHub OAuth */}
75
+ <div className="flex items-center justify-between p-2.5 bg-neutral-800 rounded">
76
+ <div className="flex items-center gap-2.5">
77
+ <img src={GithubIcon} alt="github" className="h-5 w-5" />
78
+ <span className="text-sm text-neutral-300">GitHub OAuth</span>
79
+ </div>
80
+ <div
81
+ className={cn(
82
+ 'px-1.5 py-0.5 rounded flex items-center',
83
+ isProviderConfigured('github')
84
+ ? 'bg-lime-200 text-lime-900'
85
+ : 'bg-neutral-700 text-neutral-300'
86
+ )}
87
+ >
88
+ <span className="text-xs font-medium">
89
+ {isProviderConfigured('github') ? 'Enabled' : 'Disabled'}
90
+ </span>
91
+ </div>
92
+ </div>
93
+ </div>
94
+
95
+ {/* Users Section */}
96
+ <div className="flex items-center justify-between p-3 border-t border-neutral-700">
97
+ <div className="flex items-center gap-2.5">
98
+ <Users className="w-5 h-5 text-neutral-300" />
99
+ <span className="text-sm text-neutral-300">Users</span>
100
+ </div>
101
+ <div className="flex items-center">
102
+ {userCount !== undefined && <span className="text-xs text-neutral-400">{userCount}</span>}
103
+ </div>
104
+ </div>
105
+ </div>
106
+ );
107
+ }
@@ -0,0 +1,34 @@
1
+ import { HardDrive } from 'lucide-react';
2
+ import { BucketMetadataSchema } from '@insforge/shared-schemas';
3
+
4
+ interface BucketNodeProps {
5
+ data: {
6
+ bucket: BucketMetadataSchema;
7
+ };
8
+ }
9
+
10
+ export function BucketNode({ data }: BucketNodeProps) {
11
+ const { bucket } = data;
12
+
13
+ return (
14
+ <div className="bg-neutral-900 rounded-lg border border-[#363636] min-w-[320px]">
15
+ {/* Bucket Header */}
16
+ <div className="flex items-center justify-between p-2 border-b border-neutral-800">
17
+ <div className="flex items-center gap-2">
18
+ <div className="flex items-center justify-center w-11 h-11 bg-blue-300 rounded p-1.5">
19
+ <HardDrive className="w-5 h-5 text-neutral-900" />
20
+ </div>
21
+ <div className="flex-1">
22
+ <h3 className="text-sm font-medium text-white">{bucket.name}</h3>
23
+ <p className="text-xs text-neutral-300">
24
+ {bucket.objectCount ? `${bucket.objectCount} files` : '0 files'}
25
+ </p>
26
+ </div>
27
+ </div>
28
+ {/* <div className="p-1.5">
29
+ <ExternalLink className="w-4 h-4 text-neutral-400" />
30
+ </div> */}
31
+ </div>
32
+ </div>
33
+ );
34
+ }
@@ -0,0 +1,359 @@
1
+ import { useCallback, useEffect, useMemo } from 'react';
2
+ import {
3
+ ReactFlow,
4
+ Node,
5
+ BuiltInEdge,
6
+ Controls,
7
+ MiniMap,
8
+ useNodesState,
9
+ useEdgesState,
10
+ addEdge,
11
+ Connection,
12
+ ConnectionMode,
13
+ } from '@xyflow/react';
14
+ import '@xyflow/react/dist/style.css';
15
+ import { TableNode, VisualizerTableSchema, VisualizerTableColumnSchema } from './TableNode';
16
+ import { AuthNode } from './AuthNode';
17
+ import { BucketNode } from './BucketNode';
18
+ import {
19
+ AppMetadataSchema,
20
+ StorageBucketSchema,
21
+ AuthMetadataSchema,
22
+ } from '@insforge/shared-schemas';
23
+
24
+ interface SchemaVisualizerProps {
25
+ metadata: AppMetadataSchema;
26
+ userCount?: number;
27
+ }
28
+
29
+ type TableNodeData = {
30
+ table: VisualizerTableSchema;
31
+ referencedColumns: string[];
32
+ };
33
+
34
+ type BucketNodeData = {
35
+ bucket: StorageBucketSchema;
36
+ };
37
+
38
+ type AuthNodeData = {
39
+ authMetadata: AuthMetadataSchema;
40
+ userCount?: number;
41
+ };
42
+
43
+ type CustomNodeData = TableNodeData | BucketNodeData | AuthNodeData;
44
+
45
+ const nodeTypes = {
46
+ tableNode: TableNode,
47
+ authNode: AuthNode,
48
+ bucketNode: BucketNode,
49
+ };
50
+
51
+ const getLayoutedElements = (nodes: Node<CustomNodeData>[], edges: BuiltInEdge[]) => {
52
+ // Fixed dimensions
53
+ const nodeWidth = 280;
54
+
55
+ // Calculate actual node heights based on content
56
+ const calculateNodeHeight = (node: Node<CustomNodeData>) => {
57
+ if (node.type === 'authNode') {
58
+ // Auth node has fixed content
59
+ return 150;
60
+ } else if (node.type === 'tableNode') {
61
+ // Table node height depends on columns
62
+ const tableData = node.data as TableNodeData;
63
+ const columnCount = tableData.table?.columns?.length || 0;
64
+ const headerHeight = 64; // Header with table name
65
+ const columnHeight = 48; // Each column row height
66
+ const contentHeight = columnCount > 0 ? columnCount * columnHeight : 100; // Empty state height
67
+ return headerHeight + contentHeight;
68
+ } else if (node.type === 'bucketNode') {
69
+ // Bucket node has relatively fixed height
70
+ return 200;
71
+ }
72
+ return 200; // Default
73
+ };
74
+
75
+ // Layout parameters
76
+ const horizontalGap = 100; // Gap between columns
77
+ const verticalGap = 80; // Gap between nodes in same column
78
+ const canvasMargin = 50;
79
+
80
+ // Group nodes by type
81
+ const authNodes = nodes.filter((node) => node.type === 'authNode');
82
+ const tableNodes = nodes.filter((node) => node.type === 'tableNode');
83
+ const bucketNodes = nodes.filter((node) => node.type === 'bucketNode');
84
+
85
+ // Calculate column X positions
86
+ const authX = canvasMargin;
87
+ const tableStartX = authX + nodeWidth + horizontalGap * 2;
88
+ const bucketX =
89
+ tableStartX +
90
+ Math.ceil(Math.sqrt(tableNodes.length)) * (nodeWidth + horizontalGap) +
91
+ horizontalGap;
92
+
93
+ // Helper function to distribute nodes vertically with dynamic heights
94
+ const distributeVerticallyDynamic = (
95
+ nodesToPosition: Node<CustomNodeData>[],
96
+ startY: number = canvasMargin
97
+ ) => {
98
+ const positions: number[] = [];
99
+ let currentY = startY;
100
+
101
+ nodesToPosition.forEach((node) => {
102
+ positions.push(currentY);
103
+ const nodeHeight = calculateNodeHeight(node);
104
+ currentY += nodeHeight + verticalGap;
105
+ });
106
+
107
+ return positions;
108
+ };
109
+
110
+ // Position auth nodes in left column
111
+ const authYPositions = distributeVerticallyDynamic(authNodes);
112
+ const positionedAuthNodes = authNodes.map((node, index) => ({
113
+ ...node,
114
+ position: {
115
+ x: authX,
116
+ y: authYPositions[index],
117
+ },
118
+ }));
119
+
120
+ // Position table nodes in a grid in the middle
121
+ let positionedTableNodes: Node<CustomNodeData>[] = [];
122
+ if (tableNodes.length > 0) {
123
+ const cols = Math.ceil(Math.sqrt(tableNodes.length));
124
+
125
+ // Group tables by column for better height calculation
126
+ const tablesByColumn: Node<CustomNodeData>[][] = [];
127
+ for (let col = 0; col < cols; col++) {
128
+ tablesByColumn[col] = [];
129
+ }
130
+
131
+ tableNodes.forEach((node, index) => {
132
+ const col = index % cols;
133
+ tablesByColumn[col].push(node);
134
+ });
135
+
136
+ // Calculate Y positions for each column independently
137
+ const columnYPositions: number[][] = tablesByColumn.map((columnNodes) =>
138
+ distributeVerticallyDynamic(columnNodes)
139
+ );
140
+
141
+ positionedTableNodes = tableNodes.map((node, index) => {
142
+ const col = index % cols;
143
+ const rowInColumn = Math.floor(index / cols);
144
+
145
+ return {
146
+ ...node,
147
+ position: {
148
+ x: tableStartX + col * (nodeWidth + horizontalGap),
149
+ y: columnYPositions[col][rowInColumn],
150
+ },
151
+ };
152
+ });
153
+ }
154
+
155
+ // Position bucket nodes in right column
156
+ const bucketYPositions = distributeVerticallyDynamic(bucketNodes);
157
+ const positionedBucketNodes = bucketNodes.map((node, index) => ({
158
+ ...node,
159
+ position: {
160
+ x: bucketX,
161
+ y: bucketYPositions[index],
162
+ },
163
+ }));
164
+
165
+ // Combine all positioned nodes
166
+ const layoutedNodes = [...positionedAuthNodes, ...positionedTableNodes, ...positionedBucketNodes];
167
+
168
+ return { nodes: layoutedNodes, edges };
169
+ };
170
+
171
+ const getNodeColor = (node: Node<CustomNodeData>) => {
172
+ switch (node.type) {
173
+ case 'authNode':
174
+ return '#bef264';
175
+ case 'bucketNode':
176
+ return '#93c5fd';
177
+ default:
178
+ return '#6ee7b7';
179
+ }
180
+ };
181
+
182
+ export function SchemaVisualizer({ metadata, userCount }: SchemaVisualizerProps) {
183
+ // Transform the new metadata structure to the visualizer format
184
+ const tables = useMemo(() => {
185
+ const tablesRecord = metadata.database.tables;
186
+ return Object.entries(tablesRecord).map(([tableName, tableData]): VisualizerTableSchema => {
187
+ // Check for primary key columns from indexes
188
+ const primaryKeyColumns = new Set<string>();
189
+
190
+ tableData.indexes.forEach((index) => {
191
+ if (index.isPrimary) {
192
+ // Extract column names from index definition
193
+ const match = index.indexdef.match(/\(([^)]+)\)/);
194
+ if (match) {
195
+ match[1].split(',').forEach((col) => {
196
+ primaryKeyColumns.add(col.trim().replace(/"/g, ''));
197
+ });
198
+ }
199
+ }
200
+ });
201
+
202
+ // Transform columns from the new schema format
203
+ const columns = tableData.schema.map((col) => {
204
+ const column: VisualizerTableColumnSchema = {
205
+ columnName: col.columnName,
206
+ type: col.dataType.toLowerCase().substring(0, 4), // Truncate type to first 4 characters
207
+ isPrimaryKey: primaryKeyColumns.has(col.columnName),
208
+ };
209
+
210
+ // Find foreign key info for this column
211
+ const foreignKey = tableData.foreignKeys.find((fk) => fk.columnName === col.columnName);
212
+ if (foreignKey) {
213
+ column.foreignKey = {
214
+ referenceTable: foreignKey.foreignTableName,
215
+ referenceColumn: foreignKey.foreignColumnName,
216
+ };
217
+ }
218
+
219
+ return column;
220
+ });
221
+
222
+ return {
223
+ tableName,
224
+ columns,
225
+ recordCount: tableData.recordCount ?? 0, // Use actual record count from metadata
226
+ };
227
+ });
228
+ }, [metadata.database.tables]);
229
+
230
+ const initialNodes = useMemo(() => {
231
+ // First, collect all referenced columns for each table
232
+ const referencedColumnsByTable: Record<string, string[]> = {};
233
+
234
+ tables.forEach((table) => {
235
+ table.columns.forEach((column) => {
236
+ if (column.foreignKey) {
237
+ const targetTable = column.foreignKey.referenceTable;
238
+ const targetColumn = column.foreignKey.referenceColumn;
239
+
240
+ if (!referencedColumnsByTable[targetTable]) {
241
+ referencedColumnsByTable[targetTable] = [];
242
+ }
243
+ if (!referencedColumnsByTable[targetTable].includes(targetColumn)) {
244
+ referencedColumnsByTable[targetTable].push(targetColumn);
245
+ }
246
+ }
247
+ });
248
+ });
249
+
250
+ const tableNodes: Node<TableNodeData>[] = tables.map((table) => ({
251
+ id: table.tableName,
252
+ type: 'tableNode',
253
+ position: { x: 0, y: 0 },
254
+ data: {
255
+ table,
256
+ referencedColumns: referencedColumnsByTable[table.tableName] || [],
257
+ },
258
+ }));
259
+
260
+ const bucketNodes: Node<BucketNodeData>[] = metadata.storage.buckets.map((bucket) => ({
261
+ id: `bucket-${bucket.name}`,
262
+ type: 'bucketNode',
263
+ position: { x: 0, y: 0 },
264
+ data: { bucket },
265
+ }));
266
+
267
+ const nodes: Node<CustomNodeData>[] = [...tableNodes, ...bucketNodes];
268
+
269
+ // Add authentication node if authData is provided
270
+ nodes.push({
271
+ id: 'authentication',
272
+ type: 'authNode',
273
+ position: { x: 0, y: 0 },
274
+ data: {
275
+ authMetadata: metadata.auth,
276
+ userCount,
277
+ },
278
+ });
279
+
280
+ return nodes;
281
+ }, [metadata, userCount, tables]);
282
+
283
+ const initialEdges = useMemo(() => {
284
+ const edges: BuiltInEdge[] = [];
285
+
286
+ tables.forEach((table) => {
287
+ table.columns.forEach((column) => {
288
+ if (column.foreignKey) {
289
+ const edgeId = `${table.tableName}-${column.columnName}-${column.foreignKey.referenceTable}`;
290
+ edges.push({
291
+ id: edgeId,
292
+ source: table.tableName,
293
+ target: column.foreignKey.referenceTable,
294
+ sourceHandle: `${column.columnName}-source`,
295
+ targetHandle: `${column.foreignKey.referenceColumn}-target`,
296
+ type: 'smoothstep',
297
+ animated: true,
298
+ style: { stroke: 'white', strokeWidth: 2, zIndex: 1000 },
299
+ zIndex: 1000,
300
+ pathOptions: {
301
+ offset: 40,
302
+ },
303
+ });
304
+ }
305
+ });
306
+ });
307
+
308
+ // Add authentication edges if authData exists
309
+
310
+ return edges;
311
+ }, [tables]);
312
+
313
+ const { nodes: layoutedNodes, edges: layoutedEdges } = useMemo(
314
+ () => getLayoutedElements(initialNodes, initialEdges),
315
+ [initialNodes, initialEdges]
316
+ );
317
+
318
+ const [nodes, setNodes, onNodesChange] = useNodesState(layoutedNodes);
319
+ const [edges, setEdges, onEdgesChange] = useEdgesState(layoutedEdges);
320
+
321
+ useEffect(() => {
322
+ setNodes(layoutedNodes);
323
+ setEdges(layoutedEdges);
324
+ }, [layoutedNodes, layoutedEdges, setNodes, setEdges]);
325
+
326
+ const onConnect = useCallback(
327
+ (params: Connection) => setEdges((eds) => addEdge(params, eds)),
328
+ [setEdges]
329
+ );
330
+
331
+ return (
332
+ <div className="w-full h-full">
333
+ <ReactFlow
334
+ nodes={nodes}
335
+ edges={edges}
336
+ onNodesChange={onNodesChange}
337
+ onEdgesChange={onEdgesChange}
338
+ onConnect={onConnect}
339
+ nodeTypes={nodeTypes}
340
+ connectionMode={ConnectionMode.Loose}
341
+ fitView
342
+ fitViewOptions={{ padding: 1, maxZoom: 2, minZoom: 1 }}
343
+ minZoom={0.1}
344
+ maxZoom={2}
345
+ proOptions={{ hideAttribution: true }}
346
+ elevateEdgesOnSelect={true}
347
+ colorMode="dark"
348
+ className="!bg-transparent"
349
+ >
350
+ <Controls
351
+ showInteractive={false}
352
+ className="!border !border-neutral-700 !shadow-lg"
353
+ fitViewOptions={{ padding: 1, duration: 300, maxZoom: 2, minZoom: 1 }}
354
+ />
355
+ <MiniMap nodeColor={(node: Node<CustomNodeData>) => getNodeColor(node)} />
356
+ </ReactFlow>
357
+ </div>
358
+ );
359
+ }
@@ -0,0 +1,152 @@
1
+ import { Database, Circle, Key } from 'lucide-react';
2
+ import { Handle, Position } from '@xyflow/react';
3
+
4
+ // Define the table structure expected by this component
5
+ export interface VisualizerTableColumnSchema {
6
+ columnName: string;
7
+ type: string;
8
+ isPrimaryKey?: boolean;
9
+ foreignKey?: {
10
+ referenceTable: string;
11
+ referenceColumn: string;
12
+ };
13
+ }
14
+
15
+ export interface VisualizerTableSchema {
16
+ tableName: string;
17
+ columns: VisualizerTableColumnSchema[];
18
+ recordCount?: number;
19
+ }
20
+
21
+ interface TableNodeProps {
22
+ data: {
23
+ table: VisualizerTableSchema;
24
+ referencedColumns?: string[]; // List of column names that are referenced by other tables
25
+ };
26
+ }
27
+
28
+ export function TableNode({ data }: TableNodeProps) {
29
+ const { table, referencedColumns = [] } = data;
30
+
31
+ const getColumnIcon = (isReferenced: boolean = false) => {
32
+ // If column is referenced by another table (has incoming connections)
33
+ // Show outer gray diamond with inner white diamond
34
+ if (isReferenced) {
35
+ return (
36
+ <div className="w-4 h-4 flex items-center justify-center relative">
37
+ {/* Outer gray diamond */}
38
+ <div className="w-4 h-4 bg-neutral-800 border border-white absolute transform rotate-45" />
39
+ {/* Inner white diamond */}
40
+ <div className="w-2 h-2 bg-white absolute transform rotate-45" />
41
+ </div>
42
+ );
43
+ }
44
+ return (
45
+ <div className="w-4 h-4 flex items-center justify-center relative">
46
+ {/* Outer gray diamond */}
47
+ <div className="w-4 h-4 bg-neutral-800 border border-neutral-700 absolute transform rotate-45" />
48
+ </div>
49
+ );
50
+ };
51
+
52
+ return (
53
+ <div className="bg-neutral-900 rounded-lg border border-[#363636] min-w-[320px]">
54
+ {/* Table Header */}
55
+ <div className="flex items-center justify-between p-2 border-b border-neutral-800">
56
+ <div className="flex items-center gap-2">
57
+ <div className="flex items-center justify-center w-11 h-11 bg-teal-300 rounded p-1.5">
58
+ <Database className="w-5 h-5 text-neutral-900" />
59
+ </div>
60
+ <div className="flex-1">
61
+ <h3 className="text-sm font-medium text-white">{table.tableName}</h3>
62
+ <p className="text-xs text-neutral-300">
63
+ {table.recordCount !== undefined
64
+ ? `${table.recordCount.toLocaleString()} data`
65
+ : '0 data'}
66
+ </p>
67
+ </div>
68
+ </div>
69
+ {/* <div className="p-1.5">
70
+ <ExternalLink className="w-4 h-4 text-neutral-400" />
71
+ </div> */}
72
+ </div>
73
+
74
+ {/* Columns */}
75
+ <div>
76
+ {table.columns.map((column) => (
77
+ <div
78
+ key={column.columnName}
79
+ className="flex items-center justify-between p-3 border-b border-neutral-800 relative"
80
+ >
81
+ {/* Source handle for foreign key columns - invisible and non-interactive */}
82
+ {column.foreignKey && (
83
+ <Handle
84
+ type="source"
85
+ position={Position.Right}
86
+ id={`${column.columnName}-source`}
87
+ className="!w-3 !h-3 !opacity-0 !border-0 !pointer-events-none"
88
+ style={{
89
+ right: 16,
90
+ top: '50%',
91
+ transform: 'translateY(-50%)',
92
+ pointerEvents: 'none',
93
+ }}
94
+ isConnectable={false}
95
+ />
96
+ )}
97
+
98
+ {/* Target handle for columns that can be referenced - invisible and non-interactive */}
99
+ {referencedColumns.includes(column.columnName) && (
100
+ <Handle
101
+ type="target"
102
+ position={Position.Left}
103
+ id={`${column.columnName}-target`}
104
+ className="!w-3 !h-3 !opacity-0 !border-0 !pointer-events-none"
105
+ style={{
106
+ left: 16,
107
+ top: '50%',
108
+ transform: 'translateY(-50%)',
109
+ pointerEvents: 'none',
110
+ }}
111
+ isConnectable={false}
112
+ />
113
+ )}
114
+
115
+ <div className="flex items-center gap-2.5 flex-1">
116
+ {getColumnIcon(referencedColumns.includes(column.columnName))}
117
+ <span className="text-sm text-neutral-300">{column.columnName}</span>
118
+ {column.isPrimaryKey && <Key className="w-3 h-3 text-neutral-400" />}
119
+ </div>
120
+ <div className="flex items-center gap-2.5">
121
+ <div className="px-1.5 py-0.5 bg-neutral-800 rounded flex items-center">
122
+ <span className="text-xs font-medium text-neutral-300">{column.type}</span>
123
+ </div>
124
+ {/* Show white dot with outer circle for foreign key columns, gray circle for others */}
125
+ {column.foreignKey ? (
126
+ <div className="w-5 h-5 flex items-center justify-center relative">
127
+ <Circle
128
+ className="w-5 h-5 text-white fill-none stroke-current"
129
+ strokeWidth={1.5}
130
+ />
131
+ <div className="w-2 h-2 bg-white rounded-full absolute" />
132
+ </div>
133
+ ) : (
134
+ <Circle className="w-5 h-5 text-neutral-700 fill-neutral-800 stroke-current" />
135
+ )}
136
+ </div>
137
+ </div>
138
+ ))}
139
+
140
+ {/* Empty state */}
141
+ {table.columns.length === 0 && (
142
+ <div className="flex items-center justify-center p-6">
143
+ <div className="text-center">
144
+ <Database className="w-6 h-6 text-neutral-600 mx-auto mb-2" />
145
+ <p className="text-xs text-neutral-500">No columns defined</p>
146
+ </div>
147
+ </div>
148
+ )}
149
+ </div>
150
+ </div>
151
+ );
152
+ }
@@ -0,0 +1,24 @@
1
+ export function VisualizerSkeleton() {
2
+ return (
3
+ <div className="relative min-h-screen bg-neutral-800 overflow-hidden">
4
+ {/* Dot Matrix Background */}
5
+ <div
6
+ className="absolute inset-0 opacity-50"
7
+ style={{
8
+ backgroundImage: `radial-gradient(circle, #3B3B3B 1px, transparent 1px)`,
9
+ backgroundSize: '12px 12px',
10
+ }}
11
+ />
12
+
13
+ {/* Loading content */}
14
+ <div className="relative z-10 flex items-center justify-center min-h-screen">
15
+ <div className="text-center">
16
+ <div className="inline-flex items-center gap-3 text-white">
17
+ <div className="w-8 h-8 border-4 border-blue-500 border-t-transparent rounded-full animate-spin" />
18
+ <span className="text-lg font-medium">Loading schema visualization...</span>
19
+ </div>
20
+ </div>
21
+ </div>
22
+ </div>
23
+ );
24
+ }
@@ -0,0 +1,5 @@
1
+ export { TableNode } from './TableNode';
2
+ export { AuthNode } from './AuthNode';
3
+ export { BucketNode } from './BucketNode';
4
+ export { SchemaVisualizer } from './SchemaVisualizer';
5
+ export { VisualizerSkeleton } from './VisualizerSkeleton';