@nextsparkjs/core 0.1.0-beta.92 → 0.1.0-beta.94

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 (341) hide show
  1. package/dist/components/dashboard/block-editor/array-field.d.ts.map +1 -1
  2. package/dist/components/dashboard/block-editor/array-field.js +55 -3
  3. package/dist/components/dashboard/block-editor/dynamic-form.d.ts.map +1 -1
  4. package/dist/components/dashboard/block-editor/dynamic-form.js +82 -2
  5. package/dist/components/dashboard/navigation/DynamicNavigation.d.ts.map +1 -1
  6. package/dist/components/dashboard/navigation/DynamicNavigation.js +7 -1
  7. package/dist/components/devtools/scheduled-actions/actions-table.d.ts +1 -0
  8. package/dist/components/devtools/scheduled-actions/actions-table.d.ts.map +1 -1
  9. package/dist/components/devtools/scheduled-actions/actions-table.js +182 -46
  10. package/dist/components/devtools/scheduled-actions/types.d.ts +1 -0
  11. package/dist/components/devtools/scheduled-actions/types.d.ts.map +1 -1
  12. package/dist/components/media/MediaCard.d.ts +23 -0
  13. package/dist/components/media/MediaCard.d.ts.map +1 -0
  14. package/dist/components/media/MediaCard.js +154 -0
  15. package/dist/components/media/MediaDetailPanel.d.ts +17 -0
  16. package/dist/components/media/MediaDetailPanel.d.ts.map +1 -0
  17. package/dist/components/media/MediaDetailPanel.js +331 -0
  18. package/dist/components/media/MediaGrid.d.ts +26 -0
  19. package/dist/components/media/MediaGrid.d.ts.map +1 -0
  20. package/dist/components/media/MediaGrid.js +77 -0
  21. package/dist/components/media/MediaLibrary.d.ts +20 -0
  22. package/dist/components/media/MediaLibrary.d.ts.map +1 -0
  23. package/dist/components/media/MediaLibrary.js +229 -0
  24. package/dist/components/media/MediaList.d.ts +24 -0
  25. package/dist/components/media/MediaList.d.ts.map +1 -0
  26. package/dist/components/media/MediaList.js +181 -0
  27. package/dist/components/media/MediaSelector.d.ts +19 -0
  28. package/dist/components/media/MediaSelector.d.ts.map +1 -0
  29. package/dist/components/media/MediaSelector.js +145 -0
  30. package/dist/components/media/MediaTagFilter.d.ts +16 -0
  31. package/dist/components/media/MediaTagFilter.d.ts.map +1 -0
  32. package/dist/components/media/MediaTagFilter.js +122 -0
  33. package/dist/components/media/MediaToolbar.d.ts +25 -0
  34. package/dist/components/media/MediaToolbar.d.ts.map +1 -0
  35. package/dist/components/media/MediaToolbar.js +136 -0
  36. package/dist/components/media/MediaUploadZone.d.ts +19 -0
  37. package/dist/components/media/MediaUploadZone.d.ts.map +1 -0
  38. package/dist/components/media/MediaUploadZone.js +248 -0
  39. package/dist/components/media/index.d.ts +15 -0
  40. package/dist/components/media/index.d.ts.map +1 -0
  41. package/dist/components/media/index.js +20 -0
  42. package/dist/contexts/TeamContext.js +1 -1
  43. package/dist/hooks/index.d.ts +2 -0
  44. package/dist/hooks/index.d.ts.map +1 -1
  45. package/dist/hooks/index.js +2 -0
  46. package/dist/hooks/useEnsureUserMetadata.d.ts +4 -0
  47. package/dist/hooks/useEnsureUserMetadata.d.ts.map +1 -1
  48. package/dist/hooks/useEnsureUserMetadata.js +85 -60
  49. package/dist/hooks/useEntityMutations.d.ts.map +1 -1
  50. package/dist/hooks/useEntityMutations.js +5 -9
  51. package/dist/hooks/useMedia.d.ts +56 -0
  52. package/dist/hooks/useMedia.d.ts.map +1 -0
  53. package/dist/hooks/useMedia.js +181 -0
  54. package/dist/hooks/useMediaUpload.d.ts +27 -0
  55. package/dist/hooks/useMediaUpload.d.ts.map +1 -0
  56. package/dist/hooks/useMediaUpload.js +36 -0
  57. package/dist/hooks/useUserSettings.d.ts +5 -4
  58. package/dist/hooks/useUserSettings.d.ts.map +1 -1
  59. package/dist/hooks/useUserSettings.js +42 -40
  60. package/dist/index.d.ts +1 -1
  61. package/dist/index.d.ts.map +1 -1
  62. package/dist/index.js +2 -3
  63. package/dist/lib/api/auth/dual-auth.d.ts +6 -2
  64. package/dist/lib/api/auth/dual-auth.d.ts.map +1 -1
  65. package/dist/lib/api/auth/dual-auth.js +5 -9
  66. package/dist/lib/api/entity/generic-handler.d.ts.map +1 -1
  67. package/dist/lib/api/entity/generic-handler.js +3 -3
  68. package/dist/lib/config/app.config.d.ts.map +1 -1
  69. package/dist/lib/config/app.config.js +37 -0
  70. package/dist/lib/config/config-sync.d.ts +1 -0
  71. package/dist/lib/config/config-sync.d.ts.map +1 -1
  72. package/dist/lib/config/config-sync.js +2 -0
  73. package/dist/lib/config/types.d.ts +29 -0
  74. package/dist/lib/config/types.d.ts.map +1 -1
  75. package/dist/lib/media/schemas.d.ts +39 -0
  76. package/dist/lib/media/schemas.d.ts.map +1 -0
  77. package/dist/lib/media/schemas.js +32 -0
  78. package/dist/lib/media/types.d.ts +69 -0
  79. package/dist/lib/media/types.d.ts.map +1 -0
  80. package/dist/lib/media/types.js +0 -0
  81. package/dist/lib/media/utils.d.ts +26 -0
  82. package/dist/lib/media/utils.d.ts.map +1 -0
  83. package/dist/lib/media/utils.js +33 -0
  84. package/dist/lib/rate-limit-redis.d.ts.map +1 -1
  85. package/dist/lib/rate-limit-redis.js +13 -4
  86. package/dist/lib/scheduled-actions/initializer.d.ts +6 -3
  87. package/dist/lib/scheduled-actions/initializer.d.ts.map +1 -1
  88. package/dist/lib/scheduled-actions/initializer.js +11 -6
  89. package/dist/lib/scheduled-actions/processor.d.ts +20 -4
  90. package/dist/lib/scheduled-actions/processor.d.ts.map +1 -1
  91. package/dist/lib/scheduled-actions/processor.js +128 -34
  92. package/dist/lib/scheduled-actions/registry.d.ts +3 -0
  93. package/dist/lib/scheduled-actions/registry.d.ts.map +1 -1
  94. package/dist/lib/scheduled-actions/registry.js +2 -1
  95. package/dist/lib/scheduled-actions/scheduler.d.ts +1 -1
  96. package/dist/lib/scheduled-actions/scheduler.d.ts.map +1 -1
  97. package/dist/lib/scheduled-actions/scheduler.js +76 -38
  98. package/dist/lib/scheduled-actions/types.d.ts +73 -0
  99. package/dist/lib/scheduled-actions/types.d.ts.map +1 -1
  100. package/dist/lib/selectors/core-selectors.d.ts +102 -0
  101. package/dist/lib/selectors/core-selectors.d.ts.map +1 -1
  102. package/dist/lib/selectors/core-selectors.js +3 -1
  103. package/dist/lib/selectors/domains/block-editor.selectors.d.ts +8 -0
  104. package/dist/lib/selectors/domains/block-editor.selectors.d.ts.map +1 -1
  105. package/dist/lib/selectors/domains/block-editor.selectors.js +9 -0
  106. package/dist/lib/selectors/domains/devtools.selectors.d.ts +6 -0
  107. package/dist/lib/selectors/domains/devtools.selectors.d.ts.map +1 -1
  108. package/dist/lib/selectors/domains/devtools.selectors.js +6 -0
  109. package/dist/lib/selectors/domains/index.d.ts +1 -0
  110. package/dist/lib/selectors/domains/index.d.ts.map +1 -1
  111. package/dist/lib/selectors/domains/index.js +2 -0
  112. package/dist/lib/selectors/domains/media.selectors.d.ts +96 -0
  113. package/dist/lib/selectors/domains/media.selectors.d.ts.map +1 -0
  114. package/dist/lib/selectors/domains/media.selectors.js +103 -0
  115. package/dist/lib/selectors/selectors.d.ts +204 -0
  116. package/dist/lib/selectors/selectors.d.ts.map +1 -1
  117. package/dist/lib/services/index.d.ts +2 -0
  118. package/dist/lib/services/index.d.ts.map +1 -1
  119. package/dist/lib/services/index.js +2 -0
  120. package/dist/lib/services/media.service.d.ts +158 -0
  121. package/dist/lib/services/media.service.d.ts.map +1 -0
  122. package/dist/lib/services/media.service.js +410 -0
  123. package/dist/messages/de/devtools.json +16 -0
  124. package/dist/messages/de/index.d.ts +16 -0
  125. package/dist/messages/de/index.d.ts.map +1 -1
  126. package/dist/messages/en/admin.json +4 -1
  127. package/dist/messages/en/devtools.json +16 -0
  128. package/dist/messages/en/index.d.ts +167 -0
  129. package/dist/messages/en/index.d.ts.map +1 -1
  130. package/dist/messages/en/index.js +2 -0
  131. package/dist/messages/en/index.ts +2 -0
  132. package/dist/messages/en/media.json +147 -0
  133. package/dist/messages/en/navigation.json +1 -0
  134. package/dist/messages/es/admin.json +4 -1
  135. package/dist/messages/es/devtools.json +16 -0
  136. package/dist/messages/es/index.d.ts +167 -0
  137. package/dist/messages/es/index.d.ts.map +1 -1
  138. package/dist/messages/es/index.js +2 -0
  139. package/dist/messages/es/index.ts +2 -0
  140. package/dist/messages/es/media.json +147 -0
  141. package/dist/messages/es/navigation.json +1 -0
  142. package/dist/messages/fr/devtools.json +16 -0
  143. package/dist/messages/fr/index.d.ts +16 -0
  144. package/dist/messages/fr/index.d.ts.map +1 -1
  145. package/dist/messages/it/devtools.json +16 -0
  146. package/dist/messages/it/index.d.ts +16 -0
  147. package/dist/messages/it/index.d.ts.map +1 -1
  148. package/dist/messages/pt/devtools.json +16 -0
  149. package/dist/messages/pt/index.d.ts +16 -0
  150. package/dist/messages/pt/index.d.ts.map +1 -1
  151. package/dist/migrations/017_scheduled_actions_table.sql +21 -0
  152. package/dist/migrations/021_media.sql +154 -0
  153. package/dist/migrations/090_sample_data.sql +53 -0
  154. package/dist/styles/classes.json +36 -3
  155. package/dist/styles/ui.css +1 -1
  156. package/dist/templates/app/api/devtools/config/entities/route.ts +18 -11
  157. package/dist/templates/app/api/devtools/config/theme/route.ts +5 -4
  158. package/dist/templates/app/api/devtools/tests/[...path]/route.ts +6 -5
  159. package/dist/templates/app/api/devtools/tests/route.ts +5 -4
  160. package/dist/templates/app/api/health/route.ts +6 -4
  161. package/dist/templates/app/api/internal/user-metadata/route.ts +3 -2
  162. package/dist/templates/app/api/superadmin/subscriptions/route.ts +5 -6
  163. package/dist/templates/app/api/superadmin/teams/[teamId]/route.ts +6 -7
  164. package/dist/templates/app/api/superadmin/teams/route.ts +5 -6
  165. package/dist/templates/app/api/superadmin/users/[userId]/route.ts +11 -16
  166. package/dist/templates/app/api/superadmin/users/route.ts +9 -10
  167. package/dist/templates/app/api/user/delete-account/route.ts +3 -2
  168. package/dist/templates/app/api/user/plan-flags/route.ts +11 -24
  169. package/dist/templates/app/api/user/profile/route.ts +7 -6
  170. package/dist/templates/app/api/v1/[entity]/[id]/child/[childType]/[childId]/route.ts +16 -18
  171. package/dist/templates/app/api/v1/[entity]/[id]/child/[childType]/route.ts +17 -19
  172. package/dist/templates/app/api/v1/[entity]/[id]/route.ts +10 -12
  173. package/dist/templates/app/api/v1/[entity]/route.ts +9 -11
  174. package/dist/templates/app/api/v1/api-keys/[id]/route.ts +9 -8
  175. package/dist/templates/app/api/v1/api-keys/route.ts +7 -6
  176. package/dist/templates/app/api/v1/auth/signup-with-invite/route.ts +3 -2
  177. package/dist/templates/app/api/v1/billing/cancel/route.ts +15 -14
  178. package/dist/templates/app/api/v1/billing/change-plan/route.ts +10 -9
  179. package/dist/templates/app/api/v1/billing/check-action/route.ts +8 -7
  180. package/dist/templates/app/api/v1/billing/checkout/route.ts +10 -9
  181. package/dist/templates/app/api/v1/billing/plans/route.ts +5 -4
  182. package/dist/templates/app/api/v1/billing/portal/route.ts +9 -8
  183. package/dist/templates/app/api/v1/blocks/[slug]/route.ts +4 -3
  184. package/dist/templates/app/api/v1/blocks/route.ts +3 -2
  185. package/dist/templates/app/api/v1/blocks/validate/route.ts +5 -3
  186. package/dist/templates/app/api/v1/cron/process/route.ts +4 -6
  187. package/dist/templates/app/api/v1/devtools/blocks/route.ts +3 -2
  188. package/dist/templates/app/api/v1/devtools/docs/route.ts +3 -2
  189. package/dist/templates/app/api/v1/devtools/features/route.ts +3 -2
  190. package/dist/templates/app/api/v1/devtools/flows/route.ts +3 -2
  191. package/dist/templates/app/api/v1/devtools/scheduled-actions/route.ts +125 -3
  192. package/dist/templates/app/api/v1/devtools/scheduled-actions/run/route.ts +110 -0
  193. package/dist/templates/app/api/v1/devtools/testing/route.ts +3 -2
  194. package/dist/templates/app/api/v1/media/[id]/route.ts +144 -0
  195. package/dist/templates/app/api/v1/media/[id]/tags/route.ts +154 -0
  196. package/dist/templates/app/api/v1/media/check-duplicates/route.ts +56 -0
  197. package/dist/templates/app/api/v1/media/route.ts +56 -0
  198. package/dist/templates/app/api/v1/media/upload/route.ts +157 -33
  199. package/dist/templates/app/api/v1/media-tags/route.ts +65 -0
  200. package/dist/templates/app/api/v1/plugin/[...path]/route.ts +16 -15
  201. package/dist/templates/app/api/v1/plugin/route.ts +3 -2
  202. package/dist/templates/app/api/v1/post-categories/[id]/route.ts +10 -9
  203. package/dist/templates/app/api/v1/post-categories/route.ts +5 -4
  204. package/dist/templates/app/api/v1/team-invitations/[token]/accept/route.ts +3 -3
  205. package/dist/templates/app/api/v1/team-invitations/[token]/decline/route.ts +3 -3
  206. package/dist/templates/app/api/v1/team-invitations/[token]/route.ts +3 -2
  207. package/dist/templates/app/api/v1/team-invitations/route.ts +3 -2
  208. package/dist/templates/app/api/v1/teams/[teamId]/invitations/route.ts +5 -4
  209. package/dist/templates/app/api/v1/teams/[teamId]/invoices/[invoiceNumber]/route.ts +3 -2
  210. package/dist/templates/app/api/v1/teams/[teamId]/invoices/route.ts +3 -2
  211. package/dist/templates/app/api/v1/teams/[teamId]/members/[memberId]/route.ts +5 -4
  212. package/dist/templates/app/api/v1/teams/[teamId]/members/route.ts +5 -5
  213. package/dist/templates/app/api/v1/teams/[teamId]/route.ts +31 -58
  214. package/dist/templates/app/api/v1/teams/[teamId]/subscription/route.ts +3 -2
  215. package/dist/templates/app/api/v1/teams/[teamId]/usage/[limitSlug]/route.ts +5 -4
  216. package/dist/templates/app/api/v1/teams/route.ts +18 -17
  217. package/dist/templates/app/api/v1/teams/switch/route.ts +3 -2
  218. package/dist/templates/app/api/v1/theme/[...path]/route.ts +16 -15
  219. package/dist/templates/app/api/v1/theme/route.ts +3 -2
  220. package/dist/templates/app/api/v1/users/[id]/meta/[key]/route.ts +7 -6
  221. package/dist/templates/app/api/v1/users/[id]/route.ts +9 -8
  222. package/dist/templates/app/api/v1/users/route.ts +7 -6
  223. package/dist/templates/app/dashboard/(main)/media/page.tsx +607 -0
  224. package/dist/templates/contents/themes/starter/messages/de/dev.json +106 -0
  225. package/dist/templates/contents/themes/starter/messages/de/index.ts +2 -0
  226. package/dist/templates/contents/themes/starter/messages/en/dev.json +106 -0
  227. package/dist/templates/contents/themes/starter/messages/en/index.ts +2 -0
  228. package/dist/templates/contents/themes/starter/messages/es/dev.json +106 -0
  229. package/dist/templates/contents/themes/starter/messages/es/index.ts +2 -0
  230. package/dist/templates/contents/themes/starter/messages/fr/dev.json +106 -0
  231. package/dist/templates/contents/themes/starter/messages/fr/index.ts +2 -0
  232. package/dist/templates/contents/themes/starter/messages/it/dev.json +106 -0
  233. package/dist/templates/contents/themes/starter/messages/it/index.ts +2 -0
  234. package/dist/templates/contents/themes/starter/messages/pt/dev.json +106 -0
  235. package/dist/templates/contents/themes/starter/messages/pt/index.ts +2 -0
  236. package/dist/templates/contents/themes/starter/styles/globals.css +14 -0
  237. package/dist/templates/instrumentation.ts +33 -0
  238. package/dist/types/blocks.d.ts +1 -1
  239. package/dist/types/blocks.d.ts.map +1 -1
  240. package/migrations/017_scheduled_actions_table.sql +21 -0
  241. package/migrations/021_media.sql +154 -0
  242. package/migrations/090_sample_data.sql +53 -0
  243. package/package.json +3 -2
  244. package/scripts/build/registry/config.mjs +41 -0
  245. package/scripts/build/registry/discovery/templates.mjs +0 -1
  246. package/scripts/build/registry/generators/entity-registry.mjs +16 -6
  247. package/scripts/build/registry/generators/route-handlers.mjs +8 -2
  248. package/scripts/build/registry/generators/template-registry.mjs +16 -4
  249. package/scripts/build/registry/post-build/route-cleanup.mjs +0 -1
  250. package/scripts/build/registry/validate-env.test.mjs +92 -0
  251. package/scripts/build/registry.mjs +18 -1
  252. package/scripts/deploy/vercel-deploy.mjs +1 -1
  253. package/templates/app/api/devtools/config/entities/route.ts +18 -11
  254. package/templates/app/api/devtools/config/theme/route.ts +5 -4
  255. package/templates/app/api/devtools/tests/[...path]/route.ts +6 -5
  256. package/templates/app/api/devtools/tests/route.ts +5 -4
  257. package/templates/app/api/health/route.ts +6 -4
  258. package/templates/app/api/internal/user-metadata/route.ts +3 -2
  259. package/templates/app/api/superadmin/subscriptions/route.ts +5 -6
  260. package/templates/app/api/superadmin/teams/[teamId]/route.ts +6 -7
  261. package/templates/app/api/superadmin/teams/route.ts +5 -6
  262. package/templates/app/api/superadmin/users/[userId]/route.ts +11 -16
  263. package/templates/app/api/superadmin/users/route.ts +9 -10
  264. package/templates/app/api/user/delete-account/route.ts +3 -2
  265. package/templates/app/api/user/plan-flags/route.ts +11 -24
  266. package/templates/app/api/user/profile/route.ts +7 -6
  267. package/templates/app/api/v1/[entity]/[id]/child/[childType]/[childId]/route.ts +16 -18
  268. package/templates/app/api/v1/[entity]/[id]/child/[childType]/route.ts +17 -19
  269. package/templates/app/api/v1/[entity]/[id]/route.ts +10 -12
  270. package/templates/app/api/v1/[entity]/route.ts +9 -11
  271. package/templates/app/api/v1/api-keys/[id]/route.ts +9 -8
  272. package/templates/app/api/v1/api-keys/route.ts +7 -6
  273. package/templates/app/api/v1/auth/signup-with-invite/route.ts +3 -2
  274. package/templates/app/api/v1/billing/cancel/route.ts +15 -14
  275. package/templates/app/api/v1/billing/change-plan/route.ts +10 -9
  276. package/templates/app/api/v1/billing/check-action/route.ts +8 -7
  277. package/templates/app/api/v1/billing/checkout/route.ts +10 -9
  278. package/templates/app/api/v1/billing/plans/route.ts +5 -4
  279. package/templates/app/api/v1/billing/portal/route.ts +9 -8
  280. package/templates/app/api/v1/blocks/[slug]/route.ts +4 -3
  281. package/templates/app/api/v1/blocks/route.ts +3 -2
  282. package/templates/app/api/v1/blocks/validate/route.ts +5 -3
  283. package/templates/app/api/v1/cron/process/route.ts +4 -6
  284. package/templates/app/api/v1/devtools/blocks/route.ts +3 -2
  285. package/templates/app/api/v1/devtools/docs/route.ts +3 -2
  286. package/templates/app/api/v1/devtools/features/route.ts +3 -2
  287. package/templates/app/api/v1/devtools/flows/route.ts +3 -2
  288. package/templates/app/api/v1/devtools/scheduled-actions/route.ts +125 -3
  289. package/templates/app/api/v1/devtools/scheduled-actions/run/route.ts +110 -0
  290. package/templates/app/api/v1/devtools/testing/route.ts +3 -2
  291. package/templates/app/api/v1/media/[id]/route.ts +144 -0
  292. package/templates/app/api/v1/media/[id]/tags/route.ts +154 -0
  293. package/templates/app/api/v1/media/check-duplicates/route.ts +56 -0
  294. package/templates/app/api/v1/media/route.ts +56 -0
  295. package/templates/app/api/v1/media/upload/route.ts +157 -33
  296. package/templates/app/api/v1/media-tags/route.ts +65 -0
  297. package/templates/app/api/v1/plugin/[...path]/route.ts +16 -15
  298. package/templates/app/api/v1/plugin/route.ts +3 -2
  299. package/templates/app/api/v1/post-categories/[id]/route.ts +10 -9
  300. package/templates/app/api/v1/post-categories/route.ts +5 -4
  301. package/templates/app/api/v1/team-invitations/[token]/accept/route.ts +3 -3
  302. package/templates/app/api/v1/team-invitations/[token]/decline/route.ts +3 -3
  303. package/templates/app/api/v1/team-invitations/[token]/route.ts +3 -2
  304. package/templates/app/api/v1/team-invitations/route.ts +3 -2
  305. package/templates/app/api/v1/teams/[teamId]/invitations/route.ts +5 -4
  306. package/templates/app/api/v1/teams/[teamId]/invoices/[invoiceNumber]/route.ts +3 -2
  307. package/templates/app/api/v1/teams/[teamId]/invoices/route.ts +3 -2
  308. package/templates/app/api/v1/teams/[teamId]/members/[memberId]/route.ts +5 -4
  309. package/templates/app/api/v1/teams/[teamId]/members/route.ts +5 -5
  310. package/templates/app/api/v1/teams/[teamId]/route.ts +31 -58
  311. package/templates/app/api/v1/teams/[teamId]/subscription/route.ts +3 -2
  312. package/templates/app/api/v1/teams/[teamId]/usage/[limitSlug]/route.ts +5 -4
  313. package/templates/app/api/v1/teams/route.ts +18 -17
  314. package/templates/app/api/v1/teams/switch/route.ts +3 -2
  315. package/templates/app/api/v1/theme/[...path]/route.ts +16 -15
  316. package/templates/app/api/v1/theme/route.ts +3 -2
  317. package/templates/app/api/v1/users/[id]/meta/[key]/route.ts +7 -6
  318. package/templates/app/api/v1/users/[id]/route.ts +9 -8
  319. package/templates/app/api/v1/users/route.ts +7 -6
  320. package/templates/app/dashboard/(main)/media/page.tsx +607 -0
  321. package/templates/contents/themes/starter/messages/de/dev.json +106 -0
  322. package/templates/contents/themes/starter/messages/de/index.ts +2 -0
  323. package/templates/contents/themes/starter/messages/en/dev.json +106 -0
  324. package/templates/contents/themes/starter/messages/en/index.ts +2 -0
  325. package/templates/contents/themes/starter/messages/es/dev.json +106 -0
  326. package/templates/contents/themes/starter/messages/es/index.ts +2 -0
  327. package/templates/contents/themes/starter/messages/fr/dev.json +106 -0
  328. package/templates/contents/themes/starter/messages/fr/index.ts +2 -0
  329. package/templates/contents/themes/starter/messages/it/dev.json +106 -0
  330. package/templates/contents/themes/starter/messages/it/index.ts +2 -0
  331. package/templates/contents/themes/starter/messages/pt/dev.json +106 -0
  332. package/templates/contents/themes/starter/messages/pt/index.ts +2 -0
  333. package/templates/contents/themes/starter/styles/globals.css +14 -0
  334. package/templates/instrumentation.ts +33 -0
  335. package/dist/presets/plugin/.env.example.template +0 -19
  336. package/dist/presets/plugin/entities/.gitkeep +0 -18
  337. package/dist/presets/theme/blocks/.gitkeep +0 -17
  338. package/dist/presets/theme/public/brand/.gitkeep +0 -8
  339. package/dist/presets/theme/tests/cypress/.gitkeep +0 -10
  340. package/dist/templates/contents/plugins/starter/plugin/.env.example.template +0 -19
  341. package/templates/contents/plugins/starter/plugin/.env.example.template +0 -19
