astro-tractstack 2.0.0-rc.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 (427) hide show
  1. package/LICENSE +110 -0
  2. package/README.md +56 -0
  3. package/astro.d.ts +64 -0
  4. package/bin/create-tractstack.js +483 -0
  5. package/dist/config.js +80 -0
  6. package/dist/index.js +2129 -0
  7. package/package.json +89 -0
  8. package/templates/artpacks/kCz/captainBreakfast_1080px.webp +0 -0
  9. package/templates/artpacks/kCz/captainBreakfast_1920px.webp +0 -0
  10. package/templates/artpacks/kCz/captainBreakfast_600px.webp +0 -0
  11. package/templates/artpacks/kCz/cleanDrips_1080px.webp +0 -0
  12. package/templates/artpacks/kCz/cleanDrips_1920px.webp +0 -0
  13. package/templates/artpacks/kCz/cleanDrips_600px.webp +0 -0
  14. package/templates/artpacks/kCz/crispwaves_1080px.webp +0 -0
  15. package/templates/artpacks/kCz/crispwaves_1920px.webp +0 -0
  16. package/templates/artpacks/kCz/crispwaves_600px.webp +0 -0
  17. package/templates/artpacks/kCz/dragonSkin_1080px.webp +0 -0
  18. package/templates/artpacks/kCz/dragonSkin_1920px.webp +0 -0
  19. package/templates/artpacks/kCz/dragonSkin_600px.webp +0 -0
  20. package/templates/artpacks/kCz/dragon_1080px.webp +0 -0
  21. package/templates/artpacks/kCz/dragon_1920px.webp +0 -0
  22. package/templates/artpacks/kCz/dragon_600px.webp +0 -0
  23. package/templates/artpacks/kCz/nightcity_1080px.webp +0 -0
  24. package/templates/artpacks/kCz/nightcity_1920px.webp +0 -0
  25. package/templates/artpacks/kCz/nightcity_600px.webp +0 -0
  26. package/templates/artpacks/kCz/pattern1_1080px.webp +0 -0
  27. package/templates/artpacks/kCz/pattern1_1920px.webp +0 -0
  28. package/templates/artpacks/kCz/pattern1_600px.webp +0 -0
  29. package/templates/artpacks/kCz/pattern2_1080px.webp +0 -0
  30. package/templates/artpacks/kCz/pattern2_1920px.webp +0 -0
  31. package/templates/artpacks/kCz/pattern2_600px.webp +0 -0
  32. package/templates/artpacks/kCz/skindrips_1080px.webp +0 -0
  33. package/templates/artpacks/kCz/skindrips_1920px.webp +0 -0
  34. package/templates/artpacks/kCz/skindrips_600px.webp +0 -0
  35. package/templates/artpacks/kCz/slimetime_1080px.webp +0 -0
  36. package/templates/artpacks/kCz/slimetime_1920px.webp +0 -0
  37. package/templates/artpacks/kCz/slimetime_600px.webp +0 -0
  38. package/templates/artpacks/kCz/snake_1080px.webp +0 -0
  39. package/templates/artpacks/kCz/snake_1920px.webp +0 -0
  40. package/templates/artpacks/kCz/snake_600px.webp +0 -0
  41. package/templates/artpacks/kCz/toxicshock_1080px.webp +0 -0
  42. package/templates/artpacks/kCz/toxicshock_1920px.webp +0 -0
  43. package/templates/artpacks/kCz/toxicshock_600px.webp +0 -0
  44. package/templates/artpacks/kCz/tractstack_1080px.webp +0 -0
  45. package/templates/artpacks/kCz/tractstack_1920px.webp +0 -0
  46. package/templates/artpacks/kCz/tractstack_600px.webp +0 -0
  47. package/templates/artpacks/kCz/tripdrips_1080px.webp +0 -0
  48. package/templates/artpacks/kCz/tripdrips_1920px.webp +0 -0
  49. package/templates/artpacks/kCz/tripdrips_600px.webp +0 -0
  50. package/templates/artpacks/kCz/wavedrips_1080px.webp +0 -0
  51. package/templates/artpacks/kCz/wavedrips_1920px.webp +0 -0
  52. package/templates/artpacks/kCz/wavedrips_600px.webp +0 -0
  53. package/templates/artpacks/t8k/beach_1080px.webp +0 -0
  54. package/templates/artpacks/t8k/beach_1920px.webp +0 -0
  55. package/templates/artpacks/t8k/beach_600px.webp +0 -0
  56. package/templates/artpacks/t8k/blast_1080px.webp +0 -0
  57. package/templates/artpacks/t8k/blast_1920px.webp +0 -0
  58. package/templates/artpacks/t8k/blast_600px.webp +0 -0
  59. package/templates/artpacks/t8k/bokeh_1080px.webp +0 -0
  60. package/templates/artpacks/t8k/bokeh_1920px.webp +0 -0
  61. package/templates/artpacks/t8k/bokeh_600px.webp +0 -0
  62. package/templates/artpacks/t8k/cartoon_1080px.webp +0 -0
  63. package/templates/artpacks/t8k/cartoon_1920px.webp +0 -0
  64. package/templates/artpacks/t8k/cartoon_600px.webp +0 -0
  65. package/templates/artpacks/t8k/darkeggshell_1080px.webp +0 -0
  66. package/templates/artpacks/t8k/darkeggshell_1920px.webp +0 -0
  67. package/templates/artpacks/t8k/darkeggshell_600px.webp +0 -0
  68. package/templates/artpacks/t8k/explosion_1080px.webp +0 -0
  69. package/templates/artpacks/t8k/explosion_1920px.webp +0 -0
  70. package/templates/artpacks/t8k/explosion_600px.webp +0 -0
  71. package/templates/artpacks/t8k/floral_1080px.webp +0 -0
  72. package/templates/artpacks/t8k/floral_1920px.webp +0 -0
  73. package/templates/artpacks/t8k/floral_600px.webp +0 -0
  74. package/templates/artpacks/t8k/flower_1080px.webp +0 -0
  75. package/templates/artpacks/t8k/flower_1920px.webp +0 -0
  76. package/templates/artpacks/t8k/flower_600px.webp +0 -0
  77. package/templates/artpacks/t8k/foliage_1080px.webp +0 -0
  78. package/templates/artpacks/t8k/foliage_1920px.webp +0 -0
  79. package/templates/artpacks/t8k/foliage_600px.webp +0 -0
  80. package/templates/artpacks/t8k/mist_1080px.webp +0 -0
  81. package/templates/artpacks/t8k/mist_1920px.webp +0 -0
  82. package/templates/artpacks/t8k/mist_600px.webp +0 -0
  83. package/templates/artpacks/t8k/portal_1080px.webp +0 -0
  84. package/templates/artpacks/t8k/portal_1920px.webp +0 -0
  85. package/templates/artpacks/t8k/portal_600px.webp +0 -0
  86. package/templates/artpacks/t8k/storytime_1080px.webp +0 -0
  87. package/templates/artpacks/t8k/storytime_1920px.webp +0 -0
  88. package/templates/artpacks/t8k/storytime_600px.webp +0 -0
  89. package/templates/artpacks/t8k/tacky_1080px.webp +0 -0
  90. package/templates/artpacks/t8k/tacky_1920px.webp +0 -0
  91. package/templates/artpacks/t8k/tacky_600px.webp +0 -0
  92. package/templates/artpacks/t8k/wallpaper_1080px.webp +0 -0
  93. package/templates/artpacks/t8k/wallpaper_1920px.webp +0 -0
  94. package/templates/artpacks/t8k/wallpaper_600px.webp +0 -0
  95. package/templates/brand/favicon.ico +0 -0
  96. package/templates/brand/logo.svg +19 -0
  97. package/templates/brand/static.jpg +0 -0
  98. package/templates/brand/wordmark.svg +4 -0
  99. package/templates/css/custom.css +51 -0
  100. package/templates/css/frontend.css +3519 -0
  101. package/templates/css/storykeep.css +92872 -0
  102. package/templates/custom/minimal/CodeHook.astro +53 -0
  103. package/templates/custom/minimal/CustomRoutes.astro +46 -0
  104. package/templates/custom/with-examples/CodeHook.astro +49 -0
  105. package/templates/custom/with-examples/CustomHero.astro +13 -0
  106. package/templates/custom/with-examples/CustomRoutes.astro +39 -0
  107. package/templates/custom/with-examples/pages/Collections.astro +110 -0
  108. package/templates/env.example +8 -0
  109. package/templates/fonts/Inter-Black.woff2 +0 -0
  110. package/templates/fonts/Inter-Bold.woff2 +0 -0
  111. package/templates/fonts/Inter-Regular.woff2 +0 -0
  112. package/templates/icons/h2.svg +1 -0
  113. package/templates/icons/h3.svg +1 -0
  114. package/templates/icons/h4.svg +1 -0
  115. package/templates/icons/h5.svg +1 -0
  116. package/templates/icons/image.svg +7 -0
  117. package/templates/icons/text.svg +6 -0
  118. package/templates/socials/codepen.svg +1 -0
  119. package/templates/socials/discord.svg +1 -0
  120. package/templates/socials/facebook.svg +1 -0
  121. package/templates/socials/github.svg +1 -0
  122. package/templates/socials/instagram.svg +1 -0
  123. package/templates/socials/linkedin.svg +1 -0
  124. package/templates/socials/mail.svg +1 -0
  125. package/templates/socials/rumble.svg +1 -0
  126. package/templates/socials/tiktok.svg +1 -0
  127. package/templates/socials/twitch.svg +1 -0
  128. package/templates/socials/twitter.svg +1 -0
  129. package/templates/socials/x.svg +1 -0
  130. package/templates/socials/youtube.svg +1 -0
  131. package/templates/src/client/analytics-events.ts +213 -0
  132. package/templates/src/client/belief-events.ts +205 -0
  133. package/templates/src/client/sse.ts +667 -0
  134. package/templates/src/components/Footer.astro +246 -0
  135. package/templates/src/components/Fragment.astro +70 -0
  136. package/templates/src/components/Header.astro +458 -0
  137. package/templates/src/components/Menu.tsx +196 -0
  138. package/templates/src/components/codehooks/BunnyVideoSetup.tsx +692 -0
  139. package/templates/src/components/codehooks/BunnyVideoWrapper.astro +78 -0
  140. package/templates/src/components/codehooks/EpinetDurationSelector.tsx +1020 -0
  141. package/templates/src/components/codehooks/EpinetTableView.tsx +594 -0
  142. package/templates/src/components/codehooks/EpinetWrapper.tsx +424 -0
  143. package/templates/src/components/codehooks/FeaturedContent.astro +273 -0
  144. package/templates/src/components/codehooks/FeaturedContentSetup.tsx +738 -0
  145. package/templates/src/components/codehooks/ListContent.astro +460 -0
  146. package/templates/src/components/codehooks/ListContentSetup.tsx +649 -0
  147. package/templates/src/components/codehooks/SankeyDiagram.tsx +359 -0
  148. package/templates/src/components/compositor/Compositor.tsx +144 -0
  149. package/templates/src/components/compositor/Node.tsx +415 -0
  150. package/templates/src/components/compositor/NodeWithGuid.tsx +25 -0
  151. package/templates/src/components/compositor/PanelVisibilityWrapper.tsx +87 -0
  152. package/templates/src/components/compositor/elements/Belief.tsx +148 -0
  153. package/templates/src/components/compositor/elements/BgImage.tsx +118 -0
  154. package/templates/src/components/compositor/elements/BgVisualBreak.tsx +102 -0
  155. package/templates/src/components/compositor/elements/BunnyVideo.tsx +63 -0
  156. package/templates/src/components/compositor/elements/IdentifyAs.tsx +66 -0
  157. package/templates/src/components/compositor/elements/PlayButton.tsx +19 -0
  158. package/templates/src/components/compositor/elements/SignUp.tsx +179 -0
  159. package/templates/src/components/compositor/elements/Svg.tsx +33 -0
  160. package/templates/src/components/compositor/elements/ToggleBelief.tsx +36 -0
  161. package/templates/src/components/compositor/elements/YouTubeWrapper.tsx +33 -0
  162. package/templates/src/components/compositor/nodes/BgPaneWrapper.tsx +35 -0
  163. package/templates/src/components/compositor/nodes/GhostInsertBlock.tsx +189 -0
  164. package/templates/src/components/compositor/nodes/Markdown.tsx +179 -0
  165. package/templates/src/components/compositor/nodes/Pane.tsx +277 -0
  166. package/templates/src/components/compositor/nodes/Pane_eraser.tsx +69 -0
  167. package/templates/src/components/compositor/nodes/Pane_layout.tsx +77 -0
  168. package/templates/src/components/compositor/nodes/RenderChildren.tsx +19 -0
  169. package/templates/src/components/compositor/nodes/StoryFragment.tsx +35 -0
  170. package/templates/src/components/compositor/nodes/TagElement.tsx +14 -0
  171. package/templates/src/components/compositor/nodes/Widget.tsx +115 -0
  172. package/templates/src/components/compositor/nodes/tagElements/NodeA.tsx +4 -0
  173. package/templates/src/components/compositor/nodes/tagElements/NodeA_eraser.tsx +26 -0
  174. package/templates/src/components/compositor/nodes/tagElements/NodeAnchorComponent.tsx +248 -0
  175. package/templates/src/components/compositor/nodes/tagElements/NodeBasicTag.tsx +684 -0
  176. package/templates/src/components/compositor/nodes/tagElements/NodeBasicTag_eraser.tsx +62 -0
  177. package/templates/src/components/compositor/nodes/tagElements/NodeBasicTag_insert.tsx +120 -0
  178. package/templates/src/components/compositor/nodes/tagElements/NodeBasicTag_settings.tsx +62 -0
  179. package/templates/src/components/compositor/nodes/tagElements/NodeButton.tsx +5 -0
  180. package/templates/src/components/compositor/nodes/tagElements/NodeButton_eraser.tsx +26 -0
  181. package/templates/src/components/compositor/nodes/tagElements/NodeImg.tsx +28 -0
  182. package/templates/src/components/compositor/nodes/tagElements/NodeText.tsx +18 -0
  183. package/templates/src/components/compositor/nodes/tagElements/TabIndicator.tsx +51 -0
  184. package/templates/src/components/compositor/preview/FeaturedContentPreview.tsx +128 -0
  185. package/templates/src/components/compositor/preview/ListContentPreview.tsx +213 -0
  186. package/templates/src/components/compositor/preview/OgImagePreview.tsx +223 -0
  187. package/templates/src/components/compositor/preview/PaneSnapshotGenerator.tsx +199 -0
  188. package/templates/src/components/compositor/preview/PanesPreviewGenerator.tsx +123 -0
  189. package/templates/src/components/compositor/preview/VisualBreakPreview.tsx +154 -0
  190. package/templates/src/components/edit/Header.tsx +181 -0
  191. package/templates/src/components/edit/PanelSwitch.tsx +446 -0
  192. package/templates/src/components/edit/SettingsPanel.tsx +70 -0
  193. package/templates/src/components/edit/ToolBar.tsx +101 -0
  194. package/templates/src/components/edit/ToolMode.tsx +121 -0
  195. package/templates/src/components/edit/context/ContextPaneConfig.tsx +91 -0
  196. package/templates/src/components/edit/context/ContextPaneConfig_slug.tsx +174 -0
  197. package/templates/src/components/edit/context/ContextPaneConfig_title.tsx +186 -0
  198. package/templates/src/components/edit/pane/AddPanePanel.tsx +136 -0
  199. package/templates/src/components/edit/pane/AddPanePanel_break.tsx +470 -0
  200. package/templates/src/components/edit/pane/AddPanePanel_codehook.tsx +264 -0
  201. package/templates/src/components/edit/pane/AddPanePanel_new.tsx +623 -0
  202. package/templates/src/components/edit/pane/AddPanePanel_newAICopy.tsx +107 -0
  203. package/templates/src/components/edit/pane/AddPanePanel_newAICopy_modal.tsx +217 -0
  204. package/templates/src/components/edit/pane/AddPanePanel_newCopyMode.tsx +109 -0
  205. package/templates/src/components/edit/pane/AddPanePanel_newCustomCopy.tsx +39 -0
  206. package/templates/src/components/edit/pane/AddPanePanel_reuse.tsx +445 -0
  207. package/templates/src/components/edit/pane/ConfigPanePanel.tsx +245 -0
  208. package/templates/src/components/edit/pane/PageGen.tsx +485 -0
  209. package/templates/src/components/edit/pane/PageGenSelector.tsx +238 -0
  210. package/templates/src/components/edit/pane/PageGenSpecial.tsx +362 -0
  211. package/templates/src/components/edit/pane/PageGen_preview.tsx +495 -0
  212. package/templates/src/components/edit/pane/PanePanel_impression.tsx +258 -0
  213. package/templates/src/components/edit/pane/PanePanel_path.tsx +268 -0
  214. package/templates/src/components/edit/pane/PanePanel_slug.tsx +219 -0
  215. package/templates/src/components/edit/pane/PanePanel_title.tsx +142 -0
  216. package/templates/src/components/edit/panels/StyleBreakPanel.tsx +182 -0
  217. package/templates/src/components/edit/panels/StyleCodeHookPanel.tsx +439 -0
  218. package/templates/src/components/edit/panels/StyleElementPanel.tsx +177 -0
  219. package/templates/src/components/edit/panels/StyleElementPanel_add.tsx +349 -0
  220. package/templates/src/components/edit/panels/StyleElementPanel_remove.tsx +159 -0
  221. package/templates/src/components/edit/panels/StyleElementPanel_update.tsx +320 -0
  222. package/templates/src/components/edit/panels/StyleImagePanel.tsx +460 -0
  223. package/templates/src/components/edit/panels/StyleImagePanel_add.tsx +296 -0
  224. package/templates/src/components/edit/panels/StyleImagePanel_remove.tsx +153 -0
  225. package/templates/src/components/edit/panels/StyleImagePanel_update.tsx +312 -0
  226. package/templates/src/components/edit/panels/StyleLiElementPanel.tsx +273 -0
  227. package/templates/src/components/edit/panels/StyleLiElementPanel_add.tsx +301 -0
  228. package/templates/src/components/edit/panels/StyleLiElementPanel_remove.tsx +132 -0
  229. package/templates/src/components/edit/panels/StyleLiElementPanel_update.tsx +313 -0
  230. package/templates/src/components/edit/panels/StyleLinkPanel.tsx +346 -0
  231. package/templates/src/components/edit/panels/StyleLinkPanel_add.tsx +265 -0
  232. package/templates/src/components/edit/panels/StyleLinkPanel_config.tsx +240 -0
  233. package/templates/src/components/edit/panels/StyleLinkPanel_remove.tsx +94 -0
  234. package/templates/src/components/edit/panels/StyleLinkPanel_update.tsx +110 -0
  235. package/templates/src/components/edit/panels/StyleParentPanel.tsx +263 -0
  236. package/templates/src/components/edit/panels/StyleParentPanel_add.tsx +275 -0
  237. package/templates/src/components/edit/panels/StyleParentPanel_deleteLayer.tsx +112 -0
  238. package/templates/src/components/edit/panels/StyleParentPanel_remove.tsx +87 -0
  239. package/templates/src/components/edit/panels/StyleParentPanel_update.tsx +141 -0
  240. package/templates/src/components/edit/panels/StyleWidgetPanel.tsx +428 -0
  241. package/templates/src/components/edit/panels/StyleWidgetPanel_add.tsx +292 -0
  242. package/templates/src/components/edit/panels/StyleWidgetPanel_config.tsx +190 -0
  243. package/templates/src/components/edit/panels/StyleWidgetPanel_remove.tsx +152 -0
  244. package/templates/src/components/edit/panels/StyleWidgetPanel_update.tsx +308 -0
  245. package/templates/src/components/edit/state/SaveModal.tsx +811 -0
  246. package/templates/src/components/edit/state/StylesMemory.tsx +310 -0
  247. package/templates/src/components/edit/storyfragment/StoryFragmentConfigPanel.tsx +289 -0
  248. package/templates/src/components/edit/storyfragment/StoryFragmentPanel_menu.tsx +320 -0
  249. package/templates/src/components/edit/storyfragment/StoryFragmentPanel_og.tsx +888 -0
  250. package/templates/src/components/edit/storyfragment/StoryFragmentPanel_slug.tsx +269 -0
  251. package/templates/src/components/edit/storyfragment/StoryFragmentPanel_title.tsx +190 -0
  252. package/templates/src/components/edit/widgets/BeliefWidget.tsx +183 -0
  253. package/templates/src/components/edit/widgets/BunnyWidget.tsx +134 -0
  254. package/templates/src/components/edit/widgets/IdentifyAsWidget.tsx +193 -0
  255. package/templates/src/components/edit/widgets/SignupWidget.tsx +177 -0
  256. package/templates/src/components/edit/widgets/ToggleWidget.tsx +152 -0
  257. package/templates/src/components/edit/widgets/YouTubeWidget.tsx +65 -0
  258. package/templates/src/components/fields/ActionBuilderTimeSelector.tsx +353 -0
  259. package/templates/src/components/fields/ArtpackImage.tsx +480 -0
  260. package/templates/src/components/fields/BackgroundImage.tsx +530 -0
  261. package/templates/src/components/fields/BackgroundImageWrapper.tsx +192 -0
  262. package/templates/src/components/fields/BooleanParam.tsx +67 -0
  263. package/templates/src/components/fields/BunnyMomentSelector.tsx +56 -0
  264. package/templates/src/components/fields/ColorPickerCombo.tsx +284 -0
  265. package/templates/src/components/fields/ImageUpload.tsx +405 -0
  266. package/templates/src/components/fields/MultiParam.tsx +75 -0
  267. package/templates/src/components/fields/PaneBreakCollectionSelector.tsx +97 -0
  268. package/templates/src/components/fields/PaneBreakShapeSelector.tsx +134 -0
  269. package/templates/src/components/fields/SelectedTailwindClass.tsx +44 -0
  270. package/templates/src/components/fields/SingleParam.tsx +73 -0
  271. package/templates/src/components/fields/ViewportComboBox.tsx +252 -0
  272. package/templates/src/components/form/ActionBuilderField.tsx +282 -0
  273. package/templates/src/components/form/ActionBuilderSlugSelector.tsx +182 -0
  274. package/templates/src/components/form/BooleanToggle.tsx +94 -0
  275. package/templates/src/components/form/ColorPicker.tsx +153 -0
  276. package/templates/src/components/form/DateTimeInput.tsx +638 -0
  277. package/templates/src/components/form/EnumSelect.tsx +88 -0
  278. package/templates/src/components/form/FileUpload.tsx +465 -0
  279. package/templates/src/components/form/MagicPathBuilder.tsx +546 -0
  280. package/templates/src/components/form/NumberInput.tsx +101 -0
  281. package/templates/src/components/form/ParagraphArrayInput.tsx +207 -0
  282. package/templates/src/components/form/StringArrayInput.tsx +163 -0
  283. package/templates/src/components/form/StringInput.tsx +88 -0
  284. package/templates/src/components/form/UnsavedChangesBar.tsx +295 -0
  285. package/templates/src/components/form/advanced/APIConfigSection.tsx +69 -0
  286. package/templates/src/components/form/advanced/AuthConfigSection.tsx +97 -0
  287. package/templates/src/components/form/brand/BrandAssetsSection.tsx +93 -0
  288. package/templates/src/components/form/brand/BrandColorsSection.tsx +201 -0
  289. package/templates/src/components/form/brand/SEOSection.tsx +101 -0
  290. package/templates/src/components/form/brand/SiteConfigSection.tsx +61 -0
  291. package/templates/src/components/form/brand/SocialLinksSection.tsx +393 -0
  292. package/templates/src/components/profile/ProfileConsent.tsx +65 -0
  293. package/templates/src/components/profile/ProfileCreate.tsx +462 -0
  294. package/templates/src/components/profile/ProfileEdit.tsx +409 -0
  295. package/templates/src/components/profile/ProfileSwitch.tsx +255 -0
  296. package/templates/src/components/profile/ProfileUnlock.tsx +221 -0
  297. package/templates/src/components/storykeep/Dashboard.tsx +160 -0
  298. package/templates/src/components/storykeep/Dashboard_Activity.tsx +56 -0
  299. package/templates/src/components/storykeep/Dashboard_Advanced.tsx +165 -0
  300. package/templates/src/components/storykeep/Dashboard_Analytics.tsx +451 -0
  301. package/templates/src/components/storykeep/Dashboard_Branding.tsx +95 -0
  302. package/templates/src/components/storykeep/Dashboard_Content.tsx +191 -0
  303. package/templates/src/components/storykeep/controls/UsageCell.tsx +71 -0
  304. package/templates/src/components/storykeep/controls/content/BeliefForm.tsx +378 -0
  305. package/templates/src/components/storykeep/controls/content/BeliefTable.tsx +329 -0
  306. package/templates/src/components/storykeep/controls/content/ContentBrowser.tsx +385 -0
  307. package/templates/src/components/storykeep/controls/content/ContentSummary.tsx +149 -0
  308. package/templates/src/components/storykeep/controls/content/KnownResourceForm.tsx +397 -0
  309. package/templates/src/components/storykeep/controls/content/KnownResourceTable.tsx +260 -0
  310. package/templates/src/components/storykeep/controls/content/ManageContent.tsx +439 -0
  311. package/templates/src/components/storykeep/controls/content/MenuForm.tsx +239 -0
  312. package/templates/src/components/storykeep/controls/content/MenuTable.tsx +332 -0
  313. package/templates/src/components/storykeep/controls/content/ResourceBulkIngest.tsx +724 -0
  314. package/templates/src/components/storykeep/controls/content/ResourceForm.tsx +355 -0
  315. package/templates/src/components/storykeep/controls/content/ResourceTable.tsx +222 -0
  316. package/templates/src/components/storykeep/controls/content/StoryFragmentTable.tsx +482 -0
  317. package/templates/src/components/storykeep/state/BrandingWrapper.tsx +42 -0
  318. package/templates/src/components/storykeep/state/FetchAnalytics.tsx +350 -0
  319. package/templates/src/components/storykeep/widgets/ResponsiveLine.tsx +319 -0
  320. package/templates/src/components/storykeep/widgets/Wizard.tsx +278 -0
  321. package/templates/src/components/tenant/RegistrationForm.tsx +447 -0
  322. package/templates/src/components/widgets/BunnyVideoHero.astro +775 -0
  323. package/templates/src/components/widgets/Impression.tsx +102 -0
  324. package/templates/src/components/widgets/ImpressionWrapper.tsx +214 -0
  325. package/templates/src/constants/beliefs.ts +61 -0
  326. package/templates/src/constants/brandThemes.ts +133 -0
  327. package/templates/src/constants/prompts.json +55 -0
  328. package/templates/src/constants/shapes.ts +556 -0
  329. package/templates/src/constants/stopWords.ts +116 -0
  330. package/templates/src/constants/tailwindColors.json +344 -0
  331. package/templates/src/constants.ts +274 -0
  332. package/templates/src/hooks/useFormState.ts +203 -0
  333. package/templates/src/layouts/Layout.astro +290 -0
  334. package/templates/src/lib/session.ts +126 -0
  335. package/templates/src/lib/storyData.ts +56 -0
  336. package/templates/src/middleware.ts +52 -0
  337. package/templates/src/pages/404.astro +54 -0
  338. package/templates/src/pages/[...slug]/edit.astro +216 -0
  339. package/templates/src/pages/[...slug].astro +148 -0
  340. package/templates/src/pages/api/auth/decode.ts +101 -0
  341. package/templates/src/pages/api/auth/login.ts +122 -0
  342. package/templates/src/pages/api/auth/logout.ts +37 -0
  343. package/templates/src/pages/api/auth/profile.ts +76 -0
  344. package/templates/src/pages/api/orphan-analysis.ts +106 -0
  345. package/templates/src/pages/api/tailwind.ts +116 -0
  346. package/templates/src/pages/collections/[param1].astro +65 -0
  347. package/templates/src/pages/context/[...contextSlug]/edit.astro +207 -0
  348. package/templates/src/pages/context/[...contextSlug].astro +161 -0
  349. package/templates/src/pages/llms.txt.ts +122 -0
  350. package/templates/src/pages/maint.astro +183 -0
  351. package/templates/src/pages/media/[...slug].astro +67 -0
  352. package/templates/src/pages/robots.txt.ts +36 -0
  353. package/templates/src/pages/sandbox/activate.astro +258 -0
  354. package/templates/src/pages/sandbox/register.astro +44 -0
  355. package/templates/src/pages/sandbox/success.astro +179 -0
  356. package/templates/src/pages/sitemap.xml.ts +119 -0
  357. package/templates/src/pages/storykeep/advanced.astro +69 -0
  358. package/templates/src/pages/storykeep/branding.astro +57 -0
  359. package/templates/src/pages/storykeep/content.astro +71 -0
  360. package/templates/src/pages/storykeep/init.astro +36 -0
  361. package/templates/src/pages/storykeep/login.astro +266 -0
  362. package/templates/src/pages/storykeep/logout.astro +84 -0
  363. package/templates/src/pages/storykeep/profile.astro +98 -0
  364. package/templates/src/pages/storykeep.astro +81 -0
  365. package/templates/src/stores/analytics.ts +171 -0
  366. package/templates/src/stores/backend.ts +16 -0
  367. package/templates/src/stores/navigation.ts +149 -0
  368. package/templates/src/stores/nodes.ts +2390 -0
  369. package/templates/src/stores/nodesHistory.ts +85 -0
  370. package/templates/src/stores/notificationSystem.ts +41 -0
  371. package/templates/src/stores/orphanAnalysis.ts +409 -0
  372. package/templates/src/stores/storykeep.ts +247 -0
  373. package/templates/src/types/astro.ts +86 -0
  374. package/templates/src/types/compositorTypes.ts +456 -0
  375. package/templates/src/types/formTypes.ts +281 -0
  376. package/templates/src/types/multiTenant.ts +77 -0
  377. package/templates/src/types/nodeProps.ts +66 -0
  378. package/templates/src/types/tractstack.ts +445 -0
  379. package/templates/src/utils/aai/getTitleSlug.ts +72 -0
  380. package/templates/src/utils/actions/actionButton.ts +101 -0
  381. package/templates/src/utils/actions/lispLexer.ts +57 -0
  382. package/templates/src/utils/actions/preParse_Action.ts +85 -0
  383. package/templates/src/utils/actions/preParse_Bunny.ts +50 -0
  384. package/templates/src/utils/actions/preParse_Clicked.ts +87 -0
  385. package/templates/src/utils/actions/preParse_Impression.ts +71 -0
  386. package/templates/src/utils/api/advancedConfig.ts +66 -0
  387. package/templates/src/utils/api/advancedHelpers.ts +134 -0
  388. package/templates/src/utils/api/beliefConfig.ts +87 -0
  389. package/templates/src/utils/api/beliefHelpers.ts +196 -0
  390. package/templates/src/utils/api/brandConfig.ts +126 -0
  391. package/templates/src/utils/api/brandHelpers.ts +155 -0
  392. package/templates/src/utils/api/fileHelpers.ts +306 -0
  393. package/templates/src/utils/api/menuConfig.ts +57 -0
  394. package/templates/src/utils/api/menuHelpers.ts +156 -0
  395. package/templates/src/utils/api/resourceConfig.ts +158 -0
  396. package/templates/src/utils/api/resourceHelpers.ts +72 -0
  397. package/templates/src/utils/api/tenantConfig.ts +97 -0
  398. package/templates/src/utils/api/tenantHelpers.ts +172 -0
  399. package/templates/src/utils/api.ts +183 -0
  400. package/templates/src/utils/auth.ts +150 -0
  401. package/templates/src/utils/backend.ts +243 -0
  402. package/templates/src/utils/compositor/TemplateMarkdowns.ts +118 -0
  403. package/templates/src/utils/compositor/TemplateNodes.ts +138 -0
  404. package/templates/src/utils/compositor/TemplatePanes.ts +100 -0
  405. package/templates/src/utils/compositor/allowInsert.ts +100 -0
  406. package/templates/src/utils/compositor/domHelpers.ts +37 -0
  407. package/templates/src/utils/compositor/handleClickEvent.ts +131 -0
  408. package/templates/src/utils/compositor/nodesHelper.ts +491 -0
  409. package/templates/src/utils/compositor/nodesMarkdownGenerator.ts +292 -0
  410. package/templates/src/utils/compositor/processMarkdown.ts +431 -0
  411. package/templates/src/utils/compositor/reduceNodesClassNames.ts +192 -0
  412. package/templates/src/utils/compositor/tailwindClasses.ts +1795 -0
  413. package/templates/src/utils/compositor/tailwindColors.ts +227 -0
  414. package/templates/src/utils/compositor/templateMarkdownStyles.ts +1265 -0
  415. package/templates/src/utils/compositor/typeGuards.ts +193 -0
  416. package/templates/src/utils/etl/extractor.ts +119 -0
  417. package/templates/src/utils/etl/index.ts +88 -0
  418. package/templates/src/utils/etl/loader.ts +36 -0
  419. package/templates/src/utils/etl/transformer.ts +286 -0
  420. package/templates/src/utils/helpers.ts +435 -0
  421. package/templates/src/utils/layout.ts +209 -0
  422. package/templates/src/utils/profileStorage.ts +306 -0
  423. package/templates/src/utils/useInterval.ts +27 -0
  424. package/templates/tailwind.config.cjs +169 -0
  425. package/utils/create-resolver.ts +10 -0
  426. package/utils/inject-files.ts +2140 -0
  427. package/utils/validate-config.ts +43 -0
