@btst/stack 1.1.9 → 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.
Files changed (29) hide show
  1. package/README.md +3 -168
  2. package/dist/client/components/index.d.cts +22 -0
  3. package/dist/client/components/index.d.mts +22 -0
  4. package/dist/client/components/index.d.ts +22 -0
  5. package/dist/context/index.d.cts +21 -1
  6. package/dist/context/index.d.mts +21 -1
  7. package/dist/context/index.d.ts +21 -1
  8. package/dist/packages/better-stack/src/plugins/blog/client/components/shared/on-this-page.cjs +7 -2
  9. package/dist/packages/better-stack/src/plugins/blog/client/components/shared/on-this-page.mjs +7 -2
  10. package/dist/packages/better-stack/src/plugins/blog/client/plugin.cjs +3 -3
  11. package/dist/packages/better-stack/src/plugins/blog/client/plugin.mjs +3 -3
  12. package/dist/plugins/blog/client/hooks/index.d.cts +72 -0
  13. package/dist/plugins/blog/client/hooks/index.d.mts +72 -0
  14. package/dist/plugins/blog/client/hooks/index.d.ts +72 -0
  15. package/dist/plugins/blog/client/index.d.cts +69 -0
  16. package/dist/plugins/blog/client/index.d.mts +69 -0
  17. package/dist/plugins/blog/client/index.d.ts +69 -0
  18. package/dist/plugins/blog/query-keys.d.cts +62 -0
  19. package/dist/plugins/blog/query-keys.d.mts +62 -0
  20. package/dist/plugins/blog/query-keys.d.ts +62 -0
  21. package/package.json +1 -1
  22. package/src/client/components/compose.tsx +13 -0
  23. package/src/client/components/error-boundary.tsx +9 -0
  24. package/src/context/provider.tsx +21 -1
  25. package/src/plugins/blog/api/plugin.ts +62 -3
  26. package/src/plugins/blog/client/components/shared/on-this-page.tsx +4 -2
  27. package/src/plugins/blog/client/hooks/blog-hooks.tsx +72 -0
  28. package/src/plugins/blog/client/overrides.ts +10 -0
  29. package/src/plugins/blog/client/plugin.tsx +66 -8
@@ -11,21 +11,32 @@ import 'zod';
11
11
  * Context passed to route hooks
12
12
  */
13
13
  interface RouteContext$1 {
14
+ /** Current route path */
14
15
  path: string;
16
+ /** Route parameters (e.g., { slug: "my-post" }) */
15
17
  params?: Record<string, string>;
18
+ /** Whether rendering on server (true) or client (false) */
16
19
  isSSR: boolean;
20
+ /** Additional context properties */
17
21
  [key: string]: any;
18
22
  }
19
23
  /**
20
24
  * Context passed to loader hooks
21
25
  */
22
26
  interface LoaderContext {
27
+ /** Current route path */
23
28
  path: string;
29
+ /** Route parameters (e.g., { slug: "my-post" }) */
24
30
  params?: Record<string, string>;
31
+ /** Whether rendering on server (true) or client (false) */
25
32
  isSSR: boolean;
33
+ /** Base URL for API calls */
26
34
  apiBaseURL: string;
35
+ /** Path where the API is mounted */
27
36
  apiBasePath: string;
37
+ /** Optional headers for the request */
28
38
  headers?: Headers;
39
+ /** Additional context properties */
29
40
  [key: string]: any;
30
41
  }
31
42
  /**
@@ -33,19 +44,32 @@ interface LoaderContext {
33
44
  * Note: queryClient is passed at runtime to both loader and meta (for SSR isolation)
34
45
  */
