@startsimpli/ui 0.4.37 → 0.4.38
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@startsimpli/ui",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.38",
|
|
4
4
|
"description": "Shared UI components package for StartSimpli applications",
|
|
5
5
|
"main": "./src/index.ts",
|
|
6
6
|
"types": "./src/index.ts",
|
|
@@ -33,8 +33,8 @@
|
|
|
33
33
|
"next": "^14.0.0 || ^15.0.0 || ^16.0.0",
|
|
34
34
|
"react": "^18.0.0 || ^19.0.0",
|
|
35
35
|
"react-dom": "^18.0.0 || ^19.0.0",
|
|
36
|
-
"@startsimpli/
|
|
37
|
-
"@startsimpli/
|
|
36
|
+
"@startsimpli/api": "0.5.36",
|
|
37
|
+
"@startsimpli/auth": "0.4.29"
|
|
38
38
|
},
|
|
39
39
|
"peerDependenciesMeta": {
|
|
40
40
|
"@startsimpli/api": {
|
|
@@ -12,3 +12,8 @@ export { ArticleCard, type ArticleCardProps } from './ArticleCard'
|
|
|
12
12
|
export { BlogIndex, type BlogIndexProps } from './BlogIndex'
|
|
13
13
|
export { BlogPost, type BlogPostProps } from './BlogPost'
|
|
14
14
|
export { TableOfContents, extractToc, type TableOfContentsProps, type TocItem } from './TableOfContents'
|
|
15
|
+
export {
|
|
16
|
+
resolveArticleOrFallback,
|
|
17
|
+
type ArticleSlugFetcher,
|
|
18
|
+
type ResolveArticleOptions,
|
|
19
|
+
} from './resolveArticle'
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { notFound, redirect } from 'next/navigation'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Minimal shape of a blog client's slug lookup. Matches both the shared
|
|
5
|
+
* `@startsimpli/api/blog` client (`createBlogClient().fetchPostBySlug`) and the
|
|
6
|
+
* legacy per-app `blog-api-server` modules, so callers can pass whichever they
|
|
7
|
+
* already have without migrating their data layer first.
|
|
8
|
+
*/
|
|
9
|
+
export type ArticleSlugFetcher<TPost> = (slug: string) => Promise<TPost | null | undefined>
|
|
10
|
+
|
|
11
|
+
export interface ResolveArticleOptions {
|
|
12
|
+
/**
|
|
13
|
+
* List/index route a missing article falls back to, e.g. `/resources` or
|
|
14
|
+
* `/articles`. Dead, stale, or mistyped article links land here.
|
|
15
|
+
*/
|
|
16
|
+
indexPath: string
|
|
17
|
+
/**
|
|
18
|
+
* Behaviour when no post matches the slug.
|
|
19
|
+
* - `'redirect'` (default): 307 to `indexPath` — the smart landing that keeps
|
|
20
|
+
* visitors (and crawlers following old links) inside the content instead of
|
|
21
|
+
* on a bare 404.
|
|
22
|
+
* - `'notFound'`: trigger the nearest `not-found.tsx` (404 status). Use for
|
|
23
|
+
* routes where preserving the 404 signal matters more than the soft landing.
|
|
24
|
+
*/
|
|
25
|
+
onMissing?: 'redirect' | 'notFound'
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Resolve a blog post by slug, applying the shared missing-article default.
|
|
30
|
+
*
|
|
31
|
+
* Every marketing-site article route (`/resources/[slug]`, `/articles/[slug]`,
|
|
32
|
+
* `/guides/[guide]`, …) should call this instead of hand-rolling
|
|
33
|
+
* `if (!post) notFound()` — so a non-existent or removed article URL
|
|
34
|
+
* smart-redirects to its list page by default, consistently across every app
|
|
35
|
+
* that shares the blog layer.
|
|
36
|
+
*
|
|
37
|
+
* Never returns a nullish value: it either returns the resolved post or throws
|
|
38
|
+
* the redirect / not-found control-flow signal that Next.js expects. Call it at
|
|
39
|
+
* the top level of a Server Component (do not wrap in try/catch, or the signal
|
|
40
|
+
* will be swallowed).
|
|
41
|
+
*/
|
|
42
|
+
export async function resolveArticleOrFallback<TPost>(
|
|
43
|
+
fetchPostBySlug: ArticleSlugFetcher<TPost>,
|
|
44
|
+
slug: string,
|
|
45
|
+
{ indexPath, onMissing = 'redirect' }: ResolveArticleOptions,
|
|
46
|
+
): Promise<TPost> {
|
|
47
|
+
const post = await fetchPostBySlug(slug)
|
|
48
|
+
if (post) return post
|
|
49
|
+
if (onMissing === 'notFound') notFound()
|
|
50
|
+
redirect(indexPath)
|
|
51
|
+
}
|