@actuate-media/cms-admin 0.15.0 → 0.16.1

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 (308) hide show
  1. package/dist/AdminRoot.d.ts.map +1 -1
  2. package/dist/AdminRoot.js +148 -2
  3. package/dist/AdminRoot.js.map +1 -1
  4. package/dist/__tests__/components/posts-badges.test.d.ts +2 -0
  5. package/dist/__tests__/components/posts-badges.test.d.ts.map +1 -0
  6. package/dist/__tests__/components/posts-badges.test.js +68 -0
  7. package/dist/__tests__/components/posts-badges.test.js.map +1 -0
  8. package/dist/__tests__/layout/sidebar-posts.test.d.ts +2 -0
  9. package/dist/__tests__/layout/sidebar-posts.test.d.ts.map +1 -0
  10. package/dist/__tests__/layout/sidebar-posts.test.js +162 -0
  11. package/dist/__tests__/layout/sidebar-posts.test.js.map +1 -0
  12. package/dist/__tests__/lib/page-editor-service.test.d.ts +2 -0
  13. package/dist/__tests__/lib/page-editor-service.test.d.ts.map +1 -0
  14. package/dist/__tests__/lib/page-editor-service.test.js +137 -0
  15. package/dist/__tests__/lib/page-editor-service.test.js.map +1 -0
  16. package/dist/__tests__/lib/pages-service.test.d.ts +2 -0
  17. package/dist/__tests__/lib/pages-service.test.d.ts.map +1 -0
  18. package/dist/__tests__/lib/pages-service.test.js +140 -0
  19. package/dist/__tests__/lib/pages-service.test.js.map +1 -0
  20. package/dist/__tests__/lib/post-editor-service.test.d.ts +2 -0
  21. package/dist/__tests__/lib/post-editor-service.test.d.ts.map +1 -0
  22. package/dist/__tests__/lib/post-editor-service.test.js +185 -0
  23. package/dist/__tests__/lib/post-editor-service.test.js.map +1 -0
  24. package/dist/__tests__/lib/posts-service.test.d.ts +2 -0
  25. package/dist/__tests__/lib/posts-service.test.d.ts.map +1 -0
  26. package/dist/__tests__/lib/posts-service.test.js +244 -0
  27. package/dist/__tests__/lib/posts-service.test.js.map +1 -0
  28. package/dist/__tests__/lib/useApiData.test.d.ts +2 -0
  29. package/dist/__tests__/lib/useApiData.test.d.ts.map +1 -0
  30. package/dist/__tests__/lib/useApiData.test.js +84 -0
  31. package/dist/__tests__/lib/useApiData.test.js.map +1 -0
  32. package/dist/__tests__/views/dashboard.test.d.ts +2 -0
  33. package/dist/__tests__/views/dashboard.test.d.ts.map +1 -0
  34. package/dist/__tests__/views/dashboard.test.js +138 -0
  35. package/dist/__tests__/views/dashboard.test.js.map +1 -0
  36. package/dist/__tests__/views/posts-list-view.test.d.ts +2 -0
  37. package/dist/__tests__/views/posts-list-view.test.d.ts.map +1 -0
  38. package/dist/__tests__/views/posts-list-view.test.js +98 -0
  39. package/dist/__tests__/views/posts-list-view.test.js.map +1 -0
  40. package/dist/actuate-admin.css +1 -1
  41. package/dist/components/pages/tag-badges.d.ts +21 -0
  42. package/dist/components/pages/tag-badges.d.ts.map +1 -0
  43. package/dist/components/pages/tag-badges.js +56 -0
  44. package/dist/components/pages/tag-badges.js.map +1 -0
  45. package/dist/components/posts/badges.d.ts +25 -0
  46. package/dist/components/posts/badges.d.ts.map +1 -0
  47. package/dist/components/posts/badges.js +123 -0
  48. package/dist/components/posts/badges.js.map +1 -0
  49. package/dist/components/posts/icons.d.ts +12 -0
  50. package/dist/components/posts/icons.d.ts.map +1 -0
  51. package/dist/components/posts/icons.js +26 -0
  52. package/dist/components/posts/icons.js.map +1 -0
  53. package/dist/layout/Layout.js +36 -8
  54. package/dist/layout/Layout.js.map +1 -1
  55. package/dist/layout/Sidebar.d.ts +9 -0
  56. package/dist/layout/Sidebar.d.ts.map +1 -1
  57. package/dist/layout/Sidebar.js +100 -48
  58. package/dist/layout/Sidebar.js.map +1 -1
  59. package/dist/lib/page-editor-service.d.ts +45 -0
  60. package/dist/lib/page-editor-service.d.ts.map +1 -0
  61. package/dist/lib/page-editor-service.js +153 -0
  62. package/dist/lib/page-editor-service.js.map +1 -0
  63. package/dist/lib/pages-service.d.ts +143 -0
  64. package/dist/lib/pages-service.d.ts.map +1 -0
  65. package/dist/lib/pages-service.js +444 -0
  66. package/dist/lib/pages-service.js.map +1 -0
  67. package/dist/lib/post-editor-service.d.ts +57 -0
  68. package/dist/lib/post-editor-service.d.ts.map +1 -0
  69. package/dist/lib/post-editor-service.js +234 -0
  70. package/dist/lib/post-editor-service.js.map +1 -0
  71. package/dist/lib/posts-service.d.ts +73 -0
  72. package/dist/lib/posts-service.d.ts.map +1 -0
  73. package/dist/lib/posts-service.js +245 -0
  74. package/dist/lib/posts-service.js.map +1 -0
  75. package/dist/lib/preview-link.d.ts +32 -0
  76. package/dist/lib/preview-link.d.ts.map +1 -0
  77. package/dist/lib/preview-link.js +40 -0
  78. package/dist/lib/preview-link.js.map +1 -0
  79. package/dist/router/index.d.ts +1 -0
  80. package/dist/router/index.d.ts.map +1 -1
  81. package/dist/router/index.js +9 -1
  82. package/dist/router/index.js.map +1 -1
  83. package/dist/views/Dashboard.d.ts.map +1 -1
  84. package/dist/views/Dashboard.js +64 -20
  85. package/dist/views/Dashboard.js.map +1 -1
  86. package/dist/views/Pages/PageTagManager.d.ts +14 -0
  87. package/dist/views/Pages/PageTagManager.d.ts.map +1 -0
  88. package/dist/views/Pages/PageTagManager.js +145 -0
  89. package/dist/views/Pages/PageTagManager.js.map +1 -0
  90. package/dist/views/Pages/PagesListView.d.ts +18 -0
  91. package/dist/views/Pages/PagesListView.d.ts.map +1 -0
  92. package/dist/views/Pages/PagesListView.js +287 -0
  93. package/dist/views/Pages/PagesListView.js.map +1 -0
  94. package/dist/views/Posts/NewPostPicker.d.ts +16 -0
  95. package/dist/views/Posts/NewPostPicker.d.ts.map +1 -0
  96. package/dist/views/Posts/NewPostPicker.js +59 -0
  97. package/dist/views/Posts/NewPostPicker.js.map +1 -0
  98. package/dist/views/Posts/NewPostTypeInstructions.d.ts +18 -0
  99. package/dist/views/Posts/NewPostTypeInstructions.d.ts.map +1 -0
  100. package/dist/views/Posts/NewPostTypeInstructions.js +147 -0
  101. package/dist/views/Posts/NewPostTypeInstructions.js.map +1 -0
  102. package/dist/views/Posts/PostTypesView.d.ts +26 -0
  103. package/dist/views/Posts/PostTypesView.d.ts.map +1 -0
  104. package/dist/views/Posts/PostTypesView.js +52 -0
  105. package/dist/views/Posts/PostTypesView.js.map +1 -0
  106. package/dist/views/Posts/PostsListView.d.ts +25 -0
  107. package/dist/views/Posts/PostsListView.d.ts.map +1 -0
  108. package/dist/views/Posts/PostsListView.js +239 -0
  109. package/dist/views/Posts/PostsListView.js.map +1 -0
  110. package/dist/views/Posts/post-type-presets.d.ts +32 -0
  111. package/dist/views/Posts/post-type-presets.d.ts.map +1 -0
  112. package/dist/views/Posts/post-type-presets.js +72 -0
  113. package/dist/views/Posts/post-type-presets.js.map +1 -0
  114. package/dist/views/page-builder/Widgets.d.ts +13 -0
  115. package/dist/views/page-builder/Widgets.d.ts.map +1 -0
  116. package/dist/views/page-builder/Widgets.js +15 -0
  117. package/dist/views/page-builder/Widgets.js.map +1 -0
  118. package/dist/views/page-editor/AddSectionModal.d.ts +20 -0
  119. package/dist/views/page-editor/AddSectionModal.d.ts.map +1 -0
  120. package/dist/views/page-editor/AddSectionModal.js +22 -0
  121. package/dist/views/page-editor/AddSectionModal.js.map +1 -0
  122. package/dist/views/page-editor/EditorCanvas.d.ts +18 -0
  123. package/dist/views/page-editor/EditorCanvas.d.ts.map +1 -0
  124. package/dist/views/page-editor/EditorCanvas.js +64 -0
  125. package/dist/views/page-editor/EditorCanvas.js.map +1 -0
  126. package/dist/views/page-editor/EditorTopBar.d.ts +30 -0
  127. package/dist/views/page-editor/EditorTopBar.d.ts.map +1 -0
  128. package/dist/views/page-editor/EditorTopBar.js +42 -0
  129. package/dist/views/page-editor/EditorTopBar.js.map +1 -0
  130. package/dist/views/page-editor/PagePreview.d.ts +13 -0
  131. package/dist/views/page-editor/PagePreview.d.ts.map +1 -0
  132. package/dist/views/page-editor/PagePreview.js +37 -0
  133. package/dist/views/page-editor/PagePreview.js.map +1 -0
  134. package/dist/views/page-editor/PageSectionEditor.d.ts +16 -0
  135. package/dist/views/page-editor/PageSectionEditor.d.ts.map +1 -0
  136. package/dist/views/page-editor/PageSectionEditor.js +272 -0
  137. package/dist/views/page-editor/PageSectionEditor.js.map +1 -0
  138. package/dist/views/page-editor/PageSettingsModal.d.ts +12 -0
  139. package/dist/views/page-editor/PageSettingsModal.d.ts.map +1 -0
  140. package/dist/views/page-editor/PageSettingsModal.js +52 -0
  141. package/dist/views/page-editor/PageSettingsModal.js.map +1 -0
  142. package/dist/views/page-editor/SectionInspector.d.ts +17 -0
  143. package/dist/views/page-editor/SectionInspector.d.ts.map +1 -0
  144. package/dist/views/page-editor/SectionInspector.js +98 -0
  145. package/dist/views/page-editor/SectionInspector.js.map +1 -0
  146. package/dist/views/page-editor/SectionsPanel.d.ts +15 -0
  147. package/dist/views/page-editor/SectionsPanel.d.ts.map +1 -0
  148. package/dist/views/page-editor/SectionsPanel.js +43 -0
  149. package/dist/views/page-editor/SectionsPanel.js.map +1 -0
  150. package/dist/views/page-editor/ValidationSummary.d.ts +9 -0
  151. package/dist/views/page-editor/ValidationSummary.d.ts.map +1 -0
  152. package/dist/views/page-editor/ValidationSummary.js +10 -0
  153. package/dist/views/page-editor/ValidationSummary.js.map +1 -0
  154. package/dist/views/page-editor/section-icons.d.ts +4 -0
  155. package/dist/views/page-editor/section-icons.d.ts.map +1 -0
  156. package/dist/views/page-editor/section-icons.js +20 -0
  157. package/dist/views/page-editor/section-icons.js.map +1 -0
  158. package/dist/views/page-editor/section-types.d.ts +11 -0
  159. package/dist/views/page-editor/section-types.d.ts.map +1 -0
  160. package/dist/views/page-editor/section-types.js +10 -0
  161. package/dist/views/page-editor/section-types.js.map +1 -0
  162. package/dist/views/page-editor/sections/ArticleBodySection.d.ts +13 -0
  163. package/dist/views/page-editor/sections/ArticleBodySection.d.ts.map +1 -0
  164. package/dist/views/page-editor/sections/ArticleBodySection.js +23 -0
  165. package/dist/views/page-editor/sections/ArticleBodySection.js.map +1 -0
  166. package/dist/views/page-editor/sections/AuthorBioSection.d.ts +8 -0
  167. package/dist/views/page-editor/sections/AuthorBioSection.d.ts.map +1 -0
  168. package/dist/views/page-editor/sections/AuthorBioSection.js +21 -0
  169. package/dist/views/page-editor/sections/AuthorBioSection.js.map +1 -0
  170. package/dist/views/page-editor/sections/ByTheNumbersSection.d.ts +5 -0
  171. package/dist/views/page-editor/sections/ByTheNumbersSection.d.ts.map +1 -0
  172. package/dist/views/page-editor/sections/ByTheNumbersSection.js +10 -0
  173. package/dist/views/page-editor/sections/ByTheNumbersSection.js.map +1 -0
  174. package/dist/views/page-editor/sections/CtaBannerSection.d.ts +5 -0
  175. package/dist/views/page-editor/sections/CtaBannerSection.d.ts.map +1 -0
  176. package/dist/views/page-editor/sections/CtaBannerSection.js +10 -0
  177. package/dist/views/page-editor/sections/CtaBannerSection.js.map +1 -0
  178. package/dist/views/page-editor/sections/FeatureHighlightSection.d.ts +5 -0
  179. package/dist/views/page-editor/sections/FeatureHighlightSection.d.ts.map +1 -0
  180. package/dist/views/page-editor/sections/FeatureHighlightSection.js +18 -0
  181. package/dist/views/page-editor/sections/FeatureHighlightSection.js.map +1 -0
  182. package/dist/views/page-editor/sections/GallerySection.d.ts +6 -0
  183. package/dist/views/page-editor/sections/GallerySection.d.ts.map +1 -0
  184. package/dist/views/page-editor/sections/GallerySection.js +17 -0
  185. package/dist/views/page-editor/sections/GallerySection.js.map +1 -0
  186. package/dist/views/page-editor/sections/HeroBannerSection.d.ts +5 -0
  187. package/dist/views/page-editor/sections/HeroBannerSection.d.ts.map +1 -0
  188. package/dist/views/page-editor/sections/HeroBannerSection.js +11 -0
  189. package/dist/views/page-editor/sections/HeroBannerSection.js.map +1 -0
  190. package/dist/views/page-editor/sections/MissionSection.d.ts +6 -0
  191. package/dist/views/page-editor/sections/MissionSection.d.ts.map +1 -0
  192. package/dist/views/page-editor/sections/MissionSection.js +12 -0
  193. package/dist/views/page-editor/sections/MissionSection.js.map +1 -0
  194. package/dist/views/page-editor/sections/PullQuoteSection.d.ts +6 -0
  195. package/dist/views/page-editor/sections/PullQuoteSection.d.ts.map +1 -0
  196. package/dist/views/page-editor/sections/PullQuoteSection.js +13 -0
  197. package/dist/views/page-editor/sections/PullQuoteSection.js.map +1 -0
  198. package/dist/views/page-editor/sections/RelatedPostsSection.d.ts +12 -0
  199. package/dist/views/page-editor/sections/RelatedPostsSection.d.ts.map +1 -0
  200. package/dist/views/page-editor/sections/RelatedPostsSection.js +23 -0
  201. package/dist/views/page-editor/sections/RelatedPostsSection.js.map +1 -0
  202. package/dist/views/page-editor/sections/SectionRenderer.d.ts +22 -0
  203. package/dist/views/page-editor/sections/SectionRenderer.d.ts.map +1 -0
  204. package/dist/views/page-editor/sections/SectionRenderer.js +52 -0
  205. package/dist/views/page-editor/sections/SectionRenderer.js.map +1 -0
  206. package/dist/views/page-editor/sections/index.d.ts +14 -0
  207. package/dist/views/page-editor/sections/index.d.ts.map +1 -0
  208. package/dist/views/page-editor/sections/index.js +12 -0
  209. package/dist/views/page-editor/sections/index.js.map +1 -0
  210. package/dist/views/page-editor/sections/parts.d.ts +55 -0
  211. package/dist/views/page-editor/sections/parts.d.ts.map +1 -0
  212. package/dist/views/page-editor/sections/parts.js +60 -0
  213. package/dist/views/page-editor/sections/parts.js.map +1 -0
  214. package/dist/views/page-editor/viewports.d.ts +14 -0
  215. package/dist/views/page-editor/viewports.d.ts.map +1 -0
  216. package/dist/views/page-editor/viewports.js +12 -0
  217. package/dist/views/page-editor/viewports.js.map +1 -0
  218. package/dist/views/post-editor/PostEditorCanvas.d.ts +21 -0
  219. package/dist/views/post-editor/PostEditorCanvas.d.ts.map +1 -0
  220. package/dist/views/post-editor/PostEditorCanvas.js +58 -0
  221. package/dist/views/post-editor/PostEditorCanvas.js.map +1 -0
  222. package/dist/views/post-editor/PostFieldsModal.d.ts +12 -0
  223. package/dist/views/post-editor/PostFieldsModal.d.ts.map +1 -0
  224. package/dist/views/post-editor/PostFieldsModal.js +53 -0
  225. package/dist/views/post-editor/PostFieldsModal.js.map +1 -0
  226. package/dist/views/post-editor/PostHeader.d.ts +15 -0
  227. package/dist/views/post-editor/PostHeader.d.ts.map +1 -0
  228. package/dist/views/post-editor/PostHeader.js +27 -0
  229. package/dist/views/post-editor/PostHeader.js.map +1 -0
  230. package/dist/views/post-editor/PostHeaderPanel.d.ts +11 -0
  231. package/dist/views/post-editor/PostHeaderPanel.d.ts.map +1 -0
  232. package/dist/views/post-editor/PostHeaderPanel.js +21 -0
  233. package/dist/views/post-editor/PostHeaderPanel.js.map +1 -0
  234. package/dist/views/post-editor/PostPreview.d.ts +14 -0
  235. package/dist/views/post-editor/PostPreview.d.ts.map +1 -0
  236. package/dist/views/post-editor/PostPreview.js +53 -0
  237. package/dist/views/post-editor/PostPreview.js.map +1 -0
  238. package/dist/views/post-editor/PostSectionEditor.d.ts +17 -0
  239. package/dist/views/post-editor/PostSectionEditor.d.ts.map +1 -0
  240. package/dist/views/post-editor/PostSectionEditor.js +290 -0
  241. package/dist/views/post-editor/PostSectionEditor.js.map +1 -0
  242. package/dist/views/post-editor/PostTemplateEditor.d.ts +9 -0
  243. package/dist/views/post-editor/PostTemplateEditor.d.ts.map +1 -0
  244. package/dist/views/post-editor/PostTemplateEditor.js +144 -0
  245. package/dist/views/post-editor/PostTemplateEditor.js.map +1 -0
  246. package/package.json +2 -2
  247. package/src/AdminRoot.tsx +214 -2
  248. package/src/__tests__/components/posts-badges.test.tsx +77 -0
  249. package/src/__tests__/layout/sidebar-posts.test.ts +179 -0
  250. package/src/__tests__/lib/page-editor-service.test.ts +173 -0
  251. package/src/__tests__/lib/pages-service.test.ts +157 -0
  252. package/src/__tests__/lib/post-editor-service.test.ts +215 -0
  253. package/src/__tests__/lib/posts-service.test.ts +279 -0
  254. package/src/__tests__/lib/useApiData.test.ts +105 -0
  255. package/src/__tests__/views/dashboard.test.tsx +180 -0
  256. package/src/__tests__/views/posts-list-view.test.ts +110 -0
  257. package/src/components/pages/tag-badges.tsx +104 -0
  258. package/src/components/posts/badges.tsx +170 -0
  259. package/src/components/posts/icons.ts +40 -0
  260. package/src/layout/Layout.tsx +66 -10
  261. package/src/layout/Sidebar.tsx +116 -50
  262. package/src/lib/page-editor-service.ts +233 -0
  263. package/src/lib/pages-service.ts +599 -0
  264. package/src/lib/post-editor-service.ts +342 -0
  265. package/src/lib/posts-service.ts +366 -0
  266. package/src/lib/preview-link.ts +63 -0
  267. package/src/router/index.ts +10 -1
  268. package/src/views/Dashboard.tsx +142 -89
  269. package/src/views/Pages/PageTagManager.tsx +436 -0
  270. package/src/views/Pages/PagesListView.tsx +944 -0
  271. package/src/views/Posts/NewPostPicker.tsx +147 -0
  272. package/src/views/Posts/NewPostTypeInstructions.tsx +344 -0
  273. package/src/views/Posts/PostTypesView.tsx +194 -0
  274. package/src/views/Posts/PostsListView.tsx +869 -0
  275. package/src/views/Posts/post-type-presets.ts +105 -0
  276. package/src/views/page-builder/Widgets.tsx +44 -0
  277. package/src/views/page-editor/AddSectionModal.tsx +56 -0
  278. package/src/views/page-editor/EditorCanvas.tsx +127 -0
  279. package/src/views/page-editor/EditorTopBar.tsx +233 -0
  280. package/src/views/page-editor/PagePreview.tsx +96 -0
  281. package/src/views/page-editor/PageSectionEditor.tsx +476 -0
  282. package/src/views/page-editor/PageSettingsModal.tsx +154 -0
  283. package/src/views/page-editor/SectionInspector.tsx +599 -0
  284. package/src/views/page-editor/SectionsPanel.tsx +270 -0
  285. package/src/views/page-editor/ValidationSummary.tsx +53 -0
  286. package/src/views/page-editor/section-icons.ts +33 -0
  287. package/src/views/page-editor/section-types.ts +27 -0
  288. package/src/views/page-editor/sections/ArticleBodySection.tsx +57 -0
  289. package/src/views/page-editor/sections/AuthorBioSection.tsx +61 -0
  290. package/src/views/page-editor/sections/ByTheNumbersSection.tsx +41 -0
  291. package/src/views/page-editor/sections/CtaBannerSection.tsx +29 -0
  292. package/src/views/page-editor/sections/FeatureHighlightSection.tsx +63 -0
  293. package/src/views/page-editor/sections/GallerySection.tsx +54 -0
  294. package/src/views/page-editor/sections/HeroBannerSection.tsx +44 -0
  295. package/src/views/page-editor/sections/MissionSection.tsx +22 -0
  296. package/src/views/page-editor/sections/PullQuoteSection.tsx +27 -0
  297. package/src/views/page-editor/sections/RelatedPostsSection.tsx +55 -0
  298. package/src/views/page-editor/sections/SectionRenderer.tsx +102 -0
  299. package/src/views/page-editor/sections/index.ts +13 -0
  300. package/src/views/page-editor/sections/parts.tsx +113 -0
  301. package/src/views/page-editor/viewports.ts +25 -0
  302. package/src/views/post-editor/PostEditorCanvas.tsx +123 -0
  303. package/src/views/post-editor/PostFieldsModal.tsx +196 -0
  304. package/src/views/post-editor/PostHeader.tsx +87 -0
  305. package/src/views/post-editor/PostHeaderPanel.tsx +107 -0
  306. package/src/views/post-editor/PostPreview.tsx +114 -0
  307. package/src/views/post-editor/PostSectionEditor.tsx +506 -0
  308. package/src/views/post-editor/PostTemplateEditor.tsx +347 -0