35
46
  interface BlogClientConfig {
47
+ /** Base URL for API calls (e.g., "http://localhost:3000") */
36
48
  apiBaseURL: string;
49
+ /** Path where the API is mounted (e.g., "/api/data") */
37
50
  apiBasePath: string;
51
+ /** Base URL of your site for SEO meta tags */
38
52
  siteBaseURL: string;
53
+ /** Path where pages are mounted (e.g., "/pages") */
39
54
  siteBasePath: string;
55
+ /** React Query client instance for caching */
40
56
  queryClient: QueryClient;
57
+ /** Optional SEO configuration for meta tags */
41
58
  seo?: {
59
+ /** Site name for Open Graph tags */
42
60
  siteName?: string;
61
+ /** Default author name */
43
62
  author?: string;
63
+ /** Twitter handle (e.g., "@yourhandle") */
44
64
  twitterHandle?: string;
65
+ /** Locale for Open Graph (e.g., "en_US") */
45
66
  locale?: string;
67
+ /** Default image URL for social sharing */
46
68
  defaultImage?: string;
47
69
  };
70
+ /** Optional hooks for customizing behavior */
48
71
  hooks?: BlogClientHooks;
72
+ /** Optional headers for SSR (e.g., forwarding cookies) */
49
73
  headers?: Headers;
50
74
  }
51
75
  /**
@@ -53,16 +77,51 @@ interface BlogClientConfig {
53
77
  * All hooks are optional and allow consumers to customize behavior
54
78
  */
55
79
  interface BlogClientHooks {
80
+ /**
81
+ * Called before loading posts list. Return false to cancel loading.
82
+ * @param filter - Filter parameters including published status
83
+ * @param context - Loader context with path, params, etc.
84
+ */
56
85
  beforeLoadPosts?: (filter: {
57
86
  published: boolean;
58
87
  }, context: LoaderContext) => Promise<boolean> | boolean;
88
+ /**
89
+ * Called after posts are loaded. Return false to cancel further processing.
90
+ * @param posts - Array of loaded posts or null
91
+ * @param filter - Filter parameters used
92
+ * @param context - Loader context
93
+ */
59
94
  afterLoadPosts?: (posts: Post[] | null, filter: {
60
95
  published: boolean;
61
96
  }, context: LoaderContext) => Promise<boolean> | boolean;
97
+ /**
98
+ * Called before loading a single post. Return false to cancel loading.
99
+ * @param slug - Post slug being loaded
100
+ * @param context - Loader context
101
+ */
62
102
  beforeLoadPost?: (slug: string, context: LoaderContext) => Promise<boolean> | boolean;
103
+ /**
104
+ * Called after a post is loaded. Return false to cancel further processing.
105
+ * @param post - Loaded post or null if not found
106
+ * @param slug - Post slug that was requested
107
+ * @param context - Loader context
108
+ */
63
109
  afterLoadPost?: (post: Post | null, slug: string, context: LoaderContext) => Promise<boolean> | boolean;
110
+ /**
111
+ * Called before loading the new post page. Return false to cancel.
112
+ * @param context - Loader context
113
+ */
64
114
  beforeLoadNewPost?: (context: LoaderContext) => Promise<boolean> | boolean;
115
+ /**
116
+ * Called after the new post page is loaded. Return false to cancel.
117
+ * @param context - Loader context
118
+ */
65
119
  afterLoadNewPost?: (context: LoaderContext) => Promise<boolean> | boolean;
120
+ /**
121
+ * Called when a loading error occurs
122
+ * @param error - The error that occurred
123
+ * @param context - Loader context
124
+ */
66
125
  onLoadError?: (error: Error, context: LoaderContext) => Promise<void> | void;
67
126
  }
68
127
  /**
@@ -313,9 +372,13 @@ type BlogLocalization = typeof BLOG_LOCALIZATION;
313
372
  * Context passed to lifecycle hooks
314
373
  */
315
374
  interface RouteContext {
375
+ /** Current route path */
316
376
  path: string;
377
+ /** Route parameters (e.g., { slug: "my-post" }) */
317
378
  params?: Record<string, string>;
379
+ /** Whether rendering on server (true) or client (false) */
318
380
  isSSR: boolean;
381
+ /** Additional context properties */
319
382
  [key: string]: any;
320
383
  }
321
384
  /**
@@ -325,7 +388,13 @@ interface RouteContext {
325
388
  * to customize the behavior for their framework (Next.js, React Router, etc.)
326
389
  */
