@prenta/core 0.118.0

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 (1408) hide show
  1. package/CHANGELOG.md +2974 -0
  2. package/LICENSE +21 -0
  3. package/README.md +33 -0
  4. package/dist/a11y/index.d.ts +25 -0
  5. package/dist/a11y/index.d.ts.map +1 -0
  6. package/dist/a11y/index.js +91 -0
  7. package/dist/a11y/index.js.map +1 -0
  8. package/dist/actions.d.ts +130 -0
  9. package/dist/actions.d.ts.map +1 -0
  10. package/dist/actions.js +1324 -0
  11. package/dist/actions.js.map +1 -0
  12. package/dist/ai/brand-voice-alignment.d.ts +17 -0
  13. package/dist/ai/brand-voice-alignment.d.ts.map +1 -0
  14. package/dist/ai/brand-voice-alignment.js +40 -0
  15. package/dist/ai/brand-voice-alignment.js.map +1 -0
  16. package/dist/ai/brand-voice-analyze.d.ts +71 -0
  17. package/dist/ai/brand-voice-analyze.d.ts.map +1 -0
  18. package/dist/ai/brand-voice-analyze.js +151 -0
  19. package/dist/ai/brand-voice-analyze.js.map +1 -0
  20. package/dist/ai/brand-voice-document.d.ts +56 -0
  21. package/dist/ai/brand-voice-document.d.ts.map +1 -0
  22. package/dist/ai/brand-voice-document.js +335 -0
  23. package/dist/ai/brand-voice-document.js.map +1 -0
  24. package/dist/ai/brand-voice-fix.d.ts +26 -0
  25. package/dist/ai/brand-voice-fix.d.ts.map +1 -0
  26. package/dist/ai/brand-voice-fix.js +97 -0
  27. package/dist/ai/brand-voice-fix.js.map +1 -0
  28. package/dist/ai/brand-voice-save.d.ts +11 -0
  29. package/dist/ai/brand-voice-save.d.ts.map +1 -0
  30. package/dist/ai/brand-voice-save.js +51 -0
  31. package/dist/ai/brand-voice-save.js.map +1 -0
  32. package/dist/ai/brand-voice-sources.d.ts +16 -0
  33. package/dist/ai/brand-voice-sources.d.ts.map +1 -0
  34. package/dist/ai/brand-voice-sources.js +106 -0
  35. package/dist/ai/brand-voice-sources.js.map +1 -0
  36. package/dist/ai/brand-voice-structured.d.ts +87 -0
  37. package/dist/ai/brand-voice-structured.d.ts.map +1 -0
  38. package/dist/ai/brand-voice-structured.js +438 -0
  39. package/dist/ai/brand-voice-structured.js.map +1 -0
  40. package/dist/ai/brand-voice.d.ts +40 -0
  41. package/dist/ai/brand-voice.d.ts.map +1 -0
  42. package/dist/ai/brand-voice.js +103 -0
  43. package/dist/ai/brand-voice.js.map +1 -0
  44. package/dist/ai/catalog.d.ts +35 -0
  45. package/dist/ai/catalog.d.ts.map +1 -0
  46. package/dist/ai/catalog.js +370 -0
  47. package/dist/ai/catalog.js.map +1 -0
  48. package/dist/ai/config-store.d.ts +55 -0
  49. package/dist/ai/config-store.d.ts.map +1 -0
  50. package/dist/ai/config-store.js +177 -0
  51. package/dist/ai/config-store.js.map +1 -0
  52. package/dist/ai/content-writing-generate.d.ts +19 -0
  53. package/dist/ai/content-writing-generate.d.ts.map +1 -0
  54. package/dist/ai/content-writing-generate.js +64 -0
  55. package/dist/ai/content-writing-generate.js.map +1 -0
  56. package/dist/ai/feature-defaults.d.ts +20 -0
  57. package/dist/ai/feature-defaults.d.ts.map +1 -0
  58. package/dist/ai/feature-defaults.js +104 -0
  59. package/dist/ai/feature-defaults.js.map +1 -0
  60. package/dist/ai/index.d.ts +24 -0
  61. package/dist/ai/index.d.ts.map +1 -0
  62. package/dist/ai/index.js +24 -0
  63. package/dist/ai/index.js.map +1 -0
  64. package/dist/ai/models.d.ts +37 -0
  65. package/dist/ai/models.d.ts.map +1 -0
  66. package/dist/ai/models.js +126 -0
  67. package/dist/ai/models.js.map +1 -0
  68. package/dist/ai/providers/anthropic.d.ts +10 -0
  69. package/dist/ai/providers/anthropic.d.ts.map +1 -0
  70. package/dist/ai/providers/anthropic.js +63 -0
  71. package/dist/ai/providers/anthropic.js.map +1 -0
  72. package/dist/ai/providers/custom.d.ts +12 -0
  73. package/dist/ai/providers/custom.d.ts.map +1 -0
  74. package/dist/ai/providers/custom.js +76 -0
  75. package/dist/ai/providers/custom.js.map +1 -0
  76. package/dist/ai/providers/http.d.ts +66 -0
  77. package/dist/ai/providers/http.d.ts.map +1 -0
  78. package/dist/ai/providers/http.js +185 -0
  79. package/dist/ai/providers/http.js.map +1 -0
  80. package/dist/ai/providers/index.d.ts +18 -0
  81. package/dist/ai/providers/index.d.ts.map +1 -0
  82. package/dist/ai/providers/index.js +28 -0
  83. package/dist/ai/providers/index.js.map +1 -0
  84. package/dist/ai/providers/openai.d.ts +11 -0
  85. package/dist/ai/providers/openai.d.ts.map +1 -0
  86. package/dist/ai/providers/openai.js +44 -0
  87. package/dist/ai/providers/openai.js.map +1 -0
  88. package/dist/ai/providers/xai.d.ts +10 -0
  89. package/dist/ai/providers/xai.d.ts.map +1 -0
  90. package/dist/ai/providers/xai.js +38 -0
  91. package/dist/ai/providers/xai.js.map +1 -0
  92. package/dist/ai/resolve.d.ts +41 -0
  93. package/dist/ai/resolve.d.ts.map +1 -0
  94. package/dist/ai/resolve.js +143 -0
  95. package/dist/ai/resolve.js.map +1 -0
  96. package/dist/ai/runtime.d.ts +88 -0
  97. package/dist/ai/runtime.d.ts.map +1 -0
  98. package/dist/ai/runtime.js +192 -0
  99. package/dist/ai/runtime.js.map +1 -0
  100. package/dist/ai/sampling.d.ts +17 -0
  101. package/dist/ai/sampling.d.ts.map +1 -0
  102. package/dist/ai/sampling.js +51 -0
  103. package/dist/ai/sampling.js.map +1 -0
  104. package/dist/ai/types.d.ts +193 -0
  105. package/dist/ai/types.d.ts.map +1 -0
  106. package/dist/ai/types.js +11 -0
  107. package/dist/ai/types.js.map +1 -0
  108. package/dist/ai/usage.d.ts +18 -0
  109. package/dist/ai/usage.d.ts.map +1 -0
  110. package/dist/ai/usage.js +70 -0
  111. package/dist/ai/usage.js.map +1 -0
  112. package/dist/api/collection-introspection.d.ts +46 -0
  113. package/dist/api/collection-introspection.d.ts.map +1 -0
  114. package/dist/api/collection-introspection.js +82 -0
  115. package/dist/api/collection-introspection.js.map +1 -0
  116. package/dist/api/guarded-db.d.ts +14 -0
  117. package/dist/api/guarded-db.d.ts.map +1 -0
  118. package/dist/api/guarded-db.js +62 -0
  119. package/dist/api/guarded-db.js.map +1 -0
  120. package/dist/api/handler-factory.d.ts +7 -0
  121. package/dist/api/handler-factory.d.ts.map +1 -0
  122. package/dist/api/handler-factory.js +380 -0
  123. package/dist/api/handler-factory.js.map +1 -0
  124. package/dist/api/handlers.d.ts +8 -0
  125. package/dist/api/handlers.d.ts.map +1 -0
  126. package/dist/api/handlers.js +62 -0
  127. package/dist/api/handlers.js.map +1 -0
  128. package/dist/api/index.d.ts +8 -0
  129. package/dist/api/index.d.ts.map +1 -0
  130. package/dist/api/index.js +8 -0
  131. package/dist/api/index.js.map +1 -0
  132. package/dist/api/openapi.d.ts +3 -0
  133. package/dist/api/openapi.d.ts.map +1 -0
  134. package/dist/api/openapi.js +539 -0
  135. package/dist/api/openapi.js.map +1 -0
  136. package/dist/api/route-helpers.d.ts +369 -0
  137. package/dist/api/route-helpers.d.ts.map +1 -0
  138. package/dist/api/route-helpers.js +1574 -0
  139. package/dist/api/route-helpers.js.map +1 -0
  140. package/dist/api/router.d.ts +21 -0
  141. package/dist/api/router.d.ts.map +1 -0
  142. package/dist/api/router.js +75 -0
  143. package/dist/api/router.js.map +1 -0
  144. package/dist/api/routes/ai-seo.d.ts +3 -0
  145. package/dist/api/routes/ai-seo.d.ts.map +1 -0
  146. package/dist/api/routes/ai-seo.js +482 -0
  147. package/dist/api/routes/ai-seo.js.map +1 -0
  148. package/dist/api/routes/ai-settings.d.ts +3 -0
  149. package/dist/api/routes/ai-settings.d.ts.map +1 -0
  150. package/dist/api/routes/ai-settings.js +1078 -0
  151. package/dist/api/routes/ai-settings.js.map +1 -0
  152. package/dist/api/routes/auth.d.ts +3 -0
  153. package/dist/api/routes/auth.d.ts.map +1 -0
  154. package/dist/api/routes/auth.js +877 -0
  155. package/dist/api/routes/auth.js.map +1 -0
  156. package/dist/api/routes/collab.d.ts +3 -0
  157. package/dist/api/routes/collab.d.ts.map +1 -0
  158. package/dist/api/routes/collab.js +426 -0
  159. package/dist/api/routes/collab.js.map +1 -0
  160. package/dist/api/routes/dashboard.d.ts +7 -0
  161. package/dist/api/routes/dashboard.d.ts.map +1 -0
  162. package/dist/api/routes/dashboard.js +540 -0
  163. package/dist/api/routes/dashboard.js.map +1 -0
  164. package/dist/api/routes/documents.d.ts +11 -0
  165. package/dist/api/routes/documents.d.ts.map +1 -0
  166. package/dist/api/routes/documents.js +340 -0
  167. package/dist/api/routes/documents.js.map +1 -0
  168. package/dist/api/routes/email-settings.d.ts +3 -0
  169. package/dist/api/routes/email-settings.d.ts.map +1 -0
  170. package/dist/api/routes/email-settings.js +248 -0
  171. package/dist/api/routes/email-settings.js.map +1 -0
  172. package/dist/api/routes/folders.d.ts +3 -0
  173. package/dist/api/routes/folders.d.ts.map +1 -0
  174. package/dist/api/routes/folders.js +195 -0
  175. package/dist/api/routes/folders.js.map +1 -0
  176. package/dist/api/routes/forms.d.ts +3 -0
  177. package/dist/api/routes/forms.d.ts.map +1 -0
  178. package/dist/api/routes/forms.js +776 -0
  179. package/dist/api/routes/forms.js.map +1 -0
  180. package/dist/api/routes/globals.d.ts +3 -0
  181. package/dist/api/routes/globals.d.ts.map +1 -0
  182. package/dist/api/routes/globals.js +157 -0
  183. package/dist/api/routes/globals.js.map +1 -0
  184. package/dist/api/routes/media.d.ts +3 -0
  185. package/dist/api/routes/media.d.ts.map +1 -0
  186. package/dist/api/routes/media.js +807 -0
  187. package/dist/api/routes/media.js.map +1 -0
  188. package/dist/api/routes/meta.d.ts +3 -0
  189. package/dist/api/routes/meta.d.ts.map +1 -0
  190. package/dist/api/routes/meta.js +97 -0
  191. package/dist/api/routes/meta.js.map +1 -0
  192. package/dist/api/routes/navigations.d.ts +7 -0
  193. package/dist/api/routes/navigations.d.ts.map +1 -0
  194. package/dist/api/routes/navigations.js +81 -0
  195. package/dist/api/routes/navigations.js.map +1 -0
  196. package/dist/api/routes/page-templates.d.ts +3 -0
  197. package/dist/api/routes/page-templates.d.ts.map +1 -0
  198. package/dist/api/routes/page-templates.js +265 -0
  199. package/dist/api/routes/page-templates.js.map +1 -0
  200. package/dist/api/routes/presence.d.ts +3 -0
  201. package/dist/api/routes/presence.d.ts.map +1 -0
  202. package/dist/api/routes/presence.js +35 -0
  203. package/dist/api/routes/presence.js.map +1 -0
  204. package/dist/api/routes/preview.d.ts +3 -0
  205. package/dist/api/routes/preview.d.ts.map +1 -0
  206. package/dist/api/routes/preview.js +111 -0
  207. package/dist/api/routes/preview.js.map +1 -0
  208. package/dist/api/routes/redirects.d.ts +3 -0
  209. package/dist/api/routes/redirects.d.ts.map +1 -0
  210. package/dist/api/routes/redirects.js +937 -0
  211. package/dist/api/routes/redirects.js.map +1 -0
  212. package/dist/api/routes/saved-sections.d.ts +3 -0
  213. package/dist/api/routes/saved-sections.d.ts.map +1 -0
  214. package/dist/api/routes/saved-sections.js +1071 -0
  215. package/dist/api/routes/saved-sections.js.map +1 -0
  216. package/dist/api/routes/scheduling.d.ts +3 -0
  217. package/dist/api/routes/scheduling.d.ts.map +1 -0
  218. package/dist/api/routes/scheduling.js +391 -0
  219. package/dist/api/routes/scheduling.js.map +1 -0
  220. package/dist/api/routes/sections.d.ts +3 -0
  221. package/dist/api/routes/sections.d.ts.map +1 -0
  222. package/dist/api/routes/sections.js +512 -0
  223. package/dist/api/routes/sections.js.map +1 -0
  224. package/dist/api/routes/security-center.d.ts +3 -0
  225. package/dist/api/routes/security-center.d.ts.map +1 -0
  226. package/dist/api/routes/security-center.js +71 -0
  227. package/dist/api/routes/security-center.js.map +1 -0
  228. package/dist/api/routes/seo-public.d.ts +14 -0
  229. package/dist/api/routes/seo-public.d.ts.map +1 -0
  230. package/dist/api/routes/seo-public.js +1366 -0
  231. package/dist/api/routes/seo-public.js.map +1 -0
  232. package/dist/api/routes/seo.d.ts +8 -0
  233. package/dist/api/routes/seo.d.ts.map +1 -0
  234. package/dist/api/routes/seo.js +2256 -0
  235. package/dist/api/routes/seo.js.map +1 -0
  236. package/dist/api/routes/system.d.ts +3 -0
  237. package/dist/api/routes/system.d.ts.map +1 -0
  238. package/dist/api/routes/system.js +353 -0
  239. package/dist/api/routes/system.js.map +1 -0
  240. package/dist/api/routes/url-resolution.d.ts +3 -0
  241. package/dist/api/routes/url-resolution.d.ts.map +1 -0
  242. package/dist/api/routes/url-resolution.js +522 -0
  243. package/dist/api/routes/url-resolution.js.map +1 -0
  244. package/dist/api/routes/users.d.ts +3 -0
  245. package/dist/api/routes/users.d.ts.map +1 -0
  246. package/dist/api/routes/users.js +1177 -0
  247. package/dist/api/routes/users.js.map +1 -0
  248. package/dist/api/routes/webhooks.d.ts +3 -0
  249. package/dist/api/routes/webhooks.d.ts.map +1 -0
  250. package/dist/api/routes/webhooks.js +422 -0
  251. package/dist/api/routes/webhooks.js.map +1 -0
  252. package/dist/appearance/color.d.ts +24 -0
  253. package/dist/appearance/color.d.ts.map +1 -0
  254. package/dist/appearance/color.js +90 -0
  255. package/dist/appearance/color.js.map +1 -0
  256. package/dist/appearance/favicon-icon.d.ts +12 -0
  257. package/dist/appearance/favicon-icon.d.ts.map +1 -0
  258. package/dist/appearance/favicon-icon.js +58 -0
  259. package/dist/appearance/favicon-icon.js.map +1 -0
  260. package/dist/appearance/favicon-paths.d.ts +6 -0
  261. package/dist/appearance/favicon-paths.d.ts.map +1 -0
  262. package/dist/appearance/favicon-paths.js +6 -0
  263. package/dist/appearance/favicon-paths.js.map +1 -0
  264. package/dist/appearance/index.d.ts +15 -0
  265. package/dist/appearance/index.d.ts.map +1 -0
  266. package/dist/appearance/index.js +7 -0
  267. package/dist/appearance/index.js.map +1 -0
  268. package/dist/appearance/logo-shape.d.ts +26 -0
  269. package/dist/appearance/logo-shape.d.ts.map +1 -0
  270. package/dist/appearance/logo-shape.js +62 -0
  271. package/dist/appearance/logo-shape.js.map +1 -0
  272. package/dist/appearance/resolve.d.ts +52 -0
  273. package/dist/appearance/resolve.d.ts.map +1 -0
  274. package/dist/appearance/resolve.js +219 -0
  275. package/dist/appearance/resolve.js.map +1 -0
  276. package/dist/appearance/theme-tokens.d.ts +49 -0
  277. package/dist/appearance/theme-tokens.d.ts.map +1 -0
  278. package/dist/appearance/theme-tokens.js +229 -0
  279. package/dist/appearance/theme-tokens.js.map +1 -0
  280. package/dist/appearance/types.d.ts +110 -0
  281. package/dist/appearance/types.d.ts.map +1 -0
  282. package/dist/appearance/types.js +16 -0
  283. package/dist/appearance/types.js.map +1 -0
  284. package/dist/astro-cache.d.ts +80 -0
  285. package/dist/astro-cache.d.ts.map +1 -0
  286. package/dist/astro-cache.js +108 -0
  287. package/dist/astro-cache.js.map +1 -0
  288. package/dist/astro-content.d.ts +81 -0
  289. package/dist/astro-content.d.ts.map +1 -0
  290. package/dist/astro-content.js +151 -0
  291. package/dist/astro-content.js.map +1 -0
  292. package/dist/astro-redirects.d.ts +38 -0
  293. package/dist/astro-redirects.d.ts.map +1 -0
  294. package/dist/astro-redirects.js +45 -0
  295. package/dist/astro-redirects.js.map +1 -0
  296. package/dist/astro-security.d.ts +34 -0
  297. package/dist/astro-security.d.ts.map +1 -0
  298. package/dist/astro-security.js +31 -0
  299. package/dist/astro-security.js.map +1 -0
  300. package/dist/astro-site.d.ts +69 -0
  301. package/dist/astro-site.d.ts.map +1 -0
  302. package/dist/astro-site.js +81 -0
  303. package/dist/astro-site.js.map +1 -0
  304. package/dist/auth/index.d.ts +17 -0
  305. package/dist/auth/index.d.ts.map +1 -0
  306. package/dist/auth/index.js +12 -0
  307. package/dist/auth/index.js.map +1 -0
  308. package/dist/auth/invite-email.d.ts +12 -0
  309. package/dist/auth/invite-email.d.ts.map +1 -0
  310. package/dist/auth/invite-email.js +57 -0
  311. package/dist/auth/invite-email.js.map +1 -0
  312. package/dist/auth/invites.d.ts +72 -0
  313. package/dist/auth/invites.d.ts.map +1 -0
  314. package/dist/auth/invites.js +279 -0
  315. package/dist/auth/invites.js.map +1 -0
  316. package/dist/auth/mfa-pending.d.ts +24 -0
  317. package/dist/auth/mfa-pending.d.ts.map +1 -0
  318. package/dist/auth/mfa-pending.js +38 -0
  319. package/dist/auth/mfa-pending.js.map +1 -0
  320. package/dist/auth/oauth.d.ts +139 -0
  321. package/dist/auth/oauth.d.ts.map +1 -0
  322. package/dist/auth/oauth.js +376 -0
  323. package/dist/auth/oauth.js.map +1 -0
  324. package/dist/auth/password.d.ts +57 -0
  325. package/dist/auth/password.d.ts.map +1 -0
  326. package/dist/auth/password.js +150 -0
  327. package/dist/auth/password.js.map +1 -0
  328. package/dist/auth/providers/github.d.ts +9 -0
  329. package/dist/auth/providers/github.d.ts.map +1 -0
  330. package/dist/auth/providers/github.js +10 -0
  331. package/dist/auth/providers/github.js.map +1 -0
  332. package/dist/auth/providers/google.d.ts +9 -0
  333. package/dist/auth/providers/google.d.ts.map +1 -0
  334. package/dist/auth/providers/google.js +10 -0
  335. package/dist/auth/providers/google.js.map +1 -0
  336. package/dist/auth/providers/microsoft.d.ts +9 -0
  337. package/dist/auth/providers/microsoft.d.ts.map +1 -0
  338. package/dist/auth/providers/microsoft.js +11 -0
  339. package/dist/auth/providers/microsoft.js.map +1 -0
  340. package/dist/auth/reset-email.d.ts +10 -0
  341. package/dist/auth/reset-email.d.ts.map +1 -0
  342. package/dist/auth/reset-email.js +30 -0
  343. package/dist/auth/reset-email.js.map +1 -0
  344. package/dist/auth/reset.d.ts +42 -0
  345. package/dist/auth/reset.d.ts.map +1 -0
  346. package/dist/auth/reset.js +142 -0
  347. package/dist/auth/reset.js.map +1 -0
  348. package/dist/auth/roles.d.ts +51 -0
  349. package/dist/auth/roles.d.ts.map +1 -0
  350. package/dist/auth/roles.js +159 -0
  351. package/dist/auth/roles.js.map +1 -0
  352. package/dist/auth/session.d.ts +38 -0
  353. package/dist/auth/session.d.ts.map +1 -0
  354. package/dist/auth/session.js +106 -0
  355. package/dist/auth/session.js.map +1 -0
  356. package/dist/auth/team.d.ts +99 -0
  357. package/dist/auth/team.d.ts.map +1 -0
  358. package/dist/auth/team.js +327 -0
  359. package/dist/auth/team.js.map +1 -0
  360. package/dist/auth/totp.d.ts +31 -0
  361. package/dist/auth/totp.d.ts.map +1 -0
  362. package/dist/auth/totp.js +134 -0
  363. package/dist/auth/totp.js.map +1 -0
  364. package/dist/auth/user-admin.d.ts +119 -0
  365. package/dist/auth/user-admin.d.ts.map +1 -0
  366. package/dist/auth/user-admin.js +292 -0
  367. package/dist/auth/user-admin.js.map +1 -0
  368. package/dist/backup/index.d.ts +19 -0
  369. package/dist/backup/index.d.ts.map +1 -0
  370. package/dist/backup/index.js +24 -0
  371. package/dist/backup/index.js.map +1 -0
  372. package/dist/billing/stripe-meter.d.ts +22 -0
  373. package/dist/billing/stripe-meter.d.ts.map +1 -0
  374. package/dist/billing/stripe-meter.js +101 -0
  375. package/dist/billing/stripe-meter.js.map +1 -0
  376. package/dist/brand-voice/index.d.ts +7 -0
  377. package/dist/brand-voice/index.d.ts.map +1 -0
  378. package/dist/brand-voice/index.js +7 -0
  379. package/dist/brand-voice/index.js.map +1 -0
  380. package/dist/cache/http.d.ts +140 -0
  381. package/dist/cache/http.d.ts.map +1 -0
  382. package/dist/cache/http.js +213 -0
  383. package/dist/cache/http.js.map +1 -0
  384. package/dist/cache/index.d.ts +15 -0
  385. package/dist/cache/index.d.ts.map +1 -0
  386. package/dist/cache/index.js +32 -0
  387. package/dist/cache/index.js.map +1 -0
  388. package/dist/client.d.ts +51 -0
  389. package/dist/client.d.ts.map +1 -0
  390. package/dist/client.js +59 -0
  391. package/dist/client.js.map +1 -0
  392. package/dist/codegen/index.d.ts +4 -0
  393. package/dist/codegen/index.d.ts.map +1 -0
  394. package/dist/codegen/index.js +366 -0
  395. package/dist/codegen/index.js.map +1 -0
  396. package/dist/collections/index.d.ts +19 -0
  397. package/dist/collections/index.d.ts.map +1 -0
  398. package/dist/collections/index.js +30 -0
  399. package/dist/collections/index.js.map +1 -0
  400. package/dist/collections/presets.d.ts +90 -0
  401. package/dist/collections/presets.d.ts.map +1 -0
  402. package/dist/collections/presets.js +573 -0
  403. package/dist/collections/presets.js.map +1 -0
  404. package/dist/config/define.d.ts +8 -0
  405. package/dist/config/define.d.ts.map +1 -0
  406. package/dist/config/define.js +7 -0
  407. package/dist/config/define.js.map +1 -0
  408. package/dist/config/index.d.ts +31 -0
  409. package/dist/config/index.d.ts.map +1 -0
  410. package/dist/config/index.js +196 -0
  411. package/dist/config/index.js.map +1 -0
  412. package/dist/config/runtime.d.ts +107 -0
  413. package/dist/config/runtime.d.ts.map +1 -0
  414. package/dist/config/runtime.js +77 -0
  415. package/dist/config/runtime.js.map +1 -0
  416. package/dist/config/types.d.ts +924 -0
  417. package/dist/config/types.d.ts.map +1 -0
  418. package/dist/config/types.js +2 -0
  419. package/dist/config/types.js.map +1 -0
  420. package/dist/content/ai-api.d.ts +21 -0
  421. package/dist/content/ai-api.d.ts.map +1 -0
  422. package/dist/content/ai-api.js +25 -0
  423. package/dist/content/ai-api.js.map +1 -0
  424. package/dist/content/content-graph.d.ts +25 -0
  425. package/dist/content/content-graph.d.ts.map +1 -0
  426. package/dist/content/content-graph.js +40 -0
  427. package/dist/content/content-graph.js.map +1 -0
  428. package/dist/content/extract.d.ts +7 -0
  429. package/dist/content/extract.d.ts.map +1 -0
  430. package/dist/content/extract.js +33 -0
  431. package/dist/content/extract.js.map +1 -0
  432. package/dist/content/index.d.ts +9 -0
  433. package/dist/content/index.d.ts.map +1 -0
  434. package/dist/content/index.js +6 -0
  435. package/dist/content/index.js.map +1 -0
  436. package/dist/content/slug.d.ts +40 -0
  437. package/dist/content/slug.d.ts.map +1 -0
  438. package/dist/content/slug.js +78 -0
  439. package/dist/content/slug.js.map +1 -0
  440. package/dist/content/structured-data.d.ts +163 -0
  441. package/dist/content/structured-data.d.ts.map +1 -0
  442. package/dist/content/structured-data.js +479 -0
  443. package/dist/content/structured-data.js.map +1 -0
  444. package/dist/content-health/cron.d.ts +68 -0
  445. package/dist/content-health/cron.d.ts.map +1 -0
  446. package/dist/content-health/cron.js +167 -0
  447. package/dist/content-health/cron.js.map +1 -0
  448. package/dist/content-health/scanner.d.ts +119 -0
  449. package/dist/content-health/scanner.d.ts.map +1 -0
  450. package/dist/content-health/scanner.js +257 -0
  451. package/dist/content-health/scanner.js.map +1 -0
  452. package/dist/cron/index.d.ts +90 -0
  453. package/dist/cron/index.d.ts.map +1 -0
  454. package/dist/cron/index.js +299 -0
  455. package/dist/cron/index.js.map +1 -0
  456. package/dist/db/adapters/mysql.d.ts +5 -0
  457. package/dist/db/adapters/mysql.d.ts.map +1 -0
  458. package/dist/db/adapters/mysql.js +26 -0
  459. package/dist/db/adapters/mysql.js.map +1 -0
  460. package/dist/db/adapters/postgres.d.ts +7 -0
  461. package/dist/db/adapters/postgres.d.ts.map +1 -0
  462. package/dist/db/adapters/postgres.js +25 -0
  463. package/dist/db/adapters/postgres.js.map +1 -0
  464. package/dist/db/adapters/sqlite.d.ts +5 -0
  465. package/dist/db/adapters/sqlite.d.ts.map +1 -0
  466. package/dist/db/adapters/sqlite.js +27 -0
  467. package/dist/db/adapters/sqlite.js.map +1 -0
  468. package/dist/db/client-types.d.ts +75 -0
  469. package/dist/db/client-types.d.ts.map +1 -0
  470. package/dist/db/client-types.js +2 -0
  471. package/dist/db/client-types.js.map +1 -0
  472. package/dist/db/create-adapter.d.ts +11 -0
  473. package/dist/db/create-adapter.d.ts.map +1 -0
  474. package/dist/db/create-adapter.js +43 -0
  475. package/dist/db/create-adapter.js.map +1 -0
  476. package/dist/db/find-in-batches.d.ts +12 -0
  477. package/dist/db/find-in-batches.d.ts.map +1 -0
  478. package/dist/db/find-in-batches.js +20 -0
  479. package/dist/db/find-in-batches.js.map +1 -0
  480. package/dist/db/index.d.ts +9 -0
  481. package/dist/db/index.d.ts.map +1 -0
  482. package/dist/db/index.js +5 -0
  483. package/dist/db/index.js.map +1 -0
  484. package/dist/db/model-types.d.ts +602 -0
  485. package/dist/db/model-types.d.ts.map +1 -0
  486. package/dist/db/model-types.js +14 -0
  487. package/dist/db/model-types.js.map +1 -0
  488. package/dist/db.d.ts +20 -0
  489. package/dist/db.d.ts.map +1 -0
  490. package/dist/db.js +35 -0
  491. package/dist/db.js.map +1 -0
  492. package/dist/diagnostics/api-metrics.d.ts +135 -0
  493. package/dist/diagnostics/api-metrics.d.ts.map +1 -0
  494. package/dist/diagnostics/api-metrics.js +376 -0
  495. package/dist/diagnostics/api-metrics.js.map +1 -0
  496. package/dist/diagnostics/env.d.ts +44 -0
  497. package/dist/diagnostics/env.d.ts.map +1 -0
  498. package/dist/diagnostics/env.js +354 -0
  499. package/dist/diagnostics/env.js.map +1 -0
  500. package/dist/diagnostics/logger.d.ts +38 -0
  501. package/dist/diagnostics/logger.d.ts.map +1 -0
  502. package/dist/diagnostics/logger.js +89 -0
  503. package/dist/diagnostics/logger.js.map +1 -0
  504. package/dist/diagnostics/sentry.d.ts +8 -0
  505. package/dist/diagnostics/sentry.d.ts.map +1 -0
  506. package/dist/diagnostics/sentry.js +54 -0
  507. package/dist/diagnostics/sentry.js.map +1 -0
  508. package/dist/email/resolve.d.ts +27 -0
  509. package/dist/email/resolve.d.ts.map +1 -0
  510. package/dist/email/resolve.js +68 -0
  511. package/dist/email/resolve.js.map +1 -0
  512. package/dist/email/sender.d.ts +71 -0
  513. package/dist/email/sender.d.ts.map +1 -0
  514. package/dist/email/sender.js +41 -0
  515. package/dist/email/sender.js.map +1 -0
  516. package/dist/email/settings-store.d.ts +116 -0
  517. package/dist/email/settings-store.d.ts.map +1 -0
  518. package/dist/email/settings-store.js +222 -0
  519. package/dist/email/settings-store.js.map +1 -0
  520. package/dist/fields/component-block.d.ts +23 -0
  521. package/dist/fields/component-block.d.ts.map +1 -0
  522. package/dist/fields/component-block.js +137 -0
  523. package/dist/fields/component-block.js.map +1 -0
  524. package/dist/fields/index.d.ts +19 -0
  525. package/dist/fields/index.d.ts.map +1 -0
  526. package/dist/fields/index.js +95 -0
  527. package/dist/fields/index.js.map +1 -0
  528. package/dist/fields/presets.d.ts +144 -0
  529. package/dist/fields/presets.d.ts.map +1 -0
  530. package/dist/fields/presets.js +350 -0
  531. package/dist/fields/presets.js.map +1 -0
  532. package/dist/fields/relations.d.ts +87 -0
  533. package/dist/fields/relations.d.ts.map +1 -0
  534. package/dist/fields/relations.js +231 -0
  535. package/dist/fields/relations.js.map +1 -0
  536. package/dist/forms/analytics.d.ts +62 -0
  537. package/dist/forms/analytics.d.ts.map +1 -0
  538. package/dist/forms/analytics.js +95 -0
  539. package/dist/forms/analytics.js.map +1 -0
  540. package/dist/forms/attribution.d.ts +175 -0
  541. package/dist/forms/attribution.d.ts.map +1 -0
  542. package/dist/forms/attribution.js +577 -0
  543. package/dist/forms/attribution.js.map +1 -0
  544. package/dist/forms/client-validation.d.ts +50 -0
  545. package/dist/forms/client-validation.d.ts.map +1 -0
  546. package/dist/forms/client-validation.js +215 -0
  547. package/dist/forms/client-validation.js.map +1 -0
  548. package/dist/forms/delivery.d.ts +89 -0
  549. package/dist/forms/delivery.d.ts.map +1 -0
  550. package/dist/forms/delivery.js +477 -0
  551. package/dist/forms/delivery.js.map +1 -0
  552. package/dist/forms/encryption.d.ts +20 -0
  553. package/dist/forms/encryption.d.ts.map +1 -0
  554. package/dist/forms/encryption.js +80 -0
  555. package/dist/forms/encryption.js.map +1 -0
  556. package/dist/forms/entries.d.ts +68 -0
  557. package/dist/forms/entries.d.ts.map +1 -0
  558. package/dist/forms/entries.js +557 -0
  559. package/dist/forms/entries.js.map +1 -0
  560. package/dist/forms/files.d.ts +38 -0
  561. package/dist/forms/files.d.ts.map +1 -0
  562. package/dist/forms/files.js +134 -0
  563. package/dist/forms/files.js.map +1 -0
  564. package/dist/forms/index.d.ts +21 -0
  565. package/dist/forms/index.d.ts.map +1 -0
  566. package/dist/forms/index.js +20 -0
  567. package/dist/forms/index.js.map +1 -0
  568. package/dist/forms/notify.d.ts +80 -0
  569. package/dist/forms/notify.d.ts.map +1 -0
  570. package/dist/forms/notify.js +357 -0
  571. package/dist/forms/notify.js.map +1 -0
  572. package/dist/forms/prefetch.d.ts +17 -0
  573. package/dist/forms/prefetch.d.ts.map +1 -0
  574. package/dist/forms/prefetch.js +36 -0
  575. package/dist/forms/prefetch.js.map +1 -0
  576. package/dist/forms/public-html.d.ts +22 -0
  577. package/dist/forms/public-html.d.ts.map +1 -0
  578. package/dist/forms/public-html.js +145 -0
  579. package/dist/forms/public-html.js.map +1 -0
  580. package/dist/forms/service.d.ts +102 -0
  581. package/dist/forms/service.d.ts.map +1 -0
  582. package/dist/forms/service.js +625 -0
  583. package/dist/forms/service.js.map +1 -0
  584. package/dist/forms/spam.d.ts +33 -0
  585. package/dist/forms/spam.d.ts.map +1 -0
  586. package/dist/forms/spam.js +93 -0
  587. package/dist/forms/spam.js.map +1 -0
  588. package/dist/forms/submission.d.ts +132 -0
  589. package/dist/forms/submission.d.ts.map +1 -0
  590. package/dist/forms/submission.js +348 -0
  591. package/dist/forms/submission.js.map +1 -0
  592. package/dist/forms/types.d.ts +259 -0
  593. package/dist/forms/types.d.ts.map +1 -0
  594. package/dist/forms/types.js +20 -0
  595. package/dist/forms/types.js.map +1 -0
  596. package/dist/forms/validation.d.ts +19 -0
  597. package/dist/forms/validation.d.ts.map +1 -0
  598. package/dist/forms/validation.js +166 -0
  599. package/dist/forms/validation.js.map +1 -0
  600. package/dist/forms/webhooks.d.ts +60 -0
  601. package/dist/forms/webhooks.d.ts.map +1 -0
  602. package/dist/forms/webhooks.js +360 -0
  603. package/dist/forms/webhooks.js.map +1 -0
  604. package/dist/forms/webmcp.d.ts +36 -0
  605. package/dist/forms/webmcp.d.ts.map +1 -0
  606. package/dist/forms/webmcp.js +67 -0
  607. package/dist/forms/webmcp.js.map +1 -0
  608. package/dist/forms/xlsx.d.ts +10 -0
  609. package/dist/forms/xlsx.d.ts.map +1 -0
  610. package/dist/forms/xlsx.js +207 -0
  611. package/dist/forms/xlsx.js.map +1 -0
  612. package/dist/generated/browser.d.ts +220 -0
  613. package/dist/generated/browser.js +17 -0
  614. package/dist/generated/client.d.ts +239 -0
  615. package/dist/generated/client.js +34 -0
  616. package/dist/generated/commonInputTypes.d.ts +887 -0
  617. package/dist/generated/commonInputTypes.js +10 -0
  618. package/dist/generated/enums.d.ts +83 -0
  619. package/dist/generated/enums.js +81 -0
  620. package/dist/generated/internal/class.d.ts +517 -0
  621. package/dist/generated/internal/class.js +52 -0
  622. package/dist/generated/internal/prismaNamespace.d.ts +4184 -0
  623. package/dist/generated/internal/prismaNamespace.js +932 -0
  624. package/dist/generated/internal/prismaNamespaceBrowser.d.ts +968 -0
  625. package/dist/generated/internal/prismaNamespaceBrowser.js +903 -0
  626. package/dist/generated/models/AiCrawlerHit.d.ts +1115 -0
  627. package/dist/generated/models/AiCrawlerHit.js +1 -0
  628. package/dist/generated/models/ApiKey.d.ts +1388 -0
  629. package/dist/generated/models/ApiKey.js +1 -0
  630. package/dist/generated/models/ApiRequestMetric.d.ts +1229 -0
  631. package/dist/generated/models/ApiRequestMetric.js +1 -0
  632. package/dist/generated/models/AuditLog.d.ts +1062 -0
  633. package/dist/generated/models/AuditLog.js +1 -0
  634. package/dist/generated/models/BackupRecord.d.ts +1100 -0
  635. package/dist/generated/models/BackupRecord.js +1 -0
  636. package/dist/generated/models/ContentIssue.d.ts +1318 -0
  637. package/dist/generated/models/ContentIssue.js +1 -0
  638. package/dist/generated/models/ContentLock.d.ts +1295 -0
  639. package/dist/generated/models/ContentLock.js +1 -0
  640. package/dist/generated/models/ContentTemplate.d.ts +1254 -0
  641. package/dist/generated/models/ContentTemplate.js +1 -0
  642. package/dist/generated/models/Document.d.ts +3443 -0
  643. package/dist/generated/models/Document.js +1 -0
  644. package/dist/generated/models/Folder.d.ts +1689 -0
  645. package/dist/generated/models/Folder.js +1 -0
  646. package/dist/generated/models/FormDeliveryAttempt.d.ts +1705 -0
  647. package/dist/generated/models/FormDeliveryAttempt.js +1 -0
  648. package/dist/generated/models/FormSchemaVersion.d.ts +1105 -0
  649. package/dist/generated/models/FormSchemaVersion.js +1 -0
  650. package/dist/generated/models/FormSubmission.d.ts +2125 -0
  651. package/dist/generated/models/FormSubmission.js +1 -0
  652. package/dist/generated/models/FormSubmissionFile.d.ts +1438 -0
  653. package/dist/generated/models/FormSubmissionFile.js +1 -0
  654. package/dist/generated/models/FormWebhook.d.ts +1271 -0
  655. package/dist/generated/models/FormWebhook.js +1 -0
  656. package/dist/generated/models/InAppNotification.d.ts +1295 -0
  657. package/dist/generated/models/InAppNotification.js +1 -0
  658. package/dist/generated/models/Invite.d.ts +1240 -0
  659. package/dist/generated/models/Invite.js +1 -0
  660. package/dist/generated/models/Media.d.ts +2759 -0
  661. package/dist/generated/models/Media.js +1 -0
  662. package/dist/generated/models/MediaUsage.d.ts +1301 -0
  663. package/dist/generated/models/MediaUsage.js +1 -0
  664. package/dist/generated/models/OAuthAccount.d.ts +1300 -0
  665. package/dist/generated/models/OAuthAccount.js +1 -0
  666. package/dist/generated/models/PageTemplate.d.ts +1118 -0
  667. package/dist/generated/models/PageTemplate.js +1 -0
  668. package/dist/generated/models/PasswordResetToken.d.ts +1225 -0
  669. package/dist/generated/models/PasswordResetToken.js +1 -0
  670. package/dist/generated/models/Redirect.d.ts +1342 -0
  671. package/dist/generated/models/Redirect.js +1 -0
  672. package/dist/generated/models/Redirect404Hit.d.ts +1107 -0
  673. package/dist/generated/models/Redirect404Hit.js +1 -0
  674. package/dist/generated/models/RedirectSuggestion.d.ts +1185 -0
  675. package/dist/generated/models/RedirectSuggestion.js +1 -0
  676. package/dist/generated/models/SavedSection.d.ts +1156 -0
  677. package/dist/generated/models/SavedSection.js +1 -0
  678. package/dist/generated/models/ScriptTag.d.ts +1291 -0
  679. package/dist/generated/models/ScriptTag.js +1 -0
  680. package/dist/generated/models/SeoAuditRun.d.ts +1570 -0
  681. package/dist/generated/models/SeoAuditRun.js +1 -0
  682. package/dist/generated/models/SeoIssue.d.ts +1873 -0
  683. package/dist/generated/models/SeoIssue.js +1 -0
  684. package/dist/generated/models/Session.d.ts +1330 -0
  685. package/dist/generated/models/Session.js +1 -0
  686. package/dist/generated/models/Site.d.ts +1062 -0
  687. package/dist/generated/models/Site.js +1 -0
  688. package/dist/generated/models/User.d.ts +4344 -0
  689. package/dist/generated/models/User.js +1 -0
  690. package/dist/generated/models/Version.d.ts +1334 -0
  691. package/dist/generated/models/Version.js +1 -0
  692. package/dist/generated/models/WebhookDeliveryLog.d.ts +1479 -0
  693. package/dist/generated/models/WebhookDeliveryLog.js +1 -0
  694. package/dist/generated/models/WebhookEndpoint.d.ts +1331 -0
  695. package/dist/generated/models/WebhookEndpoint.js +1 -0
  696. package/dist/generated/models/WorkflowState.d.ts +1436 -0
  697. package/dist/generated/models/WorkflowState.js +1 -0
  698. package/dist/generated/models.d.ts +37 -0
  699. package/dist/generated/models.js +1 -0
  700. package/dist/graphql/index.d.ts +11 -0
  701. package/dist/graphql/index.d.ts.map +1 -0
  702. package/dist/graphql/index.js +74 -0
  703. package/dist/graphql/index.js.map +1 -0
  704. package/dist/graphql/resolvers.d.ts +8 -0
  705. package/dist/graphql/resolvers.d.ts.map +1 -0
  706. package/dist/graphql/resolvers.js +89 -0
  707. package/dist/graphql/resolvers.js.map +1 -0
  708. package/dist/graphql/schema-builder.d.ts +3 -0
  709. package/dist/graphql/schema-builder.d.ts.map +1 -0
  710. package/dist/graphql/schema-builder.js +103 -0
  711. package/dist/graphql/schema-builder.js.map +1 -0
  712. package/dist/health/index.d.ts +27 -0
  713. package/dist/health/index.d.ts.map +1 -0
  714. package/dist/health/index.js +43 -0
  715. package/dist/health/index.js.map +1 -0
  716. package/dist/i18n/index.d.ts +22 -0
  717. package/dist/i18n/index.d.ts.map +1 -0
  718. package/dist/i18n/index.js +37 -0
  719. package/dist/i18n/index.js.map +1 -0
  720. package/dist/index.d.ts +125 -0
  721. package/dist/index.d.ts.map +1 -0
  722. package/dist/index.js +132 -0
  723. package/dist/index.js.map +1 -0
  724. package/dist/media/content-hash.d.ts +3 -0
  725. package/dist/media/content-hash.d.ts.map +1 -0
  726. package/dist/media/content-hash.js +11 -0
  727. package/dist/media/content-hash.js.map +1 -0
  728. package/dist/media/denormalize.d.ts +23 -0
  729. package/dist/media/denormalize.d.ts.map +1 -0
  730. package/dist/media/denormalize.js +170 -0
  731. package/dist/media/denormalize.js.map +1 -0
  732. package/dist/media/index.d.ts +8 -0
  733. package/dist/media/index.d.ts.map +1 -0
  734. package/dist/media/index.js +5 -0
  735. package/dist/media/index.js.map +1 -0
  736. package/dist/media/merge-document-media.d.ts +23 -0
  737. package/dist/media/merge-document-media.d.ts.map +1 -0
  738. package/dist/media/merge-document-media.js +146 -0
  739. package/dist/media/merge-document-media.js.map +1 -0
  740. package/dist/media/metadata-patch.d.ts +21 -0
  741. package/dist/media/metadata-patch.d.ts.map +1 -0
  742. package/dist/media/metadata-patch.js +37 -0
  743. package/dist/media/metadata-patch.js.map +1 -0
  744. package/dist/media/optimize.d.ts +61 -0
  745. package/dist/media/optimize.d.ts.map +1 -0
  746. package/dist/media/optimize.js +168 -0
  747. package/dist/media/optimize.js.map +1 -0
  748. package/dist/media/seo.d.ts +110 -0
  749. package/dist/media/seo.d.ts.map +1 -0
  750. package/dist/media/seo.js +263 -0
  751. package/dist/media/seo.js.map +1 -0
  752. package/dist/media/usage-sync.d.ts +37 -0
  753. package/dist/media/usage-sync.d.ts.map +1 -0
  754. package/dist/media/usage-sync.js +234 -0
  755. package/dist/media/usage-sync.js.map +1 -0
  756. package/dist/middleware.d.ts +17 -0
  757. package/dist/middleware.d.ts.map +1 -0
  758. package/dist/middleware.js +159 -0
  759. package/dist/middleware.js.map +1 -0
  760. package/dist/multisite/index.d.ts +20 -0
  761. package/dist/multisite/index.d.ts.map +1 -0
  762. package/dist/multisite/index.js +26 -0
  763. package/dist/multisite/index.js.map +1 -0
  764. package/dist/navigation/unresolved-targets.d.ts +35 -0
  765. package/dist/navigation/unresolved-targets.d.ts.map +1 -0
  766. package/dist/navigation/unresolved-targets.js +109 -0
  767. package/dist/navigation/unresolved-targets.js.map +1 -0
  768. package/dist/next/preview.d.ts +10 -0
  769. package/dist/next/preview.d.ts.map +1 -0
  770. package/dist/next/preview.js +17 -0
  771. package/dist/next/preview.js.map +1 -0
  772. package/dist/next/script-tags.d.ts +15 -0
  773. package/dist/next/script-tags.d.ts.map +1 -0
  774. package/dist/next/script-tags.js +58 -0
  775. package/dist/next/script-tags.js.map +1 -0
  776. package/dist/next-site.d.ts +29 -0
  777. package/dist/next-site.d.ts.map +1 -0
  778. package/dist/next-site.js +60 -0
  779. package/dist/next-site.js.map +1 -0
  780. package/dist/next.d.ts +33 -0
  781. package/dist/next.d.ts.map +1 -0
  782. package/dist/next.js +142 -0
  783. package/dist/next.js.map +1 -0
  784. package/dist/notifications/index.d.ts +20 -0
  785. package/dist/notifications/index.d.ts.map +1 -0
  786. package/dist/notifications/index.js +22 -0
  787. package/dist/notifications/index.js.map +1 -0
  788. package/dist/page-builder/a11y-fix.d.ts +20 -0
  789. package/dist/page-builder/a11y-fix.d.ts.map +1 -0
  790. package/dist/page-builder/a11y-fix.js +182 -0
  791. package/dist/page-builder/a11y-fix.js.map +1 -0
  792. package/dist/page-builder/ai-pipeline.d.ts +43 -0
  793. package/dist/page-builder/ai-pipeline.d.ts.map +1 -0
  794. package/dist/page-builder/ai-pipeline.js +165 -0
  795. package/dist/page-builder/ai-pipeline.js.map +1 -0
  796. package/dist/page-builder/blocks.d.ts +27 -0
  797. package/dist/page-builder/blocks.d.ts.map +1 -0
  798. package/dist/page-builder/blocks.js +313 -0
  799. package/dist/page-builder/blocks.js.map +1 -0
  800. package/dist/page-builder/design-scorer.d.ts +27 -0
  801. package/dist/page-builder/design-scorer.d.ts.map +1 -0
  802. package/dist/page-builder/design-scorer.js +612 -0
  803. package/dist/page-builder/design-scorer.js.map +1 -0
  804. package/dist/page-builder/index.d.ts +18 -0
  805. package/dist/page-builder/index.d.ts.map +1 -0
  806. package/dist/page-builder/index.js +20 -0
  807. package/dist/page-builder/index.js.map +1 -0
  808. package/dist/page-builder/schema.d.ts +107 -0
  809. package/dist/page-builder/schema.d.ts.map +1 -0
  810. package/dist/page-builder/schema.js +90 -0
  811. package/dist/page-builder/schema.js.map +1 -0
  812. package/dist/page-builder/seo-analyzer.d.ts +61 -0
  813. package/dist/page-builder/seo-analyzer.d.ts.map +1 -0
  814. package/dist/page-builder/seo-analyzer.js +778 -0
  815. package/dist/page-builder/seo-analyzer.js.map +1 -0
  816. package/dist/page-builder/templates.d.ts +9 -0
  817. package/dist/page-builder/templates.d.ts.map +1 -0
  818. package/dist/page-builder/templates.js +742 -0
  819. package/dist/page-builder/templates.js.map +1 -0
  820. package/dist/page-builder/tree.d.ts +17 -0
  821. package/dist/page-builder/tree.d.ts.map +1 -0
  822. package/dist/page-builder/tree.js +160 -0
  823. package/dist/page-builder/tree.js.map +1 -0
  824. package/dist/page-builder/types.d.ts +112 -0
  825. package/dist/page-builder/types.d.ts.map +1 -0
  826. package/dist/page-builder/types.js +4 -0
  827. package/dist/page-builder/types.js.map +1 -0
  828. package/dist/page-builder/validate.d.ts +6 -0
  829. package/dist/page-builder/validate.d.ts.map +1 -0
  830. package/dist/page-builder/validate.js +87 -0
  831. package/dist/page-builder/validate.js.map +1 -0
  832. package/dist/plan/features.d.ts +17 -0
  833. package/dist/plan/features.d.ts.map +1 -0
  834. package/dist/plan/features.js +73 -0
  835. package/dist/plan/features.js.map +1 -0
  836. package/dist/posts/query.d.ts +50 -0
  837. package/dist/posts/query.d.ts.map +1 -0
  838. package/dist/posts/query.js +105 -0
  839. package/dist/posts/query.js.map +1 -0
  840. package/dist/presence/index.d.ts +24 -0
  841. package/dist/presence/index.d.ts.map +1 -0
  842. package/dist/presence/index.js +99 -0
  843. package/dist/presence/index.js.map +1 -0
  844. package/dist/preview/event-bus.d.ts +39 -0
  845. package/dist/preview/event-bus.d.ts.map +1 -0
  846. package/dist/preview/event-bus.js +73 -0
  847. package/dist/preview/event-bus.js.map +1 -0
  848. package/dist/preview/index.d.ts +36 -0
  849. package/dist/preview/index.d.ts.map +1 -0
  850. package/dist/preview/index.js +68 -0
  851. package/dist/preview/index.js.map +1 -0
  852. package/dist/preview/sse-stream.d.ts +47 -0
  853. package/dist/preview/sse-stream.d.ts.map +1 -0
  854. package/dist/preview/sse-stream.js +111 -0
  855. package/dist/preview/sse-stream.js.map +1 -0
  856. package/dist/privacy/index.d.ts +33 -0
  857. package/dist/privacy/index.d.ts.map +1 -0
  858. package/dist/privacy/index.js +15 -0
  859. package/dist/privacy/index.js.map +1 -0
  860. package/dist/public-redirects.d.ts +78 -0
  861. package/dist/public-redirects.d.ts.map +1 -0
  862. package/dist/public-redirects.js +187 -0
  863. package/dist/public-redirects.js.map +1 -0
  864. package/dist/public-security-headers.d.ts +27 -0
  865. package/dist/public-security-headers.d.ts.map +1 -0
  866. package/dist/public-security-headers.js +40 -0
  867. package/dist/public-security-headers.js.map +1 -0
  868. package/dist/publishing/publish-sync.d.ts +50 -0
  869. package/dist/publishing/publish-sync.d.ts.map +1 -0
  870. package/dist/publishing/publish-sync.js +124 -0
  871. package/dist/publishing/publish-sync.js.map +1 -0
  872. package/dist/purge/adapters.d.ts +60 -0
  873. package/dist/purge/adapters.d.ts.map +1 -0
  874. package/dist/purge/adapters.js +81 -0
  875. package/dist/purge/adapters.js.map +1 -0
  876. package/dist/purge/index.d.ts +12 -0
  877. package/dist/purge/index.d.ts.map +1 -0
  878. package/dist/purge/index.js +10 -0
  879. package/dist/purge/index.js.map +1 -0
  880. package/dist/purge/receiver.d.ts +95 -0
  881. package/dist/purge/receiver.d.ts.map +1 -0
  882. package/dist/purge/receiver.js +176 -0
  883. package/dist/purge/receiver.js.map +1 -0
  884. package/dist/realtime/comments.d.ts +217 -0
  885. package/dist/realtime/comments.d.ts.map +1 -0
  886. package/dist/realtime/comments.js +282 -0
  887. package/dist/realtime/comments.js.map +1 -0
  888. package/dist/realtime/gateway.d.ts +107 -0
  889. package/dist/realtime/gateway.d.ts.map +1 -0
  890. package/dist/realtime/gateway.js +215 -0
  891. package/dist/realtime/gateway.js.map +1 -0
  892. package/dist/realtime/index.d.ts +21 -0
  893. package/dist/realtime/index.d.ts.map +1 -0
  894. package/dist/realtime/index.js +27 -0
  895. package/dist/realtime/index.js.map +1 -0
  896. package/dist/realtime/notification-bus.d.ts +60 -0
  897. package/dist/realtime/notification-bus.d.ts.map +1 -0
  898. package/dist/realtime/notification-bus.js +77 -0
  899. package/dist/realtime/notification-bus.js.map +1 -0
  900. package/dist/realtime/notification-stream.d.ts +50 -0
  901. package/dist/realtime/notification-stream.d.ts.map +1 -0
  902. package/dist/realtime/notification-stream.js +148 -0
  903. package/dist/realtime/notification-stream.js.map +1 -0
  904. package/dist/realtime/notifications.d.ts +201 -0
  905. package/dist/realtime/notifications.d.ts.map +1 -0
  906. package/dist/realtime/notifications.js +233 -0
  907. package/dist/realtime/notifications.js.map +1 -0
  908. package/dist/realtime/persistence.d.ts +72 -0
  909. package/dist/realtime/persistence.d.ts.map +1 -0
  910. package/dist/realtime/persistence.js +53 -0
  911. package/dist/realtime/persistence.js.map +1 -0
  912. package/dist/realtime/sse.d.ts +20 -0
  913. package/dist/realtime/sse.d.ts.map +1 -0
  914. package/dist/realtime/sse.js +31 -0
  915. package/dist/realtime/sse.js.map +1 -0
  916. package/dist/redirects/auto.d.ts +45 -0
  917. package/dist/redirects/auto.d.ts.map +1 -0
  918. package/dist/redirects/auto.js +60 -0
  919. package/dist/redirects/auto.js.map +1 -0
  920. package/dist/redirects/fix-link.d.ts +15 -0
  921. package/dist/redirects/fix-link.d.ts.map +1 -0
  922. package/dist/redirects/fix-link.js +66 -0
  923. package/dist/redirects/fix-link.js.map +1 -0
  924. package/dist/redirects/graph.d.ts +92 -0
  925. package/dist/redirects/graph.d.ts.map +1 -0
  926. package/dist/redirects/graph.js +176 -0
  927. package/dist/redirects/graph.js.map +1 -0
  928. package/dist/redirects/index.d.ts +17 -0
  929. package/dist/redirects/index.d.ts.map +1 -0
  930. package/dist/redirects/index.js +10 -0
  931. package/dist/redirects/index.js.map +1 -0
  932. package/dist/redirects/legitimacy.d.ts +23 -0
  933. package/dist/redirects/legitimacy.d.ts.map +1 -0
  934. package/dist/redirects/legitimacy.js +64 -0
  935. package/dist/redirects/legitimacy.js.map +1 -0
  936. package/dist/redirects/recovery.d.ts +92 -0
  937. package/dist/redirects/recovery.d.ts.map +1 -0
  938. package/dist/redirects/recovery.js +210 -0
  939. package/dist/redirects/recovery.js.map +1 -0
  940. package/dist/redirects/rerank.d.ts +24 -0
  941. package/dist/redirects/rerank.d.ts.map +1 -0
  942. package/dist/redirects/rerank.js +82 -0
  943. package/dist/redirects/rerank.js.map +1 -0
  944. package/dist/redirects/suggest.d.ts +23 -0
  945. package/dist/redirects/suggest.d.ts.map +1 -0
  946. package/dist/redirects/suggest.js +80 -0
  947. package/dist/redirects/suggest.js.map +1 -0
  948. package/dist/redirects/typo.d.ts +10 -0
  949. package/dist/redirects/typo.d.ts.map +1 -0
  950. package/dist/redirects/typo.js +51 -0
  951. package/dist/redirects/typo.js.map +1 -0
  952. package/dist/redirects/validate-destination.d.ts +13 -0
  953. package/dist/redirects/validate-destination.d.ts.map +1 -0
  954. package/dist/redirects/validate-destination.js +51 -0
  955. package/dist/redirects/validate-destination.js.map +1 -0
  956. package/dist/relationships/index.d.ts +13 -0
  957. package/dist/relationships/index.d.ts.map +1 -0
  958. package/dist/relationships/index.js +12 -0
  959. package/dist/relationships/index.js.map +1 -0
  960. package/dist/scheduling/index.d.ts +45 -0
  961. package/dist/scheduling/index.d.ts.map +1 -0
  962. package/dist/scheduling/index.js +141 -0
  963. package/dist/scheduling/index.js.map +1 -0
  964. package/dist/script-tags/index.d.ts +3 -0
  965. package/dist/script-tags/index.d.ts.map +1 -0
  966. package/dist/script-tags/index.js +3 -0
  967. package/dist/script-tags/index.js.map +1 -0
  968. package/dist/script-tags/invalidate.d.ts +3 -0
  969. package/dist/script-tags/invalidate.d.ts.map +1 -0
  970. package/dist/script-tags/invalidate.js +16 -0
  971. package/dist/script-tags/invalidate.js.map +1 -0
  972. package/dist/script-tags/resolve.d.ts +41 -0
  973. package/dist/script-tags/resolve.d.ts.map +1 -0
  974. package/dist/script-tags/resolve.js +130 -0
  975. package/dist/script-tags/resolve.js.map +1 -0
  976. package/dist/search/index.d.ts +25 -0
  977. package/dist/search/index.d.ts.map +1 -0
  978. package/dist/search/index.js +189 -0
  979. package/dist/search/index.js.map +1 -0
  980. package/dist/sections/bridge.d.ts +8 -0
  981. package/dist/sections/bridge.d.ts.map +1 -0
  982. package/dist/sections/bridge.js +211 -0
  983. package/dist/sections/bridge.js.map +1 -0
  984. package/dist/sections/define.d.ts +21 -0
  985. package/dist/sections/define.d.ts.map +1 -0
  986. package/dist/sections/define.js +160 -0
  987. package/dist/sections/define.js.map +1 -0
  988. package/dist/sections/embed.d.ts +24 -0
  989. package/dist/sections/embed.d.ts.map +1 -0
  990. package/dist/sections/embed.js +78 -0
  991. package/dist/sections/embed.js.map +1 -0
  992. package/dist/sections/fields.d.ts +37 -0
  993. package/dist/sections/fields.d.ts.map +1 -0
  994. package/dist/sections/fields.js +50 -0
  995. package/dist/sections/fields.js.map +1 -0
  996. package/dist/sections/helpers.d.ts +149 -0
  997. package/dist/sections/helpers.d.ts.map +1 -0
  998. package/dist/sections/helpers.js +373 -0
  999. package/dist/sections/helpers.js.map +1 -0
  1000. package/dist/sections/index.d.ts +22 -0
  1001. package/dist/sections/index.d.ts.map +1 -0
  1002. package/dist/sections/index.js +18 -0
  1003. package/dist/sections/index.js.map +1 -0
  1004. package/dist/sections/media-enrich.d.ts +45 -0
  1005. package/dist/sections/media-enrich.d.ts.map +1 -0
  1006. package/dist/sections/media-enrich.js +137 -0
  1007. package/dist/sections/media-enrich.js.map +1 -0
  1008. package/dist/sections/migrate.d.ts +42 -0
  1009. package/dist/sections/migrate.d.ts.map +1 -0
  1010. package/dist/sections/migrate.js +66 -0
  1011. package/dist/sections/migrate.js.map +1 -0
  1012. package/dist/sections/registry.d.ts +46 -0
  1013. package/dist/sections/registry.d.ts.map +1 -0
  1014. package/dist/sections/registry.js +598 -0
  1015. package/dist/sections/registry.js.map +1 -0
  1016. package/dist/sections/types.d.ts +192 -0
  1017. package/dist/sections/types.d.ts.map +1 -0
  1018. package/dist/sections/types.js +21 -0
  1019. package/dist/sections/types.js.map +1 -0
  1020. package/dist/sections/validate.d.ts +12 -0
  1021. package/dist/sections/validate.d.ts.map +1 -0
  1022. package/dist/sections/validate.js +186 -0
  1023. package/dist/sections/validate.js.map +1 -0
  1024. package/dist/sections/widget-embed.d.ts +45 -0
  1025. package/dist/sections/widget-embed.d.ts.map +1 -0
  1026. package/dist/sections/widget-embed.js +129 -0
  1027. package/dist/sections/widget-embed.js.map +1 -0
  1028. package/dist/security/access.d.ts +35 -0
  1029. package/dist/security/access.d.ts.map +1 -0
  1030. package/dist/security/access.js +133 -0
  1031. package/dist/security/access.js.map +1 -0
  1032. package/dist/security/anomaly-detection.d.ts +17 -0
  1033. package/dist/security/anomaly-detection.d.ts.map +1 -0
  1034. package/dist/security/anomaly-detection.js +39 -0
  1035. package/dist/security/anomaly-detection.js.map +1 -0
  1036. package/dist/security/api-key-enhanced.d.ts +68 -0
  1037. package/dist/security/api-key-enhanced.d.ts.map +1 -0
  1038. package/dist/security/api-key-enhanced.js +76 -0
  1039. package/dist/security/api-key-enhanced.js.map +1 -0
  1040. package/dist/security/audit.d.ts +46 -0
  1041. package/dist/security/audit.d.ts.map +1 -0
  1042. package/dist/security/audit.js +67 -0
  1043. package/dist/security/audit.js.map +1 -0
  1044. package/dist/security/breach-check.d.ts +3 -0
  1045. package/dist/security/breach-check.d.ts.map +1 -0
  1046. package/dist/security/breach-check.js +27 -0
  1047. package/dist/security/breach-check.js.map +1 -0
  1048. package/dist/security/captcha.d.ts +67 -0
  1049. package/dist/security/captcha.d.ts.map +1 -0
  1050. package/dist/security/captcha.js +186 -0
  1051. package/dist/security/captcha.js.map +1 -0
  1052. package/dist/security/center/gather.d.ts +20 -0
  1053. package/dist/security/center/gather.d.ts.map +1 -0
  1054. package/dist/security/center/gather.js +240 -0
  1055. package/dist/security/center/gather.js.map +1 -0
  1056. package/dist/security/center/index.d.ts +13 -0
  1057. package/dist/security/center/index.d.ts.map +1 -0
  1058. package/dist/security/center/index.js +4 -0
  1059. package/dist/security/center/index.js.map +1 -0
  1060. package/dist/security/center/settings.d.ts +48 -0
  1061. package/dist/security/center/settings.d.ts.map +1 -0
  1062. package/dist/security/center/settings.js +218 -0
  1063. package/dist/security/center/settings.js.map +1 -0
  1064. package/dist/security/center/status.d.ts +10 -0
  1065. package/dist/security/center/status.d.ts.map +1 -0
  1066. package/dist/security/center/status.js +249 -0
  1067. package/dist/security/center/status.js.map +1 -0
  1068. package/dist/security/center/summaries.d.ts +21 -0
  1069. package/dist/security/center/summaries.d.ts.map +1 -0
  1070. package/dist/security/center/summaries.js +65 -0
  1071. package/dist/security/center/summaries.js.map +1 -0
  1072. package/dist/security/center/types.d.ts +142 -0
  1073. package/dist/security/center/types.d.ts.map +1 -0
  1074. package/dist/security/center/types.js +12 -0
  1075. package/dist/security/center/types.js.map +1 -0
  1076. package/dist/security/client-ip.d.ts +23 -0
  1077. package/dist/security/client-ip.d.ts.map +1 -0
  1078. package/dist/security/client-ip.js +97 -0
  1079. package/dist/security/client-ip.js.map +1 -0
  1080. package/dist/security/cors.d.ts +11 -0
  1081. package/dist/security/cors.d.ts.map +1 -0
  1082. package/dist/security/cors.js +33 -0
  1083. package/dist/security/cors.js.map +1 -0
  1084. package/dist/security/csp-nonces.d.ts +5 -0
  1085. package/dist/security/csp-nonces.d.ts.map +1 -0
  1086. package/dist/security/csp-nonces.js +24 -0
  1087. package/dist/security/csp-nonces.js.map +1 -0
  1088. package/dist/security/csrf.d.ts +5 -0
  1089. package/dist/security/csrf.d.ts.map +1 -0
  1090. package/dist/security/csrf.js +20 -0
  1091. package/dist/security/csrf.js.map +1 -0
  1092. package/dist/security/encrypted-fields.d.ts +14 -0
  1093. package/dist/security/encrypted-fields.d.ts.map +1 -0
  1094. package/dist/security/encrypted-fields.js +94 -0
  1095. package/dist/security/encrypted-fields.js.map +1 -0
  1096. package/dist/security/headers.d.ts +11 -0
  1097. package/dist/security/headers.d.ts.map +1 -0
  1098. package/dist/security/headers.js +32 -0
  1099. package/dist/security/headers.js.map +1 -0
  1100. package/dist/security/index.d.ts +40 -0
  1101. package/dist/security/index.d.ts.map +1 -0
  1102. package/dist/security/index.js +26 -0
  1103. package/dist/security/index.js.map +1 -0
  1104. package/dist/security/internal-keys.d.ts +15 -0
  1105. package/dist/security/internal-keys.d.ts.map +1 -0
  1106. package/dist/security/internal-keys.js +33 -0
  1107. package/dist/security/internal-keys.js.map +1 -0
  1108. package/dist/security/ip-allowlist.d.ts +15 -0
  1109. package/dist/security/ip-allowlist.d.ts.map +1 -0
  1110. package/dist/security/ip-allowlist.js +141 -0
  1111. package/dist/security/ip-allowlist.js.map +1 -0
  1112. package/dist/security/ip-canon.d.ts +71 -0
  1113. package/dist/security/ip-canon.d.ts.map +1 -0
  1114. package/dist/security/ip-canon.js +352 -0
  1115. package/dist/security/ip-canon.js.map +1 -0
  1116. package/dist/security/middleware.d.ts +20 -0
  1117. package/dist/security/middleware.d.ts.map +1 -0
  1118. package/dist/security/middleware.js +45 -0
  1119. package/dist/security/middleware.js.map +1 -0
  1120. package/dist/security/rate-limit-policy.d.ts +40 -0
  1121. package/dist/security/rate-limit-policy.d.ts.map +1 -0
  1122. package/dist/security/rate-limit-policy.js +57 -0
  1123. package/dist/security/rate-limit-policy.js.map +1 -0
  1124. package/dist/security/rate-limit.d.ts +55 -0
  1125. package/dist/security/rate-limit.d.ts.map +1 -0
  1126. package/dist/security/rate-limit.js +298 -0
  1127. package/dist/security/rate-limit.js.map +1 -0
  1128. package/dist/security/reauth.d.ts +16 -0
  1129. package/dist/security/reauth.d.ts.map +1 -0
  1130. package/dist/security/reauth.js +56 -0
  1131. package/dist/security/reauth.js.map +1 -0
  1132. package/dist/security/redact.d.ts +12 -0
  1133. package/dist/security/redact.d.ts.map +1 -0
  1134. package/dist/security/redact.js +44 -0
  1135. package/dist/security/redact.js.map +1 -0
  1136. package/dist/security/safe-fetch.d.ts +57 -0
  1137. package/dist/security/safe-fetch.d.ts.map +1 -0
  1138. package/dist/security/safe-fetch.js +71 -0
  1139. package/dist/security/safe-fetch.js.map +1 -0
  1140. package/dist/security/sanitize-document.d.ts +14 -0
  1141. package/dist/security/sanitize-document.d.ts.map +1 -0
  1142. package/dist/security/sanitize-document.js +117 -0
  1143. package/dist/security/sanitize-document.js.map +1 -0
  1144. package/dist/security/sanitize.d.ts +13 -0
  1145. package/dist/security/sanitize.d.ts.map +1 -0
  1146. package/dist/security/sanitize.js +64 -0
  1147. package/dist/security/sanitize.js.map +1 -0
  1148. package/dist/security/secret-storage.d.ts +22 -0
  1149. package/dist/security/secret-storage.d.ts.map +1 -0
  1150. package/dist/security/secret-storage.js +75 -0
  1151. package/dist/security/secret-storage.js.map +1 -0
  1152. package/dist/security/security-txt.d.ts +12 -0
  1153. package/dist/security/security-txt.d.ts.map +1 -0
  1154. package/dist/security/security-txt.js +19 -0
  1155. package/dist/security/security-txt.js.map +1 -0
  1156. package/dist/security/session-limits.d.ts +17 -0
  1157. package/dist/security/session-limits.d.ts.map +1 -0
  1158. package/dist/security/session-limits.js +14 -0
  1159. package/dist/security/session-limits.js.map +1 -0
  1160. package/dist/security/upload.d.ts +32 -0
  1161. package/dist/security/upload.d.ts.map +1 -0
  1162. package/dist/security/upload.js +129 -0
  1163. package/dist/security/upload.js.map +1 -0
  1164. package/dist/security/webhook.d.ts +30 -0
  1165. package/dist/security/webhook.d.ts.map +1 -0
  1166. package/dist/security/webhook.js +133 -0
  1167. package/dist/security/webhook.js.map +1 -0
  1168. package/dist/seo/ai-bots.d.ts +56 -0
  1169. package/dist/seo/ai-bots.d.ts.map +1 -0
  1170. package/dist/seo/ai-bots.js +125 -0
  1171. package/dist/seo/ai-bots.js.map +1 -0
  1172. package/dist/seo/ai-crawler-log.d.ts +14 -0
  1173. package/dist/seo/ai-crawler-log.d.ts.map +1 -0
  1174. package/dist/seo/ai-crawler-log.js +48 -0
  1175. package/dist/seo/ai-crawler-log.js.map +1 -0
  1176. package/dist/seo/alt-text.d.ts +39 -0
  1177. package/dist/seo/alt-text.d.ts.map +1 -0
  1178. package/dist/seo/alt-text.js +67 -0
  1179. package/dist/seo/alt-text.js.map +1 -0
  1180. package/dist/seo/analysis.d.ts +75 -0
  1181. package/dist/seo/analysis.d.ts.map +1 -0
  1182. package/dist/seo/analysis.js +641 -0
  1183. package/dist/seo/analysis.js.map +1 -0
  1184. package/dist/seo/audit-engine.d.ts +124 -0
  1185. package/dist/seo/audit-engine.d.ts.map +1 -0
  1186. package/dist/seo/audit-engine.js +534 -0
  1187. package/dist/seo/audit-engine.js.map +1 -0
  1188. package/dist/seo/audit-runner.d.ts +84 -0
  1189. package/dist/seo/audit-runner.d.ts.map +1 -0
  1190. package/dist/seo/audit-runner.js +372 -0
  1191. package/dist/seo/audit-runner.js.map +1 -0
  1192. package/dist/seo/audit-signals.d.ts +28 -0
  1193. package/dist/seo/audit-signals.d.ts.map +1 -0
  1194. package/dist/seo/audit-signals.js +133 -0
  1195. package/dist/seo/audit-signals.js.map +1 -0
  1196. package/dist/seo/config-store.d.ts +99 -0
  1197. package/dist/seo/config-store.d.ts.map +1 -0
  1198. package/dist/seo/config-store.js +194 -0
  1199. package/dist/seo/config-store.js.map +1 -0
  1200. package/dist/seo/confirm-broken-links.d.ts +48 -0
  1201. package/dist/seo/confirm-broken-links.d.ts.map +1 -0
  1202. package/dist/seo/confirm-broken-links.js +135 -0
  1203. package/dist/seo/confirm-broken-links.js.map +1 -0
  1204. package/dist/seo/content-markdown.d.ts +26 -0
  1205. package/dist/seo/content-markdown.d.ts.map +1 -0
  1206. package/dist/seo/content-markdown.js +235 -0
  1207. package/dist/seo/content-markdown.js.map +1 -0
  1208. package/dist/seo/document-content.d.ts +18 -0
  1209. package/dist/seo/document-content.d.ts.map +1 -0
  1210. package/dist/seo/document-content.js +80 -0
  1211. package/dist/seo/document-content.js.map +1 -0
  1212. package/dist/seo/document-path.d.ts +33 -0
  1213. package/dist/seo/document-path.d.ts.map +1 -0
  1214. package/dist/seo/document-path.js +44 -0
  1215. package/dist/seo/document-path.js.map +1 -0
  1216. package/dist/seo/feeds.d.ts +44 -0
  1217. package/dist/seo/feeds.d.ts.map +1 -0
  1218. package/dist/seo/feeds.js +95 -0
  1219. package/dist/seo/feeds.js.map +1 -0
  1220. package/dist/seo/fix-brief-export.d.ts +47 -0
  1221. package/dist/seo/fix-brief-export.d.ts.map +1 -0
  1222. package/dist/seo/fix-brief-export.js +224 -0
  1223. package/dist/seo/fix-brief-export.js.map +1 -0
  1224. package/dist/seo/index-status.d.ts +56 -0
  1225. package/dist/seo/index-status.d.ts.map +1 -0
  1226. package/dist/seo/index-status.js +193 -0
  1227. package/dist/seo/index-status.js.map +1 -0
  1228. package/dist/seo/index.d.ts +68 -0
  1229. package/dist/seo/index.d.ts.map +1 -0
  1230. package/dist/seo/index.js +36 -0
  1231. package/dist/seo/index.js.map +1 -0
  1232. package/dist/seo/issue-batch-briefs.d.ts +37 -0
  1233. package/dist/seo/issue-batch-briefs.d.ts.map +1 -0
  1234. package/dist/seo/issue-batch-briefs.js +85 -0
  1235. package/dist/seo/issue-batch-briefs.js.map +1 -0
  1236. package/dist/seo/issue-content-brief.d.ts +51 -0
  1237. package/dist/seo/issue-content-brief.d.ts.map +1 -0
  1238. package/dist/seo/issue-content-brief.js +267 -0
  1239. package/dist/seo/issue-content-brief.js.map +1 -0
  1240. package/dist/seo/issue-content-research.d.ts +51 -0
  1241. package/dist/seo/issue-content-research.d.ts.map +1 -0
  1242. package/dist/seo/issue-content-research.js +303 -0
  1243. package/dist/seo/issue-content-research.js.map +1 -0
  1244. package/dist/seo/issue-fix-ai.d.ts +39 -0
  1245. package/dist/seo/issue-fix-ai.d.ts.map +1 -0
  1246. package/dist/seo/issue-fix-ai.js +320 -0
  1247. package/dist/seo/issue-fix-ai.js.map +1 -0
  1248. package/dist/seo/issue-fix-redirect.d.ts +18 -0
  1249. package/dist/seo/issue-fix-redirect.d.ts.map +1 -0
  1250. package/dist/seo/issue-fix-redirect.js +153 -0
  1251. package/dist/seo/issue-fix-redirect.js.map +1 -0
  1252. package/dist/seo/issue-fix.d.ts +72 -0
  1253. package/dist/seo/issue-fix.d.ts.map +1 -0
  1254. package/dist/seo/issue-fix.js +266 -0
  1255. package/dist/seo/issue-fix.js.map +1 -0
  1256. package/dist/seo/link-graph.d.ts +54 -0
  1257. package/dist/seo/link-graph.d.ts.map +1 -0
  1258. package/dist/seo/link-graph.js +0 -0
  1259. package/dist/seo/link-graph.js.map +1 -0
  1260. package/dist/seo/link-probe.d.ts +49 -0
  1261. package/dist/seo/link-probe.d.ts.map +1 -0
  1262. package/dist/seo/link-probe.js +147 -0
  1263. package/dist/seo/link-probe.js.map +1 -0
  1264. package/dist/seo/llms-select.d.ts +34 -0
  1265. package/dist/seo/llms-select.d.ts.map +1 -0
  1266. package/dist/seo/llms-select.js +67 -0
  1267. package/dist/seo/llms-select.js.map +1 -0
  1268. package/dist/seo/llms-txt.d.ts +40 -0
  1269. package/dist/seo/llms-txt.d.ts.map +1 -0
  1270. package/dist/seo/llms-txt.js +177 -0
  1271. package/dist/seo/llms-txt.js.map +1 -0
  1272. package/dist/seo/meta-tags.d.ts +108 -0
  1273. package/dist/seo/meta-tags.d.ts.map +1 -0
  1274. package/dist/seo/meta-tags.js +353 -0
  1275. package/dist/seo/meta-tags.js.map +1 -0
  1276. package/dist/seo/migrate-meta.d.ts +23 -0
  1277. package/dist/seo/migrate-meta.d.ts.map +1 -0
  1278. package/dist/seo/migrate-meta.js +114 -0
  1279. package/dist/seo/migrate-meta.js.map +1 -0
  1280. package/dist/seo/migrate-schema.d.ts +28 -0
  1281. package/dist/seo/migrate-schema.d.ts.map +1 -0
  1282. package/dist/seo/migrate-schema.js +119 -0
  1283. package/dist/seo/migrate-schema.js.map +1 -0
  1284. package/dist/seo/page-meta.d.ts +179 -0
  1285. package/dist/seo/page-meta.d.ts.map +1 -0
  1286. package/dist/seo/page-meta.js +710 -0
  1287. package/dist/seo/page-meta.js.map +1 -0
  1288. package/dist/seo/public-site-url.d.ts +16 -0
  1289. package/dist/seo/public-site-url.d.ts.map +1 -0
  1290. package/dist/seo/public-site-url.js +39 -0
  1291. package/dist/seo/public-site-url.js.map +1 -0
  1292. package/dist/seo/resolve-path.d.ts +22 -0
  1293. package/dist/seo/resolve-path.d.ts.map +1 -0
  1294. package/dist/seo/resolve-path.js +53 -0
  1295. package/dist/seo/resolve-path.js.map +1 -0
  1296. package/dist/seo/rewrite-public-origin.d.ts +12 -0
  1297. package/dist/seo/rewrite-public-origin.d.ts.map +1 -0
  1298. package/dist/seo/rewrite-public-origin.js +17 -0
  1299. package/dist/seo/rewrite-public-origin.js.map +1 -0
  1300. package/dist/seo/robots.d.ts +45 -0
  1301. package/dist/seo/robots.d.ts.map +1 -0
  1302. package/dist/seo/robots.js +90 -0
  1303. package/dist/seo/robots.js.map +1 -0
  1304. package/dist/seo/schema-sync.d.ts +65 -0
  1305. package/dist/seo/schema-sync.d.ts.map +1 -0
  1306. package/dist/seo/schema-sync.js +163 -0
  1307. package/dist/seo/schema-sync.js.map +1 -0
  1308. package/dist/seo/score.d.ts +132 -0
  1309. package/dist/seo/score.d.ts.map +1 -0
  1310. package/dist/seo/score.js +216 -0
  1311. package/dist/seo/score.js.map +1 -0
  1312. package/dist/seo/serving-origin.d.ts +42 -0
  1313. package/dist/seo/serving-origin.d.ts.map +1 -0
  1314. package/dist/seo/serving-origin.js +83 -0
  1315. package/dist/seo/serving-origin.js.map +1 -0
  1316. package/dist/seo/site-identity.d.ts +60 -0
  1317. package/dist/seo/site-identity.d.ts.map +1 -0
  1318. package/dist/seo/site-identity.js +109 -0
  1319. package/dist/seo/site-identity.js.map +1 -0
  1320. package/dist/seo/sitemap-notify.d.ts +123 -0
  1321. package/dist/seo/sitemap-notify.d.ts.map +1 -0
  1322. package/dist/seo/sitemap-notify.js +270 -0
  1323. package/dist/seo/sitemap-notify.js.map +1 -0
  1324. package/dist/seo/sitemap-public.d.ts +43 -0
  1325. package/dist/seo/sitemap-public.d.ts.map +1 -0
  1326. package/dist/seo/sitemap-public.js +213 -0
  1327. package/dist/seo/sitemap-public.js.map +1 -0
  1328. package/dist/seo/title-templates.d.ts +80 -0
  1329. package/dist/seo/title-templates.d.ts.map +1 -0
  1330. package/dist/seo/title-templates.js +175 -0
  1331. package/dist/seo/title-templates.js.map +1 -0
  1332. package/dist/server-site.d.ts +55 -0
  1333. package/dist/server-site.d.ts.map +1 -0
  1334. package/dist/server-site.js +184 -0
  1335. package/dist/server-site.js.map +1 -0
  1336. package/dist/setup/index.d.ts +68 -0
  1337. package/dist/setup/index.d.ts.map +1 -0
  1338. package/dist/setup/index.js +153 -0
  1339. package/dist/setup/index.js.map +1 -0
  1340. package/dist/site.d.ts +178 -0
  1341. package/dist/site.d.ts.map +1 -0
  1342. package/dist/site.js +222 -0
  1343. package/dist/site.js.map +1 -0
  1344. package/dist/storage/auto-wire.d.ts +14 -0
  1345. package/dist/storage/auto-wire.d.ts.map +1 -0
  1346. package/dist/storage/auto-wire.js +60 -0
  1347. package/dist/storage/auto-wire.js.map +1 -0
  1348. package/dist/storage/index.d.ts +21 -0
  1349. package/dist/storage/index.d.ts.map +1 -0
  1350. package/dist/storage/index.js +14 -0
  1351. package/dist/storage/index.js.map +1 -0
  1352. package/dist/templates/index.d.ts +16 -0
  1353. package/dist/templates/index.d.ts.map +1 -0
  1354. package/dist/templates/index.js +23 -0
  1355. package/dist/templates/index.js.map +1 -0
  1356. package/dist/upgrade/changelog.d.ts +18 -0
  1357. package/dist/upgrade/changelog.d.ts.map +1 -0
  1358. package/dist/upgrade/changelog.js +60 -0
  1359. package/dist/upgrade/changelog.js.map +1 -0
  1360. package/dist/upgrade/index.d.ts +7 -0
  1361. package/dist/upgrade/index.d.ts.map +1 -0
  1362. package/dist/upgrade/index.js +4 -0
  1363. package/dist/upgrade/index.js.map +1 -0
  1364. package/dist/upgrade/upgrade-pr.d.ts +16 -0
  1365. package/dist/upgrade/upgrade-pr.d.ts.map +1 -0
  1366. package/dist/upgrade/upgrade-pr.js +128 -0
  1367. package/dist/upgrade/upgrade-pr.js.map +1 -0
  1368. package/dist/upgrade/version-check.d.ts +59 -0
  1369. package/dist/upgrade/version-check.d.ts.map +1 -0
  1370. package/dist/upgrade/version-check.js +142 -0
  1371. package/dist/upgrade/version-check.js.map +1 -0
  1372. package/dist/validation/publish.d.ts +53 -0
  1373. package/dist/validation/publish.d.ts.map +1 -0
  1374. package/dist/validation/publish.js +75 -0
  1375. package/dist/validation/publish.js.map +1 -0
  1376. package/dist/webhooks/index.d.ts +99 -0
  1377. package/dist/webhooks/index.d.ts.map +1 -0
  1378. package/dist/webhooks/index.js +387 -0
  1379. package/dist/webhooks/index.js.map +1 -0
  1380. package/dist/webhooks/signing.d.ts +68 -0
  1381. package/dist/webhooks/signing.d.ts.map +1 -0
  1382. package/dist/webhooks/signing.js +98 -0
  1383. package/dist/webhooks/signing.js.map +1 -0
  1384. package/dist/workflow/index.d.ts +8 -0
  1385. package/dist/workflow/index.d.ts.map +1 -0
  1386. package/dist/workflow/index.js +56 -0
  1387. package/dist/workflow/index.js.map +1 -0
  1388. package/dist/workflows/index.d.ts +30 -0
  1389. package/dist/workflows/index.d.ts.map +1 -0
  1390. package/dist/workflows/index.js +14 -0
  1391. package/dist/workflows/index.js.map +1 -0
  1392. package/package.json +250 -0
  1393. package/prisma/migrations/0001_init/migration.sql +941 -0
  1394. package/prisma/migrations/0002_media_usage_field_path/migration.sql +11 -0
  1395. package/prisma/migrations/0003_ai_crawler_hits/migration.sql +21 -0
  1396. package/prisma/migrations/0004_drop_search_vector/migration.sql +4 -0
  1397. package/prisma/migrations/0005_script_tag_delivery_controls/migration.sql +8 -0
  1398. package/prisma/migrations/0006_totp_last_step/migration.sql +4 -0
  1399. package/prisma/migrations/0007_workflow_fk_drop_dead_enums/migration.sql +27 -0
  1400. package/prisma/migrations/0008_form_delivery_attempts/migration.sql +29 -0
  1401. package/prisma/migrations/0009_webhook_secret_rotation/migration.sql +6 -0
  1402. package/prisma/migrations/0010_media_is_decorative/migration.sql +3 -0
  1403. package/prisma/migrations/0011_media_content_hash/migration.sql +4 -0
  1404. package/prisma/migrations/0012_rename_to_prenta/migration.sql +89 -0
  1405. package/prisma/migrations/migration_lock.toml +3 -0
  1406. package/prisma/schema.prisma +995 -0
  1407. package/prisma/seed.ts +82 -0
  1408. package/schema-fragments/cms-schema.prisma +526 -0
