@wopr-network/platform-ui-core 1.27.7 → 1.27.9

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 (353) hide show
  1. package/next.config.ts +1 -2
  2. package/package.json +17 -17
  3. package/src/__tests__/account-switcher.test.tsx +21 -20
  4. package/src/__tests__/activity-page.test.tsx +2 -6
  5. package/src/__tests__/add-payment-method-dialog.test.tsx +9 -32
  6. package/src/__tests__/admin-api.test.ts +1 -6
  7. package/src/__tests__/admin-gpu-api.test.ts +1 -3
  8. package/src/__tests__/admin-marketplace-api.test.ts +1 -4
  9. package/src/__tests__/admin-middleware.test.ts +76 -83
  10. package/src/__tests__/affiliate-dashboard.test.tsx +3 -3
  11. package/src/__tests__/api-401-redirect.test.ts +46 -9
  12. package/src/__tests__/api-client.test.ts +3 -5
  13. package/src/__tests__/api-config.test.ts +22 -42
  14. package/src/__tests__/api-fleet-resources.test.ts +1 -2
  15. package/src/__tests__/api-fleet-trpc.test.ts +2 -8
  16. package/src/__tests__/api-null-guards.test.ts +3 -1
  17. package/src/__tests__/audit-log-table-pagination.test.tsx +2 -6
  18. package/src/__tests__/auth-password-reset.test.tsx +7 -21
  19. package/src/__tests__/auth-redirect.test.tsx +8 -2
  20. package/src/__tests__/auth.test.tsx +25 -23
  21. package/src/__tests__/auto-topup-card.test.tsx +4 -12
  22. package/src/__tests__/backups-tab.test.tsx +3 -4
  23. package/src/__tests__/billing-layout-nav-hidden.test.tsx +5 -37
  24. package/src/__tests__/billing-payment-org-invoices.test.tsx +2 -18
  25. package/src/__tests__/billing.test.tsx +8 -39
  26. package/src/__tests__/bot-settings/resources-tab.test.tsx +1 -3
  27. package/src/__tests__/bot-settings/storage-tab.test.tsx +1 -3
  28. package/src/__tests__/bot-settings/vps-upgrade-card.test.tsx +1 -3
  29. package/src/__tests__/bot-settings-restart.test.tsx +1 -3
  30. package/src/__tests__/bot-settings.test.tsx +2 -6
  31. package/src/__tests__/brand.test.ts +6 -26
  32. package/src/__tests__/buy-credits-panel.test.tsx +1 -3
  33. package/src/__tests__/buy-crypto-credits-panel.test.tsx +101 -119
  34. package/src/__tests__/capability-conflicts.test.ts +2 -8
  35. package/src/__tests__/capability-resolver.test.tsx +2 -12
  36. package/src/__tests__/channel-wizard.test.tsx +4 -17
  37. package/src/__tests__/chat/chat-panel.test.tsx +1 -4
  38. package/src/__tests__/chat-store.test.ts +5 -15
  39. package/src/__tests__/command-center.test.tsx +10 -12
  40. package/src/__tests__/compliance-retention-edit.test.tsx +3 -6
  41. package/src/__tests__/confirmation-tracker.test.tsx +3 -18
  42. package/src/__tests__/coupon-input.test.tsx +1 -3
  43. package/src/__tests__/create-instance.test.tsx +1 -3
  44. package/src/__tests__/credit-balance.test.tsx +4 -12
  45. package/src/__tests__/credits.test.tsx +32 -85
  46. package/src/__tests__/email-verification-banner.test.tsx +2 -6
  47. package/src/__tests__/error-boundaries.test.tsx +0 -1
  48. package/src/__tests__/fetch-pricing.test.ts +2 -1
  49. package/src/__tests__/field-oauth.test.tsx +2 -6
  50. package/src/__tests__/fixtures/mock-manifests-data.js +1 -3
  51. package/src/__tests__/fixtures/mock-manifests.ts +2 -4
  52. package/src/__tests__/fleet-health-timestamp.test.tsx +1 -8
  53. package/src/__tests__/fleet-health-update.test.tsx +1 -8
  54. package/src/__tests__/gpu-dashboard.test.tsx +2 -6
  55. package/src/__tests__/instance-detail.test.tsx +3 -9
  56. package/src/__tests__/instance-list.test.tsx +1 -5
  57. package/src/__tests__/layout-snapshots.test.tsx +64 -11
  58. package/src/__tests__/marketplace-admin.test.tsx +2 -6
  59. package/src/__tests__/marketplace.test.tsx +11 -35
  60. package/src/__tests__/merge-api-rates.test.ts +1 -6
  61. package/src/__tests__/middleware.test.ts +32 -219
  62. package/src/__tests__/next-config-headers.test.ts +1 -3
  63. package/src/__tests__/notifications.test.tsx +4 -11
  64. package/src/__tests__/oauth-buttons.test.tsx +36 -59
  65. package/src/__tests__/oauth-error-mapping.test.tsx +2 -6
  66. package/src/__tests__/observability.test.tsx +23 -36
  67. package/src/__tests__/onboarding-page.test.tsx +4 -6
  68. package/src/__tests__/org-billing-api.test.tsx +1 -6
  69. package/src/__tests__/plugin-install-flow.test.tsx +28 -58
  70. package/src/__tests__/plugin-registry.test.tsx +3 -11
  71. package/src/__tests__/plugin-tool-sync.test.ts +1 -3
  72. package/src/__tests__/plugins-catalog-error.test.tsx +2 -6
  73. package/src/__tests__/plugins-toggle-race.test.tsx +3 -5
  74. package/src/__tests__/portfolio-chart.test.tsx +2 -6
  75. package/src/__tests__/promotion-form.test.tsx +2 -6
  76. package/src/__tests__/promotions-list.test.tsx +1 -3
  77. package/src/__tests__/provider-key-api.test.ts +2 -1
  78. package/src/__tests__/resend-verification-button.test.tsx +8 -24
  79. package/src/__tests__/secrets-audit-pagination.test.tsx +1 -3
  80. package/src/__tests__/settings.test.tsx +11 -21
  81. package/src/__tests__/setup-checklist.test.tsx +3 -9
  82. package/src/__tests__/setup.ts +25 -6
  83. package/src/__tests__/snapshot-api.test.ts +2 -1
  84. package/src/__tests__/step-superpowers.test.tsx +1 -3
  85. package/src/__tests__/tenant-context.test.tsx +1 -6
  86. package/src/__tests__/tenant-keys-api.test.ts +3 -4
  87. package/src/__tests__/tenant-table-pagination.test.tsx +2 -6
  88. package/src/__tests__/terminal-log-cleanup.test.tsx +0 -1
  89. package/src/__tests__/transaction-history.test.tsx +190 -238
  90. package/src/__tests__/trpc-types.test.ts +2 -6
  91. package/src/__tests__/use-chat.test.ts +1 -3
  92. package/src/__tests__/use-plugin-setup-chat-stale-closure.test.ts +1 -4
  93. package/src/__tests__/use-sidecar-bridge.test.tsx +105 -0
  94. package/src/__tests__/use-webmcp.test.ts +1 -3
  95. package/src/__tests__/validate-elevenlabs-key.test.ts +2 -1
  96. package/src/__tests__/verify-page.test.tsx +4 -13
  97. package/src/__tests__/verify-redirect.test.tsx +2 -6
  98. package/src/app/(auth)/error.tsx +1 -7
  99. package/src/app/(auth)/forgot-password/page.tsx +4 -18
  100. package/src/app/(auth)/login/page.tsx +5 -22
  101. package/src/app/(auth)/reset-password/page.tsx +2 -12
  102. package/src/app/(auth)/signup/page.tsx +10 -44
  103. package/src/app/(auth)/verify/page.tsx +47 -0
  104. package/src/app/(dashboard)/billing/credits/page.tsx +14 -67
  105. package/src/app/(dashboard)/billing/error.tsx +2 -10
  106. package/src/app/(dashboard)/billing/layout.tsx +12 -62
  107. package/src/app/(dashboard)/billing/payment/page.tsx +17 -68
  108. package/src/app/(dashboard)/billing/plans/page.tsx +3 -9
  109. package/src/app/(dashboard)/billing/usage/hosted/page.tsx +8 -25
  110. package/src/app/(dashboard)/billing/usage/page.tsx +63 -103
  111. package/src/app/(dashboard)/changesets/[id]/changeset-detail-client.tsx +9 -27
  112. package/src/app/(dashboard)/changesets/[id]/error.tsx +2 -6
  113. package/src/app/(dashboard)/changesets/error.tsx +1 -7
  114. package/src/app/(dashboard)/chat/page.tsx +2 -6
  115. package/src/app/(dashboard)/dashboard/network/page.tsx +5 -19
  116. package/src/app/(dashboard)/error.tsx +1 -7
  117. package/src/app/(dashboard)/layout.tsx +15 -36
  118. package/src/app/(dashboard)/marketplace/[plugin]/page.tsx +14 -51
  119. package/src/app/(dashboard)/marketplace/error.tsx +1 -7
  120. package/src/app/(dashboard)/marketplace/page.tsx +6 -27
  121. package/src/app/(dashboard)/not-found.tsx +2 -5
  122. package/src/app/(dashboard)/onboarding/page.tsx +5 -22
  123. package/src/app/(dashboard)/settings/account/page.tsx +1 -6
  124. package/src/app/(dashboard)/settings/activity/page.tsx +8 -34
  125. package/src/app/(dashboard)/settings/api-keys/page.tsx +15 -60
  126. package/src/app/(dashboard)/settings/brain/page.tsx +9 -31
  127. package/src/app/(dashboard)/settings/error.tsx +2 -10
  128. package/src/app/(dashboard)/settings/notifications/page.tsx +2 -6
  129. package/src/app/(dashboard)/settings/org/page.tsx +13 -56
  130. package/src/app/(dashboard)/settings/page.tsx +1 -0
  131. package/src/app/(dashboard)/settings/profile/page.tsx +126 -73
  132. package/src/app/(dashboard)/settings/providers/page.tsx +21 -78
  133. package/src/app/(dashboard)/settings/secrets/page.tsx +13 -58
  134. package/src/app/(dashboard)/settings/security/page.tsx +31 -111
  135. package/src/app/admin/email-templates/email-templates-client.tsx +15 -58
  136. package/src/app/admin/error.tsx +1 -7
  137. package/src/app/admin/fleet-updates/error.tsx +1 -7
  138. package/src/app/admin/fleet-updates/fleet-updates-client.tsx +10 -50
  139. package/src/app/admin/layout.tsx +4 -0
  140. package/src/app/admin/payment-methods/page.tsx +9 -38
  141. package/src/app/admin/products/error.tsx +2 -7
  142. package/src/app/admin/products/page.tsx +1 -4
  143. package/src/app/admin/promotions/[id]/page.tsx +9 -38
  144. package/src/app/admin/promotions/page.tsx +9 -36
  145. package/src/app/admin/rate-overrides/page.tsx +9 -45
  146. package/src/app/auth/callback/[provider]/page.tsx +1 -8
  147. package/src/app/auth/verify/page.tsx +9 -36
  148. package/src/app/channels/error.tsx +2 -10
  149. package/src/app/channels/layout.tsx +9 -0
  150. package/src/app/channels/page.tsx +8 -20
  151. package/src/app/channels/setup/[plugin]/page.tsx +3 -5
  152. package/src/app/error.tsx +1 -7
  153. package/src/app/fleet/error.tsx +1 -7
  154. package/src/app/fleet/layout.tsx +5 -0
  155. package/src/app/fleet/settings/page.tsx +1 -3
  156. package/src/app/global-error.tsx +2 -10
  157. package/src/app/globals.css +1 -4
  158. package/src/app/instances/[id]/instance-detail-client.tsx +51 -125
  159. package/src/app/instances/error.tsx +2 -10
  160. package/src/app/instances/instance-list-client.tsx +20 -69
  161. package/src/app/instances/layout.tsx +9 -0
  162. package/src/app/instances/new/create-instance-client.tsx +10 -31
  163. package/src/app/layout.tsx +2 -10
  164. package/src/app/not-found.tsx +1 -3
  165. package/src/app/page.tsx +1 -2
  166. package/src/app/plugins/error.tsx +2 -10
  167. package/src/app/plugins/layout.tsx +5 -0
  168. package/src/app/plugins/page.tsx +16 -48
  169. package/src/app/pricing/error.tsx +1 -7
  170. package/src/app/privacy/page.tsx +93 -150
  171. package/src/app/status/error.tsx +1 -7
  172. package/src/app/terms/page.tsx +89 -144
  173. package/src/components/account-switcher.tsx +25 -52
  174. package/src/components/admin/accounting-dashboard.tsx +1 -3
  175. package/src/components/admin/admin-guard.tsx +1 -3
  176. package/src/components/admin/admin-nav.tsx +1 -3
  177. package/src/components/admin/affiliate-dashboard.tsx +25 -94
  178. package/src/components/admin/audit-log-table.tsx +13 -49
  179. package/src/components/admin/billing-health-dashboard.tsx +7 -25
  180. package/src/components/admin/bulk-actions-bar.test.tsx +1 -7
  181. package/src/components/admin/bulk-actions-bar.tsx +1 -3
  182. package/src/components/admin/bulk-export-dialog.test.tsx +1 -7
  183. package/src/components/admin/bulk-export-dialog.tsx +6 -32
  184. package/src/components/admin/bulk-grant-dialog.test.tsx +2 -6
  185. package/src/components/admin/bulk-grant-dialog.tsx +4 -15
  186. package/src/components/admin/bulk-preview-dialog.tsx +3 -12
  187. package/src/components/admin/bulk-reactivate-dialog.tsx +1 -7
  188. package/src/components/admin/bulk-select-all-banner.tsx +1 -6
  189. package/src/components/admin/bulk-suspend-dialog.tsx +5 -12
  190. package/src/components/admin/bulk-undo-toast.tsx +1 -2
  191. package/src/components/admin/compliance-dashboard.tsx +31 -101
  192. package/src/components/admin/gpu-dashboard.tsx +21 -70
  193. package/src/components/admin/grant-credits-dialog.tsx +4 -17
  194. package/src/components/admin/incident-dashboard.tsx +10 -25
  195. package/src/components/admin/inference-dashboard.tsx +14 -54
  196. package/src/components/admin/marketplace-admin.tsx +18 -60
  197. package/src/components/admin/migrations-dashboard.tsx +9 -42
  198. package/src/components/admin/onboarding-dashboard.tsx +14 -64
  199. package/src/components/admin/pool-config-dashboard.tsx +4 -10
  200. package/src/components/admin/products/fleet-form.tsx +2 -11
  201. package/src/components/admin/products/nav-editor.tsx +3 -10
  202. package/src/components/admin/promotions/promotion-form.tsx +9 -42
  203. package/src/components/admin/roles-dashboard.tsx +7 -34
  204. package/src/components/admin/suspend-dialog.tsx +4 -11
  205. package/src/components/admin/tenant-notes-panel.tsx +1 -3
  206. package/src/components/admin/tenant-row-actions.tsx +4 -20
  207. package/src/components/admin/tenant-table.tsx +12 -49
  208. package/src/components/auth/auth-redirect.tsx +11 -3
  209. package/src/components/auth/email-verification-result-banner.tsx +1 -3
  210. package/src/components/auth/resend-verification-button.tsx +2 -10
  211. package/src/components/auth/wopr-wordmark.tsx +1 -3
  212. package/src/components/billing/add-payment-method-dialog.tsx +1 -2
  213. package/src/components/billing/affiliate-dashboard.tsx +4 -16
  214. package/src/components/billing/amount-selector.tsx +1 -3
  215. package/src/components/billing/auto-topup-card.tsx +2 -11
  216. package/src/components/billing/buy-credits-panel.tsx +14 -17
  217. package/src/components/billing/byok-callout.tsx +6 -8
  218. package/src/components/billing/confirmation-tracker.tsx +4 -14
  219. package/src/components/billing/credit-balance-badge.tsx +22 -0
  220. package/src/components/billing/credit-balance.tsx +3 -9
  221. package/src/components/billing/crypto-checkout.tsx +5 -24
  222. package/src/components/billing/degraded-state-banner.tsx +1 -3
  223. package/src/components/billing/deposit-view.tsx +301 -41
  224. package/src/components/billing/dividend-banner.tsx +1 -3
  225. package/src/components/billing/dividend-eligibility.tsx +3 -12
  226. package/src/components/billing/dividend-pool-stats.tsx +6 -20
  227. package/src/components/billing/first-dividend-dialog.tsx +2 -2
  228. package/src/components/billing/org-billing-page.tsx +8 -31
  229. package/src/components/billing/payment-method-picker.tsx +2 -10
  230. package/src/components/billing/suspension-banner.tsx +2 -7
  231. package/src/components/billing/transaction-history.tsx +10 -58
  232. package/src/components/billing/unified-checkout.tsx +547 -0
  233. package/src/components/bot-settings/backups-tab.tsx +9 -33
  234. package/src/components/bot-settings/bot-settings-client.tsx +32 -134
  235. package/src/components/bot-settings/resources-tab.tsx +2 -9
  236. package/src/components/bot-settings/storage-tab.tsx +19 -48
  237. package/src/components/bot-settings/vps-info-panel.tsx +3 -11
  238. package/src/components/bot-settings/vps-upgrade-card.tsx +3 -4
  239. package/src/components/brand-hydrator.tsx +13 -0
  240. package/src/components/channel-wizard/field-interactive.tsx +1 -3
  241. package/src/components/channel-wizard/field-qr.tsx +10 -39
  242. package/src/components/channel-wizard/step-renderer.tsx +5 -28
  243. package/src/components/channel-wizard/wizard.tsx +6 -31
  244. package/src/components/chat/chat-message.tsx +1 -4
  245. package/src/components/chat/chat-panel.tsx +4 -18
  246. package/src/components/chat/chat-widget.tsx +3 -14
  247. package/src/components/dashboard/command-center.tsx +15 -61
  248. package/src/components/fleet/update-settings-card.tsx +7 -23
  249. package/src/components/instance-update-banner.tsx +130 -0
  250. package/src/components/instances/friends-tab.test.tsx +2 -9
  251. package/src/components/instances/friends-tab.tsx +18 -74
  252. package/src/components/instances/update-available-badge.tsx +2 -11
  253. package/src/components/landing/hero.tsx +3 -9
  254. package/src/components/landing/landing-page.tsx +1 -3
  255. package/src/components/landing/portfolio-chart.tsx +4 -9
  256. package/src/components/landing/story-sections.tsx +1 -3
  257. package/src/components/landing/terminal-sequence.tsx +4 -17
  258. package/src/components/marketplace/empty-state.tsx +2 -6
  259. package/src/components/marketplace/first-visit-hero.tsx +1 -3
  260. package/src/components/marketplace/install-wizard.tsx +20 -77
  261. package/src/components/marketplace/marketplace-tabs.tsx +1 -4
  262. package/src/components/marketplace/plugin-card.tsx +2 -9
  263. package/src/components/marketplace/superpower-content.tsx +1 -3
  264. package/src/components/marketplace/terminal-search.tsx +2 -8
  265. package/src/components/oauth-buttons.tsx +29 -14
  266. package/src/components/observability/fleet-health.tsx +5 -18
  267. package/src/components/observability/health-overview.tsx +7 -20
  268. package/src/components/observability/logs-viewer.tsx +8 -32
  269. package/src/components/observability/metrics-dashboard.tsx +2 -15
  270. package/src/components/onboarding/fallback-setup.tsx +6 -25
  271. package/src/components/onboarding/setup-checklist.tsx +18 -51
  272. package/src/components/onboarding/step-superpowers.tsx +1 -4
  273. package/src/components/plugin-setup/setup-chat-panel.tsx +6 -22
  274. package/src/components/pricing/dividend-calculator.tsx +6 -12
  275. package/src/components/pricing/dividend-stats.tsx +5 -17
  276. package/src/components/pricing/pricing-page.tsx +17 -36
  277. package/src/components/settings/create-org-wizard.tsx +2 -5
  278. package/src/components/sidebar.tsx +7 -42
  279. package/src/components/sidecar-frame.tsx +78 -0
  280. package/src/components/status/status-page.tsx +6 -28
  281. package/src/components/ui/alert-dialog.tsx +8 -25
  282. package/src/components/ui/badge.tsx +2 -8
  283. package/src/components/ui/banner.tsx +1 -6
  284. package/src/components/ui/card.tsx +5 -24
  285. package/src/components/ui/checkbox.tsx +1 -5
  286. package/src/components/ui/collapsible.tsx +3 -8
  287. package/src/components/ui/dialog.tsx +4 -10
  288. package/src/components/ui/dropdown-menu.tsx +9 -18
  289. package/src/components/ui/form.tsx +2 -16
  290. package/src/components/ui/popover.tsx +3 -23
  291. package/src/components/ui/progress.tsx +1 -5
  292. package/src/components/ui/radio-group.tsx +3 -15
  293. package/src/components/ui/select.tsx +4 -17
  294. package/src/components/ui/sheet.tsx +5 -19
  295. package/src/components/ui/skeleton.tsx +1 -7
  296. package/src/components/ui/table.tsx +5 -22
  297. package/src/components/ui/tabs.tsx +3 -13
  298. package/src/components/ui/tooltip.tsx +1 -1
  299. package/src/components/unified-sidebar.tsx +493 -0
  300. package/src/hooks/__tests__/use-fleet-sse.test.ts +1 -4
  301. package/src/hooks/__tests__/use-save-queue.test.ts +2 -8
  302. package/src/hooks/use-credit-balance.ts +27 -0
  303. package/src/hooks/use-my-org-role.ts +1 -3
  304. package/src/hooks/use-plugin-registry.ts +8 -14
  305. package/src/hooks/use-plugin-setup-chat.ts +2 -5
  306. package/src/hooks/use-sidecar-bridge.tsx +148 -0
  307. package/src/hooks/use-webmcp.ts +1 -4
  308. package/src/lib/__tests__/admin-api.test.ts +1 -3
  309. package/src/lib/__tests__/api-bot-crud.test.ts +8 -18
  310. package/src/lib/__tests__/api-fetch.test.ts +4 -16
  311. package/src/lib/__tests__/org-billing-api.test.ts +1 -3
  312. package/src/lib/__tests__/pricing-data.test.ts +0 -8
  313. package/src/lib/__tests__/settings-api.test.ts +1 -3
  314. package/src/lib/admin-affiliate-api.ts +2 -7
  315. package/src/lib/admin-api.ts +6 -26
  316. package/src/lib/admin-incident-api.ts +11 -19
  317. package/src/lib/admin-marketplace-api.ts +1 -5
  318. package/src/lib/api-config.test.ts +5 -50
  319. package/src/lib/api.ts +143 -122
  320. package/src/lib/auth-client.ts +1 -2
  321. package/src/lib/bot-settings-data.ts +11 -36
  322. package/src/lib/brand-config.ts +56 -115
  323. package/src/lib/brand.ts +2 -15
  324. package/src/lib/chat/use-chat.ts +2 -7
  325. package/src/lib/cost-comparison-data.test.ts +1 -3
  326. package/src/lib/cost-comparison-data.ts +1 -4
  327. package/src/lib/errors.ts +1 -4
  328. package/src/lib/fetch-utils.test.ts +26 -9
  329. package/src/lib/fetch-utils.ts +40 -11
  330. package/src/lib/logger.ts +2 -0
  331. package/src/lib/marketplace-data.ts +3 -11
  332. package/src/lib/oauth-errors.ts +2 -4
  333. package/src/lib/onboarding-data.ts +3 -11
  334. package/src/lib/org-api.ts +2 -10
  335. package/src/lib/org-billing-api.ts +5 -19
  336. package/src/lib/plugin/tool-definitions.ts +1 -2
  337. package/src/lib/require-auth.ts +57 -0
  338. package/src/lib/settings-api.ts +1 -4
  339. package/src/lib/sidecar-routes.ts +43 -0
  340. package/src/lib/trpc-server.ts +49 -0
  341. package/src/lib/trpc-types.ts +4 -6
  342. package/src/lib/trpc.tsx +12 -4
  343. package/src/lib/validate-redirect-url.ts +1 -4
  344. package/src/lib/webmcp/marketplace-onboarding-tools.ts +6 -16
  345. package/src/lib/webmcp/register.ts +1 -4
  346. package/src/lib/webmcp/tools.ts +2 -9
  347. package/src/proxy.ts +35 -212
  348. package/src/types/missing-deps.d.ts +2 -8
  349. package/tsconfig.json +1 -8
  350. package/biome.json +0 -52
  351. package/src/__tests__/__snapshots__/layout-snapshots.test.tsx.snap +0 -741
  352. package/src/__tests__/billing-byok-callout.test.tsx +0 -76
  353. package/src/lib/__tests__/__snapshots__/pricing-data.test.ts.snap +0 -112