327
390
  interface BlogPluginOverrides {
391
+ /**
392
+ * Link component for navigation
393
+ */
328
394
  Link?: ComponentType<React.ComponentProps<"a"> & Record<string, any>>;
395
+ /**
396
+ * Post card component for displaying a post
397
+ */
329
398
  PostCard?: ComponentType<{
330
399
  post: SerializedPost;
331
400
  }>;
@@ -32,17 +32,79 @@ interface BlogApiContext<TBody = any, TParams = any, TQuery = any> {
32
32
  * All hooks are optional and allow consumers to customize behavior
33
33
  */
34
34
  interface BlogBackendHooks {
35
+ /**
36
+ * Called before listing posts. Return false to deny access.
37
+ * @param filter - Query parameters for filtering posts
38
+ * @param context - Request context with headers, etc.
39
+ */
35
40
  onBeforeListPosts?: (filter: z.infer<typeof PostListQuerySchema>, context: BlogApiContext) => Promise<boolean> | boolean;
41
+ /**
42
+ * Called before creating a post. Return false to deny access.
43
+ * @param data - Post data being created
44
+ * @param context - Request context with headers, etc.
45
+ */
36
46
  onBeforeCreatePost?: (data: z.infer<typeof createPostSchema>, context: BlogApiContext) => Promise<boolean> | boolean;
47
+ /**
48
+ * Called before updating a post. Return false to deny access.
49
+ * @param postId - ID of the post being updated
50
+ * @param data - Updated post data
51
+ * @param context - Request context with headers, etc.
52
+ */
37
53
  onBeforeUpdatePost?: (postId: string, data: z.infer<typeof updatePostSchema>, context: BlogApiContext) => Promise<boolean> | boolean;
54
+ /**
55
+ * Called before deleting a post. Return false to deny access.
56
+ * @param postId - ID of the post being deleted
57
+ * @param context - Request context with headers, etc.
58
+ */
38
59
  onBeforeDeletePost?: (postId: string, context: BlogApiContext) => Promise<boolean> | boolean;
60
+ /**
61
+ * Called after posts are read successfully
62
+ * @param posts - Array of posts that were read
63
+ * @param filter - Query parameters used for filtering
64
+ * @param context - Request context
65
+ */
39
66
  onPostsRead?: (posts: Post[], filter: z.infer<typeof PostListQuerySchema>, context: BlogApiContext) => Promise<void> | void;
67
+ /**
68
+ * Called after a post is created successfully
69
+ * @param post - The created post
70
+ * @param context - Request context
71
+ */
40
72
  onPostCreated?: (post: Post, context: BlogApiContext) => Promise<void> | void;
73
+ /**
74
+ * Called after a post is updated successfully
75
+ * @param post - The updated post
76
+ * @param context - Request context
77
+ */
41
78
  onPostUpdated?: (post: Post, context: BlogApiContext) => Promise<void> | void;
79
+ /**
80
+ * Called after a post is deleted successfully
81
+ * @param postId - ID of the deleted post
82
+ * @param context - Request context
83
+ */
42
84
  onPostDeleted?: (postId: string, context: BlogApiContext) => Promise<void> | void;
85
+ /**
86
+ * Called when listing posts fails
87
+ * @param error - The error that occurred
88
+ * @param context - Request context
89
+ */
43
90
  onListPostsError?: (error: Error, context: BlogApiContext) => Promise<void> | void;
91
+ /**
92
+ * Called when creating a post fails
93
+ * @param error - The error that occurred
94
+ * @param context - Request context
95
+ */
44
96
  onCreatePostError?: (error: Error, context: BlogApiContext) => Promise<void> | void;
97
+ /**
98
+ * Called when updating a post fails
99
+ * @param error - The error that occurred
100
+ * @param context - Request context
101
+ */
45
102
  onUpdatePostError?: (error: Error, context: BlogApiContext) => Promise<void> | void;
103
+ /**
104
+ * Called when deleting a post fails
105
+ * @param error - The error that occurred
106
+ * @param context - Request context
107
+ */
46
108
  onDeletePostError?: (error: Error, context: BlogApiContext) => Promise<void> | void;
47
109
  }
48
110
  /**
@@ -32,17 +32,79 @@ interface BlogApiContext<TBody = any, TParams = any, TQuery = any> {
32
32
  * All hooks are optional and allow consumers to customize behavior
33
33
  */
34
34
  interface BlogBackendHooks {
35
+ /**
36
+ * Called before listing posts. Return false to deny access.
37
+ * @param filter - Query parameters for filtering posts
38
+ * @param context - Request context with headers, etc.
39
+ */
35
40
  onBeforeListPosts?: (filter: z.infer<typeof PostListQuerySchema>, context: BlogApiContext) => Promise<boolean> | boolean;
41
+ /**
42
+ * Called before creating a post. Return false to deny access.
43
+ * @param data - Post data being created
44
+ * @param context - Request context with headers, etc.
45
+ */
36
46
  onBeforeCreatePost?: (data: z.infer<typeof createPostSchema>, context: BlogApiContext) => Promise<boolean> | boolean;
47
+ /**
48
+ * Called before updating a post. Return false to deny access.
49
+ * @param postId - ID of the post being updated
50
+ * @param data - Updated post data
51
+ * @param context - Request context with headers, etc.
52
+ */
37
53
  onBeforeUpdatePost?: (postId: string, data: z.infer<typeof updatePostSchema>, context: BlogApiContext) => Promise<boolean> | boolean;
54
+ /**
55
+ * Called before deleting a post. Return false to deny access.
56
+ * @param postId - ID of the post being deleted
57
+ * @param context - Request context with headers, etc.
58
+ */
38
59
  onBeforeDeletePost?: (postId: string, context: BlogApiContext) => Promise<boolean> | boolean;
60
+ /**
61
+ * Called after posts are read successfully
62
+ * @param posts - Array of posts that were read
63
+ * @param filter - Query parameters used for filtering
64
+ * @param context - Request context
65
+ */
39
66
  onPostsRead?: (posts: Post[], filter: z.infer<typeof PostListQuerySchema>, context: BlogApiContext) => Promise<void> | void;
67
+ /**
68
+ * Called after a post is created successfully
69
+ * @param post - The created post
70
+ * @param context - Request context
71
+ */
40
72
  onPostCreated?: (post: Post, context: BlogApiContext) => Promise<void> | void;
73
+ /**
74
+ * Called after a post is updated successfully
75
+ * @param post - The updated post
76
+ * @param context - Request context
77
+ */
41
78
  onPostUpdated?: (post: Post, context: BlogApiContext) => Promise<void> | void;
79
+ /**
80
+ * Called after a post is deleted successfully
81
+ * @param postId - ID of the deleted post
82
+ * @param context - Request context
83
+ */
42
84
  onPostDeleted?: (postId: string, context: BlogApiContext) => Promise<void> | void;
85
+ /**
86
+ * Called when listing posts fails
87
+ * @param error - The error that occurred
88
+ * @param context - Request context
89
+ */
43
90
  onListPostsError?: (error: Error, context: BlogApiContext) => Promise<void> | void;
91
+ /**
92
+ * Called when creating a post fails
93
+ * @param error - The error that occurred
94
+ * @param context - Request context
95
+ */
44
96
  onCreatePostError?: (error: Error, context: BlogApiContext) => Promise<void> | void;
97
+ /**
98
+ * Called when updating a post fails
99
+ * @param error - The error that occurred
100
+ * @param context - Request context
101
+ */
45
102
  onUpdatePostError?: (error: Error, context: BlogApiContext) => Promise<void> | void;
103
+ /**
104
+ * Called when deleting a post fails
105
+ * @param error - The error that occurred
106
+ * @param context - Request context
107
+ */
46
108
  onDeletePostError?: (error: Error, context: BlogApiContext) => Promise<void> | void;
47
109
  }
48
110
  /**
@@ -32,17 +32,79 @@ interface BlogApiContext<TBody = any, TParams = any, TQuery = any> {
32
32
  * All hooks are optional and allow consumers to customize behavior
33
33
  */
34
34
  interface BlogBackendHooks {
35
+ /**
36
+ * Called before listing posts. Return false to deny access.
37
+ * @param filter - Query parameters for filtering posts
38
+ * @param context - Request context with headers, etc.
39
+ */
35
40
  onBeforeListPosts?: (filter: z.infer<typeof PostListQuerySchema>, context: BlogApiContext) => Promise<boolean> | boolean;
41
+ /**
42
+ * Called before creating a post. Return false to deny access.
43
+ * @param data - Post data being created
44
+ * @param context - Request context with headers, etc.
45
+ */
36
46
  onBeforeCreatePost?: (data: z.infer<typeof createPostSchema>, context: BlogApiContext) => Promise<boolean> | boolean;
47
+ /**
48
+ * Called before updating a post. Return false to deny access.
49
+ * @param postId - ID of the post being updated
50
+ * @param data - Updated post data
51
+ * @param context - Request context with headers, etc.
52
+ */
37
53
  onBeforeUpdatePost?: (postId: string, data: z.infer<typeof updatePostSchema>, context: BlogApiContext) => Promise<boolean> | boolean;
54
+ /**
55
+ * Called before deleting a post. Return false to deny access.
56
+ * @param postId - ID of the post being deleted
57
+ * @param context - Request context with headers, etc.
58
+ */
38
59
  onBeforeDeletePost?: (postId: string, context: BlogApiContext) => Promise<boolean> | boolean;
60
+ /**
61
+ * Called after posts are read successfully
62
+ * @param posts - Array of posts that were read
63
+ * @param filter - Query parameters used for filtering
64
+ * @param context - Request context
65
+ */
39
66
  onPostsRead?: (posts: Post[], filter: z.infer<typeof PostListQuerySchema>, context: BlogApiContext) => Promise<void> | void;
67
+ /**
68
+ * Called after a post is created successfully
69
+ * @param post - The created post
70
+ * @param context - Request context
71
+ */
40
72
  onPostCreated?: (post: Post, context: BlogApiContext) => Promise<void> | void;
73
+ /**
74
+ * Called after a post is updated successfully
75
+ * @param post - The updated post
76
+ * @param context - Request context
77
+ */
41
78
  onPostUpdated?: (post: Post, context: BlogApiContext) => Promise<void> | void;
79
+ /**
80
+ * Called after a post is deleted successfully
81
+ * @param postId - ID of the deleted post
82
+ * @param context - Request context
83
+ */
42
84
  onPostDeleted?: (postId: string, context: BlogApiContext) => Promise<void> | void;
85
+ /**
86
+ * Called when listing posts fails
87
+ * @param error - The error that occurred
88
+ * @param context - Request context
89
+ */
43
90
  onListPostsError?: (error: Error, context: BlogApiContext) => Promise<void> | void;
91
+ /**
92
+ * Called when creating a post fails
93
+ * @param error - The error that occurred
94
+ * @param context - Request context
95
+ */
44
96
  onCreatePostError?: (error: Error, context: BlogApiContext) => Promise<void> | void;
97
+ /**
98
+ * Called when updating a post fails
99
+ * @param error - The error that occurred
100
+ * @param context - Request context
101
+ */
45
102
  onUpdatePostError?: (error: Error, context: BlogApiContext) => Promise<void> | void;
103
+ /**
104
+ * Called when deleting a post fails
105
+ * @param error - The error that occurred
106
+ * @param context - Request context
107
+ */
46
108
  onDeletePostError?: (error: Error, context: BlogApiContext) => Promise<void> | void;
47
109
  }
48
110
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@btst/stack",
3
- "version": "1.1.9",
3
+ "version": "1.1.11",
4
4
  "description": "A composable, plugin-based library for building full-stack applications.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -55,6 +55,19 @@ export function RouteRenderer({
55
55
  );
56
56
  }