@@ -0,0 +1,1574 @@
1
+ import { assertCollectionAccess, buildRelationLookup } from '../actions.js';
2
+ import { populateRelations, parsePopulateParam } from '../fields/relations.js';
3
+ import { verifySession } from '../auth/session.js';
4
+ import { resolveEmailSender } from '../email/sender.js';
5
+ import { workspaceDomainFromUrl } from '../auth/team.js';
6
+ import { getDB } from '../db.js';
7
+ import { formatBytes } from '../media/optimize.js';
8
+ import { calculateMediaSeoScore } from '../media/seo.js';
9
+ import { publicReadCacheControl } from '../cache/http.js';
10
+ import { verifyCaptcha, getCaptchaConfig, isCaptchaProviderError } from '../security/captcha.js';
11
+ import { logEvent } from '../security/audit.js';
12
+ import { FormNotFoundError, FormValidationError, FormConflictError, FormHasSubmissionsError, } from '../forms/service.js';
13
+ import { processSubmission, SubmissionValidationError, } from '../forms/submission.js';
14
+ import { validateSubmissionFiles, FORM_UPLOAD_ALLOWED_MIME_TYPES, DEFAULT_MAX_FILE_BYTES, } from '../forms/files.js';
15
+ import { resolveSubmissionFields } from '../forms/submission.js';
16
+ import { FormWebhookError } from '../forms/webhooks.js';
17
+ import { createRateLimiter } from '../security/rate-limit.js';
18
+ import { createLogger } from '../diagnostics/logger.js';
19
+ import { captureServerException } from '../diagnostics/sentry.js';
20
+ import { getClientIp, isResolvedIp } from '../security/client-ip.js';
21
+ import { safeFetch } from '../security/safe-fetch.js';
22
+ import { validateWebhookUrl } from '../security/webhook.js';
23
+ import { enforceSessionLimits } from '../security/session-limits.js';
24
+ import { verifyReauth } from '../security/reauth.js';
25
+ import { getPrentaConfig } from '../config/runtime.js';
26
+ import { hashApiKey, looksLikeApiKey, validateApiKeyScope, validateApiKeyGlobalScope, validateApiKeyMediaScope, validateApiKeyPageBuilderScope, validateApiKeyIp, } from '../security/api-key-enhanced.js';
27
+ import { guardedDb, ModelNotAvailableError } from './guarded-db.js';
28
+ // Opaque dynamic import so Turbopack/webpack won't statically analyze the specifier.
29
+ // Returns { put, del, ... } from @vercel/blob when available.
30
+ export async function importBlobStorage() {
31
+ const mod = '@vercel/' + 'blob';
32
+ return import(/* webpackIgnore: true */ mod);
33
+ }
34
+ export async function importAIPlugin() {
35
+ const mod = '@prenta/' + 'plugin-ai';
36
+ return import(/* webpackIgnore: true */ mod);
37
+ }
38
+ /**
39
+ * Build the system/user prompt for an inline co-author action. Kept
40
+ * deterministic and small so the governed runtime can route it to any
41
+ * configured provider/model.
42
+ */
43
+ export function buildCoauthorPrompt(action, body) {
44
+ const text = body.text ?? '';
45
+ switch (action) {
46
+ case 'expand': {
47
+ const hint = body.instructions ? ` Follow this guidance: ${body.instructions}.` : '';
48
+ return {
49
+ system: 'You are an expert writer. Expand the user\u2019s text with more detail and depth ' +
50
+ 'while preserving its meaning and voice.' +
51
+ hint +
52
+ ' Return only the expanded text, with no preamble.',
53
+ user: text,
54
+ maxTokens: 2048,
55
+ };
56
+ }
57
+ case 'compress': {
58
+ const hint = typeof body.targetLength === 'number' && body.targetLength > 0
59
+ ? ` Aim for about ${body.targetLength} words.`
60
+ : '';
61
+ return {
62
+ system: 'You are an expert editor. Tighten the user\u2019s text, removing redundancy while ' +
63
+ 'preserving the key information and voice.' +
64
+ hint +
65
+ ' Return only the edited text, with no preamble.',
66
+ user: text,
67
+ maxTokens: 1024,
68
+ };
69
+ }
70
+ case 'proofread':
71
+ return {
72
+ system: 'You are a meticulous proofreader. Correct grammar, spelling, and punctuation in the ' +
73
+ 'user\u2019s text. Preserve its meaning and voice. Return only the corrected text, ' +
74
+ 'with no preamble.',
75
+ user: text,
76
+ maxTokens: 2048,
77
+ };
78
+ case 'rewrite':
79
+ default: {
80
+ const style = body.style ? ` Write in a ${body.style} style.` : '';
81
+ const tone = body.tone ? ` Use a ${body.tone} tone.` : '';
82
+ return {
83
+ system: 'You are an expert editor. Rewrite the user\u2019s text to improve clarity and flow ' +
84
+ 'while preserving its meaning.' +
85
+ style +
86
+ tone +
87
+ ' Return only the rewritten text, with no preamble.',
88
+ user: text,
89
+ maxTokens: 2048,
90
+ };
91
+ }
92
+ }
93
+ }
94
+ /**
95
+ * Common helper for the read endpoints. Reads the `?populate=` query param,
96
+ * fetches the target collection's field schema, and expands every relation
97
+ * reference into a {@link RelationSummary}. When the caller didn't ask for
98
+ * population (or the doc has no `data` field) we short-circuit and return
99
+ * the doc untouched.
100
+ *
101
+ * Lives here (not in `actions.ts`) because the rest of the actions API
102
+ * stays purely DB-shaped — population is an HTTP concern.
103
+ */
104
+ export async function maybePopulate(request, collection, doc, dbInstance, viewer) {
105
+ if (!doc)
106
+ return doc;
107
+ const url = new URL(request.url);
108
+ const opts = parsePopulateParam(url.searchParams.get('populate'));
109
+ if (!opts)
110
+ return doc;
111
+ const cfg = getPrentaConfig();
112
+ const fields = cfg?.collections?.[collection]?.fields;
113
+ if (!fields)
114
+ return doc;
115
+ const data = doc.data && typeof doc.data === 'object' ? doc.data : null;
116
+ if (!data)
117
+ return doc;
118
+ try {
119
+ const populated = await populateRelations(fields, data, buildRelationLookup(dbInstance, { viewer }), opts);
120
+ return { ...doc, data: populated };
121
+ }
122
+ catch (err) {
123
+ // Never let a populate failure mask the underlying document — log and
124
+ // return the unpopulated doc.
125
+ logger.error('populate failed', {
126
+ collection,
127
+ error: err instanceof Error ? err.message : String(err),
128
+ });
129
+ return doc;
130
+ }
131
+ }
132
+ export const SECURITY_HEADERS = {
133
+ 'Content-Type': 'application/json',
134
+ 'X-Content-Type-Options': 'nosniff',
135
+ 'X-Frame-Options': 'DENY',
136
+ 'Referrer-Policy': 'strict-origin-when-cross-origin',
137
+ };
138
+ const logger = createLogger('api');
139
+ export function json(data, status = 200) {
140
+ return new Response(JSON.stringify(data), {
141
+ status,
142
+ headers: { ...SECURITY_HEADERS },
143
+ });
144
+ }
145
+ /**
146
+ * Variant of `json()` that emits the Phase-5.5 public-read
147
+ * `Cache-Control` header when the active CMS config opts in.
148
+ *
149
+ * - Session-cookie requests always get `private, no-store`.
150
+ * - API-key / unauthenticated requests get
151
+ * `public, s-maxage=N, stale-while-revalidate=M` when
152
+ * `cache.publicReads.enabled` is true.
153
+ * - Otherwise no `Cache-Control` header is set (i.e. the response is
154
+ * uncacheable downstream, matching the pre-5.5 behaviour).
155
+ *
156
+ * Use this for the public read endpoints (`/collections/:slug`,
157
+ * `/collections/:slug/:id`, `/globals/:slug`, `/resolve`). Mutating
158
+ * endpoints stay on the plain `json()` so the CDN never caches a write
159
+ * response by accident.
160
+ */
161
+ export function jsonWithCache(data, request, status = 200) {
162
+ const cacheControl = publicReadCacheControl(request, getPrentaConfig()?.cache);
163
+ const headers = { ...SECURITY_HEADERS };
164
+ if (cacheControl)
165
+ headers['Cache-Control'] = cacheControl;
166
+ return new Response(JSON.stringify(data), { status, headers });
167
+ }
168
+ export function errorResponse(message, status) {
169
+ return json({ error: message }, status);
170
+ }
171
+ export function internalError(err, context) {
172
+ if (err instanceof ModelNotAvailableError) {
173
+ return modelNotAvailable(err.model);
174
+ }
175
+ const msg = err instanceof Error ? err.message : String(err);
176
+ logger.error(msg, context ? { context } : undefined);
177
+ // Fire-and-forget: capture lazily imports the optional @sentry/node and never
178
+ // throws, so we don't await it before returning the error response.
179
+ void captureServerException(err, { scope: 'api', extra: context ? { context } : undefined });
180
+ return errorResponse('Internal server error', 500);
181
+ }
182
+ /**
183
+ * Map a thrown action-layer error to the right HTTP status. The data layer
184
+ * throws plain `Error`s with stable, human-readable prefixes for client-fixable
185
+ * problems (publish validation, access/permission, not-found); those are 4xx
186
+ * and the message is safe to return verbatim. Anything else falls through to
187
+ * {@link internalError} (500, generic message, logged).
188
+ */
189
+ export function actionError(err, context) {
190
+ if (err instanceof ModelNotAvailableError)
191
+ return modelNotAvailable(err.model);
192
+ const conflict = uniqueConstraintConflict(err);
193
+ if (conflict)
194
+ return errorResponse(conflict, 409);
195
+ const msg = err instanceof Error ? err.message : String(err);
196
+ if (/^Cannot publish —/.test(msg))
197
+ return errorResponse(msg, 422);
198
+ if (/^Invalid media field/i.test(msg))
199
+ return errorResponse(msg, 400);
200
+ if (/insufficient permissions/i.test(msg) || /^Access denied/i.test(msg)) {
201
+ return errorResponse(msg, 403);
202
+ }
203
+ if (/not found/i.test(msg))
204
+ return errorResponse(msg, 404);
205
+ return internalError(err, context);
206
+ }
207
+ /**
208
+ * Detect a Prisma unique-constraint violation (P2002) via its stable `code`,
209
+ * without importing the heavy Prisma error class. Returns a client-safe 409
210
+ * message, or null when `err` is not a unique-constraint error. The usual
211
+ * trigger is a duplicate (collection, slug) on create/rename/publish — which
212
+ * previously fell through to a 500 (#560); a genuine live-slug clash is a
213
+ * client-fixable conflict, so it belongs at 409.
214
+ */
215
+ function uniqueConstraintConflict(err) {
216
+ if (!err || typeof err !== 'object' || err.code !== 'P2002') {
217
+ return null;
218
+ }
219
+ const target = err.meta?.target;
220
+ const fields = Array.isArray(target)
221
+ ? target.map(String)
222
+ : typeof target === 'string'
223
+ ? [target]
224
+ : [];
225
+ if (fields.some((f) => f.toLowerCase().includes('slug'))) {
226
+ return 'A document with this slug already exists in this collection.';
227
+ }
228
+ return 'That value is already in use.';
229
+ }
230
+ export function clampPageSize(raw, max = 100, fallback = 20) {
231
+ const n = Number(raw) || fallback;
232
+ return Math.min(Math.max(1, n), max);
233
+ }
234
+ /**
235
+ * Clamp a 1-based list `page` query param. Negative / zero / NaN values must
236
+ * never reach Prisma `skip` (negative OFFSET → validation error → opaque 500).
237
+ * Mirrors forms/users/redirects (`Math.max(1, …)`).
238
+ */
239
+ export function clampPage(raw, fallback = 1) {
240
+ const n = Number(raw);
241
+ if (!Number.isFinite(n) || n < 1)
242
+ return fallback;
243
+ return Math.floor(n);
244
+ }
245
+ export function mediaUrl(storageKey) {
246
+ const value = String(storageKey ?? '');
247
+ if (!value)
248
+ return '';
249
+ return value.startsWith('http://') || value.startsWith('https://') || value.startsWith('/')
250
+ ? value
251
+ : '';
252
+ }
253
+ export function normalizeMediaItem(media) {
254
+ const width = typeof media.width === 'number' ? media.width : null;
255
+ const height = typeof media.height === 'number' ? media.height : null;
256
+ const sizeBytes = Number(media.fileSize ?? media.sizeBytes ?? 0);
257
+ const isDecorative = media.isDecorative === true;
258
+ const usedOnTitles = Array.isArray(media.mediaUsages)
259
+ ? media.mediaUsages
260
+ .map((usage) => usage?.document?.title)
261
+ .filter((title) => typeof title === 'string' && title.trim().length > 0)
262
+ : [];
263
+ // Derived on every read (never persisted) so the score can't drift from the
264
+ // metadata — see `media/seo.ts` for the weight contract.
265
+ const seo = calculateMediaSeoScore({
266
+ fileName: media.filename ?? media.name ?? null,
267
+ mimeType: media.mimeType ?? media.type ?? null,
268
+ altText: media.altText ?? media.altTag ?? null,
269
+ title: media.title ?? null,
270
+ caption: media.caption ?? null,
271
+ description: media.description ?? null,
272
+ focusKeyword: media.focusKeyword ?? null,
273
+ sizeBytes,
274
+ width,
275
+ height,
276
+ isDecorative,
277
+ usedOnTitles,
278
+ });
279
+ return {
280
+ ...media,
281
+ name: media.filename ?? media.name ?? '',
282
+ type: media.mimeType ?? media.type ?? '',
283
+ size: formatBytes(sizeBytes),
284
+ sizeBytes,
285
+ date: media.createdAt ?? media.updatedAt ?? null,
286
+ url: mediaUrl(media.storageKey ?? media.url),
287
+ dimensions: width && height ? `${width}x${height}` : undefined,
288
+ format: typeof media.mimeType === 'string' ? media.mimeType.split('/')[1] : undefined,
289
+ altTag: media.altText ?? media.altTag ?? '',
290
+ isDecorative,
291
+ ...(media.aiAltText != null ? { aiAltSuggestion: media.aiAltText } : {}),
292
+ title: media.title ?? '',
293
+ seoScore: seo.score,
294
+ seoGrade: seo.grade,
295
+ seoIssues: seo.issues,
296
+ };
297
+ }
298
+ /**
299
+ * Build the admin editor route for a document that references a media asset.
300
+ * Pages own the `/pages/*` namespace; post-type collections use
301
+ * `/posts/:type/:id/edit`; everything else falls back to the generic
302
+ * collection editor. Kept in sync with the route table in `AdminRoot.tsx`.
303
+ */
304
+ export function mediaUsageEditPath(collection, id) {
305
+ if (collection === 'pages')
306
+ return `/pages/${id}/edit`;
307
+ const def = getPrentaConfig()?.collections?.[collection];
308
+ if (def?.type === 'post')
309
+ return `/posts/${collection}/${id}/edit`;
310
+ return `/collections/${collection}/${id}`;
311
+ }
312
+ /**
313
+ * Map the `MediaUsage` join rows (with their related document) into the
314
+ * `{ page, path }[]` shape the admin drawer renders. Soft-deleted documents
315
+ * are excluded so trashed pages don't keep an asset looking "in use".
316
+ */
317
+ export function buildMediaUsedOn(mediaUsages) {
318
+ if (!Array.isArray(mediaUsages))
319
+ return [];
320
+ const seen = new Set();
321
+ const out = [];
322
+ for (const usage of mediaUsages) {
323
+ const doc = usage?.document;
324
+ if (!doc || doc.deletedAt || seen.has(doc.id))
325
+ continue;
326
+ seen.add(doc.id);
327
+ out.push({
328
+ page: doc.title || '(untitled)',
329
+ path: mediaUsageEditPath(doc.collection, doc.id),
330
+ });
331
+ }
332
+ return out;
333
+ }
334
+ export const MEDIA_TITLE_STOPWORDS = new Set([
335
+ 'a',
336
+ 'an',
337
+ 'the',
338
+ 'of',
339
+ 'and',
340
+ 'or',
341
+ 'to',
342
+ 'in',
343
+ 'on',
344
+ 'for',
345
+ 'with',
346
+ 'at',
347
+ 'by',
348
+ 'from',
349
+ 'is',
350
+ 'are',
351
+ 'as',
352
+ ]);
353
+ /**
354
+ * Derive a concise, Title-Cased title from AI-generated alt text. The alt
355
+ * text is already a model output describing the image, so the title stays
356
+ * grounded in real content rather than the filename.
357
+ */
358
+ export function deriveMediaTitleFromAlt(alt) {
359
+ if (!alt)
360
+ return '';
361
+ const firstClause = (alt.split(/[.;:\n]/)[0] ?? '').trim();
362
+ const titled = firstClause
363
+ .split(/\s+/)
364
+ .slice(0, 9)
365
+ .map((word, i) => {
366
+ const lower = word.toLowerCase();
367
+ if (i > 0 && MEDIA_TITLE_STOPWORDS.has(lower))
368
+ return lower;
369
+ return word.charAt(0).toUpperCase() + word.slice(1);
370
+ })
371
+ .join(' ');
372
+ return titled.slice(0, 80).replace(/[\s,]+$/, '');
373
+ }
374
+ export function asRecord(value) {
375
+ return value && typeof value === 'object' && !Array.isArray(value)
376
+ ? value
377
+ : {};
378
+ }
379
+ export function normalizeSubmission(submission) {
380
+ const data = asRecord(submission.data);
381
+ const attribution = asRecord(submission.attribution);
382
+ return {
383
+ ...submission,
384
+ name: String(data.name ?? submission.name ?? ''),
385
+ email: String(data.email ?? submission.email ?? ''),
386
+ phone: data.phone ?? submission.phone,
387
+ message: String(data.message ?? submission.message ?? ''),
388
+ status: submission.status ?? 'new',
389
+ attribution: {
390
+ source: String(attribution.source ?? '(direct)'),
391
+ medium: String(attribution.medium ?? '(none)'),
392
+ campaign: String(attribution.campaign ?? ''),
393
+ term: String(attribution.term ?? ''),
394
+ content: String(attribution.content ?? ''),
395
+ landingPage: String(attribution.landingPage ?? ''),
396
+ referrer: String(attribution.referrer ?? ''),
397
+ deviceType: attribution.deviceType ?? 'Desktop',
398
+ clickIds: asRecord(attribution.clickIds),
399
+ capturedAt: attribution.capturedAt ?? submission.submittedAt ?? submission.createdAt ?? null,
400
+ },
401
+ };
402
+ }
403
+ /**
404
+ * Map a forms-service domain error to an HTTP response. Returns `null` for
405
+ * unrecognised errors so the caller can fall through to `internalError`.
406
+ */
407
+ export function mapFormError(err) {
408
+ if (err instanceof FormNotFoundError)
409
+ return errorResponse(err.message, 404);
410
+ if (err instanceof FormValidationError) {
411
+ return json({ error: err.message, code: 'INVALID_SCHEMA', details: err.errors }, 400);
412
+ }
413
+ if (err instanceof FormConflictError)
414
+ return errorResponse(err.message, 409);
415
+ if (err instanceof FormHasSubmissionsError) {
416
+ return json({ error: err.message, code: 'FORM_HAS_SUBMISSIONS', submissionCount: err.submissionCount }, 409);
417
+ }
418
+ if (err instanceof FormWebhookError)
419
+ return errorResponse(err.message, err.status);
420
+ return null;
421
+ }
422
+ export function parseFormsQuery(url) {
423
+ const sp = url.searchParams;
424
+ const status = sp.get('status');
425
+ const notify = sp.get('notifyEnabled');
426
+ return {
427
+ search: sp.get('search') ?? undefined,
428
+ status: status === 'active' || status === 'draft' || status === 'archived' ? status : undefined,
429
+ notifyEnabled: notify == null ? undefined : notify === 'true',
430
+ sortBy: sp.get('sortBy') ?? undefined,
431
+ sortDirection: sp.get('sortDirection') === 'asc'
432
+ ? 'asc'
433
+ : sp.get('sortDirection') === 'desc'
434
+ ? 'desc'
435
+ : undefined,
436
+ page: sp.get('page') ? clampPage(sp.get('page')) : undefined,
437
+ pageSize: sp.get('pageSize') ? clampPageSize(sp.get('pageSize')) : undefined,
438
+ };
439
+ }
440
+ export function parseEntriesQuery(url) {
441
+ const sp = url.searchParams;
442
+ const spam = sp.get('spamStatus');
443
+ const boolParam = (key) => {
444
+ const v = sp.get(key);
445
+ return v == null ? undefined : v === 'true';
446
+ };
447
+ return {
448
+ search: sp.get('search') ?? undefined,
449
+ formId: sp.get('formId') ?? sp.get('form') ?? undefined,
450
+ unread: boolParam('unread'),
451
+ starred: boolParam('starred'),
452
+ archived: boolParam('archived'),
453
+ spamStatus: spam === 'clean' || spam === 'suspected' || spam === 'spam'
454
+ ? spam
455
+ : undefined,
456
+ dateFrom: sp.get('dateFrom') ?? undefined,
457
+ dateTo: sp.get('dateTo') ?? undefined,
458
+ thisWeek: boolParam('thisWeek'),
459
+ sortBy: sp.get('sortBy') ?? undefined,
460
+ sortDirection: sp.get('sortDirection') === 'asc' ? 'asc' : undefined,
461
+ page: sp.get('page') ? clampPage(sp.get('page')) : undefined,
462
+ pageSize: sp.get('pageSize') ? clampPageSize(sp.get('pageSize')) : undefined,
463
+ };
464
+ }
465
+ // ─── Public form submission helpers ─────────────────────────────────────────
466
+ /**
467
+ * Build CORS headers for the public form endpoints. When the form has no
468
+ * embed allowlist we reflect the requesting origin (open embedding). When an
469
+ * allowlist is set we only echo the origin if it matches, so a disallowed site
470
+ * cannot read the response. Browser-level CORS is complemented by a hard
471
+ * server-side origin check in the submit handler.
472
+ */
473
+ export function formCorsHeaders(request, allowedDomains) {
474
+ const origin = request.headers.get('origin');
475
+ const headers = {
476
+ 'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
477
+ 'Access-Control-Allow-Headers': 'Content-Type, X-Requested-With',
478
+ 'Access-Control-Max-Age': '600',
479
+ Vary: 'Origin',
480
+ };
481
+ if (!allowedDomains || allowedDomains.length === 0) {
482
+ headers['Access-Control-Allow-Origin'] = origin || '*';
483
+ }
484
+ else if (origin && isOriginAllowed(origin, allowedDomains)) {
485
+ headers['Access-Control-Allow-Origin'] = origin;
486
+ }
487
+ return headers;
488
+ }
489
+ export function normalizeAllowedHost(domain) {
490
+ return domain
491
+ .trim()
492
+ .toLowerCase()
493
+ .replace(/^https?:\/\//, '')
494
+ .replace(/\/.*$/, '')
495
+ .replace(/^\*\./, '');
496
+ }
497
+ export function isOriginAllowed(origin, allowedDomains) {
498
+ let host;
499
+ try {
500
+ host = new URL(origin).hostname.toLowerCase();
501
+ }
502
+ catch {
503
+ return false;
504
+ }
505
+ return allowedDomains
506
+ .map(normalizeAllowedHost)
507
+ .some((a) => a !== '' && (host === a || host.endsWith(`.${a}`)));
508
+ }
509
+ export function jsonWithHeaders(data, status, extra) {
510
+ return new Response(JSON.stringify(data), {
511
+ status,
512
+ headers: { ...SECURITY_HEADERS, ...extra },
513
+ });
514
+ }
515
+ /**
516
+ * Parse a public submission request into field values, files, and metadata.
517
+ * Supports both `application/json` (no files) and `multipart/form-data`
518
+ * (files + fields). Unknown/oversized bodies are rejected by the caller's
519
+ * Content-Length gate before this runs.
520
+ */
521
+ export async function parseSubmissionRequest(request) {
522
+ const contentType = request.headers.get('content-type') ?? '';
523
+ if (contentType.includes('multipart/form-data')) {
524
+ const form = await request.formData();
525
+ return parseFlatSubmissionEntries(form.entries());
526
+ }
527
+ if (contentType.includes('application/x-www-form-urlencoded')) {
528
+ const text = await request.text();
529
+ return parseFlatSubmissionEntries(new URLSearchParams(text).entries());
530
+ }
531
+ // JSON body.
532
+ const body = (await request.json().catch(() => null));
533
+ if (!body)
534
+ return null;
535
+ const values = body.fields && typeof body.fields === 'object' && !Array.isArray(body.fields)
536
+ ? body.fields
537
+ : null;
538
+ if (!values)
539
+ return null;
540
+ return {
541
+ values,
542
+ files: [],
543
+ meta: {
544
+ attribution: safeJsonObject(body.attribution),
545
+ elapsedMs: typeof body.elapsedMs === 'number' && Number.isFinite(body.elapsedMs)
546
+ ? body.elapsedMs
547
+ : null,
548
+ pageUrl: typeof body.pageUrl === 'string' ? body.pageUrl : null,
549
+ captchaToken: typeof body.captchaToken === 'string' ? body.captchaToken : '',
550
+ },
551
+ };
552
+ }
553
+ async function readSubmissionFile(value) {
554
+ if (typeof value !== 'object' || value === null || !('arrayBuffer' in value))
555
+ return null;
556
+ const file = value;
557
+ if (file.size === 0 && !file.name)
558
+ return null;
559
+ const buffer = Buffer.from(await file.arrayBuffer());
560
+ return {
561
+ fieldKey: '',
562
+ fileName: file.name || 'file',
563
+ mimeType: file.type || 'application/octet-stream',
564
+ size: buffer.byteLength,
565
+ buffer,
566
+ };
567
+ }
568
+ function parseFlatSubmissionEntries(entries) {
569
+ return (async () => {
570
+ const values = {};
571
+ const files = [];
572
+ let attribution = null;
573
+ let elapsedMs = null;
574
+ let pageUrl = null;
575
+ let captchaToken = '';
576
+ let renderStartedAt = null;
577
+ for (const [key, rawValue] of entries) {
578
+ if (typeof rawValue === 'object' && rawValue !== null && 'arrayBuffer' in rawValue) {
579
+ const file = await readSubmissionFile(rawValue);
580
+ if (file) {
581
+ file.fieldKey = key;
582
+ files.push(file);
583
+ }
584
+ continue;
585
+ }
586
+ const str = String(rawValue);
587
+ if (key === '__attribution') {
588
+ attribution = safeJsonObject(str);
589
+ }
590
+ else if (key === '__elapsedMs') {
591
+ const n = Number(str);
592
+ elapsedMs = Number.isFinite(n) ? n : null;
593
+ }
594
+ else if (key === '__renderStartedAt') {
595
+ const n = Number(str);
596
+ renderStartedAt = Number.isFinite(n) ? n : null;
597
+ }
598
+ else if (key === '__pageUrl') {
599
+ pageUrl = str || null;
600
+ }
601
+ else if (key === '__captchaToken' || key === 'captchaToken') {
602
+ captchaToken = str;
603
+ }
604
+ else if (Object.prototype.hasOwnProperty.call(values, key)) {
605
+ const existing = values[key];
606
+ values[key] = Array.isArray(existing) ? [...existing, str] : [existing, str];
607
+ }
608
+ else {
609
+ values[key] = str;
610
+ }
611
+ }
612
+ if (elapsedMs === null && renderStartedAt !== null) {
613
+ elapsedMs = Math.max(0, Date.now() - renderStartedAt);
614
+ }
615
+ return { values, files, meta: { attribution, elapsedMs, pageUrl, captchaToken } };
616
+ })();
617
+ }
618
+ export function safeJsonObject(value) {
619
+ if (value && typeof value === 'object' && !Array.isArray(value)) {
620
+ return value;
621
+ }
622
+ if (typeof value === 'string' && value.trim()) {
623
+ try {
624
+ const parsed = JSON.parse(value);
625
+ if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
626
+ return parsed;
627
+ }
628
+ }
629
+ catch {
630
+ /* ignore malformed attribution blob */
631
+ }
632
+ }
633
+ return null;
634
+ }
635
+ /**
636
+ * Shared public-submission handler used by both the slug-based public route and
637
+ * the legacy by-id route. Enforces per-IP+form rate limiting, embed-origin
638
+ * allowlisting, optional CAPTCHA, file validation, then runs the core
639
+ * submission pipeline. Always responds with CORS headers so embedded forms can
640
+ * read the result.
641
+ */
642
+ export async function submitToForm(request, form) {
643
+ const allowedDomains = form.embedSettings?.allowedDomains;
644
+ const cors = formCorsHeaders(request, allowedDomains);
645
+ const ip = getClientIp(request);
646
+ const rlKey = isResolvedIp(ip) ? `formsubmit:${ip}:${form.id}` : `formsubmit:_unknown:${form.id}`;
647
+ if (!(await checkRateLimitAsync(formLimiterGlobal, rlKey))) {
648
+ return jsonWithHeaders({ error: 'Too many submissions. Please try again later.' }, 429, cors);
649
+ }
650
+ // Hard server-side origin check when an embed allowlist is configured —
651
+ // browser CORS alone does not prevent a non-browser client from posting.
652
+ if (allowedDomains && allowedDomains.length > 0) {
653
+ const origin = request.headers.get('origin');
654
+ if (origin && !isOriginAllowed(origin, allowedDomains)) {
655
+ return jsonWithHeaders({ error: 'This form cannot be submitted from this origin.' }, 403, cors);
656
+ }
657
+ }
658
+ const parsed = await parseSubmissionRequest(request);
659
+ if (!parsed) {
660
+ return jsonWithHeaders({ error: 'Invalid or missing submission body.' }, 400, cors);
661
+ }
662
+ const captchaConfig = getCaptchaConfig();
663
+ if (captchaConfig.provider !== 'none' && captchaConfig.enforce) {
664
+ const result = await verifyCaptcha(parsed.meta.captchaToken, captchaConfig, isResolvedIp(ip) ? ip : undefined);
665
+ if (!result.success) {
666
+ const providerError = isCaptchaProviderError(result);
667
+ if (providerError && captchaConfig.onProviderError === 'fail-open') {
668
+ void logEvent({
669
+ event: 'captcha.provider_error',
670
+ details: {
671
+ formId: form.id,
672
+ formSlug: form.slug,
673
+ provider: captchaConfig.provider,
674
+ policy: 'fail-open',
675
+ errorCodes: result.errorCodes ?? [],
676
+ },
677
+ }).catch(() => { });
678
+ }
679
+ else {
680
+ void logEvent({
681
+ event: providerError ? 'captcha.provider_error' : 'captcha.verify_failed',
682
+ details: {
683
+ formId: form.id,
684
+ formSlug: form.slug,
685
+ provider: captchaConfig.provider,
686
+ policy: providerError ? captchaConfig.onProviderError : 'reject',
687
+ errorCodes: result.errorCodes ?? [],
688
+ score: result.score ?? null,
689
+ },
690
+ }).catch(() => { });
691
+ return jsonWithHeaders({
692
+ error: providerError
693
+ ? 'CAPTCHA provider unavailable. Please try again shortly.'
694
+ : 'CAPTCHA verification failed. Please try again.',
695
+ code: providerError ? 'CAPTCHA_PROVIDER_ERROR' : 'CAPTCHA_FAILED',
696
+ }, 403, cors);
697
+ }
698
+ }
699
+ }
700
+ if (parsed.files.length > 0) {
701
+ const fileCfg = getPrentaConfig()?.forms?.fileUpload;
702
+ if (fileCfg?.enabled === false) {
703
+ return jsonWithHeaders({ error: 'File uploads are not enabled for this site.' }, 400, cors);
704
+ }
705
+ const { fields } = await resolveSubmissionFields(form);
706
+ const fileCheck = validateSubmissionFiles(fields, parsed.files, {
707
+ maxSizeBytes: fileCfg?.maxSizeMB ? fileCfg.maxSizeMB * 1024 * 1024 : DEFAULT_MAX_FILE_BYTES,
708
+ allowedMimeTypes: fileCfg?.allowedMimeTypes ?? FORM_UPLOAD_ALLOWED_MIME_TYPES,
709
+ });
710
+ if (!fileCheck.ok) {
711
+ return jsonWithHeaders({ error: 'File validation failed.', code: 'INVALID_FILES', details: fileCheck.errors }, 422, cors);
712
+ }
713
+ }
714
+ const ctx = {
715
+ values: parsed.values,
716
+ files: parsed.files,
717
+ ip,
718
+ userAgent: request.headers.get('user-agent'),
719
+ referrer: request.headers.get('referer'),
720
+ origin: request.headers.get('origin'),
721
+ pageUrl: parsed.meta.pageUrl,
722
+ attribution: parsed.meta.attribution,
723
+ elapsedMs: parsed.meta.elapsedMs,
724
+ };
725
+ try {
726
+ const result = await processSubmission(form, ctx);
727
+ const confirmation = result.confirmation;
728
+ if (requestPrefersHtmlResponse(request)) {
729
+ if (confirmation.type === 'redirect' && confirmation.redirectUrl) {
730
+ return Response.redirect(confirmation.redirectUrl, 303);
731
+ }
732
+ const message = confirmation.message ??
733
+ form.successMessage ??
734
+ 'Thank you! Your submission has been received.';
735
+ return htmlWithHeaders(renderSubmissionResultHtml(message, false), 201, cors);
736
+ }
737
+ return jsonWithHeaders({
738
+ data: {
739
+ success: true,
740
+ submissionId: result.submissionId,
741
+ entryNumber: result.entryNumber,
742
+ confirmation: result.confirmation,
743
+ },
744
+ }, 201, cors);
745
+ }
746
+ catch (err) {
747
+ if (err instanceof SubmissionValidationError) {
748
+ if (requestPrefersHtmlResponse(request)) {
749
+ return htmlWithHeaders(renderSubmissionResultHtml('Please check the form and try again.', true), 422, cors);
750
+ }
751
+ return jsonWithHeaders({ error: 'Validation failed.', code: 'VALIDATION_ERROR', details: err.fieldErrors }, 422, cors);
752
+ }
753
+ return internalError(err);
754
+ }
755
+ }
756
+ /** Native HTML form posts typically prefer HTML; fetch/XHR clients send Accept: application/json. */
757
+ export function requestPrefersHtmlResponse(request) {
758
+ const accept = request.headers.get('accept') ?? '';
759
+ if (accept.includes('application/json') && !accept.includes('text/html'))
760
+ return false;
761
+ const contentType = request.headers.get('content-type') ?? '';
762
+ if (contentType.includes('application/x-www-form-urlencoded') ||
763
+ contentType.includes('multipart/form-data')) {
764
+ return !accept.includes('application/json') || accept.includes('text/html');
765
+ }
766
+ return accept.includes('text/html') && !accept.includes('application/json');
767
+ }
768
+ function escapeHtmlText(value) {
769
+ return value
770
+ .replace(/&/g, '&amp;')
771
+ .replace(/</g, '&lt;')
772
+ .replace(/>/g, '&gt;')
773
+ .replace(/"/g, '&quot;');
774
+ }
775
+ function renderSubmissionResultHtml(message, isError) {
776
+ const title = isError ? 'Submission error' : 'Thank you';
777
+ const role = isError ? 'alert' : 'status';
778
+ return `<!DOCTYPE html>
779
+ <html lang="en">
780
+ <head>
781
+ <meta charset="utf-8" />
782
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
783
+ <title>${escapeHtmlText(title)}</title>
784
+ </head>
785
+ <body>
786
+ <main><p role="${role}">${escapeHtmlText(message)}</p></main>
787
+ </body>
788
+ </html>`;
789
+ }
790
+ function htmlWithHeaders(body, status, extra) {
791
+ return new Response(body, {
792
+ status,
793
+ headers: {
794
+ ...SECURITY_HEADERS,
795
+ 'Content-Type': 'text/html; charset=utf-8',
796
+ ...extra,
797
+ },
798
+ });
799
+ }
800
+ export function normalizeRedirect(redirect) {
801
+ return {
802
+ ...redirect,
803
+ from: redirect.from ?? redirect.source ?? '',
804
+ to: redirect.to ?? redirect.destination ?? '',
805
+ type: String(redirect.type ?? redirect.statusCode ?? 301),
806
+ hits: Number(redirect.hits ?? 0),
807
+ active: redirect.active ?? true,
808
+ };
809
+ }
810
+ export function normalizeSeoPage(page) {
811
+ const data = asRecord(page.data);
812
+ const structuredData = asRecord(page.structuredData);
813
+ const issueValue = structuredData.issues ?? data.issues;
814
+ const issueCount = Array.isArray(issueValue) ? issueValue.length : Number(issueValue ?? 0);
815
+ const slug = String(data.slug ?? page.slug ?? page.id ?? '');
816
+ const schemaType = data.schemaType ?? data.structuredDataType ?? structuredData.schemaType ?? '';
817
+ // Return ONLY the derived summary fields — never the raw `data` /
818
+ // `structuredData` JSONB blobs. This endpoint powers a summary table; for
819
+ // section-built pages those blobs are large, so spreading them made the
820
+ // response grow linearly with page content (megabytes for a table that only
821
+ // needs a dozen scalar fields).
822
+ return {
823
+ id: page.id,
824
+ collection: page.collection,
825
+ updatedAt: page.updatedAt,
826
+ url: data.url ?? (slug === 'home' ? '/' : `/${slug}`),
827
+ title: data.title ?? page.title ?? 'Untitled',
828
+ score: Number(structuredData.score ?? data.score ?? 0),
829
+ issues: issueCount,
830
+ metaTitle: data.metaTitle ?? '',
831
+ metaDescription: data.metaDescription ?? '',
832
+ canonical: data.canonical ?? data.canonicalUrl ?? '',
833
+ schemaType,
834
+ };
835
+ }
836
+ export function hasModel(d, name) {
837
+ try {
838
+ return d[name] && typeof d[name].findMany === 'function';
839
+ }
840
+ catch {
841
+ return false;
842
+ }
843
+ }
844
+ /** Format a number compactly, e.g. 8400 -> "8.4K", 1_200_000 -> "1.2M". */
845
+ export function formatCompactNumber(n) {
846
+ if (!Number.isFinite(n))
847
+ return '0';
848
+ if (Math.abs(n) >= 1_000_000)
849
+ return `${(n / 1_000_000).toFixed(1)}M`;
850
+ if (Math.abs(n) >= 1_000)
851
+ return `${(n / 1_000).toFixed(1)}K`;
852
+ return String(Math.round(n));
853
+ }
854
+ /** Minimal CSV line parser supporting double-quoted fields with escaped quotes. */
855
+ export function parseCsvLine(line) {
856
+ const out = [];
857
+ let cur = '';
858
+ let inQuotes = false;
859
+ for (let i = 0; i < line.length; i++) {
860
+ const ch = line[i];
861
+ if (inQuotes) {
862
+ if (ch === '"') {
863
+ if (line[i + 1] === '"') {
864
+ cur += '"';
865
+ i++;
866
+ }
867
+ else {
868
+ inQuotes = false;
869
+ }
870
+ }
871
+ else {
872
+ cur += ch;
873
+ }
874
+ }
875
+ else if (ch === '"') {
876
+ inQuotes = true;
877
+ }
878
+ else if (ch === ',') {
879
+ out.push(cur);
880
+ cur = '';
881
+ }
882
+ else {
883
+ cur += ch;
884
+ }
885
+ }
886
+ out.push(cur);
887
+ return out;
888
+ }
889
+ export function modelNotAvailable(name) {
890
+ return errorResponse(`The "${name}" model is not available in your Prisma schema. ` +
891
+ 'Run `prenta db:init` for new schemas, or carefully update the existing Prenta block, create/apply a Prisma migration, then regenerate Prisma Client. ' +
892
+ 'See https://prentacms.dev/docs/database-setup for required models.', 501);
893
+ }
894
+ /**
895
+ * XML-escape a value for safe inclusion in sitemap content. Sitemaps reject
896
+ * unescaped ampersands and angle brackets; URLs frequently contain `&` query
897
+ * separators, so this is non-optional.
898
+ */
899
+ export function escapeXml(value) {
900
+ return value
901
+ .replace(/&/g, '&amp;')
902
+ .replace(/</g, '&lt;')
903
+ .replace(/>/g, '&gt;')
904
+ .replace(/"/g, '&quot;')
905
+ .replace(/'/g, '&apos;');
906
+ }
907
+ /**
908
+ * URLs per sitemap file. The sitemaps protocol caps a single file at 50,000
909
+ * URLs, but the child-sitemap route must load each row's full `data` JSON blob
910
+ * (for path/noindex/images) into memory to build the XML — at 45k rows that is
911
+ * hundreds of MB per request on an unauthenticated, crawler-hit endpoint. We
912
+ * cap well below the protocol limit to bound per-request memory; collections
913
+ * larger than this are split across multiple `?page=N` files referenced from
914
+ * the sitemap index, so no URLs are lost.
915
+ */
916
+ export const SITEMAP_PAGE_SIZE = 10000;
917
+ /**
918
+ * Renders a 1200x630 SVG suitable for og:image. We don't pull in Satori/resvg
919
+ * because most integrators don't need PNG — major crawlers (Facebook, Twitter,
920
+ * LinkedIn, Slack, Discord) handle image/svg+xml correctly. Sites that
921
+ * specifically need PNG can override with their own /og endpoint via
922
+ * @vercel/og and point `seo.defaultOgImage` at it.
923
+ */
924
+ export function renderOgSvg(opts) {
925
+ const { title, description, siteName, bg, fg, muted } = opts;
926
+ // Naive line wrapping at ~22 chars (large font). Good enough for an OG card;
927
+ // anything longer than ~3 lines gets truncated with an ellipsis.
928
+ const wrap = (text, maxChars, maxLines) => {
929
+ const words = text.split(/\s+/);
930
+ const lines = [];
931
+ let current = '';
932
+ for (const w of words) {
933
+ if (lines.length >= maxLines)
934
+ break;
935
+ const next = current ? `${current} ${w}` : w;
936
+ if (next.length > maxChars) {
937
+ if (current)
938
+ lines.push(current);
939
+ current = w;
940
+ if (lines.length === maxLines - 1 && words.indexOf(w) < words.length - 1) {
941
+ lines.push(current.length > maxChars ? current.slice(0, maxChars - 1) + '…' : current + '…');
942
+ current = '';
943
+ break;
944
+ }
945
+ }
946
+ else {
947
+ current = next;
948
+ }
949
+ }
950
+ if (current && lines.length < maxLines)
951
+ lines.push(current);
952
+ return lines;
953
+ };
954
+ const escapeSvg = (s) => s.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
955
+ const titleLines = wrap(title, 28, 3);
956
+ const descLines = description ? wrap(description, 60, 2) : [];
957
+ const titleEls = titleLines
958
+ .map((line, i) => `<tspan x="60" dy="${i === 0 ? 0 : 78}">${escapeSvg(line)}</tspan>`)
959
+ .join('');
960
+ const descEls = descLines
961
+ .map((line, i) => `<tspan x="60" dy="${i === 0 ? 0 : 36}">${escapeSvg(line)}</tspan>`)
962
+ .join('');
963
+ // Layout: site name top-left, title bottom-left, description below title.
964
+ // Coordinates are roughly aligned to the 1200x630 spec used by every major
965
+ // social platform.
966
+ const titleY = descLines.length > 0 ? 360 : 420;
967
+ const descY = titleY + 80 * titleLines.length;
968
+ return `<?xml version="1.0" encoding="UTF-8"?>
969
+ <svg xmlns="http://www.w3.org/2000/svg" width="1200" height="630" viewBox="0 0 1200 630">
970
+ <rect width="1200" height="630" fill="${bg}"/>
971
+ ${siteName ? `<text x="60" y="100" font-family="system-ui, -apple-system, Segoe UI, Arial, sans-serif" font-size="28" font-weight="500" fill="${muted}">${escapeSvg(siteName)}</text>` : ''}
972
+ <text x="60" y="${titleY}" font-family="system-ui, -apple-system, Segoe UI, Arial, sans-serif" font-size="68" font-weight="700" fill="${fg}">${titleEls}</text>
973
+ ${descLines.length > 0 ? `<text x="60" y="${descY}" font-family="system-ui, -apple-system, Segoe UI, Arial, sans-serif" font-size="30" fill="${muted}">${descEls}</text>` : ''}
974
+ <rect x="60" y="560" width="60" height="6" fill="${fg}" rx="3"/>
975
+ </svg>`;
976
+ }
977
+ /**
978
+ * Log unexpected query failures from the `safe*` wrappers.
979
+ *
980
+ * The wrappers exist so an optional Prisma model (e.g. a delegate
981
+ * that older schemas don't define) doesn't blow up the route — they
982
+ * return the empty value (`0` / `[]`) when the model isn't available.
983
+ * That defensive contract historically swallowed *every* error,
984
+ * including Prisma's "Unknown argument" rejections that flag a real
985
+ * application bug (see the `relationLoadStrategy` regression
986
+ * discovered while landing Phase 5.5 #5). Logging the underlying
987
+ * error preserves the defensive return semantics while making the
988
+ * actual class of bug visible in Vercel + Sentry logs.
989
+ *
990
+ * Kept private to this module — it's a logging contract, not a
991
+ * public API.
992
+ */
993
+ export function logSafeQueryFailure(scope, err) {
994
+ // Defensive: never throw from a logger that wraps `safe*` queries
995
+ // — the whole point of those wrappers is to keep the request hot
996
+ // path alive.
997
+ try {
998
+ const message = err instanceof Error ? err.message : String(err);
999
+ logger.warn(`${scope} query failed`, { error: message });
1000
+ }
1001
+ catch {
1002
+ /* swallow */
1003
+ }
1004
+ }
1005
+ export async function safeCount(model, where) {
1006
+ if (!model || typeof model !== 'object')
1007
+ return 0;
1008
+ let countFn;
1009
+ try {
1010
+ countFn = model.count;
1011
+ }
1012
+ catch {
1013
+ return 0;
1014
+ }
1015
+ if (typeof countFn !== 'function')
1016
+ return 0;
1017
+ try {
1018
+ return await countFn.call(model, where ? { where } : undefined);
1019
+ }
1020
+ catch (err) {
1021
+ logSafeQueryFailure('safeCount', err);
1022
+ return 0;
1023
+ }
1024
+ }
1025
+ export async function safeFindMany(model, args) {
1026
+ if (!model || typeof model !== 'object')
1027
+ return [];
1028
+ let findManyFn;
1029
+ try {
1030
+ findManyFn = model.findMany;
1031
+ }
1032
+ catch {
1033
+ return [];
1034
+ }
1035
+ if (typeof findManyFn !== 'function')
1036
+ return [];
1037
+ try {
1038
+ return await findManyFn.call(model, args);
1039
+ }
1040
+ catch (err) {
1041
+ logSafeQueryFailure('safeFindMany', err);
1042
+ return [];
1043
+ }
1044
+ }
1045
+ /**
1046
+ * String-level gate before fetching a media `storageKey`. Delegates to
1047
+ * {@link validateWebhookUrl} (IP canonicalization for decimal/octal/hex,
1048
+ * CGNAT, metadata, etc.) so this allowlist cannot drift from the SSRF policy
1049
+ * used by `safeFetch`. Also rejects `*.local` (mDNS) which webhook validation
1050
+ * does not cover.
1051
+ */
1052
+ export function isAllowedStorageUrl(url) {
1053
+ if (!validateWebhookUrl(url).valid)
1054
+ return false;
1055
+ try {
1056
+ const h = new URL(url).hostname.toLowerCase();
1057
+ if (h.endsWith('.local'))
1058
+ return false;
1059
+ return true;
1060
+ }
1061
+ catch {
1062
+ return false;
1063
+ }
1064
+ }
1065
+ /** Max image size (bytes) we'll pull into memory for vision analysis. */
1066
+ export const MAX_VISION_IMAGE_BYTES = 8 * 1024 * 1024;
1067
+ /**
1068
+ * Fetch image bytes for multimodal (vision) analysis through {@link safeFetch}
1069
+ * (SSRF-safe: re-validates the host, DNS-resolves to defeat rebinding, no
1070
+ * redirects). Returns `null` on any failure (non-image, oversize, blocked,
1071
+ * network) so the caller transparently falls back to a context-only heuristic.
1072
+ */
1073
+ export async function fetchImageForVision(url, knownMimeType) {
1074
+ try {
1075
+ const resp = await safeFetch(url, { method: 'GET', timeoutMs: 5000 });
1076
+ if (!resp.ok)
1077
+ return null;
1078
+ const contentType = ((resp.headers.get('content-type') ?? knownMimeType ?? '').split(';')[0] ?? '').trim();
1079
+ if (!contentType.startsWith('image/'))
1080
+ return null;
1081
+ const declaredLength = Number(resp.headers.get('content-length') ?? '0');
1082
+ if (declaredLength > MAX_VISION_IMAGE_BYTES)
1083
+ return null;
1084
+ const buffer = Buffer.from(await resp.arrayBuffer());
1085
+ if (buffer.byteLength === 0 || buffer.byteLength > MAX_VISION_IMAGE_BYTES)
1086
+ return null;
1087
+ return { base64: buffer.toString('base64'), mimeType: contentType };
1088
+ }
1089
+ catch {
1090
+ return null;
1091
+ }
1092
+ }
1093
+ export const ALLOWED_SORT_FIELDS = new Set([
1094
+ 'createdAt',
1095
+ 'updatedAt',
1096
+ 'publishedAt',
1097
+ 'status',
1098
+ 'collection',
1099
+ ]);
1100
+ let _secretMissing = false;
1101
+ let _secretWarningLogged = false;
1102
+ export function getSessionSecret() {
1103
+ const secret = process.env.CMS_SECRET ?? process.env.CMS_SESSION_SECRET ?? getPrentaConfig()?.secret;
1104
+ if (!secret) {
1105
+ _secretMissing = true;
1106
+ if (!_secretWarningLogged) {
1107
+ _secretWarningLogged = true;
1108
+ console.error('[Prenta CMS] Missing CMS secret. Set the CMS_SECRET environment variable (min 32 characters) ' +
1109
+ 'or pass `secret` in your prenta.config.ts. ' +
1110
+ "Generate one with: node -e \"console.log(require('crypto').randomBytes(32).toString('hex'))\" " +
1111
+ '-- All authenticated API routes will return 503 until this is configured.');
1112
+ }
1113
+ throw new Error('CMS secret not configured');
1114
+ }
1115
+ if (secret.length < 32) {
1116
+ throw new Error('[Prenta CMS] CMS secret must be at least 32 characters (got ' +
1117
+ secret.length +
1118
+ '). ' +
1119
+ "Generate a secure value with: node -e \"console.log(require('crypto').randomBytes(32).toString('hex'))\"");
1120
+ }
1121
+ _secretMissing = false;
1122
+ return secret;
1123
+ }
1124
+ export function isSecretMissing() {
1125
+ if (_secretMissing)
1126
+ return true;
1127
+ try {
1128
+ getSessionSecret();
1129
+ return false;
1130
+ }
1131
+ catch {
1132
+ return true;
1133
+ }
1134
+ }
1135
+ export async function extractSession(request) {
1136
+ if (isSecretMissing())
1137
+ return null;
1138
+ let token;
1139
+ const authHeader = request.headers.get('Authorization');
1140
+ if (authHeader?.startsWith('Bearer ')) {
1141
+ token = authHeader.slice(7);
1142
+ }
1143
+ if (!token) {
1144
+ const cookieHeader = request.headers.get('cookie') ?? '';
1145
+ const cookies = parseCookieHeader(cookieHeader);
1146
+ token = cookies['prenta_session'];
1147
+ }
1148
+ if (!token)
1149
+ return null;
1150
+ // API key path. Keys are recognized by the `act_sk_` prefix and looked up
1151
+ // by SHA-256 hash. We never JWT-verify these tokens — they are opaque
1152
+ // random secrets stored as hashes in `prenta_api_keys`.
1153
+ if (looksLikeApiKey(token)) {
1154
+ try {
1155
+ const d = getDB();
1156
+ if (!hasModel(d, 'apiKey'))
1157
+ return null;
1158
+ const hash = await hashApiKey(token);
1159
+ const apiKey = await d.apiKey.findUnique({ where: { keyHash: hash } });
1160
+ if (!apiKey)
1161
+ return null;
1162
+ if (apiKey.revokedAt)
1163
+ return null;
1164
+ if (apiKey.expiresAt && new Date(apiKey.expiresAt).getTime() < Date.now())
1165
+ return null;
1166
+ const ipRestrictions = Array.isArray(apiKey.ipRestrictions)
1167
+ ? apiKey.ipRestrictions
1168
+ : apiKey.ipRestrictions
1169
+ ? (apiKey.ipRestrictions.allow ?? null)
1170
+ : null;
1171
+ if (ipRestrictions && ipRestrictions.length > 0) {
1172
+ const ip = getClientIp(request);
1173
+ if (!validateApiKeyIp(ipRestrictions, ip))
1174
+ return null;
1175
+ }
1176
+ const scopes = apiKey.scopes ?? {};
1177
+ // Fire-and-forget lastUsedAt update; never block the request on it.
1178
+ void d.apiKey
1179
+ .update({ where: { id: apiKey.id }, data: { lastUsedAt: new Date() } })
1180
+ .catch(() => { });
1181
+ return {
1182
+ userId: apiKey.userId,
1183
+ role: scopes.admin ? 'ADMIN' : 'API_KEY',
1184
+ sessionId: apiKey.id,
1185
+ apiKey: { id: apiKey.id, scopes },
1186
+ };
1187
+ }
1188
+ catch {
1189
+ return null;
1190
+ }
1191
+ }
1192
+ // Session JWT path.
1193
+ try {
1194
+ const payload = await verifySession(token, { secret: getSessionSecret() });
1195
+ const d = getDB();
1196
+ if (hasModel(d, 'session')) {
1197
+ const dbSession = await d.session.findUnique({
1198
+ where: { id: payload.sessionId },
1199
+ select: { revokedAt: true },
1200
+ });
1201
+ if (!dbSession || dbSession.revokedAt)
1202
+ return null;
1203
+ }
1204
+ return payload;
1205
+ }
1206
+ catch {
1207
+ return null;
1208
+ }
1209
+ }
1210
+ export function parseCookieHeader(cookieHeader) {
1211
+ const cookies = {};
1212
+ for (const pair of cookieHeader.split(';')) {
1213
+ const [key, ...rest] = pair.split('=');
1214
+ if (key) {
1215
+ cookies[key.trim()] = rest.join('=').trim();
1216
+ }
1217
+ }
1218
+ return cookies;
1219
+ }
1220
+ /**
1221
+ * Verify a password (or "current password") posted in the X-Reauth-Password
1222
+ * header against the authenticated user. Returns an error Response when the
1223
+ * header is missing or the password is wrong; returns null on success.
1224
+ *
1225
+ * Use this for high-impact account changes (TOTP enable/disable, role change,
1226
+ * delete user, etc.) so a stolen session alone cannot perform them.
1227
+ */
1228
+ export async function requirePasswordReauth(request, userId) {
1229
+ const password = request.headers.get('x-reauth-password');
1230
+ if (!password) {
1231
+ return new Response(JSON.stringify({
1232
+ error: 'Re-authentication required for this action.',
1233
+ code: 'REAUTH_REQUIRED',
1234
+ method: 'password',
1235
+ }), { status: 401, headers: { ...SECURITY_HEADERS } });
1236
+ }
1237
+ try {
1238
+ const { getDB } = await import('../db.js');
1239
+ const ok = await verifyReauth(userId, password, 'password', getDB());
1240
+ if (!ok) {
1241
+ return errorResponse('Re-authentication failed.', 401);
1242
+ }
1243
+ return null;
1244
+ }
1245
+ catch (err) {
1246
+ logger.error('reauth verify failed', {
1247
+ error: err instanceof Error ? err.message : String(err),
1248
+ });
1249
+ return errorResponse('Re-authentication failed.', 401);
1250
+ }
1251
+ }
1252
+ export async function requireAuth(request) {
1253
+ if (isSecretMissing()) {
1254
+ return {
1255
+ error: json({
1256
+ error: 'CMS secret not configured. Set CMS_SECRET or CMS_SESSION_SECRET environment variable (min 32 characters).',
1257
+ code: 'MISSING_SECRET',
1258
+ }, 503),
1259
+ };
1260
+ }
1261
+ const session = await extractSession(request);
1262
+ if (!session) {
1263
+ return { error: errorResponse('Unauthorized', 401) };
1264
+ }
1265
+ return { session };
1266
+ }
1267
+ /**
1268
+ * Check that the request's auth context permits `action` on `collection`.
1269
+ * - Session-authenticated requests fall through to `requireRole` semantics:
1270
+ * write actions require WRITE_ROLES.
1271
+ * - API-key-authenticated requests must have an explicit scope match.
1272
+ */
1273
+ export function requireCollectionScope(session, collection, action) {
1274
+ if (session.apiKey) {
1275
+ if (!validateApiKeyScope(session.apiKey.scopes, collection, action)) {
1276
+ return errorResponse(`API key does not have permission to ${action} on collection "${collection}"`, 403);
1277
+ }
1278
+ return null;
1279
+ }
1280
+ if (action === 'read')
1281
+ return null;
1282
+ return requireRole(session.role, WRITE_ROLES);
1283
+ }
1284
+ export function requireGlobalScope(session, slug) {
1285
+ if (session.apiKey) {
1286
+ if (!validateApiKeyGlobalScope(session.apiKey.scopes, slug)) {
1287
+ return errorResponse(`API key does not have permission for global "${slug}"`, 403);
1288
+ }
1289
+ }
1290
+ return null;
1291
+ }
1292
+ export function requireMediaScope(session) {
1293
+ if (session.apiKey) {
1294
+ if (!validateApiKeyMediaScope(session.apiKey.scopes)) {
1295
+ return errorResponse('API key does not have media scope', 403);
1296
+ }
1297
+ }
1298
+ return null;
1299
+ }
1300
+ export function requirePageBuilderScope(session) {
1301
+ if (session.apiKey) {
1302
+ if (!validateApiKeyPageBuilderScope(session.apiKey.scopes)) {
1303
+ return errorResponse('API key does not have pageBuilder scope', 403);
1304
+ }
1305
+ return null;
1306
+ }
1307
+ return requireRole(session.role, ADMIN_ROLES);
1308
+ }
1309
+ export function requireAdminScope(session) {
1310
+ if (session.apiKey) {
1311
+ if (!session.apiKey.scopes.admin) {
1312
+ return errorResponse('API key does not have admin scope', 403);
1313
+ }
1314
+ return null;
1315
+ }
1316
+ return requireRole(session.role, ADMIN_ROLES);
1317
+ }
1318
+ export function buildActionContext(session, db, locale) {
1319
+ return {
1320
+ userId: session.userId,
1321
+ role: session.role,
1322
+ locale,
1323
+ db,
1324
+ };
1325
+ }
1326
+ export const WRITE_ROLES = new Set(['OWNER', 'ADMIN', 'EDITOR', 'AUTHOR']);
1327
+ export const ADMIN_ROLES = new Set(['OWNER', 'ADMIN']);
1328
+ export function requireRole(role, allowedRoles) {
1329
+ if (!allowedRoles.has(role)) {
1330
+ return errorResponse('Insufficient permissions', 403);
1331
+ }
1332
+ return null;
1333
+ }
1334
+ /**
1335
+ * Build the shared {@link TeamOptions} for the Users endpoints from config +
1336
+ * session: the workspace domain (for external-domain detection) and the seat
1337
+ * cap (config-provided; null = unlimited until billing exists).
1338
+ */
1339
+ export function teamOptionsFor(session) {
1340
+ const cfg = getPrentaConfig();
1341
+ const siteUrl = process.env.NEXT_PUBLIC_SITE_URL ??
1342
+ cfg?.seo?.siteUrl ??
1343
+ null;
1344
+ const seatLimit = cfg?.users?.seatLimit ??
1345
+ cfg?.admin?.seatLimit ??
1346
+ null;
1347
+ return {
1348
+ workspaceDomain: workspaceDomainFromUrl(siteUrl),
1349
+ seatLimit,
1350
+ currentUserId: session.userId,
1351
+ };
1352
+ }
1353
+ /** Whether the configured email provider can actually send mail. */
1354
+ export function isEmailConfigured() {
1355
+ const cfg = getPrentaConfig();
1356
+ if (cfg?.platform?.email)
1357
+ return true;
1358
+ return !!resolveEmailSender();
1359
+ }
1360
+ /**
1361
+ * DB-aware variant of {@link isEmailConfigured} — also true when a provider
1362
+ * is configured through Settings > Email.
1363
+ */
1364
+ export async function isEmailConfiguredAsync(db) {
1365
+ if (isEmailConfigured())
1366
+ return true;
1367
+ const { resolveEmailSenderAsync } = await import('../email/resolve.js');
1368
+ return Boolean(await resolveEmailSenderAsync(db));
1369
+ }
1370
+ export const loginLimiter = createRateLimiter({
1371
+ maxRequests: 5,
1372
+ windowMs: 15 * 60 * 1000,
1373
+ failClosed: true,
1374
+ });
1375
+ // Fallback limiter for auth requests whose client IP can't be resolved (no
1376
+ // trusted proxy header) — those all share ONE bucket. The strict 5/15min
1377
+ // `loginLimiter` there would let a handful of anonymous requests lock every
1378
+ // user out of login (see #536), so the shared "unknown" bucket gets a much
1379
+ // higher ceiling. The real fix is resolving IPs (PRENTA_TRUST_PROXY); this
1380
+ // just bounds abuse without turning a mis-config into a global outage.
1381
+ export const loginUnknownLimiter = createRateLimiter({
1382
+ maxRequests: 100,
1383
+ windowMs: 15 * 60 * 1000,
1384
+ failClosed: true,
1385
+ });
1386
+ // Admin-gated, but bounds invitation-email volume (and accidental loops) per
1387
+ // admin: enough to onboard a team, low enough to deter abuse.
1388
+ export const inviteLimiter = createRateLimiter({ maxRequests: 20, windowMs: 60 * 60 * 1000 });
1389
+ export const totpLimiter = createRateLimiter({
1390
+ maxRequests: 10,
1391
+ windowMs: 15 * 60 * 1000,
1392
+ failClosed: true,
1393
+ });
1394
+ // Admin-gated test sends from Settings > Email — enough to debug a provider
1395
+ // setup, low enough that a compromised admin session can't spray mail.
1396
+ export const emailTestLimiter = createRateLimiter({ maxRequests: 10, windowMs: 60 * 60 * 1000 });
1397
+ export const formLimiterGlobal = createRateLimiter({ maxRequests: 10, windowMs: 60_000 });
1398
+ export const aiGenerateLimiter = createRateLimiter({
1399
+ maxRequests: 20,
1400
+ windowMs: 60 * 60 * 1000,
1401
+ failClosed: true,
1402
+ });
1403
+ // Strict per-user upload cap (security rule: uploads strict at 20/hour) — guards
1404
+ // storage cost and optimization abuse beyond the global per-IP API bucket.
1405
+ export const uploadLimiter = createRateLimiter({
1406
+ maxRequests: 20,
1407
+ windowMs: 60 * 60 * 1000,
1408
+ failClosed: true,
1409
+ });
1410
+ // Deterministic (no provider) AI-adjacent routes that scan/iterate documents.
1411
+ // Cheaper than the generation budget but still bounded against cost/DoS.
1412
+ export const aiAnalysisLimiter = createRateLimiter({
1413
+ maxRequests: 60,
1414
+ windowMs: 60 * 60 * 1000,
1415
+ failClosed: true,
1416
+ });
1417
+ // Upper bound on free-text inputs to the deterministic analysis routes
1418
+ // (document body / link-candidate content) — caps memory + scan work.
1419
+ export const AI_ANALYSIS_MAX_CHARS = 50_000;
1420
+ export const linkHealthLimiter = createRateLimiter({ maxRequests: 4, windowMs: 60 * 60 * 1000 });
1421
+ export const seoAuditLimiter = createRateLimiter({ maxRequests: 10, windowMs: 60 * 60 * 1000 });
1422
+ export async function checkRateLimitAsync(limiter, key) {
1423
+ // Explicit, environment-gated bypass for test harnesses. Production never
1424
+ // sets this — Vercel + the deploy guide both omit it. We deliberately do
1425
+ // NOT key off `NODE_ENV === 'test'` because Next.js builds run with
1426
+ // NODE_ENV=test in some CI configurations and we want rate limiting
1427
+ // exercised by unit tests that explicitly opt in.
1428
+ if (process.env.PRENTA_DISABLE_RATE_LIMIT === '1') {
1429
+ return true;
1430
+ }
1431
+ const result = await limiter.check(key);
1432
+ return result.allowed;
1433
+ }
1434
+ /**
1435
+ * Resolve the client IP from trusted-proxy headers (Vercel first, then x-real-ip,
1436
+ * then x-forwarded-for only when PRENTA_TRUST_PROXY=1). Returns 'unknown' when
1437
+ * no trustworthy source is available — callers MUST treat that case as a hard
1438
+ * failure for security-sensitive decisions like rate-limit keys and IP allowlists.
1439
+ */
1440
+ export function clientIp(request) {
1441
+ return getClientIp(request);
1442
+ }
1443
+ /**
1444
+ * Compute a before/after diff of top-level scalar (string/number/boolean) keys
1445
+ * between a stored record and an incoming patch. Used to audit settings changes
1446
+ * without ever logging nested objects (which may hold secret blobs).
1447
+ */
1448
+ export function diffScalarKeys(previous, next) {
1449
+ const isScalar = (v) => typeof v === 'string' || typeof v === 'number' || typeof v === 'boolean' || v === null;
1450
+ const changes = [];
1451
+ for (const key of Object.keys(next)) {
1452
+ const to = next[key];
1453
+ if (!isScalar(to))
1454
+ continue;
1455
+ const from = previous[key];
1456
+ if (isScalar(from) && from === to)
1457
+ continue;
1458
+ if (!isScalar(from) && from !== undefined)
1459
+ continue;
1460
+ changes.push({ key, from: isScalar(from) ? from : undefined, to });
1461
+ }
1462
+ return changes;
1463
+ }
1464
+ export const MAX_CONCURRENT_SESSIONS = 5;
1465
+ /**
1466
+ * After a successful primary-credential check, prune the user's active sessions
1467
+ * down to the configured concurrent maximum (default 5; configurable via
1468
+ * `auth.maxConcurrentSessions`). Strategy is "revoke oldest" so a user can
1469
+ * always sign in.
1470
+ */
1471
+ export async function enforceSessionLimitsForUser(d, userId) {
1472
+ if (!hasModel(d, 'session'))
1473
+ return;
1474
+ // Precedence: explicitly-saved Security setting (database) ▸ auth config ▸
1475
+ // default. We read the stored value directly (rather than the parsed
1476
+ // defaults) so an unsaved policy doesn't shadow a config-provided override.
1477
+ let storedMax;
1478
+ try {
1479
+ if (hasModel(d, 'document')) {
1480
+ const row = await d.document.findFirst({
1481
+ where: { collection: 'settings', deletedAt: null },
1482
+ select: { data: true },
1483
+ });
1484
+ const stored = row?.data?.security;
1485
+ const v = stored?.session?.maxConcurrentSessions;
1486
+ if (typeof v === 'number' && Number.isInteger(v) && v >= 0)
1487
+ storedMax = v;
1488
+ }
1489
+ }
1490
+ catch {
1491
+ /* fall back to config/default below */
1492
+ }
1493
+ const auth = getPrentaConfig()?.auth;
1494
+ const max = storedMax !== undefined
1495
+ ? storedMax
1496
+ : typeof auth?.maxConcurrentSessions === 'number' && auth.maxConcurrentSessions > 0
1497
+ ? auth.maxConcurrentSessions
1498
+ : MAX_CONCURRENT_SESSIONS;
1499
+ // 0 ⇒ unlimited: skip pruning entirely.
1500
+ if (max <= 0)
1501
+ return;
1502
+ const active = await d.session.findMany({
1503
+ where: { userId, revokedAt: null, expiresAt: { gt: new Date() } },
1504
+ select: { id: true, createdAt: true },
1505
+ });
1506
+ const decision = enforceSessionLimits(active.map((s) => ({ sessionId: s.id, userId, createdAt: s.createdAt })), { maxConcurrentSessions: max, strategy: 'revoke_oldest' });
1507
+ if (decision.sessionsToRevoke.length > 0) {
1508
+ await d.session.updateMany({
1509
+ where: { id: { in: decision.sessionsToRevoke } },
1510
+ data: { revokedAt: new Date() },
1511
+ });
1512
+ }
1513
+ }
1514
+ export function getAdminPath() {
1515
+ return process.env.PRENTA_ADMIN_PATH ?? getPrentaConfig()?.admin?.path ?? '/admin';
1516
+ }
1517
+ /**
1518
+ * Resolve a collection definition from the runtime config, tolerating both
1519
+ * the array and the record (keyed-by-slug) shapes consumers use.
1520
+ */
1521
+ export function findCollectionDef(slug) {
1522
+ const collections = getPrentaConfig()?.collections;
1523
+ if (Array.isArray(collections)) {
1524
+ return collections.find((c) => c?.slug === slug);
1525
+ }
1526
+ if (collections && typeof collections === 'object') {
1527
+ const rec = collections;
1528
+ return rec[slug] ?? Object.values(rec).find((c) => c?.slug === slug);
1529
+ }
1530
+ return undefined;
1531
+ }
1532
+ /**
1533
+ * True when `collection` declares a `sections` JSON field — the storage
1534
+ * contract the flat Page Editor + section endpoints rely on. Guards against
1535
+ * silently dropping section writes on a collection that never declared the
1536
+ * field (the field-access layer strips undeclared keys).
1537
+ */
1538
+ export function collectionSupportsSections(slug) {
1539
+ const def = findCollectionDef(slug);
1540
+ if (!def)
1541
+ return false;
1542
+ const fields = def.fields;
1543
+ if (Array.isArray(fields)) {
1544
+ return fields.some((f) => f?.name === 'sections' && f?.type === 'json');
1545
+ }
1546
+ if (fields && typeof fields === 'object') {
1547
+ const f = fields.sections;
1548
+ return f?.type === 'json';
1549
+ }
1550
+ return false;
1551
+ }
1552
+ /**
1553
+ * Gate a per-document read for endpoints (e.g. SEO analysis) that fetch a
1554
+ * document directly rather than through the collection CRUD routes. Enforces
1555
+ * both the API-key collection scope and the collection's `access.read` rule
1556
+ * so an AUTHOR scoped to one collection cannot read another's content.
1557
+ * Returns a `Response` to short-circuit with, or `null` when allowed.
1558
+ */
1559
+ export async function enforceDocumentReadAccess(session, collection) {
1560
+ const scopeErr = requireCollectionScope(session, collection, 'read');
1561
+ if (scopeErr)
1562
+ return scopeErr;
1563
+ try {
1564
+ await assertCollectionAccess(collection, 'read', buildActionContext(session, guardedDb()));
1565
+ }
1566
+ catch (err) {
1567
+ if (err instanceof Error && err.message.includes('Access denied')) {
1568
+ return errorResponse('Insufficient permissions', 403);
1569
+ }
1570
+ throw err;
1571
+ }
1572
+ return null;
1573
+ }
1574
+ //# sourceMappingURL=route-helpers.js.map