@btst/stack 1.0.0 → 1.1.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 (279) hide show
  1. package/README.md +237 -2
  2. package/dist/api/index.cjs +2 -2
  3. package/dist/api/index.d.cts +2 -2
  4. package/dist/api/index.d.mts +2 -2
  5. package/dist/api/index.d.ts +2 -2
  6. package/dist/api/index.mjs +2 -2
  7. package/dist/client/components/compose.cjs +68 -0
  8. package/dist/client/components/compose.mjs +65 -0
  9. package/dist/client/components/error-boundary.cjs +24 -0
  10. package/dist/client/components/error-boundary.mjs +22 -0
  11. package/dist/client/components/index.cjs +10 -0
  12. package/dist/client/components/index.d.cts +52 -0
  13. package/dist/client/components/index.d.mts +52 -0
  14. package/dist/client/components/index.d.ts +52 -0
  15. package/dist/client/components/index.mjs +2 -0
  16. package/dist/client/index.cjs +21 -16
  17. package/dist/client/index.d.cts +102 -14
  18. package/dist/client/index.d.mts +102 -14
  19. package/dist/client/index.d.ts +102 -14
  20. package/dist/client/index.mjs +19 -10
  21. package/dist/client/meta-utils.cjs +162 -0
  22. package/dist/client/meta-utils.mjs +160 -0
  23. package/dist/client/sitemap-utils.cjs +14 -0
  24. package/dist/client/sitemap-utils.mjs +12 -0
  25. package/dist/context/index.cjs +6 -51
  26. package/dist/context/index.d.cts +26 -26
  27. package/dist/context/index.d.mts +26 -26
  28. package/dist/context/index.d.ts +26 -26
  29. package/dist/context/index.mjs +1 -50
  30. package/dist/context/provider.cjs +51 -0
  31. package/dist/context/provider.mjs +46 -0
  32. package/dist/index.cjs +0 -2
  33. package/dist/index.d.cts +1 -1
  34. package/dist/index.d.mts +1 -1
  35. package/dist/index.d.ts +1 -1
  36. package/dist/index.mjs +0 -2
  37. package/dist/plugins/api/index.cjs +15 -0
  38. package/dist/plugins/api/index.d.cts +41 -0
  39. package/dist/plugins/api/index.d.mts +41 -0
  40. package/dist/plugins/api/index.d.ts +41 -0
  41. package/dist/plugins/api/index.mjs +9 -0
  42. package/dist/plugins/blog/api/index.cjs +11 -0
  43. package/dist/plugins/blog/api/index.d.cts +7 -0
  44. package/dist/plugins/blog/api/index.d.mts +7 -0
  45. package/dist/plugins/blog/api/index.d.ts +7 -0
  46. package/dist/plugins/blog/api/index.mjs +2 -0
  47. package/dist/plugins/blog/api/plugin.cjs +569 -0
  48. package/dist/plugins/blog/api/plugin.mjs +565 -0
  49. package/dist/plugins/blog/client/components/forms/image-field.cjs +133 -0
  50. package/dist/plugins/blog/client/components/forms/image-field.mjs +131 -0
  51. package/dist/plugins/blog/client/components/forms/markdown-editor-styles.css +30 -0
  52. package/dist/plugins/blog/client/components/forms/markdown-editor.cjs +106 -0
  53. package/dist/plugins/blog/client/components/forms/markdown-editor.mjs +104 -0
  54. package/dist/plugins/blog/client/components/forms/post-forms.cjs +401 -0
  55. package/dist/plugins/blog/client/components/forms/post-forms.mjs +398 -0
  56. package/dist/plugins/blog/client/components/forms/tags-multiselect.cjs +71 -0
  57. package/dist/plugins/blog/client/components/forms/tags-multiselect.mjs +65 -0
  58. package/dist/plugins/blog/client/components/index.cjs +17 -0
  59. package/dist/plugins/blog/client/components/index.d.cts +22 -0
  60. package/dist/plugins/blog/client/components/index.d.mts +22 -0
  61. package/dist/plugins/blog/client/components/index.d.ts +22 -0
  62. package/dist/plugins/blog/client/components/index.mjs +12 -0
  63. package/dist/plugins/blog/client/components/loading/form-page-skeleton.cjs +62 -0
  64. package/dist/plugins/blog/client/components/loading/form-page-skeleton.mjs +60 -0
  65. package/dist/plugins/blog/client/components/loading/index.cjs +20 -0
  66. package/dist/plugins/blog/client/components/loading/index.mjs +16 -0
  67. package/dist/plugins/blog/client/components/loading/list-page-skeleton.cjs +26 -0
  68. package/dist/plugins/blog/client/components/loading/list-page-skeleton.mjs +24 -0
  69. package/dist/plugins/blog/client/components/loading/page-header-skeleton.cjs +13 -0
  70. package/dist/plugins/blog/client/components/loading/page-header-skeleton.mjs +11 -0
  71. package/dist/plugins/blog/client/components/loading/post-card-skeleton.cjs +22 -0
  72. package/dist/plugins/blog/client/components/loading/post-card-skeleton.mjs +20 -0
  73. package/dist/plugins/blog/client/components/loading/post-page-skeleton.cjs +56 -0
  74. package/dist/plugins/blog/client/components/loading/post-page-skeleton.mjs +54 -0
  75. package/dist/plugins/blog/client/components/pages/404-page.cjs +19 -0
  76. package/dist/plugins/blog/client/components/pages/404-page.mjs +17 -0
  77. package/dist/plugins/blog/client/components/pages/edit-post-page.cjs +41 -0
  78. package/dist/plugins/blog/client/components/pages/edit-post-page.internal.cjs +57 -0
  79. package/dist/plugins/blog/client/components/pages/edit-post-page.internal.mjs +55 -0
  80. package/dist/plugins/blog/client/components/pages/edit-post-page.mjs +39 -0
  81. package/dist/plugins/blog/client/components/pages/home-page.cjs +41 -0
  82. package/dist/plugins/blog/client/components/pages/home-page.internal.cjs +61 -0
  83. package/dist/plugins/blog/client/components/pages/home-page.internal.mjs +59 -0
  84. package/dist/plugins/blog/client/components/pages/home-page.mjs +39 -0
  85. package/dist/plugins/blog/client/components/pages/new-post-page.cjs +37 -0
  86. package/dist/plugins/blog/client/components/pages/new-post-page.internal.cjs +53 -0
  87. package/dist/plugins/blog/client/components/pages/new-post-page.internal.mjs +51 -0
  88. package/dist/plugins/blog/client/components/pages/new-post-page.mjs +35 -0
  89. package/dist/plugins/blog/client/components/pages/post-page.cjs +39 -0
  90. package/dist/plugins/blog/client/components/pages/post-page.internal.cjs +101 -0
  91. package/dist/plugins/blog/client/components/pages/post-page.internal.mjs +99 -0
  92. package/dist/plugins/blog/client/components/pages/post-page.mjs +37 -0
  93. package/dist/plugins/blog/client/components/pages/tag-page.cjs +39 -0
  94. package/dist/plugins/blog/client/components/pages/tag-page.internal.cjs +61 -0
  95. package/dist/plugins/blog/client/components/pages/tag-page.internal.mjs +59 -0
  96. package/dist/plugins/blog/client/components/pages/tag-page.mjs +37 -0
  97. package/dist/plugins/blog/client/components/shared/better-blog-attribution.cjs +24 -0
  98. package/dist/plugins/blog/client/components/shared/better-blog-attribution.mjs +22 -0
  99. package/dist/plugins/blog/client/components/shared/default-error.cjs +18 -0
  100. package/dist/plugins/blog/client/components/shared/default-error.mjs +16 -0
  101. package/dist/plugins/blog/client/components/shared/defaults.cjs +13 -0
  102. package/dist/plugins/blog/client/components/shared/defaults.mjs +10 -0
  103. package/dist/plugins/blog/client/components/shared/empty-list.cjs +21 -0
  104. package/dist/plugins/blog/client/components/shared/empty-list.mjs +19 -0
  105. package/dist/plugins/blog/client/components/shared/error-placeholder.cjs +24 -0
  106. package/dist/plugins/blog/client/components/shared/error-placeholder.mjs +22 -0
  107. package/dist/plugins/blog/client/components/shared/highlight-text.cjs +53 -0
  108. package/dist/plugins/blog/client/components/shared/highlight-text.mjs +51 -0
  109. package/dist/plugins/blog/client/components/shared/markdown-content-styles.css +328 -0
  110. package/dist/plugins/blog/client/components/shared/markdown-content.cjs +324 -0
  111. package/dist/plugins/blog/client/components/shared/markdown-content.mjs +315 -0
  112. package/dist/plugins/blog/client/components/shared/on-this-page.cjs +161 -0
  113. package/dist/plugins/blog/client/components/shared/on-this-page.mjs +158 -0
  114. package/dist/plugins/blog/client/components/shared/page-header.cjs +40 -0
  115. package/dist/plugins/blog/client/components/shared/page-header.mjs +38 -0
  116. package/dist/plugins/blog/client/components/shared/page-layout.cjs +24 -0
  117. package/dist/plugins/blog/client/components/shared/page-layout.mjs +22 -0
  118. package/dist/plugins/blog/client/components/shared/page-wrapper.cjs +23 -0
  119. package/dist/plugins/blog/client/components/shared/page-wrapper.mjs +21 -0
  120. package/dist/plugins/blog/client/components/shared/post-card.cjs +279 -0
  121. package/dist/plugins/blog/client/components/shared/post-card.mjs +277 -0
  122. package/dist/plugins/blog/client/components/shared/post-navigation.cjs +74 -0
  123. package/dist/plugins/blog/client/components/shared/post-navigation.mjs +72 -0
  124. package/dist/plugins/blog/client/components/shared/posts-list.cjs +48 -0
  125. package/dist/plugins/blog/client/components/shared/posts-list.mjs +46 -0
  126. package/dist/plugins/blog/client/components/shared/recent-posts-carousel.cjs +59 -0
  127. package/dist/plugins/blog/client/components/shared/recent-posts-carousel.mjs +57 -0
  128. package/dist/plugins/blog/client/components/shared/search-input.cjs +136 -0
  129. package/dist/plugins/blog/client/components/shared/search-input.mjs +117 -0
  130. package/dist/plugins/blog/client/components/shared/search-modal.cjs +135 -0
  131. package/dist/plugins/blog/client/components/shared/search-modal.mjs +116 -0
  132. package/dist/plugins/blog/client/components/shared/tags-list.cjs +22 -0
  133. package/dist/plugins/blog/client/components/shared/tags-list.mjs +20 -0
  134. package/dist/plugins/blog/client/components/shared/use-route-lifecycle.cjs +50 -0
  135. package/dist/plugins/blog/client/components/shared/use-route-lifecycle.mjs +48 -0
  136. package/dist/plugins/blog/client/hooks/blog-hooks.cjs +380 -0
  137. package/dist/plugins/blog/client/hooks/blog-hooks.mjs +368 -0
  138. package/dist/plugins/blog/client/hooks/index.cjs +17 -0
  139. package/dist/plugins/blog/client/hooks/index.d.cts +150 -0
  140. package/dist/plugins/blog/client/hooks/index.d.mts +150 -0
  141. package/dist/plugins/blog/client/hooks/index.d.ts +150 -0
  142. package/dist/plugins/blog/client/hooks/index.mjs +1 -0
  143. package/dist/plugins/blog/client/hooks/use-debounce.cjs +16 -0
  144. package/dist/plugins/blog/client/hooks/use-debounce.mjs +14 -0
  145. package/dist/plugins/blog/client/index.cjs +7 -0
  146. package/dist/plugins/blog/client/index.d.cts +414 -0
  147. package/dist/plugins/blog/client/index.d.mts +414 -0
  148. package/dist/plugins/blog/client/index.d.ts +414 -0
  149. package/dist/plugins/blog/client/index.mjs +1 -0
  150. package/dist/plugins/blog/client/localization/blog-card.cjs +7 -0
  151. package/dist/plugins/blog/client/localization/blog-card.mjs +5 -0
  152. package/dist/plugins/blog/client/localization/blog-common.cjs +10 -0
  153. package/dist/plugins/blog/client/localization/blog-common.mjs +8 -0
  154. package/dist/plugins/blog/client/localization/blog-forms.cjs +40 -0
  155. package/dist/plugins/blog/client/localization/blog-forms.mjs +38 -0
  156. package/dist/plugins/blog/client/localization/blog-list.cjs +18 -0
  157. package/dist/plugins/blog/client/localization/blog-list.mjs +16 -0
  158. package/dist/plugins/blog/client/localization/blog-post.cjs +13 -0
  159. package/dist/plugins/blog/client/localization/blog-post.mjs +11 -0
  160. package/dist/plugins/blog/client/localization/index.cjs +17 -0
  161. package/dist/plugins/blog/client/localization/index.mjs +15 -0
  162. package/dist/plugins/blog/client/plugin.cjs +462 -0
  163. package/dist/plugins/blog/client/plugin.mjs +460 -0
  164. package/dist/plugins/blog/client.css +3 -0
  165. package/dist/plugins/blog/db.cjs +90 -0
  166. package/dist/plugins/blog/db.mjs +88 -0
  167. package/dist/plugins/blog/query-keys.cjs +181 -0
  168. package/dist/plugins/blog/query-keys.d.cts +530 -0
  169. package/dist/plugins/blog/query-keys.d.mts +530 -0
  170. package/dist/plugins/blog/query-keys.d.ts +530 -0
  171. package/dist/plugins/blog/query-keys.mjs +179 -0
  172. package/dist/plugins/blog/schemas.cjs +39 -0
  173. package/dist/plugins/blog/schemas.mjs +35 -0
  174. package/dist/plugins/blog/style.css +22 -0
  175. package/dist/plugins/blog/utils.cjs +97 -0
  176. package/dist/plugins/blog/utils.mjs +87 -0
  177. package/dist/plugins/client/index.cjs +15 -0
  178. package/dist/plugins/client/index.d.cts +57 -0
  179. package/dist/plugins/client/index.d.mts +57 -0
  180. package/dist/plugins/client/index.d.ts +57 -0
  181. package/dist/plugins/client/index.mjs +9 -0
  182. package/dist/{shared/stack.Br2KMECJ.cjs → plugins/utils.cjs} +1 -8
  183. package/dist/{shared/stack.CwGEQ10b.mjs → plugins/utils.mjs} +2 -8
  184. package/dist/shared/{stack.Dva9muUy.d.cts → stack.ByOugz9d.d.cts} +17 -23
  185. package/dist/shared/{stack.Dva9muUy.d.mts → stack.ByOugz9d.d.mts} +17 -23
  186. package/dist/shared/{stack.Dva9muUy.d.ts → stack.ByOugz9d.d.ts} +17 -23
  187. package/dist/shared/stack.Cr2JoQdo.d.cts +76 -0
  188. package/dist/shared/stack.Cr2JoQdo.d.mts +76 -0
  189. package/dist/shared/stack.Cr2JoQdo.d.ts +76 -0
  190. package/package.json +104 -16
  191. package/src/__tests__/plugins.test.tsx +539 -0
  192. package/src/__tests__/sitemap.test.ts +60 -0
  193. package/src/api/index.ts +73 -0
  194. package/src/client/components/compose.tsx +116 -0
  195. package/src/client/components/error-boundary.tsx +30 -0
  196. package/src/client/components/index.tsx +2 -0
  197. package/src/client/index.ts +107 -0
  198. package/src/client/meta-utils.ts +228 -0
  199. package/src/client/sitemap-utils.ts +46 -0
  200. package/src/context/index.ts +1 -0
  201. package/src/context/provider.tsx +157 -0
  202. package/src/index.ts +1 -0
  203. package/src/plugins/api/index.ts +51 -0
  204. package/src/plugins/blog/api/index.ts +2 -0
  205. package/src/plugins/blog/api/plugin.ts +759 -0
  206. package/src/plugins/blog/client/components/forms/image-field.tsx +165 -0
  207. package/src/plugins/blog/client/components/forms/markdown-editor-styles.css +30 -0
  208. package/src/plugins/blog/client/components/forms/markdown-editor.tsx +136 -0
  209. package/src/plugins/blog/client/components/forms/post-forms.tsx +531 -0
  210. package/src/plugins/blog/client/components/forms/tags-multiselect.tsx +79 -0
  211. package/src/plugins/blog/client/components/index.tsx +11 -0
  212. package/src/plugins/blog/client/components/loading/form-page-skeleton.tsx +75 -0
  213. package/src/plugins/blog/client/components/loading/index.tsx +27 -0
  214. package/src/plugins/blog/client/components/loading/list-page-skeleton.tsx +38 -0
  215. package/src/plugins/blog/client/components/loading/page-header-skeleton.tsx +10 -0
  216. package/src/plugins/blog/client/components/loading/post-card-skeleton.tsx +30 -0
  217. package/src/plugins/blog/client/components/loading/post-page-skeleton.tsx +75 -0
  218. package/src/plugins/blog/client/components/pages/404-page.tsx +23 -0
  219. package/src/plugins/blog/client/components/pages/edit-post-page.internal.tsx +60 -0
  220. package/src/plugins/blog/client/components/pages/edit-post-page.tsx +40 -0
  221. package/src/plugins/blog/client/components/pages/home-page.internal.tsx +71 -0
  222. package/src/plugins/blog/client/components/pages/home-page.tsx +42 -0
  223. package/src/plugins/blog/client/components/pages/new-post-page.internal.tsx +59 -0
  224. package/src/plugins/blog/client/components/pages/new-post-page.tsx +36 -0
  225. package/src/plugins/blog/client/components/pages/post-page.internal.tsx +142 -0
  226. package/src/plugins/blog/client/components/pages/post-page.tsx +38 -0
  227. package/src/plugins/blog/client/components/pages/tag-page.internal.tsx +74 -0
  228. package/src/plugins/blog/client/components/pages/tag-page.tsx +38 -0
  229. package/src/plugins/blog/client/components/shared/better-blog-attribution.tsx +19 -0
  230. package/src/plugins/blog/client/components/shared/default-error.tsx +20 -0
  231. package/src/plugins/blog/client/components/shared/defaults.tsx +9 -0
  232. package/src/plugins/blog/client/components/shared/empty-list.tsx +25 -0
  233. package/src/plugins/blog/client/components/shared/error-placeholder.tsx +20 -0
  234. package/src/plugins/blog/client/components/shared/highlight-text.tsx +80 -0
  235. package/src/plugins/blog/client/components/shared/markdown-content-styles.css +328 -0
  236. package/src/plugins/blog/client/components/shared/markdown-content.tsx +448 -0
  237. package/src/plugins/blog/client/components/shared/on-this-page.tsx +234 -0
  238. package/src/plugins/blog/client/components/shared/page-header.tsx +35 -0
  239. package/src/plugins/blog/client/components/shared/page-layout.tsx +23 -0
  240. package/src/plugins/blog/client/components/shared/page-wrapper.tsx +32 -0
  241. package/src/plugins/blog/client/components/shared/post-card.tsx +308 -0
  242. package/src/plugins/blog/client/components/shared/post-navigation.tsx +98 -0
  243. package/src/plugins/blog/client/components/shared/posts-list.tsx +67 -0
  244. package/src/plugins/blog/client/components/shared/recent-posts-carousel.tsx +79 -0
  245. package/src/plugins/blog/client/components/shared/search-input.tsx +146 -0
  246. package/src/plugins/blog/client/components/shared/search-modal.tsx +162 -0
  247. package/src/plugins/blog/client/components/shared/tags-list.tsx +34 -0
  248. package/src/plugins/blog/client/components/shared/use-route-lifecycle.tsx +68 -0
  249. package/src/plugins/blog/client/hooks/blog-hooks.tsx +623 -0
  250. package/src/plugins/blog/client/hooks/index.tsx +1 -0
  251. package/src/plugins/blog/client/hooks/use-debounce.ts +43 -0
  252. package/src/plugins/blog/client/index.ts +9 -0
  253. package/src/plugins/blog/client/localization/blog-card.ts +3 -0
  254. package/src/plugins/blog/client/localization/blog-common.ts +7 -0
  255. package/src/plugins/blog/client/localization/blog-forms.ts +45 -0
  256. package/src/plugins/blog/client/localization/blog-list.ts +14 -0
  257. package/src/plugins/blog/client/localization/blog-post.ts +9 -0
  258. package/src/plugins/blog/client/localization/index.ts +15 -0
  259. package/src/plugins/blog/client/overrides.ts +123 -0
  260. package/src/plugins/blog/client/plugin.tsx +672 -0
  261. package/src/plugins/blog/client.css +3 -0
  262. package/src/plugins/blog/db.ts +90 -0
  263. package/src/plugins/blog/query-keys.ts +267 -0
  264. package/src/plugins/blog/schemas.ts +39 -0
  265. package/src/plugins/blog/style.css +22 -0
  266. package/src/plugins/blog/types.ts +37 -0
  267. package/src/plugins/blog/utils.ts +144 -0
  268. package/src/plugins/client/index.ts +53 -0
  269. package/src/plugins/index.ts +0 -0
  270. package/src/plugins/utils.ts +35 -0
  271. package/src/types.ts +209 -0
  272. package/dist/plugins/index.cjs +0 -16
  273. package/dist/plugins/index.d.cts +0 -64
  274. package/dist/plugins/index.d.mts +0 -64
  275. package/dist/plugins/index.d.ts +0 -64
  276. package/dist/plugins/index.mjs +0 -11
  277. package/dist/shared/stack.DvFqFlOV.d.cts +0 -22
  278. package/dist/shared/stack.DvFqFlOV.d.mts +0 -22
  279. package/dist/shared/stack.DvFqFlOV.d.ts +0 -22
