devpush 0.0.0-stage → 1.0.0-beta.3

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 (456) hide show
  1. package/README.md +6 -2
  2. package/agent-release.json +37 -0
  3. package/dist/package/LICENSE.md +21 -0
  4. package/dist/package/migrate.mjs +182 -0
  5. package/dist/package/plugins.mjs +125 -0
  6. package/dist/package/vite.mjs +439 -0
  7. package/package.json +170 -4
  8. package/public/apple-touch-icon.png +0 -0
  9. package/public/favicon.svg +1 -0
  10. package/public/logo-email.png +0 -0
  11. package/public/robots.txt +3 -0
  12. package/public/social.png +0 -0
  13. package/src/cloudflare-runtime.d.ts +78 -0
  14. package/src/components/about-dialog.tsx +70 -0
  15. package/src/components/copy-button.tsx +74 -0
  16. package/src/components/logo-mark.tsx +20 -0
  17. package/src/components/pending-collection.tsx +103 -0
  18. package/src/components/resource-avatar.tsx +65 -0
  19. package/src/components/route-error-state.tsx +95 -0
  20. package/src/components/section-navigation.tsx +39 -0
  21. package/src/components/settings-layout.tsx +111 -0
  22. package/src/components/storage-capabilities.tsx +11 -0
  23. package/src/components/table-pager.tsx +101 -0
  24. package/src/components/ui/alert-dialog.tsx +180 -0
  25. package/src/components/ui/alert.tsx +78 -0
  26. package/src/components/ui/avatar.tsx +58 -0
  27. package/src/components/ui/badge.tsx +49 -0
  28. package/src/components/ui/button-group.tsx +38 -0
  29. package/src/components/ui/button.tsx +67 -0
  30. package/src/components/ui/card.tsx +82 -0
  31. package/src/components/ui/chart.tsx +307 -0
  32. package/src/components/ui/checkbox.tsx +30 -0
  33. package/src/components/ui/combobox.tsx +256 -0
  34. package/src/components/ui/dialog.tsx +163 -0
  35. package/src/components/ui/dropdown-menu.tsx +110 -0
  36. package/src/components/ui/empty.tsx +65 -0
  37. package/src/components/ui/field.tsx +192 -0
  38. package/src/components/ui/input-group.tsx +118 -0
  39. package/src/components/ui/input-otp.tsx +70 -0
  40. package/src/components/ui/input.tsx +19 -0
  41. package/src/components/ui/label.tsx +24 -0
  42. package/src/components/ui/pagination.tsx +34 -0
  43. package/src/components/ui/popover.tsx +86 -0
  44. package/src/components/ui/select.tsx +145 -0
  45. package/src/components/ui/skeleton.tsx +19 -0
  46. package/src/components/ui/sonner.tsx +47 -0
  47. package/src/components/ui/switch.tsx +31 -0
  48. package/src/components/ui/table.tsx +81 -0
  49. package/src/components/ui/textarea.tsx +18 -0
  50. package/src/components/ui/tooltip.tsx +55 -0
  51. package/src/features/admin/admin-settings.tsx +1048 -0
  52. package/src/features/auth/current-user-context-query.ts +11 -0
  53. package/src/features/auth/login-form.tsx +442 -0
  54. package/src/features/auth/sign-out-menu-item.tsx +42 -0
  55. package/src/features/deployments/deployment-actions.tsx +336 -0
  56. package/src/features/deployments/deployment-alias-badge.tsx +35 -0
  57. package/src/features/deployments/deployment-detail.tsx +596 -0
  58. package/src/features/deployments/deployment-filters.ts +66 -0
  59. package/src/features/deployments/deployment-list.tsx +465 -0
  60. package/src/features/deployments/deployment-visit-links.tsx +104 -0
  61. package/src/features/deployments/deployments-page.tsx +376 -0
  62. package/src/features/logs/log-scroll-area.tsx +69 -0
  63. package/src/features/notifications/email-preferences.tsx +232 -0
  64. package/src/features/onboarding/profile-onboarding.tsx +162 -0
  65. package/src/features/plugins/contributions.tsx +118 -0
  66. package/src/features/projects/backup-job-logs.tsx +143 -0
  67. package/src/features/projects/backup-policy-delete-dialog.tsx +82 -0
  68. package/src/features/projects/backup-restore-actions.tsx +573 -0
  69. package/src/features/projects/backup-status-icon.tsx +33 -0
  70. package/src/features/projects/backup-target-list.tsx +222 -0
  71. package/src/features/projects/backup-timestamp.tsx +32 -0
  72. package/src/features/projects/backup-volume-picker.tsx +158 -0
  73. package/src/features/projects/compose-service-input.tsx +64 -0
  74. package/src/features/projects/deploy-readiness.ts +28 -0
  75. package/src/features/projects/environment-dot.tsx +91 -0
  76. package/src/features/projects/log-keyword-filter.tsx +48 -0
  77. package/src/features/projects/manual-deploy-dialog.tsx +563 -0
  78. package/src/features/projects/pending-section.ts +26 -0
  79. package/src/features/projects/project-backup-details-pending.tsx +305 -0
  80. package/src/features/projects/project-backup-details.tsx +936 -0
  81. package/src/features/projects/project-backup-policy.tsx +644 -0
  82. package/src/features/projects/project-backup-utils.ts +190 -0
  83. package/src/features/projects/project-backups-pending.tsx +136 -0
  84. package/src/features/projects/project-backups.tsx +876 -0
  85. package/src/features/projects/project-environment-settings.tsx +1128 -0
  86. package/src/features/projects/project-heading.tsx +68 -0
  87. package/src/features/projects/project-layout.tsx +188 -0
  88. package/src/features/projects/project-logs.tsx +461 -0
  89. package/src/features/projects/project-overview.tsx +280 -0
  90. package/src/features/projects/project-route-pending.tsx +1064 -0
  91. package/src/features/projects/project-routing-settings.tsx +663 -0
  92. package/src/features/projects/project-settings-section.tsx +64 -0
  93. package/src/features/projects/project-settings-utils.ts +101 -0
  94. package/src/features/projects/project-settings.tsx +1472 -0
  95. package/src/features/projects/repository-picker-dialog.tsx +404 -0
  96. package/src/features/repositories/new-project-dialog-launcher.tsx +44 -0
  97. package/src/features/repositories/new-project-dialog.tsx +1204 -0
  98. package/src/features/repositories/new-project-pending.tsx +28 -0
  99. package/src/features/repositories/repository-list-pending.tsx +59 -0
  100. package/src/features/servers/server-availability-alert.tsx +44 -0
  101. package/src/features/servers/server-availability.ts +29 -0
  102. package/src/features/servers/server-avatar.tsx +52 -0
  103. package/src/features/servers/server-detail-pending.tsx +315 -0
  104. package/src/features/servers/server-detail.tsx +855 -0
  105. package/src/features/servers/server-metric-chart-pending.tsx +38 -0
  106. package/src/features/servers/server-metric-chart.tsx +182 -0
  107. package/src/features/servers/server-metric-format.ts +50 -0
  108. package/src/features/servers/server-metrics-pending.tsx +75 -0
  109. package/src/features/servers/server-metrics.tsx +136 -0
  110. package/src/features/servers/team-servers.tsx +432 -0
  111. package/src/features/settings/settings-route-pending.tsx +406 -0
  112. package/src/features/teams/backup-storage-settings.tsx +483 -0
  113. package/src/features/teams/navigation.ts +51 -0
  114. package/src/features/teams/new-team-dialog.tsx +132 -0
  115. package/src/features/teams/pending-section.ts +19 -0
  116. package/src/features/teams/team-header.tsx +1286 -0
  117. package/src/features/teams/team-heading.tsx +57 -0
  118. package/src/features/teams/team-home.tsx +391 -0
  119. package/src/features/teams/team-invite.tsx +155 -0
  120. package/src/features/teams/team-route-pending.tsx +505 -0
  121. package/src/features/teams/team-settings.tsx +1555 -0
  122. package/src/features/teams/use-workspace-updates.ts +126 -0
  123. package/src/features/users/api-keys-settings.tsx +544 -0
  124. package/src/features/users/api-scope-selection.tsx +269 -0
  125. package/src/features/users/connected-apps-settings.tsx +449 -0
  126. package/src/features/users/mcp-consent.tsx +220 -0
  127. package/src/features/users/profile-picture.tsx +171 -0
  128. package/src/features/users/user-settings.tsx +1064 -0
  129. package/src/lib/account-settings-schema.ts +78 -0
  130. package/src/lib/action-feedback.ts +81 -0
  131. package/src/lib/admin-search-schema.ts +8 -0
  132. package/src/lib/agent-release.d.mts +21 -0
  133. package/src/lib/agent-release.mjs +90 -0
  134. package/src/lib/allowlist-schema.ts +79 -0
  135. package/src/lib/api-key-schema.ts +176 -0
  136. package/src/lib/auth/client.ts +20 -0
  137. package/src/lib/avatar-schema.ts +11 -0
  138. package/src/lib/backup-cron.ts +118 -0
  139. package/src/lib/backup-database.ts +41 -0
  140. package/src/lib/backup-feature.ts +2 -0
  141. package/src/lib/backup-paths.ts +7 -0
  142. package/src/lib/backup-policy-schema.ts +85 -0
  143. package/src/lib/backup-resource-schema.ts +12 -0
  144. package/src/lib/backup-restore-schema.ts +15 -0
  145. package/src/lib/backup-storage-schema.ts +55 -0
  146. package/src/lib/deferred-module.ts +38 -0
  147. package/src/lib/deployment-logs-query.ts +63 -0
  148. package/src/lib/display-name.ts +7 -0
  149. package/src/lib/error-message.ts +119 -0
  150. package/src/lib/form-errors.ts +10 -0
  151. package/src/lib/internal-redirect.ts +25 -0
  152. package/src/lib/manual-deploy-options-query.ts +21 -0
  153. package/src/lib/notification-preferences.ts +43 -0
  154. package/src/lib/onboarding.ts +11 -0
  155. package/src/lib/operation-logs-query.ts +51 -0
  156. package/src/lib/page-head.ts +14 -0
  157. package/src/lib/project-context-query.ts +257 -0
  158. package/src/lib/project-creation-schema.ts +64 -0
  159. package/src/lib/project-env-schema.ts +23 -0
  160. package/src/lib/project-environments-schema.ts +56 -0
  161. package/src/lib/project-routing-schema.ts +94 -0
  162. package/src/lib/project-runtime-config.ts +131 -0
  163. package/src/lib/project-settings-schema.ts +75 -0
  164. package/src/lib/public-api-schema.ts +128 -0
  165. package/src/lib/public-error.ts +337 -0
  166. package/src/lib/query.ts +13 -0
  167. package/src/lib/repository-selection-query.ts +11 -0
  168. package/src/lib/resource-cache.ts +30 -0
  169. package/src/lib/runtime-id.ts +19 -0
  170. package/src/lib/search.ts +14 -0
  171. package/src/lib/server-context-query.ts +43 -0
  172. package/src/lib/server-name-schema.ts +11 -0
  173. package/src/lib/settings-conflict.ts +45 -0
  174. package/src/lib/settings-version.ts +7 -0
  175. package/src/lib/team-context-query.ts +126 -0
  176. package/src/lib/team-context-schema.ts +8 -0
  177. package/src/lib/team-forms-schema.ts +45 -0
  178. package/src/lib/team-role.ts +13 -0
  179. package/src/lib/team-settings-schema.ts +48 -0
  180. package/src/lib/urls.ts +3 -0
  181. package/src/lib/use-avatar-input.ts +61 -0
  182. package/src/lib/use-refresh-error.ts +18 -0
  183. package/src/lib/use-settings-version.ts +23 -0
  184. package/src/lib/utils.ts +6 -0
  185. package/src/lib/validation.ts +5 -0
  186. package/src/lib/workspace-updates.ts +53 -0
  187. package/src/package/config.ts +10 -0
  188. package/src/package/env.mjs +54 -0
  189. package/src/package/migrate-api.d.ts +5 -0
  190. package/src/package/migrate.ts +56 -0
  191. package/src/package/plugins.ts +141 -0
  192. package/src/package/sqlite.mjs +2 -0
  193. package/src/package/vite-api.d.ts +5 -0
  194. package/src/package/vite.ts +324 -0
  195. package/src/package/watch-guard.cjs +26 -0
  196. package/src/plugins/client.ts +57 -0
  197. package/src/plugins/contract.ts +188 -0
  198. package/src/plugins/default.server.ts +4 -0
  199. package/src/plugins/default.ui.ts +4 -0
  200. package/src/plugins/operations.ts +45 -0
  201. package/src/plugins/server.ts +197 -0
  202. package/src/plugins/version.ts +1 -0
  203. package/src/routeTree.gen.ts +1275 -0
  204. package/src/router.tsx +46 -0
  205. package/src/routes/$teamSlug.index.tsx +53 -0
  206. package/src/routes/$teamSlug.new-project.tsx +13 -0
  207. package/src/routes/$teamSlug.projects.$projectName.backups.$backupId.tsx +40 -0
  208. package/src/routes/$teamSlug.projects.$projectName.backups.tsx +82 -0
  209. package/src/routes/$teamSlug.projects.$projectName.deployments.$deploymentId.tsx +52 -0
  210. package/src/routes/$teamSlug.projects.$projectName.deployments.tsx +86 -0
  211. package/src/routes/$teamSlug.projects.$projectName.logs.tsx +55 -0
  212. package/src/routes/$teamSlug.projects.$projectName.settings.tsx +51 -0
  213. package/src/routes/$teamSlug.projects.$projectName.tsx +106 -0
  214. package/src/routes/$teamSlug.projects.tsx +40 -0
  215. package/src/routes/$teamSlug.servers.$serverId.tsx +31 -0
  216. package/src/routes/$teamSlug.servers.index.tsx +59 -0
  217. package/src/routes/$teamSlug.servers.tsx +7 -0
  218. package/src/routes/$teamSlug.settings.tsx +56 -0
  219. package/src/routes/$teamSlug.tsx +86 -0
  220. package/src/routes/[.]well-known.oauth-authorization-server.api.auth.ts +13 -0
  221. package/src/routes/[.]well-known.oauth-protected-resource.mcp.ts +6 -0
  222. package/src/routes/__root.tsx +155 -0
  223. package/src/routes/admin.tsx +55 -0
  224. package/src/routes/api/agents/certificates.ts +21 -0
  225. package/src/routes/api/agents/credentials/rotate.ts +22 -0
  226. package/src/routes/api/agents/heartbeat.ts +42 -0
  227. package/src/routes/api/agents/job-logs.ts +22 -0
  228. package/src/routes/api/agents/jobs/complete.ts +22 -0
  229. package/src/routes/api/agents/jobs/next.ts +29 -0
  230. package/src/routes/api/agents/jobs/progress.ts +32 -0
  231. package/src/routes/api/agents/jobs/status.ts +22 -0
  232. package/src/routes/api/agents/register.ts +45 -0
  233. package/src/routes/api/agents/runtime-logs.ts +22 -0
  234. package/src/routes/api/agents/switch.ts +25 -0
  235. package/src/routes/api/auth/$.ts +38 -0
  236. package/src/routes/api/cron/backups.ts +33 -0
  237. package/src/routes/api/cron/maintenance.ts +30 -0
  238. package/src/routes/api/github/install.callback.ts +51 -0
  239. package/src/routes/api/github/install.ts +41 -0
  240. package/src/routes/api/github/webhook.ts +11 -0
  241. package/src/routes/api/health.ts +19 -0
  242. package/src/routes/api/install/agent.$token.ts +50 -0
  243. package/src/routes/api/install/agent.binary.ts +83 -0
  244. package/src/routes/api/install/agent.checksum.ts +45 -0
  245. package/src/routes/api/install/agent.ts +26 -0
  246. package/src/routes/api/plugins/$.ts +11 -0
  247. package/src/routes/api/v1/$.ts +15 -0
  248. package/src/routes/api/workspaces/updates.ts +8 -0
  249. package/src/routes/auth.login.tsx +50 -0
  250. package/src/routes/deployments.$deploymentId.unavailable.tsx +21 -0
  251. package/src/routes/index.tsx +23 -0
  252. package/src/routes/invites.$token.tsx +35 -0
  253. package/src/routes/mcp.ts +13 -0
  254. package/src/routes/oauth.consent.tsx +54 -0
  255. package/src/routes/onboarding.tsx +37 -0
  256. package/src/routes/onboarding.workspace.tsx +44 -0
  257. package/src/routes/settings.tsx +30 -0
  258. package/src/routes/uploads/avatars/$fileName.ts +44 -0
  259. package/src/server/admin/access.ts +67 -0
  260. package/src/server/admin/functions.ts +122 -0
  261. package/src/server/admin/service.ts +260 -0
  262. package/src/server/agents/backup-state.ts +210 -0
  263. package/src/server/agents/certificates.ts +96 -0
  264. package/src/server/agents/claims.ts +116 -0
  265. package/src/server/agents/cleanup.ts +52 -0
  266. package/src/server/agents/compatibility.ts +116 -0
  267. package/src/server/agents/completion.ts +83 -0
  268. package/src/server/agents/container-cleanup-state.ts +51 -0
  269. package/src/server/agents/deployment-state.ts +111 -0
  270. package/src/server/agents/job-payloads.ts +159 -0
  271. package/src/server/agents/jobs.ts +1293 -0
  272. package/src/server/agents/protocol.ts +400 -0
  273. package/src/server/agents/reconciliation.ts +43 -0
  274. package/src/server/agents/runtime-logs.ts +337 -0
  275. package/src/server/agents/runtime-state.ts +125 -0
  276. package/src/server/api/admission.ts +12 -0
  277. package/src/server/api/authorization.ts +115 -0
  278. package/src/server/api/credentials.ts +569 -0
  279. package/src/server/api/cursors.ts +72 -0
  280. package/src/server/api/errors.ts +24 -0
  281. package/src/server/api/functions.ts +60 -0
  282. package/src/server/api/handler.ts +274 -0
  283. package/src/server/api/mutation-operations.ts +913 -0
  284. package/src/server/api/operation-context.ts +139 -0
  285. package/src/server/api/operation-contract.ts +1099 -0
  286. package/src/server/api/operations.ts +64 -0
  287. package/src/server/api/read-operations.ts +891 -0
  288. package/src/server/api/retention.ts +33 -0
  289. package/src/server/auth/access.ts +109 -0
  290. package/src/server/auth/auth-methods.ts +28 -0
  291. package/src/server/auth/better-auth.ts +518 -0
  292. package/src/server/auth/context.ts +28 -0
  293. package/src/server/auth/email-change.ts +47 -0
  294. package/src/server/auth/email-delivery.ts +51 -0
  295. package/src/server/auth/provider-identity.ts +19 -0
  296. package/src/server/auth/provider-tokens.ts +27 -0
  297. package/src/server/auth/session.server.ts +19 -0
  298. package/src/server/backups/database-commands.ts +40 -0
  299. package/src/server/backups/destination.ts +12 -0
  300. package/src/server/backups/feature.ts +6 -0
  301. package/src/server/backups/policy-validation.ts +51 -0
  302. package/src/server/backups/queue-state.ts +137 -0
  303. package/src/server/backups/service.ts +918 -0
  304. package/src/server/backups/storage-functions.ts +42 -0
  305. package/src/server/backups/storage-state.ts +67 -0
  306. package/src/server/backups/storage.ts +237 -0
  307. package/src/server/config.ts +274 -0
  308. package/src/server/db/atomic.ts +16 -0
  309. package/src/server/db/d1-connection.ts +83 -0
  310. package/src/server/db/errors.ts +17 -0
  311. package/src/server/db/index.cloudflare.ts +15 -0
  312. package/src/server/db/index.ts +35 -0
  313. package/src/server/db/libsql-connection.ts +72 -0
  314. package/src/server/db/log-schema.ts +102 -0
  315. package/src/server/db/migrate-libsql.ts +29 -0
  316. package/src/server/db/migrate-sqlite.ts +47 -0
  317. package/src/server/db/migration-history.ts +12 -0
  318. package/src/server/db/migrations-logs/0000_initial.sql +48 -0
  319. package/src/server/db/migrations-logs/0001_runtime_log_order.sql +2 -0
  320. package/src/server/db/migrations-logs/meta/0000_snapshot.json +327 -0
  321. package/src/server/db/migrations-logs/meta/0001_snapshot.json +329 -0
  322. package/src/server/db/migrations-logs/meta/_journal.json +20 -0
  323. package/src/server/db/migrations-sqlite/0000_initial.sql +894 -0
  324. package/src/server/db/migrations-sqlite/0001_agent_indexes.sql +5 -0
  325. package/src/server/db/migrations-sqlite/meta/0000_snapshot.json +5816 -0
  326. package/src/server/db/migrations-sqlite/meta/0001_snapshot.json +5841 -0
  327. package/src/server/db/migrations-sqlite/meta/_journal.json +20 -0
  328. package/src/server/db/oauth-schema.ts +216 -0
  329. package/src/server/db/remote-config.ts +47 -0
  330. package/src/server/db/schema.ts +1218 -0
  331. package/src/server/db/sqlite-connection.ts +94 -0
  332. package/src/server/db/sqlite-files.ts +27 -0
  333. package/src/server/db/sqlite-params.ts +19 -0
  334. package/src/server/db/worker-connection.ts +47 -0
  335. package/src/server/deleted-resources.ts +133 -0
  336. package/src/server/deletion-recovery.ts +272 -0
  337. package/src/server/email/layout.tsx +85 -0
  338. package/src/server/email/notification-template.tsx +62 -0
  339. package/src/server/email/otp-template.tsx +48 -0
  340. package/src/server/email/service.ts +251 -0
  341. package/src/server/email/team-invite-template.tsx +59 -0
  342. package/src/server/email/theme.ts +10 -0
  343. package/src/server/github/http.ts +14 -0
  344. package/src/server/github/install-state.ts +48 -0
  345. package/src/server/github/service.ts +831 -0
  346. package/src/server/github/webhook-handler.ts +198 -0
  347. package/src/server/github/webhooks.ts +205 -0
  348. package/src/server/http/auth.ts +11 -0
  349. package/src/server/http/error-diagnostics.ts +66 -0
  350. package/src/server/http/plugin-request.ts +15 -0
  351. package/src/server/http/proxy-request.ts +53 -0
  352. package/src/server/http/public-errors.ts +48 -0
  353. package/src/server/http/rate-limit.ts +59 -0
  354. package/src/server/http/request-body.ts +54 -0
  355. package/src/server/http/request-ip.ts +54 -0
  356. package/src/server/http/security-headers.ts +36 -0
  357. package/src/server/install/agent-binary.ts +230 -0
  358. package/src/server/install/agent-script.ts +565 -0
  359. package/src/server/install/release-binary.ts +65 -0
  360. package/src/server/logs/budget.ts +72 -0
  361. package/src/server/logs/cleanup.ts +64 -0
  362. package/src/server/logs/deployments.ts +98 -0
  363. package/src/server/logs/operations.ts +145 -0
  364. package/src/server/logs/retention.ts +39 -0
  365. package/src/server/maintenance.ts +61 -0
  366. package/src/server/mcp/connections.ts +358 -0
  367. package/src/server/mcp/functions.ts +63 -0
  368. package/src/server/mcp/handler.ts +295 -0
  369. package/src/server/mcp/oauth-discovery.cloudflare.ts +6 -0
  370. package/src/server/mcp/oauth-discovery.ts +8 -0
  371. package/src/server/mcp/oauth-provider.ts +220 -0
  372. package/src/server/mcp/urls.ts +20 -0
  373. package/src/server/notifications/functions.ts +51 -0
  374. package/src/server/notifications/observe.ts +303 -0
  375. package/src/server/notifications/outbox.ts +335 -0
  376. package/src/server/notifications/preferences.ts +131 -0
  377. package/src/server/notifications/query.ts +16 -0
  378. package/src/server/plugins/scope.ts +17 -0
  379. package/src/server/plugins/server-context.ts +157 -0
  380. package/src/server/plugins/services.server.ts +10 -0
  381. package/src/server/plugins/workspace-access.ts +74 -0
  382. package/src/server/projects/backup-policy-state.ts +68 -0
  383. package/src/server/projects/backups.ts +219 -0
  384. package/src/server/projects/compose-metadata.ts +174 -0
  385. package/src/server/projects/configuration-state.ts +90 -0
  386. package/src/server/projects/context.ts +909 -0
  387. package/src/server/projects/creation-state.ts +46 -0
  388. package/src/server/projects/deletion-state.ts +69 -0
  389. package/src/server/projects/deployment-config.ts +139 -0
  390. package/src/server/projects/deployment-logs.ts +97 -0
  391. package/src/server/projects/deployments.ts +1123 -0
  392. package/src/server/projects/domain-state.ts +71 -0
  393. package/src/server/projects/environment-branches.ts +75 -0
  394. package/src/server/projects/environment-state.ts +158 -0
  395. package/src/server/projects/environments.ts +424 -0
  396. package/src/server/projects/functions.ts +748 -0
  397. package/src/server/projects/generated-hostname.ts +77 -0
  398. package/src/server/projects/hostnames.ts +244 -0
  399. package/src/server/projects/mappers.ts +457 -0
  400. package/src/server/projects/names.ts +41 -0
  401. package/src/server/projects/project-env-vars.ts +28 -0
  402. package/src/server/projects/queue-state.ts +118 -0
  403. package/src/server/projects/repository.ts +368 -0
  404. package/src/server/projects/route-state.ts +37 -0
  405. package/src/server/projects/runtime-logs.ts +97 -0
  406. package/src/server/projects/settings-context.ts +58 -0
  407. package/src/server/projects/settings.ts +738 -0
  408. package/src/server/projects/types.ts +355 -0
  409. package/src/server/realtime/agent-events.ts +32 -0
  410. package/src/server/realtime/node-broadcaster.ts +71 -0
  411. package/src/server/realtime/node-handler.ts +55 -0
  412. package/src/server/realtime/service.ts +125 -0
  413. package/src/server/realtime/workspace-object.cloudflare.ts +62 -0
  414. package/src/server/realtime/workspace-object.ts +3 -0
  415. package/src/server/resources/color.ts +13 -0
  416. package/src/server/runtime/background.ts +21 -0
  417. package/src/server/runtime/node-asset-cache-plugin.ts +12 -0
  418. package/src/server/runtime/node-scheduler-plugin.ts +17 -0
  419. package/src/server/runtime/node-scheduler.ts +51 -0
  420. package/src/server/runtime/platform-env.cloudflare.ts +17 -0
  421. package/src/server/runtime/platform-env.ts +19 -0
  422. package/src/server/runtime/scheduled-task-error.ts +9 -0
  423. package/src/server/scheduled-tasks.ts +28 -0
  424. package/src/server/secrets/crypto.ts +56 -0
  425. package/src/server/servers/functions.ts +233 -0
  426. package/src/server/servers/lifecycle.ts +122 -0
  427. package/src/server/servers/metric-history-schema.ts +24 -0
  428. package/src/server/servers/metric-history.ts +55 -0
  429. package/src/server/servers/monitoring.ts +592 -0
  430. package/src/server/servers/service.ts +1211 -0
  431. package/src/server/servers/status.ts +32 -0
  432. package/src/server/servers/switch.ts +96 -0
  433. package/src/server/settings-version.ts +43 -0
  434. package/src/server/storage/capabilities.ts +6 -0
  435. package/src/server/storage/local-object-store.cloudflare.ts +7 -0
  436. package/src/server/storage/local-object-store.ts +50 -0
  437. package/src/server/storage/object-store.ts +160 -0
  438. package/src/server/teams/creation.ts +51 -0
  439. package/src/server/teams/delete-policy.ts +13 -0
  440. package/src/server/teams/deletion-state.ts +80 -0
  441. package/src/server/teams/functions.ts +324 -0
  442. package/src/server/teams/invitations.ts +38 -0
  443. package/src/server/teams/membership.ts +86 -0
  444. package/src/server/teams/queries.ts +261 -0
  445. package/src/server/teams/service.ts +1127 -0
  446. package/src/server/teams/types.ts +101 -0
  447. package/src/server/uploads/avatar.ts +133 -0
  448. package/src/server/users/deletion-state.ts +93 -0
  449. package/src/server/users/functions.ts +160 -0
  450. package/src/server/users/onboarding.ts +17 -0
  451. package/src/server/users/service.ts +644 -0
  452. package/src/server.ts +55 -0
  453. package/src/styles/shadcn.css +653 -0
  454. package/src/styles/theme.css +76 -0
  455. package/src/styles.css +177 -0
  456. package/wrangler.template.jsonc +37 -0
