@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.
- package/README.md +237 -2
- package/dist/api/index.cjs +2 -2
- package/dist/api/index.d.cts +2 -2
- package/dist/api/index.d.mts +2 -2
- package/dist/api/index.d.ts +2 -2
- package/dist/api/index.mjs +2 -2
- package/dist/client/components/compose.cjs +68 -0
- package/dist/client/components/compose.mjs +65 -0
- package/dist/client/components/error-boundary.cjs +24 -0
- package/dist/client/components/error-boundary.mjs +22 -0
- package/dist/client/components/index.cjs +10 -0
- package/dist/client/components/index.d.cts +52 -0
- package/dist/client/components/index.d.mts +52 -0
- package/dist/client/components/index.d.ts +52 -0
- package/dist/client/components/index.mjs +2 -0
- package/dist/client/index.cjs +21 -16
- package/dist/client/index.d.cts +102 -14
- package/dist/client/index.d.mts +102 -14
- package/dist/client/index.d.ts +102 -14
- package/dist/client/index.mjs +19 -10
- package/dist/client/meta-utils.cjs +162 -0
- package/dist/client/meta-utils.mjs +160 -0
- package/dist/client/sitemap-utils.cjs +14 -0
- package/dist/client/sitemap-utils.mjs +12 -0
- package/dist/context/index.cjs +6 -51
- package/dist/context/index.d.cts +26 -26
- package/dist/context/index.d.mts +26 -26
- package/dist/context/index.d.ts +26 -26
- package/dist/context/index.mjs +1 -50
- package/dist/context/provider.cjs +51 -0
- package/dist/context/provider.mjs +46 -0
- package/dist/index.cjs +0 -2
- package/dist/index.d.cts +1 -1
- package/dist/index.d.mts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.mjs +0 -2
- package/dist/plugins/api/index.cjs +15 -0
- package/dist/plugins/api/index.d.cts +41 -0
- package/dist/plugins/api/index.d.mts +41 -0
- package/dist/plugins/api/index.d.ts +41 -0
- package/dist/plugins/api/index.mjs +9 -0
- package/dist/plugins/blog/api/index.cjs +11 -0
- package/dist/plugins/blog/api/index.d.cts +7 -0
- package/dist/plugins/blog/api/index.d.mts +7 -0
- package/dist/plugins/blog/api/index.d.ts +7 -0
- package/dist/plugins/blog/api/index.mjs +2 -0
- package/dist/plugins/blog/api/plugin.cjs +569 -0
- package/dist/plugins/blog/api/plugin.mjs +565 -0
- package/dist/plugins/blog/client/components/forms/image-field.cjs +133 -0
- package/dist/plugins/blog/client/components/forms/image-field.mjs +131 -0
- package/dist/plugins/blog/client/components/forms/markdown-editor-styles.css +30 -0
- package/dist/plugins/blog/client/components/forms/markdown-editor.cjs +106 -0
- package/dist/plugins/blog/client/components/forms/markdown-editor.mjs +104 -0
- package/dist/plugins/blog/client/components/forms/post-forms.cjs +401 -0
- package/dist/plugins/blog/client/components/forms/post-forms.mjs +398 -0
- package/dist/plugins/blog/client/components/forms/tags-multiselect.cjs +71 -0
- package/dist/plugins/blog/client/components/forms/tags-multiselect.mjs +65 -0
- package/dist/plugins/blog/client/components/index.cjs +17 -0
- package/dist/plugins/blog/client/components/index.d.cts +22 -0
- package/dist/plugins/blog/client/components/index.d.mts +22 -0
- package/dist/plugins/blog/client/components/index.d.ts +22 -0
- package/dist/plugins/blog/client/components/index.mjs +12 -0
- package/dist/plugins/blog/client/components/loading/form-page-skeleton.cjs +62 -0
- package/dist/plugins/blog/client/components/loading/form-page-skeleton.mjs +60 -0
- package/dist/plugins/blog/client/components/loading/index.cjs +20 -0
- package/dist/plugins/blog/client/components/loading/index.mjs +16 -0
- package/dist/plugins/blog/client/components/loading/list-page-skeleton.cjs +26 -0
- package/dist/plugins/blog/client/components/loading/list-page-skeleton.mjs +24 -0
- package/dist/plugins/blog/client/components/loading/page-header-skeleton.cjs +13 -0
- package/dist/plugins/blog/client/components/loading/page-header-skeleton.mjs +11 -0
- package/dist/plugins/blog/client/components/loading/post-card-skeleton.cjs +22 -0
- package/dist/plugins/blog/client/components/loading/post-card-skeleton.mjs +20 -0
- package/dist/plugins/blog/client/components/loading/post-page-skeleton.cjs +56 -0
- package/dist/plugins/blog/client/components/loading/post-page-skeleton.mjs +54 -0
- package/dist/plugins/blog/client/components/pages/404-page.cjs +19 -0
- package/dist/plugins/blog/client/components/pages/404-page.mjs +17 -0
- package/dist/plugins/blog/client/components/pages/edit-post-page.cjs +41 -0
- package/dist/plugins/blog/client/components/pages/edit-post-page.internal.cjs +57 -0
- package/dist/plugins/blog/client/components/pages/edit-post-page.internal.mjs +55 -0
- package/dist/plugins/blog/client/components/pages/edit-post-page.mjs +39 -0
- package/dist/plugins/blog/client/components/pages/home-page.cjs +41 -0
- package/dist/plugins/blog/client/components/pages/home-page.internal.cjs +61 -0
- package/dist/plugins/blog/client/components/pages/home-page.internal.mjs +59 -0
- package/dist/plugins/blog/client/components/pages/home-page.mjs +39 -0
- package/dist/plugins/blog/client/components/pages/new-post-page.cjs +37 -0
- package/dist/plugins/blog/client/components/pages/new-post-page.internal.cjs +53 -0
- package/dist/plugins/blog/client/components/pages/new-post-page.internal.mjs +51 -0
- package/dist/plugins/blog/client/components/pages/new-post-page.mjs +35 -0
- package/dist/plugins/blog/client/components/pages/post-page.cjs +39 -0
- package/dist/plugins/blog/client/components/pages/post-page.internal.cjs +101 -0
- package/dist/plugins/blog/client/components/pages/post-page.internal.mjs +99 -0
- package/dist/plugins/blog/client/components/pages/post-page.mjs +37 -0
- package/dist/plugins/blog/client/components/pages/tag-page.cjs +39 -0
- package/dist/plugins/blog/client/components/pages/tag-page.internal.cjs +61 -0
- package/dist/plugins/blog/client/components/pages/tag-page.internal.mjs +59 -0
- package/dist/plugins/blog/client/components/pages/tag-page.mjs +37 -0
- package/dist/plugins/blog/client/components/shared/better-blog-attribution.cjs +24 -0
- package/dist/plugins/blog/client/components/shared/better-blog-attribution.mjs +22 -0
- package/dist/plugins/blog/client/components/shared/default-error.cjs +18 -0
- package/dist/plugins/blog/client/components/shared/default-error.mjs +16 -0
- package/dist/plugins/blog/client/components/shared/defaults.cjs +13 -0
- package/dist/plugins/blog/client/components/shared/defaults.mjs +10 -0
- package/dist/plugins/blog/client/components/shared/empty-list.cjs +21 -0
- package/dist/plugins/blog/client/components/shared/empty-list.mjs +19 -0
- package/dist/plugins/blog/client/components/shared/error-placeholder.cjs +24 -0
- package/dist/plugins/blog/client/components/shared/error-placeholder.mjs +22 -0
- package/dist/plugins/blog/client/components/shared/highlight-text.cjs +53 -0
- package/dist/plugins/blog/client/components/shared/highlight-text.mjs +51 -0
- package/dist/plugins/blog/client/components/shared/markdown-content-styles.css +328 -0
- package/dist/plugins/blog/client/components/shared/markdown-content.cjs +324 -0
- package/dist/plugins/blog/client/components/shared/markdown-content.mjs +315 -0
- package/dist/plugins/blog/client/components/shared/on-this-page.cjs +161 -0
- package/dist/plugins/blog/client/components/shared/on-this-page.mjs +158 -0
- package/dist/plugins/blog/client/components/shared/page-header.cjs +40 -0
- package/dist/plugins/blog/client/components/shared/page-header.mjs +38 -0
- package/dist/plugins/blog/client/components/shared/page-layout.cjs +24 -0
- package/dist/plugins/blog/client/components/shared/page-layout.mjs +22 -0
- package/dist/plugins/blog/client/components/shared/page-wrapper.cjs +23 -0
- package/dist/plugins/blog/client/components/shared/page-wrapper.mjs +21 -0
- package/dist/plugins/blog/client/components/shared/post-card.cjs +279 -0
- package/dist/plugins/blog/client/components/shared/post-card.mjs +277 -0
- package/dist/plugins/blog/client/components/shared/post-navigation.cjs +74 -0
- package/dist/plugins/blog/client/components/shared/post-navigation.mjs +72 -0
- package/dist/plugins/blog/client/components/shared/posts-list.cjs +48 -0
- package/dist/plugins/blog/client/components/shared/posts-list.mjs +46 -0
- package/dist/plugins/blog/client/components/shared/recent-posts-carousel.cjs +59 -0
- package/dist/plugins/blog/client/components/shared/recent-posts-carousel.mjs +57 -0
- package/dist/plugins/blog/client/components/shared/search-input.cjs +136 -0
- package/dist/plugins/blog/client/components/shared/search-input.mjs +117 -0
- package/dist/plugins/blog/client/components/shared/search-modal.cjs +135 -0
- package/dist/plugins/blog/client/components/shared/search-modal.mjs +116 -0
- package/dist/plugins/blog/client/components/shared/tags-list.cjs +22 -0
- package/dist/plugins/blog/client/components/shared/tags-list.mjs +20 -0
- package/dist/plugins/blog/client/components/shared/use-route-lifecycle.cjs +50 -0
- package/dist/plugins/blog/client/components/shared/use-route-lifecycle.mjs +48 -0
- package/dist/plugins/blog/client/hooks/blog-hooks.cjs +380 -0
- package/dist/plugins/blog/client/hooks/blog-hooks.mjs +368 -0
- package/dist/plugins/blog/client/hooks/index.cjs +17 -0
- package/dist/plugins/blog/client/hooks/index.d.cts +150 -0
- package/dist/plugins/blog/client/hooks/index.d.mts +150 -0
- package/dist/plugins/blog/client/hooks/index.d.ts +150 -0
- package/dist/plugins/blog/client/hooks/index.mjs +1 -0
- package/dist/plugins/blog/client/hooks/use-debounce.cjs +16 -0
- package/dist/plugins/blog/client/hooks/use-debounce.mjs +14 -0
- package/dist/plugins/blog/client/index.cjs +7 -0
- package/dist/plugins/blog/client/index.d.cts +414 -0
- package/dist/plugins/blog/client/index.d.mts +414 -0
- package/dist/plugins/blog/client/index.d.ts +414 -0
- package/dist/plugins/blog/client/index.mjs +1 -0
- package/dist/plugins/blog/client/localization/blog-card.cjs +7 -0
- package/dist/plugins/blog/client/localization/blog-card.mjs +5 -0
- package/dist/plugins/blog/client/localization/blog-common.cjs +10 -0
- package/dist/plugins/blog/client/localization/blog-common.mjs +8 -0
- package/dist/plugins/blog/client/localization/blog-forms.cjs +40 -0
- package/dist/plugins/blog/client/localization/blog-forms.mjs +38 -0
- package/dist/plugins/blog/client/localization/blog-list.cjs +18 -0
- package/dist/plugins/blog/client/localization/blog-list.mjs +16 -0
- package/dist/plugins/blog/client/localization/blog-post.cjs +13 -0
- package/dist/plugins/blog/client/localization/blog-post.mjs +11 -0
- package/dist/plugins/blog/client/localization/index.cjs +17 -0
- package/dist/plugins/blog/client/localization/index.mjs +15 -0
- package/dist/plugins/blog/client/plugin.cjs +462 -0
- package/dist/plugins/blog/client/plugin.mjs +460 -0
- package/dist/plugins/blog/client.css +3 -0
- package/dist/plugins/blog/db.cjs +90 -0
- package/dist/plugins/blog/db.mjs +88 -0
- package/dist/plugins/blog/query-keys.cjs +181 -0
- package/dist/plugins/blog/query-keys.d.cts +530 -0
- package/dist/plugins/blog/query-keys.d.mts +530 -0
- package/dist/plugins/blog/query-keys.d.ts +530 -0
- package/dist/plugins/blog/query-keys.mjs +179 -0
- package/dist/plugins/blog/schemas.cjs +39 -0
- package/dist/plugins/blog/schemas.mjs +35 -0
- package/dist/plugins/blog/style.css +22 -0
- package/dist/plugins/blog/utils.cjs +97 -0
- package/dist/plugins/blog/utils.mjs +87 -0
- package/dist/plugins/client/index.cjs +15 -0
- package/dist/plugins/client/index.d.cts +57 -0
- package/dist/plugins/client/index.d.mts +57 -0
- package/dist/plugins/client/index.d.ts +57 -0
- package/dist/plugins/client/index.mjs +9 -0
- package/dist/{shared/stack.Br2KMECJ.cjs → plugins/utils.cjs} +1 -8
- package/dist/{shared/stack.CwGEQ10b.mjs → plugins/utils.mjs} +2 -8
- package/dist/shared/{stack.Dva9muUy.d.cts → stack.ByOugz9d.d.cts} +17 -23
- package/dist/shared/{stack.Dva9muUy.d.mts → stack.ByOugz9d.d.mts} +17 -23
- package/dist/shared/{stack.Dva9muUy.d.ts → stack.ByOugz9d.d.ts} +17 -23
- package/dist/shared/stack.Cr2JoQdo.d.cts +76 -0
- package/dist/shared/stack.Cr2JoQdo.d.mts +76 -0
- package/dist/shared/stack.Cr2JoQdo.d.ts +76 -0
- package/package.json +104 -16
- package/src/__tests__/plugins.test.tsx +539 -0
- package/src/__tests__/sitemap.test.ts +60 -0
- package/src/api/index.ts +73 -0
- package/src/client/components/compose.tsx +116 -0
- package/src/client/components/error-boundary.tsx +30 -0
- package/src/client/components/index.tsx +2 -0
- package/src/client/index.ts +107 -0
- package/src/client/meta-utils.ts +228 -0
- package/src/client/sitemap-utils.ts +46 -0
- package/src/context/index.ts +1 -0
- package/src/context/provider.tsx +157 -0
- package/src/index.ts +1 -0
- package/src/plugins/api/index.ts +51 -0
- package/src/plugins/blog/api/index.ts +2 -0
- package/src/plugins/blog/api/plugin.ts +759 -0
- package/src/plugins/blog/client/components/forms/image-field.tsx +165 -0
- package/src/plugins/blog/client/components/forms/markdown-editor-styles.css +30 -0
- package/src/plugins/blog/client/components/forms/markdown-editor.tsx +136 -0
- package/src/plugins/blog/client/components/forms/post-forms.tsx +531 -0
- package/src/plugins/blog/client/components/forms/tags-multiselect.tsx +79 -0
- package/src/plugins/blog/client/components/index.tsx +11 -0
- package/src/plugins/blog/client/components/loading/form-page-skeleton.tsx +75 -0
- package/src/plugins/blog/client/components/loading/index.tsx +27 -0
- package/src/plugins/blog/client/components/loading/list-page-skeleton.tsx +38 -0
- package/src/plugins/blog/client/components/loading/page-header-skeleton.tsx +10 -0
- package/src/plugins/blog/client/components/loading/post-card-skeleton.tsx +30 -0
- package/src/plugins/blog/client/components/loading/post-page-skeleton.tsx +75 -0
- package/src/plugins/blog/client/components/pages/404-page.tsx +23 -0
- package/src/plugins/blog/client/components/pages/edit-post-page.internal.tsx +60 -0
- package/src/plugins/blog/client/components/pages/edit-post-page.tsx +40 -0
- package/src/plugins/blog/client/components/pages/home-page.internal.tsx +71 -0
- package/src/plugins/blog/client/components/pages/home-page.tsx +42 -0
- package/src/plugins/blog/client/components/pages/new-post-page.internal.tsx +59 -0
- package/src/plugins/blog/client/components/pages/new-post-page.tsx +36 -0
- package/src/plugins/blog/client/components/pages/post-page.internal.tsx +142 -0
- package/src/plugins/blog/client/components/pages/post-page.tsx +38 -0
- package/src/plugins/blog/client/components/pages/tag-page.internal.tsx +74 -0
- package/src/plugins/blog/client/components/pages/tag-page.tsx +38 -0
- package/src/plugins/blog/client/components/shared/better-blog-attribution.tsx +19 -0
- package/src/plugins/blog/client/components/shared/default-error.tsx +20 -0
- package/src/plugins/blog/client/components/shared/defaults.tsx +9 -0
- package/src/plugins/blog/client/components/shared/empty-list.tsx +25 -0
- package/src/plugins/blog/client/components/shared/error-placeholder.tsx +20 -0
- package/src/plugins/blog/client/components/shared/highlight-text.tsx +80 -0
- package/src/plugins/blog/client/components/shared/markdown-content-styles.css +328 -0
- package/src/plugins/blog/client/components/shared/markdown-content.tsx +448 -0
- package/src/plugins/blog/client/components/shared/on-this-page.tsx +234 -0
- package/src/plugins/blog/client/components/shared/page-header.tsx +35 -0
- package/src/plugins/blog/client/components/shared/page-layout.tsx +23 -0
- package/src/plugins/blog/client/components/shared/page-wrapper.tsx +32 -0
- package/src/plugins/blog/client/components/shared/post-card.tsx +308 -0
- package/src/plugins/blog/client/components/shared/post-navigation.tsx +98 -0
- package/src/plugins/blog/client/components/shared/posts-list.tsx +67 -0
- package/src/plugins/blog/client/components/shared/recent-posts-carousel.tsx +79 -0
- package/src/plugins/blog/client/components/shared/search-input.tsx +146 -0
- package/src/plugins/blog/client/components/shared/search-modal.tsx +162 -0
- package/src/plugins/blog/client/components/shared/tags-list.tsx +34 -0
- package/src/plugins/blog/client/components/shared/use-route-lifecycle.tsx +68 -0
- package/src/plugins/blog/client/hooks/blog-hooks.tsx +623 -0
- package/src/plugins/blog/client/hooks/index.tsx +1 -0
- package/src/plugins/blog/client/hooks/use-debounce.ts +43 -0
- package/src/plugins/blog/client/index.ts +9 -0
- package/src/plugins/blog/client/localization/blog-card.ts +3 -0
- package/src/plugins/blog/client/localization/blog-common.ts +7 -0
- package/src/plugins/blog/client/localization/blog-forms.ts +45 -0
- package/src/plugins/blog/client/localization/blog-list.ts +14 -0
- package/src/plugins/blog/client/localization/blog-post.ts +9 -0
- package/src/plugins/blog/client/localization/index.ts +15 -0
- package/src/plugins/blog/client/overrides.ts +123 -0
- package/src/plugins/blog/client/plugin.tsx +672 -0
- package/src/plugins/blog/client.css +3 -0
- package/src/plugins/blog/db.ts +90 -0
- package/src/plugins/blog/query-keys.ts +267 -0
- package/src/plugins/blog/schemas.ts +39 -0
- package/src/plugins/blog/style.css +22 -0
- package/src/plugins/blog/types.ts +37 -0
- package/src/plugins/blog/utils.ts +144 -0
- package/src/plugins/client/index.ts +53 -0
- package/src/plugins/index.ts +0 -0
- package/src/plugins/utils.ts +35 -0
- package/src/types.ts +209 -0
- package/dist/plugins/index.cjs +0 -16
- package/dist/plugins/index.d.cts +0 -64
- package/dist/plugins/index.d.mts +0 -64
- package/dist/plugins/index.d.ts +0 -64
- package/dist/plugins/index.mjs +0 -11
- package/dist/shared/stack.DvFqFlOV.d.cts +0 -22
- package/dist/shared/stack.DvFqFlOV.d.mts +0 -22
- package/dist/shared/stack.DvFqFlOV.d.ts +0 -22
package/README.md
CHANGED
|
@@ -1,3 +1,238 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @BTST - Better Stack
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
<div align="center">
|
|
4
|
+
|
|
5
|
+
**Add complete full-stack features to your React app in minutes, not weeks**
|
|
6
|
+
|
|
7
|
+
[](https://www.npmjs.com/package/@btst/stack)
|
|
8
|
+
[](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)
|
package/dist/api/index.cjs
CHANGED
|
@@ -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
|
|
21
|
+
basePath
|
|
22
22
|
});
|
|
23
23
|
return {
|
|
24
24
|
handler: router.handler,
|
package/dist/api/index.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export { B as BackendPlugin } from '../shared/stack.
|
|
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';
|
package/dist/api/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export { B as BackendPlugin } from '../shared/stack.
|
|
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';
|
package/dist/api/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export { B as BackendPlugin } from '../shared/stack.
|
|
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';
|
package/dist/api/index.mjs
CHANGED
|
@@ -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
|
|
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 };
|