@@ -1,4 +1,4 @@
1
- import { render } from "@testing-library/react";
1
+ import { render, screen } from "@testing-library/react";
2
2
  import { describe, expect, it, vi } from "vitest";
3
3
 
4
4
  // --- Mocks ---
@@ -15,7 +15,11 @@ vi.mock("next/font/google", () => ({
15
15
 
16
16
  vi.mock("better-auth/react", () => ({
17
17
  createAuthClient: () => ({
18
- useSession: () => ({ data: null, isPending: false, error: null }),
18
+ useSession: () => ({
19
+ data: { user: { id: "u1", email: "test@test.com" }, session: { id: "s1" } },
20
+ isPending: false,
21
+ error: null,
22
+ }),
19
23
  signIn: { email: vi.fn(), social: vi.fn() },
20
24
  signUp: { email: vi.fn() },
21
25
  signOut: vi.fn(),
@@ -23,10 +27,30 @@ vi.mock("better-auth/react", () => ({
23
27
  }));
24
28
 
25
29
  vi.mock("@/lib/auth-client", () => ({
26
- useSession: () => ({ data: null, isPending: false, error: null }),
30
+ useSession: () => ({
31
+ data: { user: { id: "u1", email: "test@test.com" }, session: { id: "s1" } },
32
+ isPending: false,
33
+ error: null,
34
+ }),
27
35
  signOut: vi.fn(),
28
36
  }));
29
37
 
38
+ vi.mock("@/lib/require-auth", () => ({
39
+ useRequireAuth: () => ({
40
+ user: { id: "u1", email: "test@test.com" },
41
+ session: { id: "s1" },
42
+ isPending: false,
43
+ isAuthed: true,
44
+ }),
45
+ useRequireAdmin: () => ({
46
+ user: { id: "u1", email: "test@test.com", role: "platform_admin" },
47
+ session: { id: "s1" },
48
+ isPending: false,
49
+ isAuthed: true,
50
+ isAdmin: true,
51
+ }),
52
+ }));
53
+
30
54
  vi.mock("@/components/sidebar", () => ({
31
55
  Sidebar: () => <div data-testid="sidebar">Sidebar</div>,
32
56
  SidebarContent: ({ onNavigate: _o }: { onNavigate?: () => void }) => (
@@ -62,6 +86,22 @@ vi.mock("@/lib/api", () => ({
62
86
  vi.mock("@/lib/api-config", () => ({
63
87
  SITE_URL: "https://localhost",
64
88
  PLATFORM_BASE_URL: "https://localhost",
89
+ API_BASE_URL: "https://localhost/api",
90
+ }));
91
+
92
+ vi.mock("@/lib/brand-config", () => ({
93
+ getBrandConfig: () => ({
94
+ chatEnabled: true,
95
+ tenantCookieName: "platform_tenant_id",
96
+ homePath: "/marketplace",
97
+ productName: "Platform",
98
+ brandName: "Platform",
99
+ domain: "localhost",
100
+ navItems: [],
101
+ }),
102
+ productName: () => "Platform",
103
+ brandName: () => "Platform",
104
+ setBrandConfig: vi.fn(),
65
105
  }));
66
106
 
67
107
  vi.mock("@/components/auth/email-verification-banner", () => ({
@@ -102,7 +142,8 @@ describe("Layout snapshots", () => {
102
142
  <div>auth child</div>
103
143
  </AuthLayout>,
104
144
  );
105
- expect(container).toMatchSnapshot();
145
+ expect(container.querySelector(".min-h-screen")).not.toBeNull();
146
+ expect(container.textContent).toContain("auth child");
106
147
  });
107
148
 
108
149
  it("DashboardLayout renders sidebar + main with mobile sheet", async () => {
@@ -112,7 +153,12 @@ describe("Layout snapshots", () => {
112
153
  <div>dashboard child</div>
113
154
  </DashboardLayout>,
114
155
  );
115
- expect(container).toMatchSnapshot();
156
+ expect(screen.getByTestId("sidebar")).not.toBeNull();
157
+ expect(container.textContent).toContain("dashboard child");
158
+ // Desktop layout
159
+ expect(container.querySelector(".hidden.lg\\:flex")).not.toBeNull();
160
+ // Mobile layout
161
+ expect(container.querySelector(".lg\\:hidden")).not.toBeNull();
116
162
  });
117
163
 
118
164
  it("FleetLayout renders sidebar + main", async () => {
@@ -122,7 +168,8 @@ describe("Layout snapshots", () => {
122
168
  <div>fleet child</div>
123
169
  </FleetLayout>,
124
170
  );
125
- expect(container).toMatchSnapshot();
171
+ expect(screen.getByTestId("sidebar")).not.toBeNull();
172
+ expect(container.textContent).toContain("fleet child");
126
173
  });
127
174
 
128
175
  it("PluginsLayout renders sidebar + main", async () => {
@@ -132,7 +179,8 @@ describe("Layout snapshots", () => {
132
179
  <div>plugins child</div>
133
180
  </PluginsLayout>,
134
181
  );
135
- expect(container).toMatchSnapshot();
182
+ expect(screen.getByTestId("sidebar")).not.toBeNull();
183
+ expect(container.textContent).toContain("plugins child");
136
184
  });
137
185
 
138
186
  it("AdminLayout renders desktop sidebar + admin nav + mobile fallback", async () => {
@@ -142,7 +190,9 @@ describe("Layout snapshots", () => {
142
190
  <div>admin child</div>
143
191
  </AdminLayout>,
144
192
  );
145
- expect(container).toMatchSnapshot();
193
+ expect(screen.getByTestId("sidebar")).not.toBeNull();
194
+ expect(screen.getByTestId("admin-nav")).not.toBeNull();
195
+ expect(container.textContent).toContain("admin child");
146
196
  });
147
197
 
148
198
  it("SettingsLayout renders desktop nav + mobile sheet", async () => {
@@ -152,16 +202,19 @@ describe("Layout snapshots", () => {
152
202
  <div>settings child</div>
153
203
  </SettingsLayout>,
154
204
  );
155
- expect(container).toMatchSnapshot();
205
+ expect(container.textContent).toContain("settings child");
206
+ expect(container.textContent).toContain("Settings");
156
207
  });
157
208
 
158
- it("BillingLayout renders billing nav sidebar + content area", async () => {
209
+ it("BillingLayout renders content area + footer", async () => {
159
210
  const { default: BillingLayout } = await import("@/app/(dashboard)/billing/layout");
160
211
  const { container } = render(
161
212
  <BillingLayout>
162
213
  <div>billing child</div>
163
214
  </BillingLayout>,
164
215
  );
165
- expect(container).toMatchSnapshot();
216
+ expect(container.textContent).toContain("billing child");
217
+ expect(container.textContent).toContain("Terms of Service");
218
+ expect(container.textContent).toContain("Privacy Policy");
166
219
  });
167
220
  });
@@ -113,9 +113,7 @@ describe("admin-marketplace-api", () => {
113
113
  const { updatePlugin } = await import("../lib/admin-marketplace-api");
114
114
  mockUpdatePlugin.mockRejectedValue(new Error("tRPC unavailable"));
115
115
 
116
- await expect(updatePlugin({ id: "discord", notes: "fallback note" })).rejects.toThrow(
117
- "tRPC unavailable",
118
- );
116
+ await expect(updatePlugin({ id: "discord", notes: "fallback note" })).rejects.toThrow("tRPC unavailable");
119
117
  });
120
118
  });
121
119
 
@@ -143,9 +141,7 @@ describe("admin-marketplace-api", () => {
143
141
  const { addPluginByNpm } = await import("../lib/admin-marketplace-api");
144
142
  mockAddPlugin.mockRejectedValue(new Error("tRPC unavailable"));
145
143
 
146
- await expect(addPluginByNpm({ npm_package: "@wopr-network/plugin-test" })).rejects.toThrow(
147
- "tRPC unavailable",
148
- );
144
+ await expect(addPluginByNpm({ npm_package: "@wopr-network/plugin-test" })).rejects.toThrow("tRPC unavailable");
149
145
  });
150
146
  });
151
147
  });
@@ -33,11 +33,7 @@ vi.mock("next/navigation", () => ({
33
33
 
34
34
  // --- Mock next/link ---
35
35
  vi.mock("next/link", () => ({
36
- default: ({
37
- children,
38
- href,
39
- ...props
40
- }: { children: React.ReactNode; href: string } & Record<string, unknown>) => (
36
+ default: ({ children, href, ...props }: { children: React.ReactNode; href: string } & Record<string, unknown>) => (
41
37
  <a href={href} {...props}>
42
38
  {children}
43
39
  </a>
@@ -307,9 +303,7 @@ describe("PluginDetailPage", () => {
307
303
 
308
304
  it("renders plugin detail page with manifest info", async () => {
309
305
  mockParams.plugin = "discord";
310
- const { default: PluginDetailPage } = await import(
311
- "../app/(dashboard)/marketplace/[plugin]/page"
312
- );
306
+ const { default: PluginDetailPage } = await import("../app/(dashboard)/marketplace/[plugin]/page");
313
307
  renderWithQueryClient(<PluginDetailPage />);
314
308
 
315
309
  // Wait for loading
@@ -321,9 +315,7 @@ describe("PluginDetailPage", () => {
321
315
 
322
316
  it("shows not found for invalid plugin id", async () => {
323
317
  mockParams.plugin = "nonexistent-plugin";
324
- const { default: PluginDetailPage } = await import(
325
- "../app/(dashboard)/marketplace/[plugin]/page"
326
- );
318
+ const { default: PluginDetailPage } = await import("../app/(dashboard)/marketplace/[plugin]/page");
327
319
  renderWithQueryClient(<PluginDetailPage />);
328
320
 
329
321
  expect(await screen.findByText("Plugin not found.")).toBeInTheDocument();
@@ -331,9 +323,7 @@ describe("PluginDetailPage", () => {
331
323
 
332
324
  it("shows hosted adapter info for eligible plugins", async () => {
333
325
  mockParams.plugin = "semantic-memory";
334
- const { default: PluginDetailPage } = await import(
335
- "../app/(dashboard)/marketplace/[plugin]/page"
336
- );
326
+ const { default: PluginDetailPage } = await import("../app/(dashboard)/marketplace/[plugin]/page");
337
327
  renderWithQueryClient(<PluginDetailPage />);
338
328
 
339
329
  await screen.findByText("A Bot That Never Forgets");
@@ -344,9 +334,7 @@ describe("PluginDetailPage", () => {
344
334
 
345
335
  it("shows requirements for plugins with dependencies", async () => {
346
336
  mockParams.plugin = "meeting-transcriber";
347
- const { default: PluginDetailPage } = await import(
348
- "../app/(dashboard)/marketplace/[plugin]/page"
349
- );
337
+ const { default: PluginDetailPage } = await import("../app/(dashboard)/marketplace/[plugin]/page");
350
338
  renderWithQueryClient(<PluginDetailPage />);
351
339
 
352
340
  await screen.findByText("Fire Your Secretary");
@@ -356,9 +344,7 @@ describe("PluginDetailPage", () => {
356
344
  it("opens install wizard when Install button is clicked", async () => {
357
345
  const user = userEvent.setup();
358
346
  mockParams.plugin = "webhooks";
359
- const { default: PluginDetailPage } = await import(
360
- "../app/(dashboard)/marketplace/[plugin]/page"
361
- );
347
+ const { default: PluginDetailPage } = await import("../app/(dashboard)/marketplace/[plugin]/page");
362
348
  renderWithQueryClient(<PluginDetailPage />);
363
349
 
364
350
  await screen.findByText("Webhooks");
@@ -371,9 +357,7 @@ describe("PluginDetailPage", () => {
371
357
 
372
358
  it("shows changelog entries", async () => {
373
359
  mockParams.plugin = "discord";
374
- const { default: PluginDetailPage } = await import(
375
- "../app/(dashboard)/marketplace/[plugin]/page"
376
- );
360
+ const { default: PluginDetailPage } = await import("../app/(dashboard)/marketplace/[plugin]/page");
377
361
  renderWithQueryClient(<PluginDetailPage />);
378
362
 
379
363
  await screen.findByText("Discord");
@@ -389,9 +373,7 @@ describe("PluginDetailPage", () => {
389
373
 
390
374
  it("shows configuration schema", async () => {
391
375
  mockParams.plugin = "discord";
392
- const { default: PluginDetailPage } = await import(
393
- "../app/(dashboard)/marketplace/[plugin]/page"
394
- );
376
+ const { default: PluginDetailPage } = await import("../app/(dashboard)/marketplace/[plugin]/page");
395
377
  renderWithQueryClient(<PluginDetailPage />);
396
378
 
397
379
  await screen.findByText("Discord");
@@ -405,9 +387,7 @@ describe("PluginDetailPage", () => {
405
387
  });
406
388
 
407
389
  describe("InstallWizard", () => {
408
- const mockBots = [
409
- { id: "00000000-0000-4000-8000-000000000001", name: "My Bot", state: "running" },
410
- ];
390
+ const mockBots = [{ id: "00000000-0000-4000-8000-000000000001", name: "My Bot", state: "running" }];
411
391
 
412
392
  beforeEach(() => {
413
393
  // Mock fetch for listBots so the wizard doesn't hang on network calls
@@ -458,9 +438,7 @@ describe("InstallWizard", () => {
458
438
 
459
439
  // First phase is bot-select — wait for bots to load and select one
460
440
  const user = userEvent.setup();
461
- expect(
462
- await screen.findByText("Select which bot to install this plugin on"),
463
- ).toBeInTheDocument();
441
+ expect(await screen.findByText("Select which bot to install this plugin on")).toBeInTheDocument();
464
442
 
465
443
  // Wait for bots to load
466
444
  const botButton = await screen.findByText("My Bot");
@@ -477,9 +455,7 @@ describe("InstallWizard", () => {
477
455
 
478
456
  // Should now show provider selector
479
457
  expect(
480
- screen.getByText(
481
- "Some capabilities can be provided by Platform Hosted services. Choose for each:",
482
- ),
458
+ screen.getByText("Some capabilities can be provided by Platform Hosted services. Choose for each:"),
483
459
  ).toBeInTheDocument();
484
460
  expect(screen.getByText("LLM")).toBeInTheDocument();
485
461
  expect(screen.getByText("STT")).toBeInTheDocument();
@@ -21,12 +21,7 @@ describe("mergeApiRates", () => {
21
21
  tts: [{ name: "TTS", unit: "1K chars", price: 0.2 }],
22
22
  });
23
23
 
24
- expect(result.map((c) => c.category)).toEqual([
25
- "Text Generation",
26
- "Voice",
27
- "Image Generation",
28
- "Messaging",
29
- ]);
24
+ expect(result.map((c) => c.category)).toEqual(["Text Generation", "Voice", "Image Generation", "Messaging"]);
30
25
  });
31
26
 
32
27
  it("handles unknown capability keys with fallback", () => {
@@ -1,5 +1,5 @@
1
1
  import { NextRequest } from "next/server";
2
- import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
2
+ import { afterEach, describe, expect, it, vi } from "vitest";
3
3
  import middleware, { config, validateCsrfOrigin } from "../proxy";
4
4
 
5
5
  /**
@@ -38,118 +38,36 @@ function buildRequest(
38
38
  return req;
39
39
  }
40
40
 
41
- function isRedirect(res: Response): boolean {
42
- return res.status >= 300 && res.status < 400;
43
- }
44
-
45
- function redirectPath(res: Response): string {
46
- const loc = res.headers.get("location");
47
- if (!loc) return "";
48
- try {
49
- const u = new URL(loc);
50
- return u.pathname + (u.search ? u.search : "");
51
- } catch {
52
- // Non-URL location string (e.g. relative path) — return as-is
53
- return loc;
54
- }
55
- }
56
-
57
41
  function isPassThrough(res: Response): boolean {
58
42
  return res.headers.get("x-middleware-next") === "1";
59
43
  }
60
44
 
61
45
  describe("middleware", () => {
62
- beforeEach(() => {
63
- // Reset to a safe no-session mock by default.
64
- // Tests that need fetch (admin routes) stub it themselves.
65
- vi.stubGlobal("fetch", vi.fn().mockResolvedValue({ ok: false }));
66
- delete process.env.NEXT_PUBLIC_APP_DOMAIN;
67
- });
68
-
69
46
  afterEach(() => {
70
47
  vi.unstubAllGlobals();
71
- delete process.env.NEXT_PUBLIC_APP_DOMAIN;
72
48
  });
73
49
 
74
50
  // ---------------------------------------------------------------------------
75
- // Unauthenticated redirects
51
+ // Pass-through with CSP (middleware no longer does auth — useRequireAuth handles it)
76
52
  // ---------------------------------------------------------------------------
77
- describe("unauthenticated redirects", () => {
78
- it("redirects /dashboard to /login with callbackUrl", async () => {
53
+ describe("pass-through with CSP", () => {
54
+ it("passes /dashboard through without session cookie", async () => {
79
55
  const req = buildRequest("/dashboard");
80
56
  const res = await middleware(req);
81
- expect(isRedirect(res)).toBe(true);
82
- const loc = redirectPath(res);
83
- expect(loc).toContain("/login");
84
- expect(loc).toContain("callbackUrl=%2Fdashboard");
57
+ expect(isPassThrough(res)).toBe(true);
58
+ expect(res.headers.get("content-security-policy")).not.toBeNull();
85
59
  });
86
60
 
87
- it("redirects /marketplace to /login with callbackUrl", async () => {
61
+ it("passes /marketplace through without session cookie", async () => {
88
62
  const req = buildRequest("/marketplace");
89
63
  const res = await middleware(req);
90
- expect(isRedirect(res)).toBe(true);
91
- expect(redirectPath(res)).toContain("/login");
92
- expect(redirectPath(res)).toContain("callbackUrl=%2Fmarketplace");
64
+ expect(isPassThrough(res)).toBe(true);
93
65
  });
94
66
 
95
- it("redirects /settings to /login", async () => {
67
+ it("passes /settings through without session cookie", async () => {
96
68
  const req = buildRequest("/settings");
97
69
  const res = await middleware(req);
98
- expect(isRedirect(res)).toBe(true);
99
- expect(redirectPath(res)).toContain("/login");
100
- });
101
-
102
- it("does not treat an empty session cookie as authenticated", async () => {
103
- const req = buildRequest("/dashboard", {
104
- cookies: { "better-auth.session_token": "" },
105
- });
106
- const res = await middleware(req);
107
- expect(isRedirect(res)).toBe(true);
108
- expect(redirectPath(res)).toContain("/login");
109
- });
110
-
111
- it("does not treat a whitespace-only session cookie as authenticated", async () => {
112
- const req = buildRequest("/dashboard", {
113
- cookies: { "better-auth.session_token": " " },
114
- });
115
- const res = await middleware(req);
116
- expect(isRedirect(res)).toBe(true);
117
- expect(redirectPath(res)).toContain("/login");
118
- });
119
- });
120
-
121
- // ---------------------------------------------------------------------------
122
- // Open redirect prevention — callbackUrl sanitization
123
- // ---------------------------------------------------------------------------
124
- describe("callbackUrl sanitization (open redirect prevention)", () => {
125
- it("sanitizes percent-encoded protocol-relative pathname in callbackUrl", async () => {
126
- // /%2F%2Fevil-redirect decodes to //evil-redirect — must be rejected.
127
- // Path has no dot so it reaches the session check (not static-file bypass).
128
- const req = buildRequest("/%2F%2Fevil-redirect");
129
- const res = await middleware(req);
130
- expect(isRedirect(res)).toBe(true);
131
- const loc = redirectPath(res);
132
- expect(loc).toContain("/login");
133
- // callbackUrl must be "/" (sanitized), not the malicious path
134
- expect(loc).toContain("callbackUrl=%2F");
135
- expect(loc).not.toContain("evil-redirect");
136
- });
137
-
138
- it("preserves legitimate callbackUrl for normal paths", async () => {
139
- const req = buildRequest("/settings/profile");
140
- const res = await middleware(req);
141
- expect(isRedirect(res)).toBe(true);
142
- const loc = redirectPath(res);
143
- expect(loc).toContain("callbackUrl=%2Fsettings%2Fprofile");
144
- });
145
-
146
- it("sanitizes mixed-case percent-encoded bypass attempts", async () => {
147
- // /%2f%2Fevil-redirect also decodes to //evil-redirect — path has no dot.
148
- const req = buildRequest("/%2f%2Fevil-redirect");
149
- const res = await middleware(req);
150
- expect(isRedirect(res)).toBe(true);
151
- const loc = redirectPath(res);
152
- expect(loc).not.toContain("evil-redirect");
70
+ expect(isPassThrough(res)).toBe(true);
153
71
  });
154
72
  });
155
73
 
@@ -277,68 +195,42 @@ describe("middleware", () => {
277
195
  expect(isPassThrough(res)).toBe(true);
278
196
  });
279
197
 
280
- it("does NOT allow /terms/extra (exact match only, no prefix)", async () => {
198
+ it("passes /terms/extra through (auth handled client-side)", async () => {
281
199
  const req = buildRequest("/terms/extra");
282
200
  const res = await middleware(req);
283
- expect(isRedirect(res)).toBe(true);
284
- expect(redirectPath(res)).toContain("/login");
201
+ expect(isPassThrough(res)).toBe(true);
285
202
  });
286
203
 
287
- it("does NOT allow /pricing/enterprise (exact match only)", async () => {
204
+ it("passes /pricing/enterprise through (auth handled client-side)", async () => {
288
205
  const req = buildRequest("/pricing/enterprise");
289
206
  const res = await middleware(req);
290
- expect(isRedirect(res)).toBe(true);
291
- expect(redirectPath(res)).toContain("/login");
207
+ expect(isPassThrough(res)).toBe(true);
292
208
  });
293
209
 
294
- it("does NOT allow /privacy/policy (exact match only)", async () => {
210
+ it("passes /privacy/policy through (auth handled client-side)", async () => {
295
211
  const req = buildRequest("/privacy/policy");
296
212
  const res = await middleware(req);
297
- expect(isRedirect(res)).toBe(true);
298
- expect(redirectPath(res)).toContain("/login");
213
+ expect(isPassThrough(res)).toBe(true);
299
214
  });
300
215
  });
301
216
 
302
217
  // ---------------------------------------------------------------------------
303
218
  // Root path (/) — special handling
304
219
  // ---------------------------------------------------------------------------
305
- describe("root path special handling", () => {
306
- it("allows unauthenticated GET / (public exact path)", async () => {
220
+ describe("root path", () => {
221
+ it("passes GET / through with CSP (auth handled client-side)", async () => {
307
222
  const req = buildRequest("/");
308
223
  const res = await middleware(req);
309
224
  expect(isPassThrough(res)).toBe(true);
225
+ expect(res.headers.get("content-security-policy")).not.toBeNull();
310
226
  });
311
227
 
312
- it("redirects authenticated GET / to /marketplace (no app domain configured)", async () => {
313
- const req = buildRequest("/", {
314
- cookies: { "better-auth.session_token": "valid-token" },
315
- });
316
- const res = await middleware(req);
317
- expect(isRedirect(res)).toBe(true);
318
- expect(redirectPath(res)).toBe("/marketplace");
319
- });
320
-
321
- it("redirects authenticated GET / to app domain when NEXT_PUBLIC_APP_DOMAIN is set and host is the marketing domain", async () => {
322
- process.env.NEXT_PUBLIC_APP_DOMAIN = "app.localhost";
323
- const req = buildRequest("/", {
324
- cookies: { "better-auth.session_token": "valid-token" },
325
- host: "localhost",
326
- });
327
- const res = await middleware(req);
328
- expect(isRedirect(res)).toBe(true);
329
- const loc = res.headers.get("location");
330
- expect(loc).toBe("https://app.localhost/marketplace");
331
- });
332
-
333
- it("redirects authenticated GET / to /marketplace when already on app subdomain", async () => {
334
- process.env.NEXT_PUBLIC_APP_DOMAIN = "app.localhost";
228
+ it("passes authenticated GET / through with CSP", async () => {
335
229
  const req = buildRequest("/", {
336
230
  cookies: { "better-auth.session_token": "valid-token" },
337
- host: "app.localhost",
338
231
  });
339
232
  const res = await middleware(req);
340
- expect(isRedirect(res)).toBe(true);
341
- expect(redirectPath(res)).toBe("/marketplace");
233
+ expect(isPassThrough(res)).toBe(true);
342
234
  });
343
235
  });
344
236
 
@@ -370,11 +262,10 @@ describe("middleware", () => {
370
262
  expect(isPassThrough(res)).toBe(true);
371
263
  });
372
264
 
373
- it("redirects unauthenticated GET /api/something to /login", async () => {
265
+ it("passes /api/something through (auth handled client-side)", async () => {
374
266
  const req = buildRequest("/api/something");
375
267
  const res = await middleware(req);
376
- expect(isRedirect(res)).toBe(true);
377
- expect(redirectPath(res)).toContain("/login");
268
+ expect(isPassThrough(res)).toBe(true);
378
269
  });
379
270
 
380
271
  it("passes /api/something through with a valid session cookie", async () => {
@@ -393,81 +284,20 @@ describe("middleware", () => {
393
284
  });
394
285
 
395
286
  // ---------------------------------------------------------------------------
396
- // Admin route authorization combined with session check
287
+ // All routes pass through (auth is handled client-side by useRequireAuth)
397
288
  // ---------------------------------------------------------------------------
398
- describe("admin route authorization", () => {
399
- it("redirects unauthenticated /admin to /login (not /marketplace)", async () => {
289
+ describe("all routes pass through with CSP", () => {
290
+ it("passes /admin through (auth handled client-side)", async () => {
400
291
  const req = buildRequest("/admin");
401
292
  const res = await middleware(req);
402
- expect(isRedirect(res)).toBe(true);
403
- expect(redirectPath(res)).toContain("/login");
404
- });
405
-
406
- it("redirects authenticated non-admin to /marketplace when accessing /admin", async () => {
407
- vi.stubGlobal(
408
- "fetch",
409
- vi.fn().mockResolvedValue({
410
- ok: true,
411
- json: async () => ({ user: { role: "user" } }),
412
- }),
413
- );
414
- const req = buildRequest("/admin", {
415
- cookies: { "better-auth.session_token": "valid-token" },
416
- });
417
- const res = await middleware(req);
418
- expect(isRedirect(res)).toBe(true);
419
- expect(redirectPath(res)).toBe("/marketplace");
420
- });
421
-
422
- it("allows platform_admin through to /admin", async () => {
423
- vi.stubGlobal(
424
- "fetch",
425
- vi.fn().mockResolvedValue({
426
- ok: true,
427
- json: async () => ({ user: { role: "platform_admin" } }),
428
- }),
429
- );
430
- const req = buildRequest("/admin", {
431
- cookies: { "better-auth.session_token": "valid-token" },
432
- });
433
- const res = await middleware(req);
434
293
  expect(isPassThrough(res)).toBe(true);
294
+ expect(res.headers.get("content-security-policy")).not.toBeNull();
435
295
  });
436
296
 
437
- it("redirects non-admin from /admin/users to /marketplace", async () => {
438
- vi.stubGlobal(
439
- "fetch",
440
- vi.fn().mockResolvedValue({
441
- ok: true,
442
- json: async () => ({ user: { role: "user" } }),
443
- }),
444
- );
445
- const req = buildRequest("/admin/users", {
446
- cookies: { "better-auth.session_token": "valid-token" },
447
- });
448
- const res = await middleware(req);
449
- expect(isRedirect(res)).toBe(true);
450
- expect(redirectPath(res)).toBe("/marketplace");
451
- });
452
-
453
- it("redirects to /marketplace when session fetch fails (fail closed)", async () => {
454
- vi.stubGlobal("fetch", vi.fn().mockRejectedValue(new Error("network error")));
455
- const req = buildRequest("/admin", {
456
- cookies: { "better-auth.session_token": "valid-token" },
457
- });
297
+ it("passes /admin/users through", async () => {
298
+ const req = buildRequest("/admin/users");
458
299
  const res = await middleware(req);
459
- expect(isRedirect(res)).toBe(true);
460
- expect(redirectPath(res)).toBe("/marketplace");
461
- });
462
-
463
- it("does not call fetch for non-admin routes", async () => {
464
- const fetchSpy = vi.fn().mockResolvedValue({ ok: false });
465
- vi.stubGlobal("fetch", fetchSpy);
466
- const req = buildRequest("/marketplace", {
467
- cookies: { "better-auth.session_token": "valid-token" },
468
- });
469
- await middleware(req);
470
- expect(fetchSpy).not.toHaveBeenCalled();
300
+ expect(isPassThrough(res)).toBe(true);
471
301
  });
472
302
  });
473
303
 
@@ -660,8 +490,8 @@ describe("CSP nonce in middleware", () => {
660
490
  expect(csp).toContain("default-src 'self'");
661
491
  expect(csp).toMatch(/style-src-elem 'self' 'unsafe-inline' 'nonce-[A-Za-z0-9+/=_-]+'/);
662
492
 
663
- expect(csp).toContain("img-src 'self' data: blob:");
664
- expect(csp).toContain("frame-src https://js.stripe.com");
493
+ expect(csp).toContain("img-src 'self' data: blob: https:");
494
+ expect(csp).toContain("frame-src 'self' https://js.stripe.com");
665
495
  expect(csp).toContain("frame-ancestors 'none'");
666
496
  expect(csp).toContain("object-src 'none'");
667
497
  });
@@ -677,23 +507,6 @@ describe("CSP nonce in middleware", () => {
677
507
  const res = await middleware(req);
678
508
  expect(res.headers.get("vary")).toBe("*");
679
509
  });
680
-
681
- it("does not overwrite stricter Cache-Control on admin routes", async () => {
682
- vi.stubGlobal(
683
- "fetch",
684
- vi
685
- .fn()
686
- .mockResolvedValue(
687
- new Response(JSON.stringify({ user: { role: "platform_admin" } }), { status: 200 }),
688
- ),
689
- );
690
- const req = buildRequest("/admin/dashboard", {
691
- cookies: { "better-auth.session_token": "valid-token" },
692
- });
693
- const res = await middleware(req);
694
- // Admin route sets stricter cache headers; withCsp should not overwrite
695
- expect(res.headers.get("cache-control")).toBe("no-store, no-cache, must-revalidate");
696
- });
697
510
  });
698
511
 
699
512
  // ---------------------------------------------------------------------------
@@ -11,9 +11,7 @@ describe("next.config headers", () => {
11
11
  expect(headers).toHaveLength(1);
12
12
  expect(headers[0].source).toBe("/:path*");
13
13
 
14
- const headerMap = new Map(
15
- headers[0].headers.map((h: { key: string; value: string }) => [h.key, h.value]),
16
- );
14
+ const headerMap = new Map(headers[0].headers.map((h: { key: string; value: string }) => [h.key, h.value]));
17
15
  expect(headerMap.get("X-Frame-Options")).toBe("DENY");
18
16
  expect(headerMap.get("X-Content-Type-Options")).toBe("nosniff");
19
17
  // CSP is now set exclusively by middleware (nonce-based), not next.config.ts