@@ -0,0 +1,234 @@
1
+ /**
2
+ * Post Editor service — data layer for the flat, section-based Post editor
3
+ * (`/posts/:type/:id/edit`) and the per-type Post Template editor
4
+ * (`/posts/types/:slug/template`).
5
+ *
6
+ * A post is a document in its post-type collection (e.g. `blog`). The editor
7
+ * reads/writes the post's fields (title, slug, excerpt, featured image,
8
+ * body, SEO) plus a `data.sections` JSON list of `PageSection`s — the same
9
+ * flat model the Page Editor uses. New posts inherit their section list from
10
+ * the post type's template.
11
+ *
12
+ * A template is a single document per post type in the hidden
13
+ * `post-templates` collection, matched by `slug` == the post-type slug. It
14
+ * stores the design-driven header layout (`data.header`) and the default
15
+ * section list (`data.sections`).
16
+ *
17
+ * Persistence reuses the existing collection document API, so there is no
18
+ * new endpoint and no Prisma migration — `sections` is declared as `json`
19
+ * on each post-type collection (and `header`/`sections` on `post-templates`)
20
+ * so the field-access layer doesn't strip them on write.
21
+ */
22
+ import { cmsApi } from './api.js';
23
+ import { getSectionType, createSection, addSection, removeSection, duplicateSection, toggleSectionVisibility, moveSection, reorderSections, updateSectionContent, updateSectionSettings, updateSectionMeta, coerceSections, coercePostHeader, defaultPostHeader, validateSectionContent, } from '@actuate-media/cms-core/page-sections';
24
+ // Re-export the pure section helpers so the post editor imports them from
25
+ // one place (mirrors page-editor-service).
26
+ export { createSection, addSection, removeSection, duplicateSection, toggleSectionVisibility, moveSection, reorderSections, updateSectionContent, updateSectionSettings, updateSectionMeta, };
27
+ export const TEMPLATES_COLLECTION = 'post-templates';
28
+ // ─── Validation ──────────────────────────────────────────────────────
29
+ function isBlank(v) {
30
+ return v === undefined || v === null || (typeof v === 'string' && v.trim() === '');
31
+ }
32
+ /**
33
+ * Validate a post for save/publish. Section rules are delegated to the
34
+ * shared `validateSectionContent` so the editor and the server/MCP enforce
35
+ * identical content rules; post-level rules (title/slug, SEO) live here.
36
+ */
37
+ export function validatePost(post, forPublish = false) {
38
+ const errors = [];
39
+ const warnings = [];
40
+ if (isBlank(post.title))
41
+ errors.push('Post title is required.');
42
+ if (isBlank(post.slug))
43
+ errors.push('Post slug is required.');
44
+ if (forPublish) {
45
+ const visible = post.sections.filter((s) => s.visible);
46
+ for (const section of visible) {
47
+ const def = getSectionType(section.sectionType);
48
+ const label = section.name || def?.name || 'Section';
49
+ const result = validateSectionContent(section.sectionType, section.content, section.settings);
50
+ for (const issue of result.errors)
51
+ errors.push(`${label}: ${issue.message}`);
52
+ for (const issue of result.warnings)
53
+ warnings.push(`${label}: ${issue.message}`);
54
+ }
55
+ if (isBlank(post.seoTitle))
56
+ warnings.push('Add an SEO title to improve search results.');
57
+ if (isBlank(post.seoDescription)) {
58
+ warnings.push('Add an SEO description to improve search results.');
59
+ }
60
+ }
61
+ return { errors, warnings };
62
+ }
63
+ /** Validate a template for save (section content only — no post fields). */
64
+ export function validateTemplate(template) {
65
+ const errors = [];
66
+ const warnings = [];
67
+ for (const section of template.sections.filter((s) => s.visible)) {
68
+ const def = getSectionType(section.sectionType);
69
+ const label = section.name || def?.name || 'Section';
70
+ const result = validateSectionContent(section.sectionType, section.content, section.settings);
71
+ for (const issue of result.errors)
72
+ errors.push(`${label}: ${issue.message}`);
73
+ for (const issue of result.warnings)
74
+ warnings.push(`${label}: ${issue.message}`);
75
+ }
76
+ return { errors, warnings };
77
+ }
78
+ function dataStr(data, key) {
79
+ const v = data[key];
80
+ return typeof v === 'string' ? v : '';
81
+ }
82
+ function rowToEditorPost(postType, doc) {
83
+ const data = doc.data ?? {};
84
+ return {
85
+ id: doc.id,
86
+ postType,
87
+ title: doc.title ?? dataStr(data, 'title'),
88
+ slug: doc.slug ?? dataStr(data, 'slug'),
89
+ excerpt: dataStr(data, 'excerpt'),
90
+ featuredImage: dataStr(data, 'featuredImage'),
91
+ body: dataStr(data, 'body'),
92
+ category: dataStr(data, 'category'),
93
+ status: doc.status ?? 'DRAFT',
94
+ publishDate: data.publishDate ?? doc.scheduledAt ?? null,
95
+ sections: coerceSections(data.sections),
96
+ seoTitle: dataStr(data, 'metaTitle'),
97
+ seoDescription: dataStr(data, 'metaDescription'),
98
+ publishedAt: doc.publishedAt ?? null,
99
+ updatedAt: doc.updatedAt ?? null,
100
+ };
101
+ }
102
+ /**
103
+ * Load a post type's template. Looks up the single `post-templates` document
104
+ * whose slug matches the post type. Returns an unsaved default (centered
105
+ * header, empty section list) when the type has no template yet.
106
+ */
107
+ export async function fetchTemplateForEditor(postType) {
108
+ const res = await cmsApi(`/collections/${TEMPLATES_COLLECTION}?pageSize=200`);
109
+ const docs = res.data?.docs ?? [];
110
+ const match = docs.find((d) => (d.slug ?? d.data?.slug) === postType);
111
+ if (!match) {
112
+ return { docId: null, postType, header: defaultPostHeader(), sections: [] };
113
+ }
114
+ const data = match.data ?? {};
115
+ return {
116
+ docId: match.id,
117
+ postType,
118
+ header: coercePostHeader(data.header),
119
+ sections: coerceSections(data.sections),
120
+ };
121
+ }
122
+ function templateWriteBody(template, extra) {
123
+ return JSON.stringify({
124
+ title: `${template.postType} template`,
125
+ slug: template.postType,
126
+ postType: template.postType,
127
+ header: template.header,
128
+ sections: template.sections,
129
+ ...extra,
130
+ });
131
+ }
132
+ /** Persist a post type's template (creates the doc on first save). */
133
+ export async function saveTemplate(template) {
134
+ const endpoint = template.docId
135
+ ? `/collections/${TEMPLATES_COLLECTION}/${template.docId}`
136
+ : `/collections/${TEMPLATES_COLLECTION}`;
137
+ const res = await cmsApi(endpoint, {
138
+ method: template.docId ? 'PUT' : 'POST',
139
+ body: templateWriteBody(template, { status: 'PUBLISHED' }),
140
+ });
141
+ if (res.error || !res.data)
142
+ throw new Error(res.error ?? 'Failed to save template');
143
+ const data = res.data.data ?? {};
144
+ return {
145
+ docId: res.data.id,
146
+ postType: template.postType,
147
+ header: coercePostHeader(data.header),
148
+ sections: coerceSections(data.sections),
149
+ };
150
+ }
151
+ // ─── Posts ───────────────────────────────────────────────────────────
152
+ export async function fetchPostForEditor(postType, id) {
153
+ const res = await cmsApi(`/collections/${encodeURIComponent(postType)}/${id}`);
154
+ if (res.error || !res.data)
155
+ throw new Error(res.error ?? 'Failed to load post');
156
+ return rowToEditorPost(postType, res.data);
157
+ }
158
+ function toWriteBody(post, extra) {
159
+ const payload = {
160
+ title: post.title,
161
+ slug: post.slug,
162
+ excerpt: post.excerpt,
163
+ featuredImage: post.featuredImage,
164
+ body: post.body,
165
+ category: post.category,
166
+ publishDate: post.publishDate,
167
+ sections: post.sections,
168
+ metaTitle: post.seoTitle,
169
+ metaDescription: post.seoDescription,
170
+ ...extra,
171
+ };
172
+ return JSON.stringify(payload);
173
+ }
174
+ export async function createPost(post) {
175
+ const res = await cmsApi(`/collections/${encodeURIComponent(post.postType)}`, {
176
+ method: 'POST',
177
+ body: toWriteBody(post),
178
+ });
179
+ if (res.error || !res.data)
180
+ throw new Error(res.error ?? 'Failed to create post');
181
+ return rowToEditorPost(post.postType, res.data);
182
+ }
183
+ export async function savePostDraft(post) {
184
+ if (!post.id)
185
+ return createPost(post);
186
+ const res = await cmsApi(`/collections/${encodeURIComponent(post.postType)}/${post.id}`, { method: 'PUT', body: toWriteBody(post) });
187
+ if (res.error || !res.data)
188
+ throw new Error(res.error ?? 'Failed to save draft');
189
+ return rowToEditorPost(post.postType, res.data);
190
+ }
191
+ export async function publishPost(post) {
192
+ const endpoint = post.id
193
+ ? `/collections/${encodeURIComponent(post.postType)}/${post.id}`
194
+ : `/collections/${encodeURIComponent(post.postType)}`;
195
+ const res = await cmsApi(endpoint, {
196
+ method: post.id ? 'PUT' : 'POST',
197
+ body: toWriteBody(post, { status: 'PUBLISHED' }),
198
+ });
199
+ if (res.error || !res.data)
200
+ throw new Error(res.error ?? 'Failed to publish post');
201
+ return rowToEditorPost(post.postType, res.data);
202
+ }
203
+ /** A blank post scaffold seeded with the post type's template sections. */
204
+ export async function emptyPostFromTemplate(postType) {
205
+ let sections = [];
206
+ try {
207
+ const template = await fetchTemplateForEditor(postType);
208
+ // Clone with fresh ids so the post owns its own sections, decoupled from
209
+ // the template (editing the post never mutates the type template).
210
+ sections = template.sections.map((s) => ({ ...s, id: createSection(s.sectionType).id }));
211
+ }
212
+ catch {
213
+ // No template / fetch failure → start from an empty section list.
214
+ sections = [];
215
+ }
216
+ return {
217
+ id: null,
218
+ postType,
219
+ title: '',
220
+ slug: '',
221
+ excerpt: '',
222
+ featuredImage: '',
223
+ body: '',
224
+ category: '',
225
+ status: 'DRAFT',
226
+ publishDate: null,
227
+ sections,
228
+ seoTitle: '',
229
+ seoDescription: '',
230
+ publishedAt: null,
231
+ updatedAt: null,
232
+ };
233
+ }
234
+ //# sourceMappingURL=post-editor-service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"post-editor-service.js","sourceRoot":"","sources":["../../src/lib/post-editor-service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,73 @@
1
+ export type PostStatus = 'DRAFT' | 'PUBLISHED' | 'SCHEDULED' | 'ARCHIVED';
2
+ export interface PostType {
3
+ id: string;
4
+ slug: string;
5
+ name: string;
6
+ singularName: string;
7
+ description: string | null;
8
+ urlBase: string;
9
+ accentColor: string | null;
10
+ icon: string | null;
11
+ postCount: number;
12
+ fieldCount: number;
13
+ active: boolean;
14
+ }
15
+ export interface PostAuthor {
16
+ id: string;
17
+ name: string;
18
+ initials: string;
19
+ }
20
+ export interface Post {
21
+ id: string;
22
+ title: string;
23
+ slug: string;
24
+ postTypeId: string;
25
+ postTypeName: string;
26
+ postTypeAccentColor: string | null;
27
+ authorId: string;
28
+ authorName: string;
29
+ authorInitials: string;
30
+ status: PostStatus;
31
+ publishDate: string | null;
32
+ updatedDate: string;
33
+ seoScore: number | null;
34
+ createdAt: string;
35
+ updatedAt: string;
36
+ }
37
+ export interface PostsQuery {
38
+ search?: string;
39
+ postType?: string | 'all';
40
+ status?: PostStatus | 'all';
41
+ author?: string | 'all';
42
+ sortBy?: 'title' | 'type' | 'author' | 'status' | 'date' | 'seo';
43
+ sortDirection?: 'asc' | 'desc';
44
+ page?: number;
45
+ pageSize?: number;
46
+ }
47
+ export interface PostsResult {
48
+ posts: Post[];
49
+ total: number;
50
+ page: number;
51
+ pageSize: number;
52
+ types: PostType[];
53
+ authors: PostAuthor[];
54
+ }
55
+ export declare function fetchPostTypes(): Promise<PostType[]>;
56
+ export declare function fetchPosts(query?: PostsQuery): Promise<PostsResult>;
57
+ export declare function bulkUpdatePosts(ids: Array<{
58
+ postTypeId: string;
59
+ id: string;
60
+ }>, payload: {
61
+ status?: PostStatus;
62
+ }): Promise<{
63
+ ok: number;
64
+ failed: number;
65
+ }>;
66
+ export declare function bulkDeletePosts(ids: Array<{
67
+ postTypeId: string;
68
+ id: string;
69
+ }>): Promise<{
70
+ ok: number;
71
+ failed: number;
72
+ }>;
73
+ //# sourceMappingURL=posts-service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"posts-service.d.ts","sourceRoot":"","sources":["../../src/lib/posts-service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,245 @@
1
+ /**
2
+ * Posts service — thin client over the existing REST endpoints,
3
+ * shaped around the data the Posts admin area needs.
4
+ *
5
+ * V1 strategy: aggregate across post-typed collections client-side
6
+ * (one `/collections/:slug` call per type) and join authors via
7
+ * `/users`. Each view component pulls from `fetchPosts()` so when
8
+ * a server-side `/api/cms/posts` endpoint lands later, only this
9
+ * file changes — callers consume the same `Post[]` / `PostType[]`
10
+ * shapes.
11
+ *
12
+ * Why not a single `/collections/all` call?
13
+ * - Each collection has its own access-control check, so listing
14
+ * across collections must respect per-collection `read` rules.
15
+ * The existing endpoint enforces this per-call; mixing them
16
+ * server-side would need a new auth boundary.
17
+ * - V1 traffic is small (the seed is 15 rows); a few extra
18
+ * parallel requests are well under the rate-limit budget.
19
+ *
20
+ * Promote to a single endpoint when:
21
+ * - Any post-type collection grows past ~5k rows, OR
22
+ * - Page-load latency exceeds 300ms in the admin metrics.
23
+ */
24
+ import { cmsApi } from './api.js';
25
+ function isPostsCollection(c) {
26
+ return c.type === 'post' || (typeof c.group === 'string' && c.group.toLowerCase() === 'posts');
27
+ }
28
+ async function fetchPostCollections() {
29
+ const res = await cmsApi('/collections');
30
+ if (res.error || !res.data)
31
+ return [];
32
+ return res.data.filter((c) => !c.hidden && isPostsCollection(c));
33
+ }
34
+ export async function fetchPostTypes() {
35
+ const cols = await fetchPostCollections();
36
+ // Counts are filled in by `fetchPosts` so the cards / tabs reflect
37
+ // the same numbers users see in the table. When called from a page
38
+ // that doesn't load posts (the New Post picker), the count stays
39
+ // at 0 until the next fetchPosts call.
40
+ return cols.map(collectionToPostType);
41
+ }
42
+ function collectionToPostType(c, count = 0) {
43
+ return {
44
+ id: c.slug,
45
+ slug: c.slug,
46
+ name: c.labels.plural,
47
+ singularName: c.labels.singular,
48
+ description: c.description ?? null,
49
+ urlBase: `/${c.urlPrefix || c.slug}`,
50
+ accentColor: c.color ?? null,
51
+ icon: c.icon ?? null,
52
+ postCount: count,
53
+ fieldCount: c.fieldCount,
54
+ active: true,
55
+ };
56
+ }
57
+ function initialsFromName(name, email) {
58
+ if (name && name.trim().length > 0) {
59
+ const parts = name.trim().split(/\s+/);
60
+ const first = parts[0]?.[0] ?? '';
61
+ const last = parts.length > 1 ? (parts[parts.length - 1]?.[0] ?? '') : '';
62
+ return (first + last).toUpperCase() || (email[0] ?? '?').toUpperCase();
63
+ }
64
+ return (email[0] ?? '?').toUpperCase();
65
+ }
66
+ async function fetchAuthorsMap() {
67
+ const res = await cmsApi('/users?pageSize=200');
68
+ const list = Array.isArray(res.data)
69
+ ? res.data
70
+ : (res.data?.users ?? []);
71
+ const map = new Map();
72
+ for (const u of list) {
73
+ map.set(u.id, {
74
+ id: u.id,
75
+ name: u.name?.trim() || u.email,
76
+ initials: initialsFromName(u.name, u.email),
77
+ });
78
+ }
79
+ return map;
80
+ }
81
+ function extractSeoScore(data) {
82
+ if (!data)
83
+ return null;
84
+ const candidates = ['seoScore', 'seo_score', 'metaScore'];
85
+ for (const k of candidates) {
86
+ const v = data[k];
87
+ if (typeof v === 'number' && Number.isFinite(v))
88
+ return v;
89
+ }
90
+ return null;
91
+ }
92
+ function pickDisplayDate(d) {
93
+ if (d.status === 'SCHEDULED' && d.scheduledAt)
94
+ return d.scheduledAt;
95
+ if (d.status === 'PUBLISHED' && d.publishedAt)
96
+ return d.publishedAt;
97
+ return d.updatedAt;
98
+ }
99
+ export async function fetchPosts(query = {}) {
100
+ const cols = await fetchPostCollections();
101
+ const authorsMap = await fetchAuthorsMap();
102
+ // Pull every doc for each post type (cap at 500 to avoid runaway
103
+ // requests; pagination + filtering still happens client-side over
104
+ // this set). When a type grows past 500 docs we'll need the
105
+ // server-side aggregate endpoint described in this file's header.
106
+ const perTypeRaw = await Promise.all(cols.map(async (c) => {
107
+ const res = await cmsApi(`/collections/${encodeURIComponent(c.slug)}?pageSize=500&sort=updatedAt&order=desc`);
108
+ const docs = (res.data?.docs ?? []);
109
+ return { col: c, docs };
110
+ }));
111
+ // Map docs → Post objects.
112
+ const allPosts = [];
113
+ for (const { col, docs } of perTypeRaw) {
114
+ for (const d of docs) {
115
+ const author = (d.createdById && authorsMap.get(d.createdById)) ||
116
+ (d.updatedById && authorsMap.get(d.updatedById)) || {
117
+ id: '',
118
+ name: 'Unknown',
119
+ initials: '??',
120
+ };
121
+ allPosts.push({
122
+ id: d.id,
123
+ title: d.title ?? d.data?.title ?? '(untitled)',
124
+ slug: d.slug ?? d.data?.slug ?? '',
125
+ postTypeId: col.slug,
126
+ postTypeName: col.labels.singular,
127
+ postTypeAccentColor: col.color ?? null,
128
+ authorId: author.id,
129
+ authorName: author.name,
130
+ authorInitials: author.initials,
131
+ status: d.status,
132
+ publishDate: d.publishedAt ?? d.scheduledAt ?? null,
133
+ updatedDate: pickDisplayDate(d),
134
+ seoScore: extractSeoScore(d.data),
135
+ createdAt: d.createdAt,
136
+ updatedAt: d.updatedAt,
137
+ });
138
+ }
139
+ }
140
+ // Compute per-type counts BEFORE filtering — the tabs at the top
141
+ // of the page reflect the dataset, not the active query.
142
+ const countsBySlug = new Map();
143
+ for (const p of allPosts) {
144
+ countsBySlug.set(p.postTypeId, (countsBySlug.get(p.postTypeId) ?? 0) + 1);
145
+ }
146
+ const types = cols.map((c) => collectionToPostType(c, countsBySlug.get(c.slug) ?? 0));
147
+ // Apply filters / search.
148
+ const search = (query.search ?? '').trim().toLowerCase();
149
+ const filtered = allPosts.filter((p) => {
150
+ if (query.postType && query.postType !== 'all' && p.postTypeId !== query.postType)
151
+ return false;
152
+ if (query.status && query.status !== 'all' && p.status !== query.status)
153
+ return false;
154
+ if (query.author && query.author !== 'all' && p.authorId !== query.author)
155
+ return false;
156
+ if (search) {
157
+ const haystack = `${p.title} ${p.slug} ${p.authorName} ${p.postTypeName}`.toLowerCase();
158
+ if (!haystack.includes(search))
159
+ return false;
160
+ }
161
+ return true;
162
+ });
163
+ // Sort.
164
+ const sortBy = query.sortBy ?? 'date';
165
+ const dir = query.sortDirection ?? 'desc';
166
+ filtered.sort((a, b) => {
167
+ const cmp = comparePostsFor(sortBy, a, b);
168
+ return dir === 'asc' ? cmp : -cmp;
169
+ });
170
+ // Paginate.
171
+ const page = Math.max(1, query.page ?? 1);
172
+ const pageSize = Math.max(1, query.pageSize ?? 10);
173
+ const start = (page - 1) * pageSize;
174
+ const paged = filtered.slice(start, start + pageSize);
175
+ // Unique authors from the full set (so the dropdown lists every
176
+ // contributor, not just the currently visible page).
177
+ const authorIds = new Set();
178
+ const authors = [];
179
+ for (const p of allPosts) {
180
+ if (p.authorId && !authorIds.has(p.authorId)) {
181
+ authorIds.add(p.authorId);
182
+ authors.push({ id: p.authorId, name: p.authorName, initials: p.authorInitials });
183
+ }
184
+ }
185
+ authors.sort((a, b) => a.name.localeCompare(b.name));
186
+ return {
187
+ posts: paged,
188
+ total: filtered.length,
189
+ page,
190
+ pageSize,
191
+ types,
192
+ authors,
193
+ };
194
+ }
195
+ function comparePostsFor(by, a, b) {
196
+ switch (by) {
197
+ case 'title':
198
+ return a.title.localeCompare(b.title);
199
+ case 'type':
200
+ return a.postTypeName.localeCompare(b.postTypeName);
201
+ case 'author':
202
+ return a.authorName.localeCompare(b.authorName);
203
+ case 'status':
204
+ return a.status.localeCompare(b.status);
205
+ case 'seo': {
206
+ const av = a.seoScore ?? -1;
207
+ const bv = b.seoScore ?? -1;
208
+ return av - bv;
209
+ }
210
+ case 'date':
211
+ default:
212
+ return new Date(a.updatedDate).getTime() - new Date(b.updatedDate).getTime();
213
+ }
214
+ }
215
+ // ─── Bulk mutations ──────────────────────────────────────────────────
216
+ export async function bulkUpdatePosts(ids, payload) {
217
+ let ok = 0;
218
+ let failed = 0;
219
+ for (const { postTypeId, id } of ids) {
220
+ const res = await cmsApi(`/collections/${encodeURIComponent(postTypeId)}/${id}`, {
221
+ method: 'PUT',
222
+ body: JSON.stringify(payload),
223
+ });
224
+ if (res.error)
225
+ failed++;
226
+ else
227
+ ok++;
228
+ }
229
+ return { ok, failed };
230
+ }
231
+ export async function bulkDeletePosts(ids) {
232
+ let ok = 0;
233
+ let failed = 0;
234
+ for (const { postTypeId, id } of ids) {
235
+ const res = await cmsApi(`/collections/${encodeURIComponent(postTypeId)}/${id}`, {
236
+ method: 'DELETE',
237
+ });
238
+ if (res.error)
239
+ failed++;
240
+ else
241
+ ok++;
242
+ }
243
+ return { ok, failed };
244
+ }
245
+ //# sourceMappingURL=posts-service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"posts-service.js","sourceRoot":"","sources":["../../src/lib/posts-service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Mint a signed, time-boxed preview token for a single document. The token is
3
+ * bound to the (collection, documentId) pair server-side, so it can only
4
+ * unlock the exact draft it was issued for. Used to build public preview URLs
5
+ * (`/<prefix>/<slug>?preview=<token>`) that render a draft through the
6
+ * consumer's real front-end — see {@link buildPublicPreviewUrl}.
7
+ */
8
+ export declare function createPreviewToken(collection: string, documentId: string, ttlSeconds?: number): Promise<{
9
+ token: string;
10
+ expiresAt: string;
11
+ slug?: string | null;
12
+ }>;
13
+ export interface PublicPreviewUrlParts {
14
+ /** Absolute site origin, e.g. `https://example.com`. Trailing slash optional. */
15
+ siteUrl: string;
16
+ /** Collection URL prefix (e.g. `blog`). Empty for top-level page collections. */
17
+ urlPrefix?: string | null;
18
+ /** Document slug used in the public path. */
19
+ slug: string;
20
+ /** Signed preview token from {@link createPreviewToken}. */
21
+ token: string;
22
+ }
23
+ /**
24
+ * Build the public URL a draft preview opens at — the SAME path the
25
+ * `/resolve` endpoint matches: `<urlPrefix>/<slug>` (or `/<slug>` for
26
+ * top-level pages), with the preview token on the query string so the
27
+ * public route can drop the `PUBLISHED` filter for this one document.
28
+ */
29
+ export declare function buildPublicPreviewUrl({ siteUrl, urlPrefix, slug, token, }: PublicPreviewUrlParts): string;
30
+ /** Resolve the public site origin: explicit config first, else the current tab's origin. */
31
+ export declare function resolveSiteUrl(configSiteUrl?: string | null): string;
32
+ //# sourceMappingURL=preview-link.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preview-link.d.ts","sourceRoot":"","sources":["../../src/lib/preview-link.ts"],"names":[],"mappings":"AAEA;;;;;;GAMG;AACH,wBAAsB,kBAAkB,CACtC,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM,EAClB,UAAU,CAAC,EAAE,MAAM,GAClB,OAAO,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAC,CAYrE;AAED,MAAM,WAAW,qBAAqB;IACpC,iFAAiF;IACjF,OAAO,EAAE,MAAM,CAAA;IACf,iFAAiF;IACjF,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,6CAA6C;IAC7C,IAAI,EAAE,MAAM,CAAA;IACZ,4DAA4D;IAC5D,KAAK,EAAE,MAAM,CAAA;CACd;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,EACpC,OAAO,EACP,SAAS,EACT,IAAI,EACJ,KAAK,GACN,EAAE,qBAAqB,GAAG,MAAM,CAMhC;AAED,4FAA4F;AAC5F,wBAAgB,cAAc,CAAC,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAIpE"}