@@ -0,0 +1,65 @@
1
+ import { NextRequest } from 'next/server'
2
+ import { authenticateRequest, hasRequiredScope } from '@nextsparkjs/core/lib/api/auth/dual-auth'
3
+ import { createApiResponse, createApiError } from '@nextsparkjs/core/lib/api/helpers'
4
+ import { withRateLimitTier } from '@nextsparkjs/core/lib/api/rate-limit'
5
+ import { MediaService } from '@nextsparkjs/core/lib/services/media.service'
6
+
7
+ /**
8
+ * GET /api/v1/media-tags
9
+ *
10
+ * List all available media tags (taxonomies of type 'media_tag').
11
+ *
12
+ * Authentication: Requires valid session or API key with media:read scope
13
+ */
14
+ export const GET = withRateLimitTier(async (request: NextRequest) => {
15
+ try {
16
+ const authResult = await authenticateRequest(request)
17
+ if (!authResult.success) {
18
+ return createApiError('Unauthorized', 401)
19
+ }
20
+
21
+ if (!hasRequiredScope(authResult, 'media:read')) {
22
+ return createApiError('Insufficient permissions', 403)
23
+ }
24
+
25
+ const tags = await MediaService.getTags(authResult.user!.id)
26
+ return createApiResponse(tags)
27
+ } catch (error) {
28
+ console.error('[Media Tags API] Error listing tags:', error)
29
+ return createApiError('Failed to list media tags', 500)
30
+ }
31
+ }, 'read')
32
+
33
+ /**
34
+ * POST /api/v1/media-tags
35
+ *
36
+ * Create a new media tag.
37
+ * Body: { name: string }
38
+ *
39
+ * Authentication: Requires valid session or API key with media:write scope
40
+ */
41
+ export const POST = withRateLimitTier(async (request: NextRequest) => {
42
+ try {
43
+ const authResult = await authenticateRequest(request)
44
+ if (!authResult.success) {
45
+ return createApiError('Unauthorized', 401)
46
+ }
47
+
48
+ if (!hasRequiredScope(authResult, 'media:write')) {
49
+ return createApiError('Insufficient permissions', 403)
50
+ }
51
+
52
+ const body = await request.json()
53
+ const { name } = body
54
+
55
+ if (!name || typeof name !== 'string' || !name.trim()) {
56
+ return createApiError('Tag name is required', 400)
57
+ }
58
+
59
+ const tag = await MediaService.createTag(name, authResult.user!.id)
60
+ return createApiResponse(tag, undefined, 201)
61
+ } catch (error) {
62
+ console.error('[Media Tags API] Error creating tag:', error)
63
+ return createApiError('Failed to create media tag', 500)
64
+ }
65
+ }, 'write')
@@ -8,46 +8,47 @@
8
8
  import { NextRequest, NextResponse } from 'next/server'
