@nextsparkjs/core 0.1.0-beta.185 → 0.1.0-beta.187

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 (518) hide show
  1. package/dist/components/app/guards/DeveloperGuard.d.ts.map +1 -1
  2. package/dist/components/app/guards/DeveloperGuard.js +8 -4
  3. package/dist/components/app/guards/SuperAdminGuard.d.ts.map +1 -1
  4. package/dist/components/app/guards/SuperAdminGuard.js +8 -4
  5. package/dist/components/app/layouts/PublicFooter.d.ts.map +1 -1
  6. package/dist/components/app/layouts/PublicFooter.js +2 -2
  7. package/dist/components/app/layouts/PublicNavbar.d.ts.map +1 -1
  8. package/dist/components/app/layouts/PublicNavbar.js +2 -2
  9. package/dist/components/app/misc/ThemeToggle.d.ts.map +1 -1
  10. package/dist/components/app/misc/ThemeToggle.js +4 -1
  11. package/dist/components/auth/DevKeyring.d.ts.map +1 -1
  12. package/dist/components/auth/DevKeyring.js +10 -2
  13. package/dist/components/auth/SessionCookieRefresher.d.ts +21 -0
  14. package/dist/components/auth/SessionCookieRefresher.d.ts.map +1 -0
  15. package/dist/components/auth/SessionCookieRefresher.js +11 -0
  16. package/dist/components/auth/forms/LoginForm.d.ts.map +1 -1
  17. package/dist/components/auth/forms/LoginForm.js +300 -6
  18. package/dist/components/auth/index.d.ts +1 -0
  19. package/dist/components/auth/index.d.ts.map +1 -1
  20. package/dist/components/auth/index.js +2 -0
  21. package/dist/components/billing/InvoicesTable.d.ts.map +1 -1
  22. package/dist/components/billing/InvoicesTable.js +4 -3
  23. package/dist/components/dashboard/block-editor/dynamic-form.d.ts.map +1 -1
  24. package/dist/components/dashboard/layouts/AuthGuard.d.ts.map +1 -1
  25. package/dist/components/dashboard/layouts/AuthGuard.js +2 -2
  26. package/dist/components/dashboard/layouts/DashboardAuthLayout.d.ts.map +1 -1
  27. package/dist/components/dashboard/layouts/DashboardAuthLayout.js +2 -2
  28. package/dist/components/dashboard/layouts/DashboardAuthSkeleton.d.ts +17 -0
  29. package/dist/components/dashboard/layouts/DashboardAuthSkeleton.d.ts.map +1 -0
  30. package/dist/components/dashboard/layouts/DashboardAuthSkeleton.js +52 -0
  31. package/dist/components/dashboard/layouts/Sidebar.d.ts.map +1 -1
  32. package/dist/components/dashboard/layouts/Sidebar.js +2 -3
  33. package/dist/components/dashboard/layouts/TopNavbar.js +2 -2
  34. package/dist/components/dashboard/layouts/index.d.ts +1 -0
  35. package/dist/components/dashboard/layouts/index.d.ts.map +1 -1
  36. package/dist/components/dashboard/layouts/index.js +2 -0
  37. package/dist/components/devtools/api-explorer/ApiExplorer.js +4 -4
  38. package/dist/components/entities/EntityFieldRenderer.d.ts.map +1 -1
  39. package/dist/components/entities/EntityFieldRenderer.js +3 -0
  40. package/dist/components/entities/EntityTable.d.ts.map +1 -1
  41. package/dist/components/entities/EntityTable.js +2 -1
  42. package/dist/components/entities/wrappers/EntityFormWrapper.d.ts.map +1 -1
  43. package/dist/components/entities/wrappers/EntityFormWrapper.js +7 -2
  44. package/dist/components/patterns/pages/PatternEditPage.d.ts +5 -1
  45. package/dist/components/patterns/pages/PatternEditPage.d.ts.map +1 -1
  46. package/dist/components/patterns/pages/PatternEditPage.js +16 -5
  47. package/dist/components/patterns/pages/PatternsCreatePage.d.ts +2 -1
  48. package/dist/components/patterns/pages/PatternsCreatePage.d.ts.map +1 -1
  49. package/dist/components/patterns/pages/PatternsCreatePage.js +17 -5
  50. package/dist/components/ui/combobox.d.ts +8 -1
  51. package/dist/components/ui/combobox.d.ts.map +1 -1
  52. package/dist/components/ui/combobox.js +37 -44
  53. package/dist/components/ui/country-select.d.ts +3 -1
  54. package/dist/components/ui/country-select.d.ts.map +1 -1
  55. package/dist/components/ui/country-select.js +3 -1
  56. package/dist/components/ui/currency-select.d.ts +3 -1
  57. package/dist/components/ui/currency-select.d.ts.map +1 -1
  58. package/dist/components/ui/currency-select.js +3 -1
  59. package/dist/components/ui/sheet.d.ts +5 -0
  60. package/dist/components/ui/sheet.d.ts.map +1 -1
  61. package/dist/components/ui/sheet.js +46 -26
  62. package/dist/components/ui/timezone-select.d.ts +3 -1
  63. package/dist/components/ui/timezone-select.d.ts.map +1 -1
  64. package/dist/components/ui/timezone-select.js +3 -1
  65. package/dist/contexts/TeamContext.d.ts +6 -0
  66. package/dist/contexts/TeamContext.d.ts.map +1 -1
  67. package/dist/contexts/TeamContext.js +8 -1
  68. package/dist/emails/otp-verification.d.ts.map +1 -1
  69. package/dist/emails/reset-password.d.ts.map +1 -1
  70. package/dist/emails/team-invitation.d.ts.map +1 -1
  71. package/dist/emails/team-invitation.js +3 -2
  72. package/dist/emails/verify-email.d.ts.map +1 -1
  73. package/dist/hooks/index.d.ts +1 -0
  74. package/dist/hooks/index.d.ts.map +1 -1
  75. package/dist/hooks/index.js +1 -0
  76. package/dist/hooks/useAuth.d.ts +29 -0
  77. package/dist/hooks/useAuth.d.ts.map +1 -1
  78. package/dist/hooks/useAuth.js +24 -0
  79. package/dist/hooks/useEnsureUserMetadata.d.ts +22 -0
  80. package/dist/hooks/useEnsureUserMetadata.d.ts.map +1 -1
  81. package/dist/hooks/useEnsureUserMetadata.js +18 -5
  82. package/dist/hooks/useSessionCookieRefresh.d.ts +35 -0
  83. package/dist/hooks/useSessionCookieRefresh.d.ts.map +1 -0
  84. package/dist/hooks/useSessionCookieRefresh.js +49 -0
  85. package/dist/lib/api/api-error.d.ts +3 -1
  86. package/dist/lib/api/api-error.d.ts.map +1 -1
  87. package/dist/lib/api/api-error.js +4 -2
  88. package/dist/lib/api/auth/dual-auth.d.ts +38 -3
  89. package/dist/lib/api/auth/dual-auth.d.ts.map +1 -1
  90. package/dist/lib/api/auth/dual-auth.js +42 -7
  91. package/dist/lib/api/auth/permissions.d.ts +7 -2
  92. package/dist/lib/api/auth/permissions.d.ts.map +1 -1
  93. package/dist/lib/api/auth/permissions.js +2 -2
  94. package/dist/lib/api/auth/scope-policy.d.ts +71 -0
  95. package/dist/lib/api/auth/scope-policy.d.ts.map +1 -0
  96. package/dist/lib/api/auth/scope-policy.js +53 -0
  97. package/dist/lib/api/auth.d.ts +15 -0
  98. package/dist/lib/api/auth.d.ts.map +1 -1
  99. package/dist/lib/api/auth.js +17 -4
  100. package/dist/lib/api/entities.js +2 -1
  101. package/dist/lib/api/entity/audit-log.d.ts +35 -0
  102. package/dist/lib/api/entity/audit-log.d.ts.map +1 -0
  103. package/dist/lib/api/entity/audit-log.js +35 -0
  104. package/dist/lib/api/entity/generic-handler.d.ts.map +1 -1
  105. package/dist/lib/api/entity/generic-handler.js +333 -138
  106. package/dist/lib/api/entity-handler.js +2 -2
  107. package/dist/lib/api/helpers.d.ts +29 -4
  108. package/dist/lib/api/helpers.d.ts.map +1 -1
  109. package/dist/lib/api/helpers.js +30 -6
  110. package/dist/lib/api/keys.d.ts +16 -0
  111. package/dist/lib/api/keys.d.ts.map +1 -1
  112. package/dist/lib/api/keys.js +19 -1
  113. package/dist/lib/api/routes/patterns-usages.d.ts.map +1 -1
  114. package/dist/lib/api/routes/patterns-usages.js +3 -11
  115. package/dist/lib/auth/auth-methods.d.ts +52 -0
  116. package/dist/lib/auth/auth-methods.d.ts.map +1 -0
  117. package/dist/lib/auth/auth-methods.js +63 -0
  118. package/dist/lib/auth/registration-guard-plugin.d.ts.map +1 -1
  119. package/dist/lib/auth/registration-helpers.d.ts +9 -0
  120. package/dist/lib/auth/registration-helpers.d.ts.map +1 -1
  121. package/dist/lib/auth/registration-helpers.js +8 -2
  122. package/dist/lib/auth/security-notifications/device-fingerprint.d.ts +52 -0
  123. package/dist/lib/auth/security-notifications/device-fingerprint.d.ts.map +1 -0
  124. package/dist/lib/auth/security-notifications/device-fingerprint.js +42 -0
  125. package/dist/lib/auth/security-notifications/dispatcher.d.ts +37 -0
  126. package/dist/lib/auth/security-notifications/dispatcher.d.ts.map +1 -0
  127. package/dist/lib/auth/security-notifications/dispatcher.js +157 -0
  128. package/dist/lib/auth/security-notifications/index.d.ts +20 -0
  129. package/dist/lib/auth/security-notifications/index.d.ts.map +1 -0
  130. package/dist/lib/auth/security-notifications/index.js +22 -0
  131. package/dist/lib/auth/security-notifications/registry.d.ts +36 -0
  132. package/dist/lib/auth/security-notifications/registry.d.ts.map +1 -0
  133. package/dist/lib/auth/security-notifications/registry.js +26 -0
  134. package/dist/lib/auth/security-notifications/templates/email-changed.d.ts +12 -0
  135. package/dist/lib/auth/security-notifications/templates/email-changed.d.ts.map +1 -0
  136. package/dist/lib/auth/security-notifications/templates/email-changed.js +43 -0
  137. package/dist/lib/auth/security-notifications/templates/new-device-login.d.ts +7 -0
  138. package/dist/lib/auth/security-notifications/templates/new-device-login.d.ts.map +1 -0
  139. package/dist/lib/auth/security-notifications/templates/new-device-login.js +46 -0
  140. package/dist/lib/auth/security-notifications/templates/password-changed.d.ts +7 -0
  141. package/dist/lib/auth/security-notifications/templates/password-changed.d.ts.map +1 -0
  142. package/dist/lib/auth/security-notifications/templates/password-changed.js +35 -0
  143. package/dist/lib/auth/security-notifications/templates/shared.d.ts +36 -0
  144. package/dist/lib/auth/security-notifications/templates/shared.d.ts.map +1 -0
  145. package/dist/lib/auth/security-notifications/templates/shared.js +68 -0
  146. package/dist/lib/auth/security-notifications/types.d.ts +74 -0
  147. package/dist/lib/auth/security-notifications/types.d.ts.map +1 -0
  148. package/dist/lib/auth/session-config.d.ts +37 -0
  149. package/dist/lib/auth/session-config.d.ts.map +1 -0
  150. package/dist/lib/auth/session-config.js +59 -0
  151. package/dist/lib/auth/session-refresh.d.ts +46 -0
  152. package/dist/lib/auth/session-refresh.d.ts.map +1 -0
  153. package/dist/lib/auth/session-refresh.js +11 -0
  154. package/dist/lib/auth-client.d.ts +1639 -0
  155. package/dist/lib/auth-client.d.ts.map +1 -0
  156. package/dist/lib/auth-client.js +13 -1
  157. package/dist/lib/auth.d.ts.map +1 -1
  158. package/dist/lib/auth.js +45 -11
  159. package/dist/lib/billing/gateways/factory.d.ts +8 -3
  160. package/dist/lib/billing/gateways/factory.d.ts.map +1 -1
  161. package/dist/lib/billing/gateways/factory.js +37 -23
  162. package/dist/lib/billing/stripe-webhook.d.ts.map +1 -1
  163. package/dist/lib/billing/stripe-webhook.js +2 -1
  164. package/dist/lib/config/app.config.d.ts.map +1 -1
  165. package/dist/lib/config/app.config.js +71 -0
  166. package/dist/lib/config/config-sync.d.ts +1 -4
  167. package/dist/lib/config/config-sync.d.ts.map +1 -1
  168. package/dist/lib/config/config-sync.js +6 -2
  169. package/dist/lib/config/types.d.ts +130 -0
  170. package/dist/lib/config/types.d.ts.map +1 -1
  171. package/dist/lib/email/templates.d.ts +3 -3
  172. package/dist/lib/email/templates.d.ts.map +1 -1
  173. package/dist/lib/entities/api-generator.js +5 -5
  174. package/dist/lib/entities/entity-hooks.d.ts +9 -1
  175. package/dist/lib/entities/entity-hooks.d.ts.map +1 -1
  176. package/dist/lib/entities/entity-hooks.js +77 -4
  177. package/dist/lib/entities/schema-generator.d.ts.map +1 -1
  178. package/dist/lib/entities/schema-generator.js +20 -4
  179. package/dist/lib/entities/serialization.d.ts +2 -2
  180. package/dist/lib/entities/serialization.d.ts.map +1 -1
  181. package/dist/lib/entities/serialization.js +3 -2
  182. package/dist/lib/entities/types.d.ts +20 -0
  183. package/dist/lib/entities/types.d.ts.map +1 -1
  184. package/dist/lib/locale.d.ts.map +1 -1
  185. package/dist/lib/locale.js +4 -1
  186. package/dist/lib/mcp/audit.d.ts +8 -4
  187. package/dist/lib/mcp/audit.d.ts.map +1 -1
  188. package/dist/lib/plugins/hook-system.d.ts +13 -0
  189. package/dist/lib/plugins/hook-system.d.ts.map +1 -1
  190. package/dist/lib/plugins/hook-system.js +32 -2
  191. package/dist/lib/scheduled-actions/handlers/index.d.ts +1 -0
  192. package/dist/lib/scheduled-actions/handlers/index.d.ts.map +1 -1
  193. package/dist/lib/scheduled-actions/handlers/index.js +3 -1
  194. package/dist/lib/scheduled-actions/handlers/security-notification.d.ts +26 -0
  195. package/dist/lib/scheduled-actions/handlers/security-notification.d.ts.map +1 -0
  196. package/dist/lib/scheduled-actions/handlers/security-notification.js +54 -0
  197. package/dist/lib/selectors/core-selectors.d.ts +12 -0
  198. package/dist/lib/selectors/core-selectors.d.ts.map +1 -1
  199. package/dist/lib/selectors/domains/auth.selectors.d.ts +12 -0
  200. package/dist/lib/selectors/domains/auth.selectors.d.ts.map +1 -1
  201. package/dist/lib/selectors/domains/auth.selectors.js +14 -1
  202. package/dist/lib/selectors/selectors.d.ts +24 -0
  203. package/dist/lib/selectors/selectors.d.ts.map +1 -1
  204. package/dist/lib/services/api-docs.service.d.ts +5 -1
  205. package/dist/lib/services/api-docs.service.d.ts.map +1 -1
  206. package/dist/lib/services/api-presets.service.d.ts +6 -1
  207. package/dist/lib/services/api-presets.service.d.ts.map +1 -1
  208. package/dist/lib/services/feature.service.d.ts +58 -4
  209. package/dist/lib/services/feature.service.d.ts.map +1 -1
  210. package/dist/lib/services/generic-entity.service.d.ts.map +1 -1
  211. package/dist/lib/services/generic-entity.service.js +16 -32
  212. package/dist/lib/services/membership.service.d.ts +10 -2
  213. package/dist/lib/services/membership.service.d.ts.map +1 -1
  214. package/dist/lib/services/membership.service.js +37 -16
  215. package/dist/lib/services/permission.service.d.ts +10 -4
  216. package/dist/lib/services/permission.service.d.ts.map +1 -1
  217. package/dist/lib/services/plugin.service.d.ts +1 -1
  218. package/dist/lib/services/plugin.service.d.ts.map +1 -1
  219. package/dist/lib/services/subscription.service.d.ts.map +1 -1
  220. package/dist/lib/services/subscription.service.js +2 -1
  221. package/dist/lib/services/team-member.service.d.ts +15 -3
  222. package/dist/lib/services/team-member.service.d.ts.map +1 -1
  223. package/dist/lib/services/team-member.service.js +27 -6
  224. package/dist/lib/services/team.service.d.ts.map +1 -1
  225. package/dist/lib/teams/permissions.d.ts +3 -1
  226. package/dist/lib/teams/permissions.d.ts.map +1 -1
  227. package/dist/lib/teams/permissions.js +6 -0
  228. package/dist/lib/teams/schema.d.ts.map +1 -1
  229. package/dist/lib/teams/schema.js +3 -0
  230. package/dist/lib/template-resolver.d.ts +3 -3
  231. package/dist/lib/theme/forced-theme.d.ts +34 -0
  232. package/dist/lib/theme/forced-theme.d.ts.map +1 -0
  233. package/dist/lib/theme/forced-theme.js +27 -0
  234. package/dist/lib/theme/get-default-theme-mode.d.ts +3 -0
  235. package/dist/lib/theme/get-default-theme-mode.d.ts.map +1 -1
  236. package/dist/lib/theme/get-default-theme-mode.js +5 -3
  237. package/dist/lib/utils/cors.d.ts +13 -0
  238. package/dist/lib/utils/cors.d.ts.map +1 -1
  239. package/dist/lib/utils/cors.js +20 -0
  240. package/dist/messages/de/auth.json +25 -2
  241. package/dist/messages/de/index.d.ts +83 -0
  242. package/dist/messages/de/index.d.ts.map +1 -1
  243. package/dist/messages/de/index.js +2 -0
  244. package/dist/messages/de/index.ts +2 -0
  245. package/dist/messages/de/navigation.json +3 -1
  246. package/dist/messages/en/auth.json +25 -2
  247. package/dist/messages/en/index.d.ts +83 -0
  248. package/dist/messages/en/index.d.ts.map +1 -1
  249. package/dist/messages/en/index.js +2 -0
  250. package/dist/messages/en/index.ts +2 -0
  251. package/dist/messages/en/navigation.json +3 -1
  252. package/dist/messages/es/auth.json +25 -2
  253. package/dist/messages/es/index.d.ts +83 -0
  254. package/dist/messages/es/index.d.ts.map +1 -1
  255. package/dist/messages/es/index.js +2 -0
  256. package/dist/messages/es/index.ts +2 -0
  257. package/dist/messages/es/navigation.json +3 -1
  258. package/dist/messages/fr/auth.json +25 -2
  259. package/dist/messages/fr/index.d.ts +83 -0
  260. package/dist/messages/fr/index.d.ts.map +1 -1
  261. package/dist/messages/fr/index.js +2 -0
  262. package/dist/messages/fr/index.ts +2 -0
  263. package/dist/messages/fr/navigation.json +3 -1
  264. package/dist/messages/it/auth.json +25 -2
  265. package/dist/messages/it/index.d.ts +83 -0
  266. package/dist/messages/it/index.d.ts.map +1 -1
  267. package/dist/messages/it/index.js +2 -0
  268. package/dist/messages/it/index.ts +2 -0
  269. package/dist/messages/it/navigation.json +3 -1
  270. package/dist/messages/pt/auth.json +25 -2
  271. package/dist/messages/pt/index.d.ts +83 -0
  272. package/dist/messages/pt/index.d.ts.map +1 -1
  273. package/dist/messages/pt/index.js +2 -0
  274. package/dist/messages/pt/index.ts +2 -0
  275. package/dist/messages/pt/navigation.json +3 -1
  276. package/dist/migrations/005_api_audit_log.sql +2 -1
  277. package/dist/migrations/025_api_audit_log_nullable_api_key.sql +12 -0
  278. package/dist/migrations/026_login_events.sql +91 -0
  279. package/dist/migrations/027_rls_bypass_developer_and_system_admin_team.sql +197 -0
  280. package/dist/nextspark-registries.d.ts +580 -236
  281. package/dist/providers/DashboardProviders.d.ts +12 -3
  282. package/dist/providers/DashboardProviders.d.ts.map +1 -1
  283. package/dist/providers/DashboardProviders.js +6 -2
  284. package/dist/providers/theme-provider.d.ts +19 -2
  285. package/dist/providers/theme-provider.d.ts.map +1 -1
  286. package/dist/providers/theme-provider.js +6 -2
  287. package/dist/templates/app/(auth)/signup/page.tsx +8 -0
  288. package/dist/templates/app/(public)/[...slug]/page.tsx +9 -14
  289. package/dist/templates/app/api/auth/[...all]/route.ts +23 -1
  290. package/dist/templates/app/api/health/route.ts +4 -2
  291. package/dist/templates/app/api/internal/user-metadata/route.ts +5 -3
  292. package/dist/templates/app/api/mcp/route.ts +4 -1
  293. package/dist/templates/app/api/superadmin/subscriptions/route.ts +2 -1
  294. package/dist/templates/app/api/superadmin/teams/[teamId]/route.ts +4 -3
  295. package/dist/templates/app/api/superadmin/users/[userId]/route.ts +10 -3
  296. package/dist/templates/app/api/v1/api-keys/[id]/route.ts +50 -71
  297. package/dist/templates/app/api/v1/api-keys/route.ts +21 -43
  298. package/dist/templates/app/api/v1/auth/signup-with-invite/route.ts +14 -14
  299. package/dist/templates/app/api/v1/billing/cancel/route.ts +7 -6
  300. package/dist/templates/app/api/v1/billing/change-plan/route.ts +5 -4
  301. package/dist/templates/app/api/v1/billing/check-action/route.ts +6 -4
  302. package/dist/templates/app/api/v1/billing/checkout/route.ts +7 -5
  303. package/dist/templates/app/api/v1/billing/plans/route.ts +8 -12
  304. package/dist/templates/app/api/v1/billing/portal/route.ts +7 -5
  305. package/dist/templates/app/api/v1/billing/webhooks/polar/route.ts +1 -1
  306. package/dist/templates/app/api/v1/devtools/blocks/route.ts +5 -4
  307. package/dist/templates/app/api/v1/devtools/features/route.ts +5 -4
  308. package/dist/templates/app/api/v1/devtools/flows/route.ts +5 -4
  309. package/dist/templates/app/api/v1/devtools/scheduled-actions/route.ts +9 -7
  310. package/dist/templates/app/api/v1/devtools/scheduled-actions/run/route.ts +5 -4
  311. package/dist/templates/app/api/v1/devtools/testing/route.ts +5 -4
  312. package/dist/templates/app/api/v1/media/[id]/route.ts +13 -25
  313. package/dist/templates/app/api/v1/media/[id]/tags/route.ts +17 -25
  314. package/dist/templates/app/api/v1/media/check-duplicates/route.ts +5 -7
  315. package/dist/templates/app/api/v1/media/route.ts +5 -9
  316. package/dist/templates/app/api/v1/media/upload/route.ts +9 -21
  317. package/dist/templates/app/api/v1/media-tags/route.ts +9 -13
  318. package/dist/templates/app/api/v1/patterns/[id]/usages/route.ts +11 -19
  319. package/dist/templates/app/api/v1/post-categories/[id]/route.ts +9 -7
  320. package/dist/templates/app/api/v1/post-categories/route.ts +5 -4
  321. package/dist/templates/app/api/v1/team-invitations/[token]/accept/route.ts +16 -18
  322. package/dist/templates/app/api/v1/team-invitations/[token]/decline/route.ts +15 -17
  323. package/dist/templates/app/api/v1/team-invitations/[token]/route.ts +8 -8
  324. package/dist/templates/app/api/v1/team-invitations/route.ts +9 -11
  325. package/dist/templates/app/api/v1/teams/[teamId]/invitations/route.ts +41 -32
  326. package/dist/templates/app/api/v1/teams/[teamId]/invoices/[invoiceNumber]/route.ts +15 -16
  327. package/dist/templates/app/api/v1/teams/[teamId]/invoices/route.ts +14 -15
  328. package/dist/templates/app/api/v1/teams/[teamId]/members/[memberId]/route.ts +32 -36
  329. package/dist/templates/app/api/v1/teams/[teamId]/members/route.ts +33 -33
  330. package/dist/templates/app/api/v1/teams/[teamId]/route.ts +35 -41
  331. package/dist/templates/app/api/v1/teams/[teamId]/subscription/route.ts +5 -3
  332. package/dist/templates/app/api/v1/teams/[teamId]/usage/[limitSlug]/route.ts +10 -6
  333. package/dist/templates/app/api/v1/teams/route.ts +33 -27
  334. package/dist/templates/app/api/v1/teams/switch/route.ts +11 -13
  335. package/dist/templates/app/api/v1/theme/[...path]/route.ts +23 -2
  336. package/dist/templates/app/api/v1/users/[id]/meta/[key]/route.ts +32 -77
  337. package/dist/templates/app/api/v1/users/[id]/route.ts +35 -68
  338. package/dist/templates/app/api/v1/users/route.ts +21 -25
  339. package/dist/templates/app/dashboard/layout.tsx +2 -6
  340. package/dist/templates/app/layout.ppr.tsx +15 -15
  341. package/dist/templates/app/layout.tsx +46 -20
  342. package/dist/templates/contents/themes/starter/config/app.config.ts +19 -0
  343. package/dist/templates/contents/themes/starter/config/theme.config.ts +6 -0
  344. package/dist/templates/contents/themes/starter/styles/globals.css +6 -2
  345. package/dist/templates/proxy.ts +46 -10
  346. package/dist/templates/tsconfig.json +4 -0
  347. package/dist/types/theme.d.ts +9 -0
  348. package/dist/types/theme.d.ts.map +1 -1
  349. package/migrations/005_api_audit_log.sql +2 -1
  350. package/migrations/025_api_audit_log_nullable_api_key.sql +12 -0
  351. package/migrations/026_login_events.sql +91 -0
  352. package/migrations/027_rls_bypass_developer_and_system_admin_team.sql +197 -0
  353. package/package.json +14 -11
  354. package/scripts/build/registry/__tests__/scope-registry.test.mjs +18 -0
  355. package/scripts/build/registry/generators/scope-registry.mjs +9 -2
  356. package/templates/app/(auth)/signup/page.tsx +8 -0
  357. package/templates/app/(public)/[...slug]/page.tsx +9 -14
  358. package/templates/app/api/auth/[...all]/route.ts +23 -1
  359. package/templates/app/api/health/route.ts +4 -2
  360. package/templates/app/api/internal/user-metadata/route.ts +5 -3
  361. package/templates/app/api/mcp/route.ts +4 -1
  362. package/templates/app/api/superadmin/subscriptions/route.ts +2 -1
  363. package/templates/app/api/superadmin/teams/[teamId]/route.ts +4 -3
  364. package/templates/app/api/superadmin/users/[userId]/route.ts +10 -3
  365. package/templates/app/api/v1/api-keys/[id]/route.ts +50 -71
  366. package/templates/app/api/v1/api-keys/route.ts +21 -43
  367. package/templates/app/api/v1/auth/signup-with-invite/route.ts +14 -14
  368. package/templates/app/api/v1/billing/cancel/route.ts +7 -6
  369. package/templates/app/api/v1/billing/change-plan/route.ts +5 -4
  370. package/templates/app/api/v1/billing/check-action/route.ts +6 -4
  371. package/templates/app/api/v1/billing/checkout/route.ts +7 -5
  372. package/templates/app/api/v1/billing/plans/route.ts +8 -12
  373. package/templates/app/api/v1/billing/portal/route.ts +7 -5
  374. package/templates/app/api/v1/billing/webhooks/polar/route.ts +1 -1
  375. package/templates/app/api/v1/devtools/blocks/route.ts +5 -4
  376. package/templates/app/api/v1/devtools/features/route.ts +5 -4
  377. package/templates/app/api/v1/devtools/flows/route.ts +5 -4
  378. package/templates/app/api/v1/devtools/scheduled-actions/route.ts +9 -7
  379. package/templates/app/api/v1/devtools/scheduled-actions/run/route.ts +5 -4
  380. package/templates/app/api/v1/devtools/testing/route.ts +5 -4
  381. package/templates/app/api/v1/media/[id]/route.ts +13 -25
  382. package/templates/app/api/v1/media/[id]/tags/route.ts +17 -25
  383. package/templates/app/api/v1/media/check-duplicates/route.ts +5 -7
  384. package/templates/app/api/v1/media/route.ts +5 -9
  385. package/templates/app/api/v1/media/upload/route.ts +9 -21
  386. package/templates/app/api/v1/media-tags/route.ts +9 -13
  387. package/templates/app/api/v1/patterns/[id]/usages/route.ts +11 -19
  388. package/templates/app/api/v1/post-categories/[id]/route.ts +9 -7
  389. package/templates/app/api/v1/post-categories/route.ts +5 -4
  390. package/templates/app/api/v1/team-invitations/[token]/accept/route.ts +16 -18
  391. package/templates/app/api/v1/team-invitations/[token]/decline/route.ts +15 -17
  392. package/templates/app/api/v1/team-invitations/[token]/route.ts +8 -8
  393. package/templates/app/api/v1/team-invitations/route.ts +9 -11
  394. package/templates/app/api/v1/teams/[teamId]/invitations/route.ts +41 -32
  395. package/templates/app/api/v1/teams/[teamId]/invoices/[invoiceNumber]/route.ts +15 -16
  396. package/templates/app/api/v1/teams/[teamId]/invoices/route.ts +14 -15
  397. package/templates/app/api/v1/teams/[teamId]/members/[memberId]/route.ts +32 -36
  398. package/templates/app/api/v1/teams/[teamId]/members/route.ts +33 -33
  399. package/templates/app/api/v1/teams/[teamId]/route.ts +35 -41
  400. package/templates/app/api/v1/teams/[teamId]/subscription/route.ts +5 -3
  401. package/templates/app/api/v1/teams/[teamId]/usage/[limitSlug]/route.ts +10 -6
  402. package/templates/app/api/v1/teams/route.ts +33 -27
  403. package/templates/app/api/v1/teams/switch/route.ts +11 -13
  404. package/templates/app/api/v1/theme/[...path]/route.ts +23 -2
  405. package/templates/app/api/v1/users/[id]/meta/[key]/route.ts +32 -77
  406. package/templates/app/api/v1/users/[id]/route.ts +35 -68
  407. package/templates/app/api/v1/users/route.ts +21 -25
  408. package/templates/app/dashboard/layout.tsx +2 -6
  409. package/templates/app/layout.ppr.tsx +15 -15
  410. package/templates/app/layout.tsx +46 -20
  411. package/templates/contents/themes/starter/config/app.config.ts +19 -0
  412. package/templates/contents/themes/starter/config/theme.config.ts +6 -0
  413. package/templates/contents/themes/starter/styles/globals.css +6 -2
  414. package/templates/proxy.ts +46 -10
  415. package/templates/tsconfig.json +4 -0
  416. package/tests/jest/__mocks__/@nextsparkjs/registries/permissions-registry.ts +23 -0
  417. package/tests/jest/__mocks__/next-server.js +24 -0
  418. package/dist/components/teams/TeamSwitchModal.d.js +0 -0
  419. package/dist/components/ui/accordion.d.js +0 -0
  420. package/dist/components/ui/avatar.d.js +0 -0
  421. package/dist/components/ui/badge.d.js +0 -0
  422. package/dist/components/ui/button.d.js +0 -0
  423. package/dist/components/ui/card.d.js +0 -0
  424. package/dist/components/ui/dialog.d.js +0 -0
  425. package/dist/components/ui/dropdown-menu.d.js +0 -0
  426. package/dist/components/ui/input.d.js +0 -0
  427. package/dist/components/ui/progress.d.js +0 -0
  428. package/dist/components/ui/scroll-area.d.js +0 -0
  429. package/dist/components/ui/select.d.js +0 -0
  430. package/dist/components/ui/separator.d.js +0 -0
  431. package/dist/components/ui/table.d.js +0 -0
  432. package/dist/components/ui/textarea.d.js +0 -0
  433. package/dist/contexts/TeamContext.d.js +0 -0
  434. package/dist/hooks/useAuth.d.js +0 -0
  435. package/dist/hooks/useLastAuthMethod.d.js +0 -0
  436. package/dist/hooks/useOrigin.d.js +0 -0
  437. package/dist/lib/api/auth/dual-auth.d.js +0 -0
  438. package/dist/lib/api/auth.d.js +0 -0
  439. package/dist/lib/api/cache.d.js +0 -0
  440. package/dist/lib/api/keys.d.js +0 -0
  441. package/dist/lib/auth-client.d.js +0 -0
  442. package/dist/lib/auth-context.d.js +0 -0
  443. package/dist/lib/auth.d.js +0 -0
  444. package/dist/lib/billing/config-types.d.js +0 -0
  445. package/dist/lib/billing/enforcement.d.js +0 -0
  446. package/dist/lib/billing/helpers.d.js +0 -0
  447. package/dist/lib/billing/jobs.d.js +0 -0
  448. package/dist/lib/billing/types.d.js +0 -0
  449. package/dist/lib/blocks/loader.d.js +0 -0
  450. package/dist/lib/config/app.config.d.js +0 -0
  451. package/dist/lib/config/config-loader.d.js +0 -0
  452. package/dist/lib/config/config-sync.d.js +0 -0
  453. package/dist/lib/config/config-types.d.js +0 -0
  454. package/dist/lib/config/config-utils.d.js +0 -0
  455. package/dist/lib/config/dashboard-types.d.js +0 -0
  456. package/dist/lib/config/dashboard.config.d.js +0 -0
  457. package/dist/lib/config/icon-map.d.js +0 -0
  458. package/dist/lib/config/index.d.js +0 -7
  459. package/dist/lib/config/nextspark-types.d.js +0 -0
  460. package/dist/lib/config/roles-merge.d.js +0 -0
  461. package/dist/lib/config/types.d.js +0 -0
  462. package/dist/lib/db.d.js +0 -0
  463. package/dist/lib/email/factory.d.js +0 -0
  464. package/dist/lib/email/index.d.js +0 -3
  465. package/dist/lib/email/providers/console.d.js +0 -0
  466. package/dist/lib/email/providers/resend.d.js +0 -0
  467. package/dist/lib/email/templates.d.js +0 -0
  468. package/dist/lib/email/types.d.js +0 -0
  469. package/dist/lib/entities/types.d.js +0 -0
  470. package/dist/lib/permissions/system.d.js +0 -0
  471. package/dist/lib/permissions/types.d.js +0 -0
  472. package/dist/lib/plugins/hook-system.d.js +0 -0
  473. package/dist/lib/scheduled-actions/cleanup.d.js +0 -0
  474. package/dist/lib/scheduled-actions/index.d.js +0 -19
  475. package/dist/lib/scheduled-actions/initializer.d.js +0 -1
  476. package/dist/lib/scheduled-actions/processor.d.js +0 -0
  477. package/dist/lib/scheduled-actions/registry.d.js +0 -0
  478. package/dist/lib/scheduled-actions/scheduler.d.js +0 -0
  479. package/dist/lib/scheduled-actions/types.d.js +0 -0
  480. package/dist/lib/services/api-routes.service.d.js +0 -0
  481. package/dist/lib/services/entity-type.service.d.js +0 -0
  482. package/dist/lib/services/index.d.js +0 -40
  483. package/dist/lib/services/invoice.service.d.js +0 -0
  484. package/dist/lib/services/membership.service.d.js +0 -0
  485. package/dist/lib/services/meta.service.d.js +0 -0
  486. package/dist/lib/services/middleware.service.d.js +0 -0
  487. package/dist/lib/services/namespace.service.d.js +0 -0
  488. package/dist/lib/services/permission.service.d.js +0 -0
  489. package/dist/lib/services/plan.service.d.js +0 -0
  490. package/dist/lib/services/plugin.service.d.js +0 -0
  491. package/dist/lib/services/route-handler.service.d.js +0 -0
  492. package/dist/lib/services/scope.service.d.js +0 -0
  493. package/dist/lib/services/subscription.service.d.js +0 -0
  494. package/dist/lib/services/team-member.service.d.js +0 -0
  495. package/dist/lib/services/team.service.d.js +0 -0
  496. package/dist/lib/services/theme.service.d.js +0 -0
  497. package/dist/lib/services/usage.service.d.js +0 -0
  498. package/dist/lib/services/user-flags.service.d.js +0 -0
  499. package/dist/lib/services/user.service.d.js +0 -0
  500. package/dist/lib/teams/helpers.d.js +0 -0
  501. package/dist/lib/teams/permissions.d.js +0 -0
  502. package/dist/lib/teams/types.d.js +0 -0
  503. package/dist/lib/utils/config-merge.d.js +0 -0
  504. package/dist/lib/utils/file-logger.d.js +0 -0
  505. package/dist/lib/utils.d.js +0 -0
  506. package/dist/styles/classes.json +0 -1093
  507. package/dist/styles/ui.css +0 -2
  508. package/dist/types/auth.d.js +0 -0
  509. package/dist/types/blocks.d.js +0 -0
  510. package/dist/types/meta.types.d.js +0 -0
  511. package/dist/types/plugin.d.js +0 -0
  512. package/dist/types/testing.types.d.js +0 -0
  513. package/dist/types/theme.d.js +0 -0
  514. package/dist/types/user.types.d.js +0 -0
  515. package/scripts/build/build-ui-css.mjs +0 -351
  516. /package/dist/{components/app/layouts/PublicFooter.d.js → lib/auth/security-notifications/types.js} +0 -0
  517. /package/dist/{components/app/layouts/PublicNavbar.d.js → nextspark-registries.d.js} +0 -0
  518. /package/dist/{components/app/misc/ThemeToggle.d.js → types/use-intl-react-shim.d.js} +0 -0
