@btst/stack 1.1.10 → 1.1.11
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 +3 -168
- package/dist/client/components/index.d.cts +22 -0
- package/dist/client/components/index.d.mts +22 -0
- package/dist/client/components/index.d.ts +22 -0
- package/dist/context/index.d.cts +21 -1
- package/dist/context/index.d.mts +21 -1
- package/dist/context/index.d.ts +21 -1
- package/dist/packages/better-stack/src/plugins/blog/client/components/shared/on-this-page.cjs +7 -2
- package/dist/packages/better-stack/src/plugins/blog/client/components/shared/on-this-page.mjs +7 -2
- package/dist/plugins/blog/api/index.d.cts +1 -1
- package/dist/plugins/blog/api/index.d.mts +1 -1
- package/dist/plugins/blog/api/index.d.ts +1 -1
- package/dist/plugins/blog/client/hooks/index.d.cts +74 -2
- package/dist/plugins/blog/client/hooks/index.d.mts +74 -2
- package/dist/plugins/blog/client/hooks/index.d.ts +74 -2
- package/dist/plugins/blog/client/index.d.cts +70 -1
- package/dist/plugins/blog/client/index.d.mts +70 -1
- package/dist/plugins/blog/client/index.d.ts +70 -1
- package/dist/plugins/blog/query-keys.d.cts +64 -2
- package/dist/plugins/blog/query-keys.d.mts +64 -2
- package/dist/plugins/blog/query-keys.d.ts +64 -2
- package/package.json +1 -1
- package/src/client/components/compose.tsx +13 -0
- package/src/client/components/error-boundary.tsx +9 -0
- package/src/context/provider.tsx +21 -1
- package/src/plugins/blog/api/plugin.ts +62 -3
- package/src/plugins/blog/client/components/shared/on-this-page.tsx +4 -2
- package/src/plugins/blog/client/hooks/blog-hooks.tsx +72 -0
- package/src/plugins/blog/client/overrides.ts +10 -0
- package/src/plugins/blog/client/plugin.tsx +59 -5
- package/dist/shared/{stack.Cr2JoQdo.d.cts → stack.CoPoHVfV.d.cts} +1 -1
- package/dist/shared/{stack.Cr2JoQdo.d.mts → stack.CoPoHVfV.d.mts} +1 -1
- package/dist/shared/{stack.Cr2JoQdo.d.ts → stack.CoPoHVfV.d.ts} +1 -1
package/README.md
CHANGED
|
@@ -30,21 +30,6 @@ Better Stack lets you **add these features in minutes** as composable plugins th
|
|
|
30
30
|
- **Lifecycle hooks** - Intercept at any point: authorization, data transformation, analytics, caching, webhooks
|
|
31
31
|
- **Horizontal features** - Perfect for blog, scheduling, feedback, newsletters, AI assistants, comments—anything reusable across apps
|
|
32
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
33
|
|
|
49
34
|
## Installation
|
|
50
35
|
|
|
@@ -60,165 +45,15 @@ npm install -D @btst/cli
|
|
|
60
45
|
|
|
61
46
|
The CLI helps generate migrations, Prisma schemas, and other database artifacts from your plugin schemas.
|
|
62
47
|
|
|
63
|
-
|
|
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.
|
|
48
|
+
Learn more about Better Stack, full installation, usage instructions and available plugins in the [documentation](https://www.better-stack.ai/docs).
|
|
213
49
|
|
|
214
50
|
## The Bigger Picture
|
|
215
51
|
|
|
216
52
|
Better Stack transforms how you think about building apps:
|
|
217
53
|
|
|
218
54
|
- **Open source** - Share complete features, not just code snippets. Someone can add a newsletter plugin to their Next.js app in minutes
|
|
219
|
-
- **Fast development** - Add 5 features in an afternoon instead of 5
|
|
220
|
-
- **
|
|
221
|
-
- **SaaS platforms** - Offer feature plugins your customers can compose. They pick blog + scheduling + AI assistant, mix and match to build their ideal app
|
|
55
|
+
- **Fast development** - Add 5 features in an afternoon instead of 5 months. Validate ideas faster
|
|
56
|
+
- **Framework and Database Agnostic** - Use any framework and database you want. Better Stack works with any modern framework and database.
|
|
222
57
|
|
|
223
58
|
|
|
224
59
|
Each plugin is a complete, self-contained horizontal full-stack feature. No framework lock-in. Just add it and it works.
|
|
@@ -28,6 +28,19 @@ declare function RouteRenderer({ router, path, NotFoundComponent, onNotFound, on
|
|
|
28
28
|
onError: (error: Error, info: ErrorInfo) => void;
|
|
29
29
|
props?: any;
|
|
30
30
|
}): react_jsx_runtime.JSX.Element;
|
|
31
|
+
/**
|
|
32
|
+
* Renders a route with Suspense and ErrorBoundary wrappers.
|
|
33
|
+
* Handles loading states, error boundaries, and not-found scenarios for a single route.
|
|
34
|
+
*
|
|
35
|
+
* @param path - The current route path
|
|
36
|
+
* @param PageComponent - The page component to render
|
|
37
|
+
* @param ErrorComponent - Optional error fallback component
|
|
38
|
+
* @param LoadingComponent - Component to show during suspense
|
|
39
|
+
* @param onNotFound - Optional callback when route is not found
|
|
40
|
+
* @param NotFoundComponent - Optional component to show for 404s
|
|
41
|
+
* @param props - Additional props to pass to the page component
|
|
42
|
+
* @param onError - Error handler callback for the error boundary
|
|
43
|
+
*/
|
|
31
44
|
declare function ComposedRoute({ path, PageComponent, ErrorComponent, LoadingComponent, onNotFound, NotFoundComponent, props, onError, }: {
|
|
32
45
|
path: string;
|
|
33
46
|
PageComponent: react__default.ComponentType<any>;
|
|
@@ -41,6 +54,15 @@ declare function ComposedRoute({ path, PageComponent, ErrorComponent, LoadingCom
|
|
|
41
54
|
onError: (error: Error, info: ErrorInfo) => void;
|
|
42
55
|
}): react_jsx_runtime.JSX.Element | undefined;
|
|
43
56
|
|
|
57
|
+
/**
|
|
58
|
+
* Error boundary wrapper component for catching and handling React errors.
|
|
59
|
+
* Wraps react-error-boundary with a simplified API.
|
|
60
|
+
*
|
|
61
|
+
* @param children - Child components to wrap with error boundary
|
|
62
|
+
* @param FallbackComponent - Component to render when an error occurs
|
|
63
|
+
* @param resetKeys - Array of values that will reset the error boundary when changed
|
|
64
|
+
* @param onError - Callback invoked when an error is caught
|
|
65
|
+
*/
|
|
44
66
|
declare function ErrorBoundary({ children, FallbackComponent, resetKeys, onError, }: {
|
|
45
67
|
children: React.ReactNode;
|
|
46
68
|
FallbackComponent: React.ComponentType<FallbackProps>;
|
|
@@ -28,6 +28,19 @@ declare function RouteRenderer({ router, path, NotFoundComponent, onNotFound, on
|
|
|
28
28
|
onError: (error: Error, info: ErrorInfo) => void;
|
|
29
29
|
props?: any;
|
|
30
30
|
}): react_jsx_runtime.JSX.Element;
|
|
31
|
+
/**
|
|
32
|
+
* Renders a route with Suspense and ErrorBoundary wrappers.
|
|
33
|
+
* Handles loading states, error boundaries, and not-found scenarios for a single route.
|
|
34
|
+
*
|
|
35
|
+
* @param path - The current route path
|
|
36
|
+
* @param PageComponent - The page component to render
|
|
37
|
+
* @param ErrorComponent - Optional error fallback component
|
|
38
|
+
* @param LoadingComponent - Component to show during suspense
|
|
39
|
+
* @param onNotFound - Optional callback when route is not found
|
|
40
|
+
* @param NotFoundComponent - Optional component to show for 404s
|
|
41
|
+
* @param props - Additional props to pass to the page component
|
|
42
|
+
* @param onError - Error handler callback for the error boundary
|
|
43
|
+
*/
|
|
31
44
|
declare function ComposedRoute({ path, PageComponent, ErrorComponent, LoadingComponent, onNotFound, NotFoundComponent, props, onError, }: {
|
|
32
45
|
path: string;
|
|
33
46
|
PageComponent: react__default.ComponentType<any>;
|
|
@@ -41,6 +54,15 @@ declare function ComposedRoute({ path, PageComponent, ErrorComponent, LoadingCom
|
|
|
41
54
|
onError: (error: Error, info: ErrorInfo) => void;
|
|
42
55
|
}): react_jsx_runtime.JSX.Element | undefined;
|
|
43
56
|
|
|
57
|
+
/**
|
|
58
|
+
* Error boundary wrapper component for catching and handling React errors.
|
|
59
|
+
* Wraps react-error-boundary with a simplified API.
|
|
60
|
+
*
|
|
61
|
+
* @param children - Child components to wrap with error boundary
|
|
62
|
+
* @param FallbackComponent - Component to render when an error occurs
|
|
63
|
+
* @param resetKeys - Array of values that will reset the error boundary when changed
|
|
64
|
+
* @param onError - Callback invoked when an error is caught
|
|
65
|
+
*/
|
|
44
66
|
declare function ErrorBoundary({ children, FallbackComponent, resetKeys, onError, }: {
|
|
45
67
|
children: React.ReactNode;
|
|
46
68
|
FallbackComponent: React.ComponentType<FallbackProps>;
|
|
@@ -28,6 +28,19 @@ declare function RouteRenderer({ router, path, NotFoundComponent, onNotFound, on
|
|
|
28
28
|
onError: (error: Error, info: ErrorInfo) => void;
|
|
29
29
|
props?: any;
|
|
30
30
|
}): react_jsx_runtime.JSX.Element;
|
|
31
|
+
/**
|
|
32
|
+
* Renders a route with Suspense and ErrorBoundary wrappers.
|
|
33
|
+
* Handles loading states, error boundaries, and not-found scenarios for a single route.
|
|
34
|
+
*
|
|
35
|
+
* @param path - The current route path
|
|
36
|
+
* @param PageComponent - The page component to render
|
|
37
|
+
* @param ErrorComponent - Optional error fallback component
|
|
38
|
+
* @param LoadingComponent - Component to show during suspense
|
|
39
|
+
* @param onNotFound - Optional callback when route is not found
|
|
40
|
+
* @param NotFoundComponent - Optional component to show for 404s
|
|
41
|
+
* @param props - Additional props to pass to the page component
|
|
42
|
+
* @param onError - Error handler callback for the error boundary
|
|
43
|
+
*/
|
|
31
44
|
declare function ComposedRoute({ path, PageComponent, ErrorComponent, LoadingComponent, onNotFound, NotFoundComponent, props, onError, }: {
|
|
32
45
|
path: string;
|
|
33
46
|
PageComponent: react__default.ComponentType<any>;
|
|
@@ -41,6 +54,15 @@ declare function ComposedRoute({ path, PageComponent, ErrorComponent, LoadingCom
|
|
|
41
54
|
onError: (error: Error, info: ErrorInfo) => void;
|
|
42
55
|
}): react_jsx_runtime.JSX.Element | undefined;
|
|
43
56
|
|
|
57
|
+
/**
|
|
58
|
+
* Error boundary wrapper component for catching and handling React errors.
|
|
59
|
+
* Wraps react-error-boundary with a simplified API.
|
|
60
|
+
*
|
|
61
|
+
* @param children - Child components to wrap with error boundary
|
|
62
|
+
* @param FallbackComponent - Component to render when an error occurs
|
|
63
|
+
* @param resetKeys - Array of values that will reset the error boundary when changed
|
|
64
|
+
* @param onError - Callback invoked when an error is caught
|
|
65
|
+
*/
|
|
44
66
|
declare function ErrorBoundary({ children, FallbackComponent, resetKeys, onError, }: {
|
|
45
67
|
children: React.ReactNode;
|
|
46
68
|
FallbackComponent: React.ComponentType<FallbackProps>;
|
package/dist/context/index.d.cts
CHANGED
|
@@ -50,8 +50,16 @@ declare function BetterStackProvider<TPluginOverrides extends Record<string, any
|
|
|
50
50
|
basePath: string;
|
|
51
51
|
}): react_jsx_runtime.JSX.Element;
|
|
52
52
|
/**
|
|
53
|
-
* Hook to access the entire context
|
|
53
|
+
* Hook to access the entire BetterStack context
|
|
54
54
|
* Useful if you need access to multiple plugins or the full context
|
|
55
|
+
*
|
|
56
|
+
* @returns The full context value including overrides and basePath
|
|
57
|
+
* @throws Error if used outside of BetterStackProvider
|
|
58
|
+
*
|
|
59
|
+
* @example
|
|
60
|
+
* ```tsx
|
|
61
|
+
* const { overrides, basePath } = useBetterStack<MyPluginOverrides>();
|
|
62
|
+
* ```
|
|
55
63
|
*/
|
|
56
64
|
declare function useBetterStack<TPluginOverrides extends Record<string, any> = Record<string, any>>(): BetterStackContextValue<TPluginOverrides>;
|
|
57
65
|
type OverridesResult<TOverrides, TDefaults> = undefined extends TDefaults ? TOverrides : TOverrides & Required<Pick<TDefaults & {}, keyof TDefaults>>;
|
|
@@ -81,6 +89,18 @@ type OverridesResult<TOverrides, TDefaults> = undefined extends TDefaults ? TOve
|
|
|
81
89
|
* ```
|
|
82
90
|
*/
|
|
83
91
|
declare function usePluginOverrides<TOverrides = any, TDefaults extends Partial<TOverrides> | undefined = undefined>(pluginName: string, defaultValues?: TDefaults): OverridesResult<TOverrides, TDefaults>;
|
|
92
|
+
/**
|
|
93
|
+
* Hook to access the base path where the client router is mounted
|
|
94
|
+
*
|
|
95
|
+
* @returns The base path string (e.g., "/pages")
|
|
96
|
+
* @throws Error if used outside of BetterStackProvider
|
|
97
|
+
*
|
|
98
|
+
* @example
|
|
99
|
+
* ```tsx
|
|
100
|
+
* const basePath = useBasePath();
|
|
101
|
+
* // basePath = "/pages"
|
|
102
|
+
* ```
|
|
103
|
+
*/
|
|
84
104
|
declare function useBasePath(): string;
|
|
85
105
|
|
|
86
106
|
export { BetterStackProvider, useBasePath, useBetterStack, usePluginOverrides };
|
package/dist/context/index.d.mts
CHANGED
|
@@ -50,8 +50,16 @@ declare function BetterStackProvider<TPluginOverrides extends Record<string, any
|
|
|
50
50
|
basePath: string;
|
|
51
51
|
}): react_jsx_runtime.JSX.Element;
|
|
52
52
|
/**
|
|
53
|
-
* Hook to access the entire context
|
|
53
|
+
* Hook to access the entire BetterStack context
|
|
54
54
|
* Useful if you need access to multiple plugins or the full context
|
|
55
|
+
*
|
|
56
|
+
* @returns The full context value including overrides and basePath
|
|
57
|
+
* @throws Error if used outside of BetterStackProvider
|
|
58
|
+
*
|
|
59
|
+
* @example
|
|
60
|
+
* ```tsx
|
|
61
|
+
* const { overrides, basePath } = useBetterStack<MyPluginOverrides>();
|
|
62
|
+
* ```
|
|
55
63
|
*/
|
|
56
64
|
declare function useBetterStack<TPluginOverrides extends Record<string, any> = Record<string, any>>(): BetterStackContextValue<TPluginOverrides>;
|
|
57
65
|
type OverridesResult<TOverrides, TDefaults> = undefined extends TDefaults ? TOverrides : TOverrides & Required<Pick<TDefaults & {}, keyof TDefaults>>;
|
|
@@ -81,6 +89,18 @@ type OverridesResult<TOverrides, TDefaults> = undefined extends TDefaults ? TOve
|
|
|
81
89
|
* ```
|
|
82
90
|
*/
|
|
83
91
|
declare function usePluginOverrides<TOverrides = any, TDefaults extends Partial<TOverrides> | undefined = undefined>(pluginName: string, defaultValues?: TDefaults): OverridesResult<TOverrides, TDefaults>;
|
|
92
|
+
/**
|
|
93
|
+
* Hook to access the base path where the client router is mounted
|
|
94
|
+
*
|
|
95
|
+
* @returns The base path string (e.g., "/pages")
|
|
96
|
+
* @throws Error if used outside of BetterStackProvider
|
|
97
|
+
*
|
|
98
|
+
* @example
|
|
99
|
+
* ```tsx
|
|
100
|
+
* const basePath = useBasePath();
|
|
101
|
+
* // basePath = "/pages"
|
|
102
|
+
* ```
|
|
103
|
+
*/
|
|
84
104
|
declare function useBasePath(): string;
|
|
85
105
|
|
|
86
106
|
export { BetterStackProvider, useBasePath, useBetterStack, usePluginOverrides };
|
package/dist/context/index.d.ts
CHANGED
|
@@ -50,8 +50,16 @@ declare function BetterStackProvider<TPluginOverrides extends Record<string, any
|
|
|
50
50
|
basePath: string;
|
|
51
51
|
}): react_jsx_runtime.JSX.Element;
|
|
52
52
|
/**
|
|
53
|
-
* Hook to access the entire context
|
|
53
|
+
* Hook to access the entire BetterStack context
|
|
54
54
|
* Useful if you need access to multiple plugins or the full context
|
|
55
|
+
*
|
|
56
|
+
* @returns The full context value including overrides and basePath
|
|
57
|
+
* @throws Error if used outside of BetterStackProvider
|
|
58
|
+
*
|
|
59
|
+
* @example
|
|
60
|
+
* ```tsx
|
|
61
|
+
* const { overrides, basePath } = useBetterStack<MyPluginOverrides>();
|
|
62
|
+
* ```
|
|
55
63
|
*/
|
|
56
64
|
declare function useBetterStack<TPluginOverrides extends Record<string, any> = Record<string, any>>(): BetterStackContextValue<TPluginOverrides>;
|
|
57
65
|
type OverridesResult<TOverrides, TDefaults> = undefined extends TDefaults ? TOverrides : TOverrides & Required<Pick<TDefaults & {}, keyof TDefaults>>;
|
|
@@ -81,6 +89,18 @@ type OverridesResult<TOverrides, TDefaults> = undefined extends TDefaults ? TOve
|
|
|
81
89
|
* ```
|
|
82
90
|
*/
|
|
83
91
|
declare function usePluginOverrides<TOverrides = any, TDefaults extends Partial<TOverrides> | undefined = undefined>(pluginName: string, defaultValues?: TDefaults): OverridesResult<TOverrides, TDefaults>;
|
|
92
|
+
/**
|
|
93
|
+
* Hook to access the base path where the client router is mounted
|
|
94
|
+
*
|
|
95
|
+
* @returns The base path string (e.g., "/pages")
|
|
96
|
+
* @throws Error if used outside of BetterStackProvider
|
|
97
|
+
*
|
|
98
|
+
* @example
|
|
99
|
+
* ```tsx
|
|
100
|
+
* const basePath = useBasePath();
|
|
101
|
+
* // basePath = "/pages"
|
|
102
|
+
* ```
|
|
103
|
+
*/
|
|
84
104
|
declare function useBasePath(): string;
|
|
85
105
|
|
|
86
106
|
export { BetterStackProvider, useBasePath, useBetterStack, usePluginOverrides };
|
package/dist/packages/better-stack/src/plugins/blog/client/components/shared/on-this-page.cjs
CHANGED
|
@@ -7,6 +7,7 @@ const utils = require('../../../utils.cjs');
|
|
|
7
7
|
const context = require('@btst/stack/context');
|
|
8
8
|
const index = require('../../localization/index.cjs');
|
|
9
9
|
const select = require('../../../../../../../ui/src/components/select.cjs');
|
|
10
|
+
const lucideReact = require('lucide-react');
|
|
10
11
|
|
|
11
12
|
function OnThisPage({ markdown, className }) {
|
|
12
13
|
const { localization } = context.usePluginOverrides("blog", {
|
|
@@ -39,8 +40,12 @@ function OnThisPage({ markdown, className }) {
|
|
|
39
40
|
className
|
|
40
41
|
),
|
|
41
42
|
"aria-label": "Table of contents",
|
|
42
|
-
children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "overflow-y-auto px-2", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-
|
|
43
|
-
/* @__PURE__ */ jsxRuntime.
|
|
43
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "overflow-y-auto px-2", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-1 p-4 pt-0 text-sm", children: [
|
|
44
|
+
/* @__PURE__ */ jsxRuntime.jsxs("p", { className: "flex items-center gap-2 font-semibold text-muted-foreground sticky top-0 h-6 text-xs", children: [
|
|
45
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.TextAlignStart, { className: "w-3 h-3" }),
|
|
46
|
+
" ",
|
|
47
|
+
localization.BLOG_POST_ON_THIS_PAGE
|
|
48
|
+
] }),
|
|
44
49
|
headings.map(({ id, text, level }) => {
|
|
45
50
|
const paddingLeft = level === 1 ? "0" : level === 2 ? "0.5rem" : level === 3 ? "1rem" : level === 4 ? "1.5rem" : level === 5 ? "2rem" : level === 6 ? "2.5rem" : "0";
|
|
46
51
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
package/dist/packages/better-stack/src/plugins/blog/client/components/shared/on-this-page.mjs
CHANGED
|
@@ -5,6 +5,7 @@ import { cn, slugify } from '../../../utils.mjs';
|
|
|
5
5
|
import { usePluginOverrides } from '@btst/stack/context';
|
|
6
6
|
import { BLOG_LOCALIZATION } from '../../localization/index.mjs';
|
|
7
7
|
import { Select, SelectTrigger, SelectValue, SelectContent, SelectItem } from '../../../../../../../ui/src/components/select.mjs';
|
|
8
|
+
import { TextAlignStart } from 'lucide-react';
|
|
8
9
|
|
|
9
10
|
function OnThisPage({ markdown, className }) {
|
|
10
11
|
const { localization } = usePluginOverrides("blog", {
|
|
@@ -37,8 +38,12 @@ function OnThisPage({ markdown, className }) {
|
|
|
37
38
|
className
|
|
38
39
|
),
|
|
39
40
|
"aria-label": "Table of contents",
|
|
40
|
-
children: /* @__PURE__ */ jsx("div", { className: "overflow-y-auto px-2", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-
|
|
41
|
-
/* @__PURE__ */
|
|
41
|
+
children: /* @__PURE__ */ jsx("div", { className: "overflow-y-auto px-2", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1 p-4 pt-0 text-sm", children: [
|
|
42
|
+
/* @__PURE__ */ jsxs("p", { className: "flex items-center gap-2 font-semibold text-muted-foreground sticky top-0 h-6 text-xs", children: [
|
|
43
|
+
/* @__PURE__ */ jsx(TextAlignStart, { className: "w-3 h-3" }),
|
|
44
|
+
" ",
|
|
45
|
+
localization.BLOG_POST_ON_THIS_PAGE
|
|
46
|
+
] }),
|
|
42
47
|
headings.map(({ id, text, level }) => {
|
|
43
48
|
const paddingLeft = level === 1 ? "0" : level === 2 ? "0.5rem" : level === 3 ? "1rem" : level === 4 ? "1.5rem" : level === 5 ? "2rem" : level === 6 ? "2.5rem" : "0";
|
|
44
49
|
return /* @__PURE__ */ jsx(
|
|
@@ -2,6 +2,6 @@ export { B as BlogApiContext, c as BlogApiRouter, a as BlogBackendHooks, N as Ne
|
|
|
2
2
|
import '@btst/stack/plugins/api';
|
|
3
3
|
import 'better-call';
|
|
4
4
|
import 'zod';
|
|
5
|
-
import '../../../shared/stack.
|
|
5
|
+
import '../../../shared/stack.CoPoHVfV.cjs';
|
|
6
6
|
import '@tanstack/react-query';
|
|
7
7
|
import '@btst/stack/plugins/client';
|
|
@@ -2,6 +2,6 @@ export { B as BlogApiContext, c as BlogApiRouter, a as BlogBackendHooks, N as Ne
|
|
|
2
2
|
import '@btst/stack/plugins/api';
|
|
3
3
|
import 'better-call';
|
|
4
4
|
import 'zod';
|
|
5
|
-
import '../../../shared/stack.
|
|
5
|
+
import '../../../shared/stack.CoPoHVfV.mjs';
|
|
6
6
|
import '@tanstack/react-query';
|
|
7
7
|
import '@btst/stack/plugins/client';
|
|
@@ -2,6 +2,6 @@ export { B as BlogApiContext, c as BlogApiRouter, a as BlogBackendHooks, N as Ne
|
|
|
2
2
|
import '@btst/stack/plugins/api';
|
|
3
3
|
import 'better-call';
|
|
4
4
|
import 'zod';
|
|
5
|
-
import '../../../shared/stack.
|
|
5
|
+
import '../../../shared/stack.CoPoHVfV.js';
|
|
6
6
|
import '@tanstack/react-query';
|
|
7
7
|
import '@btst/stack/plugins/client';
|
|
@@ -1,48 +1,95 @@
|
|
|
1
1
|
import * as _tanstack_react_query from '@tanstack/react-query';
|
|
2
|
-
import { S as SerializedPost, c as createPostSchema, u as updatePostSchema, a as SerializedTag } from '../../../../shared/stack.
|
|
2
|
+
import { S as SerializedPost, c as createPostSchema, u as updatePostSchema, a as SerializedTag } from '../../../../shared/stack.CoPoHVfV.cjs';
|
|
3
3
|
import { z } from 'zod';
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* Options for the usePosts hook
|
|
7
|
+
*/
|
|
5
8
|
interface UsePostsOptions {
|
|
9
|
+
/** Filter posts by tag name */
|
|
6
10
|
tag?: string;
|
|
11
|
+
/** Filter posts by tag slug */
|
|
7
12
|
tagSlug?: string;
|
|
13
|
+
/** Number of posts to fetch per page (default: 10) */
|
|
8
14
|
limit?: number;
|
|
15
|
+
/** Whether to enable the query (default: true) */
|
|
9
16
|
enabled?: boolean;
|
|
17
|
+
/** Search query to filter posts by title, content, or excerpt */
|
|
10
18
|
query?: string;
|
|
19
|
+
/** Filter by published status */
|
|
11
20
|
published?: boolean;
|
|
21
|
+
/** Filter by specific post slug */
|
|
12
22
|
slug?: string;
|
|
13
23
|
}
|
|
24
|
+
/**
|
|
25
|
+
* Result from the usePosts hook
|
|
26
|
+
*/
|
|
14
27
|
interface UsePostsResult {
|
|
28
|
+
/** Array of fetched posts */
|
|
15
29
|
posts: SerializedPost[];
|
|
30
|
+
/** Whether the initial load is in progress */
|
|
16
31
|
isLoading: boolean;
|
|
32
|
+
/** Error if the query failed */
|
|
17
33
|
error: Error | null;
|
|
34
|
+
/** Function to load the next page of posts */
|
|
18
35
|
loadMore: () => void;
|
|
36
|
+
/** Whether there are more posts to load */
|
|
19
37
|
hasMore: boolean;
|
|
38
|
+
/** Whether the next page is being loaded */
|
|
20
39
|
isLoadingMore: boolean;
|
|
40
|
+
/** Function to refetch the posts */
|
|
21
41
|
refetch: () => void;
|
|
22
42
|
}
|
|
43
|
+
/**
|
|
44
|
+
* Options for the usePostSearch hook
|
|
45
|
+
*/
|
|
23
46
|
interface UsePostSearchOptions {
|
|
47
|
+
/** Search query string to filter posts */
|
|
24
48
|
query: string;
|
|
49
|
+
/** Whether to enable the search query (default: true) */
|
|
25
50
|
enabled?: boolean;
|
|
51
|
+
/** Debounce delay in milliseconds (default: 300) */
|
|
26
52
|
debounceMs?: number;
|
|
53
|
+
/** Number of results to return (default: 10) */
|
|
27
54
|
limit?: number;
|
|
55
|
+
/** Filter by published status (default: true) */
|
|
28
56
|
published?: boolean;
|
|
29
57
|
}
|
|
58
|
+
/**
|
|
59
|
+
* Result from the usePostSearch hook
|
|
60
|
+
*/
|
|
30
61
|
interface UsePostSearchResult {
|
|
62
|
+
/** Array of posts matching the search query */
|
|
31
63
|
posts: SerializedPost[];
|
|
64
|
+
/** Alias for posts (React Query compatibility) */
|
|
32
65
|
data: SerializedPost[];
|
|
66
|
+
/** Whether the search is in progress */
|
|
33
67
|
isLoading: boolean;
|
|
68
|
+
/** Error if the search failed */
|
|
34
69
|
error: Error | null;
|
|
70
|
+
/** Function to refetch the search results */
|
|
35
71
|
refetch: () => void;
|
|
72
|
+
/** Whether a search is currently in progress (includes debounce time) */
|
|
36
73
|
isSearching: boolean;
|
|
74
|
+
/** The debounced search query being used */
|
|
37
75
|
searchQuery: string;
|
|
38
76
|
}
|
|
77
|
+
/**
|
|
78
|
+
* Result from the usePost hook
|
|
79
|
+
*/
|
|
39
80
|
interface UsePostResult {
|
|
81
|
+
/** The fetched post, or null if not found */
|
|
40
82
|
post: SerializedPost | null;
|
|
83
|
+
/** Whether the post is being loaded */
|
|
41
84
|
isLoading: boolean;
|
|
85
|
+
/** Error if the query failed */
|
|
42
86
|
error: Error | null;
|
|
87
|
+
/** Function to refetch the post */
|
|
43
88
|
refetch: () => void;
|
|
44
89
|
}
|
|
90
|
+
/** Input type for creating a new post */
|
|
45
91
|
type PostCreateInput = z.infer<typeof createPostSchema>;
|
|
92
|
+
/** Input type for updating an existing post */
|
|
46
93
|
type PostUpdateInput = z.infer<typeof updatePostSchema>;
|
|
47
94
|
/**
|
|
48
95
|
* Hook for fetching paginated posts with load more functionality
|
|
@@ -81,8 +128,8 @@ declare function useSuspenseTags(): {
|
|
|
81
128
|
};
|
|
82
129
|
/** Create a new post */
|
|
83
130
|
declare function useCreatePost(): _tanstack_react_query.UseMutationResult<SerializedPost | null, Error, {
|
|
84
|
-
title: string;
|
|
85
131
|
published: boolean;
|
|
132
|
+
title: string;
|
|
86
133
|
content: string;
|
|
87
134
|
excerpt: string;
|
|
88
135
|
tags: ({
|
|
@@ -114,14 +161,26 @@ declare function useDeletePost(): _tanstack_react_query.UseMutationResult<{
|
|
|
114
161
|
* Debounces the query and preserves last successful results to avoid flicker.
|
|
115
162
|
*/
|
|
116
163
|
declare function usePostSearch({ query, enabled, debounceMs, limit, published, }: UsePostSearchOptions): UsePostSearchResult;
|
|
164
|
+
/**
|
|
165
|
+
* Options for the useNextPreviousPosts hook
|
|
166
|
+
*/
|
|
117
167
|
interface UseNextPreviousPostsOptions {
|
|
168
|
+
/** Whether to enable the query (default: true) */
|
|
118
169
|
enabled?: boolean;
|
|
119
170
|
}
|
|
171
|
+
/**
|
|
172
|
+
* Result from the useNextPreviousPosts hook
|
|
173
|
+
*/
|
|
120
174
|
interface UseNextPreviousPostsResult {
|
|
175
|
+
/** The previous post (older), or null if none exists */
|
|
121
176
|
previousPost: SerializedPost | null;
|
|
177
|
+
/** The next post (newer), or null if none exists */
|
|
122
178
|
nextPost: SerializedPost | null;
|
|
179
|
+
/** Whether the query is loading */
|
|
123
180
|
isLoading: boolean;
|
|
181
|
+
/** Error if the query failed */
|
|
124
182
|
error: Error | null;
|
|
183
|
+
/** Function to refetch the posts */
|
|
125
184
|
refetch: () => void;
|
|
126
185
|
}
|
|
127
186
|
/**
|
|
@@ -132,15 +191,28 @@ declare function useNextPreviousPosts(createdAt: string | Date, options?: UseNex
|
|
|
132
191
|
ref: (node: Element | null) => void;
|
|
133
192
|
inView: boolean;
|
|
134
193
|
};
|
|
194
|
+
/**
|
|
195
|
+
* Options for the useRecentPosts hook
|
|
196
|
+
*/
|
|
135
197
|
interface UseRecentPostsOptions {
|
|
198
|
+
/** Maximum number of recent posts to fetch (default: 5) */
|
|
136
199
|
limit?: number;
|
|
200
|
+
/** Slug of a post to exclude from results */
|
|
137
201
|
excludeSlug?: string;
|
|
202
|
+
/** Whether to enable the query (default: true) */
|
|
138
203
|
enabled?: boolean;
|
|
139
204
|
}
|
|
205
|
+
/**
|
|
206
|
+
* Result from the useRecentPosts hook
|
|
207
|
+
*/
|
|
140
208
|
interface UseRecentPostsResult {
|
|
209
|
+
/** Array of recent posts */
|
|
141
210
|
recentPosts: SerializedPost[];
|
|
211
|
+
/** Whether the query is loading */
|
|
142
212
|
isLoading: boolean;
|
|
213
|
+
/** Error if the query failed */
|
|
143
214
|
error: Error | null;
|
|
215
|
+
/** Function to refetch the posts */
|
|
144
216
|
refetch: () => void;
|
|
145
217
|
}
|
|
146
218
|
/**
|