@autoblogwriter/sdk 1.0.2 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -10
- package/dist/index.cjs +101 -7
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +23 -19
- package/dist/index.d.ts +23 -19
- package/dist/index.js +100 -7
- package/dist/index.js.map +1 -1
- package/dist/metadata-DUQ3-Hhw.d.ts +29 -0
- package/dist/metadata-Ihd3IqKu.d.cts +29 -0
- package/dist/next.cjs +712 -0
- package/dist/next.cjs.map +1 -0
- package/dist/next.d.cts +17 -0
- package/dist/next.d.ts +17 -0
- package/dist/next.js +669 -0
- package/dist/next.js.map +1 -0
- package/dist/react.cjs +183 -0
- package/dist/react.cjs.map +1 -0
- package/dist/react.d.cts +30 -0
- package/dist/react.d.ts +30 -0
- package/dist/react.js +154 -0
- package/dist/react.js.map +1 -0
- package/dist/revalidate.d.cts +19 -1
- package/dist/revalidate.d.ts +19 -1
- package/dist/styles.css +236 -0
- package/dist/{revalidate-445OJMx_.d.cts → types-CCDRs0sO.d.cts} +1 -17
- package/dist/{revalidate-445OJMx_.d.ts → types-CCDRs0sO.d.ts} +1 -17
- package/package.json +26 -2
package/dist/next.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/auth.ts","../src/errors.ts","../src/utils.ts","../src/client.ts","../src/env.ts","../src/metadata.ts","../src/sitemap.ts","../src/robots.ts","../src/next/helpers.ts","../src/revalidate.ts","../src/next/route-handlers.ts"],"sourcesContent":["import { AuthMode } from \"./types\";\n\nexport function buildAuthHeaders(apiKey: string, authMode: AuthMode = \"bearer\"): Record<string, string> {\n if (authMode === \"x-api-key\") {\n return { \"x-api-key\": apiKey };\n }\n return { Authorization: `Bearer ${apiKey}` };\n}\n","export class BlogAutoError extends Error {\n public readonly causeError?: unknown;\n\n constructor(message: string, options?: { cause?: unknown }) {\n super(message);\n this.name = new.target.name;\n this.causeError = options?.cause;\n }\n}\n\nexport class ConfigError extends BlogAutoError {}\n\nexport interface ApiErrorDetails {\n status: number;\n code?: string;\n details?: unknown;\n}\n\nexport class ApiError extends BlogAutoError {\n public readonly status: number;\n public readonly code?: string;\n public readonly details?: unknown;\n\n constructor(message: string, info: ApiErrorDetails, options?: { cause?: unknown }) {\n super(message, options);\n this.status = info.status;\n this.code = info.code;\n this.details = info.details;\n }\n}\n\nexport class NotFoundError extends ApiError {\n constructor(message: string, info?: Partial<ApiErrorDetails>) {\n super(message, { status: info?.status ?? 404, code: info?.code, details: info?.details });\n }\n}\n","import { ApiError, ConfigError } from \"./errors\";\nimport { AuthMode, BlogAutoClientConfig, InternalClientConfig } from \"./types\";\n\nexport const DEFAULT_TIMEOUT_MS = 10_000;\n\nexport function normalizeApiUrl(apiUrl: string): string {\n if (!apiUrl) {\n throw new ConfigError(\"apiUrl is required\");\n }\n\n return apiUrl.replace(/\\/$/, \"\");\n}\n\nexport function resolveClientConfig(config: BlogAutoClientConfig): InternalClientConfig {\n if (!config) {\n throw new ConfigError(\"Client configuration is required\");\n }\n\n const apiKey = config.apiKey?.trim();\n if (!apiKey) {\n throw new ConfigError(\"apiKey is required to authenticate with BlogAuto\");\n }\n\n if (!config.workspaceId && !config.workspaceSlug) {\n throw new ConfigError(\"Provide either workspaceId or workspaceSlug\");\n }\n\n const authMode: AuthMode = config.authMode ?? \"bearer\";\n\n return {\n apiKey,\n apiUrl: normalizeApiUrl(config.apiUrl),\n workspaceId: config.workspaceId,\n workspaceSlug: config.workspaceSlug,\n authMode,\n fetch: config.fetch ?? globalThis.fetch.bind(globalThis),\n headers: { ...(config.headers ?? {}) },\n timeoutMs: config.timeoutMs ?? DEFAULT_TIMEOUT_MS,\n };\n}\n\nexport function buildQuery(params: Record<string, string | number | undefined | null>): string {\n const query = new URLSearchParams();\n Object.entries(params).forEach(([key, value]) => {\n if (value === undefined || value === null || value === \"\") {\n return;\n }\n query.set(key, String(value));\n });\n const qs = query.toString();\n return qs ? `?${qs}` : \"\";\n}\n\nexport async function withTimeout<T>(promise: Promise<T>, timeoutMs?: number): Promise<T> {\n if (!timeoutMs) {\n return promise;\n }\n\n let timeoutId: ReturnType<typeof setTimeout>;\n\n const timeoutPromise = new Promise<never>((_, reject) => {\n timeoutId = setTimeout(() => {\n reject(\n new ApiError(`Request timed out after ${timeoutMs}ms`, {\n status: 408,\n }),\n );\n }, timeoutMs);\n });\n\n try {\n return await Promise.race([promise, timeoutPromise]);\n } finally {\n clearTimeout(timeoutId!);\n }\n}\n\nexport function mergeHeaders(\n ...headerObjects: Array<Record<string, string> | undefined>\n): Record<string, string> {\n return headerObjects.reduce<Record<string, string>>((acc, headers) => {\n if (!headers) {\n return acc;\n }\n Object.entries(headers).forEach(([key, value]) => {\n if (value === undefined || value === null) {\n return;\n }\n acc[key.toLowerCase()] = value;\n });\n return acc;\n }, {});\n}\n","import { buildAuthHeaders } from \"./auth\";\nimport { ApiError, BlogAutoError, ConfigError } from \"./errors\";\nimport { buildQuery, mergeHeaders, resolveClientConfig, withTimeout } from \"./utils\";\nimport type {\n BlogAutoClientConfig,\n BlogPost,\n FetchRequestOptions,\n PaginatedList,\n PostsResponse,\n} from \"./types\";\n\ninterface RequestOptions {\n path: string;\n method?: string;\n query?: Record<string, string | number | undefined | null>;\n body?: unknown;\n allowNotFound?: boolean;\n headers?: Record<string, string>;\n cache?: RequestCache;\n next?: FetchRequestOptions[\"next\"];\n}\n\nexport interface BlogAutoClient {\n getPosts(params?: { limit?: number; cursor?: string } & FetchRequestOptions): Promise<PostsResponse>;\n getPostBySlug(slug: string, options?: FetchRequestOptions): Promise<BlogPost | null>;\n getSitemapEntries(): Promise<Array<{ slug: string; updatedAt: string }>>;\n}\n\nexport function createBlogAutoClient(config: BlogAutoClientConfig): BlogAutoClient {\n const resolved = resolveClientConfig(config);\n const fetchImpl = resolved.fetch;\n const debug =\n typeof process !== \"undefined\" &&\n typeof process.env !== \"undefined\" &&\n process.env.BLOGAUTO_DEBUG === \"true\";\n const debugLog = (...args: unknown[]) => {\n if (debug) {\n // eslint-disable-next-line no-console\n console.log(\"[blogauto]\", ...args);\n }\n };\n\n function ensureWorkspaceSlug(): string {\n if (!resolved.workspaceSlug) {\n throw new ConfigError(\"workspaceSlug is required to call the BlogAuto public API\");\n }\n return resolved.workspaceSlug;\n }\n\n function buildUrl(path: string, query?: Record<string, string | number | undefined | null>): string {\n const qs = buildQuery(query ?? {});\n return `${resolved.apiUrl}${path}${qs}`;\n }\n\n function unwrapSuccessPayload<T>(payload: unknown): T {\n if (payload && typeof payload === \"object\") {\n const maybePayload = payload as { success?: boolean; data?: T; error?: { message?: string } };\n if (\"success\" in maybePayload) {\n if (maybePayload.success === false) {\n throw new ApiError(\n maybePayload.error?.message ?? \"BlogAuto request failed\",\n { status: 500, details: maybePayload },\n );\n }\n if (maybePayload.success && \"data\" in maybePayload) {\n return maybePayload.data as T;\n }\n }\n }\n return payload as T;\n }\n\n async function request<T>(opts: RequestOptions): Promise<T> {\n const method = opts.method ?? \"GET\";\n const url = buildUrl(opts.path, opts.query);\n\n const authHeaders = buildAuthHeaders(resolved.apiKey, resolved.authMode);\n const baseHeaders = mergeHeaders(resolved.headers, authHeaders, opts.headers);\n\n const init: RequestInit = {\n method,\n headers: baseHeaders,\n };\n\n if (opts.body) {\n init.body = typeof opts.body === \"string\" ? opts.body : JSON.stringify(opts.body);\n init.headers = {\n ...baseHeaders,\n \"content-type\": baseHeaders[\"content-type\"] ?? \"application/json\",\n };\n }\n\n if (opts.cache !== undefined) {\n init.cache = opts.cache;\n }\n\n if (opts.next) {\n (init as RequestInit & { next?: FetchRequestOptions[\"next\"] }).next = opts.next;\n }\n\n const startedAt = Date.now();\n try {\n const response = await withTimeout(fetchImpl(url, init), resolved.timeoutMs);\n debugLog(\"response\", {\n method,\n url,\n status: response.status,\n durationMs: Date.now() - startedAt,\n });\n if (opts.allowNotFound && response.status === 404) {\n return null as T;\n }\n\n const contentType = response.headers.get(\"content-type\") ?? \"\";\n const isJson = contentType.includes(\"application/json\");\n const payload = isJson ? await response.json().catch(() => undefined) : await response.text();\n\n if (!response.ok) {\n if (response.status === 404 && opts.allowNotFound) {\n return null as T;\n }\n\n const errorBody = payload as any;\n const message =\n errorBody?.error?.message ?? errorBody?.message ?? `Request failed with status ${response.status}`;\n throw new ApiError(message, {\n status: response.status,\n code: errorBody?.error?.code ?? errorBody?.code,\n details: errorBody?.error?.details ?? errorBody,\n });\n }\n\n if (isJson) {\n return unwrapSuccessPayload<T>(payload);\n }\n\n return (payload as T) ?? (undefined as T);\n } catch (error) {\n debugLog(\"request failed\", {\n method,\n url,\n durationMs: Date.now() - startedAt,\n error: error instanceof Error ? error.message : error,\n });\n if (error instanceof BlogAutoError) {\n throw error;\n }\n const causeMessage =\n error instanceof Error ? error.message : typeof error === \"string\" ? error : undefined;\n const message = causeMessage\n ? `Network request to BlogAuto failed: ${causeMessage}`\n : \"Network request to BlogAuto failed\";\n throw new ApiError(\n message,\n {\n status: 0,\n details: { url, method },\n },\n { cause: error },\n );\n }\n }\n\n async function fetchBlogPage(\n limit: number,\n page: number,\n cacheOptions?: FetchRequestOptions,\n ): Promise<PaginatedList<BlogPost>> {\n const workspaceSlug = ensureWorkspaceSlug();\n debugLog(\"fetch posts page\", { workspaceSlug, limit, page });\n return request<PaginatedList<BlogPost>>({\n path: `/v1/public/${encodeURIComponent(workspaceSlug)}/blogs`,\n query: { limit, page },\n cache: cacheOptions?.cache,\n next: cacheOptions?.next,\n });\n }\n\n return {\n async getPosts(params) {\n const limit = params?.limit ?? 20;\n const cursorPage = params?.cursor ? Number(params.cursor) : undefined;\n const page = Number.isFinite(cursorPage) && cursorPage! >= 1 ? cursorPage! : 1;\n const data = await fetchBlogPage(limit, page, params);\n return {\n posts: data.items,\n nextCursor: data.pagination.hasMore ? String(data.pagination.page + 1) : undefined,\n };\n },\n async getPostBySlug(slug: string, options?: FetchRequestOptions) {\n if (!slug) {\n throw new ApiError(\"slug is required\", { status: 400 });\n }\n const workspaceSlug = ensureWorkspaceSlug();\n const post = await request<{ post: BlogPost } | null>({\n path: `/v1/public/${encodeURIComponent(workspaceSlug)}/blogs/${encodeURIComponent(slug)}`,\n allowNotFound: true,\n cache: options?.cache,\n next: options?.next,\n });\n if (post === null) {\n return null;\n }\n return post.post;\n },\n async getSitemapEntries() {\n const entries: Array<{ slug: string; updatedAt: string }> = [];\n const limit = 100;\n let page = 1;\n\n while (true) {\n const pageData = await fetchBlogPage(limit, page);\n pageData.items.forEach((post) => {\n entries.push({ slug: post.slug, updatedAt: post.updatedAt });\n });\n\n if (!pageData.pagination.hasMore) {\n break;\n }\n\n page = pageData.pagination.page + 1;\n }\n\n return entries;\n },\n };\n}\n","import { createBlogAutoClient, type BlogAutoClient } from \"./client\";\n\nexport interface BlogAutoEnvConfig {\n client: BlogAutoClient;\n workspaceSlug: string;\n workspaceId?: string;\n siteUrl: string;\n revalidateSecret?: string;\n analyticsToken?: string;\n analyticsProxy: string;\n apiUrl: string;\n apiKey: string;\n tags: {\n posts: string;\n post: (slug: string) => string;\n sitemap: string;\n };\n}\n\nlet _instance: BlogAutoEnvConfig | null = null;\n\nfunction requireEnv(name: string): string {\n const value = process.env[name];\n if (!value) {\n throw new Error(`Missing required environment variable: ${name}`);\n }\n return value;\n}\n\nexport function createBlogAutoFromEnv(): BlogAutoEnvConfig {\n if (_instance) {\n return _instance;\n }\n\n const apiUrl = process.env.BLOGAUTO_API_URL ?? \"https://api.blogauto.io\";\n const apiKey = requireEnv(\"BLOGAUTO_API_KEY\");\n const workspaceSlug = requireEnv(\"BLOGAUTO_WORKSPACE_SLUG\");\n const workspaceId = process.env.BLOGAUTO_WORKSPACE_ID;\n const siteUrl =\n process.env.SITE_URL ?? process.env.NEXT_PUBLIC_SITE_URL ?? \"http://localhost:3000\";\n const revalidateSecret = process.env.BLOGAUTO_REVALIDATE_SECRET;\n const analyticsToken = process.env.NEXT_PUBLIC_ANALYTICS_TOKEN;\n const analyticsProxy =\n process.env.NEXT_PUBLIC_ANALYTICS_PROXY ?? \"/api/blogauto/analytics\";\n\n const client = createBlogAutoClient({\n apiUrl,\n apiKey,\n workspaceSlug,\n workspaceId,\n });\n\n _instance = {\n client,\n workspaceSlug,\n workspaceId,\n siteUrl,\n revalidateSecret,\n analyticsToken,\n analyticsProxy,\n apiUrl,\n apiKey,\n tags: {\n posts: `blogauto:${workspaceSlug}:posts`,\n post: (slug: string) => `blogauto:${workspaceSlug}:post:${slug}`,\n sitemap: `blogauto:${workspaceSlug}:sitemap`,\n },\n };\n\n return _instance;\n}\n","import type { BlogPost } from \"./types\";\n\nexport interface NextMetadata {\n title?: string;\n description?: string;\n keywords?: string[];\n alternates?: {\n canonical?: string;\n };\n openGraph?: {\n type?: string;\n title?: string;\n description?: string;\n images?: Array<{ url: string }>;\n publishedTime?: string;\n modifiedTime?: string;\n };\n twitter?: {\n card?: string;\n title?: string;\n description?: string;\n images?: string[];\n };\n}\n\nexport function buildNextMetadata(post: BlogPost): NextMetadata {\n const title = post.seo?.title ?? post.title;\n const description = post.seo?.description ?? post.excerpt;\n const keywords = post.seo?.keywords;\n const canonical = post.metadata?.canonicalUrl;\n const ogImageUrl = post.metadata?.ogImageUrl;\n\n const metadata: NextMetadata = {\n title,\n description,\n };\n\n if (keywords && keywords.length > 0) {\n metadata.keywords = keywords;\n }\n\n if (canonical) {\n metadata.alternates = { canonical };\n }\n\n metadata.openGraph = {\n type: \"article\",\n title,\n description,\n publishedTime: post.publishedAt,\n modifiedTime: post.updatedAt,\n };\n\n if (ogImageUrl) {\n metadata.openGraph.images = [{ url: ogImageUrl }];\n }\n\n metadata.twitter = {\n card: ogImageUrl ? \"summary_large_image\" : \"summary\",\n title,\n description,\n };\n\n if (ogImageUrl) {\n metadata.twitter.images = [ogImageUrl];\n }\n\n return metadata;\n}\n","import { BuildSitemapOptions, MetadataRouteSitemap, MetadataRouteSitemapEntry } from \"./types\";\n\nfunction normalizeSiteUrl(url: string): string {\n return url.replace(/\\/$/, \"\");\n}\n\nfunction normalizeRoutePrefix(routePrefix?: string): string {\n if (!routePrefix || routePrefix === \"/\") {\n return \"\";\n }\n const withSlash = routePrefix.startsWith(\"/\") ? routePrefix : `/${routePrefix}`;\n return withSlash.endsWith(\"/\") ? withSlash.slice(0, -1) : withSlash;\n}\n\nfunction normalizeSlug(slug: string): string {\n return slug.replace(/^\\/+/, \"\").replace(/\\/+$/, \"\");\n}\n\nexport function buildSitemap(opts: BuildSitemapOptions): MetadataRouteSitemap {\n const siteUrl = normalizeSiteUrl(opts.siteUrl);\n const prefix = normalizeRoutePrefix(opts.routePrefix ?? \"/blog\");\n\n const entries: MetadataRouteSitemapEntry[] = opts.entries.map((entry) => {\n const slug = normalizeSlug(entry.slug);\n const hasSlug = slug.length > 0;\n const slugFragment = hasSlug ? `/${slug}` : \"\";\n const path = prefix ? `${prefix}${slugFragment}` : hasSlug ? `/${slug}` : \"/\";\n return {\n url: `${siteUrl}${path}`,\n lastModified: entry.updatedAt,\n };\n });\n\n return entries;\n}\n","import { BuildRobotsOptions, MetadataRouteRobots } from \"./types\";\n\nfunction normalizeSiteUrl(url: string): string {\n return url.replace(/\\/$/, \"\");\n}\n\nfunction normalizePath(path: string): string {\n if (!path) {\n return \"/sitemap.xml\";\n }\n if (!path.startsWith(\"/\")) {\n return `/${path}`;\n }\n return path;\n}\n\nexport function buildRobots(opts: BuildRobotsOptions): MetadataRouteRobots {\n const siteUrl = normalizeSiteUrl(opts.siteUrl);\n const sitemapPath = normalizePath(opts.sitemapPath ?? \"/sitemap.xml\");\n const sitemapUrl = `${siteUrl}${sitemapPath}`;\n\n return {\n rules: [{ userAgent: \"*\", allow: \"/\" }],\n sitemap: sitemapUrl,\n };\n}\n","import { createBlogAutoFromEnv } from \"../env\";\nimport { buildNextMetadata, type NextMetadata } from \"../metadata\";\nimport { buildSitemap } from \"../sitemap\";\nimport { buildRobots } from \"../robots\";\nimport type { BlogPost, PostsResponse, MetadataRouteSitemap, MetadataRouteRobots } from \"../types\";\n\nexport interface FetchBlogPostsOptions {\n limit?: number;\n cursor?: string;\n}\n\nexport async function fetchBlogPosts(options?: FetchBlogPostsOptions): Promise<PostsResponse> {\n const { client, tags } = createBlogAutoFromEnv();\n return client.getPosts({\n limit: options?.limit,\n cursor: options?.cursor,\n next: { tags: [tags.posts] },\n });\n}\n\nexport async function fetchBlogPost(slug: string): Promise<BlogPost> {\n const { client, tags } = createBlogAutoFromEnv();\n const post = await client.getPostBySlug(slug, {\n next: { tags: [tags.post(slug)] },\n });\n\n if (!post) {\n // Dynamic import to avoid pulling next/navigation into non-Next environments\n const nav = await import(/* webpackIgnore: true */ \"next/navigation\" as string) as {\n notFound: () => never;\n };\n nav.notFound();\n // notFound() throws, but TS can't infer `never` through dynamic import\n throw new Error(\"notFound\");\n }\n\n return post;\n}\n\nexport async function generatePostMetadata(slug: string): Promise<NextMetadata> {\n const { client, tags } = createBlogAutoFromEnv();\n const post = await client.getPostBySlug(slug, {\n next: { tags: [tags.post(slug)] },\n });\n\n if (!post) {\n return {};\n }\n\n return buildNextMetadata(post);\n}\n\nexport async function generateBlogSitemap(): Promise<MetadataRouteSitemap> {\n const { client, siteUrl } = createBlogAutoFromEnv();\n const entries = await client.getSitemapEntries();\n return buildSitemap({ siteUrl, routePrefix: \"/blog\", entries });\n}\n\nexport function generateBlogRobots(): MetadataRouteRobots {\n const { siteUrl } = createBlogAutoFromEnv();\n return buildRobots({ siteUrl, sitemapPath: \"/sitemap.xml\" });\n}\n","import crypto from \"crypto\";\nimport type { BlogAutoRevalidatePayload, RevalidatePathFn, RevalidateTagFn } from \"./types\";\n\nexport interface VerifyWebhookSignatureOptions {\n rawBody: string | Buffer;\n signature: string | null;\n secret: string;\n}\n\nexport interface CreateRevalidateRouteHandlerOptions {\n secret: string;\n allowedSkewSeconds?: number;\n revalidatePath?: RevalidatePathFn;\n revalidateTag?: RevalidateTagFn;\n revalidatePaths?: (payload: BlogAutoRevalidatePayload) => string[];\n revalidateTags?: (payload: BlogAutoRevalidatePayload) => string[];\n}\n\ninterface JsonResponseInit extends ResponseInit {\n status?: number;\n}\n\nfunction jsonResponse(body: unknown, init?: JsonResponseInit): Response {\n const headers = new Headers(init?.headers);\n headers.set(\"content-type\", \"application/json\");\n return new Response(JSON.stringify(body), {\n ...init,\n headers,\n });\n}\n\nfunction normalizeSignature(signature: string | null): Buffer | null {\n if (!signature) {\n return null;\n }\n const trimmed = signature.trim();\n if (!trimmed) {\n return null;\n }\n\n const withoutPrefix = trimmed.startsWith(\"sha256=\") ? trimmed.slice(7) : trimmed;\n if (!withoutPrefix) {\n return null;\n }\n\n try {\n return Buffer.from(withoutPrefix, \"hex\");\n } catch {\n return null;\n }\n}\n\nexport function verifyWebhookSignature(opts: VerifyWebhookSignatureOptions): boolean {\n const { rawBody, signature, secret } = opts;\n if (!secret) {\n throw new Error(\"Secret is required to verify webhook signatures\");\n }\n const providedSignature = normalizeSignature(signature);\n if (!providedSignature) {\n return false;\n }\n\n const bodyBuffer = typeof rawBody === \"string\" ? Buffer.from(rawBody) : rawBody;\n const expected = crypto.createHmac(\"sha256\", secret).update(bodyBuffer).digest();\n\n if (providedSignature.length !== expected.length) {\n return false;\n }\n\n return crypto.timingSafeEqual(providedSignature, expected);\n}\n\nfunction defaultPaths(payload: BlogAutoRevalidatePayload): string[] {\n const paths = new Set<string>([\"/sitemap.xml\", \"/robots.txt\", \"/blog\"]);\n if (payload.postSlug) {\n paths.add(`/blog/${payload.postSlug}`);\n }\n return Array.from(paths);\n}\n\nfunction defaultTags(payload: BlogAutoRevalidatePayload): string[] {\n const tags = new Set<string>();\n if (payload.workspaceSlug) {\n tags.add(`blogauto:${payload.workspaceSlug}:sitemap`);\n tags.add(`blogauto:${payload.workspaceSlug}:posts`);\n if (payload.postSlug) {\n tags.add(`blogauto:${payload.workspaceSlug}:post:${payload.postSlug}`);\n }\n }\n return Array.from(tags);\n}\n\nfunction dedupe(values: string[] | undefined): string[] {\n if (!values || values.length === 0) {\n return [];\n }\n return Array.from(\n new Set(\n values\n .filter((value) => typeof value === \"string\" && value.trim().length > 0)\n .map((value) => value.trim()),\n ),\n );\n}\n\nfunction determineRouteType(path: string): \"route\" | \"page\" {\n const lower = path.toLowerCase();\n if (lower.endsWith(\".xml\") || lower.endsWith(\".txt\")) {\n return \"route\";\n }\n return \"page\";\n}\n\nexport function createRevalidateRouteHandler(\n options: CreateRevalidateRouteHandlerOptions,\n): (request: Request) => Promise<Response> {\n if (!options.secret) {\n throw new Error(\"secret is required for createRevalidateRouteHandler\");\n }\n\n const allowedSkewMs = Math.max(1, options.allowedSkewSeconds ?? 300) * 1000;\n const pathBuilder = options.revalidatePaths ?? defaultPaths;\n const tagBuilder = options.revalidateTags ?? defaultTags;\n\n return async function blogAutoRevalidateHandler(request: Request): Promise<Response> {\n const signature = request.headers.get(\"x-blogauto-signature\");\n const receivedAt = Date.now();\n\n const rawBody = await request.text();\n const isValid = verifyWebhookSignature({\n rawBody,\n signature,\n secret: options.secret,\n });\n\n if (!isValid) {\n return jsonResponse({ error: \"Invalid webhook signature\" }, { status: 401 });\n }\n\n let payload: BlogAutoRevalidatePayload;\n try {\n payload = JSON.parse(rawBody) as BlogAutoRevalidatePayload;\n } catch {\n return jsonResponse({ error: \"Invalid JSON payload\" }, { status: 400 });\n }\n\n if (!payload.workspaceSlug || typeof payload.workspaceSlug !== \"string\") {\n return jsonResponse({ error: \"workspaceSlug is required\" }, { status: 400 });\n }\n\n if (!payload.ts || typeof payload.ts !== \"string\") {\n return jsonResponse({ error: \"Timestamp is required\" }, { status: 400 });\n }\n\n const payloadTs = Date.parse(payload.ts);\n if (Number.isNaN(payloadTs)) {\n return jsonResponse({ error: \"Invalid timestamp\" }, { status: 400 });\n }\n\n if (Math.abs(receivedAt - payloadTs) > allowedSkewMs) {\n return jsonResponse({ error: \"Webhook timestamp is outside allowed skew\" }, { status: 409 });\n }\n\n const paths = dedupe(pathBuilder(payload));\n const tags = dedupe(tagBuilder(payload));\n\n try {\n if (options.revalidatePath && paths.length > 0) {\n await Promise.all(\n paths.map((path) => options.revalidatePath!(path, determineRouteType(path))),\n );\n }\n\n if (options.revalidateTag && tags.length > 0) {\n await Promise.all(tags.map((tag) => options.revalidateTag!(tag)));\n }\n } catch (error) {\n return jsonResponse(\n { error: \"Failed to revalidate cache\", details: (error as Error).message },\n { status: 500 },\n );\n }\n\n return jsonResponse({\n ok: true,\n event: payload.event ?? \"post.published\",\n revalidated: {\n paths,\n tags,\n },\n });\n };\n}\n","import { createBlogAutoFromEnv } from \"../env\";\nimport { createRevalidateRouteHandler } from \"../revalidate\";\nimport { buildAuthHeaders } from \"../auth\";\n\nexport function createEnvRevalidateHandler(): (request: Request) => Promise<Response> {\n return async (request: Request): Promise<Response> => {\n // Lazy init so env vars are read at request time, not module load time\n const { revalidateSecret } = createBlogAutoFromEnv();\n\n if (!revalidateSecret) {\n return new Response(\n JSON.stringify({ error: \"Missing BLOGAUTO_REVALIDATE_SECRET\" }),\n { status: 500, headers: { \"content-type\": \"application/json\" } },\n );\n }\n\n // Dynamic import to avoid pulling next/cache into non-Next environments\n const nextCache = await import(/* webpackIgnore: true */ \"next/cache\" as string) as {\n revalidatePath: (path: string, type?: \"page\" | \"layout\" | \"route\") => void;\n revalidateTag: (tag: string) => void;\n };\n const { revalidatePath, revalidateTag } = nextCache;\n\n const handler = createRevalidateRouteHandler({\n secret: revalidateSecret,\n revalidatePath,\n revalidateTag,\n });\n\n return handler(request);\n };\n}\n\nexport function createEnvAnalyticsHandler(): (request: Request) => Promise<Response> {\n return async (request: Request): Promise<Response> => {\n const { analyticsToken, apiUrl, apiKey, workspaceId, workspaceSlug } =\n createBlogAutoFromEnv();\n\n if (!analyticsToken) {\n return new Response(\n JSON.stringify({ error: \"Analytics proxy disabled\" }),\n { status: 503, headers: { \"content-type\": \"application/json\" } },\n );\n }\n\n const provided = request.headers.get(\"x-api-key\");\n if (provided !== analyticsToken) {\n return new Response(\n JSON.stringify({ error: \"Unauthorized\" }),\n { status: 401, headers: { \"content-type\": \"application/json\" } },\n );\n }\n\n const payload = await request.json();\n const envelope = {\n ...payload,\n workspaceId: workspaceId ?? payload.workspaceId,\n workspaceSlug: workspaceSlug ?? payload.workspaceSlug,\n };\n\n const response = await fetch(`${apiUrl}/v1/analytics/ingest`, {\n method: \"POST\",\n headers: {\n \"content-type\": \"application/json\",\n ...buildAuthHeaders(apiKey, \"bearer\"),\n },\n body: JSON.stringify(envelope),\n });\n\n const text = await response.text();\n const contentType = response.headers.get(\"content-type\") ?? \"application/json\";\n\n return new Response(text || \"{}\", {\n status: response.status,\n headers: { \"content-type\": contentType },\n });\n };\n}\n"],"mappings":";AAEO,SAAS,iBAAiB,QAAgB,WAAqB,UAAkC;AACtG,MAAI,aAAa,aAAa;AAC5B,WAAO,EAAE,aAAa,OAAO;AAAA,EAC/B;AACA,SAAO,EAAE,eAAe,UAAU,MAAM,GAAG;AAC7C;;;ACPO,IAAM,gBAAN,cAA4B,MAAM;AAAA,EAGvC,YAAY,SAAiB,SAA+B;AAC1D,UAAM,OAAO;AACb,SAAK,OAAO,WAAW;AACvB,SAAK,aAAa,SAAS;AAAA,EAC7B;AACF;AAEO,IAAM,cAAN,cAA0B,cAAc;AAAC;AAQzC,IAAM,WAAN,cAAuB,cAAc;AAAA,EAK1C,YAAY,SAAiB,MAAuB,SAA+B;AACjF,UAAM,SAAS,OAAO;AACtB,SAAK,SAAS,KAAK;AACnB,SAAK,OAAO,KAAK;AACjB,SAAK,UAAU,KAAK;AAAA,EACtB;AACF;;;AC1BO,IAAM,qBAAqB;AAE3B,SAAS,gBAAgB,QAAwB;AACtD,MAAI,CAAC,QAAQ;AACX,UAAM,IAAI,YAAY,oBAAoB;AAAA,EAC5C;AAEA,SAAO,OAAO,QAAQ,OAAO,EAAE;AACjC;AAEO,SAAS,oBAAoB,QAAoD;AACtF,MAAI,CAAC,QAAQ;AACX,UAAM,IAAI,YAAY,kCAAkC;AAAA,EAC1D;AAEA,QAAM,SAAS,OAAO,QAAQ,KAAK;AACnC,MAAI,CAAC,QAAQ;AACX,UAAM,IAAI,YAAY,kDAAkD;AAAA,EAC1E;AAEA,MAAI,CAAC,OAAO,eAAe,CAAC,OAAO,eAAe;AAChD,UAAM,IAAI,YAAY,6CAA6C;AAAA,EACrE;AAEA,QAAM,WAAqB,OAAO,YAAY;AAE9C,SAAO;AAAA,IACL;AAAA,IACA,QAAQ,gBAAgB,OAAO,MAAM;AAAA,IACrC,aAAa,OAAO;AAAA,IACpB,eAAe,OAAO;AAAA,IACtB;AAAA,IACA,OAAO,OAAO,SAAS,WAAW,MAAM,KAAK,UAAU;AAAA,IACvD,SAAS,EAAE,GAAI,OAAO,WAAW,CAAC,EAAG;AAAA,IACrC,WAAW,OAAO,aAAa;AAAA,EACjC;AACF;AAEO,SAAS,WAAW,QAAoE;AAC7F,QAAM,QAAQ,IAAI,gBAAgB;AAClC,SAAO,QAAQ,MAAM,EAAE,QAAQ,CAAC,CAAC,KAAK,KAAK,MAAM;AAC/C,QAAI,UAAU,UAAa,UAAU,QAAQ,UAAU,IAAI;AACzD;AAAA,IACF;AACA,UAAM,IAAI,KAAK,OAAO,KAAK,CAAC;AAAA,EAC9B,CAAC;AACD,QAAM,KAAK,MAAM,SAAS;AAC1B,SAAO,KAAK,IAAI,EAAE,KAAK;AACzB;AAEA,eAAsB,YAAe,SAAqB,WAAgC;AACxF,MAAI,CAAC,WAAW;AACd,WAAO;AAAA,EACT;AAEA,MAAI;AAEJ,QAAM,iBAAiB,IAAI,QAAe,CAAC,GAAG,WAAW;AACvD,gBAAY,WAAW,MAAM;AAC3B;AAAA,QACE,IAAI,SAAS,2BAA2B,SAAS,MAAM;AAAA,UACrD,QAAQ;AAAA,QACV,CAAC;AAAA,MACH;AAAA,IACF,GAAG,SAAS;AAAA,EACd,CAAC;AAED,MAAI;AACF,WAAO,MAAM,QAAQ,KAAK,CAAC,SAAS,cAAc,CAAC;AAAA,EACrD,UAAE;AACA,iBAAa,SAAU;AAAA,EACzB;AACF;AAEO,SAAS,gBACX,eACqB;AACxB,SAAO,cAAc,OAA+B,CAAC,KAAK,YAAY;AACpE,QAAI,CAAC,SAAS;AACZ,aAAO;AAAA,IACT;AACA,WAAO,QAAQ,OAAO,EAAE,QAAQ,CAAC,CAAC,KAAK,KAAK,MAAM;AAChD,UAAI,UAAU,UAAa,UAAU,MAAM;AACzC;AAAA,MACF;AACA,UAAI,IAAI,YAAY,CAAC,IAAI;AAAA,IAC3B,CAAC;AACD,WAAO;AAAA,EACT,GAAG,CAAC,CAAC;AACP;;;AChEO,SAAS,qBAAqB,QAA8C;AACjF,QAAM,WAAW,oBAAoB,MAAM;AAC3C,QAAM,YAAY,SAAS;AAC3B,QAAM,QACJ,OAAO,YAAY,eACnB,OAAO,QAAQ,QAAQ,eACvB,QAAQ,IAAI,mBAAmB;AACjC,QAAM,WAAW,IAAI,SAAoB;AACvC,QAAI,OAAO;AAET,cAAQ,IAAI,cAAc,GAAG,IAAI;AAAA,IACnC;AAAA,EACF;AAEA,WAAS,sBAA8B;AACrC,QAAI,CAAC,SAAS,eAAe;AAC3B,YAAM,IAAI,YAAY,2DAA2D;AAAA,IACnF;AACA,WAAO,SAAS;AAAA,EAClB;AAEA,WAAS,SAAS,MAAc,OAAoE;AAClG,UAAM,KAAK,WAAW,SAAS,CAAC,CAAC;AACjC,WAAO,GAAG,SAAS,MAAM,GAAG,IAAI,GAAG,EAAE;AAAA,EACvC;AAEA,WAAS,qBAAwB,SAAqB;AACpD,QAAI,WAAW,OAAO,YAAY,UAAU;AAC1C,YAAM,eAAe;AACrB,UAAI,aAAa,cAAc;AAC7B,YAAI,aAAa,YAAY,OAAO;AAClC,gBAAM,IAAI;AAAA,YACR,aAAa,OAAO,WAAW;AAAA,YAC/B,EAAE,QAAQ,KAAK,SAAS,aAAa;AAAA,UACvC;AAAA,QACF;AACA,YAAI,aAAa,WAAW,UAAU,cAAc;AAClD,iBAAO,aAAa;AAAA,QACtB;AAAA,MACF;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAEA,iBAAe,QAAW,MAAkC;AAC1D,UAAM,SAAS,KAAK,UAAU;AAC9B,UAAM,MAAM,SAAS,KAAK,MAAM,KAAK,KAAK;AAE1C,UAAM,cAAc,iBAAiB,SAAS,QAAQ,SAAS,QAAQ;AACvE,UAAM,cAAc,aAAa,SAAS,SAAS,aAAa,KAAK,OAAO;AAE5E,UAAM,OAAoB;AAAA,MACxB;AAAA,MACA,SAAS;AAAA,IACX;AAEA,QAAI,KAAK,MAAM;AACb,WAAK,OAAO,OAAO,KAAK,SAAS,WAAW,KAAK,OAAO,KAAK,UAAU,KAAK,IAAI;AAChF,WAAK,UAAU;AAAA,QACb,GAAG;AAAA,QACH,gBAAgB,YAAY,cAAc,KAAK;AAAA,MACjD;AAAA,IACF;AAEA,QAAI,KAAK,UAAU,QAAW;AAC5B,WAAK,QAAQ,KAAK;AAAA,IACpB;AAEA,QAAI,KAAK,MAAM;AACb,MAAC,KAA8D,OAAO,KAAK;AAAA,IAC7E;AAEA,UAAM,YAAY,KAAK,IAAI;AAC3B,QAAI;AACF,YAAM,WAAW,MAAM,YAAY,UAAU,KAAK,IAAI,GAAG,SAAS,SAAS;AAC3E,eAAS,YAAY;AAAA,QACnB;AAAA,QACA;AAAA,QACA,QAAQ,SAAS;AAAA,QACjB,YAAY,KAAK,IAAI,IAAI;AAAA,MAC3B,CAAC;AACD,UAAI,KAAK,iBAAiB,SAAS,WAAW,KAAK;AACjD,eAAO;AAAA,MACT;AAEA,YAAM,cAAc,SAAS,QAAQ,IAAI,cAAc,KAAK;AAC5D,YAAM,SAAS,YAAY,SAAS,kBAAkB;AACtD,YAAM,UAAU,SAAS,MAAM,SAAS,KAAK,EAAE,MAAM,MAAM,MAAS,IAAI,MAAM,SAAS,KAAK;AAE5F,UAAI,CAAC,SAAS,IAAI;AAChB,YAAI,SAAS,WAAW,OAAO,KAAK,eAAe;AACjD,iBAAO;AAAA,QACT;AAEA,cAAM,YAAY;AAClB,cAAM,UACJ,WAAW,OAAO,WAAW,WAAW,WAAW,8BAA8B,SAAS,MAAM;AAClG,cAAM,IAAI,SAAS,SAAS;AAAA,UAC1B,QAAQ,SAAS;AAAA,UACjB,MAAM,WAAW,OAAO,QAAQ,WAAW;AAAA,UAC3C,SAAS,WAAW,OAAO,WAAW;AAAA,QACxC,CAAC;AAAA,MACH;AAEA,UAAI,QAAQ;AACV,eAAO,qBAAwB,OAAO;AAAA,MACxC;AAEA,aAAQ,WAAkB;AAAA,IAC5B,SAAS,OAAO;AACd,eAAS,kBAAkB;AAAA,QACzB;AAAA,QACA;AAAA,QACA,YAAY,KAAK,IAAI,IAAI;AAAA,QACzB,OAAO,iBAAiB,QAAQ,MAAM,UAAU;AAAA,MAClD,CAAC;AACD,UAAI,iBAAiB,eAAe;AAClC,cAAM;AAAA,MACR;AACA,YAAM,eACJ,iBAAiB,QAAQ,MAAM,UAAU,OAAO,UAAU,WAAW,QAAQ;AAC/E,YAAM,UAAU,eACZ,uCAAuC,YAAY,KACnD;AACJ,YAAM,IAAI;AAAA,QACR;AAAA,QACA;AAAA,UACE,QAAQ;AAAA,UACR,SAAS,EAAE,KAAK,OAAO;AAAA,QACzB;AAAA,QACA,EAAE,OAAO,MAAM;AAAA,MACjB;AAAA,IACF;AAAA,EACF;AAEA,iBAAe,cACb,OACA,MACA,cACkC;AAClC,UAAM,gBAAgB,oBAAoB;AAC1C,aAAS,oBAAoB,EAAE,eAAe,OAAO,KAAK,CAAC;AAC3D,WAAO,QAAiC;AAAA,MACtC,MAAM,cAAc,mBAAmB,aAAa,CAAC;AAAA,MACrD,OAAO,EAAE,OAAO,KAAK;AAAA,MACrB,OAAO,cAAc;AAAA,MACrB,MAAM,cAAc;AAAA,IACtB,CAAC;AAAA,EACH;AAEA,SAAO;AAAA,IACL,MAAM,SAAS,QAAQ;AACrB,YAAM,QAAQ,QAAQ,SAAS;AAC/B,YAAM,aAAa,QAAQ,SAAS,OAAO,OAAO,MAAM,IAAI;AAC5D,YAAM,OAAO,OAAO,SAAS,UAAU,KAAK,cAAe,IAAI,aAAc;AAC7E,YAAM,OAAO,MAAM,cAAc,OAAO,MAAM,MAAM;AACpD,aAAO;AAAA,QACL,OAAO,KAAK;AAAA,QACZ,YAAY,KAAK,WAAW,UAAU,OAAO,KAAK,WAAW,OAAO,CAAC,IAAI;AAAA,MAC3E;AAAA,IACF;AAAA,IACA,MAAM,cAAc,MAAc,SAA+B;AAC/D,UAAI,CAAC,MAAM;AACT,cAAM,IAAI,SAAS,oBAAoB,EAAE,QAAQ,IAAI,CAAC;AAAA,MACxD;AACA,YAAM,gBAAgB,oBAAoB;AAC1C,YAAM,OAAO,MAAM,QAAmC;AAAA,QACpD,MAAM,cAAc,mBAAmB,aAAa,CAAC,UAAU,mBAAmB,IAAI,CAAC;AAAA,QACvF,eAAe;AAAA,QACf,OAAO,SAAS;AAAA,QAChB,MAAM,SAAS;AAAA,MACjB,CAAC;AACD,UAAI,SAAS,MAAM;AACjB,eAAO;AAAA,MACT;AACA,aAAO,KAAK;AAAA,IACd;AAAA,IACA,MAAM,oBAAoB;AACxB,YAAM,UAAsD,CAAC;AAC7D,YAAM,QAAQ;AACd,UAAI,OAAO;AAEX,aAAO,MAAM;AACX,cAAM,WAAW,MAAM,cAAc,OAAO,IAAI;AAChD,iBAAS,MAAM,QAAQ,CAAC,SAAS;AAC/B,kBAAQ,KAAK,EAAE,MAAM,KAAK,MAAM,WAAW,KAAK,UAAU,CAAC;AAAA,QAC7D,CAAC;AAED,YAAI,CAAC,SAAS,WAAW,SAAS;AAChC;AAAA,QACF;AAEA,eAAO,SAAS,WAAW,OAAO;AAAA,MACpC;AAEA,aAAO;AAAA,IACT;AAAA,EACF;AACF;;;AC/MA,IAAI,YAAsC;AAE1C,SAAS,WAAW,MAAsB;AACxC,QAAM,QAAQ,QAAQ,IAAI,IAAI;AAC9B,MAAI,CAAC,OAAO;AACV,UAAM,IAAI,MAAM,0CAA0C,IAAI,EAAE;AAAA,EAClE;AACA,SAAO;AACT;AAEO,SAAS,wBAA2C;AACzD,MAAI,WAAW;AACb,WAAO;AAAA,EACT;AAEA,QAAM,SAAS,QAAQ,IAAI,oBAAoB;AAC/C,QAAM,SAAS,WAAW,kBAAkB;AAC5C,QAAM,gBAAgB,WAAW,yBAAyB;AAC1D,QAAM,cAAc,QAAQ,IAAI;AAChC,QAAM,UACJ,QAAQ,IAAI,YAAY,QAAQ,IAAI,wBAAwB;AAC9D,QAAM,mBAAmB,QAAQ,IAAI;AACrC,QAAM,iBAAiB,QAAQ,IAAI;AACnC,QAAM,iBACJ,QAAQ,IAAI,+BAA+B;AAE7C,QAAM,SAAS,qBAAqB;AAAA,IAClC;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,CAAC;AAED,cAAY;AAAA,IACV;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,MAAM;AAAA,MACJ,OAAO,YAAY,aAAa;AAAA,MAChC,MAAM,CAAC,SAAiB,YAAY,aAAa,SAAS,IAAI;AAAA,MAC9D,SAAS,YAAY,aAAa;AAAA,IACpC;AAAA,EACF;AAEA,SAAO;AACT;;;AC7CO,SAAS,kBAAkB,MAA8B;AAC9D,QAAM,QAAQ,KAAK,KAAK,SAAS,KAAK;AACtC,QAAM,cAAc,KAAK,KAAK,eAAe,KAAK;AAClD,QAAM,WAAW,KAAK,KAAK;AAC3B,QAAM,YAAY,KAAK,UAAU;AACjC,QAAM,aAAa,KAAK,UAAU;AAElC,QAAM,WAAyB;AAAA,IAC7B;AAAA,IACA;AAAA,EACF;AAEA,MAAI,YAAY,SAAS,SAAS,GAAG;AACnC,aAAS,WAAW;AAAA,EACtB;AAEA,MAAI,WAAW;AACb,aAAS,aAAa,EAAE,UAAU;AAAA,EACpC;AAEA,WAAS,YAAY;AAAA,IACnB,MAAM;AAAA,IACN;AAAA,IACA;AAAA,IACA,eAAe,KAAK;AAAA,IACpB,cAAc,KAAK;AAAA,EACrB;AAEA,MAAI,YAAY;AACd,aAAS,UAAU,SAAS,CAAC,EAAE,KAAK,WAAW,CAAC;AAAA,EAClD;AAEA,WAAS,UAAU;AAAA,IACjB,MAAM,aAAa,wBAAwB;AAAA,IAC3C;AAAA,IACA;AAAA,EACF;AAEA,MAAI,YAAY;AACd,aAAS,QAAQ,SAAS,CAAC,UAAU;AAAA,EACvC;AAEA,SAAO;AACT;;;AClEA,SAAS,iBAAiB,KAAqB;AAC7C,SAAO,IAAI,QAAQ,OAAO,EAAE;AAC9B;AAEA,SAAS,qBAAqB,aAA8B;AAC1D,MAAI,CAAC,eAAe,gBAAgB,KAAK;AACvC,WAAO;AAAA,EACT;AACA,QAAM,YAAY,YAAY,WAAW,GAAG,IAAI,cAAc,IAAI,WAAW;AAC7E,SAAO,UAAU,SAAS,GAAG,IAAI,UAAU,MAAM,GAAG,EAAE,IAAI;AAC5D;AAEA,SAAS,cAAc,MAAsB;AAC3C,SAAO,KAAK,QAAQ,QAAQ,EAAE,EAAE,QAAQ,QAAQ,EAAE;AACpD;AAEO,SAAS,aAAa,MAAiD;AAC5E,QAAM,UAAU,iBAAiB,KAAK,OAAO;AAC7C,QAAM,SAAS,qBAAqB,KAAK,eAAe,OAAO;AAE/D,QAAM,UAAuC,KAAK,QAAQ,IAAI,CAAC,UAAU;AACvE,UAAM,OAAO,cAAc,MAAM,IAAI;AACrC,UAAM,UAAU,KAAK,SAAS;AAC9B,UAAM,eAAe,UAAU,IAAI,IAAI,KAAK;AAC5C,UAAM,OAAO,SAAS,GAAG,MAAM,GAAG,YAAY,KAAK,UAAU,IAAI,IAAI,KAAK;AAC1E,WAAO;AAAA,MACL,KAAK,GAAG,OAAO,GAAG,IAAI;AAAA,MACtB,cAAc,MAAM;AAAA,IACtB;AAAA,EACF,CAAC;AAED,SAAO;AACT;;;AChCA,SAASA,kBAAiB,KAAqB;AAC7C,SAAO,IAAI,QAAQ,OAAO,EAAE;AAC9B;AAEA,SAAS,cAAc,MAAsB;AAC3C,MAAI,CAAC,MAAM;AACT,WAAO;AAAA,EACT;AACA,MAAI,CAAC,KAAK,WAAW,GAAG,GAAG;AACzB,WAAO,IAAI,IAAI;AAAA,EACjB;AACA,SAAO;AACT;AAEO,SAAS,YAAY,MAA+C;AACzE,QAAM,UAAUA,kBAAiB,KAAK,OAAO;AAC7C,QAAM,cAAc,cAAc,KAAK,eAAe,cAAc;AACpE,QAAM,aAAa,GAAG,OAAO,GAAG,WAAW;AAE3C,SAAO;AAAA,IACL,OAAO,CAAC,EAAE,WAAW,KAAK,OAAO,IAAI,CAAC;AAAA,IACtC,SAAS;AAAA,EACX;AACF;;;ACdA,eAAsB,eAAe,SAAyD;AAC5F,QAAM,EAAE,QAAQ,KAAK,IAAI,sBAAsB;AAC/C,SAAO,OAAO,SAAS;AAAA,IACrB,OAAO,SAAS;AAAA,IAChB,QAAQ,SAAS;AAAA,IACjB,MAAM,EAAE,MAAM,CAAC,KAAK,KAAK,EAAE;AAAA,EAC7B,CAAC;AACH;AAEA,eAAsB,cAAc,MAAiC;AACnE,QAAM,EAAE,QAAQ,KAAK,IAAI,sBAAsB;AAC/C,QAAM,OAAO,MAAM,OAAO,cAAc,MAAM;AAAA,IAC5C,MAAM,EAAE,MAAM,CAAC,KAAK,KAAK,IAAI,CAAC,EAAE;AAAA,EAClC,CAAC;AAED,MAAI,CAAC,MAAM;AAET,UAAM,MAAM,MAAM;AAAA;AAAA,MAAiC;AAAA,IAA2B;AAG9E,QAAI,SAAS;AAEb,UAAM,IAAI,MAAM,UAAU;AAAA,EAC5B;AAEA,SAAO;AACT;AAEA,eAAsB,qBAAqB,MAAqC;AAC9E,QAAM,EAAE,QAAQ,KAAK,IAAI,sBAAsB;AAC/C,QAAM,OAAO,MAAM,OAAO,cAAc,MAAM;AAAA,IAC5C,MAAM,EAAE,MAAM,CAAC,KAAK,KAAK,IAAI,CAAC,EAAE;AAAA,EAClC,CAAC;AAED,MAAI,CAAC,MAAM;AACT,WAAO,CAAC;AAAA,EACV;AAEA,SAAO,kBAAkB,IAAI;AAC/B;AAEA,eAAsB,sBAAqD;AACzE,QAAM,EAAE,QAAQ,QAAQ,IAAI,sBAAsB;AAClD,QAAM,UAAU,MAAM,OAAO,kBAAkB;AAC/C,SAAO,aAAa,EAAE,SAAS,aAAa,SAAS,QAAQ,CAAC;AAChE;AAEO,SAAS,qBAA0C;AACxD,QAAM,EAAE,QAAQ,IAAI,sBAAsB;AAC1C,SAAO,YAAY,EAAE,SAAS,aAAa,eAAe,CAAC;AAC7D;;;AC7DA,OAAO,YAAY;AAsBnB,SAAS,aAAa,MAAe,MAAmC;AACtE,QAAM,UAAU,IAAI,QAAQ,MAAM,OAAO;AACzC,UAAQ,IAAI,gBAAgB,kBAAkB;AAC9C,SAAO,IAAI,SAAS,KAAK,UAAU,IAAI,GAAG;AAAA,IACxC,GAAG;AAAA,IACH;AAAA,EACF,CAAC;AACH;AAEA,SAAS,mBAAmB,WAAyC;AACnE,MAAI,CAAC,WAAW;AACd,WAAO;AAAA,EACT;AACA,QAAM,UAAU,UAAU,KAAK;AAC/B,MAAI,CAAC,SAAS;AACZ,WAAO;AAAA,EACT;AAEA,QAAM,gBAAgB,QAAQ,WAAW,SAAS,IAAI,QAAQ,MAAM,CAAC,IAAI;AACzE,MAAI,CAAC,eAAe;AAClB,WAAO;AAAA,EACT;AAEA,MAAI;AACF,WAAO,OAAO,KAAK,eAAe,KAAK;AAAA,EACzC,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEO,SAAS,uBAAuB,MAA8C;AACnF,QAAM,EAAE,SAAS,WAAW,OAAO,IAAI;AACvC,MAAI,CAAC,QAAQ;AACX,UAAM,IAAI,MAAM,iDAAiD;AAAA,EACnE;AACA,QAAM,oBAAoB,mBAAmB,SAAS;AACtD,MAAI,CAAC,mBAAmB;AACtB,WAAO;AAAA,EACT;AAEA,QAAM,aAAa,OAAO,YAAY,WAAW,OAAO,KAAK,OAAO,IAAI;AACxE,QAAM,WAAW,OAAO,WAAW,UAAU,MAAM,EAAE,OAAO,UAAU,EAAE,OAAO;AAE/E,MAAI,kBAAkB,WAAW,SAAS,QAAQ;AAChD,WAAO;AAAA,EACT;AAEA,SAAO,OAAO,gBAAgB,mBAAmB,QAAQ;AAC3D;AAEA,SAAS,aAAa,SAA8C;AAClE,QAAM,QAAQ,oBAAI,IAAY,CAAC,gBAAgB,eAAe,OAAO,CAAC;AACtE,MAAI,QAAQ,UAAU;AACpB,UAAM,IAAI,SAAS,QAAQ,QAAQ,EAAE;AAAA,EACvC;AACA,SAAO,MAAM,KAAK,KAAK;AACzB;AAEA,SAAS,YAAY,SAA8C;AACjE,QAAM,OAAO,oBAAI,IAAY;AAC7B,MAAI,QAAQ,eAAe;AACzB,SAAK,IAAI,YAAY,QAAQ,aAAa,UAAU;AACpD,SAAK,IAAI,YAAY,QAAQ,aAAa,QAAQ;AAClD,QAAI,QAAQ,UAAU;AACpB,WAAK,IAAI,YAAY,QAAQ,aAAa,SAAS,QAAQ,QAAQ,EAAE;AAAA,IACvE;AAAA,EACF;AACA,SAAO,MAAM,KAAK,IAAI;AACxB;AAEA,SAAS,OAAO,QAAwC;AACtD,MAAI,CAAC,UAAU,OAAO,WAAW,GAAG;AAClC,WAAO,CAAC;AAAA,EACV;AACA,SAAO,MAAM;AAAA,IACX,IAAI;AAAA,MACF,OACG,OAAO,CAAC,UAAU,OAAO,UAAU,YAAY,MAAM,KAAK,EAAE,SAAS,CAAC,EACtE,IAAI,CAAC,UAAU,MAAM,KAAK,CAAC;AAAA,IAChC;AAAA,EACF;AACF;AAEA,SAAS,mBAAmB,MAAgC;AAC1D,QAAM,QAAQ,KAAK,YAAY;AAC/B,MAAI,MAAM,SAAS,MAAM,KAAK,MAAM,SAAS,MAAM,GAAG;AACpD,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAEO,SAAS,6BACd,SACyC;AACzC,MAAI,CAAC,QAAQ,QAAQ;AACnB,UAAM,IAAI,MAAM,qDAAqD;AAAA,EACvE;AAEA,QAAM,gBAAgB,KAAK,IAAI,GAAG,QAAQ,sBAAsB,GAAG,IAAI;AACvE,QAAM,cAAc,QAAQ,mBAAmB;AAC/C,QAAM,aAAa,QAAQ,kBAAkB;AAE7C,SAAO,eAAe,0BAA0B,SAAqC;AACnF,UAAM,YAAY,QAAQ,QAAQ,IAAI,sBAAsB;AAC5D,UAAM,aAAa,KAAK,IAAI;AAE5B,UAAM,UAAU,MAAM,QAAQ,KAAK;AACnC,UAAM,UAAU,uBAAuB;AAAA,MACrC;AAAA,MACA;AAAA,MACA,QAAQ,QAAQ;AAAA,IAClB,CAAC;AAED,QAAI,CAAC,SAAS;AACZ,aAAO,aAAa,EAAE,OAAO,4BAA4B,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,IAC7E;AAEA,QAAI;AACJ,QAAI;AACF,gBAAU,KAAK,MAAM,OAAO;AAAA,IAC9B,QAAQ;AACN,aAAO,aAAa,EAAE,OAAO,uBAAuB,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,IACxE;AAEA,QAAI,CAAC,QAAQ,iBAAiB,OAAO,QAAQ,kBAAkB,UAAU;AACvE,aAAO,aAAa,EAAE,OAAO,4BAA4B,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,IAC7E;AAEA,QAAI,CAAC,QAAQ,MAAM,OAAO,QAAQ,OAAO,UAAU;AACjD,aAAO,aAAa,EAAE,OAAO,wBAAwB,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,IACzE;AAEA,UAAM,YAAY,KAAK,MAAM,QAAQ,EAAE;AACvC,QAAI,OAAO,MAAM,SAAS,GAAG;AAC3B,aAAO,aAAa,EAAE,OAAO,oBAAoB,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,IACrE;AAEA,QAAI,KAAK,IAAI,aAAa,SAAS,IAAI,eAAe;AACpD,aAAO,aAAa,EAAE,OAAO,4CAA4C,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,IAC7F;AAEA,UAAM,QAAQ,OAAO,YAAY,OAAO,CAAC;AACzC,UAAM,OAAO,OAAO,WAAW,OAAO,CAAC;AAEvC,QAAI;AACF,UAAI,QAAQ,kBAAkB,MAAM,SAAS,GAAG;AAC9C,cAAM,QAAQ;AAAA,UACZ,MAAM,IAAI,CAAC,SAAS,QAAQ,eAAgB,MAAM,mBAAmB,IAAI,CAAC,CAAC;AAAA,QAC7E;AAAA,MACF;AAEA,UAAI,QAAQ,iBAAiB,KAAK,SAAS,GAAG;AAC5C,cAAM,QAAQ,IAAI,KAAK,IAAI,CAAC,QAAQ,QAAQ,cAAe,GAAG,CAAC,CAAC;AAAA,MAClE;AAAA,IACF,SAAS,OAAO;AACd,aAAO;AAAA,QACL,EAAE,OAAO,8BAA8B,SAAU,MAAgB,QAAQ;AAAA,QACzE,EAAE,QAAQ,IAAI;AAAA,MAChB;AAAA,IACF;AAEA,WAAO,aAAa;AAAA,MAClB,IAAI;AAAA,MACJ,OAAO,QAAQ,SAAS;AAAA,MACxB,aAAa;AAAA,QACX;AAAA,QACA;AAAA,MACF;AAAA,IACF,CAAC;AAAA,EACH;AACF;;;AC5LO,SAAS,6BAAsE;AACpF,SAAO,OAAO,YAAwC;AAEpD,UAAM,EAAE,iBAAiB,IAAI,sBAAsB;AAEnD,QAAI,CAAC,kBAAkB;AACrB,aAAO,IAAI;AAAA,QACT,KAAK,UAAU,EAAE,OAAO,qCAAqC,CAAC;AAAA,QAC9D,EAAE,QAAQ,KAAK,SAAS,EAAE,gBAAgB,mBAAmB,EAAE;AAAA,MACjE;AAAA,IACF;AAGA,UAAM,YAAY,MAAM;AAAA;AAAA,MAAiC;AAAA,IAAsB;AAI/E,UAAM,EAAE,gBAAgB,cAAc,IAAI;AAE1C,UAAM,UAAU,6BAA6B;AAAA,MAC3C,QAAQ;AAAA,MACR;AAAA,MACA;AAAA,IACF,CAAC;AAED,WAAO,QAAQ,OAAO;AAAA,EACxB;AACF;AAEO,SAAS,4BAAqE;AACnF,SAAO,OAAO,YAAwC;AACpD,UAAM,EAAE,gBAAgB,QAAQ,QAAQ,aAAa,cAAc,IACjE,sBAAsB;AAExB,QAAI,CAAC,gBAAgB;AACnB,aAAO,IAAI;AAAA,QACT,KAAK,UAAU,EAAE,OAAO,2BAA2B,CAAC;AAAA,QACpD,EAAE,QAAQ,KAAK,SAAS,EAAE,gBAAgB,mBAAmB,EAAE;AAAA,MACjE;AAAA,IACF;AAEA,UAAM,WAAW,QAAQ,QAAQ,IAAI,WAAW;AAChD,QAAI,aAAa,gBAAgB;AAC/B,aAAO,IAAI;AAAA,QACT,KAAK,UAAU,EAAE,OAAO,eAAe,CAAC;AAAA,QACxC,EAAE,QAAQ,KAAK,SAAS,EAAE,gBAAgB,mBAAmB,EAAE;AAAA,MACjE;AAAA,IACF;AAEA,UAAM,UAAU,MAAM,QAAQ,KAAK;AACnC,UAAM,WAAW;AAAA,MACf,GAAG;AAAA,MACH,aAAa,eAAe,QAAQ;AAAA,MACpC,eAAe,iBAAiB,QAAQ;AAAA,IAC1C;AAEA,UAAM,WAAW,MAAM,MAAM,GAAG,MAAM,wBAAwB;AAAA,MAC5D,QAAQ;AAAA,MACR,SAAS;AAAA,QACP,gBAAgB;AAAA,QAChB,GAAG,iBAAiB,QAAQ,QAAQ;AAAA,MACtC;AAAA,MACA,MAAM,KAAK,UAAU,QAAQ;AAAA,IAC/B,CAAC;AAED,UAAM,OAAO,MAAM,SAAS,KAAK;AACjC,UAAM,cAAc,SAAS,QAAQ,IAAI,cAAc,KAAK;AAE5D,WAAO,IAAI,SAAS,QAAQ,MAAM;AAAA,MAChC,QAAQ,SAAS;AAAA,MACjB,SAAS,EAAE,gBAAgB,YAAY;AAAA,IACzC,CAAC;AAAA,EACH;AACF;","names":["normalizeSiteUrl"]}
|
package/dist/react.cjs
ADDED
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/react/index.ts
|
|
21
|
+
var react_exports = {};
|
|
22
|
+
__export(react_exports, {
|
|
23
|
+
BlogPost: () => BlogPost,
|
|
24
|
+
BlogPostList: () => BlogPostList,
|
|
25
|
+
Markdown: () => Markdown
|
|
26
|
+
});
|
|
27
|
+
module.exports = __toCommonJS(react_exports);
|
|
28
|
+
|
|
29
|
+
// src/render.ts
|
|
30
|
+
var CODE_BLOCK_TOKEN = "__BLOGAUTO_CODE_BLOCK_";
|
|
31
|
+
function escapeHtml(value) {
|
|
32
|
+
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
33
|
+
}
|
|
34
|
+
function renderInline(input) {
|
|
35
|
+
let output = escapeHtml(input);
|
|
36
|
+
output = output.replace(/\*\*(.+?)\*\*/g, "<strong>$1</strong>");
|
|
37
|
+
output = output.replace(/\*(.+?)\*/g, "<em>$1</em>");
|
|
38
|
+
output = output.replace(/`([^`]+)`/g, (_, code) => `<code>${code}</code>`);
|
|
39
|
+
output = output.replace(/!\[([^\]]*)\]\(([^)]+)\)/g, (_, alt, src) => {
|
|
40
|
+
const safeSrc = escapeHtml(src);
|
|
41
|
+
const safeAlt = escapeHtml(alt);
|
|
42
|
+
return `<img src="${safeSrc}" alt="${safeAlt}" />`;
|
|
43
|
+
});
|
|
44
|
+
output = output.replace(/\[(.+?)\]\((.+?)\)/g, (_, label, href) => {
|
|
45
|
+
const safeHref = escapeHtml(href);
|
|
46
|
+
return `<a href="${safeHref}" target="_blank" rel="noreferrer">${label}</a>`;
|
|
47
|
+
});
|
|
48
|
+
output = output.replace(/\n/g, "<br />");
|
|
49
|
+
return output;
|
|
50
|
+
}
|
|
51
|
+
function replaceCodeBlocks(markdown, blocks) {
|
|
52
|
+
return markdown.replace(/```([\s\S]*?)```/g, (_, code) => {
|
|
53
|
+
const index = blocks.push(`<pre><code>${escapeHtml(code.trim())}</code></pre>`) - 1;
|
|
54
|
+
return `${CODE_BLOCK_TOKEN}${index}__`;
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
function restoreCodeBlocks(html, blocks) {
|
|
58
|
+
return html.replace(/__BLOGAUTO_CODE_BLOCK_(\d+)__/g, (_, rawIndex) => blocks[Number(rawIndex)] ?? "");
|
|
59
|
+
}
|
|
60
|
+
function renderMarkdownToHtml(markdown) {
|
|
61
|
+
if (!markdown) {
|
|
62
|
+
return "";
|
|
63
|
+
}
|
|
64
|
+
const codeBlocks = [];
|
|
65
|
+
const withoutCode = replaceCodeBlocks(markdown, codeBlocks);
|
|
66
|
+
const normalized = withoutCode.replace(/\r\n/g, "\n");
|
|
67
|
+
const withHeadingBreaks = normalized.replace(/\n(#{1,6}\s)/g, "\n\n$1").replace(/(#{1,6}\s[^\n]+)\n(?!#|\n)/g, "$1\n\n");
|
|
68
|
+
const blocks = withHeadingBreaks.split(/\n{2,}/).map((block) => block.trim()).filter(Boolean);
|
|
69
|
+
const htmlBlocks = blocks.map((block) => {
|
|
70
|
+
const headingMatch = block.match(/^(#{1,6})\s+(.*)$/);
|
|
71
|
+
if (headingMatch) {
|
|
72
|
+
const level = headingMatch[1].length;
|
|
73
|
+
const content = renderInline(headingMatch[2]);
|
|
74
|
+
return `<h${level}>${content}</h${level}>`;
|
|
75
|
+
}
|
|
76
|
+
const imageMatch = block.match(/^!\[([^\]]*)\]\(([^)]+)\)$/);
|
|
77
|
+
if (imageMatch) {
|
|
78
|
+
const safeSrc = escapeHtml(imageMatch[2]);
|
|
79
|
+
const safeAlt = escapeHtml(imageMatch[1]);
|
|
80
|
+
return `<img src="${safeSrc}" alt="${safeAlt}" />`;
|
|
81
|
+
}
|
|
82
|
+
return `<p>${renderInline(block)}</p>`;
|
|
83
|
+
});
|
|
84
|
+
const html = htmlBlocks.join("\n");
|
|
85
|
+
return restoreCodeBlocks(html, codeBlocks);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// src/react/Markdown.tsx
|
|
89
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
90
|
+
function Markdown({ source, className = "ba-markdown" }) {
|
|
91
|
+
if (!source) {
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
95
|
+
"div",
|
|
96
|
+
{
|
|
97
|
+
className,
|
|
98
|
+
dangerouslySetInnerHTML: { __html: renderMarkdownToHtml(source) }
|
|
99
|
+
}
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// src/react/BlogPost.tsx
|
|
104
|
+
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
105
|
+
function formatDate(dateStr) {
|
|
106
|
+
return new Date(dateStr).toLocaleDateString(void 0, {
|
|
107
|
+
year: "numeric",
|
|
108
|
+
month: "long",
|
|
109
|
+
day: "numeric"
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
function BlogPost({
|
|
113
|
+
post,
|
|
114
|
+
showTitle = true,
|
|
115
|
+
showDate = true,
|
|
116
|
+
className = "ba-post",
|
|
117
|
+
renderContent
|
|
118
|
+
}) {
|
|
119
|
+
const readingTime = post.metadata?.readingTimeMinutes;
|
|
120
|
+
const dateStr = post.publishedAt ?? post.updatedAt;
|
|
121
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("article", { className, children: [
|
|
122
|
+
showTitle && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h1", { className: "ba-post-title", children: post.title }),
|
|
123
|
+
(showDate || readingTime) && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("p", { className: "ba-post-meta", children: [
|
|
124
|
+
showDate && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("time", { dateTime: dateStr, children: formatDate(dateStr) }),
|
|
125
|
+
showDate && readingTime && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "ba-post-meta-sep", children: " \xB7 " }),
|
|
126
|
+
readingTime && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "ba-post-reading-time", children: [
|
|
127
|
+
readingTime,
|
|
128
|
+
" min read"
|
|
129
|
+
] })
|
|
130
|
+
] }),
|
|
131
|
+
renderContent ? renderContent(post.content) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Markdown, { source: post.content })
|
|
132
|
+
] });
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// src/react/BlogPostList.tsx
|
|
136
|
+
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
137
|
+
function DefaultLink({
|
|
138
|
+
href,
|
|
139
|
+
className,
|
|
140
|
+
children
|
|
141
|
+
}) {
|
|
142
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("a", { href, className, children });
|
|
143
|
+
}
|
|
144
|
+
function formatDate2(dateStr) {
|
|
145
|
+
return new Date(dateStr).toLocaleDateString(void 0, {
|
|
146
|
+
year: "numeric",
|
|
147
|
+
month: "long",
|
|
148
|
+
day: "numeric"
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
function BlogPostList({
|
|
152
|
+
posts,
|
|
153
|
+
title = "Latest posts",
|
|
154
|
+
routePrefix = "/blog",
|
|
155
|
+
linkComponent,
|
|
156
|
+
className = "ba-listing",
|
|
157
|
+
renderCard
|
|
158
|
+
}) {
|
|
159
|
+
const Link = linkComponent ?? DefaultLink;
|
|
160
|
+
const prefix = routePrefix.replace(/\/$/, "");
|
|
161
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("section", { className, children: [
|
|
162
|
+
title && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h1", { className: "ba-listing-title", children: title }),
|
|
163
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "ba-posts", children: posts.map((post) => {
|
|
164
|
+
const href = `${prefix}/${post.slug}`;
|
|
165
|
+
if (renderCard) {
|
|
166
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { children: renderCard(post, href) }, post.id);
|
|
167
|
+
}
|
|
168
|
+
const dateStr = post.publishedAt ?? post.updatedAt;
|
|
169
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("article", { className: "ba-post-card", children: [
|
|
170
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h2", { className: "ba-post-card-title", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Link, { href, className: "ba-post-card-link", children: post.title }) }),
|
|
171
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "ba-post-card-meta", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("time", { dateTime: dateStr, children: formatDate2(dateStr) }) }),
|
|
172
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "ba-post-card-excerpt", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Markdown, { source: post.excerpt ?? post.content.slice(0, 180) }) })
|
|
173
|
+
] }, post.id);
|
|
174
|
+
}) })
|
|
175
|
+
] });
|
|
176
|
+
}
|
|
177
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
178
|
+
0 && (module.exports = {
|
|
179
|
+
BlogPost,
|
|
180
|
+
BlogPostList,
|
|
181
|
+
Markdown
|
|
182
|
+
});
|
|
183
|
+
//# sourceMappingURL=react.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/react/index.ts","../src/render.ts","../src/react/Markdown.tsx","../src/react/BlogPost.tsx","../src/react/BlogPostList.tsx"],"sourcesContent":["export { Markdown, type MarkdownProps } from \"./Markdown\";\nexport { BlogPost, type BlogPostProps } from \"./BlogPost\";\nexport { BlogPostList, type BlogPostListProps } from \"./BlogPostList\";\n","const CODE_BLOCK_TOKEN = \"__BLOGAUTO_CODE_BLOCK_\";\n\nfunction escapeHtml(value: string): string {\n return value\n .replace(/&/g, \"&\")\n .replace(/</g, \"<\")\n .replace(/>/g, \">\")\n .replace(/\"/g, \""\")\n .replace(/'/g, \"'\");\n}\n\nfunction renderInline(input: string): string {\n let output = escapeHtml(input);\n output = output.replace(/\\*\\*(.+?)\\*\\*/g, \"<strong>$1</strong>\");\n output = output.replace(/\\*(.+?)\\*/g, \"<em>$1</em>\");\n output = output.replace(/`([^`]+)`/g, (_, code) => `<code>${code}</code>`);\n output = output.replace(/!\\[([^\\]]*)\\]\\(([^)]+)\\)/g, (_, alt, src) => {\n const safeSrc = escapeHtml(src);\n const safeAlt = escapeHtml(alt);\n return `<img src=\"${safeSrc}\" alt=\"${safeAlt}\" />`;\n });\n output = output.replace(/\\[(.+?)\\]\\((.+?)\\)/g, (_, label, href) => {\n const safeHref = escapeHtml(href);\n return `<a href=\"${safeHref}\" target=\"_blank\" rel=\"noreferrer\">${label}</a>`;\n });\n output = output.replace(/\\n/g, \"<br />\");\n return output;\n}\n\nfunction replaceCodeBlocks(markdown: string, blocks: string[]): string {\n return markdown.replace(/```([\\s\\S]*?)```/g, (_, code) => {\n const index = blocks.push(`<pre><code>${escapeHtml(code.trim())}</code></pre>`) - 1;\n return `${CODE_BLOCK_TOKEN}${index}__`;\n });\n}\n\nfunction restoreCodeBlocks(html: string, blocks: string[]): string {\n return html.replace(/__BLOGAUTO_CODE_BLOCK_(\\d+)__/g, (_, rawIndex) => blocks[Number(rawIndex)] ?? \"\");\n}\n\nexport function renderMarkdownToHtml(markdown: string): string {\n if (!markdown) {\n return \"\";\n }\n\n const codeBlocks: string[] = [];\n const withoutCode = replaceCodeBlocks(markdown, codeBlocks);\n const normalized = withoutCode.replace(/\\r\\n/g, \"\\n\");\n // Insert blank lines around heading lines so they become separate blocks\n const withHeadingBreaks = normalized\n .replace(/\\n(#{1,6}\\s)/g, \"\\n\\n$1\")\n .replace(/(#{1,6}\\s[^\\n]+)\\n(?!#|\\n)/g, \"$1\\n\\n\");\n const blocks = withHeadingBreaks\n .split(/\\n{2,}/)\n .map((block) => block.trim())\n .filter(Boolean);\n\n const htmlBlocks = blocks.map((block) => {\n const headingMatch = block.match(/^(#{1,6})\\s+(.*)$/);\n if (headingMatch) {\n const level = headingMatch[1].length;\n const content = renderInline(headingMatch[2]);\n return `<h${level}>${content}</h${level}>`;\n }\n\n // Check if block is a standalone image\n const imageMatch = block.match(/^!\\[([^\\]]*)\\]\\(([^)]+)\\)$/);\n if (imageMatch) {\n const safeSrc = escapeHtml(imageMatch[2]);\n const safeAlt = escapeHtml(imageMatch[1]);\n return `<img src=\"${safeSrc}\" alt=\"${safeAlt}\" />`;\n }\n\n return `<p>${renderInline(block)}</p>`;\n });\n\n const html = htmlBlocks.join(\"\\n\");\n return restoreCodeBlocks(html, codeBlocks);\n}\n","import { renderMarkdownToHtml } from \"../render\";\n\nexport interface MarkdownProps {\n source?: string | null;\n className?: string;\n}\n\nexport function Markdown({ source, className = \"ba-markdown\" }: MarkdownProps) {\n if (!source) {\n return null;\n }\n\n return (\n <div\n className={className}\n dangerouslySetInnerHTML={{ __html: renderMarkdownToHtml(source) }}\n />\n );\n}\n","import type { BlogPost as BlogPostType } from \"../types\";\nimport { Markdown } from \"./Markdown\";\n\nexport interface BlogPostProps {\n post: BlogPostType;\n showTitle?: boolean;\n showDate?: boolean;\n className?: string;\n renderContent?: (content: string) => React.ReactNode;\n}\n\nfunction formatDate(dateStr: string): string {\n return new Date(dateStr).toLocaleDateString(undefined, {\n year: \"numeric\",\n month: \"long\",\n day: \"numeric\",\n });\n}\n\nexport function BlogPost({\n post,\n showTitle = true,\n showDate = true,\n className = \"ba-post\",\n renderContent,\n}: BlogPostProps) {\n const readingTime = post.metadata?.readingTimeMinutes;\n const dateStr = post.publishedAt ?? post.updatedAt;\n\n return (\n <article className={className}>\n {showTitle && <h1 className=\"ba-post-title\">{post.title}</h1>}\n {(showDate || readingTime) && (\n <p className=\"ba-post-meta\">\n {showDate && (\n <time dateTime={dateStr}>{formatDate(dateStr)}</time>\n )}\n {showDate && readingTime && <span className=\"ba-post-meta-sep\"> · </span>}\n {readingTime && (\n <span className=\"ba-post-reading-time\">{readingTime} min read</span>\n )}\n </p>\n )}\n {renderContent ? renderContent(post.content) : <Markdown source={post.content} />}\n </article>\n );\n}\n","import type { BlogPost } from \"../types\";\nimport { Markdown } from \"./Markdown\";\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype LinkComponent = React.ComponentType<any>;\n\nexport interface BlogPostListProps {\n posts: BlogPost[];\n title?: string;\n routePrefix?: string;\n linkComponent?: LinkComponent;\n className?: string;\n renderCard?: (post: BlogPost, href: string) => React.ReactNode;\n}\n\nfunction DefaultLink({\n href,\n className,\n children,\n}: {\n href: string;\n className?: string;\n children: React.ReactNode;\n}) {\n return (\n <a href={href} className={className}>\n {children}\n </a>\n );\n}\n\nfunction formatDate(dateStr: string): string {\n return new Date(dateStr).toLocaleDateString(undefined, {\n year: \"numeric\",\n month: \"long\",\n day: \"numeric\",\n });\n}\n\nexport function BlogPostList({\n posts,\n title = \"Latest posts\",\n routePrefix = \"/blog\",\n linkComponent,\n className = \"ba-listing\",\n renderCard,\n}: BlogPostListProps) {\n const Link = linkComponent ?? DefaultLink;\n const prefix = routePrefix.replace(/\\/$/, \"\");\n\n return (\n <section className={className}>\n {title && <h1 className=\"ba-listing-title\">{title}</h1>}\n <div className=\"ba-posts\">\n {posts.map((post) => {\n const href = `${prefix}/${post.slug}`;\n\n if (renderCard) {\n return <div key={post.id}>{renderCard(post, href)}</div>;\n }\n\n const dateStr = post.publishedAt ?? post.updatedAt;\n\n return (\n <article key={post.id} className=\"ba-post-card\">\n <h2 className=\"ba-post-card-title\">\n <Link href={href} className=\"ba-post-card-link\">\n {post.title}\n </Link>\n </h2>\n <p className=\"ba-post-card-meta\">\n <time dateTime={dateStr}>{formatDate(dateStr)}</time>\n </p>\n <div className=\"ba-post-card-excerpt\">\n <Markdown source={post.excerpt ?? post.content.slice(0, 180)} />\n </div>\n </article>\n );\n })}\n </div>\n </section>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,IAAM,mBAAmB;AAEzB,SAAS,WAAW,OAAuB;AACzC,SAAO,MACJ,QAAQ,MAAM,OAAO,EACrB,QAAQ,MAAM,MAAM,EACpB,QAAQ,MAAM,MAAM,EACpB,QAAQ,MAAM,QAAQ,EACtB,QAAQ,MAAM,OAAO;AAC1B;AAEA,SAAS,aAAa,OAAuB;AAC3C,MAAI,SAAS,WAAW,KAAK;AAC7B,WAAS,OAAO,QAAQ,kBAAkB,qBAAqB;AAC/D,WAAS,OAAO,QAAQ,cAAc,aAAa;AACnD,WAAS,OAAO,QAAQ,cAAc,CAAC,GAAG,SAAS,SAAS,IAAI,SAAS;AACzE,WAAS,OAAO,QAAQ,6BAA6B,CAAC,GAAG,KAAK,QAAQ;AACpE,UAAM,UAAU,WAAW,GAAG;AAC9B,UAAM,UAAU,WAAW,GAAG;AAC9B,WAAO,aAAa,OAAO,UAAU,OAAO;AAAA,EAC9C,CAAC;AACD,WAAS,OAAO,QAAQ,uBAAuB,CAAC,GAAG,OAAO,SAAS;AACjE,UAAM,WAAW,WAAW,IAAI;AAChC,WAAO,YAAY,QAAQ,sCAAsC,KAAK;AAAA,EACxE,CAAC;AACD,WAAS,OAAO,QAAQ,OAAO,QAAQ;AACvC,SAAO;AACT;AAEA,SAAS,kBAAkB,UAAkB,QAA0B;AACrE,SAAO,SAAS,QAAQ,qBAAqB,CAAC,GAAG,SAAS;AACxD,UAAM,QAAQ,OAAO,KAAK,cAAc,WAAW,KAAK,KAAK,CAAC,CAAC,eAAe,IAAI;AAClF,WAAO,GAAG,gBAAgB,GAAG,KAAK;AAAA,EACpC,CAAC;AACH;AAEA,SAAS,kBAAkB,MAAc,QAA0B;AACjE,SAAO,KAAK,QAAQ,kCAAkC,CAAC,GAAG,aAAa,OAAO,OAAO,QAAQ,CAAC,KAAK,EAAE;AACvG;AAEO,SAAS,qBAAqB,UAA0B;AAC7D,MAAI,CAAC,UAAU;AACb,WAAO;AAAA,EACT;AAEA,QAAM,aAAuB,CAAC;AAC9B,QAAM,cAAc,kBAAkB,UAAU,UAAU;AAC1D,QAAM,aAAa,YAAY,QAAQ,SAAS,IAAI;AAEpD,QAAM,oBAAoB,WACvB,QAAQ,iBAAiB,QAAQ,EACjC,QAAQ,+BAA+B,QAAQ;AAClD,QAAM,SAAS,kBACZ,MAAM,QAAQ,EACd,IAAI,CAAC,UAAU,MAAM,KAAK,CAAC,EAC3B,OAAO,OAAO;AAEjB,QAAM,aAAa,OAAO,IAAI,CAAC,UAAU;AACvC,UAAM,eAAe,MAAM,MAAM,mBAAmB;AACpD,QAAI,cAAc;AAChB,YAAM,QAAQ,aAAa,CAAC,EAAE;AAC9B,YAAM,UAAU,aAAa,aAAa,CAAC,CAAC;AAC5C,aAAO,KAAK,KAAK,IAAI,OAAO,MAAM,KAAK;AAAA,IACzC;AAGA,UAAM,aAAa,MAAM,MAAM,4BAA4B;AAC3D,QAAI,YAAY;AACd,YAAM,UAAU,WAAW,WAAW,CAAC,CAAC;AACxC,YAAM,UAAU,WAAW,WAAW,CAAC,CAAC;AACxC,aAAO,aAAa,OAAO,UAAU,OAAO;AAAA,IAC9C;AAEA,WAAO,MAAM,aAAa,KAAK,CAAC;AAAA,EAClC,CAAC;AAED,QAAM,OAAO,WAAW,KAAK,IAAI;AACjC,SAAO,kBAAkB,MAAM,UAAU;AAC3C;;;ACjEI;AANG,SAAS,SAAS,EAAE,QAAQ,YAAY,cAAc,GAAkB;AAC7E,MAAI,CAAC,QAAQ;AACX,WAAO;AAAA,EACT;AAEA,SACE;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA,yBAAyB,EAAE,QAAQ,qBAAqB,MAAM,EAAE;AAAA;AAAA,EAClE;AAEJ;;;ACaoB,IAAAA,sBAAA;AApBpB,SAAS,WAAW,SAAyB;AAC3C,SAAO,IAAI,KAAK,OAAO,EAAE,mBAAmB,QAAW;AAAA,IACrD,MAAM;AAAA,IACN,OAAO;AAAA,IACP,KAAK;AAAA,EACP,CAAC;AACH;AAEO,SAAS,SAAS;AAAA,EACvB;AAAA,EACA,YAAY;AAAA,EACZ,WAAW;AAAA,EACX,YAAY;AAAA,EACZ;AACF,GAAkB;AAChB,QAAM,cAAc,KAAK,UAAU;AACnC,QAAM,UAAU,KAAK,eAAe,KAAK;AAEzC,SACE,8CAAC,aAAQ,WACN;AAAA,iBAAa,6CAAC,QAAG,WAAU,iBAAiB,eAAK,OAAM;AAAA,KACtD,YAAY,gBACZ,8CAAC,OAAE,WAAU,gBACV;AAAA,kBACC,6CAAC,UAAK,UAAU,SAAU,qBAAW,OAAO,GAAE;AAAA,MAE/C,YAAY,eAAe,6CAAC,UAAK,WAAU,oBAAmB,oBAAG;AAAA,MACjE,eACC,8CAAC,UAAK,WAAU,wBAAwB;AAAA;AAAA,QAAY;AAAA,SAAS;AAAA,OAEjE;AAAA,IAED,gBAAgB,cAAc,KAAK,OAAO,IAAI,6CAAC,YAAS,QAAQ,KAAK,SAAS;AAAA,KACjF;AAEJ;;;ACrBI,IAAAC,sBAAA;AAVJ,SAAS,YAAY;AAAA,EACnB;AAAA,EACA;AAAA,EACA;AACF,GAIG;AACD,SACE,6CAAC,OAAE,MAAY,WACZ,UACH;AAEJ;AAEA,SAASC,YAAW,SAAyB;AAC3C,SAAO,IAAI,KAAK,OAAO,EAAE,mBAAmB,QAAW;AAAA,IACrD,MAAM;AAAA,IACN,OAAO;AAAA,IACP,KAAK;AAAA,EACP,CAAC;AACH;AAEO,SAAS,aAAa;AAAA,EAC3B;AAAA,EACA,QAAQ;AAAA,EACR,cAAc;AAAA,EACd;AAAA,EACA,YAAY;AAAA,EACZ;AACF,GAAsB;AACpB,QAAM,OAAO,iBAAiB;AAC9B,QAAM,SAAS,YAAY,QAAQ,OAAO,EAAE;AAE5C,SACE,8CAAC,aAAQ,WACN;AAAA,aAAS,6CAAC,QAAG,WAAU,oBAAoB,iBAAM;AAAA,IAClD,6CAAC,SAAI,WAAU,YACZ,gBAAM,IAAI,CAAC,SAAS;AACnB,YAAM,OAAO,GAAG,MAAM,IAAI,KAAK,IAAI;AAEnC,UAAI,YAAY;AACd,eAAO,6CAAC,SAAmB,qBAAW,MAAM,IAAI,KAA/B,KAAK,EAA4B;AAAA,MACpD;AAEA,YAAM,UAAU,KAAK,eAAe,KAAK;AAEzC,aACE,8CAAC,aAAsB,WAAU,gBAC/B;AAAA,qDAAC,QAAG,WAAU,sBACZ,uDAAC,QAAK,MAAY,WAAU,qBACzB,eAAK,OACR,GACF;AAAA,QACA,6CAAC,OAAE,WAAU,qBACX,uDAAC,UAAK,UAAU,SAAU,UAAAA,YAAW,OAAO,GAAE,GAChD;AAAA,QACA,6CAAC,SAAI,WAAU,wBACb,uDAAC,YAAS,QAAQ,KAAK,WAAW,KAAK,QAAQ,MAAM,GAAG,GAAG,GAAG,GAChE;AAAA,WAXY,KAAK,EAYnB;AAAA,IAEJ,CAAC,GACH;AAAA,KACF;AAEJ;","names":["import_jsx_runtime","import_jsx_runtime","formatDate"]}
|
package/dist/react.d.cts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import { a as BlogPost$1 } from './types-CCDRs0sO.cjs';
|
|
3
|
+
|
|
4
|
+
interface MarkdownProps {
|
|
5
|
+
source?: string | null;
|
|
6
|
+
className?: string;
|
|
7
|
+
}
|
|
8
|
+
declare function Markdown({ source, className }: MarkdownProps): react_jsx_runtime.JSX.Element | null;
|
|
9
|
+
|
|
10
|
+
interface BlogPostProps {
|
|
11
|
+
post: BlogPost$1;
|
|
12
|
+
showTitle?: boolean;
|
|
13
|
+
showDate?: boolean;
|
|
14
|
+
className?: string;
|
|
15
|
+
renderContent?: (content: string) => React.ReactNode;
|
|
16
|
+
}
|
|
17
|
+
declare function BlogPost({ post, showTitle, showDate, className, renderContent, }: BlogPostProps): react_jsx_runtime.JSX.Element;
|
|
18
|
+
|
|
19
|
+
type LinkComponent = React.ComponentType<any>;
|
|
20
|
+
interface BlogPostListProps {
|
|
21
|
+
posts: BlogPost$1[];
|
|
22
|
+
title?: string;
|
|
23
|
+
routePrefix?: string;
|
|
24
|
+
linkComponent?: LinkComponent;
|
|
25
|
+
className?: string;
|
|
26
|
+
renderCard?: (post: BlogPost$1, href: string) => React.ReactNode;
|
|
27
|
+
}
|
|
28
|
+
declare function BlogPostList({ posts, title, routePrefix, linkComponent, className, renderCard, }: BlogPostListProps): react_jsx_runtime.JSX.Element;
|
|
29
|
+
|
|
30
|
+
export { BlogPost, BlogPostList, type BlogPostListProps, type BlogPostProps, Markdown, type MarkdownProps };
|
package/dist/react.d.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import { a as BlogPost$1 } from './types-CCDRs0sO.js';
|
|
3
|
+
|
|
4
|
+
interface MarkdownProps {
|
|
5
|
+
source?: string | null;
|
|
6
|
+
className?: string;
|
|
7
|
+
}
|
|
8
|
+
declare function Markdown({ source, className }: MarkdownProps): react_jsx_runtime.JSX.Element | null;
|
|
9
|
+
|
|
10
|
+
interface BlogPostProps {
|
|
11
|
+
post: BlogPost$1;
|
|
12
|
+
showTitle?: boolean;
|
|
13
|
+
showDate?: boolean;
|
|
14
|
+
className?: string;
|
|
15
|
+
renderContent?: (content: string) => React.ReactNode;
|
|
16
|
+
}
|
|
17
|
+
declare function BlogPost({ post, showTitle, showDate, className, renderContent, }: BlogPostProps): react_jsx_runtime.JSX.Element;
|
|
18
|
+
|
|
19
|
+
type LinkComponent = React.ComponentType<any>;
|
|
20
|
+
interface BlogPostListProps {
|
|
21
|
+
posts: BlogPost$1[];
|
|
22
|
+
title?: string;
|
|
23
|
+
routePrefix?: string;
|
|
24
|
+
linkComponent?: LinkComponent;
|
|
25
|
+
className?: string;
|
|
26
|
+
renderCard?: (post: BlogPost$1, href: string) => React.ReactNode;
|
|
27
|
+
}
|
|
28
|
+
declare function BlogPostList({ posts, title, routePrefix, linkComponent, className, renderCard, }: BlogPostListProps): react_jsx_runtime.JSX.Element;
|
|
29
|
+
|
|
30
|
+
export { BlogPost, BlogPostList, type BlogPostListProps, type BlogPostProps, Markdown, type MarkdownProps };
|
package/dist/react.js
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
// src/render.ts
|
|
2
|
+
var CODE_BLOCK_TOKEN = "__BLOGAUTO_CODE_BLOCK_";
|
|
3
|
+
function escapeHtml(value) {
|
|
4
|
+
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
5
|
+
}
|
|
6
|
+
function renderInline(input) {
|
|
7
|
+
let output = escapeHtml(input);
|
|
8
|
+
output = output.replace(/\*\*(.+?)\*\*/g, "<strong>$1</strong>");
|
|
9
|
+
output = output.replace(/\*(.+?)\*/g, "<em>$1</em>");
|
|
10
|
+
output = output.replace(/`([^`]+)`/g, (_, code) => `<code>${code}</code>`);
|
|
11
|
+
output = output.replace(/!\[([^\]]*)\]\(([^)]+)\)/g, (_, alt, src) => {
|
|
12
|
+
const safeSrc = escapeHtml(src);
|
|
13
|
+
const safeAlt = escapeHtml(alt);
|
|
14
|
+
return `<img src="${safeSrc}" alt="${safeAlt}" />`;
|
|
15
|
+
});
|
|
16
|
+
output = output.replace(/\[(.+?)\]\((.+?)\)/g, (_, label, href) => {
|
|
17
|
+
const safeHref = escapeHtml(href);
|
|
18
|
+
return `<a href="${safeHref}" target="_blank" rel="noreferrer">${label}</a>`;
|
|
19
|
+
});
|
|
20
|
+
output = output.replace(/\n/g, "<br />");
|
|
21
|
+
return output;
|
|
22
|
+
}
|
|
23
|
+
function replaceCodeBlocks(markdown, blocks) {
|
|
24
|
+
return markdown.replace(/```([\s\S]*?)```/g, (_, code) => {
|
|
25
|
+
const index = blocks.push(`<pre><code>${escapeHtml(code.trim())}</code></pre>`) - 1;
|
|
26
|
+
return `${CODE_BLOCK_TOKEN}${index}__`;
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
function restoreCodeBlocks(html, blocks) {
|
|
30
|
+
return html.replace(/__BLOGAUTO_CODE_BLOCK_(\d+)__/g, (_, rawIndex) => blocks[Number(rawIndex)] ?? "");
|
|
31
|
+
}
|
|
32
|
+
function renderMarkdownToHtml(markdown) {
|
|
33
|
+
if (!markdown) {
|
|
34
|
+
return "";
|
|
35
|
+
}
|
|
36
|
+
const codeBlocks = [];
|
|
37
|
+
const withoutCode = replaceCodeBlocks(markdown, codeBlocks);
|
|
38
|
+
const normalized = withoutCode.replace(/\r\n/g, "\n");
|
|
39
|
+
const withHeadingBreaks = normalized.replace(/\n(#{1,6}\s)/g, "\n\n$1").replace(/(#{1,6}\s[^\n]+)\n(?!#|\n)/g, "$1\n\n");
|
|
40
|
+
const blocks = withHeadingBreaks.split(/\n{2,}/).map((block) => block.trim()).filter(Boolean);
|
|
41
|
+
const htmlBlocks = blocks.map((block) => {
|
|
42
|
+
const headingMatch = block.match(/^(#{1,6})\s+(.*)$/);
|
|
43
|
+
if (headingMatch) {
|
|
44
|
+
const level = headingMatch[1].length;
|
|
45
|
+
const content = renderInline(headingMatch[2]);
|
|
46
|
+
return `<h${level}>${content}</h${level}>`;
|
|
47
|
+
}
|
|
48
|
+
const imageMatch = block.match(/^!\[([^\]]*)\]\(([^)]+)\)$/);
|
|
49
|
+
if (imageMatch) {
|
|
50
|
+
const safeSrc = escapeHtml(imageMatch[2]);
|
|
51
|
+
const safeAlt = escapeHtml(imageMatch[1]);
|
|
52
|
+
return `<img src="${safeSrc}" alt="${safeAlt}" />`;
|
|
53
|
+
}
|
|
54
|
+
return `<p>${renderInline(block)}</p>`;
|
|
55
|
+
});
|
|
56
|
+
const html = htmlBlocks.join("\n");
|
|
57
|
+
return restoreCodeBlocks(html, codeBlocks);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// src/react/Markdown.tsx
|
|
61
|
+
import { jsx } from "react/jsx-runtime";
|
|
62
|
+
function Markdown({ source, className = "ba-markdown" }) {
|
|
63
|
+
if (!source) {
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
return /* @__PURE__ */ jsx(
|
|
67
|
+
"div",
|
|
68
|
+
{
|
|
69
|
+
className,
|
|
70
|
+
dangerouslySetInnerHTML: { __html: renderMarkdownToHtml(source) }
|
|
71
|
+
}
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// src/react/BlogPost.tsx
|
|
76
|
+
import { jsx as jsx2, jsxs } from "react/jsx-runtime";
|
|
77
|
+
function formatDate(dateStr) {
|
|
78
|
+
return new Date(dateStr).toLocaleDateString(void 0, {
|
|
79
|
+
year: "numeric",
|
|
80
|
+
month: "long",
|
|
81
|
+
day: "numeric"
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
function BlogPost({
|
|
85
|
+
post,
|
|
86
|
+
showTitle = true,
|
|
87
|
+
showDate = true,
|
|
88
|
+
className = "ba-post",
|
|
89
|
+
renderContent
|
|
90
|
+
}) {
|
|
91
|
+
const readingTime = post.metadata?.readingTimeMinutes;
|
|
92
|
+
const dateStr = post.publishedAt ?? post.updatedAt;
|
|
93
|
+
return /* @__PURE__ */ jsxs("article", { className, children: [
|
|
94
|
+
showTitle && /* @__PURE__ */ jsx2("h1", { className: "ba-post-title", children: post.title }),
|
|
95
|
+
(showDate || readingTime) && /* @__PURE__ */ jsxs("p", { className: "ba-post-meta", children: [
|
|
96
|
+
showDate && /* @__PURE__ */ jsx2("time", { dateTime: dateStr, children: formatDate(dateStr) }),
|
|
97
|
+
showDate && readingTime && /* @__PURE__ */ jsx2("span", { className: "ba-post-meta-sep", children: " \xB7 " }),
|
|
98
|
+
readingTime && /* @__PURE__ */ jsxs("span", { className: "ba-post-reading-time", children: [
|
|
99
|
+
readingTime,
|
|
100
|
+
" min read"
|
|
101
|
+
] })
|
|
102
|
+
] }),
|
|
103
|
+
renderContent ? renderContent(post.content) : /* @__PURE__ */ jsx2(Markdown, { source: post.content })
|
|
104
|
+
] });
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// src/react/BlogPostList.tsx
|
|
108
|
+
import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
109
|
+
function DefaultLink({
|
|
110
|
+
href,
|
|
111
|
+
className,
|
|
112
|
+
children
|
|
113
|
+
}) {
|
|
114
|
+
return /* @__PURE__ */ jsx3("a", { href, className, children });
|
|
115
|
+
}
|
|
116
|
+
function formatDate2(dateStr) {
|
|
117
|
+
return new Date(dateStr).toLocaleDateString(void 0, {
|
|
118
|
+
year: "numeric",
|
|
119
|
+
month: "long",
|
|
120
|
+
day: "numeric"
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
function BlogPostList({
|
|
124
|
+
posts,
|
|
125
|
+
title = "Latest posts",
|
|
126
|
+
routePrefix = "/blog",
|
|
127
|
+
linkComponent,
|
|
128
|
+
className = "ba-listing",
|
|
129
|
+
renderCard
|
|
130
|
+
}) {
|
|
131
|
+
const Link = linkComponent ?? DefaultLink;
|
|
132
|
+
const prefix = routePrefix.replace(/\/$/, "");
|
|
133
|
+
return /* @__PURE__ */ jsxs2("section", { className, children: [
|
|
134
|
+
title && /* @__PURE__ */ jsx3("h1", { className: "ba-listing-title", children: title }),
|
|
135
|
+
/* @__PURE__ */ jsx3("div", { className: "ba-posts", children: posts.map((post) => {
|
|
136
|
+
const href = `${prefix}/${post.slug}`;
|
|
137
|
+
if (renderCard) {
|
|
138
|
+
return /* @__PURE__ */ jsx3("div", { children: renderCard(post, href) }, post.id);
|
|
139
|
+
}
|
|
140
|
+
const dateStr = post.publishedAt ?? post.updatedAt;
|
|
141
|
+
return /* @__PURE__ */ jsxs2("article", { className: "ba-post-card", children: [
|
|
142
|
+
/* @__PURE__ */ jsx3("h2", { className: "ba-post-card-title", children: /* @__PURE__ */ jsx3(Link, { href, className: "ba-post-card-link", children: post.title }) }),
|
|
143
|
+
/* @__PURE__ */ jsx3("p", { className: "ba-post-card-meta", children: /* @__PURE__ */ jsx3("time", { dateTime: dateStr, children: formatDate2(dateStr) }) }),
|
|
144
|
+
/* @__PURE__ */ jsx3("div", { className: "ba-post-card-excerpt", children: /* @__PURE__ */ jsx3(Markdown, { source: post.excerpt ?? post.content.slice(0, 180) }) })
|
|
145
|
+
] }, post.id);
|
|
146
|
+
}) })
|
|
147
|
+
] });
|
|
148
|
+
}
|
|
149
|
+
export {
|
|
150
|
+
BlogPost,
|
|
151
|
+
BlogPostList,
|
|
152
|
+
Markdown
|
|
153
|
+
};
|
|
154
|
+
//# sourceMappingURL=react.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/render.ts","../src/react/Markdown.tsx","../src/react/BlogPost.tsx","../src/react/BlogPostList.tsx"],"sourcesContent":["const CODE_BLOCK_TOKEN = \"__BLOGAUTO_CODE_BLOCK_\";\n\nfunction escapeHtml(value: string): string {\n return value\n .replace(/&/g, \"&\")\n .replace(/</g, \"<\")\n .replace(/>/g, \">\")\n .replace(/\"/g, \""\")\n .replace(/'/g, \"'\");\n}\n\nfunction renderInline(input: string): string {\n let output = escapeHtml(input);\n output = output.replace(/\\*\\*(.+?)\\*\\*/g, \"<strong>$1</strong>\");\n output = output.replace(/\\*(.+?)\\*/g, \"<em>$1</em>\");\n output = output.replace(/`([^`]+)`/g, (_, code) => `<code>${code}</code>`);\n output = output.replace(/!\\[([^\\]]*)\\]\\(([^)]+)\\)/g, (_, alt, src) => {\n const safeSrc = escapeHtml(src);\n const safeAlt = escapeHtml(alt);\n return `<img src=\"${safeSrc}\" alt=\"${safeAlt}\" />`;\n });\n output = output.replace(/\\[(.+?)\\]\\((.+?)\\)/g, (_, label, href) => {\n const safeHref = escapeHtml(href);\n return `<a href=\"${safeHref}\" target=\"_blank\" rel=\"noreferrer\">${label}</a>`;\n });\n output = output.replace(/\\n/g, \"<br />\");\n return output;\n}\n\nfunction replaceCodeBlocks(markdown: string, blocks: string[]): string {\n return markdown.replace(/```([\\s\\S]*?)```/g, (_, code) => {\n const index = blocks.push(`<pre><code>${escapeHtml(code.trim())}</code></pre>`) - 1;\n return `${CODE_BLOCK_TOKEN}${index}__`;\n });\n}\n\nfunction restoreCodeBlocks(html: string, blocks: string[]): string {\n return html.replace(/__BLOGAUTO_CODE_BLOCK_(\\d+)__/g, (_, rawIndex) => blocks[Number(rawIndex)] ?? \"\");\n}\n\nexport function renderMarkdownToHtml(markdown: string): string {\n if (!markdown) {\n return \"\";\n }\n\n const codeBlocks: string[] = [];\n const withoutCode = replaceCodeBlocks(markdown, codeBlocks);\n const normalized = withoutCode.replace(/\\r\\n/g, \"\\n\");\n // Insert blank lines around heading lines so they become separate blocks\n const withHeadingBreaks = normalized\n .replace(/\\n(#{1,6}\\s)/g, \"\\n\\n$1\")\n .replace(/(#{1,6}\\s[^\\n]+)\\n(?!#|\\n)/g, \"$1\\n\\n\");\n const blocks = withHeadingBreaks\n .split(/\\n{2,}/)\n .map((block) => block.trim())\n .filter(Boolean);\n\n const htmlBlocks = blocks.map((block) => {\n const headingMatch = block.match(/^(#{1,6})\\s+(.*)$/);\n if (headingMatch) {\n const level = headingMatch[1].length;\n const content = renderInline(headingMatch[2]);\n return `<h${level}>${content}</h${level}>`;\n }\n\n // Check if block is a standalone image\n const imageMatch = block.match(/^!\\[([^\\]]*)\\]\\(([^)]+)\\)$/);\n if (imageMatch) {\n const safeSrc = escapeHtml(imageMatch[2]);\n const safeAlt = escapeHtml(imageMatch[1]);\n return `<img src=\"${safeSrc}\" alt=\"${safeAlt}\" />`;\n }\n\n return `<p>${renderInline(block)}</p>`;\n });\n\n const html = htmlBlocks.join(\"\\n\");\n return restoreCodeBlocks(html, codeBlocks);\n}\n","import { renderMarkdownToHtml } from \"../render\";\n\nexport interface MarkdownProps {\n source?: string | null;\n className?: string;\n}\n\nexport function Markdown({ source, className = \"ba-markdown\" }: MarkdownProps) {\n if (!source) {\n return null;\n }\n\n return (\n <div\n className={className}\n dangerouslySetInnerHTML={{ __html: renderMarkdownToHtml(source) }}\n />\n );\n}\n","import type { BlogPost as BlogPostType } from \"../types\";\nimport { Markdown } from \"./Markdown\";\n\nexport interface BlogPostProps {\n post: BlogPostType;\n showTitle?: boolean;\n showDate?: boolean;\n className?: string;\n renderContent?: (content: string) => React.ReactNode;\n}\n\nfunction formatDate(dateStr: string): string {\n return new Date(dateStr).toLocaleDateString(undefined, {\n year: \"numeric\",\n month: \"long\",\n day: \"numeric\",\n });\n}\n\nexport function BlogPost({\n post,\n showTitle = true,\n showDate = true,\n className = \"ba-post\",\n renderContent,\n}: BlogPostProps) {\n const readingTime = post.metadata?.readingTimeMinutes;\n const dateStr = post.publishedAt ?? post.updatedAt;\n\n return (\n <article className={className}>\n {showTitle && <h1 className=\"ba-post-title\">{post.title}</h1>}\n {(showDate || readingTime) && (\n <p className=\"ba-post-meta\">\n {showDate && (\n <time dateTime={dateStr}>{formatDate(dateStr)}</time>\n )}\n {showDate && readingTime && <span className=\"ba-post-meta-sep\"> · </span>}\n {readingTime && (\n <span className=\"ba-post-reading-time\">{readingTime} min read</span>\n )}\n </p>\n )}\n {renderContent ? renderContent(post.content) : <Markdown source={post.content} />}\n </article>\n );\n}\n","import type { BlogPost } from \"../types\";\nimport { Markdown } from \"./Markdown\";\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype LinkComponent = React.ComponentType<any>;\n\nexport interface BlogPostListProps {\n posts: BlogPost[];\n title?: string;\n routePrefix?: string;\n linkComponent?: LinkComponent;\n className?: string;\n renderCard?: (post: BlogPost, href: string) => React.ReactNode;\n}\n\nfunction DefaultLink({\n href,\n className,\n children,\n}: {\n href: string;\n className?: string;\n children: React.ReactNode;\n}) {\n return (\n <a href={href} className={className}>\n {children}\n </a>\n );\n}\n\nfunction formatDate(dateStr: string): string {\n return new Date(dateStr).toLocaleDateString(undefined, {\n year: \"numeric\",\n month: \"long\",\n day: \"numeric\",\n });\n}\n\nexport function BlogPostList({\n posts,\n title = \"Latest posts\",\n routePrefix = \"/blog\",\n linkComponent,\n className = \"ba-listing\",\n renderCard,\n}: BlogPostListProps) {\n const Link = linkComponent ?? DefaultLink;\n const prefix = routePrefix.replace(/\\/$/, \"\");\n\n return (\n <section className={className}>\n {title && <h1 className=\"ba-listing-title\">{title}</h1>}\n <div className=\"ba-posts\">\n {posts.map((post) => {\n const href = `${prefix}/${post.slug}`;\n\n if (renderCard) {\n return <div key={post.id}>{renderCard(post, href)}</div>;\n }\n\n const dateStr = post.publishedAt ?? post.updatedAt;\n\n return (\n <article key={post.id} className=\"ba-post-card\">\n <h2 className=\"ba-post-card-title\">\n <Link href={href} className=\"ba-post-card-link\">\n {post.title}\n </Link>\n </h2>\n <p className=\"ba-post-card-meta\">\n <time dateTime={dateStr}>{formatDate(dateStr)}</time>\n </p>\n <div className=\"ba-post-card-excerpt\">\n <Markdown source={post.excerpt ?? post.content.slice(0, 180)} />\n </div>\n </article>\n );\n })}\n </div>\n </section>\n );\n}\n"],"mappings":"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","names":["jsx","jsx","jsxs","formatDate"]}
|
package/dist/revalidate.d.cts
CHANGED
|
@@ -1 +1,19 @@
|
|
|
1
|
-
|
|
1
|
+
import { R as RevalidatePathFn, g as RevalidateTagFn, f as BlogAutoRevalidatePayload } from './types-CCDRs0sO.cjs';
|
|
2
|
+
|
|
3
|
+
interface VerifyWebhookSignatureOptions {
|
|
4
|
+
rawBody: string | Buffer;
|
|
5
|
+
signature: string | null;
|
|
6
|
+
secret: string;
|
|
7
|
+
}
|
|
8
|
+
interface CreateRevalidateRouteHandlerOptions {
|
|
9
|
+
secret: string;
|
|
10
|
+
allowedSkewSeconds?: number;
|
|
11
|
+
revalidatePath?: RevalidatePathFn;
|
|
12
|
+
revalidateTag?: RevalidateTagFn;
|
|
13
|
+
revalidatePaths?: (payload: BlogAutoRevalidatePayload) => string[];
|
|
14
|
+
revalidateTags?: (payload: BlogAutoRevalidatePayload) => string[];
|
|
15
|
+
}
|
|
16
|
+
declare function verifyWebhookSignature(opts: VerifyWebhookSignatureOptions): boolean;
|
|
17
|
+
declare function createRevalidateRouteHandler(options: CreateRevalidateRouteHandlerOptions): (request: Request) => Promise<Response>;
|
|
18
|
+
|
|
19
|
+
export { type CreateRevalidateRouteHandlerOptions, type VerifyWebhookSignatureOptions, createRevalidateRouteHandler, verifyWebhookSignature };
|
package/dist/revalidate.d.ts
CHANGED
|
@@ -1 +1,19 @@
|
|
|
1
|
-
|
|
1
|
+
import { R as RevalidatePathFn, g as RevalidateTagFn, f as BlogAutoRevalidatePayload } from './types-CCDRs0sO.js';
|
|
2
|
+
|
|
3
|
+
interface VerifyWebhookSignatureOptions {
|
|
4
|
+
rawBody: string | Buffer;
|
|
5
|
+
signature: string | null;
|
|
6
|
+
secret: string;
|
|
7
|
+
}
|
|
8
|
+
interface CreateRevalidateRouteHandlerOptions {
|
|
9
|
+
secret: string;
|
|
10
|
+
allowedSkewSeconds?: number;
|
|
11
|
+
revalidatePath?: RevalidatePathFn;
|
|
12
|
+
revalidateTag?: RevalidateTagFn;
|
|
13
|
+
revalidatePaths?: (payload: BlogAutoRevalidatePayload) => string[];
|
|
14
|
+
revalidateTags?: (payload: BlogAutoRevalidatePayload) => string[];
|
|
15
|
+
}
|
|
16
|
+
declare function verifyWebhookSignature(opts: VerifyWebhookSignatureOptions): boolean;
|
|
17
|
+
declare function createRevalidateRouteHandler(options: CreateRevalidateRouteHandlerOptions): (request: Request) => Promise<Response>;
|
|
18
|
+
|
|
19
|
+
export { type CreateRevalidateRouteHandlerOptions, type VerifyWebhookSignatureOptions, createRevalidateRouteHandler, verifyWebhookSignature };
|