@@ -0,0 +1,72 @@
1
+ import type {
2
+ ResourceConfig,
3
+ ResourceState,
4
+ FieldErrors,
5
+ } from '@/types/tractstack';
6
+
7
+ export function convertToLocalState(backend: ResourceConfig): ResourceState {
8
+ return {
9
+ id: backend.id || '',
10
+ title: backend.title || '',
11
+ slug: backend.slug || '',
12
+ categorySlug: backend.categorySlug || '',
13
+ oneliner: backend.oneliner || '',
14
+ optionsPayload: backend.optionsPayload || {},
15
+ actionLisp: backend.actionLisp || undefined,
16
+ };
17
+ }
18
+
19
+ export function convertToBackendFormat(local: ResourceState): ResourceConfig {
20
+ return {
21
+ id: local.id,
22
+ title: local.title,
23
+ slug: local.slug,
24
+ categorySlug: local.categorySlug,
25
+ oneliner: local.oneliner,
26
+ optionsPayload: local.optionsPayload || {},
27
+ actionLisp: local.actionLisp || undefined,
28
+ };
29
+ }
30
+
31
+ export function validateResource(state: ResourceState): FieldErrors {
32
+ const errors: FieldErrors = {};
33
+
34
+ if (!state.title?.trim()) {
35
+ errors.title = 'Title is required';
36
+ }
37
+
38
+ if (!state.slug?.trim()) {
39
+ errors.slug = 'Slug is required';
40
+ } else if (!/^[a-z0-9-]+$/.test(state.slug)) {
41
+ errors.slug =
42
+ 'Slug must contain only lowercase letters, numbers, and hyphens';
43
+ }
44
+
45
+ if (!state.categorySlug?.trim()) {
46
+ errors.categorySlug = 'Category is required';
47
+ }
48
+
49
+ return errors;
50
+ }
51
+
52
+ export function resourceStateIntercept(
53
+ updatedState: ResourceState,
54
+ fieldName: string
55
+ ): ResourceState {
56
+ const newState = { ...updatedState };
57
+
58
+ if (fieldName === 'title' || fieldName === 'categorySlug') {
59
+ if (newState.title && newState.categorySlug) {
60
+ const titleSlug = newState.title
61
+ .toLowerCase()
62
+ .replace(/[^a-z0-9\s-]/g, '')
63
+ .replace(/\s+/g, '-')
64
+ .replace(/-+/g, '-')
65
+ .trim();
66
+
67
+ newState.slug = `${newState.categorySlug}-${titleSlug}`;
68
+ }
69
+ }
70
+
71
+ return newState;
72
+ }
@@ -0,0 +1,97 @@
1
+ // Tenant configuration API utilities following TractStack v2 patterns
2
+ import { TractStackAPI } from '../api';
3
+ import type {
4
+ TenantProvisioningData,
5
+ TenantCapacity,
6
+ TenantProvisioningResponse,
7
+ } from '@/types/multiTenant';
8
+ import type { TenantActivationRequest } from '@/types/multiTenant';
9
+
10
+ /**
11
+ * Check tenant capacity and existing tenants
12
+ */
13
+ export async function checkTenantCapacity(
14
+ tenantId: string
15
+ ): Promise<TenantCapacity> {
16
+ const api = new TractStackAPI(tenantId);
17
+ try {
18
+ const response = await api.get<TenantCapacity>('/api/v1/tenant/capacity');
19
+
20
+ if (!response.success || !response.data) {
21
+ throw new Error(response.error || 'No data received from server');
22
+ }
23
+
24
+ const data = response.data;
25
+
26
+ // Validate response structure to match backend
27
+ if (
28
+ typeof data.available !== 'boolean' ||
29
+ typeof data.currentTenants !== 'number' ||
30
+ typeof data.maxTenants !== 'number' ||
31
+ typeof data.availableSlots !== 'number'
32
+ ) {
33
+ throw new Error('Invalid response format from server');
34
+ }
35
+
36
+ return data;
37
+ } catch (error) {
38
+ console.error('Failed to fetch tenant capacity:', error);
39
+ throw new Error('Failed to load tenant capacity information');
40
+ }
41
+ }
42
+
43
+ /**
44
+ * Provision a new tenant with reserved status
45
+ */
46
+ export async function provisionTenant(
47
+ tenantId: string,
48
+ data: TenantProvisioningData
49
+ ): Promise<TenantProvisioningResponse> {
50
+ const api = new TractStackAPI(tenantId);
51
+ try {
52
+ const response = await api.post<TenantProvisioningResponse>(
53
+ '/api/v1/tenant/provision',
54
+ data
55
+ );
56
+
57
+ if (!response.success || !response.data) {
58
+ throw new Error(response.error || 'Failed to provision tenant');
59
+ }
60
+
61
+ return response.data;
62
+ } catch (error) {
63
+ console.error('Failed to provision tenant:', error);
64
+
65
+ if (error instanceof Error) {
66
+ throw error;
67
+ }
68
+
69
+ throw new Error('Failed to provision tenant');
70
+ }
71
+ }
72
+
73
+ /**
74
+ * Activate a tenant using the activation token
75
+ */
76
+ export async function activateTenant(
77
+ tenantId: string,
78
+ token: string
79
+ ): Promise<void> {
80
+ const api = new TractStackAPI(tenantId);
81
+ try {
82
+ const request: TenantActivationRequest = { token };
83
+ const response = await api.post('/api/v1/activate-tenant', request);
84
+
85
+ if (!response.success) {
86
+ throw new Error(response.error || 'Failed to activate tenant');
87
+ }
88
+ } catch (error) {
89
+ console.error('Failed to activate tenant:', error);
90
+
91
+ if (error instanceof Error) {
92
+ throw error;
93
+ }
94
+
95
+ throw new Error('Failed to activate tenant');
96
+ }
97
+ }
@@ -0,0 +1,172 @@
1
+ // Tenant form validation and state helpers following TractStack v2 patterns
2
+ import type { TenantProvisioningData } from '@/types/multiTenant';
3
+ import type {
4
+ TenantRegistrationState,
5
+ TenantValidationErrors,
6
+ } from '@/types/multiTenant';
7
+
8
+ /**
9
+ * Convert tenant provisioning data to local form state
10
+ */
11
+ export function convertToLocalState(
12
+ data?: TenantProvisioningData
13
+ ): TenantRegistrationState {
14
+ if (!data) {
15
+ return {
16
+ tenantId: '',
17
+ adminPassword: '',
18
+ confirmPassword: '',
19
+ name: '',
20
+ email: '',
21
+ tursoEnabled: false,
22
+ tursoDatabaseURL: '',
23
+ tursoAuthToken: '',
24
+ };
25
+ }
26
+
27
+ return {
28
+ tenantId: data.tenantId,
29
+ adminPassword: data.adminPassword,
30
+ confirmPassword: '',
31
+ name: data.name,
32
+ email: data.adminEmail,
33
+ tursoEnabled: data.tursoEnabled,
34
+ tursoDatabaseURL: data.tursoDatabaseURL || '',
35
+ tursoAuthToken: data.tursoAuthToken || '',
36
+ };
37
+ }
38
+
39
+ /**
40
+ * Convert local form state to backend format
41
+ */
42
+ export function convertToBackendFormat(
43
+ state: TenantRegistrationState
44
+ ): TenantProvisioningData {
45
+ const data: TenantProvisioningData = {
46
+ tenantId: state.tenantId.trim(),
47
+ adminPassword: state.adminPassword.trim(),
48
+ name: state.name.trim(),
49
+ adminEmail: state.email.trim(),
50
+ tursoEnabled: state.tursoEnabled,
51
+ };
52
+
53
+ // Only include Turso credentials if enabled
54
+ if (state.tursoEnabled) {
55
+ data.tursoDatabaseURL = state.tursoDatabaseURL.trim();
56
+ data.tursoAuthToken = state.tursoAuthToken.trim();
57
+ }
58
+
59
+ return data;
60
+ }
61
+
62
+ /**
63
+ * Validate tenant registration form
64
+ */
65
+ export function validateTenantRegistration(
66
+ state: TenantRegistrationState,
67
+ existingTenants?: string[],
68
+ isInitMode?: boolean
69
+ ): TenantValidationErrors {
70
+ const errors: TenantValidationErrors = {};
71
+
72
+ // Skip ALL tenant ID validation in init mode
73
+ if (!isInitMode) {
74
+ const tenantId = state.tenantId.trim();
75
+ if (!tenantId) {
76
+ errors.tenantId = 'Tenant ID is required';
77
+ } else if (tenantId.length < 3 || tenantId.length > 12) {
78
+ errors.tenantId = 'Tenant ID must be 3-12 characters long';
79
+ } else if (tenantId !== tenantId.toLowerCase()) {
80
+ errors.tenantId = 'Tenant ID must be lowercase';
81
+ } else if (!/^[a-z0-9-]+$/.test(tenantId)) {
82
+ errors.tenantId =
83
+ 'Tenant ID can only contain lowercase letters, numbers, and dashes';
84
+ } else if (tenantId === 'default') {
85
+ errors.tenantId = "'default' is a reserved tenant ID";
86
+ } else if (existingTenants && existingTenants.includes(tenantId)) {
87
+ errors.tenantId = 'This tenant ID is already taken';
88
+ }
89
+ }
90
+
91
+ // Admin password validation
92
+ if (!state.adminPassword.trim()) {
93
+ errors.adminPassword = 'Admin password is required';
94
+ } else if (state.adminPassword.length < 8) {
95
+ errors.adminPassword = 'Admin password must be at least 8 characters long';
96
+ }
97
+
98
+ // Password confirmation validation - only if main password is valid
99
+ if (!errors.adminPassword && state.adminPassword !== state.confirmPassword) {
100
+ errors.confirmPassword = 'Passwords do not match';
101
+ }
102
+
103
+ // Name validation
104
+ if (!state.name.trim()) {
105
+ errors.name = 'Name is required';
106
+ }
107
+
108
+ // Email validation
109
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
110
+ if (!state.email.trim()) {
111
+ errors.email = 'Email is required';
112
+ } else if (!emailRegex.test(state.email.trim())) {
113
+ errors.email = 'Please enter a valid email address';
114
+ }
115
+
116
+ // Turso validation (if enabled)
117
+ if (state.tursoEnabled) {
118
+ if (!state.tursoDatabaseURL.trim()) {
119
+ errors.tursoDatabaseURL =
120
+ 'Turso Database URL is required when Turso is enabled';
121
+ } else if (!state.tursoDatabaseURL.startsWith('libsql://')) {
122
+ errors.tursoDatabaseURL =
123
+ 'Turso Database URL must start with "libsql://"';
124
+ }
125
+
126
+ if (!state.tursoAuthToken.trim()) {
127
+ errors.tursoAuthToken =
128
+ 'Turso Auth Token is required when Turso is enabled';
129
+ }
130
+ }
131
+
132
+ return errors;
133
+ }
134
+
135
+ /**
136
+ * State interceptor for cross-field logic
137
+ */
138
+ export function tenantStateIntercept(
139
+ newState: TenantRegistrationState,
140
+ field: keyof TenantRegistrationState,
141
+ value: any
142
+ ): TenantRegistrationState {
143
+ // Clear Turso fields when disabled
144
+ if (field === 'tursoEnabled' && !value) {
145
+ return {
146
+ ...newState,
147
+ tursoEnabled: false,
148
+ tursoDatabaseURL: '',
149
+ tursoAuthToken: '',
150
+ };
151
+ }
152
+
153
+ // Clear confirmation password when main password changes
154
+ if (field === 'adminPassword') {
155
+ return {
156
+ ...newState,
157
+ adminPassword: value,
158
+ confirmPassword: '', // Clear confirmation to force re-entry
159
+ };
160
+ }
161
+
162
+ // Normalize tenant ID
163
+ if (field === 'tenantId' && typeof value === 'string') {
164
+ return {
165
+ ...newState,
166
+ tenantId: value.toLowerCase().replace(/[^a-z0-9-]/g, ''),
167
+ };
168
+ }
169
+
170
+ // Default behavior
171
+ return newState;
172
+ }
@@ -0,0 +1,183 @@
1
+ export interface APIResponse<T = any> {
2
+ success: boolean;
3
+ data?: T;
4
+ error?: string;
5
+ message?: string;
6
+ }
7
+
8
+ export interface TractStackEvent {
9
+ type: 'page_view' | 'click' | 'form_submit' | 'custom';
10
+ target?: string;
11
+ data?: Record<string, any>;
12
+ timestamp?: number;
13
+ sessionId?: string;
14
+ userId?: string;
15
+ }
16
+
17
+ function getConfig() {
18
+ // Server-side safety check
19
+ if (typeof window === 'undefined') {
20
+ return {
21
+ goBackend: import.meta.env.PUBLIC_GO_BACKEND || 'http://localhost:8080',
22
+ tenantId: import.meta.env.PUBLIC_TENANTID || 'default',
23
+ };
24
+ }
25
+
26
+ return {
27
+ goBackend: import.meta.env.PUBLIC_GO_BACKEND || 'http://localhost:8080',
28
+ tenantId:
29
+ window.TRACTSTACK_CONFIG?.tenantId ||
30
+ import.meta.env.PUBLIC_TENANTID ||
31
+ 'default',
32
+ };
33
+ }
34
+
35
+ //function getTenantFromDomain(): string {
36
+ // if (typeof window === 'undefined') return 'default';
37
+ //
38
+ // const hostname = window.location.hostname;
39
+ //
40
+ // //if (hostname === 'localhost' || hostname === '127.0.0.1') {
41
+ // // return 'default';
42
+ // //}
43
+ //
44
+ // const parts = hostname.split('.');
45
+ // if (
46
+ // parts.length >= 4 &&
47
+ // parts[1] === 'sandbox' &&
48
+ // ['tractstack', 'freewebpress'].includes(parts[2]) &&
49
+ // parts[3] === 'com'
50
+ // ) {
51
+ // return parts[0];
52
+ // }
53
+ //
54
+ // return 'default';
55
+ //}
56
+
57
+ export class TractStackAPI {
58
+ private baseUrl: string;
59
+ private tenantId: string;
60
+
61
+ constructor(tenantId?: string) {
62
+ const config = getConfig();
63
+ this.baseUrl = config.goBackend;
64
+ this.tenantId = tenantId || config.tenantId;
65
+ }
66
+
67
+ async request<T = any>(
68
+ endpoint: string,
69
+ options: RequestInit = {}
70
+ ): Promise<APIResponse<T>> {
71
+ const url = `${this.baseUrl}${endpoint.startsWith('/') ? endpoint : `/${endpoint}`}`;
72
+
73
+ const defaultHeaders = {
74
+ 'Content-Type': 'application/json',
75
+ 'X-Tenant-ID': this.tenantId,
76
+ };
77
+
78
+ try {
79
+ const response = await fetch(url, {
80
+ ...options,
81
+ credentials: 'include',
82
+ headers: {
83
+ ...defaultHeaders,
84
+ ...options.headers,
85
+ },
86
+ });
87
+
88
+ const data = await response.json();
89
+
90
+ if (!response.ok) {
91
+ return {
92
+ success: false,
93
+ error: data.error || `HTTP ${response.status}`,
94
+ data: data.data,
95
+ };
96
+ }
97
+
98
+ return {
99
+ success: true,
100
+ data: data.data || data,
101
+ message: data.message,
102
+ };
103
+ } catch (error) {
104
+ return {
105
+ success: false,
106
+ error: error instanceof Error ? error.message : 'Network error',
107
+ };
108
+ }
109
+ }
110
+
111
+ async get<T = any>(endpoint: string): Promise<APIResponse<T>> {
112
+ return this.request<T>(endpoint, { method: 'GET' });
113
+ }
114
+
115
+ async post<T = any>(endpoint: string, data?: any): Promise<APIResponse<T>> {
116
+ return this.request<T>(endpoint, {
117
+ method: 'POST',
118
+ body: data ? JSON.stringify(data) : undefined,
119
+ });
120
+ }
121
+
122
+ async put<T = any>(endpoint: string, data?: any): Promise<APIResponse<T>> {
123
+ return this.request<T>(endpoint, {
124
+ method: 'PUT',
125
+ body: data ? JSON.stringify(data) : undefined,
126
+ });
127
+ }
128
+
129
+ async trackEvent(event: TractStackEvent): Promise<APIResponse> {
130
+ return this.post('/api/v1/events', {
131
+ ...event,
132
+ timestamp: event.timestamp || Date.now(),
133
+ });
134
+ }
135
+
136
+ async getContent(slug: string): Promise<APIResponse> {
137
+ return this.get(`/api/v1/content/${slug}`);
138
+ }
139
+
140
+ async getFragment(fragmentId: string): Promise<APIResponse> {
141
+ return this.get(`/api/v1/fragments/${fragmentId}`);
142
+ }
143
+
144
+ getTenantId(): string {
145
+ return this.tenantId;
146
+ }
147
+
148
+ setTenantId(tenantId: string): void {
149
+ this.tenantId = tenantId;
150
+ }
151
+
152
+ async getContentMapWithTimestamp(
153
+ lastUpdated?: number
154
+ ): Promise<APIResponse<{ data: any[]; lastUpdated: number }>> {
155
+ let endpoint = 'api/v1/content/full-map';
156
+ if (lastUpdated) {
157
+ endpoint += `?lastUpdated=${lastUpdated}`;
158
+ }
159
+
160
+ // Use the raw request method to get the full response
161
+ const response = await this.request(endpoint);
162
+
163
+ // For this endpoint, the backend returns {data: [...], lastUpdated: 123} directly
164
+ // So response.data IS the {data: [...], lastUpdated: 123} object
165
+ return response as APIResponse<{ data: any[]; lastUpdated: number }>;
166
+ }
167
+ }
168
+
169
+ export function handleAPIResponse<T>(
170
+ response: APIResponse<T>,
171
+ onSuccess?: (data: T) => void,
172
+ onError?: (error: string) => void
173
+ ): boolean {
174
+ if (response.success && response.data) {
175
+ onSuccess?.(response.data);
176
+ return true;
177
+ } else {
178
+ const error = response.error || 'Unknown error occurred';
179
+ onError?.(error);
180
+ console.error('TractStack API Error:', error);
181
+ return false;
182
+ }
183
+ }
@@ -0,0 +1,150 @@
1
+ /**
2
+ * Admin/Editor Authentication Utilities
3
+ * Uses role-specific cookies for secure admin authentication
4
+ */
5
+
6
+ export interface AdminAuthClaims {
7
+ role: 'admin' | 'editor';
8
+ tenantId: string;
9
+ type: 'admin_auth';
10
+ iat: number;
11
+ exp: number;
12
+ }
13
+
14
+ /**
15
+ * Check if user is authenticated (either admin or editor)
16
+ */
17
+ export function isAuthenticated(astro: any): boolean {
18
+ const adminCookie = astro.cookies.get('admin_auth');
19
+ const editorCookie = astro.cookies.get('editor_auth');
20
+
21
+ if (adminCookie?.value) {
22
+ const claims = validateAdminToken(adminCookie.value);
23
+ if (claims && claims.role === 'admin') return true;
24
+ }
25
+
26
+ if (editorCookie?.value) {
27
+ const claims = validateAdminToken(editorCookie.value);
28
+ if (claims && claims.role === 'editor') return true;
29
+ }
30
+
31
+ return false;
32
+ }
33
+
34
+ /**
35
+ * Check if user has admin role
36
+ */
37
+ export function isAdmin(astro: any): boolean {
38
+ const adminCookie = astro.cookies.get('admin_auth');
39
+ if (!adminCookie?.value) return false;
40
+
41
+ const claims = validateAdminToken(adminCookie.value);
42
+ return claims?.role === 'admin';
43
+ }
44
+
45
+ /**
46
+ * Check if user has editor role
47
+ */
48
+ export function isEditor(astro: any): boolean {
49
+ const editorCookie = astro.cookies.get('editor_auth');
50
+ if (!editorCookie?.value) return false;
51
+
52
+ const claims = validateAdminToken(editorCookie.value);
53
+ return claims?.role === 'editor';
54
+ }
55
+
56
+ /**
57
+ * Get user role (admin, editor, or null)
58
+ */
59
+ export function getUserRole(astro: any): 'admin' | 'editor' | null {
60
+ const adminCookie = astro.cookies.get('admin_auth');
61
+ if (adminCookie?.value) {
62
+ const claims = validateAdminToken(adminCookie.value);
63
+ if (claims?.role === 'admin') return 'admin';
64
+ }
65
+
66
+ const editorCookie = astro.cookies.get('editor_auth');
67
+ if (editorCookie?.value) {
68
+ const claims = validateAdminToken(editorCookie.value);
69
+ if (claims?.role === 'editor') return 'editor';
70
+ }
71
+
72
+ return null;
73
+ }
74
+
75
+ /**
76
+ * Page-level protection: Require admin role
77
+ * Returns redirect response if unauthorized, undefined if authorized
78
+ */
79
+ export function requireAdmin(astro: any): Response | undefined {
80
+ if (!isAdmin(astro)) {
81
+ return astro.redirect('/storykeep/login');
82
+ }
83
+ }
84
+
85
+ /**
86
+ * Page-level protection: Require editor role
87
+ * Returns redirect response if unauthorized, undefined if authorized
88
+ */
89
+ export function requireEditor(astro: any): Response | undefined {
90
+ if (!isEditor(astro)) {
91
+ return astro.redirect('/storykeep/login');
92
+ }
93
+ }
94
+
95
+ /**
96
+ * Page-level protection: Require admin OR editor role
97
+ * Returns redirect response if unauthorized, undefined if authorized
98
+ */
99
+ export function requireAdminOrEditor(astro: any): Response | undefined {
100
+ if (!isAuthenticated(astro)) {
101
+ return astro.redirect('/storykeep/login');
102
+ }
103
+ }
104
+
105
+ /**
106
+ * Get admin token for API requests
107
+ * Returns the JWT token from the appropriate cookie
108
+ */
109
+ export function getAdminToken(astro: any): string | null {
110
+ const adminCookie = astro.cookies.get('admin_auth');
111
+ if (adminCookie?.value) {
112
+ const claims = validateAdminToken(adminCookie.value);
113
+ if (claims?.role === 'admin') return adminCookie.value;
114
+ }
115
+
116
+ const editorCookie = astro.cookies.get('editor_auth');
117
+ if (editorCookie?.value) {
118
+ const claims = validateAdminToken(editorCookie.value);
119
+ if (claims?.role === 'editor') return editorCookie.value;
120
+ }
121
+
122
+ return null;
123
+ }
124
+
125
+ /**
126
+ * Validate JWT token and extract claims
127
+ * Note: This is a simplified client-side validation
128
+ * Real validation happens on the backend
129
+ */
130
+ function validateAdminToken(token: string): AdminAuthClaims | null {
131
+ try {
132
+ // Split JWT token
133
+ const parts = token.split('.');
134
+ if (parts.length !== 3) return null;
135
+
136
+ // Decode payload (base64url)
137
+ const payload = parts[1];
138
+ const decoded = atob(payload.replace(/-/g, '+').replace(/_/g, '/'));
139
+ const claims = JSON.parse(decoded) as AdminAuthClaims;
140
+
141
+ // Basic validation
142
+ if (claims.type !== 'admin_auth') return null;
143
+ if (!claims.role || !['admin', 'editor'].includes(claims.role)) return null;
144
+ if (Date.now() / 1000 > claims.exp) return null; // Token expired
145
+
146
+ return claims;
147
+ } catch {
148
+ return null;
149
+ }
150
+ }