9
9
  import { PluginService, type RouteFileEndpoint, type PluginRegistryEntry } from '@nextsparkjs/core/lib/services'
10
10
  import { PLUGIN_REGISTRY } from '@nextsparkjs/registries/plugin-registry'
11
+ import { withRateLimitTier } from '@nextsparkjs/core/lib/api/rate-limit'
11
12
 
12
- export async function GET(
13
+ export const GET = withRateLimitTier(async (
13
14
  request: NextRequest,
14
15
  { params }: { params: Promise<{ path: string[] }> }
15
- ) {
16
+ ) => {
16
17
  const { path } = await params
17
18
  return handlePluginRequest(request, path, 'GET')
18
- }
19
+ }, 'read');
19
20
 
20
- export async function POST(
21
+ export const POST = withRateLimitTier(async (
21
22
  request: NextRequest,
22
23
  { params }: { params: Promise<{ path: string[] }> }
23
- ) {
24
+ ) => {
24
25
  const { path } = await params
25
26
  return handlePluginRequest(request, path, 'POST')
26
- }
27
+ }, 'write');
27
28
 
28
- export async function PUT(
29
+ export const PUT = withRateLimitTier(async (
29
30
  request: NextRequest,
30
31
  { params }: { params: Promise<{ path: string[] }> }
31
- ) {
32
+ ) => {
32
33
  const { path } = await params
33
34
  return handlePluginRequest(request, path, 'PUT')
34
- }
35
+ }, 'write');
35
36
 