57
57
 
58
+ /**
59
+ * Renders a route with Suspense and ErrorBoundary wrappers.
60
+ * Handles loading states, error boundaries, and not-found scenarios for a single route.
61
+ *
62
+ * @param path - The current route path
63
+ * @param PageComponent - The page component to render
64
+ * @param ErrorComponent - Optional error fallback component
65
+ * @param LoadingComponent - Component to show during suspense
66
+ * @param onNotFound - Optional callback when route is not found
67
+ * @param NotFoundComponent - Optional component to show for 404s
68
+ * @param props - Additional props to pass to the page component
69
+ * @param onError - Error handler callback for the error boundary
70
+ */
58
71
  export function ComposedRoute({
59
72
  path,
60
73
  PageComponent,
@@ -7,6 +7,15 @@ import {
7
7
 
8
8
  export type { FallbackProps } from "react-error-boundary";
9
9
 
10
+ /**
11
+ * Error boundary wrapper component for catching and handling React errors.
12
+ * Wraps react-error-boundary with a simplified API.
13
+ *
14
+ * @param children - Child components to wrap with error boundary
15
+ * @param FallbackComponent - Component to render when an error occurs
16
+ * @param resetKeys - Array of values that will reset the error boundary when changed
17
+ * @param onError - Callback invoked when an error is caught
18
+ */
10
19
  export function ErrorBoundary({
11
20
  children,
12
21
  FallbackComponent,
@@ -75,8 +75,16 @@ export function BetterStackProvider<
75
75
  }
76
76
 
77
77
  /**
78
- * Hook to access the entire context
78
+ * Hook to access the entire BetterStack context
79
79
  * Useful if you need access to multiple plugins or the full context
80
+ *
81
+ * @returns The full context value including overrides and basePath
82
+ * @throws Error if used outside of BetterStackProvider
83
+ *
84
+ * @example
85
+ * ```tsx
86
+ * const { overrides, basePath } = useBetterStack<MyPluginOverrides>();
87
+ * ```
80
88
  */
81
89
  export function useBetterStack<
82
90
  TPluginOverrides extends Record<string, any> = Record<string, any>,
@@ -145,6 +153,18 @@ export function usePluginOverrides<
145
153
  return overrides as OverridesResult<TOverrides, TDefaults>;
146
154
  }
147
155
 
156
+ /**
157
+ * Hook to access the base path where the client router is mounted
158
+ *
159
+ * @returns The base path string (e.g., "/pages")
160
+ * @throws Error if used outside of BetterStackProvider
161
+ *
162
+ * @example
163
+ * ```tsx
164
+ * const basePath = useBasePath();
165
+ * // basePath = "/pages"
166
+ * ```
167
+ */
148
168
  export function useBasePath() {
149
169
  const context = useBetterStack();
150
170
  if (!context) {
@@ -45,51 +45,110 @@ export interface BlogApiContext<TBody = any, TParams = any, TQuery = any> {
45
45
  * All hooks are optional and allow consumers to customize behavior
46
46
  */
47
47
  export interface BlogBackendHooks {
48
- // Hooks - called before the operation
48
+ /**
49
+ * Called before listing posts. Return false to deny access.
50
+ * @param filter - Query parameters for filtering posts
51
+ * @param context - Request context with headers, etc.
52
+ */
49
53
  onBeforeListPosts?: (
50
54
  filter: z.infer<typeof PostListQuerySchema>,
51
55
  context: BlogApiContext,
52
56
  ) => Promise<boolean> | boolean;
57
+ /**
58
+ * Called before creating a post. Return false to deny access.
59
+ * @param data - Post data being created
60
+ * @param context - Request context with headers, etc.
61
+ */
53
62
  onBeforeCreatePost?: (
54
63
  data: z.infer<typeof createPostSchema>,
55
64
  context: BlogApiContext,
56
65
  ) => Promise<boolean> | boolean;
66
+ /**
67
+ * Called before updating a post. Return false to deny access.
68
+ * @param postId - ID of the post being updated
69
+ * @param data - Updated post data
70
+ * @param context - Request context with headers, etc.
71
+ */
57
72
  onBeforeUpdatePost?: (
58
73
  postId: string,
59
74
  data: z.infer<typeof updatePostSchema>,
60
75
  context: BlogApiContext,
61
76
  ) => Promise<boolean> | boolean;
77
+ /**
78
+ * Called before deleting a post. Return false to deny access.
79
+ * @param postId - ID of the post being deleted
80
+ * @param context - Request context with headers, etc.
81
+ */
62
82
  onBeforeDeletePost?: (
63
83
  postId: string,
64
84
  context: BlogApiContext,
65
85
  ) => Promise<boolean> | boolean;
66
86
 
67
- // Lifecycle hooks - called after the operation
87
+ /**
88
+ * Called after posts are read successfully
89
+ * @param posts - Array of posts that were read
90
+ * @param filter - Query parameters used for filtering
91
+ * @param context - Request context
92
+ */
68
93
  onPostsRead?: (
69
94
  posts: Post[],
70
95
  filter: z.infer<typeof PostListQuerySchema>,
71
96
  context: BlogApiContext,
72
97
  ) => Promise<void> | void;
98
+ /**
99
+ * Called after a post is created successfully
100
+ * @param post - The created post
101
+ * @param context - Request context
102
+ */
73
103
  onPostCreated?: (post: Post, context: BlogApiContext) => Promise<void> | void;
104
+ /**
105
+ * Called after a post is updated successfully
106
+ * @param post - The updated post
107
+ * @param context - Request context
108
+ */
74
109
  onPostUpdated?: (post: Post, context: BlogApiContext) => Promise<void> | void;
110
+ /**
111
+ * Called after a post is deleted successfully
112
+ * @param postId - ID of the deleted post
113
+ * @param context - Request context
114
+ */
75
115
  onPostDeleted?: (
76
116
  postId: string,
77
117
  context: BlogApiContext,
78
118
  ) => Promise<void> | void;
79
119
 
80
- // Error hooks - called when operations fail
120
+ /**
121
+ * Called when listing posts fails
122
+ * @param error - The error that occurred
123
+ * @param context - Request context
124
+ */
81
125
  onListPostsError?: (
82
126
  error: Error,
83
127
  context: BlogApiContext,
84
128
  ) => Promise<void> | void;
129
+ /**
130
+ * Called when creating a post fails
131
+ * @param error - The error that occurred
132
+ * @param context - Request context
133
+ */
85
134
  onCreatePostError?: (
86
135
  error: Error,
87
136
  context: BlogApiContext,
88
137
  ) => Promise<void> | void;
138
+ /**
139
+ * Called when updating a post fails
140
+ * @param error - The error that occurred
141
+ * @param context - Request context
142
+ */
89
143
  onUpdatePostError?: (
90
144
  error: Error,
91
145
  context: BlogApiContext,
92
146
  ) => Promise<void> | void;
147
+ /**
148
+ * Called when deleting a post fails
149
+ * @param error - The error that occurred
150
+ * @param context - Request context
151
+ */
93
152
  onDeletePostError?: (
94
153
  error: Error,
95
154
  context: BlogApiContext,
@@ -12,6 +12,7 @@ import {
12
12
  SelectTrigger,
13
13
  SelectValue,
14
14
  } from "@workspace/ui/components/select";
15
+ import { TextAlignStart } from "lucide-react";
15
16
 
16
17
  interface Heading {
17
18
  id: string;
@@ -62,8 +63,9 @@ export function OnThisPage({ markdown, className }: OnThisPageProps) {
62
63
  aria-label="Table of contents"
63
64
  >
64
65
  <div className="overflow-y-auto px-2">
65
- <div className="flex flex-col gap-2 p-4 pt-0 text-sm">
66
- <p className="font-semibold text-muted-foreground sticky top-0 h-6 text-xs">
66
+ <div className="flex flex-col gap-1 p-4 pt-0 text-sm">
67
+ <p className="flex items-center gap-2 font-semibold text-muted-foreground sticky top-0 h-6 text-xs">
68
+ <TextAlignStart className="w-3 h-3" />{" "}
67
69
  {localization.BLOG_POST_ON_THIS_PAGE}
68
70
  </p>
69
71
  {headings.map(({ id, text, level }) => {