@autoblogwriter/sdk 1.0.2
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 +296 -0
- package/dist/index.cjs +592 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +69 -0
- package/dist/index.d.ts +69 -0
- package/dist/index.js +542 -0
- package/dist/index.js.map +1 -0
- package/dist/revalidate-445OJMx_.d.cts +116 -0
- package/dist/revalidate-445OJMx_.d.ts +116 -0
- package/dist/revalidate.cjs +185 -0
- package/dist/revalidate.cjs.map +1 -0
- package/dist/revalidate.d.cts +1 -0
- package/dist/revalidate.d.ts +1 -0
- package/dist/revalidate.js +149 -0
- package/dist/revalidate.js.map +1 -0
- package/package.json +40 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/auth.ts","../src/errors.ts","../src/utils.ts","../src/client.ts","../src/sitemap.ts","../src/robots.ts","../src/render.ts","../src/revalidate.ts","../src/metadata.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\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 try {\n const response = await withTimeout(fetchImpl(url, init), resolved.timeoutMs);\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 if (error instanceof BlogAutoError) {\n throw error;\n }\n throw new ApiError(\"Network request to BlogAuto failed\", { status: 0 }, { cause: error });\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 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 { 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","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 blocks = withoutCode\n .replace(/\\r\\n/g, \"\\n\")\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 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 type { BlogPost } from \"./types\";\n\nexport interface NextMetadata {\n title?: string;\n description?: string;\n alternates?: {\n canonical?: string;\n };\n openGraph?: {\n title?: string;\n description?: string;\n images?: Array<{ url: 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 canonical = post.metadata?.canonicalUrl;\n const ogImageUrl = post.metadata?.ogImageUrl;\n\n const metadata: NextMetadata = {\n title,\n description,\n };\n\n if (canonical) {\n metadata.alternates = { canonical };\n }\n\n if (ogImageUrl) {\n metadata.openGraph = {\n title,\n description,\n images: [{ url: ogImageUrl }],\n };\n }\n\n return metadata;\n}\n"],"mappings":"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","names":["normalizeSiteUrl"]}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
type AuthMode = "bearer" | "x-api-key";
|
|
2
|
+
interface BlogAutoClientConfig {
|
|
3
|
+
apiUrl: string;
|
|
4
|
+
apiKey: string;
|
|
5
|
+
workspaceId?: string;
|
|
6
|
+
workspaceSlug?: string;
|
|
7
|
+
authMode?: AuthMode;
|
|
8
|
+
fetch?: typeof fetch;
|
|
9
|
+
headers?: Record<string, string>;
|
|
10
|
+
timeoutMs?: number;
|
|
11
|
+
}
|
|
12
|
+
interface InternalClientConfig extends Required<Pick<BlogAutoClientConfig, "apiUrl" | "apiKey">> {
|
|
13
|
+
workspaceId?: string;
|
|
14
|
+
workspaceSlug?: string;
|
|
15
|
+
authMode: AuthMode;
|
|
16
|
+
fetch: typeof fetch;
|
|
17
|
+
headers: Record<string, string>;
|
|
18
|
+
timeoutMs?: number;
|
|
19
|
+
}
|
|
20
|
+
interface BlogPost {
|
|
21
|
+
id: string;
|
|
22
|
+
title: string;
|
|
23
|
+
slug: string;
|
|
24
|
+
excerpt?: string;
|
|
25
|
+
content: string;
|
|
26
|
+
status: "DRAFT" | "PUBLISHED" | "HIDDEN";
|
|
27
|
+
seo?: {
|
|
28
|
+
title?: string;
|
|
29
|
+
description?: string;
|
|
30
|
+
keywords?: string[];
|
|
31
|
+
};
|
|
32
|
+
metadata?: {
|
|
33
|
+
canonicalUrl?: string;
|
|
34
|
+
ogImageUrl?: string;
|
|
35
|
+
readingTimeMinutes?: number;
|
|
36
|
+
wordCount?: number;
|
|
37
|
+
};
|
|
38
|
+
publishedAt?: string;
|
|
39
|
+
updatedAt: string;
|
|
40
|
+
}
|
|
41
|
+
interface PostsResponse {
|
|
42
|
+
posts: BlogPost[];
|
|
43
|
+
nextCursor?: string;
|
|
44
|
+
}
|
|
45
|
+
interface PaginatedList<T> {
|
|
46
|
+
items: T[];
|
|
47
|
+
pagination: {
|
|
48
|
+
page: number;
|
|
49
|
+
limit: number;
|
|
50
|
+
total: number;
|
|
51
|
+
totalPages: number;
|
|
52
|
+
hasMore: boolean;
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
interface SitemapEntry {
|
|
56
|
+
slug: string;
|
|
57
|
+
updatedAt?: string;
|
|
58
|
+
}
|
|
59
|
+
type MetadataRouteSitemapEntry = {
|
|
60
|
+
url: string;
|
|
61
|
+
lastModified?: string;
|
|
62
|
+
};
|
|
63
|
+
type MetadataRouteSitemap = MetadataRouteSitemapEntry[];
|
|
64
|
+
interface MetadataRouteRobotsRule {
|
|
65
|
+
userAgent: string;
|
|
66
|
+
allow?: string | string[];
|
|
67
|
+
disallow?: string | string[];
|
|
68
|
+
}
|
|
69
|
+
interface MetadataRouteRobots {
|
|
70
|
+
rules: MetadataRouteRobotsRule | MetadataRouteRobotsRule[];
|
|
71
|
+
sitemap?: string | string[];
|
|
72
|
+
}
|
|
73
|
+
interface BuildSitemapOptions {
|
|
74
|
+
siteUrl: string;
|
|
75
|
+
routePrefix?: string;
|
|
76
|
+
entries: SitemapEntry[];
|
|
77
|
+
}
|
|
78
|
+
interface BuildRobotsOptions {
|
|
79
|
+
siteUrl: string;
|
|
80
|
+
sitemapPath?: string;
|
|
81
|
+
}
|
|
82
|
+
interface BlogAutoRevalidatePayload {
|
|
83
|
+
workspaceSlug: string;
|
|
84
|
+
postSlug?: string | null;
|
|
85
|
+
event: string;
|
|
86
|
+
ts: string;
|
|
87
|
+
[key: string]: unknown;
|
|
88
|
+
}
|
|
89
|
+
type RevalidatePathFn = (path: string, type?: "page" | "layout" | "route") => void | Promise<void>;
|
|
90
|
+
type RevalidateTagFn = (tag: string) => void | Promise<void>;
|
|
91
|
+
interface FetchNextConfig {
|
|
92
|
+
revalidate?: number | false;
|
|
93
|
+
tags?: string[];
|
|
94
|
+
}
|
|
95
|
+
interface FetchRequestOptions {
|
|
96
|
+
cache?: RequestCache;
|
|
97
|
+
next?: FetchNextConfig;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
interface VerifyWebhookSignatureOptions {
|
|
101
|
+
rawBody: string | Buffer;
|
|
102
|
+
signature: string | null;
|
|
103
|
+
secret: string;
|
|
104
|
+
}
|
|
105
|
+
interface CreateRevalidateRouteHandlerOptions {
|
|
106
|
+
secret: string;
|
|
107
|
+
allowedSkewSeconds?: number;
|
|
108
|
+
revalidatePath?: RevalidatePathFn;
|
|
109
|
+
revalidateTag?: RevalidateTagFn;
|
|
110
|
+
revalidatePaths?: (payload: BlogAutoRevalidatePayload) => string[];
|
|
111
|
+
revalidateTags?: (payload: BlogAutoRevalidatePayload) => string[];
|
|
112
|
+
}
|
|
113
|
+
declare function verifyWebhookSignature(opts: VerifyWebhookSignatureOptions): boolean;
|
|
114
|
+
declare function createRevalidateRouteHandler(options: CreateRevalidateRouteHandlerOptions): (request: Request) => Promise<Response>;
|
|
115
|
+
|
|
116
|
+
export { type AuthMode as A, type BlogAutoClientConfig as B, type CreateRevalidateRouteHandlerOptions as C, type FetchRequestOptions as F, type InternalClientConfig as I, type MetadataRouteSitemap as M, type PostsResponse as P, type RevalidatePathFn as R, type SitemapEntry as S, type VerifyWebhookSignatureOptions as V, type BlogPost as a, type BuildSitemapOptions as b, type BuildRobotsOptions as c, type MetadataRouteRobots as d, createRevalidateRouteHandler as e, type PaginatedList as f, type BlogAutoRevalidatePayload as g, type RevalidateTagFn as h, type FetchNextConfig as i, verifyWebhookSignature as v };
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
type AuthMode = "bearer" | "x-api-key";
|
|
2
|
+
interface BlogAutoClientConfig {
|
|
3
|
+
apiUrl: string;
|
|
4
|
+
apiKey: string;
|
|
5
|
+
workspaceId?: string;
|
|
6
|
+
workspaceSlug?: string;
|
|
7
|
+
authMode?: AuthMode;
|
|
8
|
+
fetch?: typeof fetch;
|
|
9
|
+
headers?: Record<string, string>;
|
|
10
|
+
timeoutMs?: number;
|
|
11
|
+
}
|
|
12
|
+
interface InternalClientConfig extends Required<Pick<BlogAutoClientConfig, "apiUrl" | "apiKey">> {
|
|
13
|
+
workspaceId?: string;
|
|
14
|
+
workspaceSlug?: string;
|
|
15
|
+
authMode: AuthMode;
|
|
16
|
+
fetch: typeof fetch;
|
|
17
|
+
headers: Record<string, string>;
|
|
18
|
+
timeoutMs?: number;
|
|
19
|
+
}
|
|
20
|
+
interface BlogPost {
|
|
21
|
+
id: string;
|
|
22
|
+
title: string;
|
|
23
|
+
slug: string;
|
|
24
|
+
excerpt?: string;
|
|
25
|
+
content: string;
|
|
26
|
+
status: "DRAFT" | "PUBLISHED" | "HIDDEN";
|
|
27
|
+
seo?: {
|
|
28
|
+
title?: string;
|
|
29
|
+
description?: string;
|
|
30
|
+
keywords?: string[];
|
|
31
|
+
};
|
|
32
|
+
metadata?: {
|
|
33
|
+
canonicalUrl?: string;
|
|
34
|
+
ogImageUrl?: string;
|
|
35
|
+
readingTimeMinutes?: number;
|
|
36
|
+
wordCount?: number;
|
|
37
|
+
};
|
|
38
|
+
publishedAt?: string;
|
|
39
|
+
updatedAt: string;
|
|
40
|
+
}
|
|
41
|
+
interface PostsResponse {
|
|
42
|
+
posts: BlogPost[];
|
|
43
|
+
nextCursor?: string;
|
|
44
|
+
}
|
|
45
|
+
interface PaginatedList<T> {
|
|
46
|
+
items: T[];
|
|
47
|
+
pagination: {
|
|
48
|
+
page: number;
|
|
49
|
+
limit: number;
|
|
50
|
+
total: number;
|
|
51
|
+
totalPages: number;
|
|
52
|
+
hasMore: boolean;
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
interface SitemapEntry {
|
|
56
|
+
slug: string;
|
|
57
|
+
updatedAt?: string;
|
|
58
|
+
}
|
|
59
|
+
type MetadataRouteSitemapEntry = {
|
|
60
|
+
url: string;
|
|
61
|
+
lastModified?: string;
|
|
62
|
+
};
|
|
63
|
+
type MetadataRouteSitemap = MetadataRouteSitemapEntry[];
|
|
64
|
+
interface MetadataRouteRobotsRule {
|
|
65
|
+
userAgent: string;
|
|
66
|
+
allow?: string | string[];
|
|
67
|
+
disallow?: string | string[];
|
|
68
|
+
}
|
|
69
|
+
interface MetadataRouteRobots {
|
|
70
|
+
rules: MetadataRouteRobotsRule | MetadataRouteRobotsRule[];
|
|
71
|
+
sitemap?: string | string[];
|
|
72
|
+
}
|
|
73
|
+
interface BuildSitemapOptions {
|
|
74
|
+
siteUrl: string;
|
|
75
|
+
routePrefix?: string;
|
|
76
|
+
entries: SitemapEntry[];
|
|
77
|
+
}
|
|
78
|
+
interface BuildRobotsOptions {
|
|
79
|
+
siteUrl: string;
|
|
80
|
+
sitemapPath?: string;
|
|
81
|
+
}
|
|
82
|
+
interface BlogAutoRevalidatePayload {
|
|
83
|
+
workspaceSlug: string;
|
|
84
|
+
postSlug?: string | null;
|
|
85
|
+
event: string;
|
|
86
|
+
ts: string;
|
|
87
|
+
[key: string]: unknown;
|
|
88
|
+
}
|
|
89
|
+
type RevalidatePathFn = (path: string, type?: "page" | "layout" | "route") => void | Promise<void>;
|
|
90
|
+
type RevalidateTagFn = (tag: string) => void | Promise<void>;
|
|
91
|
+
interface FetchNextConfig {
|
|
92
|
+
revalidate?: number | false;
|
|
93
|
+
tags?: string[];
|
|
94
|
+
}
|
|
95
|
+
interface FetchRequestOptions {
|
|
96
|
+
cache?: RequestCache;
|
|
97
|
+
next?: FetchNextConfig;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
interface VerifyWebhookSignatureOptions {
|
|
101
|
+
rawBody: string | Buffer;
|
|
102
|
+
signature: string | null;
|
|
103
|
+
secret: string;
|
|
104
|
+
}
|
|
105
|
+
interface CreateRevalidateRouteHandlerOptions {
|
|
106
|
+
secret: string;
|
|
107
|
+
allowedSkewSeconds?: number;
|
|
108
|
+
revalidatePath?: RevalidatePathFn;
|
|
109
|
+
revalidateTag?: RevalidateTagFn;
|
|
110
|
+
revalidatePaths?: (payload: BlogAutoRevalidatePayload) => string[];
|
|
111
|
+
revalidateTags?: (payload: BlogAutoRevalidatePayload) => string[];
|
|
112
|
+
}
|
|
113
|
+
declare function verifyWebhookSignature(opts: VerifyWebhookSignatureOptions): boolean;
|
|
114
|
+
declare function createRevalidateRouteHandler(options: CreateRevalidateRouteHandlerOptions): (request: Request) => Promise<Response>;
|
|
115
|
+
|
|
116
|
+
export { type AuthMode as A, type BlogAutoClientConfig as B, type CreateRevalidateRouteHandlerOptions as C, type FetchRequestOptions as F, type InternalClientConfig as I, type MetadataRouteSitemap as M, type PostsResponse as P, type RevalidatePathFn as R, type SitemapEntry as S, type VerifyWebhookSignatureOptions as V, type BlogPost as a, type BuildSitemapOptions as b, type BuildRobotsOptions as c, type MetadataRouteRobots as d, createRevalidateRouteHandler as e, type PaginatedList as f, type BlogAutoRevalidatePayload as g, type RevalidateTagFn as h, type FetchNextConfig as i, verifyWebhookSignature as v };
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/revalidate.ts
|
|
31
|
+
var revalidate_exports = {};
|
|
32
|
+
__export(revalidate_exports, {
|
|
33
|
+
createRevalidateRouteHandler: () => createRevalidateRouteHandler,
|
|
34
|
+
verifyWebhookSignature: () => verifyWebhookSignature
|
|
35
|
+
});
|
|
36
|
+
module.exports = __toCommonJS(revalidate_exports);
|
|
37
|
+
var import_crypto = __toESM(require("crypto"), 1);
|
|
38
|
+
function jsonResponse(body, init) {
|
|
39
|
+
const headers = new Headers(init?.headers);
|
|
40
|
+
headers.set("content-type", "application/json");
|
|
41
|
+
return new Response(JSON.stringify(body), {
|
|
42
|
+
...init,
|
|
43
|
+
headers
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
function normalizeSignature(signature) {
|
|
47
|
+
if (!signature) {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
const trimmed = signature.trim();
|
|
51
|
+
if (!trimmed) {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
const withoutPrefix = trimmed.startsWith("sha256=") ? trimmed.slice(7) : trimmed;
|
|
55
|
+
if (!withoutPrefix) {
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
try {
|
|
59
|
+
return Buffer.from(withoutPrefix, "hex");
|
|
60
|
+
} catch {
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
function verifyWebhookSignature(opts) {
|
|
65
|
+
const { rawBody, signature, secret } = opts;
|
|
66
|
+
if (!secret) {
|
|
67
|
+
throw new Error("Secret is required to verify webhook signatures");
|
|
68
|
+
}
|
|
69
|
+
const providedSignature = normalizeSignature(signature);
|
|
70
|
+
if (!providedSignature) {
|
|
71
|
+
return false;
|
|
72
|
+
}
|
|
73
|
+
const bodyBuffer = typeof rawBody === "string" ? Buffer.from(rawBody) : rawBody;
|
|
74
|
+
const expected = import_crypto.default.createHmac("sha256", secret).update(bodyBuffer).digest();
|
|
75
|
+
if (providedSignature.length !== expected.length) {
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
return import_crypto.default.timingSafeEqual(providedSignature, expected);
|
|
79
|
+
}
|
|
80
|
+
function defaultPaths(payload) {
|
|
81
|
+
const paths = /* @__PURE__ */ new Set(["/sitemap.xml", "/robots.txt", "/blog"]);
|
|
82
|
+
if (payload.postSlug) {
|
|
83
|
+
paths.add(`/blog/${payload.postSlug}`);
|
|
84
|
+
}
|
|
85
|
+
return Array.from(paths);
|
|
86
|
+
}
|
|
87
|
+
function defaultTags(payload) {
|
|
88
|
+
const tags = /* @__PURE__ */ new Set();
|
|
89
|
+
if (payload.workspaceSlug) {
|
|
90
|
+
tags.add(`blogauto:${payload.workspaceSlug}:sitemap`);
|
|
91
|
+
tags.add(`blogauto:${payload.workspaceSlug}:posts`);
|
|
92
|
+
if (payload.postSlug) {
|
|
93
|
+
tags.add(`blogauto:${payload.workspaceSlug}:post:${payload.postSlug}`);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return Array.from(tags);
|
|
97
|
+
}
|
|
98
|
+
function dedupe(values) {
|
|
99
|
+
if (!values || values.length === 0) {
|
|
100
|
+
return [];
|
|
101
|
+
}
|
|
102
|
+
return Array.from(
|
|
103
|
+
new Set(
|
|
104
|
+
values.filter((value) => typeof value === "string" && value.trim().length > 0).map((value) => value.trim())
|
|
105
|
+
)
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
function determineRouteType(path) {
|
|
109
|
+
const lower = path.toLowerCase();
|
|
110
|
+
if (lower.endsWith(".xml") || lower.endsWith(".txt")) {
|
|
111
|
+
return "route";
|
|
112
|
+
}
|
|
113
|
+
return "page";
|
|
114
|
+
}
|
|
115
|
+
function createRevalidateRouteHandler(options) {
|
|
116
|
+
if (!options.secret) {
|
|
117
|
+
throw new Error("secret is required for createRevalidateRouteHandler");
|
|
118
|
+
}
|
|
119
|
+
const allowedSkewMs = Math.max(1, options.allowedSkewSeconds ?? 300) * 1e3;
|
|
120
|
+
const pathBuilder = options.revalidatePaths ?? defaultPaths;
|
|
121
|
+
const tagBuilder = options.revalidateTags ?? defaultTags;
|
|
122
|
+
return async function blogAutoRevalidateHandler(request) {
|
|
123
|
+
const signature = request.headers.get("x-blogauto-signature");
|
|
124
|
+
const receivedAt = Date.now();
|
|
125
|
+
const rawBody = await request.text();
|
|
126
|
+
const isValid = verifyWebhookSignature({
|
|
127
|
+
rawBody,
|
|
128
|
+
signature,
|
|
129
|
+
secret: options.secret
|
|
130
|
+
});
|
|
131
|
+
if (!isValid) {
|
|
132
|
+
return jsonResponse({ error: "Invalid webhook signature" }, { status: 401 });
|
|
133
|
+
}
|
|
134
|
+
let payload;
|
|
135
|
+
try {
|
|
136
|
+
payload = JSON.parse(rawBody);
|
|
137
|
+
} catch {
|
|
138
|
+
return jsonResponse({ error: "Invalid JSON payload" }, { status: 400 });
|
|
139
|
+
}
|
|
140
|
+
if (!payload.workspaceSlug || typeof payload.workspaceSlug !== "string") {
|
|
141
|
+
return jsonResponse({ error: "workspaceSlug is required" }, { status: 400 });
|
|
142
|
+
}
|
|
143
|
+
if (!payload.ts || typeof payload.ts !== "string") {
|
|
144
|
+
return jsonResponse({ error: "Timestamp is required" }, { status: 400 });
|
|
145
|
+
}
|
|
146
|
+
const payloadTs = Date.parse(payload.ts);
|
|
147
|
+
if (Number.isNaN(payloadTs)) {
|
|
148
|
+
return jsonResponse({ error: "Invalid timestamp" }, { status: 400 });
|
|
149
|
+
}
|
|
150
|
+
if (Math.abs(receivedAt - payloadTs) > allowedSkewMs) {
|
|
151
|
+
return jsonResponse({ error: "Webhook timestamp is outside allowed skew" }, { status: 409 });
|
|
152
|
+
}
|
|
153
|
+
const paths = dedupe(pathBuilder(payload));
|
|
154
|
+
const tags = dedupe(tagBuilder(payload));
|
|
155
|
+
try {
|
|
156
|
+
if (options.revalidatePath && paths.length > 0) {
|
|
157
|
+
await Promise.all(
|
|
158
|
+
paths.map((path) => options.revalidatePath(path, determineRouteType(path)))
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
if (options.revalidateTag && tags.length > 0) {
|
|
162
|
+
await Promise.all(tags.map((tag) => options.revalidateTag(tag)));
|
|
163
|
+
}
|
|
164
|
+
} catch (error) {
|
|
165
|
+
return jsonResponse(
|
|
166
|
+
{ error: "Failed to revalidate cache", details: error.message },
|
|
167
|
+
{ status: 500 }
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
return jsonResponse({
|
|
171
|
+
ok: true,
|
|
172
|
+
event: payload.event ?? "post.published",
|
|
173
|
+
revalidated: {
|
|
174
|
+
paths,
|
|
175
|
+
tags
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
181
|
+
0 && (module.exports = {
|
|
182
|
+
createRevalidateRouteHandler,
|
|
183
|
+
verifyWebhookSignature
|
|
184
|
+
});
|
|
185
|
+
//# sourceMappingURL=revalidate.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/revalidate.ts"],"sourcesContent":["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"],"mappings":"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","names":["crypto"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { C as CreateRevalidateRouteHandlerOptions, V as VerifyWebhookSignatureOptions, e as createRevalidateRouteHandler, v as verifyWebhookSignature } from './revalidate-445OJMx_.cjs';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { C as CreateRevalidateRouteHandlerOptions, V as VerifyWebhookSignatureOptions, e as createRevalidateRouteHandler, v as verifyWebhookSignature } from './revalidate-445OJMx_.js';
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
// src/revalidate.ts
|
|
2
|
+
import crypto from "crypto";
|
|
3
|
+
function jsonResponse(body, init) {
|
|
4
|
+
const headers = new Headers(init?.headers);
|
|
5
|
+
headers.set("content-type", "application/json");
|
|
6
|
+
return new Response(JSON.stringify(body), {
|
|
7
|
+
...init,
|
|
8
|
+
headers
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
function normalizeSignature(signature) {
|
|
12
|
+
if (!signature) {
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
const trimmed = signature.trim();
|
|
16
|
+
if (!trimmed) {
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
const withoutPrefix = trimmed.startsWith("sha256=") ? trimmed.slice(7) : trimmed;
|
|
20
|
+
if (!withoutPrefix) {
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
try {
|
|
24
|
+
return Buffer.from(withoutPrefix, "hex");
|
|
25
|
+
} catch {
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
function verifyWebhookSignature(opts) {
|
|
30
|
+
const { rawBody, signature, secret } = opts;
|
|
31
|
+
if (!secret) {
|
|
32
|
+
throw new Error("Secret is required to verify webhook signatures");
|
|
33
|
+
}
|
|
34
|
+
const providedSignature = normalizeSignature(signature);
|
|
35
|
+
if (!providedSignature) {
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
const bodyBuffer = typeof rawBody === "string" ? Buffer.from(rawBody) : rawBody;
|
|
39
|
+
const expected = crypto.createHmac("sha256", secret).update(bodyBuffer).digest();
|
|
40
|
+
if (providedSignature.length !== expected.length) {
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
return crypto.timingSafeEqual(providedSignature, expected);
|
|
44
|
+
}
|
|
45
|
+
function defaultPaths(payload) {
|
|
46
|
+
const paths = /* @__PURE__ */ new Set(["/sitemap.xml", "/robots.txt", "/blog"]);
|
|
47
|
+
if (payload.postSlug) {
|
|
48
|
+
paths.add(`/blog/${payload.postSlug}`);
|
|
49
|
+
}
|
|
50
|
+
return Array.from(paths);
|
|
51
|
+
}
|
|
52
|
+
function defaultTags(payload) {
|
|
53
|
+
const tags = /* @__PURE__ */ new Set();
|
|
54
|
+
if (payload.workspaceSlug) {
|
|
55
|
+
tags.add(`blogauto:${payload.workspaceSlug}:sitemap`);
|
|
56
|
+
tags.add(`blogauto:${payload.workspaceSlug}:posts`);
|
|
57
|
+
if (payload.postSlug) {
|
|
58
|
+
tags.add(`blogauto:${payload.workspaceSlug}:post:${payload.postSlug}`);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return Array.from(tags);
|
|
62
|
+
}
|
|
63
|
+
function dedupe(values) {
|
|
64
|
+
if (!values || values.length === 0) {
|
|
65
|
+
return [];
|
|
66
|
+
}
|
|
67
|
+
return Array.from(
|
|
68
|
+
new Set(
|
|
69
|
+
values.filter((value) => typeof value === "string" && value.trim().length > 0).map((value) => value.trim())
|
|
70
|
+
)
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
function determineRouteType(path) {
|
|
74
|
+
const lower = path.toLowerCase();
|
|
75
|
+
if (lower.endsWith(".xml") || lower.endsWith(".txt")) {
|
|
76
|
+
return "route";
|
|
77
|
+
}
|
|
78
|
+
return "page";
|
|
79
|
+
}
|
|
80
|
+
function createRevalidateRouteHandler(options) {
|
|
81
|
+
if (!options.secret) {
|
|
82
|
+
throw new Error("secret is required for createRevalidateRouteHandler");
|
|
83
|
+
}
|
|
84
|
+
const allowedSkewMs = Math.max(1, options.allowedSkewSeconds ?? 300) * 1e3;
|
|
85
|
+
const pathBuilder = options.revalidatePaths ?? defaultPaths;
|
|
86
|
+
const tagBuilder = options.revalidateTags ?? defaultTags;
|
|
87
|
+
return async function blogAutoRevalidateHandler(request) {
|
|
88
|
+
const signature = request.headers.get("x-blogauto-signature");
|
|
89
|
+
const receivedAt = Date.now();
|
|
90
|
+
const rawBody = await request.text();
|
|
91
|
+
const isValid = verifyWebhookSignature({
|
|
92
|
+
rawBody,
|
|
93
|
+
signature,
|
|
94
|
+
secret: options.secret
|
|
95
|
+
});
|
|
96
|
+
if (!isValid) {
|
|
97
|
+
return jsonResponse({ error: "Invalid webhook signature" }, { status: 401 });
|
|
98
|
+
}
|
|
99
|
+
let payload;
|
|
100
|
+
try {
|
|
101
|
+
payload = JSON.parse(rawBody);
|
|
102
|
+
} catch {
|
|
103
|
+
return jsonResponse({ error: "Invalid JSON payload" }, { status: 400 });
|
|
104
|
+
}
|
|
105
|
+
if (!payload.workspaceSlug || typeof payload.workspaceSlug !== "string") {
|
|
106
|
+
return jsonResponse({ error: "workspaceSlug is required" }, { status: 400 });
|
|
107
|
+
}
|
|
108
|
+
if (!payload.ts || typeof payload.ts !== "string") {
|
|
109
|
+
return jsonResponse({ error: "Timestamp is required" }, { status: 400 });
|
|
110
|
+
}
|
|
111
|
+
const payloadTs = Date.parse(payload.ts);
|
|
112
|
+
if (Number.isNaN(payloadTs)) {
|
|
113
|
+
return jsonResponse({ error: "Invalid timestamp" }, { status: 400 });
|
|
114
|
+
}
|
|
115
|
+
if (Math.abs(receivedAt - payloadTs) > allowedSkewMs) {
|
|
116
|
+
return jsonResponse({ error: "Webhook timestamp is outside allowed skew" }, { status: 409 });
|
|
117
|
+
}
|
|
118
|
+
const paths = dedupe(pathBuilder(payload));
|
|
119
|
+
const tags = dedupe(tagBuilder(payload));
|
|
120
|
+
try {
|
|
121
|
+
if (options.revalidatePath && paths.length > 0) {
|
|
122
|
+
await Promise.all(
|
|
123
|
+
paths.map((path) => options.revalidatePath(path, determineRouteType(path)))
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
if (options.revalidateTag && tags.length > 0) {
|
|
127
|
+
await Promise.all(tags.map((tag) => options.revalidateTag(tag)));
|
|
128
|
+
}
|
|
129
|
+
} catch (error) {
|
|
130
|
+
return jsonResponse(
|
|
131
|
+
{ error: "Failed to revalidate cache", details: error.message },
|
|
132
|
+
{ status: 500 }
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
return jsonResponse({
|
|
136
|
+
ok: true,
|
|
137
|
+
event: payload.event ?? "post.published",
|
|
138
|
+
revalidated: {
|
|
139
|
+
paths,
|
|
140
|
+
tags
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
export {
|
|
146
|
+
createRevalidateRouteHandler,
|
|
147
|
+
verifyWebhookSignature
|
|
148
|
+
};
|
|
149
|
+
//# sourceMappingURL=revalidate.js.map
|