package/README.md CHANGED
@@ -1,3 +1,238 @@
1
- # BETTER STACK
1
+ # @BTST - Better Stack
2
2
 
3
- WIP
3
+ <div align="center">
4
+
5
+ **Add complete full-stack features to your React app in minutes, not weeks**
6
+
7
+ [![npm version](https://img.shields.io/npm/v/@btst/stack.svg)](https://www.npmjs.com/package/@btst/stack)
8
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
9
+
10
+ [📖 Documentation](https://www.btst.ai) • [🐛 Issues](https://github.com/olliethedev/better-stack/issues)
11
+
12
+ </div>
13
+
14
+ ---
15
+
16
+ ## What Problem Does This Solve?
17
+
18
+ Your app needs a blog. Or a scheduling system. Or user feedback collection. Or an AI assistant. These are **horizontal features**—capabilities that cut across your entire app, not specific to your core domain.
19
+
20
+ Building them from scratch means weeks of work: routes, API endpoints, database schemas, authentication, SSR, metadata, hooks, forms, error handling...
21
+
22
+ Better Stack lets you **add these features in minutes** as composable plugins that work across any React framework.
23
+
24
+ - **Composable architecture** - Mix and match features like LEGO blocks. Add blog + scheduling + feedback + newsletters, all working together seamlessly
25
+ - **Framework agnostic** - One feature works with Next.js App Router, React Router, TanStack Router, Remix—switch frameworks without rewriting
26
+ - **Plugin overrides** - Leverage framework-specific features via overrides. Use Next.js `Image` and `Link`, React Router's `Link`, or any framework's components
27
+ - **Full-stack in one package** - Each feature includes routes, API endpoints, database schemas, React components, hooks, loaders, and metadata
28
+ - **Zero boilerplate** - No wiring up routes, API handlers, or query clients. Just configure and it works
29
+ - **First-class SSR** - Server-side rendering, data prefetching, and SEO metadata generation built-in
30
+ - **Lifecycle hooks** - Intercept at any point: authorization, data transformation, analytics, caching, webhooks
31
+ - **Horizontal features** - Perfect for blog, scheduling, feedback, newsletters, AI assistants, comments—anything reusable across apps
32
+
33
+ ## What Can You Add?
34
+
35
+ **Blog** - Content management, editor, drafts, publishing, SEO, RSS feeds
36
+
37
+ **Scheduling** - Calendar views, time slot booking, availability management, reminders
38
+
39
+ **Feedback** - In-app feedback widgets, user surveys, bug reporting, feature requests
40
+
41
+ **Newsletters** - Subscriber management, email campaigns, unsubscribe handling, analytics
42
+
43
+ **AI Assistant** - Chat interfaces, prompt templates, conversation history, streaming responses
44
+
45
+ **Comments** - Threaded discussions, moderation, reactions, notifications
46
+
47
+ And any other horizontal feature your app needs. Each comes with a complete UI, backend, and data layer.
48
+
49
+ ## Installation
50
+
51
+ ```bash
52
+ npm install @btst/stack
53
+ ```
54
+
55
+ For database schema management, install the CLI:
56
+
57
+ ```bash
58
+ npm install -D @btst/cli
59
+ ```
60
+
61
+ The CLI helps generate migrations, Prisma schemas, and other database artifacts from your plugin schemas.
62
+
63
+ ## Quick Example: Add a Blog to Next.js
64
+
65
+ ### 1. Backend API (`lib/better-stack.ts`)
66
+
67
+ ```ts
68
+ import { betterStack } from "@btst/stack"
69
+ import { blogBackendPlugin } from "@btst/stack/plugins/blog/api"
70
+ import { createPrismaAdapter } from "@btst/adapter-prisma"
71
+
72
+ const { handler, dbSchema } = betterStack({
73
+ basePath: "/api/data",
74
+ plugins: {
75
+ blog: blogBackendPlugin()
76
+ },
77
+ adapter: (db) => createPrismaAdapter(db)({})
78
+ })
79
+
80
+ export { handler, dbSchema }
81
+ ```
82
+
83
+ **Note:** `betterStack()` returns both `handler` and `dbSchema`. The `dbSchema` contains all merged database schemas from your plugins. Use [@btst/cli](https://www.npmjs.com/package/@btst/cli) to generate migrations, Prisma schemas, or other database artifacts from your `dbSchema`.
84
+
85
+ For example, to generate a Prisma schema from your `dbSchema`:
86
+
87
+ ```bash
88
+ npx @btst/cli generate --orm prisma --config lib/better-db.ts --output prisma/schema.prisma --filter-auth
89
+ ```
90
+
91
+ This reads your `dbSchema` export and generates the corresponding Prisma schema file.
92
+
93
+ ### 2. API Route (`app/api/[[...]]/route.ts`)
94
+
95
+ ```ts
96
+ import { handler } from "@/lib/better-stack"
97
+
98
+ export const GET = handler
99
+ export const POST = handler
100
+ ```
101
+
102
+ ### 3. Client Setup (`lib/better-stack-client.tsx`)
103
+
104
+ ```ts
105
+ import { createStackClient } from "@btst/stack/client"
106
+ import { blogClientPlugin } from "@btst/stack/plugins/blog/client"
107
+
108
+ export const getStackClient = (queryClient: QueryClient) => {
109
+ return createStackClient({
110
+ plugins: {
111
+ blog: blogClientPlugin({
112
+ queryClient,
113
+ apiBaseURL: baseURL,
114
+ apiBasePath: "/api/data",
115
+ siteBaseURL: baseURL,
116
+ siteBasePath: "/pages"
117
+ })
118
+ }
119
+ })
120
+ }
121
+ ```
122
+
123
+ ### 4. Page Handler (`app/pages/[[...all]]/page.tsx`)
124
+
125
+ ```ts
126
+ export default async function Page({ params }) {
127
+ const path = `/${(await params).all?.join("/") || ""}`
128
+ const stackClient = getStackClient(queryClient)
129
+ const route = stackClient.router.getRoute(path)
130
+
131
+ if (route?.loader) await route.loader()
132
+
133
+ return (
134
+ <HydrationBoundary state={dehydrate(queryClient)}>
135
+ <ClientRouteResolver path={path} />
136
+ </HydrationBoundary>
137
+ )
138
+ }
139
+ ```
140
+
141
+ ### 5. Layout Provider (`app/pages/[[...all]]/layout.tsx`)
142
+
143
+ ```ts
144
+ import { BetterStackProvider } from "@btst/stack/context"
145
+ import Link from "next/link"
146
+ import Image from "next/image"
147
+ import { useRouter } from "next/navigation"
148
+
149
+ export default function Layout({ children }) {
150
+ const router = useRouter()
151
+
152
+ return (
153
+ <BetterStackProvider
154
+ basePath="/pages"
155
+ overrides={{
156
+ blog: {
157
+ // Use Next.js optimized Image component
158
+ Image: (props) => (
159
+ <Image
160
+ alt={props.alt || ""}
161
+ src={props.src || ""}
162
+ width={400}
163
+ height={300}
164
+ />
165
+ ),
166
+ // Use Next.js Link for client-side navigation
167
+ navigate: (path) => router.push(path),
168
+ refresh: () => router.refresh()
169
+ }
170
+ }}
171
+ >
172
+ {children}
173
+ </BetterStackProvider>
174
+ )
175
+ }
176
+ ```
177
+
178
+ ### 6. Sitemap Generation (`app/sitemap.ts`)
179
+
180
+ ```ts
181
+ import type { MetadataRoute } from "next"
182
+ import { QueryClient } from "@tanstack/react-query"
183
+ import { getStackClient } from "@/lib/better-stack-client"
184
+
185
+ export const dynamic = "force-dynamic"
186
+
187
+ export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
188
+ const queryClient = new QueryClient()
189
+ const stackClient = getStackClient(queryClient)
190
+ const entries = await stackClient.generateSitemap()
191
+
192
+ return entries.map((e) => ({
193
+ url: e.url,
194
+ lastModified: e.lastModified,
195
+ changeFrequency: e.changeFrequency,
196
+ priority: e.priority,
197
+ }))
198
+ }
199
+ ```
200
+
201
+ **That's it.** Your blog feature is live with:
202
+ - ✅ `/blog` - Post listing page
203
+ - ✅ `/blog/[slug]` - Individual post pages
204
+ - ✅ `/blog/new` - Create post editor
205
+ - ✅ `/blog/[slug]/edit` - Edit post page
206
+ - ✅ Full CRUD API (`/api/data/blog/*`)
207
+ - ✅ Server-side rendering
208
+ - ✅ Automatic metadata generation
209
+ - ✅ Automatic sitemap generation
210
+ - ✅ React Query hooks (`usePosts`, `usePost`, etc.)
211
+
212
+ Now add scheduling, feedback, or newsletters the same way. Each feature is independent and composable.
213
+
214
+ ## The Bigger Picture
215
+
216
+ Better Stack transforms how you think about building apps:
217
+
218
+ • **Internal teams** - Build shared features once, use across multiple apps. Your marketing team's blog plugin works in the main app, the docs site, and the landing page
219
+ • **Open source** - Share complete features, not just code snippets. Someone can add your newsletter feature to their Next.js app in minutes
220
+ • **Agencies** - Create a library of reusable features. Drop scheduling into client A's app, feedback into client B's app, both using React Router
221
+ • **SaaS platforms** - Offer feature plugins your customers can compose. They pick blog + scheduling + AI assistant, mix and match to build their ideal app
222
+ • **Rapid prototyping** - Add 5 features in an afternoon instead of 5 weeks. Validate ideas faster
223
+
224
+ Each feature is a complete, self-contained full-stack capability. No configuration files. No code generation. No framework lock-in. Just add it and it works.
225
+
226
+ ## Learn More
227
+
228
+ For complete documentation, examples, and plugin development guides, visit **[https://www.btst.ai](https://www.btst.ai)**
229
+
230
+ ## Examples
231
+
232
+ - [Next.js App Router](./examples/nextjs) - Full SSR with App Router
233
+ - [React Router](./examples/react-router) - Client-side routing with React Router
234
+ - [TanStack Router](./examples/tanstack) - Type-safe routing with TanStack Router
235
+
236
+ ## License
237
+
238
+ MIT © [olliethedev](https://github.com/olliethedev)
@@ -4,7 +4,7 @@ const betterCall = require('better-call');
4
4
  const db = require('@btst/db');
5
5
 
6
6
  function betterStack(config) {
7
- const { plugins, adapter, dbSchema } = config;
7
+ const { plugins, adapter, dbSchema, basePath } = config;
8
8
  const allRoutes = {};
9
9
  let betterDbSchema = dbSchema ?? db.defineDb({});
10
10
  for (const [pluginKey, plugin] of Object.entries(plugins)) {
@@ -18,7 +18,7 @@ function betterStack(config) {
18
18
  }
19
19
  }
20
20
  const router = betterCall.createRouter(allRoutes, {
21
- basePath: "/api"
21
+ basePath
22
22
  });
23
23
  return {
24
24
  handler: router.handler,
@@ -1,5 +1,5 @@
1
- import { e as PrefixedPluginRoutes, f as BackendLibConfig, g as BackendLib } from '../shared/stack.Dva9muUy.cjs';
2
- export { B as BackendPlugin } from '../shared/stack.Dva9muUy.cjs';
1
+ import { d as PrefixedPluginRoutes, e as BackendLibConfig, f as BackendLib } from '../shared/stack.ByOugz9d.cjs';
2
+ export { B as BackendPlugin } from '../shared/stack.ByOugz9d.cjs';
3
3
  import '@btst/yar';
4
4
  import '@btst/db';
5
5
  import 'better-call';
@@ -1,5 +1,5 @@
1
- import { e as PrefixedPluginRoutes, f as BackendLibConfig, g as BackendLib } from '../shared/stack.Dva9muUy.mjs';
2
- export { B as BackendPlugin } from '../shared/stack.Dva9muUy.mjs';
1
+ import { d as PrefixedPluginRoutes, e as BackendLibConfig, f as BackendLib } from '../shared/stack.ByOugz9d.mjs';
2
+ export { B as BackendPlugin } from '../shared/stack.ByOugz9d.mjs';
3
3
  import '@btst/yar';
4
4
  import '@btst/db';
5
5
  import 'better-call';
@@ -1,5 +1,5 @@
1
- import { e as PrefixedPluginRoutes, f as BackendLibConfig, g as BackendLib } from '../shared/stack.Dva9muUy.js';
2
- export { B as BackendPlugin } from '../shared/stack.Dva9muUy.js';
1
+ import { d as PrefixedPluginRoutes, e as BackendLibConfig, f as BackendLib } from '../shared/stack.ByOugz9d.js';
2
+ export { B as BackendPlugin } from '../shared/stack.ByOugz9d.js';
3
3
  import '@btst/yar';
4
4
  import '@btst/db';
5
5
  import 'better-call';
@@ -2,7 +2,7 @@ import { createRouter } from 'better-call';
2
2
  import { defineDb } from '@btst/db';
3
3
 
4
4
  function betterStack(config) {
5
- const { plugins, adapter, dbSchema } = config;
5
+ const { plugins, adapter, dbSchema, basePath } = config;
6
6
  const allRoutes = {};
7
7
  let betterDbSchema = dbSchema ?? defineDb({});
8
8
  for (const [pluginKey, plugin] of Object.entries(plugins)) {
@@ -16,7 +16,7 @@ function betterStack(config) {
16
16
  }
17
17
  }
18
18
  const router = createRouter(allRoutes, {
19
- basePath: "/api"
19
+ basePath
20
20
  });
21
21
  return {
22
22
  handler: router.handler,
@@ -0,0 +1,68 @@
1
+ "use client";
2
+ 'use strict';
3
+
4
+ const jsxRuntime = require('react/jsx-runtime');
5
+ const React = require('react');
6
+ const errorBoundary = require('./error-boundary.cjs');
7
+
8
+ function RouteRenderer({
9
+ router,
10
+ path,
11
+ NotFoundComponent,
12
+ onNotFound,
13
+ onError,
14
+ props
15
+ }) {
16
+ const route = router.getRoute(path);
17
+ return /* @__PURE__ */ jsxRuntime.jsx(
18
+ ComposedRoute,
19
+ {
20
+ path,
21
+ PageComponent: route?.PageComponent,
22
+ ErrorComponent: route?.ErrorComponent,
23
+ LoadingComponent: route?.LoadingComponent,
24
+ onNotFound,
25
+ NotFoundComponent,
26
+ onError,
27
+ props
28
+ }
29
+ );
30
+ }
31
+ function ComposedRoute({
32
+ path,
33
+ PageComponent,
34
+ ErrorComponent,
35
+ LoadingComponent,
36
+ onNotFound,
37
+ NotFoundComponent,
38
+ props,
39
+ onError
40
+ }) {
41
+ if (PageComponent) {
42
+ const content = /* @__PURE__ */ jsxRuntime.jsx(PageComponent, { ...props });
43
+ const isBrowser = typeof window !== "undefined";
44
+ const suspenseFallback = isBrowser && LoadingComponent ? /* @__PURE__ */ jsxRuntime.jsx(LoadingComponent, {}) : null;
45
+ if (ErrorComponent) {
46
+ return /* @__PURE__ */ jsxRuntime.jsx(React.Suspense, { fallback: suspenseFallback, children: /* @__PURE__ */ jsxRuntime.jsx(
47
+ errorBoundary.ErrorBoundary,
48
+ {
49
+ FallbackComponent: ErrorComponent,
50
+ resetKeys: [path],
51
+ onError,
52
+ children: /* @__PURE__ */ jsxRuntime.jsx(React.Suspense, { fallback: suspenseFallback, children: content }, `inner-${path}`)
53
+ }
54
+ ) }, `outer-${path}`);
55
+ }
56
+ return /* @__PURE__ */ jsxRuntime.jsx(React.Suspense, { fallback: suspenseFallback, children: content }, path);
57
+ } else {
58
+ if (onNotFound) {
59
+ onNotFound();
60
+ }
61
+ if (NotFoundComponent) {
62
+ return /* @__PURE__ */ jsxRuntime.jsx(NotFoundComponent, { message: `Unknown route: ${path}` });
63
+ }
64
+ }
65
+ }
66
+
67
+ exports.ComposedRoute = ComposedRoute;
68
+ exports.RouteRenderer = RouteRenderer;
@@ -0,0 +1,65 @@
1
+ "use client";
2
+ import { jsx } from 'react/jsx-runtime';
3
+ import { Suspense } from 'react';
4
+ import { ErrorBoundary } from './error-boundary.mjs';
5
+
6
+ function RouteRenderer({
7
+ router,
8
+ path,
9
+ NotFoundComponent,
10
+ onNotFound,
11
+ onError,
12
+ props
13
+ }) {
14
+ const route = router.getRoute(path);
15
+ return /* @__PURE__ */ jsx(
16
+ ComposedRoute,
17
+ {
18
+ path,
19
+ PageComponent: route?.PageComponent,
20
+ ErrorComponent: route?.ErrorComponent,
21
+ LoadingComponent: route?.LoadingComponent,
22
+ onNotFound,
23
+ NotFoundComponent,
24
+ onError,
25
+ props
26
+ }
27
+ );
28
+ }
29
+ function ComposedRoute({
30
+ path,
31
+ PageComponent,
32
+ ErrorComponent,
33
+ LoadingComponent,
34
+ onNotFound,
35
+ NotFoundComponent,
36
+ props,
37
+ onError
38
+ }) {
39
+ if (PageComponent) {
40
+ const content = /* @__PURE__ */ jsx(PageComponent, { ...props });
41
+ const isBrowser = typeof window !== "undefined";
42
+ const suspenseFallback = isBrowser && LoadingComponent ? /* @__PURE__ */ jsx(LoadingComponent, {}) : null;
43
+ if (ErrorComponent) {
44
+ return /* @__PURE__ */ jsx(Suspense, { fallback: suspenseFallback, children: /* @__PURE__ */ jsx(
45
+ ErrorBoundary,
46
+ {
47
+ FallbackComponent: ErrorComponent,
48
+ resetKeys: [path],
49
+ onError,
50
+ children: /* @__PURE__ */ jsx(Suspense, { fallback: suspenseFallback, children: content }, `inner-${path}`)
51
+ }
52
+ ) }, `outer-${path}`);
53
+ }
54
+ return /* @__PURE__ */ jsx(Suspense, { fallback: suspenseFallback, children: content }, path);
55
+ } else {
56
+ if (onNotFound) {
57
+ onNotFound();
58
+ }
59
+ if (NotFoundComponent) {
60
+ return /* @__PURE__ */ jsx(NotFoundComponent, { message: `Unknown route: ${path}` });
61
+ }
62
+ }
63
+ }
64
+
65
+ export { ComposedRoute, RouteRenderer };
@@ -0,0 +1,24 @@
1
+ "use client";
2
+ 'use strict';
3
+
4
+ const jsxRuntime = require('react/jsx-runtime');
5
+ const reactErrorBoundary = require('react-error-boundary');
6
+
7
+ function ErrorBoundary({
8
+ children,
9
+ FallbackComponent,
10
+ resetKeys,
11
+ onError
12
+ }) {
13
+ return /* @__PURE__ */ jsxRuntime.jsx(
14
+ reactErrorBoundary.ErrorBoundary,
15
+ {
16
+ FallbackComponent,
17
+ onError,
18
+ resetKeys,
19
+ children
20
+ }
21
+ );
22
+ }
23
+
24
+ exports.ErrorBoundary = ErrorBoundary;
@@ -0,0 +1,22 @@
1
+ "use client";
2
+ import { jsx } from 'react/jsx-runtime';
3
+ import { ErrorBoundary as ErrorBoundary$1 } from 'react-error-boundary';
4
+
5
+ function ErrorBoundary({
6
+ children,
7
+ FallbackComponent,
8
+ resetKeys,
9
+ onError
10
+ }) {
11
+ return /* @__PURE__ */ jsx(
12
+ ErrorBoundary$1,
13
+ {
14
+ FallbackComponent,
15
+ onError,
16
+ resetKeys,
17
+ children
18
+ }
19
+ );
20
+ }
21
+
22
+ export { ErrorBoundary };
@@ -0,0 +1,10 @@
1
+ 'use strict';
2
+
3
+ const compose = require('./compose.cjs');
4
+ const errorBoundary = require('./error-boundary.cjs');
5
+
6
+
7
+
8
+ exports.ComposedRoute = compose.ComposedRoute;
9
+ exports.RouteRenderer = compose.RouteRenderer;
10
+ exports.ErrorBoundary = errorBoundary.ErrorBoundary;
@@ -0,0 +1,52 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import react__default, { ErrorInfo } from 'react';
3
+ import { FallbackProps } from 'react-error-boundary';
4
+ export { FallbackProps } from 'react-error-boundary';
5
+ import { createRouter } from '@btst/yar';
6
+
7
+ /**
8
+ * Route type with optional components
9
+ */
10
+ type RouteWithComponents = {
11
+ PageComponent?: react__default.ComponentType;
12
+ ErrorComponent?: react__default.ComponentType<FallbackProps>;
13
+ LoadingComponent?: react__default.ComponentType;
14
+ } | null | undefined;
15
+ /**
16
+ * Composes the route content with Suspense and Error Boundary
17
+ * Resolves the route on the client-side where component references are available
18
+ *
19
+ * This is marked "use client" so it can access component references safely
20
+ */
21
+ declare function RouteRenderer({ router, path, NotFoundComponent, onNotFound, onError, props, }: {
22
+ router: ReturnType<typeof createRouter>;
23
+ path: string;
24
+ NotFoundComponent?: react__default.ComponentType<{
25
+ message: string;
26
+ }>;
27
+ onNotFound?: () => never;
28
+ onError: (error: Error, info: ErrorInfo) => void;
29
+ props?: any;
30
+ }): react_jsx_runtime.JSX.Element;
31
+ declare function ComposedRoute({ path, PageComponent, ErrorComponent, LoadingComponent, onNotFound, NotFoundComponent, props, onError, }: {
32
+ path: string;
33
+ PageComponent: react__default.ComponentType<any>;
34
+ ErrorComponent?: react__default.ComponentType<FallbackProps>;
35
+ LoadingComponent: react__default.ComponentType;
36
+ onNotFound?: () => never;
37
+ NotFoundComponent?: react__default.ComponentType<{
38
+ message: string;
39
+ }>;
40
+ props?: any;
41
+ onError: (error: Error, info: ErrorInfo) => void;
42
+ }): react_jsx_runtime.JSX.Element | undefined;
43
+
44
+ declare function ErrorBoundary({ children, FallbackComponent, resetKeys, onError, }: {
45
+ children: React.ReactNode;
46
+ FallbackComponent: React.ComponentType<FallbackProps>;
47
+ resetKeys?: Array<string | number | boolean | null | undefined>;
48
+ onError: (error: Error, info: ErrorInfo) => void;
49
+ }): react_jsx_runtime.JSX.Element;
50
+
51
+ export { ComposedRoute, ErrorBoundary, RouteRenderer };
52
+ export type { RouteWithComponents };
@@ -0,0 +1,52 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import react__default, { ErrorInfo } from 'react';
3
+ import { FallbackProps } from 'react-error-boundary';
4
+ export { FallbackProps } from 'react-error-boundary';
5
+ import { createRouter } from '@btst/yar';
6
+
7
+ /**
8
+ * Route type with optional components
9
+ */
10
+ type RouteWithComponents = {
11
+ PageComponent?: react__default.ComponentType;
12
+ ErrorComponent?: react__default.ComponentType<FallbackProps>;
13
+ LoadingComponent?: react__default.ComponentType;
14
+ } | null | undefined;
15
+ /**
16
+ * Composes the route content with Suspense and Error Boundary
17
+ * Resolves the route on the client-side where component references are available
18
+ *
19
+ * This is marked "use client" so it can access component references safely
20
+ */
21
+ declare function RouteRenderer({ router, path, NotFoundComponent, onNotFound, onError, props, }: {
22
+ router: ReturnType<typeof createRouter>;
23
+ path: string;
24
+ NotFoundComponent?: react__default.ComponentType<{
25
+ message: string;
26
+ }>;
27
+ onNotFound?: () => never;
28
+ onError: (error: Error, info: ErrorInfo) => void;
29
+ props?: any;
30
+ }): react_jsx_runtime.JSX.Element;
31
+ declare function ComposedRoute({ path, PageComponent, ErrorComponent, LoadingComponent, onNotFound, NotFoundComponent, props, onError, }: {
32
+ path: string;
33
+ PageComponent: react__default.ComponentType<any>;
34
+ ErrorComponent?: react__default.ComponentType<FallbackProps>;
35
+ LoadingComponent: react__default.ComponentType;
36
+ onNotFound?: () => never;
37
+ NotFoundComponent?: react__default.ComponentType<{
38
+ message: string;
39
+ }>;
40
+ props?: any;
41
+ onError: (error: Error, info: ErrorInfo) => void;
42
+ }): react_jsx_runtime.JSX.Element | undefined;
43
+
44
+ declare function ErrorBoundary({ children, FallbackComponent, resetKeys, onError, }: {
45
+ children: React.ReactNode;
46
+ FallbackComponent: React.ComponentType<FallbackProps>;
47
+ resetKeys?: Array<string | number | boolean | null | undefined>;
48
+ onError: (error: Error, info: ErrorInfo) => void;
49
+ }): react_jsx_runtime.JSX.Element;
50
+
51
+ export { ComposedRoute, ErrorBoundary, RouteRenderer };
52
+ export type { RouteWithComponents };
@@ -0,0 +1,52 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import react__default, { ErrorInfo } from 'react';
3
+ import { FallbackProps } from 'react-error-boundary';
4
+ export { FallbackProps } from 'react-error-boundary';
5
+ import { createRouter } from '@btst/yar';
6
+
7
+ /**
8
+ * Route type with optional components
9
+ */
10
+ type RouteWithComponents = {
11
+ PageComponent?: react__default.ComponentType;
12
+ ErrorComponent?: react__default.ComponentType<FallbackProps>;
13
+ LoadingComponent?: react__default.ComponentType;
14
+ } | null | undefined;
15
+ /**
16
+ * Composes the route content with Suspense and Error Boundary
17
+ * Resolves the route on the client-side where component references are available
18
+ *
19
+ * This is marked "use client" so it can access component references safely
20
+ */
21
+ declare function RouteRenderer({ router, path, NotFoundComponent, onNotFound, onError, props, }: {
22
+ router: ReturnType<typeof createRouter>;
23
+ path: string;
24
+ NotFoundComponent?: react__default.ComponentType<{
25
+ message: string;
26
+ }>;
27
+ onNotFound?: () => never;
28
+ onError: (error: Error, info: ErrorInfo) => void;
29
+ props?: any;
30
+ }): react_jsx_runtime.JSX.Element;
31
+ declare function ComposedRoute({ path, PageComponent, ErrorComponent, LoadingComponent, onNotFound, NotFoundComponent, props, onError, }: {
32
+ path: string;
33
+ PageComponent: react__default.ComponentType<any>;
34
+ ErrorComponent?: react__default.ComponentType<FallbackProps>;
35
+ LoadingComponent: react__default.ComponentType;
36
+ onNotFound?: () => never;
37
+ NotFoundComponent?: react__default.ComponentType<{
38
+ message: string;
39
+ }>;
40
+ props?: any;
41
+ onError: (error: Error, info: ErrorInfo) => void;
42
+ }): react_jsx_runtime.JSX.Element | undefined;
43
+
44
+ declare function ErrorBoundary({ children, FallbackComponent, resetKeys, onError, }: {
45
+ children: React.ReactNode;
46
+ FallbackComponent: React.ComponentType<FallbackProps>;
47
+ resetKeys?: Array<string | number | boolean | null | undefined>;
48
+ onError: (error: Error, info: ErrorInfo) => void;
49
+ }): react_jsx_runtime.JSX.Element;
50
+
51
+ export { ComposedRoute, ErrorBoundary, RouteRenderer };
52
+ export type { RouteWithComponents };
@@ -0,0 +1,2 @@
1
+ export { ComposedRoute, RouteRenderer } from './compose.mjs';
2
+ export { ErrorBoundary } from './error-boundary.mjs';