@mailoo/blog 0.0.0-stage → 1.33.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/LICENSE +16 -0
- package/README.md +142 -2
- package/dist/client/article-body.cjs +115 -0
- package/dist/client/article-body.cjs.map +1 -0
- package/dist/client/article-body.d.cts +15 -0
- package/dist/client/article-body.d.ts +15 -0
- package/dist/client/article-body.js +80 -0
- package/dist/client/article-body.js.map +1 -0
- package/dist/client/index.cjs +776 -0
- package/dist/client/index.cjs.map +1 -0
- package/dist/client/index.d.cts +6 -0
- package/dist/client/index.d.ts +6 -0
- package/dist/client/index.js +731 -0
- package/dist/client/index.js.map +1 -0
- package/dist/client/linked-links.cjs +125 -0
- package/dist/client/linked-links.cjs.map +1 -0
- package/dist/client/linked-links.d.cts +31 -0
- package/dist/client/linked-links.d.ts +31 -0
- package/dist/client/linked-links.js +100 -0
- package/dist/client/linked-links.js.map +1 -0
- package/dist/client/marks.cjs +300 -0
- package/dist/client/marks.cjs.map +1 -0
- package/dist/client/marks.d.cts +30 -0
- package/dist/client/marks.d.ts +30 -0
- package/dist/client/marks.js +276 -0
- package/dist/client/marks.js.map +1 -0
- package/dist/client/printable.cjs +106 -0
- package/dist/client/printable.cjs.map +1 -0
- package/dist/client/printable.d.cts +12 -0
- package/dist/client/printable.d.ts +12 -0
- package/dist/client/printable.js +82 -0
- package/dist/client/printable.js.map +1 -0
- package/dist/client/share.cjs +234 -0
- package/dist/client/share.cjs.map +1 -0
- package/dist/client/share.d.cts +41 -0
- package/dist/client/share.d.ts +41 -0
- package/dist/client/share.js +205 -0
- package/dist/client/share.js.map +1 -0
- package/dist/index.cjs +89 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +5 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +60 -0
- package/dist/index.js.map +1 -0
- package/dist/routes/index.cjs +266 -0
- package/dist/routes/index.cjs.map +1 -0
- package/dist/routes/index.d.cts +87 -0
- package/dist/routes/index.d.ts +87 -0
- package/dist/routes/index.js +240 -0
- package/dist/routes/index.js.map +1 -0
- package/dist/server/index.cjs +368 -0
- package/dist/server/index.cjs.map +1 -0
- package/dist/server/index.d.cts +132 -0
- package/dist/server/index.d.ts +132 -0
- package/dist/server/index.js +339 -0
- package/dist/server/index.js.map +1 -0
- package/dist/utils-DXbN9MTi.d.cts +120 -0
- package/dist/utils-DXbN9MTi.d.ts +120 -0
- package/package.json +110 -3
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/server/index.ts","../../src/server/client.ts","../../src/server/fetch-bff.ts","../../src/server/utils.ts"],"sourcesContent":["export type {\n BlogPostApiData,\n BlogPostLinkedLinkApi,\n BlogCategoryApi,\n BlogTagApi,\n BlogListQuery,\n BlogListPagination,\n MailooBlogFetchOptions,\n BlogPostReactionSentiment,\n BlogArticleMarkCounts,\n BlogArticleMarkState,\n SetBlogReactionInput,\n SetBlogFeedbackInput,\n} from '../types.js'\nexport {\n createMailooBlogClient,\n getMailooBlogConfigFromEnv,\n type MailooBlogConfig,\n type MailooBlogClient,\n type CreateMailooBlogClientOptions,\n} from './client.js'\nexport {\n fetchBlogPostBySlug,\n fetchBlogPostJsonLd,\n applyJsonLdPlaceholders,\n} from './fetch-bff.js'\nexport {\n firstEmbedImageUrlFromArticle,\n BLOG_ARTICLE_PROSE_CLASSNAME,\n} from './utils.js'\n","import { unstable_rethrow } from 'next/navigation'\nimport type { MailooIntegrationConfig } from '@mailoo/next-core'\nimport { readMailooIntegrationConfig } from '@mailoo/next-core'\nimport {\n rewriteMailooBlogMediaUrls,\n rewriteMailooPublicImageUrls,\n rewriteMailooPublicImageUrlsAnyHost,\n} from '@mailoo/images'\nimport type {\n BlogArticleMarkState,\n BlogCategoryApi,\n BlogTagApi,\n BlogListPagination,\n BlogListQuery,\n BlogPostApiData,\n MailooBlogFetchOptions,\n SetBlogFeedbackInput,\n SetBlogReactionInput,\n} from '../types.js'\n\nexport type MailooBlogConfig = MailooIntegrationConfig\nexport type { MailooBlogFetchOptions }\n\n/** Host image proxy. Omit to keep canonical Mailoo embed URLs. */\nexport type CreateMailooBlogClientOptions = {\n /**\n * Integrator prefix ending with `?id=` (absolute or same-origin path).\n * Example: `/api/v1/images/public/content?id=`\n */\n imageProxyPrefixWithIdEquals?: string\n}\n\nconst DEFAULT_REVALIDATE = 60\n\n/** Read MAILOO_BLOG_* env (API, API_KEY, PROJECT_UID, INTEGRATION_ID). */\nexport function getMailooBlogConfigFromEnv(): MailooBlogConfig | null {\n return readMailooIntegrationConfig({ prefix: 'MAILOO_BLOG' })\n}\n\nfunction blogBasePath(config: MailooBlogConfig): string {\n return `${config.apiBaseUrl}/api/v1/blog/${config.projectUid}/integrations/${config.integrationId}`\n}\n\nfunction resolveRevalidate(options?: MailooBlogFetchOptions): number {\n return options?.revalidate ?? DEFAULT_REVALIDATE\n}\n\nasync function blogFetchJson(\n config: MailooBlogConfig,\n path: string,\n query?: Record<string, string | undefined>,\n options?: MailooBlogFetchOptions\n): Promise<\n { ok: true; json: unknown; status: number } | { ok: false; status: number }\n> {\n const url = new URL(`${blogBasePath(config)}${path}`)\n if (query) {\n for (const [k, v] of Object.entries(query)) {\n if (v !== undefined && v !== '') url.searchParams.set(k, v)\n }\n }\n try {\n const revalidate = resolveRevalidate(options)\n const res = await fetch(url.toString(), {\n method: 'GET',\n headers: {\n 'Content-Type': 'application/json',\n 'X-API-Key': config.apiKey,\n },\n next: { revalidate },\n } as RequestInit)\n if (!res.ok) {\n return { ok: false, status: res.status }\n }\n return { ok: true, json: await res.json(), status: res.status }\n } catch (error) {\n unstable_rethrow(error)\n return { ok: false, status: 500 }\n }\n}\n\nasync function blogPostJson(\n config: MailooBlogConfig,\n path: string,\n body: unknown\n): Promise<\n | { ok: true; data: BlogArticleMarkState; status: number }\n | { ok: false; status: number }\n> {\n try {\n const res = await fetch(`${blogBasePath(config)}${path}`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'X-API-Key': config.apiKey,\n },\n body: JSON.stringify(body),\n cache: 'no-store',\n })\n if (!res.ok) {\n return { ok: false, status: res.status }\n }\n const json = (await res.json()) as { data?: BlogArticleMarkState }\n if (!json.data || typeof json.data !== 'object') {\n return { ok: false, status: 500 }\n }\n return { ok: true, data: json.data, status: res.status }\n } catch (error) {\n unstable_rethrow(error)\n return { ok: false, status: 500 }\n }\n}\n\nasync function blogGet<T>(\n config: MailooBlogConfig,\n path: string,\n query?: Record<string, string | undefined>,\n options?: MailooBlogFetchOptions\n): Promise<\n { ok: true; data: T; status: number } | { ok: false; status: number }\n> {\n const fetched = await blogFetchJson(config, path, query, options)\n if (!fetched.ok) return fetched\n const json = fetched.json as { data?: T } & T\n const data =\n (json as { data?: T }).data !== undefined\n ? (json as { data: T }).data\n : (json as T)\n return { ok: true, data, status: fetched.status }\n}\n\nfunction isBlogListPagination(value: unknown): value is BlogListPagination {\n if (value === null || typeof value !== 'object') return false\n const p = value as Record<string, unknown>\n return (\n typeof p.page === 'number' &&\n typeof p.limit === 'number' &&\n typeof p.total === 'number' &&\n typeof p.totalPages === 'number' &&\n typeof p.hasNextPage === 'boolean' &&\n typeof p.hasPrevPage === 'boolean'\n )\n}\n\nfunction queryRecord(query: BlogListQuery): Record<string, string | undefined> {\n const q: Record<string, string | undefined> = {}\n for (const [k, v] of Object.entries(query)) {\n if (v === undefined) continue\n q[k] = String(v)\n }\n return q\n}\n\nfunction imageProxyPrefix(\n options?: CreateMailooBlogClientOptions\n): string | undefined {\n const raw = options?.imageProxyPrefixWithIdEquals?.trim()\n return raw || undefined\n}\n\nfunction rewritePost(\n post: BlogPostApiData,\n canonicalPrefixWithIdEquals: string,\n proxyPrefixWithIdEquals: string\n): BlogPostApiData {\n return rewriteMailooBlogMediaUrls(post, {\n canonicalPrefixWithIdEquals,\n proxyPrefixWithIdEquals,\n })\n}\n\ntype CategoryDescription = {\n description?: string\n descriptionHtml?: string\n imagePublicEmbedBaseUrl?: string\n}\n\nexport function createMailooBlogClient(\n config: MailooBlogConfig,\n clientOptions?: CreateMailooBlogClientOptions\n) {\n const proxyPrefix = imageProxyPrefix(clientOptions)\n return {\n async listPosts(\n query: BlogListQuery = {},\n options?: MailooBlogFetchOptions\n ): Promise<\n | {\n ok: true\n data: BlogPostApiData[]\n pagination: BlogListPagination\n imagePublicEmbedBaseUrl: string\n status: number\n }\n | { ok: false; status: number }\n > {\n const fetched = await blogFetchJson(\n config,\n '',\n queryRecord(query),\n options\n )\n if (!fetched.ok) return fetched\n const json = fetched.json\n if (json === null || typeof json !== 'object') {\n return { ok: false, status: 500 }\n }\n const body = json as {\n data?: unknown\n pagination?: unknown\n imagePublicEmbedBaseUrl?: unknown\n }\n const imagePublicEmbedBaseUrl =\n typeof body.imagePublicEmbedBaseUrl === 'string'\n ? body.imagePublicEmbedBaseUrl.trim()\n : ''\n if (\n !Array.isArray(body.data) ||\n !isBlogListPagination(body.pagination) ||\n !imagePublicEmbedBaseUrl\n ) {\n return { ok: false, status: 500 }\n }\n const posts = body.data as BlogPostApiData[]\n return {\n ok: true,\n data: proxyPrefix\n ? posts.map((post) =>\n rewritePost(post, imagePublicEmbedBaseUrl, proxyPrefix)\n )\n : posts,\n pagination: body.pagination,\n imagePublicEmbedBaseUrl,\n status: fetched.status,\n }\n },\n\n async getPostBySlug(\n slug: string,\n locale?: string,\n options?: MailooBlogFetchOptions\n ) {\n const result = await blogGet<BlogPostApiData>(\n config,\n `/slug/${encodeURIComponent(slug)}`,\n locale ? { locale } : undefined,\n options\n )\n if (!result.ok || !proxyPrefix) return result\n const canonical = result.data.imagePublicEmbedBaseUrl?.trim()\n if (!canonical) return { ok: false as const, status: 500 }\n return {\n ...result,\n data: rewritePost(result.data, canonical, proxyPrefix),\n }\n },\n\n async getPostJsonLd(\n slug: string,\n locale?: string,\n options?: MailooBlogFetchOptions\n ) {\n const result = await blogGet<{ jsonLd: Record<string, unknown> }>(\n config,\n `/slug/${encodeURIComponent(slug)}/json-ld`,\n locale ? { locale } : undefined,\n options\n )\n if (!result.ok || !proxyPrefix) return result\n const rewritten = rewriteMailooPublicImageUrlsAnyHost(\n JSON.stringify(result.data.jsonLd),\n proxyPrefix\n )\n try {\n return {\n ...result,\n data: { jsonLd: JSON.parse(rewritten) as Record<string, unknown> },\n }\n } catch {\n return { ok: false as const, status: 500 }\n }\n },\n\n async listCategories(locale?: string, options?: MailooBlogFetchOptions) {\n return blogGet<BlogCategoryApi[]>(\n config,\n '/categories',\n locale ? { locale } : undefined,\n options\n )\n },\n\n async listTags(locale?: string, options?: MailooBlogFetchOptions) {\n return blogGet<BlogTagApi[]>(\n config,\n '/tags',\n locale ? { locale } : undefined,\n options\n )\n },\n\n async getTagBySlug(\n slug: string,\n locale?: string,\n options?: MailooBlogFetchOptions\n ) {\n return blogGet<BlogTagApi>(\n config,\n `/tags/slug/${encodeURIComponent(slug)}`,\n locale ? { locale } : undefined,\n options\n )\n },\n\n async getCategoryDescription(\n slug: string,\n locale?: string,\n options?: MailooBlogFetchOptions\n ) {\n const result = await blogGet<CategoryDescription>(\n config,\n `/categories/slug/${encodeURIComponent(slug)}/description`,\n locale ? { locale } : undefined,\n options\n )\n if (!result.ok || !proxyPrefix) return result\n const canonical = result.data.imagePublicEmbedBaseUrl?.trim()\n if (!canonical) return { ok: false as const, status: 500 }\n const opts = {\n canonicalPrefixWithIdEquals: canonical,\n proxyPrefixWithIdEquals: proxyPrefix,\n }\n return {\n ...result,\n data: {\n ...result.data,\n description:\n typeof result.data.description === 'string'\n ? rewriteMailooPublicImageUrls(result.data.description, opts)\n : result.data.description,\n descriptionHtml:\n typeof result.data.descriptionHtml === 'string'\n ? rewriteMailooPublicImageUrls(result.data.descriptionHtml, opts)\n : result.data.descriptionHtml,\n },\n }\n },\n\n async setReaction(slug: string, input: SetBlogReactionInput) {\n return blogPostJson(\n config,\n `/slug/${encodeURIComponent(slug)}/reaction`,\n input\n )\n },\n\n async setFeedback(slug: string, input: SetBlogFeedbackInput) {\n return blogPostJson(\n config,\n `/slug/${encodeURIComponent(slug)}/feedback`,\n input\n )\n },\n }\n}\n\nexport type MailooBlogClient = ReturnType<typeof createMailooBlogClient>\n","import { unstable_rethrow } from 'next/navigation'\nimport { getRequestOriginBaseUrl } from '@mailoo/next-core/request-origin'\nimport type { BlogPostApiData } from '../types.js'\n\n/**\n * Fetch a blog post via the host app's same-origin BFF\n * (`GET /api/v1/blog/slug/:slug`).\n * Never throws on network / Host / JSON failures — returns `{ ok: false }`.\n */\nexport async function fetchBlogPostBySlug(\n slug: string,\n locale: string,\n options?: { bffBasePath?: string; revalidate?: number }\n): Promise<\n { ok: true; data: BlogPostApiData } | { ok: false; status: number }\n> {\n try {\n const base = await getRequestOriginBaseUrl()\n const bffBase = options?.bffBasePath ?? '/api/v1/blog'\n const revalidate = options?.revalidate ?? 60\n const slugUrl = new URL(\n `${base}${bffBase}/slug/${encodeURIComponent(slug)}`\n )\n if (locale) slugUrl.searchParams.set('locale', locale)\n const res = await fetch(slugUrl.toString(), {\n next: { revalidate },\n } as RequestInit)\n\n if (!res.ok) {\n return { ok: false, status: res.status }\n }\n\n const json = (await res.json()) as { data?: BlogPostApiData }\n const data = json?.data\n if (!data?.id) {\n return { ok: false, status: 404 }\n }\n\n return { ok: true, data }\n } catch (error) {\n unstable_rethrow(error)\n return { ok: false, status: 500 }\n }\n}\n\n/**\n * Fetch BlogPosting JSON-LD via same-origin BFF.\n * Returns null on any failure (never throws).\n */\nexport async function fetchBlogPostJsonLd(\n slug: string,\n locale: string,\n options?: { bffBasePath?: string; revalidate?: number }\n): Promise<Record<string, unknown> | null> {\n try {\n const base = await getRequestOriginBaseUrl()\n const bffBase = options?.bffBasePath ?? '/api/v1/blog'\n const revalidate = options?.revalidate ?? 60\n const url = new URL(\n `${base}${bffBase}/slug/${encodeURIComponent(slug)}/json-ld`\n )\n if (locale) url.searchParams.set('locale', locale)\n const res = await fetch(url.toString(), {\n next: { revalidate },\n } as RequestInit)\n if (!res.ok) return null\n const json = (await res.json()) as {\n data?: { jsonLd?: Record<string, unknown> }\n }\n return json?.data?.jsonLd ?? null\n } catch (error) {\n unstable_rethrow(error)\n return null\n }\n}\n\n/** Replace Mailoo JSON-LD placeholders with site-specific values. */\nexport function applyJsonLdPlaceholders(\n jsonLd: Record<string, unknown>,\n vars: { canonicalUrl: string; origin: string; locale: string }\n): Record<string, unknown> {\n const raw = JSON.stringify(jsonLd)\n if (!raw.includes('{{canonicalUrl}}') && !raw.includes('{{origin}}')) {\n return jsonLd\n }\n const replaced = raw\n .replaceAll('{{canonicalUrl}}', vars.canonicalUrl)\n .replaceAll('{{origin}}', vars.origin)\n .replaceAll('{{locale}}', vars.locale)\n return JSON.parse(replaced) as Record<string, unknown>\n}\n","/**\n * First absolute image URL for OG/Twitter previews: prefer HTML from the API,\n * then markdown image syntax in content.\n */\nconst HTML_IMG_SRC = /<img[^>]+src=[\"']([^\"']+)[\"']/i\nconst MD_IMAGE = /!\\[[^\\]]*]\\(([^)\\s]+)\\)/\n\nfunction normalizeUrl(raw: string): string | null {\n const u = raw.trim()\n if (!u) return null\n if (\n u.startsWith('http://') ||\n u.startsWith('https://') ||\n u.startsWith('/')\n ) {\n return u\n }\n return null\n}\n\nexport function firstEmbedImageUrlFromArticle(\n htmlContent: string | null | undefined,\n markdownContent: string | null | undefined\n): string | null {\n if (htmlContent) {\n const m = htmlContent.match(HTML_IMG_SRC)\n if (m?.[1]) {\n const url = normalizeUrl(m[1])\n if (url) return url\n }\n }\n if (markdownContent) {\n const m = markdownContent.match(MD_IMAGE)\n if (m?.[1]) {\n const url = normalizeUrl(m[1])\n if (url) return url\n }\n }\n return null\n}\n\n/**\n * Default Tailwind Typography class string for article body.\n * Hosts may pass a custom `className` instead.\n */\nexport const BLOG_ARTICLE_PROSE_CLASSNAME = [\n 'prose',\n 'prose-gray',\n 'prose-lg',\n 'max-w-none',\n 'dark:prose-invert',\n 'prose-a:text-blue-600',\n 'hover:prose-a:text-blue-700',\n 'dark:prose-a:text-blue-400',\n 'dark:hover:prose-a:text-blue-300',\n 'prose-blockquote:border-blue-500',\n 'prose-img:rounded-lg',\n 'prose-img:shadow-sm',\n].join(' ')\n"],"mappings":"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","names":["import_navigation"]}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { d as BlogListQuery, M as MailooBlogFetchOptions, B as BlogPostApiData, e as BlogListPagination, b as BlogCategoryApi, c as BlogTagApi, S as SetBlogReactionInput, h as BlogArticleMarkState, i as SetBlogFeedbackInput } from '../utils-DXbN9MTi.cjs';
|
|
2
|
+
export { j as BLOG_ARTICLE_PROSE_CLASSNAME, g as BlogArticleMarkCounts, a as BlogPostLinkedLinkApi, f as BlogPostReactionSentiment, k as firstEmbedImageUrlFromArticle } from '../utils-DXbN9MTi.cjs';
|
|
3
|
+
import { MailooIntegrationConfig } from '@mailoo/next-core';
|
|
4
|
+
|
|
5
|
+
type MailooBlogConfig = MailooIntegrationConfig;
|
|
6
|
+
|
|
7
|
+
/** Host image proxy. Omit to keep canonical Mailoo embed URLs. */
|
|
8
|
+
type CreateMailooBlogClientOptions = {
|
|
9
|
+
/**
|
|
10
|
+
* Integrator prefix ending with `?id=` (absolute or same-origin path).
|
|
11
|
+
* Example: `/api/v1/images/public/content?id=`
|
|
12
|
+
*/
|
|
13
|
+
imageProxyPrefixWithIdEquals?: string;
|
|
14
|
+
};
|
|
15
|
+
/** Read MAILOO_BLOG_* env (API, API_KEY, PROJECT_UID, INTEGRATION_ID). */
|
|
16
|
+
declare function getMailooBlogConfigFromEnv(): MailooBlogConfig | null;
|
|
17
|
+
type CategoryDescription = {
|
|
18
|
+
description?: string;
|
|
19
|
+
descriptionHtml?: string;
|
|
20
|
+
imagePublicEmbedBaseUrl?: string;
|
|
21
|
+
};
|
|
22
|
+
declare function createMailooBlogClient(config: MailooBlogConfig, clientOptions?: CreateMailooBlogClientOptions): {
|
|
23
|
+
listPosts(query?: BlogListQuery, options?: MailooBlogFetchOptions): Promise<{
|
|
24
|
+
ok: true;
|
|
25
|
+
data: BlogPostApiData[];
|
|
26
|
+
pagination: BlogListPagination;
|
|
27
|
+
imagePublicEmbedBaseUrl: string;
|
|
28
|
+
status: number;
|
|
29
|
+
} | {
|
|
30
|
+
ok: false;
|
|
31
|
+
status: number;
|
|
32
|
+
}>;
|
|
33
|
+
getPostBySlug(slug: string, locale?: string, options?: MailooBlogFetchOptions): Promise<{
|
|
34
|
+
ok: false;
|
|
35
|
+
status: number;
|
|
36
|
+
} | {
|
|
37
|
+
ok: true;
|
|
38
|
+
data: BlogPostApiData;
|
|
39
|
+
status: number;
|
|
40
|
+
}>;
|
|
41
|
+
getPostJsonLd(slug: string, locale?: string, options?: MailooBlogFetchOptions): Promise<{
|
|
42
|
+
ok: false;
|
|
43
|
+
status: number;
|
|
44
|
+
} | {
|
|
45
|
+
ok: true;
|
|
46
|
+
data: {
|
|
47
|
+
jsonLd: Record<string, unknown>;
|
|
48
|
+
};
|
|
49
|
+
status: number;
|
|
50
|
+
}>;
|
|
51
|
+
listCategories(locale?: string, options?: MailooBlogFetchOptions): Promise<{
|
|
52
|
+
ok: false;
|
|
53
|
+
status: number;
|
|
54
|
+
} | {
|
|
55
|
+
ok: true;
|
|
56
|
+
data: BlogCategoryApi[];
|
|
57
|
+
status: number;
|
|
58
|
+
}>;
|
|
59
|
+
listTags(locale?: string, options?: MailooBlogFetchOptions): Promise<{
|
|
60
|
+
ok: false;
|
|
61
|
+
status: number;
|
|
62
|
+
} | {
|
|
63
|
+
ok: true;
|
|
64
|
+
data: BlogTagApi[];
|
|
65
|
+
status: number;
|
|
66
|
+
}>;
|
|
67
|
+
getTagBySlug(slug: string, locale?: string, options?: MailooBlogFetchOptions): Promise<{
|
|
68
|
+
ok: false;
|
|
69
|
+
status: number;
|
|
70
|
+
} | {
|
|
71
|
+
ok: true;
|
|
72
|
+
data: BlogTagApi;
|
|
73
|
+
status: number;
|
|
74
|
+
}>;
|
|
75
|
+
getCategoryDescription(slug: string, locale?: string, options?: MailooBlogFetchOptions): Promise<{
|
|
76
|
+
ok: false;
|
|
77
|
+
status: number;
|
|
78
|
+
} | {
|
|
79
|
+
ok: true;
|
|
80
|
+
data: CategoryDescription;
|
|
81
|
+
status: number;
|
|
82
|
+
}>;
|
|
83
|
+
setReaction(slug: string, input: SetBlogReactionInput): Promise<{
|
|
84
|
+
ok: true;
|
|
85
|
+
data: BlogArticleMarkState;
|
|
86
|
+
status: number;
|
|
87
|
+
} | {
|
|
88
|
+
ok: false;
|
|
89
|
+
status: number;
|
|
90
|
+
}>;
|
|
91
|
+
setFeedback(slug: string, input: SetBlogFeedbackInput): Promise<{
|
|
92
|
+
ok: true;
|
|
93
|
+
data: BlogArticleMarkState;
|
|
94
|
+
status: number;
|
|
95
|
+
} | {
|
|
96
|
+
ok: false;
|
|
97
|
+
status: number;
|
|
98
|
+
}>;
|
|
99
|
+
};
|
|
100
|
+
type MailooBlogClient = ReturnType<typeof createMailooBlogClient>;
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Fetch a blog post via the host app's same-origin BFF
|
|
104
|
+
* (`GET /api/v1/blog/slug/:slug`).
|
|
105
|
+
* Never throws on network / Host / JSON failures — returns `{ ok: false }`.
|
|
106
|
+
*/
|
|
107
|
+
declare function fetchBlogPostBySlug(slug: string, locale: string, options?: {
|
|
108
|
+
bffBasePath?: string;
|
|
109
|
+
revalidate?: number;
|
|
110
|
+
}): Promise<{
|
|
111
|
+
ok: true;
|
|
112
|
+
data: BlogPostApiData;
|
|
113
|
+
} | {
|
|
114
|
+
ok: false;
|
|
115
|
+
status: number;
|
|
116
|
+
}>;
|
|
117
|
+
/**
|
|
118
|
+
* Fetch BlogPosting JSON-LD via same-origin BFF.
|
|
119
|
+
* Returns null on any failure (never throws).
|
|
120
|
+
*/
|
|
121
|
+
declare function fetchBlogPostJsonLd(slug: string, locale: string, options?: {
|
|
122
|
+
bffBasePath?: string;
|
|
123
|
+
revalidate?: number;
|
|
124
|
+
}): Promise<Record<string, unknown> | null>;
|
|
125
|
+
/** Replace Mailoo JSON-LD placeholders with site-specific values. */
|
|
126
|
+
declare function applyJsonLdPlaceholders(jsonLd: Record<string, unknown>, vars: {
|
|
127
|
+
canonicalUrl: string;
|
|
128
|
+
origin: string;
|
|
129
|
+
locale: string;
|
|
130
|
+
}): Record<string, unknown>;
|
|
131
|
+
|
|
132
|
+
export { BlogArticleMarkState, BlogCategoryApi, BlogListPagination, BlogListQuery, BlogPostApiData, BlogTagApi, type CreateMailooBlogClientOptions, type MailooBlogClient, type MailooBlogConfig, MailooBlogFetchOptions, SetBlogFeedbackInput, SetBlogReactionInput, applyJsonLdPlaceholders, createMailooBlogClient, fetchBlogPostBySlug, fetchBlogPostJsonLd, getMailooBlogConfigFromEnv };
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { d as BlogListQuery, M as MailooBlogFetchOptions, B as BlogPostApiData, e as BlogListPagination, b as BlogCategoryApi, c as BlogTagApi, S as SetBlogReactionInput, h as BlogArticleMarkState, i as SetBlogFeedbackInput } from '../utils-DXbN9MTi.js';
|
|
2
|
+
export { j as BLOG_ARTICLE_PROSE_CLASSNAME, g as BlogArticleMarkCounts, a as BlogPostLinkedLinkApi, f as BlogPostReactionSentiment, k as firstEmbedImageUrlFromArticle } from '../utils-DXbN9MTi.js';
|
|
3
|
+
import { MailooIntegrationConfig } from '@mailoo/next-core';
|
|
4
|
+
|
|
5
|
+
type MailooBlogConfig = MailooIntegrationConfig;
|
|
6
|
+
|
|
7
|
+
/** Host image proxy. Omit to keep canonical Mailoo embed URLs. */
|
|
8
|
+
type CreateMailooBlogClientOptions = {
|
|
9
|
+
/**
|
|
10
|
+
* Integrator prefix ending with `?id=` (absolute or same-origin path).
|
|
11
|
+
* Example: `/api/v1/images/public/content?id=`
|
|
12
|
+
*/
|
|
13
|
+
imageProxyPrefixWithIdEquals?: string;
|
|
14
|
+
};
|
|
15
|
+
/** Read MAILOO_BLOG_* env (API, API_KEY, PROJECT_UID, INTEGRATION_ID). */
|
|
16
|
+
declare function getMailooBlogConfigFromEnv(): MailooBlogConfig | null;
|
|
17
|
+
type CategoryDescription = {
|
|
18
|
+
description?: string;
|
|
19
|
+
descriptionHtml?: string;
|
|
20
|
+
imagePublicEmbedBaseUrl?: string;
|
|
21
|
+
};
|
|
22
|
+
declare function createMailooBlogClient(config: MailooBlogConfig, clientOptions?: CreateMailooBlogClientOptions): {
|
|
23
|
+
listPosts(query?: BlogListQuery, options?: MailooBlogFetchOptions): Promise<{
|
|
24
|
+
ok: true;
|
|
25
|
+
data: BlogPostApiData[];
|
|
26
|
+
pagination: BlogListPagination;
|
|
27
|
+
imagePublicEmbedBaseUrl: string;
|
|
28
|
+
status: number;
|
|
29
|
+
} | {
|
|
30
|
+
ok: false;
|
|
31
|
+
status: number;
|
|
32
|
+
}>;
|
|
33
|
+
getPostBySlug(slug: string, locale?: string, options?: MailooBlogFetchOptions): Promise<{
|
|
34
|
+
ok: false;
|
|
35
|
+
status: number;
|
|
36
|
+
} | {
|
|
37
|
+
ok: true;
|
|
38
|
+
data: BlogPostApiData;
|
|
39
|
+
status: number;
|
|
40
|
+
}>;
|
|
41
|
+
getPostJsonLd(slug: string, locale?: string, options?: MailooBlogFetchOptions): Promise<{
|
|
42
|
+
ok: false;
|
|
43
|
+
status: number;
|
|
44
|
+
} | {
|
|
45
|
+
ok: true;
|
|
46
|
+
data: {
|
|
47
|
+
jsonLd: Record<string, unknown>;
|
|
48
|
+
};
|
|
49
|
+
status: number;
|
|
50
|
+
}>;
|
|
51
|
+
listCategories(locale?: string, options?: MailooBlogFetchOptions): Promise<{
|
|
52
|
+
ok: false;
|
|
53
|
+
status: number;
|
|
54
|
+
} | {
|
|
55
|
+
ok: true;
|
|
56
|
+
data: BlogCategoryApi[];
|
|
57
|
+
status: number;
|
|
58
|
+
}>;
|
|
59
|
+
listTags(locale?: string, options?: MailooBlogFetchOptions): Promise<{
|
|
60
|
+
ok: false;
|
|
61
|
+
status: number;
|
|
62
|
+
} | {
|
|
63
|
+
ok: true;
|
|
64
|
+
data: BlogTagApi[];
|
|
65
|
+
status: number;
|
|
66
|
+
}>;
|
|
67
|
+
getTagBySlug(slug: string, locale?: string, options?: MailooBlogFetchOptions): Promise<{
|
|
68
|
+
ok: false;
|
|
69
|
+
status: number;
|
|
70
|
+
} | {
|
|
71
|
+
ok: true;
|
|
72
|
+
data: BlogTagApi;
|
|
73
|
+
status: number;
|
|
74
|
+
}>;
|
|
75
|
+
getCategoryDescription(slug: string, locale?: string, options?: MailooBlogFetchOptions): Promise<{
|
|
76
|
+
ok: false;
|
|
77
|
+
status: number;
|
|
78
|
+
} | {
|
|
79
|
+
ok: true;
|
|
80
|
+
data: CategoryDescription;
|
|
81
|
+
status: number;
|
|
82
|
+
}>;
|
|
83
|
+
setReaction(slug: string, input: SetBlogReactionInput): Promise<{
|
|
84
|
+
ok: true;
|
|
85
|
+
data: BlogArticleMarkState;
|
|
86
|
+
status: number;
|
|
87
|
+
} | {
|
|
88
|
+
ok: false;
|
|
89
|
+
status: number;
|
|
90
|
+
}>;
|
|
91
|
+
setFeedback(slug: string, input: SetBlogFeedbackInput): Promise<{
|
|
92
|
+
ok: true;
|
|
93
|
+
data: BlogArticleMarkState;
|
|
94
|
+
status: number;
|
|
95
|
+
} | {
|
|
96
|
+
ok: false;
|
|
97
|
+
status: number;
|
|
98
|
+
}>;
|
|
99
|
+
};
|
|
100
|
+
type MailooBlogClient = ReturnType<typeof createMailooBlogClient>;
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Fetch a blog post via the host app's same-origin BFF
|
|
104
|
+
* (`GET /api/v1/blog/slug/:slug`).
|
|
105
|
+
* Never throws on network / Host / JSON failures — returns `{ ok: false }`.
|
|
106
|
+
*/
|
|
107
|
+
declare function fetchBlogPostBySlug(slug: string, locale: string, options?: {
|
|
108
|
+
bffBasePath?: string;
|
|
109
|
+
revalidate?: number;
|
|
110
|
+
}): Promise<{
|
|
111
|
+
ok: true;
|
|
112
|
+
data: BlogPostApiData;
|
|
113
|
+
} | {
|
|
114
|
+
ok: false;
|
|
115
|
+
status: number;
|
|
116
|
+
}>;
|
|
117
|
+
/**
|
|
118
|
+
* Fetch BlogPosting JSON-LD via same-origin BFF.
|
|
119
|
+
* Returns null on any failure (never throws).
|
|
120
|
+
*/
|
|
121
|
+
declare function fetchBlogPostJsonLd(slug: string, locale: string, options?: {
|
|
122
|
+
bffBasePath?: string;
|
|
123
|
+
revalidate?: number;
|
|
124
|
+
}): Promise<Record<string, unknown> | null>;
|
|
125
|
+
/** Replace Mailoo JSON-LD placeholders with site-specific values. */
|
|
126
|
+
declare function applyJsonLdPlaceholders(jsonLd: Record<string, unknown>, vars: {
|
|
127
|
+
canonicalUrl: string;
|
|
128
|
+
origin: string;
|
|
129
|
+
locale: string;
|
|
130
|
+
}): Record<string, unknown>;
|
|
131
|
+
|
|
132
|
+
export { BlogArticleMarkState, BlogCategoryApi, BlogListPagination, BlogListQuery, BlogPostApiData, BlogTagApi, type CreateMailooBlogClientOptions, type MailooBlogClient, type MailooBlogConfig, MailooBlogFetchOptions, SetBlogFeedbackInput, SetBlogReactionInput, applyJsonLdPlaceholders, createMailooBlogClient, fetchBlogPostBySlug, fetchBlogPostJsonLd, getMailooBlogConfigFromEnv };
|
|
@@ -0,0 +1,339 @@
|
|
|
1
|
+
// src/server/client.ts
|
|
2
|
+
import { unstable_rethrow } from "next/navigation";
|
|
3
|
+
import { readMailooIntegrationConfig } from "@mailoo/next-core";
|
|
4
|
+
import {
|
|
5
|
+
rewriteMailooBlogMediaUrls,
|
|
6
|
+
rewriteMailooPublicImageUrls,
|
|
7
|
+
rewriteMailooPublicImageUrlsAnyHost
|
|
8
|
+
} from "@mailoo/images";
|
|
9
|
+
var DEFAULT_REVALIDATE = 60;
|
|
10
|
+
function getMailooBlogConfigFromEnv() {
|
|
11
|
+
return readMailooIntegrationConfig({ prefix: "MAILOO_BLOG" });
|
|
12
|
+
}
|
|
13
|
+
function blogBasePath(config) {
|
|
14
|
+
return `${config.apiBaseUrl}/api/v1/blog/${config.projectUid}/integrations/${config.integrationId}`;
|
|
15
|
+
}
|
|
16
|
+
function resolveRevalidate(options) {
|
|
17
|
+
return options?.revalidate ?? DEFAULT_REVALIDATE;
|
|
18
|
+
}
|
|
19
|
+
async function blogFetchJson(config, path, query, options) {
|
|
20
|
+
const url = new URL(`${blogBasePath(config)}${path}`);
|
|
21
|
+
if (query) {
|
|
22
|
+
for (const [k, v] of Object.entries(query)) {
|
|
23
|
+
if (v !== void 0 && v !== "") url.searchParams.set(k, v);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
try {
|
|
27
|
+
const revalidate = resolveRevalidate(options);
|
|
28
|
+
const res = await fetch(url.toString(), {
|
|
29
|
+
method: "GET",
|
|
30
|
+
headers: {
|
|
31
|
+
"Content-Type": "application/json",
|
|
32
|
+
"X-API-Key": config.apiKey
|
|
33
|
+
},
|
|
34
|
+
next: { revalidate }
|
|
35
|
+
});
|
|
36
|
+
if (!res.ok) {
|
|
37
|
+
return { ok: false, status: res.status };
|
|
38
|
+
}
|
|
39
|
+
return { ok: true, json: await res.json(), status: res.status };
|
|
40
|
+
} catch (error) {
|
|
41
|
+
unstable_rethrow(error);
|
|
42
|
+
return { ok: false, status: 500 };
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
async function blogPostJson(config, path, body) {
|
|
46
|
+
try {
|
|
47
|
+
const res = await fetch(`${blogBasePath(config)}${path}`, {
|
|
48
|
+
method: "POST",
|
|
49
|
+
headers: {
|
|
50
|
+
"Content-Type": "application/json",
|
|
51
|
+
"X-API-Key": config.apiKey
|
|
52
|
+
},
|
|
53
|
+
body: JSON.stringify(body),
|
|
54
|
+
cache: "no-store"
|
|
55
|
+
});
|
|
56
|
+
if (!res.ok) {
|
|
57
|
+
return { ok: false, status: res.status };
|
|
58
|
+
}
|
|
59
|
+
const json = await res.json();
|
|
60
|
+
if (!json.data || typeof json.data !== "object") {
|
|
61
|
+
return { ok: false, status: 500 };
|
|
62
|
+
}
|
|
63
|
+
return { ok: true, data: json.data, status: res.status };
|
|
64
|
+
} catch (error) {
|
|
65
|
+
unstable_rethrow(error);
|
|
66
|
+
return { ok: false, status: 500 };
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
async function blogGet(config, path, query, options) {
|
|
70
|
+
const fetched = await blogFetchJson(config, path, query, options);
|
|
71
|
+
if (!fetched.ok) return fetched;
|
|
72
|
+
const json = fetched.json;
|
|
73
|
+
const data = json.data !== void 0 ? json.data : json;
|
|
74
|
+
return { ok: true, data, status: fetched.status };
|
|
75
|
+
}
|
|
76
|
+
function isBlogListPagination(value) {
|
|
77
|
+
if (value === null || typeof value !== "object") return false;
|
|
78
|
+
const p = value;
|
|
79
|
+
return typeof p.page === "number" && typeof p.limit === "number" && typeof p.total === "number" && typeof p.totalPages === "number" && typeof p.hasNextPage === "boolean" && typeof p.hasPrevPage === "boolean";
|
|
80
|
+
}
|
|
81
|
+
function queryRecord(query) {
|
|
82
|
+
const q = {};
|
|
83
|
+
for (const [k, v] of Object.entries(query)) {
|
|
84
|
+
if (v === void 0) continue;
|
|
85
|
+
q[k] = String(v);
|
|
86
|
+
}
|
|
87
|
+
return q;
|
|
88
|
+
}
|
|
89
|
+
function imageProxyPrefix(options) {
|
|
90
|
+
const raw = options?.imageProxyPrefixWithIdEquals?.trim();
|
|
91
|
+
return raw || void 0;
|
|
92
|
+
}
|
|
93
|
+
function rewritePost(post, canonicalPrefixWithIdEquals, proxyPrefixWithIdEquals) {
|
|
94
|
+
return rewriteMailooBlogMediaUrls(post, {
|
|
95
|
+
canonicalPrefixWithIdEquals,
|
|
96
|
+
proxyPrefixWithIdEquals
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
function createMailooBlogClient(config, clientOptions) {
|
|
100
|
+
const proxyPrefix = imageProxyPrefix(clientOptions);
|
|
101
|
+
return {
|
|
102
|
+
async listPosts(query = {}, options) {
|
|
103
|
+
const fetched = await blogFetchJson(
|
|
104
|
+
config,
|
|
105
|
+
"",
|
|
106
|
+
queryRecord(query),
|
|
107
|
+
options
|
|
108
|
+
);
|
|
109
|
+
if (!fetched.ok) return fetched;
|
|
110
|
+
const json = fetched.json;
|
|
111
|
+
if (json === null || typeof json !== "object") {
|
|
112
|
+
return { ok: false, status: 500 };
|
|
113
|
+
}
|
|
114
|
+
const body = json;
|
|
115
|
+
const imagePublicEmbedBaseUrl = typeof body.imagePublicEmbedBaseUrl === "string" ? body.imagePublicEmbedBaseUrl.trim() : "";
|
|
116
|
+
if (!Array.isArray(body.data) || !isBlogListPagination(body.pagination) || !imagePublicEmbedBaseUrl) {
|
|
117
|
+
return { ok: false, status: 500 };
|
|
118
|
+
}
|
|
119
|
+
const posts = body.data;
|
|
120
|
+
return {
|
|
121
|
+
ok: true,
|
|
122
|
+
data: proxyPrefix ? posts.map(
|
|
123
|
+
(post) => rewritePost(post, imagePublicEmbedBaseUrl, proxyPrefix)
|
|
124
|
+
) : posts,
|
|
125
|
+
pagination: body.pagination,
|
|
126
|
+
imagePublicEmbedBaseUrl,
|
|
127
|
+
status: fetched.status
|
|
128
|
+
};
|
|
129
|
+
},
|
|
130
|
+
async getPostBySlug(slug, locale, options) {
|
|
131
|
+
const result = await blogGet(
|
|
132
|
+
config,
|
|
133
|
+
`/slug/${encodeURIComponent(slug)}`,
|
|
134
|
+
locale ? { locale } : void 0,
|
|
135
|
+
options
|
|
136
|
+
);
|
|
137
|
+
if (!result.ok || !proxyPrefix) return result;
|
|
138
|
+
const canonical = result.data.imagePublicEmbedBaseUrl?.trim();
|
|
139
|
+
if (!canonical) return { ok: false, status: 500 };
|
|
140
|
+
return {
|
|
141
|
+
...result,
|
|
142
|
+
data: rewritePost(result.data, canonical, proxyPrefix)
|
|
143
|
+
};
|
|
144
|
+
},
|
|
145
|
+
async getPostJsonLd(slug, locale, options) {
|
|
146
|
+
const result = await blogGet(
|
|
147
|
+
config,
|
|
148
|
+
`/slug/${encodeURIComponent(slug)}/json-ld`,
|
|
149
|
+
locale ? { locale } : void 0,
|
|
150
|
+
options
|
|
151
|
+
);
|
|
152
|
+
if (!result.ok || !proxyPrefix) return result;
|
|
153
|
+
const rewritten = rewriteMailooPublicImageUrlsAnyHost(
|
|
154
|
+
JSON.stringify(result.data.jsonLd),
|
|
155
|
+
proxyPrefix
|
|
156
|
+
);
|
|
157
|
+
try {
|
|
158
|
+
return {
|
|
159
|
+
...result,
|
|
160
|
+
data: { jsonLd: JSON.parse(rewritten) }
|
|
161
|
+
};
|
|
162
|
+
} catch {
|
|
163
|
+
return { ok: false, status: 500 };
|
|
164
|
+
}
|
|
165
|
+
},
|
|
166
|
+
async listCategories(locale, options) {
|
|
167
|
+
return blogGet(
|
|
168
|
+
config,
|
|
169
|
+
"/categories",
|
|
170
|
+
locale ? { locale } : void 0,
|
|
171
|
+
options
|
|
172
|
+
);
|
|
173
|
+
},
|
|
174
|
+
async listTags(locale, options) {
|
|
175
|
+
return blogGet(
|
|
176
|
+
config,
|
|
177
|
+
"/tags",
|
|
178
|
+
locale ? { locale } : void 0,
|
|
179
|
+
options
|
|
180
|
+
);
|
|
181
|
+
},
|
|
182
|
+
async getTagBySlug(slug, locale, options) {
|
|
183
|
+
return blogGet(
|
|
184
|
+
config,
|
|
185
|
+
`/tags/slug/${encodeURIComponent(slug)}`,
|
|
186
|
+
locale ? { locale } : void 0,
|
|
187
|
+
options
|
|
188
|
+
);
|
|
189
|
+
},
|
|
190
|
+
async getCategoryDescription(slug, locale, options) {
|
|
191
|
+
const result = await blogGet(
|
|
192
|
+
config,
|
|
193
|
+
`/categories/slug/${encodeURIComponent(slug)}/description`,
|
|
194
|
+
locale ? { locale } : void 0,
|
|
195
|
+
options
|
|
196
|
+
);
|
|
197
|
+
if (!result.ok || !proxyPrefix) return result;
|
|
198
|
+
const canonical = result.data.imagePublicEmbedBaseUrl?.trim();
|
|
199
|
+
if (!canonical) return { ok: false, status: 500 };
|
|
200
|
+
const opts = {
|
|
201
|
+
canonicalPrefixWithIdEquals: canonical,
|
|
202
|
+
proxyPrefixWithIdEquals: proxyPrefix
|
|
203
|
+
};
|
|
204
|
+
return {
|
|
205
|
+
...result,
|
|
206
|
+
data: {
|
|
207
|
+
...result.data,
|
|
208
|
+
description: typeof result.data.description === "string" ? rewriteMailooPublicImageUrls(result.data.description, opts) : result.data.description,
|
|
209
|
+
descriptionHtml: typeof result.data.descriptionHtml === "string" ? rewriteMailooPublicImageUrls(result.data.descriptionHtml, opts) : result.data.descriptionHtml
|
|
210
|
+
}
|
|
211
|
+
};
|
|
212
|
+
},
|
|
213
|
+
async setReaction(slug, input) {
|
|
214
|
+
return blogPostJson(
|
|
215
|
+
config,
|
|
216
|
+
`/slug/${encodeURIComponent(slug)}/reaction`,
|
|
217
|
+
input
|
|
218
|
+
);
|
|
219
|
+
},
|
|
220
|
+
async setFeedback(slug, input) {
|
|
221
|
+
return blogPostJson(
|
|
222
|
+
config,
|
|
223
|
+
`/slug/${encodeURIComponent(slug)}/feedback`,
|
|
224
|
+
input
|
|
225
|
+
);
|
|
226
|
+
}
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// src/server/fetch-bff.ts
|
|
231
|
+
import { unstable_rethrow as unstable_rethrow2 } from "next/navigation";
|
|
232
|
+
import { getRequestOriginBaseUrl } from "@mailoo/next-core/request-origin";
|
|
233
|
+
async function fetchBlogPostBySlug(slug, locale, options) {
|
|
234
|
+
try {
|
|
235
|
+
const base = await getRequestOriginBaseUrl();
|
|
236
|
+
const bffBase = options?.bffBasePath ?? "/api/v1/blog";
|
|
237
|
+
const revalidate = options?.revalidate ?? 60;
|
|
238
|
+
const slugUrl = new URL(
|
|
239
|
+
`${base}${bffBase}/slug/${encodeURIComponent(slug)}`
|
|
240
|
+
);
|
|
241
|
+
if (locale) slugUrl.searchParams.set("locale", locale);
|
|
242
|
+
const res = await fetch(slugUrl.toString(), {
|
|
243
|
+
next: { revalidate }
|
|
244
|
+
});
|
|
245
|
+
if (!res.ok) {
|
|
246
|
+
return { ok: false, status: res.status };
|
|
247
|
+
}
|
|
248
|
+
const json = await res.json();
|
|
249
|
+
const data = json?.data;
|
|
250
|
+
if (!data?.id) {
|
|
251
|
+
return { ok: false, status: 404 };
|
|
252
|
+
}
|
|
253
|
+
return { ok: true, data };
|
|
254
|
+
} catch (error) {
|
|
255
|
+
unstable_rethrow2(error);
|
|
256
|
+
return { ok: false, status: 500 };
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
async function fetchBlogPostJsonLd(slug, locale, options) {
|
|
260
|
+
try {
|
|
261
|
+
const base = await getRequestOriginBaseUrl();
|
|
262
|
+
const bffBase = options?.bffBasePath ?? "/api/v1/blog";
|
|
263
|
+
const revalidate = options?.revalidate ?? 60;
|
|
264
|
+
const url = new URL(
|
|
265
|
+
`${base}${bffBase}/slug/${encodeURIComponent(slug)}/json-ld`
|
|
266
|
+
);
|
|
267
|
+
if (locale) url.searchParams.set("locale", locale);
|
|
268
|
+
const res = await fetch(url.toString(), {
|
|
269
|
+
next: { revalidate }
|
|
270
|
+
});
|
|
271
|
+
if (!res.ok) return null;
|
|
272
|
+
const json = await res.json();
|
|
273
|
+
return json?.data?.jsonLd ?? null;
|
|
274
|
+
} catch (error) {
|
|
275
|
+
unstable_rethrow2(error);
|
|
276
|
+
return null;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
function applyJsonLdPlaceholders(jsonLd, vars) {
|
|
280
|
+
const raw = JSON.stringify(jsonLd);
|
|
281
|
+
if (!raw.includes("{{canonicalUrl}}") && !raw.includes("{{origin}}")) {
|
|
282
|
+
return jsonLd;
|
|
283
|
+
}
|
|
284
|
+
const replaced = raw.replaceAll("{{canonicalUrl}}", vars.canonicalUrl).replaceAll("{{origin}}", vars.origin).replaceAll("{{locale}}", vars.locale);
|
|
285
|
+
return JSON.parse(replaced);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// src/server/utils.ts
|
|
289
|
+
var HTML_IMG_SRC = /<img[^>]+src=["']([^"']+)["']/i;
|
|
290
|
+
var MD_IMAGE = /!\[[^\]]*]\(([^)\s]+)\)/;
|
|
291
|
+
function normalizeUrl(raw) {
|
|
292
|
+
const u = raw.trim();
|
|
293
|
+
if (!u) return null;
|
|
294
|
+
if (u.startsWith("http://") || u.startsWith("https://") || u.startsWith("/")) {
|
|
295
|
+
return u;
|
|
296
|
+
}
|
|
297
|
+
return null;
|
|
298
|
+
}
|
|
299
|
+
function firstEmbedImageUrlFromArticle(htmlContent, markdownContent) {
|
|
300
|
+
if (htmlContent) {
|
|
301
|
+
const m = htmlContent.match(HTML_IMG_SRC);
|
|
302
|
+
if (m?.[1]) {
|
|
303
|
+
const url = normalizeUrl(m[1]);
|
|
304
|
+
if (url) return url;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
if (markdownContent) {
|
|
308
|
+
const m = markdownContent.match(MD_IMAGE);
|
|
309
|
+
if (m?.[1]) {
|
|
310
|
+
const url = normalizeUrl(m[1]);
|
|
311
|
+
if (url) return url;
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
return null;
|
|
315
|
+
}
|
|
316
|
+
var BLOG_ARTICLE_PROSE_CLASSNAME = [
|
|
317
|
+
"prose",
|
|
318
|
+
"prose-gray",
|
|
319
|
+
"prose-lg",
|
|
320
|
+
"max-w-none",
|
|
321
|
+
"dark:prose-invert",
|
|
322
|
+
"prose-a:text-blue-600",
|
|
323
|
+
"hover:prose-a:text-blue-700",
|
|
324
|
+
"dark:prose-a:text-blue-400",
|
|
325
|
+
"dark:hover:prose-a:text-blue-300",
|
|
326
|
+
"prose-blockquote:border-blue-500",
|
|
327
|
+
"prose-img:rounded-lg",
|
|
328
|
+
"prose-img:shadow-sm"
|
|
329
|
+
].join(" ");
|
|
330
|
+
export {
|
|
331
|
+
BLOG_ARTICLE_PROSE_CLASSNAME,
|
|
332
|
+
applyJsonLdPlaceholders,
|
|
333
|
+
createMailooBlogClient,
|
|
334
|
+
fetchBlogPostBySlug,
|
|
335
|
+
fetchBlogPostJsonLd,
|
|
336
|
+
firstEmbedImageUrlFromArticle,
|
|
337
|
+
getMailooBlogConfigFromEnv
|
|
338
|
+
};
|
|
339
|
+
//# sourceMappingURL=index.js.map
|