@@ -1,5 +1,5 @@
1
1
  import { NextRequest, NextResponse } from 'next/server'
2
- import { authenticateRequest } from '@nextsparkjs/core/lib/api/auth/dual-auth'
2
+ import { authenticateRequest, createAuthFailureResponse } from '@nextsparkjs/core/lib/api/auth/dual-auth'
3
3
  import { query as dbQuery } from '@nextsparkjs/core/lib/db'
4
4
  import { z } from 'zod'
5
5
  import { withRateLimitTier } from '@nextsparkjs/core/lib/api/rate-limit'
@@ -52,10 +52,11 @@ export const PUT = withRateLimitTier(async (
52
52
  { params }: { params: Promise<{ id: string }> }
53
53
  ): Promise<NextResponse> => {
54
54
  try {
55
- // Dual authentication: API key or session
56
- const authResult = await authenticateRequest(request)
55
+ // Dual authentication: API key or session; the API-key scope is declared
56
+ // at the entry point, which fails closed for keys that lack it (#93).
57
+ const authResult = await authenticateRequest(request, { requiredScope: 'posts:write' })
57
58
  if (!authResult.success || !authResult.user) {
58
- return NextResponse.json({ success: false, error: 'Unauthorized' }, { status: 401 })
59
+ return createAuthFailureResponse(authResult)
59
60
  }
60
61
 
61
62
  const body = await request.json()
@@ -192,10 +193,11 @@ export const DELETE = withRateLimitTier(async (
192
193
  { params }: { params: Promise<{ id: string }> }
193
194
  ): Promise<NextResponse> => {
194
195
  try {
195
- // Dual authentication: API key or session
196
- const authResult = await authenticateRequest(request)
196
+ // Dual authentication: API key or session; the API-key scope is declared
197
+ // at the entry point, which fails closed for keys that lack it (#93).
198
+ const authResult = await authenticateRequest(request, { requiredScope: 'posts:delete' })
197
199
  if (!authResult.success || !authResult.user) {
198
- return NextResponse.json({ success: false, error: 'Unauthorized' }, { status: 401 })
200
+ return createAuthFailureResponse(authResult)
199
201
  }
200
202
 
201
203
  const { id } = await params
@@ -1,5 +1,5 @@
1
1
  import { NextRequest, NextResponse } from 'next/server'
2
- import { authenticateRequest } from '@nextsparkjs/core/lib/api/auth/dual-auth'
2
+ import { authenticateRequest, createAuthFailureResponse } from '@nextsparkjs/core/lib/api/auth/dual-auth'
3
3
  import { query as dbQuery } from '@nextsparkjs/core/lib/db'
4
4
  import { z } from 'zod'
5
5
  import { withRateLimitTier } from '@nextsparkjs/core/lib/api/rate-limit'
@@ -50,10 +50,11 @@ export const GET = withRateLimitTier(async () => {
50
50
  // POST /api/v1/post-categories - Create category
51
51
  export const POST = withRateLimitTier(async (request: NextRequest) => {
52
52
  try {
53
- // Dual authentication: API key or session
54
- const authResult = await authenticateRequest(request)
53
+ // Dual authentication: API key or session; the API-key scope is declared
54
+ // at the entry point, which fails closed for keys that lack it (#93).
55
+ const authResult = await authenticateRequest(request, { requiredScope: 'posts:write' })
55
56
  if (!authResult.success || !authResult.user) {
56
- return NextResponse.json({ success: false, error: 'Unauthorized' }, { status: 401 })
57
+ return createAuthFailureResponse(authResult)
57
58
  }
58
59
 
59
60
  const body = await request.json()
@@ -7,28 +7,26 @@ import {
7
7
  handleCorsPreflightRequest,
8
8
  addCorsHeaders,
9
9
  } from '@nextsparkjs/core/lib/api/helpers'
10
- import { authenticateRequest } from '@nextsparkjs/core/lib/api/auth/dual-auth'
10
+ import { authenticateRequest, createAuthFailureResponse } from '@nextsparkjs/core/lib/api/auth/dual-auth'
11
11
  import { checkRateLimit, withRateLimitTier } from '@nextsparkjs/core/lib/api/rate-limit'
12
12
  import { RATE_LIMITS } from '@nextsparkjs/core/lib/api/keys'
13
13
  import type { TeamInvitation, TeamMember } from '@nextsparkjs/core/lib/teams/types'
14
14
 
15
15
  // Handle CORS preflight
16
- export async function OPTIONS() {
17
- return handleCorsPreflightRequest()
16
+ export async function OPTIONS(request: NextRequest) {
17
+ return handleCorsPreflightRequest(request)
18
18
  }
19
19
 
20
20
  // POST /api/v1/team-invitations/:token/accept - Accept invitation and become member
21
21
  export const POST = withRateLimitTier(withApiLogging(
22
22
  async (req: NextRequest, { params }: { params: Promise<{ token: string }> }): Promise<NextResponse> => {
23
23
  try {
24
- // Authenticate using dual auth
25
- const authResult = await authenticateRequest(req)
24
+ // Authenticate using dual auth; the API-key scope is declared at the entry
25
+ // point, which fails closed for keys that lack it (#93).
26
+ const authResult = await authenticateRequest(req, { requiredScope: 'teams:write' })
26
27
 
27
28
  if (!authResult.success) {
28
- return NextResponse.json(
29
- { success: false, error: 'Authentication required', code: 'AUTHENTICATION_FAILED' },
30
- { status: 401 }
31
- )
29
+ return createAuthFailureResponse(authResult)
32
30
  }
33
31
 
34
32
  if (authResult.rateLimitResponse) {
@@ -47,7 +45,7 @@ export const POST = withRateLimitTier(withApiLogging(
47
45
  { retryAfter: Math.ceil((rateLimit.resetTime - Date.now()) / 1000) },
48
46
  'RATE_LIMIT_EXCEEDED'
49
47
  )
50
- return addCorsHeaders(response)
48
+ return addCorsHeaders(response, req)
51
49
  }
52
50
 
53
51
  const { token } = await params
@@ -55,7 +53,7 @@ export const POST = withRateLimitTier(withApiLogging(
55
53
  // Validate that token is not empty
56
54
  if (!token || token.trim() === '') {
57
55
  const response = createApiError('Invitation token is required', 400, null, 'MISSING_TOKEN')
58
- return addCorsHeaders(response)
56
+ return addCorsHeaders(response, req)
59
57
  }
60
58
 
61
59
  // Get invitation by token
@@ -67,7 +65,7 @@ export const POST = withRateLimitTier(withApiLogging(
67
65
 
68
66
  if (!invitation) {
69
67
  const response = createApiError('Invitation not found', 404, null, 'INVITATION_NOT_FOUND')
70
- return addCorsHeaders(response)
68
+ return addCorsHeaders(response, req)
71
69
  }
72
70
 
73
71
  // Verify invitation is for the current user's email
@@ -78,7 +76,7 @@ export const POST = withRateLimitTier(withApiLogging(
78
76
  null,
79
77
  'EMAIL_MISMATCH'
80
78
  )
81
- return addCorsHeaders(response)
79
+ return addCorsHeaders(response, req)
82
80
  }
83
81
 
84
82
  // Check if invitation is pending
@@ -89,7 +87,7 @@ export const POST = withRateLimitTier(withApiLogging(
89
87
  null,
90
88
  'INVITATION_NOT_PENDING'
91
89
  )
92
- return addCorsHeaders(response)
90
+ return addCorsHeaders(response, req)
93
91
  }
94
92
 
95
93
  // Check if invitation has expired
@@ -103,7 +101,7 @@ export const POST = withRateLimitTier(withApiLogging(
103
101
  )
104
102
 
105
103
  const response = createApiError('Invitation has expired', 410, null, 'INVITATION_EXPIRED')
106
- return addCorsHeaders(response)
104
+ return addCorsHeaders(response, req)
107
105
  }
108
106
 
109
107
  // Check if user is already a member
@@ -115,7 +113,7 @@ export const POST = withRateLimitTier(withApiLogging(
115
113
 
116
114
  if (existingMember) {
117
115
  const response = createApiError('You are already a member of this team', 409, null, 'ALREADY_MEMBER')
118
- return addCorsHeaders(response)
116
+ return addCorsHeaders(response, req)
119
117
  }
120
118
 
121
119
  // Use transaction to ensure atomicity
@@ -165,7 +163,7 @@ export const POST = withRateLimitTier(withApiLogging(
165
163
  )
166
164
 
167
165
  const response = createApiResponse(memberWithUser, { created: true }, 201)
168
- return addCorsHeaders(response)
166
+ return addCorsHeaders(response, req)
169
167
  } catch (error) {
170
168
  await tx.rollback()
171
169
  throw error
@@ -173,7 +171,7 @@ export const POST = withRateLimitTier(withApiLogging(
173
171
  } catch (error) {
174
172
  console.error('Error accepting invitation:', error)
175
173
  const response = createApiError('Internal server error', 500)
176
- return addCorsHeaders(response)
174
+ return addCorsHeaders(response, req)
177
175
  }
178
176
  }
179
177
  ), 'write');
@@ -7,28 +7,26 @@ import {
7
7
  handleCorsPreflightRequest,
8
8
  addCorsHeaders,
9
9
  } from '@nextsparkjs/core/lib/api/helpers'
10
- import { authenticateRequest } from '@nextsparkjs/core/lib/api/auth/dual-auth'
10
+ import { authenticateRequest, createAuthFailureResponse } from '@nextsparkjs/core/lib/api/auth/dual-auth'
11
11
  import { checkRateLimit, withRateLimitTier } from '@nextsparkjs/core/lib/api/rate-limit'
12
12
  import { RATE_LIMITS } from '@nextsparkjs/core/lib/api/keys'
13
13
  import type { TeamInvitation } from '@nextsparkjs/core/lib/teams/types'
14
14
 
15
15
  // Handle CORS preflight
16
- export async function OPTIONS() {
17
- return handleCorsPreflightRequest()
16
+ export async function OPTIONS(request: NextRequest) {
17
+ return handleCorsPreflightRequest(request)
18
18
  }
19
19
 
20
20
  // POST /api/v1/team-invitations/:token/decline - Decline invitation
21
21
  export const POST = withRateLimitTier(withApiLogging(
22
22
  async (req: NextRequest, { params }: { params: Promise<{ token: string }> }): Promise<NextResponse> => {
23
23
  try {
24
- // Authenticate using dual auth
25
- const authResult = await authenticateRequest(req)
24
+ // Authenticate using dual auth; the API-key scope is declared at the entry
25
+ // point, which fails closed for keys that lack it (#93).
26
+ const authResult = await authenticateRequest(req, { requiredScope: 'teams:write' })
26
27
 
27
28
  if (!authResult.success) {
28
- return NextResponse.json(
29
- { success: false, error: 'Authentication required', code: 'AUTHENTICATION_FAILED' },
30
- { status: 401 }
31
- )
29
+ return createAuthFailureResponse(authResult)
32
30
  }
33
31
 
34
32
  if (authResult.rateLimitResponse) {
@@ -47,7 +45,7 @@ export const POST = withRateLimitTier(withApiLogging(
47
45
  { retryAfter: Math.ceil((rateLimit.resetTime - Date.now()) / 1000) },
48
46
  'RATE_LIMIT_EXCEEDED'
49
47
  )
50
- return addCorsHeaders(response)
48
+ return addCorsHeaders(response, req)
51
49
  }
52
50
 
53
51
  const { token } = await params
@@ -55,7 +53,7 @@ export const POST = withRateLimitTier(withApiLogging(
55
53
  // Validate that token is not empty
56
54
  if (!token || token.trim() === '') {
57
55
  const response = createApiError('Invitation token is required', 400, null, 'MISSING_TOKEN')
58
- return addCorsHeaders(response)
56
+ return addCorsHeaders(response, req)
59
57
  }
60
58
 
61
59
  // Get invitation by token
@@ -67,7 +65,7 @@ export const POST = withRateLimitTier(withApiLogging(
67
65
 
68
66
  if (!invitation) {
69
67
  const response = createApiError('Invitation not found', 404, null, 'INVITATION_NOT_FOUND')
70
- return addCorsHeaders(response)
68
+ return addCorsHeaders(response, req)
71
69
  }
72
70
 
73
71
  // Verify invitation is for the current user's email
@@ -78,7 +76,7 @@ export const POST = withRateLimitTier(withApiLogging(
78
76
  null,
79
77
  'EMAIL_MISMATCH'
80
78
  )
81
- return addCorsHeaders(response)
79
+ return addCorsHeaders(response, req)
82
80
  }
83
81
 
84
82
  // Check if invitation is pending
@@ -89,7 +87,7 @@ export const POST = withRateLimitTier(withApiLogging(
89
87
  null,
90
88
  'INVITATION_NOT_PENDING'
91
89
  )
92
- return addCorsHeaders(response)
90
+ return addCorsHeaders(response, req)
93
91
  }
94
92
 
95
93
  // Update invitation status to declined
@@ -104,17 +102,17 @@ export const POST = withRateLimitTier(withApiLogging(
104
102
 
105
103
  if (result.rows.length === 0) {
106
104
  const response = createApiError('Invitation not found', 404, null, 'INVITATION_NOT_FOUND')
107
- return addCorsHeaders(response)
105
+ return addCorsHeaders(response, req)
108
106
  }
109
107
 
110
108
  const declinedInvitation = result.rows[0]
111
109
 
112
110
  const response = createApiResponse(declinedInvitation)
113
- return addCorsHeaders(response)
111
+ return addCorsHeaders(response, req)
114
112
  } catch (error) {
115
113
  console.error('Error declining invitation:', error)
116
114
  const response = createApiError('Internal server error', 500)
117
- return addCorsHeaders(response)
115
+ return addCorsHeaders(response, req)
118
116
  }
119
117
  }
120
118
  ), 'write');
@@ -11,8 +11,8 @@ import type { TeamInvitation } from '@nextsparkjs/core/lib/teams/types'
11
11
  import { withRateLimitTier } from '@nextsparkjs/core/lib/api/rate-limit'
12
12
 
13
13
  // Handle CORS preflight
14
- export async function OPTIONS() {
15
- return handleCorsPreflightRequest()
14
+ export async function OPTIONS(request: NextRequest) {
15
+ return handleCorsPreflightRequest(request)
16
16
  }
17
17
 
18
18
  // GET /api/v1/team-invitations/:token - Get invitation details (public endpoint for preview)
@@ -24,7 +24,7 @@ export const GET = withRateLimitTier(withApiLogging(
24
24
  // Validate that token is not empty
25
25
  if (!token || token.trim() === '') {
26
26
  const response = createApiError('Invitation token is required', 400, null, 'MISSING_TOKEN')
27
- return addCorsHeaders(response)
27
+ return addCorsHeaders(response, req)
28
28
  }
29
29
 
30
30
  // Get invitation by token with team and inviter details
@@ -49,14 +49,14 @@ export const GET = withRateLimitTier(withApiLogging(
49
49
 
50
50
  if (!invitation) {
51
51
  const response = createApiError('Invitation not found', 404, null, 'INVITATION_NOT_FOUND')
52
- return addCorsHeaders(response)
52
+ return addCorsHeaders(response, req)
53
53
  }
54
54
 
55
55
  // Check if invitation has expired
56
56
  const expiresAt = new Date(invitation.expiresAt)
57
57
  if (expiresAt < new Date()) {
58
58
  const response = createApiError('Invitation has expired', 410, null, 'INVITATION_EXPIRED')
59
- return addCorsHeaders(response)
59
+ return addCorsHeaders(response, req)
60
60
  }
61
61
 
62
62
  // Check if invitation is still pending
@@ -67,7 +67,7 @@ export const GET = withRateLimitTier(withApiLogging(
67
67
  null,
68
68
  `INVITATION_${invitation.status.toUpperCase()}`
69
69
  )
70
- return addCorsHeaders(response)
70
+ return addCorsHeaders(response, req)
71
71
  }
72
72
 
73
73
  // Return public invitation details (don't expose sensitive data)
@@ -80,11 +80,11 @@ export const GET = withRateLimitTier(withApiLogging(
80
80
  }
81
81
 
82
82
  const response = createApiResponse(publicDetails)
83
- return addCorsHeaders(response)
83
+ return addCorsHeaders(response, req)
84
84
  } catch (error) {
85
85
  console.error('Error fetching invitation:', error)
86
86
  const response = createApiError('Internal server error', 500)
87
- return addCorsHeaders(response)
87
+ return addCorsHeaders(response, req)
88
88
  }
89
89
  }
90
90
  ), 'read');
@@ -8,27 +8,25 @@ import {
8
8
  handleCorsPreflightRequest,
9
9
  addCorsHeaders,
10
10
  } from '@nextsparkjs/core/lib/api/helpers'
11
- import { authenticateRequest } from '@nextsparkjs/core/lib/api/auth/dual-auth'
11
+ import { authenticateRequest, createAuthFailureResponse } from '@nextsparkjs/core/lib/api/auth/dual-auth'
12
12
  import { invitationListQuerySchema } from '@nextsparkjs/core/lib/teams/schema'
13
13
  import type { TeamInvitation } from '@nextsparkjs/core/lib/teams/types'
14
14
  import { withRateLimitTier } from '@nextsparkjs/core/lib/api/rate-limit'
15
15
 
16
16
  // Handle CORS preflight
17
- export async function OPTIONS() {
18
- return handleCorsPreflightRequest()
17
+ export async function OPTIONS(request: NextRequest) {
18
+ return handleCorsPreflightRequest(request)
19
19
  }
20
20
 
21
21
  // GET /api/v1/team-invitations - List pending invitations for current user
22
22
  export const GET = withRateLimitTier(withApiLogging(async (req: NextRequest): Promise<NextResponse> => {
23
23
  try {
24
- // Authenticate using dual auth
25
- const authResult = await authenticateRequest(req)
24
+ // Authenticate using dual auth; the API-key scope is declared at the entry
25
+ // point, which fails closed for keys that lack it (#93).
26
+ const authResult = await authenticateRequest(req, { requiredScope: 'teams:read' })
26
27
 
27
28
  if (!authResult.success) {
28
- return NextResponse.json(
29
- { success: false, error: 'Authentication required', code: 'AUTHENTICATION_FAILED' },
30
- { status: 401 }
31
- )
29
+ return createAuthFailureResponse(authResult)
32
30
  }
33
31
 
34
32
  if (authResult.rateLimitResponse) {
@@ -106,10 +104,10 @@ export const GET = withRateLimitTier(withApiLogging(async (req: NextRequest): Pr
106
104
  const paginationMeta = createPaginationMeta(page, limit, total)
107
105
 
108
106
  const response = createApiResponse(invitations, paginationMeta)
109
- return addCorsHeaders(response)
107
+ return addCorsHeaders(response, req)
110
108
  } catch (error) {
111
109
  console.error('Error fetching invitations:', error)
112
110
  const response = createApiError('Internal server error', 500)
113
- return addCorsHeaders(response)
111
+ return addCorsHeaders(response, req)
114
112
  }
115
113
  }), 'read');
@@ -8,28 +8,26 @@ import {
8
8
  handleCorsPreflightRequest,
9
9
  addCorsHeaders,
10
10
  } from '@nextsparkjs/core/lib/api/helpers'
11
- import { authenticateRequest } from '@nextsparkjs/core/lib/api/auth/dual-auth'
11
+ import { authenticateRequest, createAuthFailureResponse } from '@nextsparkjs/core/lib/api/auth/dual-auth'
12
12
  import { withRateLimitTier } from '@nextsparkjs/core/lib/api/rate-limit'
13
- import { TeamMemberService, MembershipService } from '@nextsparkjs/core/lib/services'
13
+ import { MembershipService } from '@nextsparkjs/core/lib/services'
14
14
  import type { TeamInvitation } from '@nextsparkjs/core/lib/teams/types'
15
15
 
16
16
  // Handle CORS preflight
17
- export async function OPTIONS() {
18
- return handleCorsPreflightRequest()
17
+ export async function OPTIONS(request: NextRequest) {
18
+ return handleCorsPreflightRequest(request)
19
19
  }
20
20
 
21
21
  // GET /api/v1/teams/:teamId/invitations - List pending invitations for a team
22
22
  export const GET = withRateLimitTier(withApiLogging(
23
23
  async (req: NextRequest, { params }: { params: Promise<{ teamId: string }> }): Promise<NextResponse> => {
24
24
  try {
25
- // Authenticate using dual auth
26
- const authResult = await authenticateRequest(req)
25
+ // Authenticate using dual auth; the API-key scope is declared at the entry
26
+ // point, which fails closed for keys that lack it (#93).
27
+ const authResult = await authenticateRequest(req, { requiredScope: 'teams:read' })
27
28
 
28
29
  if (!authResult.success) {
29
- return NextResponse.json(
30
- { success: false, error: 'Authentication required', code: 'AUTHENTICATION_FAILED' },
31
- { status: 401 }
32
- )
30
+ return createAuthFailureResponse(authResult)
33
31
  }
34
32
 
35
33
  if (authResult.rateLimitResponse) {
@@ -41,15 +39,22 @@ export const GET = withRateLimitTier(withApiLogging(
41
39
  // Validate that teamId is not empty
42
40
  if (!teamId || teamId.trim() === '') {
43
41
  const response = createApiError('Team ID is required', 400, null, 'MISSING_TEAM_ID')
44
- return addCorsHeaders(response)
42
+ return addCorsHeaders(response, req)
45
43
  }
46
44
 
47
- // Check if user is a member of the team
48
- const isMember = await TeamMemberService.isMember(teamId, authResult.user!.id)
45
+ // Listing invitations — including who invited whom, to what role, and
46
+ // when it expires — is gated by the same 'team.members.invite'
47
+ // permission that already protects creating/cancelling one, not bare
48
+ // membership: an invitation's raw acceptance token grants whoever holds
49
+ // it the invitee's role, so a low-privilege member (member/viewer, who
50
+ // typically lack 'team.members.invite') must not be able to read it
51
+ // (see GHSA-rw2j-9mxg-rx98).
52
+ const membership = await MembershipService.get(authResult.user!.id, teamId)
53
+ const actionResult = membership.canPerformAction('team.members.invite')
49
54
 
50
- if (!isMember) {
55
+ if (!actionResult.allowed) {
51
56
  const response = createApiError('Team not found or access denied', 404, null, 'TEAM_NOT_FOUND')
52
- return addCorsHeaders(response)
57
+ return addCorsHeaders(response, req)
53
58
  }
54
59
 
55
60
  // Parse query parameters
@@ -59,15 +64,21 @@ export const GET = withRateLimitTier(withApiLogging(
59
64
  const status = searchParams.get('status') || 'pending'
60
65
  const offset = (page - 1) * limit
61
66
 
62
- // Fetch team invitations
67
+ // Fetch team invitations — explicit column list, deliberately excluding
68
+ // "token": it is a bearer credential that grants the invitee's role to
69
+ // whoever holds it, and has no business leaving this endpoint at all
70
+ // (see GHSA-rw2j-9mxg-rx98). The accept flow re-derives it server-side
71
+ // from the URL param it already has; nothing legitimate reads it back
72
+ // from this list response.
63
73
  const invitations = await queryWithRLS<
64
- TeamInvitation & {
74
+ Omit<TeamInvitation, 'token'> & {
65
75
  inviterName: string | null
66
76
  inviterEmail: string
67
77
  }
68
78
  >(
69
79
  `SELECT
70
- ti.*,
80
+ ti.id, ti."teamId", ti.email, ti.role, ti.status, ti."invitedBy",
81
+ ti."expiresAt", ti."acceptedAt", ti."declinedAt", ti."createdAt", ti."updatedAt",
71
82
  u.name as "inviterName",
72
83
  u.email as "inviterEmail"
73
84
  FROM "team_invitations" ti
@@ -92,11 +103,11 @@ export const GET = withRateLimitTier(withApiLogging(
92
103
  const paginationMeta = createPaginationMeta(page, limit, total)
93
104
 
94
105
  const response = createApiResponse(invitations, paginationMeta)
95
- return addCorsHeaders(response)
106
+ return addCorsHeaders(response, req)
96
107
  } catch (error) {
97
108
  console.error('Error fetching team invitations:', error)
98
109
  const response = createApiError('Internal server error', 500)
99
- return addCorsHeaders(response)
110
+ return addCorsHeaders(response, req)
100
111
  }
101
112
  }
102
113
  ), 'read')
@@ -105,14 +116,12 @@ export const GET = withRateLimitTier(withApiLogging(
105
116
  export const DELETE = withRateLimitTier(withApiLogging(
106
117
  async (req: NextRequest, { params }: { params: Promise<{ teamId: string }> }): Promise<NextResponse> => {
107
118
  try {
108
- // Authenticate using dual auth
109
- const authResult = await authenticateRequest(req)
119
+ // Authenticate using dual auth; the API-key scope is declared at the entry
120
+ // point, which fails closed for keys that lack it (#93).
121
+ const authResult = await authenticateRequest(req, { requiredScope: 'teams:write' })
110
122
 
111
123
  if (!authResult.success) {
112
- return NextResponse.json(
113
- { success: false, error: 'Authentication required', code: 'AUTHENTICATION_FAILED' },
114
- { status: 401 }
115
- )
124
+ return createAuthFailureResponse(authResult)
116
125
  }
117
126
 
118
127
  if (authResult.rateLimitResponse) {
@@ -127,12 +136,12 @@ export const DELETE = withRateLimitTier(withApiLogging(
127
136
 
128
137
  if (!invitationId) {
129
138
  const response = createApiError('Invitation ID is required', 400, null, 'MISSING_INVITATION_ID')
130
- return addCorsHeaders(response)
139
+ return addCorsHeaders(response, req)
131
140
  }
132
141
 
133
142
  // Check if user has permission to manage invitations using MembershipService
134
143
  const membership = await MembershipService.get(authResult.user!.id, teamId)
135
- const actionResult = membership.canPerformAction('members.invite')
144
+ const actionResult = membership.canPerformAction('team.members.invite')
136
145
 
137
146
  if (!actionResult.allowed) {
138
147
  const response = NextResponse.json(
@@ -144,7 +153,7 @@ export const DELETE = withRateLimitTier(withApiLogging(
144
153
  },
145
154
  { status: 403 }
146
155
  )
147
- return addCorsHeaders(response)
156
+ return addCorsHeaders(response, req)
148
157
  }
149
158
 
150
159
  // Update invitation status to 'cancelled' (or delete it)
@@ -158,15 +167,15 @@ export const DELETE = withRateLimitTier(withApiLogging(
158
167
 
159
168
  if (result.rowCount === 0) {
160
169
  const response = createApiError('Invitation not found or already processed', 404, null, 'INVITATION_NOT_FOUND')
161
- return addCorsHeaders(response)
170
+ return addCorsHeaders(response, req)
162
171
  }
163
172
 
164
173
  const response = createApiResponse({ deleted: true, id: invitationId })
165
- return addCorsHeaders(response)
174
+ return addCorsHeaders(response, req)
166
175
  } catch (error) {
167
176
  console.error('Error cancelling invitation:', error)
168
177
  const response = createApiError('Internal server error', 500)
169
- return addCorsHeaders(response)
178
+ return addCorsHeaders(response, req)
170
179
  }
171
180
  }
172
181
  ), 'write')
@@ -7,14 +7,14 @@ import {
7
7
  handleCorsPreflightRequest,
8
8
  addCorsHeaders,
9
9
  } from '@nextsparkjs/core/lib/api/helpers'
10
- import { authenticateRequest } from '@nextsparkjs/core/lib/api/auth/dual-auth'
10
+ import { authenticateRequest, createAuthFailureResponse } from '@nextsparkjs/core/lib/api/auth/dual-auth'
11
11
  import { withRateLimitTier } from '@nextsparkjs/core/lib/api/rate-limit'
12
12
  import { MembershipService } from '@nextsparkjs/core/lib/services'
13
13
  import type { InvoiceResponse } from '@nextsparkjs/core/lib/validation/invoices'
14
14
 
15
15
  // Handle CORS preflight
16
- export async function OPTIONS() {
17
- return handleCorsPreflightRequest()
16
+ export async function OPTIONS(request: NextRequest) {
17
+ return handleCorsPreflightRequest(request)
18
18
  }
19
19
 
20
20
  // GET /api/v1/teams/:teamId/invoices/:invoiceNumber - Get single invoice (owner only)
@@ -24,14 +24,13 @@ export const GET = withRateLimitTier(withApiLogging(
24
24
  { params }: { params: Promise<{ teamId: string; invoiceNumber: string }> }
25
25
  ): Promise<NextResponse> => {
26
26
  try {
27
- // Authenticate using dual auth (API key OR session)
28
- const authResult = await authenticateRequest(req)
27
+ // Authenticate using dual auth (API key OR session); the API-key scope
28
+ // is declared at the entry point, which fails closed for keys that
29
+ // lack it (#93).
30
+ const authResult = await authenticateRequest(req, { requiredScope: 'billing:read' })
29
31
 
30
32
  if (!authResult.success) {
31
- return NextResponse.json(
32
- { success: false, error: 'Authentication required', code: 'AUTHENTICATION_FAILED' },
33
- { status: 401 }
34
- )
33
+ return createAuthFailureResponse(authResult)
35
34
  }
36
35
 
37
36
  if (authResult.rateLimitResponse) {
@@ -43,18 +42,18 @@ export const GET = withRateLimitTier(withApiLogging(
43
42
  // Validate that teamId is not empty
44
43
  if (!teamId || teamId.trim() === '') {
45
44
  const response = createApiError('Team ID is required', 400, null, 'MISSING_TEAM_ID')
46
- return addCorsHeaders(response)
45
+ return addCorsHeaders(response, req)
47
46
  }
48
47
 
49
48
  // Validate that invoiceNumber is not empty
50
49
  if (!invoiceNumber || invoiceNumber.trim() === '') {
51
50
  const response = createApiError('Invoice number is required', 400, null, 'MISSING_INVOICE_NUMBER')
52
- return addCorsHeaders(response)
51
+ return addCorsHeaders(response, req)
53
52
  }
54
53
 
55
54
  // Check if user has permission to view invoices using MembershipService
56
55
  const membership = await MembershipService.get(authResult.user!.id, teamId)
57
- const actionResult = membership.canPerformAction('billing.invoices')
56
+ const actionResult = membership.canPerformAction('team.billing.view')
58
57
 
59
58
  if (!actionResult.allowed) {
60
59
  const response = NextResponse.json(
@@ -66,7 +65,7 @@ export const GET = withRateLimitTier(withApiLogging(
66
65
  },
67
66
  { status: 403 }
68
67
  )
69
- return addCorsHeaders(response)
68
+ return addCorsHeaders(response, req)
70
69
  }
71
70
 
72
71
  // Query single invoice by invoiceNumber
@@ -92,15 +91,15 @@ export const GET = withRateLimitTier(withApiLogging(
92
91
 
93
92
  if (invoices.length === 0) {
94
93
  const response = createApiError('Invoice not found', 404, null, 'INVOICE_NOT_FOUND')
95
- return addCorsHeaders(response)
94
+ return addCorsHeaders(response, req)
96
95
  }
97
96
 
98
97
  const response = createApiResponse(invoices[0])
99
- return addCorsHeaders(response)
98
+ return addCorsHeaders(response, req)
100
99
  } catch (error) {
101
100
  console.error('Error fetching invoice:', error)
102
101
  const response = createApiError('Internal server error', 500)
103
- return addCorsHeaders(response)
102
+ return addCorsHeaders(response, req)
104
103
  }
105
104
  }
106
105
  ), 'read')