36
- export async function DELETE(
37
+ export const DELETE = withRateLimitTier(async (
37
38
  request: NextRequest,
38
39
  { params }: { params: Promise<{ path: string[] }> }
39
- ) {
40
+ ) => {
40
41
  const { path } = await params
41
42
  return handlePluginRequest(request, path, 'DELETE')
42
- }
43
+ }, 'write');
43
44
 
44
- export async function PATCH(
45
+ export const PATCH = withRateLimitTier(async (
45
46
  request: NextRequest,
46
47
  { params }: { params: Promise<{ path: string[] }> }
47
- ) {
48
+ ) => {
48
49
  const { path } = await params
49
50
  return handlePluginRequest(request, path, 'PATCH')
50
- }
51
+ }, 'write');
51
52
 
52
53
  /**
53
54
  * Handle plugin API requests with nested paths
@@ -8,10 +8,11 @@
8
8
  import { NextResponse } from 'next/server'
9
9
  import { PluginService, type PluginRegistryEntry } from '@nextsparkjs/core/lib/services'
10
10
  import { PLUGIN_REGISTRY } from '@nextsparkjs/registries/plugin-registry'
11
+ import { withRateLimitTier } from '@nextsparkjs/core/lib/api/rate-limit'
11
12
 
12
- export async function GET() {
13
+ export const GET = withRateLimitTier(async () => {
13
14
  return listPluginsWithAPI()
14
- }
15
+ }, 'read');
15
16
 
16
17
  /**
17
18
  * List all plugins with their API status
@@ -2,6 +2,7 @@ import { NextRequest, NextResponse } from 'next/server'
2
2
  import { authenticateRequest } 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
+ import { withRateLimitTier } from '@nextsparkjs/core/lib/api/rate-limit'
5
6
 
6
7
  const updateCategorySchema = z.object({
7
8
  name: z.string().min(1).max(255).optional(),
@@ -16,10 +17,10 @@ const updateCategorySchema = z.object({
16
17
  })
17
18
 
18
19
  // GET /api/v1/post-categories/:id - Get category by ID
19
- export async function GET(
20
+ export const GET = withRateLimitTier(async (
20
21
  request: NextRequest,
21
22
  { params }: { params: Promise<{ id: string }> }
22
- ): Promise<NextResponse> {
23
+ ): Promise<NextResponse> => {
23
24
  try {
24
25
  const { id } = await params
25
26
 
@@ -43,13 +44,13 @@ export async function GET(
43
44
  console.error('Error in post-categories API:', err)
44
45
  return NextResponse.json({ success: false, error: 'Internal server error' }, { status: 500 })
45
46
  }
46
- }
47
+ }, 'read');
47
48
 
48
49
  // PUT /api/v1/post-categories/:id - Update category
49
- export async function PUT(
50
+ export const PUT = withRateLimitTier(async (
50
51
  request: NextRequest,
51
52
  { params }: { params: Promise<{ id: string }> }
52
- ): Promise<NextResponse> {
53
+ ): Promise<NextResponse> => {
53
54
  try {
54
55
  // Dual authentication: API key or session
55
56
  const authResult = await authenticateRequest(request)
@@ -183,13 +184,13 @@ export async function PUT(
183
184
  const errorMessage = err instanceof Error ? err.message : 'Internal server error'
184
185
  return NextResponse.json({ success: false, error: errorMessage }, { status: 500 })
185
186
  }
186
- }
187
+ }, 'write');
187
188
 
188
189
  // DELETE /api/v1/post-categories/:id - Delete category
189
- export async function DELETE(
190
+ export const DELETE = withRateLimitTier(async (
190
191
  request: NextRequest,
191
192
  { params }: { params: Promise<{ id: string }> }
192
- ): Promise<NextResponse> {
193
+ ): Promise<NextResponse> => {
193
194
  try {
194
195
  // Dual authentication: API key or session
195
196
  const authResult = await authenticateRequest(request)
@@ -252,4 +253,4 @@ export async function DELETE(
252
253
  console.error('Error in post-categories API DELETE:', err)
253
254
  return NextResponse.json({ success: false, error: 'Internal server error' }, { status: 500 })
254
255
  }
255
- }
256
+ }, 'write');
@@ -2,6 +2,7 @@ import { NextRequest, NextResponse } from 'next/server'
2
2
  import { authenticateRequest } 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
+ import { withRateLimitTier } from '@nextsparkjs/core/lib/api/rate-limit'
5
6
 
6
7
  const createCategorySchema = z.object({
7
8
  name: z.string().min(1).max(255),
@@ -26,7 +27,7 @@ function generateSlug(name: string): string {
26
27
  }
27
28
 
28
29
  // GET /api/v1/post-categories - List categories (filters taxonomies by type = 'post_category')
29
- export async function GET() {
30
+ export const GET = withRateLimitTier(async () => {
30
31
  try {
31
32
  const result = await dbQuery(
32
33
  `SELECT id, name, slug, description, icon, color, "parentId", "order", "isDefault", "isActive",
@@ -44,10 +45,10 @@ export async function GET() {
44
45
  console.error('Error fetching post categories:', error)
45
46
  return NextResponse.json({ success: false, error: 'Internal server error' }, { status: 500 })
46
47
  }
47
- }
48
+ }, 'read');
48
49
 
49
50
  // POST /api/v1/post-categories - Create category
50
- export async function POST(request: NextRequest) {
51
+ export const POST = withRateLimitTier(async (request: NextRequest) => {
51
52
  try {
52
53
  // Dual authentication: API key or session
53
54
  const authResult = await authenticateRequest(request)
@@ -116,4 +117,4 @@ export async function POST(request: NextRequest) {
116
117
  console.error('Error creating post category:', error)
117
118
  return NextResponse.json({ success: false, error: 'Internal server error' }, { status: 500 })
118
119
  }
119
- }
120
+ }, 'write');
@@ -8,7 +8,7 @@ import {
8
8
  addCorsHeaders,
9
9
  } from '@nextsparkjs/core/lib/api/helpers'
10
10
  import { authenticateRequest } from '@nextsparkjs/core/lib/api/auth/dual-auth'
11
- import { checkRateLimit } from '@nextsparkjs/core/lib/api/rate-limit'
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
 
@@ -18,7 +18,7 @@ export async function OPTIONS() {
18
18
  }
19
19
 
20
20
  // POST /api/v1/team-invitations/:token/accept - Accept invitation and become member
21
- export const POST = withApiLogging(
21
+ export const POST = withRateLimitTier(withApiLogging(
22
22
  async (req: NextRequest, { params }: { params: Promise<{ token: string }> }): Promise<NextResponse> => {
23
23
  try {
24
24
  // Authenticate using dual auth
@@ -176,4 +176,4 @@ export const POST = withApiLogging(
176
176
  return addCorsHeaders(response)
177
177
  }
178
178
  }
179
- )
179
+ ), 'write');
@@ -8,7 +8,7 @@ import {
8
8
  addCorsHeaders,
9
9
  } from '@nextsparkjs/core/lib/api/helpers'
10
10
  import { authenticateRequest } from '@nextsparkjs/core/lib/api/auth/dual-auth'
11
- import { checkRateLimit } from '@nextsparkjs/core/lib/api/rate-limit'
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
 
@@ -18,7 +18,7 @@ export async function OPTIONS() {
18
18
  }
19
19
 
20
20
  // POST /api/v1/team-invitations/:token/decline - Decline invitation
21
- export const POST = withApiLogging(
21
+ export const POST = withRateLimitTier(withApiLogging(
22
22
  async (req: NextRequest, { params }: { params: Promise<{ token: string }> }): Promise<NextResponse> => {
23
23
  try {
24
24
  // Authenticate using dual auth
@@ -117,4 +117,4 @@ export const POST = withApiLogging(
117
117
  return addCorsHeaders(response)
118
118
  }
119
119
  }
120
- )
120
+ ), 'write');
@@ -8,6 +8,7 @@ import {
8
8
  addCorsHeaders,
9
9
  } from '@nextsparkjs/core/lib/api/helpers'
10
10
  import type { TeamInvitation } from '@nextsparkjs/core/lib/teams/types'
11
+ import { withRateLimitTier } from '@nextsparkjs/core/lib/api/rate-limit'
11
12
 
12
13
  // Handle CORS preflight
13
14
  export async function OPTIONS() {
@@ -15,7 +16,7 @@ export async function OPTIONS() {
15
16
  }
16
17
 
17
18
  // GET /api/v1/team-invitations/:token - Get invitation details (public endpoint for preview)
18
- export const GET = withApiLogging(
19
+ export const GET = withRateLimitTier(withApiLogging(
19
20
  async (req: NextRequest, { params }: { params: Promise<{ token: string }> }): Promise<NextResponse> => {
20
21
  try {
21
22
  const { token } = await params
@@ -86,4 +87,4 @@ export const GET = withApiLogging(
86
87
  return addCorsHeaders(response)
87
88
  }
88
89
  }
89
- )
90
+ ), 'read');
@@ -11,6 +11,7 @@ import {
11
11
  import { authenticateRequest } 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
+ import { withRateLimitTier } from '@nextsparkjs/core/lib/api/rate-limit'
14
15
 
15
16
  // Handle CORS preflight
16
17
  export async function OPTIONS() {
@@ -18,7 +19,7 @@ export async function OPTIONS() {
18
19
  }
19
20
 
20
21
  // GET /api/v1/team-invitations - List pending invitations for current user
21
- export const GET = withApiLogging(async (req: NextRequest): Promise<NextResponse> => {
22
+ export const GET = withRateLimitTier(withApiLogging(async (req: NextRequest): Promise<NextResponse> => {
22
23
  try {
23
24
  // Authenticate using dual auth
24
25
  const authResult = await authenticateRequest(req)
@@ -111,4 +112,4 @@ export const GET = withApiLogging(async (req: NextRequest): Promise<NextResponse
111
112
  const response = createApiError('Internal server error', 500)
112
113
  return addCorsHeaders(response)
113
114
  }
114
- })
115
+ }), 'read');
@@ -9,6 +9,7 @@ import {
9
9
  addCorsHeaders,
10
10
  } from '@nextsparkjs/core/lib/api/helpers'
11
11
  import { authenticateRequest } from '@nextsparkjs/core/lib/api/auth/dual-auth'
12
+ import { withRateLimitTier } from '@nextsparkjs/core/lib/api/rate-limit'
12
13
  import { TeamMemberService, MembershipService } from '@nextsparkjs/core/lib/services'
13
14
  import type { TeamInvitation } from '@nextsparkjs/core/lib/teams/types'
14
15
 
@@ -18,7 +19,7 @@ export async function OPTIONS() {
18
19
  }
19
20
 
20
21
  // GET /api/v1/teams/:teamId/invitations - List pending invitations for a team
21
- export const GET = withApiLogging(
22
+ export const GET = withRateLimitTier(withApiLogging(
22
23
  async (req: NextRequest, { params }: { params: Promise<{ teamId: string }> }): Promise<NextResponse> => {
23
24
  try {
24
25
  // Authenticate using dual auth
@@ -98,10 +99,10 @@ export const GET = withApiLogging(
98
99
  return addCorsHeaders(response)
99
100
  }
100
101
  }
101
- )
102
+ ), 'read')
102
103
 
103
104
  // DELETE /api/v1/teams/:teamId/invitations/:invitationId - Cancel/revoke an invitation
104
- export const DELETE = withApiLogging(
105
+ export const DELETE = withRateLimitTier(withApiLogging(
105
106
  async (req: NextRequest, { params }: { params: Promise<{ teamId: string }> }): Promise<NextResponse> => {
106
107
  try {
107
108
  // Authenticate using dual auth
@@ -168,4 +169,4 @@ export const DELETE = withApiLogging(
168
169
  return addCorsHeaders(response)
169
170
  }
170
171
  }
171
- )
172
+ ), 'write')
@@ -8,6 +8,7 @@ import {
8
8
  addCorsHeaders,
9
9
  } from '@nextsparkjs/core/lib/api/helpers'
10
10
  import { authenticateRequest } from '@nextsparkjs/core/lib/api/auth/dual-auth'
11
+ import { withRateLimitTier } from '@nextsparkjs/core/lib/api/rate-limit'
11
12
  import { MembershipService } from '@nextsparkjs/core/lib/services'
12
13
  import type { InvoiceResponse } from '@nextsparkjs/core/lib/validation/invoices'
13
14
 
@@ -17,7 +18,7 @@ export async function OPTIONS() {
17
18
  }
18
19
 
19
20
  // GET /api/v1/teams/:teamId/invoices/:invoiceNumber - Get single invoice (owner only)
20
- export const GET = withApiLogging(
21
+ export const GET = withRateLimitTier(withApiLogging(
21
22
  async (
22
23
  req: NextRequest,
23
24
  { params }: { params: Promise<{ teamId: string; invoiceNumber: string }> }
@@ -102,4 +103,4 @@ export const GET = withApiLogging(
102
103
  return addCorsHeaders(response)
103
104
  }
104
105
  }
105
- )
106
+ ), 'read')
@@ -9,6 +9,7 @@ import {
9
9
  addCorsHeaders,
10
10
  } from '@nextsparkjs/core/lib/api/helpers'
11
11
  import { authenticateRequest } from '@nextsparkjs/core/lib/api/auth/dual-auth'
12
+ import { withRateLimitTier } from '@nextsparkjs/core/lib/api/rate-limit'
12
13
  import { MembershipService } from '@nextsparkjs/core/lib/services'
13
14
  import { invoiceQuerySchema } from '@nextsparkjs/core/lib/validation/invoices'
14
15
  import type { InvoiceResponse } from '@nextsparkjs/core/lib/validation/invoices'
@@ -19,7 +20,7 @@ export async function OPTIONS() {
19
20
  }
20
21
 
21
22
  // GET /api/v1/teams/:teamId/invoices - List team invoices (owner only)
22
- export const GET = withApiLogging(
23
+ export const GET = withRateLimitTier(withApiLogging(
23
24
  async (req: NextRequest, { params }: { params: Promise<{ teamId: string }> }): Promise<NextResponse> => {
24
25
  try {
25
26
  // Authenticate using dual auth (API key OR session)
@@ -122,4 +123,4 @@ export const GET = withApiLogging(
122
123
  return addCorsHeaders(response)
123
124
  }
124
125
  }
125
- )
126
+ ), 'read')
@@ -8,6 +8,7 @@ import {
8
8
  addCorsHeaders,
9
9
  } from '@nextsparkjs/core/lib/api/helpers'
10
10
  import { authenticateRequest } from '@nextsparkjs/core/lib/api/auth/dual-auth'
11
+ import { withRateLimitTier } from '@nextsparkjs/core/lib/api/rate-limit'
11
12
  import { updateMemberRoleSchema } from '@nextsparkjs/core/lib/teams/schema'
12
13
  import { MembershipService } from '@nextsparkjs/core/lib/services'
13
14
  import { validateRoleTransition, canManageRole } from '@nextsparkjs/core/lib/teams/permissions'
@@ -19,7 +20,7 @@ export async function OPTIONS() {
19
20
  }
20
21
 
21
22
  // PATCH /api/v1/teams/:teamId/members/:memberId - Update member role
22
- export const PATCH = withApiLogging(
23
+ export const PATCH = withRateLimitTier(withApiLogging(
23
24
  async (
24
25
  req: NextRequest,
25
26
  { params }: { params: Promise<{ teamId: string; memberId: string }> }
@@ -155,10 +156,10 @@ export const PATCH = withApiLogging(
155
156
  return addCorsHeaders(response)
156
157
  }
157
158
  }
158
- )
159
+ ), 'write')
159
160
 
160
161
  // DELETE /api/v1/teams/:teamId/members/:memberId - Remove member from team
161
- export const DELETE = withApiLogging(
162
+ export const DELETE = withRateLimitTier(withApiLogging(
162
163
  async (
163
164
  req: NextRequest,
164
165
  { params }: { params: Promise<{ teamId: string; memberId: string }> }
@@ -260,4 +261,4 @@ export const DELETE = withApiLogging(
260
261
  return addCorsHeaders(response)
261
262
  }
262
263
  }
263
- )
264
+ ), 'write')
@@ -10,7 +10,7 @@ import {
10
10
  } from '@nextsparkjs/core/lib/api/helpers'
11
11
  import { authenticateRequest } from '@nextsparkjs/core/lib/api/auth/dual-auth'
12
12
  import { isSuperAdmin } from '@nextsparkjs/core/lib/api/auth/permissions'
13
- import { checkRateLimit } from '@nextsparkjs/core/lib/api/rate-limit'
13
+ import { checkRateLimit, withRateLimitTier } from '@nextsparkjs/core/lib/api/rate-limit'
14
14
  import { RATE_LIMITS } from '@nextsparkjs/core/lib/api/keys'
15
15
  import { inviteMemberSchema, memberListQuerySchema } from '@nextsparkjs/core/lib/teams/schema'
16
16
  import { TeamMemberService, MembershipService } from '@nextsparkjs/core/lib/services'
@@ -37,7 +37,7 @@ export async function OPTIONS() {
37
37
  }
38
38
 
39
39
  // GET /api/v1/teams/:teamId/members - List team members
40
- export const GET = withApiLogging(
40
+ export const GET = withRateLimitTier(withApiLogging(
41
41
  async (req: NextRequest, { params }: { params: Promise<{ teamId: string }> }): Promise<NextResponse> => {
42
42
  try {
43
43
  // Authenticate using dual auth
@@ -159,10 +159,10 @@ export const GET = withApiLogging(
159
159
  return addCorsHeaders(response)
160
160
  }
161
161
  }
162
- )
162
+ ), 'read')
163
163
 
164
164
  // POST /api/v1/teams/:teamId/members - Invite new member (creates invitation)
165
- export const POST = withApiLogging(
165
+ export const POST = withRateLimitTier(withApiLogging(
166
166
  async (req: NextRequest, { params }: { params: Promise<{ teamId: string }> }): Promise<NextResponse> => {
167
167
  try {
168
168
  // Authenticate using dual auth
@@ -355,4 +355,4 @@ export const POST = withApiLogging(
355
355
  return addCorsHeaders(response)
356
356
  }
357
357
  }
358
- )
358
+ ), 'write')
@@ -8,7 +8,8 @@ import {
8
8
  addCorsHeaders,
9
9
  } from '@nextsparkjs/core/lib/api/helpers'
10
10
  import { authenticateRequest } from '@nextsparkjs/core/lib/api/auth/dual-auth'
11
- import { ownerUpdateTeamSchema, adminUpdateTeamSchema } from '@nextsparkjs/core/lib/teams/schema'
11
+ import { withRateLimitTier } from '@nextsparkjs/core/lib/api/rate-limit'
12
+ import { updateTeamSchema } from '@nextsparkjs/core/lib/teams/schema'
12
13
  import { TeamService, MembershipService } from '@nextsparkjs/core/lib/services'
13
14
  import type { Team, TeamRole } from '@nextsparkjs/core/lib/teams/types'
14
15
 
@@ -18,7 +19,7 @@ export async function OPTIONS() {
18
19
  }
19
20
 
20
21
  // GET /api/v1/teams/:teamId - Get team details
21
- export const GET = withApiLogging(
22
+ export const GET = withRateLimitTier(withApiLogging(
22
23
  async (req: NextRequest, { params }: { params: Promise<{ teamId: string }> }): Promise<NextResponse> => {
23
24
  try {
24
25
  // Authenticate using dual auth
@@ -87,10 +88,10 @@ export const GET = withApiLogging(
87
88
  return addCorsHeaders(response)
88
89
  }
89
90
  }
90
- )
91
+ ), 'read')
91
92
 
92
93
  // PATCH /api/v1/teams/:teamId - Update team (owners/admins only)
93
- export const PATCH = withApiLogging(
94
+ export const PATCH = withRateLimitTier(withApiLogging(
94
95
  async (req: NextRequest, { params }: { params: Promise<{ teamId: string }> }): Promise<NextResponse> => {
95
96
  try {
96
97
  // Authenticate using dual auth
@@ -115,56 +116,28 @@ export const PATCH = withApiLogging(
115
116
  return addCorsHeaders(response)
116
117
  }
117
118
 
118
- const body = await req.json()
119
-
120
- // FIX #2: Check owner-only requirement FIRST for name/description fields
121
- // This provides clearer error messages to users
122
- // Issue: https://github.com/NextSpark-js/nextspark/pull/1 (Issue #2)
123
- // FIX #1: Use 'in' operator to check property existence (not value truthiness)
124
- // This prevents type coercion bypass with falsy values (empty string, null, 0, false)
125
- // Issue: https://github.com/NextSpark-js/nextspark/pull/1 (Issue #1)
126
- const isOwnerOnlyUpdate = 'name' in body || 'description' in body
127
-
128
- if (isOwnerOnlyUpdate) {
129
- // Fetch team to verify ownership BEFORE other checks
130
- const team = await TeamService.getById(teamId, authResult.user!.id)
131
-
132
- if (!team || team.ownerId !== authResult.user!.id) {
133
- const response = createApiError(
134
- 'Only team owners can edit team name and description',
135
- 403,
136
- null,
137
- 'OWNER_ONLY'
138
- )
139
- return addCorsHeaders(response)
140
- }
141
-
142
- // Proceed with owner update (skip general permission check)
143
- } else {
144
- // Check general teams.update permission for non-owner-only fields
145
- const membership = await MembershipService.get(authResult.user!.id, teamId)
146
- const actionResult = membership.canPerformAction('teams.update')
147
-
148
- if (!actionResult.allowed) {
149
- const response = NextResponse.json(
150
- {
151
- success: false,
152
- error: actionResult.message,
153
- reason: actionResult.reason,
154
- meta: actionResult.meta,
155
- },
156
- { status: 403 }
157
- )
158
- return addCorsHeaders(response)
159
- }
119
+ // Check if user has permission to edit team using MembershipService
120
+ const membership = await MembershipService.get(authResult.user!.id, teamId)
121
+ const actionResult = membership.canPerformAction('teams.update')
122
+
123
+ if (!actionResult.allowed) {
124
+ const response = NextResponse.json(
125
+ {
126
+ success: false,
127
+ error: actionResult.message,
128
+ reason: actionResult.reason,
129
+ meta: actionResult.meta,
130
+ },
131
+ { status: 403 }
132
+ )
133
+ return addCorsHeaders(response)
160
134
  }
161
135
 
162
- // Validate with appropriate schema based on update type
163
- const schema = isOwnerOnlyUpdate ? ownerUpdateTeamSchema : adminUpdateTeamSchema
164
- const validatedData = schema.parse(body) as Record<string, unknown>
136
+ const body = await req.json()
137
+ const validatedData = updateTeamSchema.parse(body)
165
138
 
166
139
  // Check if slug is being changed and if it's available
167
- if ('slug' in validatedData && typeof validatedData.slug === 'string') {
140
+ if (validatedData.slug) {
168
141
  const slugAvailable = await TeamService.isSlugAvailable(validatedData.slug, teamId)
169
142
  if (!slugAvailable) {
170
143
  const response = createApiError('Team slug already exists', 409, null, 'SLUG_EXISTS')
@@ -177,27 +150,27 @@ export const PATCH = withApiLogging(
177
150
  const values = []
178
151
  let paramCount = 1
179
152
 
180
- if ('name' in validatedData && validatedData.name !== undefined) {
153
+ if (validatedData.name !== undefined) {
181
154
  updates.push(`name = $${paramCount++}`)
182
155
  values.push(validatedData.name)
183
156
  }
184
157
 
185
- if ('slug' in validatedData && validatedData.slug !== undefined) {
158
+ if (validatedData.slug !== undefined) {
186
159
  updates.push(`slug = $${paramCount++}`)
187
160
  values.push(validatedData.slug)
188
161
  }
189
162
 
190
- if ('description' in validatedData && validatedData.description !== undefined) {
163
+ if (validatedData.description !== undefined) {
191
164
  updates.push(`description = $${paramCount++}`)
192
165
  values.push(validatedData.description)
193
166
  }
194
167
 
195
- if ('avatarUrl' in validatedData && validatedData.avatarUrl !== undefined) {
168
+ if (validatedData.avatarUrl !== undefined) {
196
169
  updates.push(`"avatarUrl" = $${paramCount++}`)
197
170
  values.push(validatedData.avatarUrl)
198
171
  }
199
172
 
200
- if ('settings' in validatedData && validatedData.settings !== undefined) {
173
+ if (validatedData.settings !== undefined) {
201
174
  updates.push(`settings = $${paramCount++}`)
202
175
  values.push(JSON.stringify(validatedData.settings))
203
176
  }
@@ -256,10 +229,10 @@ export const PATCH = withApiLogging(
256
229
  return addCorsHeaders(response)
257
230
  }
258
231
  }
259
- )
232
+ ), 'write')
260
233
 
261
234
  // DELETE /api/v1/teams/:teamId - Delete team (owners only, NOT personal teams)
262
- export const DELETE = withApiLogging(
235
+ export const DELETE = withRateLimitTier(withApiLogging(
263
236
  async (req: NextRequest, { params }: { params: Promise<{ teamId: string }> }): Promise<NextResponse> => {
264
237
  try {
265
238
  // Authenticate using dual auth
@@ -319,4 +292,4 @@ export const DELETE = withApiLogging(
319
292
  return addCorsHeaders(response)
320
293
  }
321
294
  }
322
- )
295
+ ), 'write')