@@ -0,0 +1,78 @@
1
+ /* Ambient modules require inline import types to keep this file a script. */
2
+ /* eslint-disable @typescript-eslint/consistent-type-imports */
3
+ // Keep this ambient file a script so cloudflare:workers is declared, not augmented.
4
+
5
+ type D1Database = import('@cloudflare/workers-types').D1Database
6
+
7
+ interface R2ObjectBody {
8
+ arrayBuffer: () => Promise<ArrayBuffer>
9
+ httpMetadata?: {
10
+ contentType?: string
11
+ }
12
+ }
13
+
14
+ interface R2Bucket {
15
+ delete: (key: string) => Promise<void>
16
+ get: (key: string) => Promise<R2ObjectBody | null>
17
+ head: (key: string) => Promise<{ size: number } | null>
18
+ put: (
19
+ key: string,
20
+ value: ArrayBuffer | ArrayBufferView | string,
21
+ options?: { httpMetadata?: { contentType?: string } },
22
+ ) => Promise<unknown>
23
+ }
24
+
25
+ interface WorkerVersionMetadata {
26
+ id: string
27
+ tag: string
28
+ }
29
+
30
+ interface WorkspaceUpdatesNamespace {
31
+ getByName: (name: string) => {
32
+ fetch: (request: Request) => Promise<Response>
33
+ publish: (kind: string, activeSessionIds: string[]) => Promise<void>
34
+ }
35
+ }
36
+
37
+ declare const WebSocketPair: typeof import('@cloudflare/workers-types').WebSocketPair
38
+ declare const WebSocketRequestResponsePair: typeof import('@cloudflare/workers-types').WebSocketRequestResponsePair
39
+ interface ResponseInit {
40
+ webSocket?: import('@cloudflare/workers-types').WebSocket
41
+ }
42
+
43
+ interface Env {
44
+ DEVPUSH_WORKSPACE_UPDATES?: WorkspaceUpdatesNamespace
45
+ DEVPUSH_DATABASE: D1Database
46
+ DEVPUSH_LOG_DATABASE: D1Database
47
+ DEVPUSH_DATABASE_URL?: string
48
+ DEVPUSH_LOG_DATABASE_URL?: string
49
+ DEVPUSH_DATABASE_AUTH_TOKEN?: string
50
+ DEVPUSH_LOG_DATABASE_AUTH_TOKEN?: string
51
+ DEVPUSH_OBJECTS?: R2Bucket
52
+ }
53
+
54
+ interface ExecutionContext {
55
+ waitUntil: (promise: Promise<unknown>) => void
56
+ }
57
+
58
+ interface ScheduledController {
59
+ scheduledTime: number
60
+ }
61
+
62
+ interface ExportedHandler<TEnvironment> {
63
+ fetch?: (
64
+ request: Request,
65
+ env: TEnvironment,
66
+ context: ExecutionContext,
67
+ ) => Response | Promise<Response>
68
+ scheduled?: (
69
+ controller: ScheduledController,
70
+ env: TEnvironment,
71
+ context: ExecutionContext,
72
+ ) => void | Promise<void>
73
+ }
74
+
75
+ declare module 'cloudflare:workers' {
76
+ export const env: Env
77
+ export const DurableObject: typeof import('@cloudflare/workers-types').DurableObject
78
+ }
@@ -0,0 +1,70 @@
1
+ import { LogoMark } from '#devpush/components/logo-mark'
2
+ import {
3
+ Dialog,
4
+ DialogContent,
5
+ DialogDescription,
6
+ DialogHeader,
7
+ DialogTitle,
8
+ } from '#devpush/components/ui/dialog'
9
+ import packageJson from '../../package.json'
10
+
11
+ const links = [
12
+ { label: 'Website', href: 'https://devpu.sh', text: 'devpu.sh' },
13
+ {
14
+ label: 'Documentation',
15
+ href: 'https://devpu.sh/docs',
16
+ text: 'devpu.sh/docs',
17
+ },
18
+ { label: 'Discord', href: 'https://devpu.sh/chat', text: 'devpu.sh/chat' },
19
+ {
20
+ label: 'GitHub',
21
+ href: 'https://github.com/hunvreus/devpush',
22
+ text: 'hunvreus/devpush',
23
+ },
24
+ ]
25
+
26
+ export function AboutDialog({
27
+ open,
28
+ onOpenChange,
29
+ }: {
30
+ open: boolean
31
+ onOpenChange: (open: boolean) => void
32
+ }) {
33
+ return (
34
+ <Dialog open={open} onOpenChange={onOpenChange}>
35
+ <DialogContent className="sm:max-w-[22rem]">
36
+ <DialogHeader className="items-center gap-3 text-center sm:text-center">
37
+ <div className="flex size-15 shrink-0 items-center justify-center rounded-lg bg-orange-600 text-white">
38
+ <LogoMark className="size-9 shrink-0" />
39
+ </div>
40
+ <DialogTitle>devpush</DialogTitle>
41
+ <DialogDescription>
42
+ Deploy your applications on your own servers.
43
+ </DialogDescription>
44
+ </DialogHeader>
45
+ <div className="rounded-lg border text-sm">
46
+ <div className="flex items-center justify-between gap-3 border-b px-4 py-3">
47
+ <span className="text-muted-foreground">Version</span>
48
+ <span>{packageJson.version}</span>
49
+ </div>
50
+ {links.map(({ label, href, text }) => (
51
+ <div
52
+ key={label}
53
+ className="flex items-center justify-between gap-3 border-b px-4 py-3 last:border-b-0"
54
+ >
55
+ <span className="text-muted-foreground">{label}</span>
56
+ <a
57
+ className="inline-flex items-center gap-2 hover:underline"
58
+ href={href}
59
+ target="_blank"
60
+ rel="noreferrer noopener"
61
+ >
62
+ {text}
63
+ </a>
64
+ </div>
65
+ ))}
66
+ </div>
67
+ </DialogContent>
68
+ </Dialog>
69
+ )
70
+ }
@@ -0,0 +1,74 @@
1
+ import { useEffect, useState } from 'react'
2
+ import type { ReactNode } from 'react'
3
+ import { CheckIcon, CopyIcon } from 'lucide-react'
4
+ import { Button } from '#devpush/components/ui/button'
5
+ import { InputGroupButton } from '#devpush/components/ui/input-group'
6
+
7
+ export function CopyButton({
8
+ value,
9
+ label,
10
+ size,
11
+ inputGroup = false,
12
+ disabled = false,
13
+ children,
14
+ className,
15
+ }: {
16
+ value: string
17
+ label: string
18
+ size?: 'icon-xs' | 'icon-sm'
19
+ inputGroup?: boolean
20
+ disabled?: boolean
21
+ children?: ReactNode
22
+ className?: string
23
+ }) {
24
+ const [feedback, setFeedback] = useState<{
25
+ status: 'copied' | 'failed'
26
+ attempt: number
27
+ } | null>(null)
28
+ const [busy, setBusy] = useState(false)
29
+ useEffect(() => {
30
+ if (!feedback) return
31
+ const timer = setTimeout(() => {
32
+ setFeedback(null)
33
+ }, 1500)
34
+ return () => clearTimeout(timer)
35
+ }, [feedback])
36
+ const feedbackLabel =
37
+ feedback?.status === 'copied'
38
+ ? 'Copied'
39
+ : feedback?.status === 'failed'
40
+ ? 'Unable to copy'
41
+ : 'Copy'
42
+ const Control = inputGroup ? InputGroupButton : Button
43
+ async function copy() {
44
+ setBusy(true)
45
+ try {
46
+ await navigator.clipboard.writeText(value)
47
+ setFeedback((previous) => ({
48
+ status: 'copied',
49
+ attempt: (previous?.attempt ?? 0) + 1,
50
+ }))
51
+ } catch {
52
+ setFeedback((previous) => ({
53
+ status: 'failed',
54
+ attempt: (previous?.attempt ?? 0) + 1,
55
+ }))
56
+ } finally {
57
+ setBusy(false)
58
+ }
59
+ }
60
+ return (
61
+ <Control
62
+ type="button"
63
+ variant="ghost"
64
+ size={children ? 'sm' : (size ?? (inputGroup ? 'icon-xs' : 'icon-sm'))}
65
+ className={className}
66
+ disabled={disabled || busy}
67
+ aria-label={feedback ? feedbackLabel : label}
68
+ onClick={() => void copy()}
69
+ >
70
+ {children}
71
+ {feedback?.status === 'copied' ? <CheckIcon /> : <CopyIcon />}
72
+ </Control>
73
+ )
74
+ }
@@ -0,0 +1,20 @@
1
+ export function LogoMark({
2
+ className = 'size-4.5 shrink-0',
3
+ }: {
4
+ className?: string
5
+ }) {
6
+ return (
7
+ <svg
8
+ viewBox="0 0 120 96"
9
+ fill="currentColor"
10
+ xmlns="http://www.w3.org/2000/svg"
11
+ className={className}
12
+ aria-hidden="true"
13
+ >
14
+ <path
15
+ fillRule="evenodd"
16
+ d="M48 0H96L120 24V72H72V96H24L0 72V24H48V0ZM24 48V72H48V48H24ZM72 24V48H96V24H72Z"
17
+ />
18
+ </svg>
19
+ )
20
+ }
@@ -0,0 +1,103 @@
1
+ import {
2
+ Table,
3
+ TableBody,
4
+ TableCell,
5
+ TableHead,
6
+ TableHeader,
7
+ TableRow,
8
+ } from '#devpush/components/ui/table'
9
+ import { Skeleton } from '#devpush/components/ui/skeleton'
10
+
11
+ export function PendingList({
12
+ label,
13
+ rows = 1,
14
+ }: {
15
+ label: string
16
+ rows?: number
17
+ }) {
18
+ return (
19
+ <div
20
+ className="scrollbar overflow-x-auto rounded-xl border bg-card text-card-foreground"
21
+ aria-label={label}
22
+ aria-busy="true"
23
+ >
24
+ {Array.from({ length: rows }, (_, index) => (
25
+ <div
26
+ className="flex h-11 min-w-[20rem] items-center gap-4 border-b px-4 last:border-b-0"
27
+ key={index}
28
+ >
29
+ <Skeleton className="size-4 shrink-0 rounded-full" />
30
+ <Skeleton className="h-4 w-20 shrink-0" />
31
+ <Skeleton className="h-4 w-32 shrink-0" />
32
+ <Skeleton className="h-4 w-20 shrink-0" />
33
+ <Skeleton className="-mr-2 size-8 shrink-0" />
34
+ </div>
35
+ ))}
36
+ </div>
37
+ )
38
+ }
39
+
40
+ export function PendingTable({
41
+ label,
42
+ columns,
43
+ avatar = false,
44
+ secondaryName = false,
45
+ }: {
46
+ label: string
47
+ columns: Array<{ title: string; className?: string }>
48
+ avatar?: boolean
49
+ secondaryName?: boolean
50
+ }) {
51
+ return (
52
+ <div
53
+ className="scrollbar overflow-x-auto rounded-xl border bg-card text-card-foreground"
54
+ aria-label={label}
55
+ aria-busy="true"
56
+ >
57
+ <Table>
58
+ <TableHeader>
59
+ <TableRow>
60
+ {columns.map((column, index) => (
61
+ <TableHead
62
+ className={`${column.className ?? ''} ${column.title ? 'first:pl-4 last:pr-4' : ''}`}
63
+ key={index}
64
+ >
65
+ {column.title || <span className="sr-only">Actions</span>}
66
+ </TableHead>
67
+ ))}
68
+ </TableRow>
69
+ </TableHeader>
70
+ <TableBody>
71
+ <TableRow>
72
+ {columns.map((column, index) => (
73
+ <TableCell
74
+ className={column.title ? 'first:pl-4 last:pr-4' : undefined}
75
+ key={index}
76
+ >
77
+ {column.title ? (
78
+ <div className="flex items-center gap-3">
79
+ {avatar && index === 0 ? (
80
+ <Skeleton className="size-8 shrink-0 rounded-full" />
81
+ ) : null}
82
+ {secondaryName && index === 0 ? (
83
+ <div className="space-y-1">
84
+ <Skeleton className="h-4 w-24 max-w-full" />
85
+ <Skeleton className="h-4 w-16 max-w-full" />
86
+ </div>
87
+ ) : (
88
+ <Skeleton className="h-4 w-24 max-w-full" />
89
+ )}
90
+ </div>
91
+ ) : (
92
+ <div className="ml-auto w-fit">
93
+ <Skeleton className="-mr-2 size-8" />
94
+ </div>
95
+ )}
96
+ </TableCell>
97
+ ))}
98
+ </TableRow>
99
+ </TableBody>
100
+ </Table>
101
+ </div>
102
+ )
103
+ }
@@ -0,0 +1,65 @@
1
+ import { createElement } from 'react'
2
+ import { cn } from '#devpush/lib/utils'
3
+
4
+ const sizeClasses = {
5
+ xs: 'size-5 rounded text-xs font-semibold',
6
+ sm: 'size-6 rounded-md text-sm font-semibold',
7
+ md: 'size-10 rounded-lg text-xl font-medium',
8
+ lg: 'size-14 rounded-xl text-4xl font-medium',
9
+ default: 'size-8 rounded-lg text-lg font-semibold',
10
+ } as const
11
+
12
+ const colorClasses = {
13
+ amber:
14
+ 'from-amber-100 to-amber-100/50 border-amber-200 text-amber-500 dark:from-amber-950/50 dark:to-amber-950 dark:border-amber-900',
15
+ blue: 'from-blue-100 to-blue-100/50 border-blue-200 text-blue-500 dark:from-blue-950/50 dark:to-blue-950 dark:border-blue-900',
16
+ green:
17
+ 'from-green-100 to-green-100/50 border-green-200 text-green-500 dark:from-green-950/50 dark:to-green-950 dark:border-green-900',
18
+ neutral:
19
+ 'from-neutral-100 to-neutral-100/50 border-neutral-200 text-neutral-500 dark:from-neutral-800/70 dark:to-neutral-900 dark:border-neutral-700 dark:text-neutral-400',
20
+ rose: 'from-rose-100 to-rose-100/50 border-rose-200 text-rose-500 dark:from-rose-950/50 dark:to-rose-950 dark:border-rose-900',
21
+ lime: 'from-lime-100 to-lime-100/50 border-lime-200 text-lime-500 dark:from-lime-950/50 dark:to-lime-950 dark:border-lime-900',
22
+ sky: 'from-sky-100 to-sky-100/50 border-sky-200 text-sky-500 dark:from-sky-950/50 dark:to-sky-950 dark:border-sky-900',
23
+ zinc: 'from-zinc-100 to-zinc-100/50 border-zinc-200 text-zinc-500 dark:from-zinc-950/50 dark:to-zinc-950 dark:border-zinc-900',
24
+ } as const
25
+
26
+ export type ResourceColor = keyof typeof colorClasses
27
+
28
+ type ResourceAvatarProps = {
29
+ 'data-icon'?: 'inline-start' | 'inline-end'
30
+ name: string
31
+ src?: string
32
+ color?: ResourceColor
33
+ size?: keyof typeof sizeClasses
34
+ className?: string
35
+ }
36
+
37
+ export function ResourceAvatar({
38
+ 'data-icon': dataIcon,
39
+ name,
40
+ src,
41
+ color = 'zinc',
42
+ size = 'default',
43
+ className,
44
+ }: ResourceAvatarProps) {
45
+ const initial = name.trim().charAt(0).toLocaleUpperCase()
46
+
47
+ if (src) {
48
+ return createElement('img', {
49
+ 'data-icon': dataIcon,
50
+ src,
51
+ alt: name,
52
+ className: cn('shrink-0 object-cover', sizeClasses[size], className),
53
+ })
54
+ }
55
+
56
+ return createElement('span', {
57
+ 'data-icon': dataIcon,
58
+ className: cn(
59
+ 'flex shrink-0 items-center justify-center border bg-gradient-to-tl uppercase',
60
+ sizeClasses[size],
61
+ colorClasses[color],
62
+ className,
63
+ ),
64
+ }, initial)
65
+ }
@@ -0,0 +1,95 @@
1
+ import { publicErrorMessages } from '#devpush/lib/public-error'
2
+ import type { ErrorComponentProps } from '@tanstack/react-router'
3
+ import { ArrowLeftIcon } from 'lucide-react'
4
+
5
+ import { Button } from '#devpush/components/ui/button'
6
+ import {
7
+ Empty,
8
+ EmptyContent,
9
+ EmptyDescription,
10
+ EmptyHeader,
11
+ EmptyTitle,
12
+ } from '#devpush/components/ui/empty'
13
+
14
+ export function RouteNotFoundState({
15
+ description,
16
+ title,
17
+ }: {
18
+ description: string
19
+ title: string
20
+ }) {
21
+ return (
22
+ <main className="flex min-h-svh items-center justify-center p-6">
23
+ <Empty className="max-w-md border-0">
24
+ <EmptyHeader>
25
+ <EmptyTitle>{title}</EmptyTitle>
26
+ <EmptyDescription>{description}</EmptyDescription>
27
+ </EmptyHeader>
28
+ <EmptyContent>
29
+ <Button asChild variant="outline">
30
+ <a href="/">
31
+ <ArrowLeftIcon data-icon="inline-start" />
32
+ Go home
33
+ </a>
34
+ </Button>
35
+ </EmptyContent>
36
+ </Empty>
37
+ </main>
38
+ )
39
+ }
40
+
41
+ export function RouteErrorState({ error }: ErrorComponentProps) {
42
+ const pathname =
43
+ typeof window === 'undefined' ? null : window.location.pathname
44
+
45
+ if (isNotFoundError(error)) {
46
+ return (
47
+ <RouteNotFoundState
48
+ title="Not found"
49
+ description="The team, project, or resource you requested does not exist, or you do not have access."
50
+ />
51
+ )
52
+ }
53
+
54
+ return (
55
+ <main className="flex min-h-svh items-center justify-center p-6">
56
+ <Empty className="max-w-md border-0">
57
+ <EmptyHeader>
58
+ <EmptyTitle>Something went wrong</EmptyTitle>
59
+ <EmptyDescription>
60
+ The app hit an unexpected error while loading this page.
61
+ </EmptyDescription>
62
+ </EmptyHeader>
63
+ {pathname !== '/' ? (
64
+ <EmptyContent>
65
+ <Button asChild variant="outline">
66
+ <a href="/">
67
+ <ArrowLeftIcon data-icon="inline-start" />
68
+ Go home
69
+ </a>
70
+ </Button>
71
+ </EmptyContent>
72
+ ) : null}
73
+ </Empty>
74
+ </main>
75
+ )
76
+ }
77
+
78
+ function isNotFoundError(error: unknown) {
79
+ // Server actions serialize PublicError as a plain object, not an Error instance.
80
+ if (
81
+ error &&
82
+ typeof error === 'object' &&
83
+ 'code' in error &&
84
+ typeof error.code === 'string' &&
85
+ error.code.endsWith('_NOT_FOUND') &&
86
+ Object.hasOwn(publicErrorMessages, error.code)
87
+ )
88
+ return true
89
+
90
+ if (!(error instanceof Error)) {
91
+ return false
92
+ }
93
+
94
+ return /\bnot found\.?$/i.test(error.message)
95
+ }
@@ -0,0 +1,39 @@
1
+ import { Button } from '#devpush/components/ui/button'
2
+ import { cn } from '#devpush/lib/utils'
3
+
4
+ export type SectionNavigationItem = {
5
+ href: `#${string}`
6
+ label: string
7
+ destructive?: boolean
8
+ }
9
+
10
+ export function SectionNavigation({
11
+ className,
12
+ items,
13
+ }: {
14
+ className?: string
15
+ items: Array<SectionNavigationItem>
16
+ }) {
17
+ return (
18
+ <nav
19
+ aria-label="Page sections"
20
+ className={cn('flex flex-col gap-y-1', className)}
21
+ >
22
+ {items.map((item) => (
23
+ <Button
24
+ asChild
25
+ className={cn(
26
+ 'w-full justify-start px-2',
27
+ item.destructive &&
28
+ 'text-destructive hover:bg-destructive/10 hover:text-destructive dark:hover:bg-destructive/20',
29
+ )}
30
+ key={item.href}
31
+ size="sm"
32
+ variant="ghost"
33
+ >
34
+ <a href={item.href}>{item.label}</a>
35
+ </Button>
36
+ ))}
37
+ </nav>
38
+ )
39
+ }
@@ -0,0 +1,111 @@
1
+ import { Link } from '@tanstack/react-router'
2
+ import { ArrowLeftIcon } from 'lucide-react'
3
+ import type { ReactNode } from 'react'
4
+
5
+ import { SectionNavigation } from '#devpush/components/section-navigation'
6
+ import type { SectionNavigationItem } from '#devpush/components/section-navigation'
7
+ import {
8
+ Card,
9
+ CardContent,
10
+ CardDescription,
11
+ CardFooter,
12
+ CardHeader,
13
+ CardTitle,
14
+ } from '#devpush/components/ui/card'
15
+ import { Button } from '#devpush/components/ui/button'
16
+ import {
17
+ Tooltip,
18
+ TooltipContent,
19
+ TooltipTrigger,
20
+ } from '#devpush/components/ui/tooltip'
21
+
22
+ export function StandaloneSettingsLayout({
23
+ ariaLabel,
24
+ busy = false,
25
+ children,
26
+ navigation,
27
+ title,
28
+ }: {
29
+ ariaLabel?: string
30
+ busy?: boolean
31
+ children: ReactNode
32
+ navigation: Array<SectionNavigationItem>
33
+ title: string
34
+ }) {
35
+ return (
36
+ <main className="min-h-screen bg-background text-foreground">
37
+ <div
38
+ className="mx-auto w-full max-w-screen-xl space-y-8 p-8"
39
+ aria-busy={busy || undefined}
40
+ aria-label={ariaLabel}
41
+ >
42
+ <header className="flex items-center gap-x-3">
43
+ <Tooltip>
44
+ <TooltipTrigger asChild>
45
+ <Button asChild variant="outline" size="icon-sm">
46
+ <Link to="/" aria-label="Back to home">
47
+ <ArrowLeftIcon />
48
+ </Link>
49
+ </Button>
50
+ </TooltipTrigger>
51
+ <TooltipContent>Back to home</TooltipContent>
52
+ </Tooltip>
53
+ <h1 className="text-2xl font-medium">{title}</h1>
54
+ </header>
55
+ <div className="flex flex-row-reverse gap-x-8">
56
+ <SectionNavigation
57
+ className="sticky top-8 hidden h-full min-w-64 lg:flex"
58
+ items={navigation}
59
+ />
60
+ <div className="w-full min-w-0 flex-1 space-y-8">{children}</div>
61
+ </div>
62
+ </div>
63
+ </main>
64
+ )
65
+ }
66
+
67
+ export function SettingsLayout({
68
+ children,
69
+ navigation,
70
+ }: {
71
+ children: ReactNode
72
+ navigation: Array<SectionNavigationItem>
73
+ }) {
74
+ return (
75
+ <div className="grid min-w-0 gap-8 lg:grid-cols-[minmax(0,1fr)_240px]">
76
+ <div className="min-w-0 space-y-8">{children}</div>
77
+ <aside className="hidden lg:block">
78
+ <SectionNavigation className="sticky top-[122px]" items={navigation} />
79
+ </aside>
80
+ </div>
81
+ )
82
+ }
83
+
84
+ export function SettingsSection({
85
+ children,
86
+ contentClassName,
87
+ description,
88
+ footer,
89
+ id,
90
+ title,
91
+ }: {
92
+ children: ReactNode
93
+ contentClassName?: string
94
+ description: ReactNode
95
+ footer?: ReactNode
96
+ id: string
97
+ title: string
98
+ }) {
99
+ return (
100
+ <Card id={id} className="scroll-mt-8">
101
+ <CardHeader className="border-b">
102
+ <CardTitle>{title}</CardTitle>
103
+ <CardDescription>{description}</CardDescription>
104
+ </CardHeader>
105
+ <CardContent className={contentClassName}>{children}</CardContent>
106
+ {footer ? (
107
+ <CardFooter className="justify-end gap-x-2">{footer}</CardFooter>
108
+ ) : null}
109
+ </Card>
110
+ )
111
+ }
@@ -0,0 +1,11 @@
1
+ import { createContext, useContext } from 'react'
2
+ import type { ReactNode } from 'react'
3
+
4
+ export const ImageUploadsContext = createContext(true)
5
+ export function useImageUploads() {
6
+ return useContext(ImageUploadsContext)
7
+ }
8
+
9
+ export function ImageUploadControls({ children }: { children: ReactNode }) {
10
+ return useImageUploads() ? children : null
11
+ }