@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
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,592 @@
|
|
|
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/index.ts
|
|
31
|
+
var src_exports = {};
|
|
32
|
+
__export(src_exports, {
|
|
33
|
+
ApiError: () => ApiError,
|
|
34
|
+
BlogAutoError: () => BlogAutoError,
|
|
35
|
+
ConfigError: () => ConfigError,
|
|
36
|
+
DEFAULT_TIMEOUT_MS: () => DEFAULT_TIMEOUT_MS,
|
|
37
|
+
NotFoundError: () => NotFoundError,
|
|
38
|
+
buildAuthHeaders: () => buildAuthHeaders,
|
|
39
|
+
buildNextMetadata: () => buildNextMetadata,
|
|
40
|
+
buildRobots: () => buildRobots,
|
|
41
|
+
buildSitemap: () => buildSitemap,
|
|
42
|
+
createBlogAutoClient: () => createBlogAutoClient,
|
|
43
|
+
createRevalidateRouteHandler: () => createRevalidateRouteHandler,
|
|
44
|
+
renderMarkdownToHtml: () => renderMarkdownToHtml,
|
|
45
|
+
resolveClientConfig: () => resolveClientConfig,
|
|
46
|
+
verifyWebhookSignature: () => verifyWebhookSignature
|
|
47
|
+
});
|
|
48
|
+
module.exports = __toCommonJS(src_exports);
|
|
49
|
+
|
|
50
|
+
// src/auth.ts
|
|
51
|
+
function buildAuthHeaders(apiKey, authMode = "bearer") {
|
|
52
|
+
if (authMode === "x-api-key") {
|
|
53
|
+
return { "x-api-key": apiKey };
|
|
54
|
+
}
|
|
55
|
+
return { Authorization: `Bearer ${apiKey}` };
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// src/errors.ts
|
|
59
|
+
var BlogAutoError = class extends Error {
|
|
60
|
+
constructor(message, options) {
|
|
61
|
+
super(message);
|
|
62
|
+
this.name = new.target.name;
|
|
63
|
+
this.causeError = options?.cause;
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
var ConfigError = class extends BlogAutoError {
|
|
67
|
+
};
|
|
68
|
+
var ApiError = class extends BlogAutoError {
|
|
69
|
+
constructor(message, info, options) {
|
|
70
|
+
super(message, options);
|
|
71
|
+
this.status = info.status;
|
|
72
|
+
this.code = info.code;
|
|
73
|
+
this.details = info.details;
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
var NotFoundError = class extends ApiError {
|
|
77
|
+
constructor(message, info) {
|
|
78
|
+
super(message, { status: info?.status ?? 404, code: info?.code, details: info?.details });
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
// src/utils.ts
|
|
83
|
+
var DEFAULT_TIMEOUT_MS = 1e4;
|
|
84
|
+
function normalizeApiUrl(apiUrl) {
|
|
85
|
+
if (!apiUrl) {
|
|
86
|
+
throw new ConfigError("apiUrl is required");
|
|
87
|
+
}
|
|
88
|
+
return apiUrl.replace(/\/$/, "");
|
|
89
|
+
}
|
|
90
|
+
function resolveClientConfig(config) {
|
|
91
|
+
if (!config) {
|
|
92
|
+
throw new ConfigError("Client configuration is required");
|
|
93
|
+
}
|
|
94
|
+
const apiKey = config.apiKey?.trim();
|
|
95
|
+
if (!apiKey) {
|
|
96
|
+
throw new ConfigError("apiKey is required to authenticate with BlogAuto");
|
|
97
|
+
}
|
|
98
|
+
if (!config.workspaceId && !config.workspaceSlug) {
|
|
99
|
+
throw new ConfigError("Provide either workspaceId or workspaceSlug");
|
|
100
|
+
}
|
|
101
|
+
const authMode = config.authMode ?? "bearer";
|
|
102
|
+
return {
|
|
103
|
+
apiKey,
|
|
104
|
+
apiUrl: normalizeApiUrl(config.apiUrl),
|
|
105
|
+
workspaceId: config.workspaceId,
|
|
106
|
+
workspaceSlug: config.workspaceSlug,
|
|
107
|
+
authMode,
|
|
108
|
+
fetch: config.fetch ?? globalThis.fetch.bind(globalThis),
|
|
109
|
+
headers: { ...config.headers ?? {} },
|
|
110
|
+
timeoutMs: config.timeoutMs ?? DEFAULT_TIMEOUT_MS
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
function buildQuery(params) {
|
|
114
|
+
const query = new URLSearchParams();
|
|
115
|
+
Object.entries(params).forEach(([key, value]) => {
|
|
116
|
+
if (value === void 0 || value === null || value === "") {
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
query.set(key, String(value));
|
|
120
|
+
});
|
|
121
|
+
const qs = query.toString();
|
|
122
|
+
return qs ? `?${qs}` : "";
|
|
123
|
+
}
|
|
124
|
+
async function withTimeout(promise, timeoutMs) {
|
|
125
|
+
if (!timeoutMs) {
|
|
126
|
+
return promise;
|
|
127
|
+
}
|
|
128
|
+
let timeoutId;
|
|
129
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
130
|
+
timeoutId = setTimeout(() => {
|
|
131
|
+
reject(
|
|
132
|
+
new ApiError(`Request timed out after ${timeoutMs}ms`, {
|
|
133
|
+
status: 408
|
|
134
|
+
})
|
|
135
|
+
);
|
|
136
|
+
}, timeoutMs);
|
|
137
|
+
});
|
|
138
|
+
try {
|
|
139
|
+
return await Promise.race([promise, timeoutPromise]);
|
|
140
|
+
} finally {
|
|
141
|
+
clearTimeout(timeoutId);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
function mergeHeaders(...headerObjects) {
|
|
145
|
+
return headerObjects.reduce((acc, headers) => {
|
|
146
|
+
if (!headers) {
|
|
147
|
+
return acc;
|
|
148
|
+
}
|
|
149
|
+
Object.entries(headers).forEach(([key, value]) => {
|
|
150
|
+
if (value === void 0 || value === null) {
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
acc[key.toLowerCase()] = value;
|
|
154
|
+
});
|
|
155
|
+
return acc;
|
|
156
|
+
}, {});
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// src/client.ts
|
|
160
|
+
function createBlogAutoClient(config) {
|
|
161
|
+
const resolved = resolveClientConfig(config);
|
|
162
|
+
const fetchImpl = resolved.fetch;
|
|
163
|
+
function ensureWorkspaceSlug() {
|
|
164
|
+
if (!resolved.workspaceSlug) {
|
|
165
|
+
throw new ConfigError("workspaceSlug is required to call the BlogAuto public API");
|
|
166
|
+
}
|
|
167
|
+
return resolved.workspaceSlug;
|
|
168
|
+
}
|
|
169
|
+
function buildUrl(path, query) {
|
|
170
|
+
const qs = buildQuery(query ?? {});
|
|
171
|
+
return `${resolved.apiUrl}${path}${qs}`;
|
|
172
|
+
}
|
|
173
|
+
function unwrapSuccessPayload(payload) {
|
|
174
|
+
if (payload && typeof payload === "object") {
|
|
175
|
+
const maybePayload = payload;
|
|
176
|
+
if ("success" in maybePayload) {
|
|
177
|
+
if (maybePayload.success === false) {
|
|
178
|
+
throw new ApiError(
|
|
179
|
+
maybePayload.error?.message ?? "BlogAuto request failed",
|
|
180
|
+
{ status: 500, details: maybePayload }
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
if (maybePayload.success && "data" in maybePayload) {
|
|
184
|
+
return maybePayload.data;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
return payload;
|
|
189
|
+
}
|
|
190
|
+
async function request(opts) {
|
|
191
|
+
const method = opts.method ?? "GET";
|
|
192
|
+
const url = buildUrl(opts.path, opts.query);
|
|
193
|
+
const authHeaders = buildAuthHeaders(resolved.apiKey, resolved.authMode);
|
|
194
|
+
const baseHeaders = mergeHeaders(resolved.headers, authHeaders, opts.headers);
|
|
195
|
+
const init = {
|
|
196
|
+
method,
|
|
197
|
+
headers: baseHeaders
|
|
198
|
+
};
|
|
199
|
+
if (opts.body) {
|
|
200
|
+
init.body = typeof opts.body === "string" ? opts.body : JSON.stringify(opts.body);
|
|
201
|
+
init.headers = {
|
|
202
|
+
...baseHeaders,
|
|
203
|
+
"content-type": baseHeaders["content-type"] ?? "application/json"
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
if (opts.cache !== void 0) {
|
|
207
|
+
init.cache = opts.cache;
|
|
208
|
+
}
|
|
209
|
+
if (opts.next) {
|
|
210
|
+
init.next = opts.next;
|
|
211
|
+
}
|
|
212
|
+
try {
|
|
213
|
+
const response = await withTimeout(fetchImpl(url, init), resolved.timeoutMs);
|
|
214
|
+
if (opts.allowNotFound && response.status === 404) {
|
|
215
|
+
return null;
|
|
216
|
+
}
|
|
217
|
+
const contentType = response.headers.get("content-type") ?? "";
|
|
218
|
+
const isJson = contentType.includes("application/json");
|
|
219
|
+
const payload = isJson ? await response.json().catch(() => void 0) : await response.text();
|
|
220
|
+
if (!response.ok) {
|
|
221
|
+
if (response.status === 404 && opts.allowNotFound) {
|
|
222
|
+
return null;
|
|
223
|
+
}
|
|
224
|
+
const errorBody = payload;
|
|
225
|
+
const message = errorBody?.error?.message ?? errorBody?.message ?? `Request failed with status ${response.status}`;
|
|
226
|
+
throw new ApiError(message, {
|
|
227
|
+
status: response.status,
|
|
228
|
+
code: errorBody?.error?.code ?? errorBody?.code,
|
|
229
|
+
details: errorBody?.error?.details ?? errorBody
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
if (isJson) {
|
|
233
|
+
return unwrapSuccessPayload(payload);
|
|
234
|
+
}
|
|
235
|
+
return payload ?? void 0;
|
|
236
|
+
} catch (error) {
|
|
237
|
+
if (error instanceof BlogAutoError) {
|
|
238
|
+
throw error;
|
|
239
|
+
}
|
|
240
|
+
throw new ApiError("Network request to BlogAuto failed", { status: 0 }, { cause: error });
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
async function fetchBlogPage(limit, page, cacheOptions) {
|
|
244
|
+
const workspaceSlug = ensureWorkspaceSlug();
|
|
245
|
+
return request({
|
|
246
|
+
path: `/v1/public/${encodeURIComponent(workspaceSlug)}/blogs`,
|
|
247
|
+
query: { limit, page },
|
|
248
|
+
cache: cacheOptions?.cache,
|
|
249
|
+
next: cacheOptions?.next
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
return {
|
|
253
|
+
async getPosts(params) {
|
|
254
|
+
const limit = params?.limit ?? 20;
|
|
255
|
+
const cursorPage = params?.cursor ? Number(params.cursor) : void 0;
|
|
256
|
+
const page = Number.isFinite(cursorPage) && cursorPage >= 1 ? cursorPage : 1;
|
|
257
|
+
const data = await fetchBlogPage(limit, page, params);
|
|
258
|
+
return {
|
|
259
|
+
posts: data.items,
|
|
260
|
+
nextCursor: data.pagination.hasMore ? String(data.pagination.page + 1) : void 0
|
|
261
|
+
};
|
|
262
|
+
},
|
|
263
|
+
async getPostBySlug(slug, options) {
|
|
264
|
+
if (!slug) {
|
|
265
|
+
throw new ApiError("slug is required", { status: 400 });
|
|
266
|
+
}
|
|
267
|
+
const workspaceSlug = ensureWorkspaceSlug();
|
|
268
|
+
const post = await request({
|
|
269
|
+
path: `/v1/public/${encodeURIComponent(workspaceSlug)}/blogs/${encodeURIComponent(slug)}`,
|
|
270
|
+
allowNotFound: true,
|
|
271
|
+
cache: options?.cache,
|
|
272
|
+
next: options?.next
|
|
273
|
+
});
|
|
274
|
+
if (post === null) {
|
|
275
|
+
return null;
|
|
276
|
+
}
|
|
277
|
+
return post.post;
|
|
278
|
+
},
|
|
279
|
+
async getSitemapEntries() {
|
|
280
|
+
const entries = [];
|
|
281
|
+
const limit = 100;
|
|
282
|
+
let page = 1;
|
|
283
|
+
while (true) {
|
|
284
|
+
const pageData = await fetchBlogPage(limit, page);
|
|
285
|
+
pageData.items.forEach((post) => {
|
|
286
|
+
entries.push({ slug: post.slug, updatedAt: post.updatedAt });
|
|
287
|
+
});
|
|
288
|
+
if (!pageData.pagination.hasMore) {
|
|
289
|
+
break;
|
|
290
|
+
}
|
|
291
|
+
page = pageData.pagination.page + 1;
|
|
292
|
+
}
|
|
293
|
+
return entries;
|
|
294
|
+
}
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// src/sitemap.ts
|
|
299
|
+
function normalizeSiteUrl(url) {
|
|
300
|
+
return url.replace(/\/$/, "");
|
|
301
|
+
}
|
|
302
|
+
function normalizeRoutePrefix(routePrefix) {
|
|
303
|
+
if (!routePrefix || routePrefix === "/") {
|
|
304
|
+
return "";
|
|
305
|
+
}
|
|
306
|
+
const withSlash = routePrefix.startsWith("/") ? routePrefix : `/${routePrefix}`;
|
|
307
|
+
return withSlash.endsWith("/") ? withSlash.slice(0, -1) : withSlash;
|
|
308
|
+
}
|
|
309
|
+
function normalizeSlug(slug) {
|
|
310
|
+
return slug.replace(/^\/+/, "").replace(/\/+$/, "");
|
|
311
|
+
}
|
|
312
|
+
function buildSitemap(opts) {
|
|
313
|
+
const siteUrl = normalizeSiteUrl(opts.siteUrl);
|
|
314
|
+
const prefix = normalizeRoutePrefix(opts.routePrefix ?? "/blog");
|
|
315
|
+
const entries = opts.entries.map((entry) => {
|
|
316
|
+
const slug = normalizeSlug(entry.slug);
|
|
317
|
+
const hasSlug = slug.length > 0;
|
|
318
|
+
const slugFragment = hasSlug ? `/${slug}` : "";
|
|
319
|
+
const path = prefix ? `${prefix}${slugFragment}` : hasSlug ? `/${slug}` : "/";
|
|
320
|
+
return {
|
|
321
|
+
url: `${siteUrl}${path}`,
|
|
322
|
+
lastModified: entry.updatedAt
|
|
323
|
+
};
|
|
324
|
+
});
|
|
325
|
+
return entries;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// src/robots.ts
|
|
329
|
+
function normalizeSiteUrl2(url) {
|
|
330
|
+
return url.replace(/\/$/, "");
|
|
331
|
+
}
|
|
332
|
+
function normalizePath(path) {
|
|
333
|
+
if (!path) {
|
|
334
|
+
return "/sitemap.xml";
|
|
335
|
+
}
|
|
336
|
+
if (!path.startsWith("/")) {
|
|
337
|
+
return `/${path}`;
|
|
338
|
+
}
|
|
339
|
+
return path;
|
|
340
|
+
}
|
|
341
|
+
function buildRobots(opts) {
|
|
342
|
+
const siteUrl = normalizeSiteUrl2(opts.siteUrl);
|
|
343
|
+
const sitemapPath = normalizePath(opts.sitemapPath ?? "/sitemap.xml");
|
|
344
|
+
const sitemapUrl = `${siteUrl}${sitemapPath}`;
|
|
345
|
+
return {
|
|
346
|
+
rules: [{ userAgent: "*", allow: "/" }],
|
|
347
|
+
sitemap: sitemapUrl
|
|
348
|
+
};
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
// src/render.ts
|
|
352
|
+
var CODE_BLOCK_TOKEN = "__BLOGAUTO_CODE_BLOCK_";
|
|
353
|
+
function escapeHtml(value) {
|
|
354
|
+
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
355
|
+
}
|
|
356
|
+
function renderInline(input) {
|
|
357
|
+
let output = escapeHtml(input);
|
|
358
|
+
output = output.replace(/\*\*(.+?)\*\*/g, "<strong>$1</strong>");
|
|
359
|
+
output = output.replace(/\*(.+?)\*/g, "<em>$1</em>");
|
|
360
|
+
output = output.replace(/`([^`]+)`/g, (_, code) => `<code>${code}</code>`);
|
|
361
|
+
output = output.replace(/!\[([^\]]*)\]\(([^)]+)\)/g, (_, alt, src) => {
|
|
362
|
+
const safeSrc = escapeHtml(src);
|
|
363
|
+
const safeAlt = escapeHtml(alt);
|
|
364
|
+
return `<img src="${safeSrc}" alt="${safeAlt}" />`;
|
|
365
|
+
});
|
|
366
|
+
output = output.replace(/\[(.+?)\]\((.+?)\)/g, (_, label, href) => {
|
|
367
|
+
const safeHref = escapeHtml(href);
|
|
368
|
+
return `<a href="${safeHref}" target="_blank" rel="noreferrer">${label}</a>`;
|
|
369
|
+
});
|
|
370
|
+
output = output.replace(/\n/g, "<br />");
|
|
371
|
+
return output;
|
|
372
|
+
}
|
|
373
|
+
function replaceCodeBlocks(markdown, blocks) {
|
|
374
|
+
return markdown.replace(/```([\s\S]*?)```/g, (_, code) => {
|
|
375
|
+
const index = blocks.push(`<pre><code>${escapeHtml(code.trim())}</code></pre>`) - 1;
|
|
376
|
+
return `${CODE_BLOCK_TOKEN}${index}__`;
|
|
377
|
+
});
|
|
378
|
+
}
|
|
379
|
+
function restoreCodeBlocks(html, blocks) {
|
|
380
|
+
return html.replace(/__BLOGAUTO_CODE_BLOCK_(\d+)__/g, (_, rawIndex) => blocks[Number(rawIndex)] ?? "");
|
|
381
|
+
}
|
|
382
|
+
function renderMarkdownToHtml(markdown) {
|
|
383
|
+
if (!markdown) {
|
|
384
|
+
return "";
|
|
385
|
+
}
|
|
386
|
+
const codeBlocks = [];
|
|
387
|
+
const withoutCode = replaceCodeBlocks(markdown, codeBlocks);
|
|
388
|
+
const blocks = withoutCode.replace(/\r\n/g, "\n").split(/\n{2,}/).map((block) => block.trim()).filter(Boolean);
|
|
389
|
+
const htmlBlocks = blocks.map((block) => {
|
|
390
|
+
const headingMatch = block.match(/^(#{1,6})\s+(.*)$/);
|
|
391
|
+
if (headingMatch) {
|
|
392
|
+
const level = headingMatch[1].length;
|
|
393
|
+
const content = renderInline(headingMatch[2]);
|
|
394
|
+
return `<h${level}>${content}</h${level}>`;
|
|
395
|
+
}
|
|
396
|
+
const imageMatch = block.match(/^!\[([^\]]*)\]\(([^)]+)\)$/);
|
|
397
|
+
if (imageMatch) {
|
|
398
|
+
const safeSrc = escapeHtml(imageMatch[2]);
|
|
399
|
+
const safeAlt = escapeHtml(imageMatch[1]);
|
|
400
|
+
return `<img src="${safeSrc}" alt="${safeAlt}" />`;
|
|
401
|
+
}
|
|
402
|
+
return `<p>${renderInline(block)}</p>`;
|
|
403
|
+
});
|
|
404
|
+
const html = htmlBlocks.join("\n");
|
|
405
|
+
return restoreCodeBlocks(html, codeBlocks);
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
// src/revalidate.ts
|
|
409
|
+
var import_crypto = __toESM(require("crypto"), 1);
|
|
410
|
+
function jsonResponse(body, init) {
|
|
411
|
+
const headers = new Headers(init?.headers);
|
|
412
|
+
headers.set("content-type", "application/json");
|
|
413
|
+
return new Response(JSON.stringify(body), {
|
|
414
|
+
...init,
|
|
415
|
+
headers
|
|
416
|
+
});
|
|
417
|
+
}
|
|
418
|
+
function normalizeSignature(signature) {
|
|
419
|
+
if (!signature) {
|
|
420
|
+
return null;
|
|
421
|
+
}
|
|
422
|
+
const trimmed = signature.trim();
|
|
423
|
+
if (!trimmed) {
|
|
424
|
+
return null;
|
|
425
|
+
}
|
|
426
|
+
const withoutPrefix = trimmed.startsWith("sha256=") ? trimmed.slice(7) : trimmed;
|
|
427
|
+
if (!withoutPrefix) {
|
|
428
|
+
return null;
|
|
429
|
+
}
|
|
430
|
+
try {
|
|
431
|
+
return Buffer.from(withoutPrefix, "hex");
|
|
432
|
+
} catch {
|
|
433
|
+
return null;
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
function verifyWebhookSignature(opts) {
|
|
437
|
+
const { rawBody, signature, secret } = opts;
|
|
438
|
+
if (!secret) {
|
|
439
|
+
throw new Error("Secret is required to verify webhook signatures");
|
|
440
|
+
}
|
|
441
|
+
const providedSignature = normalizeSignature(signature);
|
|
442
|
+
if (!providedSignature) {
|
|
443
|
+
return false;
|
|
444
|
+
}
|
|
445
|
+
const bodyBuffer = typeof rawBody === "string" ? Buffer.from(rawBody) : rawBody;
|
|
446
|
+
const expected = import_crypto.default.createHmac("sha256", secret).update(bodyBuffer).digest();
|
|
447
|
+
if (providedSignature.length !== expected.length) {
|
|
448
|
+
return false;
|
|
449
|
+
}
|
|
450
|
+
return import_crypto.default.timingSafeEqual(providedSignature, expected);
|
|
451
|
+
}
|
|
452
|
+
function defaultPaths(payload) {
|
|
453
|
+
const paths = /* @__PURE__ */ new Set(["/sitemap.xml", "/robots.txt", "/blog"]);
|
|
454
|
+
if (payload.postSlug) {
|
|
455
|
+
paths.add(`/blog/${payload.postSlug}`);
|
|
456
|
+
}
|
|
457
|
+
return Array.from(paths);
|
|
458
|
+
}
|
|
459
|
+
function defaultTags(payload) {
|
|
460
|
+
const tags = /* @__PURE__ */ new Set();
|
|
461
|
+
if (payload.workspaceSlug) {
|
|
462
|
+
tags.add(`blogauto:${payload.workspaceSlug}:sitemap`);
|
|
463
|
+
tags.add(`blogauto:${payload.workspaceSlug}:posts`);
|
|
464
|
+
if (payload.postSlug) {
|
|
465
|
+
tags.add(`blogauto:${payload.workspaceSlug}:post:${payload.postSlug}`);
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
return Array.from(tags);
|
|
469
|
+
}
|
|
470
|
+
function dedupe(values) {
|
|
471
|
+
if (!values || values.length === 0) {
|
|
472
|
+
return [];
|
|
473
|
+
}
|
|
474
|
+
return Array.from(
|
|
475
|
+
new Set(
|
|
476
|
+
values.filter((value) => typeof value === "string" && value.trim().length > 0).map((value) => value.trim())
|
|
477
|
+
)
|
|
478
|
+
);
|
|
479
|
+
}
|
|
480
|
+
function determineRouteType(path) {
|
|
481
|
+
const lower = path.toLowerCase();
|
|
482
|
+
if (lower.endsWith(".xml") || lower.endsWith(".txt")) {
|
|
483
|
+
return "route";
|
|
484
|
+
}
|
|
485
|
+
return "page";
|
|
486
|
+
}
|
|
487
|
+
function createRevalidateRouteHandler(options) {
|
|
488
|
+
if (!options.secret) {
|
|
489
|
+
throw new Error("secret is required for createRevalidateRouteHandler");
|
|
490
|
+
}
|
|
491
|
+
const allowedSkewMs = Math.max(1, options.allowedSkewSeconds ?? 300) * 1e3;
|
|
492
|
+
const pathBuilder = options.revalidatePaths ?? defaultPaths;
|
|
493
|
+
const tagBuilder = options.revalidateTags ?? defaultTags;
|
|
494
|
+
return async function blogAutoRevalidateHandler(request) {
|
|
495
|
+
const signature = request.headers.get("x-blogauto-signature");
|
|
496
|
+
const receivedAt = Date.now();
|
|
497
|
+
const rawBody = await request.text();
|
|
498
|
+
const isValid = verifyWebhookSignature({
|
|
499
|
+
rawBody,
|
|
500
|
+
signature,
|
|
501
|
+
secret: options.secret
|
|
502
|
+
});
|
|
503
|
+
if (!isValid) {
|
|
504
|
+
return jsonResponse({ error: "Invalid webhook signature" }, { status: 401 });
|
|
505
|
+
}
|
|
506
|
+
let payload;
|
|
507
|
+
try {
|
|
508
|
+
payload = JSON.parse(rawBody);
|
|
509
|
+
} catch {
|
|
510
|
+
return jsonResponse({ error: "Invalid JSON payload" }, { status: 400 });
|
|
511
|
+
}
|
|
512
|
+
if (!payload.workspaceSlug || typeof payload.workspaceSlug !== "string") {
|
|
513
|
+
return jsonResponse({ error: "workspaceSlug is required" }, { status: 400 });
|
|
514
|
+
}
|
|
515
|
+
if (!payload.ts || typeof payload.ts !== "string") {
|
|
516
|
+
return jsonResponse({ error: "Timestamp is required" }, { status: 400 });
|
|
517
|
+
}
|
|
518
|
+
const payloadTs = Date.parse(payload.ts);
|
|
519
|
+
if (Number.isNaN(payloadTs)) {
|
|
520
|
+
return jsonResponse({ error: "Invalid timestamp" }, { status: 400 });
|
|
521
|
+
}
|
|
522
|
+
if (Math.abs(receivedAt - payloadTs) > allowedSkewMs) {
|
|
523
|
+
return jsonResponse({ error: "Webhook timestamp is outside allowed skew" }, { status: 409 });
|
|
524
|
+
}
|
|
525
|
+
const paths = dedupe(pathBuilder(payload));
|
|
526
|
+
const tags = dedupe(tagBuilder(payload));
|
|
527
|
+
try {
|
|
528
|
+
if (options.revalidatePath && paths.length > 0) {
|
|
529
|
+
await Promise.all(
|
|
530
|
+
paths.map((path) => options.revalidatePath(path, determineRouteType(path)))
|
|
531
|
+
);
|
|
532
|
+
}
|
|
533
|
+
if (options.revalidateTag && tags.length > 0) {
|
|
534
|
+
await Promise.all(tags.map((tag) => options.revalidateTag(tag)));
|
|
535
|
+
}
|
|
536
|
+
} catch (error) {
|
|
537
|
+
return jsonResponse(
|
|
538
|
+
{ error: "Failed to revalidate cache", details: error.message },
|
|
539
|
+
{ status: 500 }
|
|
540
|
+
);
|
|
541
|
+
}
|
|
542
|
+
return jsonResponse({
|
|
543
|
+
ok: true,
|
|
544
|
+
event: payload.event ?? "post.published",
|
|
545
|
+
revalidated: {
|
|
546
|
+
paths,
|
|
547
|
+
tags
|
|
548
|
+
}
|
|
549
|
+
});
|
|
550
|
+
};
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
// src/metadata.ts
|
|
554
|
+
function buildNextMetadata(post) {
|
|
555
|
+
const title = post.seo?.title ?? post.title;
|
|
556
|
+
const description = post.seo?.description ?? post.excerpt;
|
|
557
|
+
const canonical = post.metadata?.canonicalUrl;
|
|
558
|
+
const ogImageUrl = post.metadata?.ogImageUrl;
|
|
559
|
+
const metadata = {
|
|
560
|
+
title,
|
|
561
|
+
description
|
|
562
|
+
};
|
|
563
|
+
if (canonical) {
|
|
564
|
+
metadata.alternates = { canonical };
|
|
565
|
+
}
|
|
566
|
+
if (ogImageUrl) {
|
|
567
|
+
metadata.openGraph = {
|
|
568
|
+
title,
|
|
569
|
+
description,
|
|
570
|
+
images: [{ url: ogImageUrl }]
|
|
571
|
+
};
|
|
572
|
+
}
|
|
573
|
+
return metadata;
|
|
574
|
+
}
|
|
575
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
576
|
+
0 && (module.exports = {
|
|
577
|
+
ApiError,
|
|
578
|
+
BlogAutoError,
|
|
579
|
+
ConfigError,
|
|
580
|
+
DEFAULT_TIMEOUT_MS,
|
|
581
|
+
NotFoundError,
|
|
582
|
+
buildAuthHeaders,
|
|
583
|
+
buildNextMetadata,
|
|
584
|
+
buildRobots,
|
|
585
|
+
buildSitemap,
|
|
586
|
+
createBlogAutoClient,
|
|
587
|
+
createRevalidateRouteHandler,
|
|
588
|
+
renderMarkdownToHtml,
|
|
589
|
+
resolveClientConfig,
|
|
590
|
+
verifyWebhookSignature
|
|
591
|
+
});
|
|
592
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../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":["export { createBlogAutoClient } from \"./client\";\nexport type { BlogAutoClient } from \"./client\";\n\nexport { buildSitemap } from \"./sitemap\";\nexport { buildRobots } from \"./robots\";\nexport { renderMarkdownToHtml } from \"./render\";\nexport { verifyWebhookSignature, createRevalidateRouteHandler } from \"./revalidate\";\nexport { buildNextMetadata, type NextMetadata } from \"./metadata\";\n\nexport { buildAuthHeaders } from \"./auth\";\nexport { DEFAULT_TIMEOUT_MS, resolveClientConfig } from \"./utils\";\nexport { BlogAutoError, ConfigError, ApiError, NotFoundError } from \"./errors\";\n\nexport type {\n BlogAutoClientConfig,\n BlogPost,\n BuildRobotsOptions,\n BuildSitemapOptions,\n MetadataRouteRobots,\n MetadataRouteSitemap,\n PaginatedList,\n PostsResponse,\n SitemapEntry,\n BlogAutoRevalidatePayload,\n RevalidatePathFn,\n RevalidateTagFn,\n FetchRequestOptions,\n FetchNextConfig,\n} from \"./types\";\n","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","crypto"]}
|