folderblog 0.0.1 → 0.0.3
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 +109 -48
- package/dist/chunk-2TZSVPNP.cjs +148 -0
- package/dist/chunk-3RG5ZIWI.js +8 -0
- package/dist/chunk-6TFXNIO6.cjs +495 -0
- package/dist/chunk-B43UAOPC.js +475 -0
- package/dist/chunk-D26H5722.js +132 -0
- package/dist/chunk-E7PYGJA7.cjs +39 -0
- package/dist/chunk-J3Y3HEBF.cjs +1858 -0
- package/dist/chunk-K76XLEC7.js +76 -0
- package/dist/chunk-LPPBVXJ7.js +1786 -0
- package/dist/chunk-OBGZSXTJ.cjs +10 -0
- package/dist/chunk-Q6EXKX6K.js +17 -0
- package/dist/chunk-Q6EYTOTM.cjs +78 -0
- package/dist/chunk-UCXXH2MP.cjs +20 -0
- package/dist/chunk-XQD3UUL5.js +34 -0
- package/dist/cli/bin.cjs +25 -0
- package/dist/cli/bin.d.cts +1 -0
- package/dist/cli/bin.d.ts +1 -0
- package/dist/cli/bin.js +23 -0
- package/dist/cli/index.cjs +22 -0
- package/dist/cli/index.d.cts +39 -0
- package/dist/cli/index.d.ts +39 -0
- package/dist/cli/index.js +15 -0
- package/dist/config-ADPY6IQS.d.cts +473 -0
- package/dist/config-Dctsdeo6.d.ts +473 -0
- package/dist/index.cjs +458 -1
- package/dist/index.d.cts +78 -9
- package/dist/index.d.ts +78 -9
- package/dist/index.js +100 -1
- package/dist/local/index.cjs +785 -0
- package/dist/local/index.d.cts +268 -0
- package/dist/local/index.d.ts +268 -0
- package/dist/local/index.js +772 -0
- package/dist/output-0P0br3Jc.d.cts +452 -0
- package/dist/output-0P0br3Jc.d.ts +452 -0
- package/dist/plugins/embed-cloudflare-ai.cjs +166 -0
- package/dist/plugins/embed-cloudflare-ai.d.cts +73 -0
- package/dist/plugins/embed-cloudflare-ai.d.ts +73 -0
- package/dist/plugins/embed-cloudflare-ai.js +156 -0
- package/dist/plugins/embed-transformers.cjs +121 -0
- package/dist/plugins/embed-transformers.d.cts +55 -0
- package/dist/plugins/embed-transformers.d.ts +55 -0
- package/dist/plugins/embed-transformers.js +113 -0
- package/dist/plugins/similarity.cjs +19 -0
- package/dist/plugins/similarity.d.cts +41 -0
- package/dist/plugins/similarity.d.ts +41 -0
- package/dist/plugins/similarity.js +2 -0
- package/dist/processor/index.cjs +349 -0
- package/dist/processor/index.d.cts +495 -0
- package/dist/processor/index.d.ts +495 -0
- package/dist/processor/index.js +4 -0
- package/dist/processor/plugins.cjs +63 -0
- package/dist/processor/plugins.d.cts +176 -0
- package/dist/processor/plugins.d.ts +176 -0
- package/dist/processor/plugins.js +2 -0
- package/dist/processor/types.cjs +67 -0
- package/dist/processor/types.d.cts +48 -0
- package/dist/processor/types.d.ts +48 -0
- package/dist/processor/types.js +2 -0
- package/dist/seo/index.cjs +289 -0
- package/dist/seo/index.d.cts +95 -0
- package/dist/seo/index.d.ts +95 -0
- package/dist/seo/index.js +274 -0
- package/dist/server/index.cjs +33 -0
- package/dist/server/index.d.cts +56 -0
- package/dist/server/index.d.ts +56 -0
- package/dist/server/index.js +31 -0
- package/package.json +98 -11
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
import '../chunk-3RG5ZIWI.js';
|
|
2
|
+
|
|
3
|
+
// src/seo/index.ts
|
|
4
|
+
function escapeMarkup(str, aposEntity = "'") {
|
|
5
|
+
if (!str) return "";
|
|
6
|
+
return str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, aposEntity);
|
|
7
|
+
}
|
|
8
|
+
function escapeXml(str) {
|
|
9
|
+
return escapeMarkup(str, "'");
|
|
10
|
+
}
|
|
11
|
+
function escapeHtml(str) {
|
|
12
|
+
return escapeMarkup(str, "'");
|
|
13
|
+
}
|
|
14
|
+
function makeAbsoluteUrl(path, siteUrl) {
|
|
15
|
+
if (path.startsWith("http://") || path.startsWith("https://")) return path;
|
|
16
|
+
if (path.startsWith("//")) return `https:${path}`;
|
|
17
|
+
if (path.startsWith("/")) return `${siteUrl}${path}`;
|
|
18
|
+
return `${siteUrl}/${path}`;
|
|
19
|
+
}
|
|
20
|
+
function generateMetaTags(site, options) {
|
|
21
|
+
const title = options.title ?? site.siteName;
|
|
22
|
+
const description = options.description ?? site.siteDescription ?? "";
|
|
23
|
+
const image = options.image ?? site.ogImage ?? "";
|
|
24
|
+
const url = options.url ?? site.siteUrl;
|
|
25
|
+
const type = options.type ?? "website";
|
|
26
|
+
const absoluteImage = image ? makeAbsoluteUrl(image, site.siteUrl) : "";
|
|
27
|
+
const tags = [
|
|
28
|
+
{ title },
|
|
29
|
+
{ name: "description", content: description },
|
|
30
|
+
{ tagName: "link", rel: "canonical", href: url },
|
|
31
|
+
{ property: "og:title", content: title },
|
|
32
|
+
{ property: "og:description", content: description },
|
|
33
|
+
{ property: "og:url", content: url },
|
|
34
|
+
{ property: "og:type", content: type },
|
|
35
|
+
{ property: "og:site_name", content: site.siteName },
|
|
36
|
+
{ name: "twitter:card", content: "summary_large_image" },
|
|
37
|
+
{ name: "twitter:title", content: title },
|
|
38
|
+
{ name: "twitter:description", content: description }
|
|
39
|
+
];
|
|
40
|
+
if (absoluteImage) {
|
|
41
|
+
tags.push(
|
|
42
|
+
{ property: "og:image", content: absoluteImage },
|
|
43
|
+
{ name: "twitter:image", content: absoluteImage }
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
if (type === "article" && options.article) {
|
|
47
|
+
const { article } = options;
|
|
48
|
+
if (article.publishedTime) {
|
|
49
|
+
tags.push({ property: "article:published_time", content: article.publishedTime });
|
|
50
|
+
}
|
|
51
|
+
if (article.modifiedTime) {
|
|
52
|
+
tags.push({ property: "article:modified_time", content: article.modifiedTime });
|
|
53
|
+
}
|
|
54
|
+
if (article.tags) {
|
|
55
|
+
for (const tag of article.tags) {
|
|
56
|
+
tags.push({ property: "article:tag", content: tag });
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return tags;
|
|
61
|
+
}
|
|
62
|
+
function generatePostMetaTags(site, post, url) {
|
|
63
|
+
return generateMetaTags(site, {
|
|
64
|
+
title: `${post.title} | ${site.siteName}`,
|
|
65
|
+
description: post.description || post.excerpt,
|
|
66
|
+
image: post.coverImage,
|
|
67
|
+
url,
|
|
68
|
+
type: "article",
|
|
69
|
+
article: {
|
|
70
|
+
publishedTime: post.date,
|
|
71
|
+
author: post.authorName ?? site.author?.name,
|
|
72
|
+
tags: post.tags
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
function generateListMetaTags(site, title, description, url, pageNumber) {
|
|
77
|
+
const pageTitle = pageNumber && pageNumber > 1 ? `${title} - Page ${pageNumber} | ${site.siteName}` : `${title} | ${site.siteName}`;
|
|
78
|
+
const tags = generateMetaTags(site, {
|
|
79
|
+
title: pageTitle,
|
|
80
|
+
description,
|
|
81
|
+
url,
|
|
82
|
+
type: "website"
|
|
83
|
+
});
|
|
84
|
+
if (pageNumber && pageNumber > 1) {
|
|
85
|
+
tags.push({ name: "robots", content: "noindex, follow" });
|
|
86
|
+
}
|
|
87
|
+
return tags;
|
|
88
|
+
}
|
|
89
|
+
function postToArticleJsonLd(site, post, url) {
|
|
90
|
+
return {
|
|
91
|
+
"@context": "https://schema.org",
|
|
92
|
+
"@type": "BlogPosting",
|
|
93
|
+
headline: post.title,
|
|
94
|
+
description: post.description || post.excerpt,
|
|
95
|
+
author: {
|
|
96
|
+
"@type": post.authorName ? "Person" : "Organization",
|
|
97
|
+
name: post.authorName ?? site.author?.name ?? site.siteName
|
|
98
|
+
},
|
|
99
|
+
publisher: {
|
|
100
|
+
"@type": "Organization",
|
|
101
|
+
name: site.siteName
|
|
102
|
+
},
|
|
103
|
+
datePublished: post.date,
|
|
104
|
+
mainEntityOfPage: { "@type": "WebPage", "@id": url },
|
|
105
|
+
keywords: post.tags?.join(", "),
|
|
106
|
+
...post.coverImage ? { image: [makeAbsoluteUrl(post.coverImage, site.siteUrl)] } : {}
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
function createItemListJsonLd(posts, listName, listDescription, baseUrl) {
|
|
110
|
+
return {
|
|
111
|
+
"@context": "https://schema.org",
|
|
112
|
+
"@type": "ItemList",
|
|
113
|
+
name: listName,
|
|
114
|
+
description: listDescription,
|
|
115
|
+
numberOfItems: posts.length,
|
|
116
|
+
itemListElement: posts.map((post, index) => ({
|
|
117
|
+
"@type": "ListItem",
|
|
118
|
+
position: index + 1,
|
|
119
|
+
url: baseUrl ? `${baseUrl}/${post.slug}` : `/${post.slug}`,
|
|
120
|
+
name: post.title
|
|
121
|
+
}))
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
function createBreadcrumbJsonLd(items) {
|
|
125
|
+
return {
|
|
126
|
+
"@context": "https://schema.org",
|
|
127
|
+
"@type": "BreadcrumbList",
|
|
128
|
+
itemListElement: items.map((item, index) => ({
|
|
129
|
+
"@type": "ListItem",
|
|
130
|
+
position: index + 1,
|
|
131
|
+
name: item.name,
|
|
132
|
+
item: item.url
|
|
133
|
+
}))
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
function createOrganizationJsonLd(site) {
|
|
137
|
+
const sameAs = [];
|
|
138
|
+
if (site.social?.twitter) sameAs.push(site.social.twitter);
|
|
139
|
+
if (site.social?.github) sameAs.push(site.social.github);
|
|
140
|
+
return {
|
|
141
|
+
"@context": "https://schema.org",
|
|
142
|
+
"@type": "Organization",
|
|
143
|
+
name: site.siteName,
|
|
144
|
+
url: site.siteUrl,
|
|
145
|
+
description: site.siteDescription,
|
|
146
|
+
...sameAs.length > 0 ? { sameAs } : {}
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
function createWebsiteJsonLd(site, searchUrl) {
|
|
150
|
+
const jsonLd = {
|
|
151
|
+
"@context": "https://schema.org",
|
|
152
|
+
"@type": "WebSite",
|
|
153
|
+
name: site.siteName,
|
|
154
|
+
url: site.siteUrl,
|
|
155
|
+
description: site.siteDescription
|
|
156
|
+
};
|
|
157
|
+
if (searchUrl) {
|
|
158
|
+
jsonLd.potentialAction = {
|
|
159
|
+
"@type": "SearchAction",
|
|
160
|
+
target: {
|
|
161
|
+
"@type": "EntryPoint",
|
|
162
|
+
urlTemplate: searchUrl
|
|
163
|
+
},
|
|
164
|
+
"query-input": "required name=search_term_string"
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
return jsonLd;
|
|
168
|
+
}
|
|
169
|
+
function serializeJsonLd(jsonLd) {
|
|
170
|
+
return JSON.stringify(jsonLd, null, 2);
|
|
171
|
+
}
|
|
172
|
+
function generateRssFeed(options) {
|
|
173
|
+
const feedUrl = `${options.baseUrl}/api/feed.xml`;
|
|
174
|
+
const postPath = options.postPath ?? "/posts";
|
|
175
|
+
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
176
|
+
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
|
|
177
|
+
<channel>
|
|
178
|
+
<title>${escapeXml(options.title)}</title>
|
|
179
|
+
<description>${escapeXml(options.description)}</description>
|
|
180
|
+
<link>${escapeXml(options.baseUrl)}</link>
|
|
181
|
+
<atom:link href="${escapeXml(feedUrl)}" rel="self" type="application/rss+xml"/>
|
|
182
|
+
<lastBuildDate>${(/* @__PURE__ */ new Date()).toUTCString()}</lastBuildDate>
|
|
183
|
+
${options.posts.map((post) => {
|
|
184
|
+
const postUrl = `${options.baseUrl}${postPath}/${encodeURIComponent(post.slug)}`;
|
|
185
|
+
return `
|
|
186
|
+
<item>
|
|
187
|
+
<title>${escapeXml(post.title)}</title>
|
|
188
|
+
<link>${escapeXml(postUrl)}</link>
|
|
189
|
+
<guid isPermaLink="true">${escapeXml(postUrl)}</guid>
|
|
190
|
+
<description>${escapeXml(post.description || post.excerpt)}</description>
|
|
191
|
+
${post.date ? `<pubDate>${new Date(post.date).toUTCString()}</pubDate>` : ""}
|
|
192
|
+
${(post.tags ?? []).map((tag) => `<category>${escapeXml(tag)}</category>`).join("\n ")}
|
|
193
|
+
</item>`;
|
|
194
|
+
}).join("")}
|
|
195
|
+
</channel>
|
|
196
|
+
</rss>`;
|
|
197
|
+
}
|
|
198
|
+
function generateJsonFeed(options) {
|
|
199
|
+
const postPath = options.postPath ?? "/posts";
|
|
200
|
+
return {
|
|
201
|
+
version: "https://jsonfeed.org/version/1.1",
|
|
202
|
+
title: options.title,
|
|
203
|
+
description: options.description,
|
|
204
|
+
home_page_url: options.baseUrl,
|
|
205
|
+
feed_url: `${options.baseUrl}/api/feed.json`,
|
|
206
|
+
icon: options.logo,
|
|
207
|
+
items: options.posts.map((post) => {
|
|
208
|
+
const postUrl = `${options.baseUrl}${postPath}/${encodeURIComponent(post.slug)}`;
|
|
209
|
+
return {
|
|
210
|
+
id: postUrl,
|
|
211
|
+
url: postUrl,
|
|
212
|
+
title: post.title,
|
|
213
|
+
summary: post.description || post.excerpt,
|
|
214
|
+
image: post.coverImage,
|
|
215
|
+
date_published: post.date,
|
|
216
|
+
authors: post.authorName ? [{ name: post.authorName }] : [],
|
|
217
|
+
tags: post.tags
|
|
218
|
+
};
|
|
219
|
+
})
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
function generateSitemap(baseUrl, posts, options) {
|
|
223
|
+
const postPath = options?.postPath ?? "/posts";
|
|
224
|
+
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
225
|
+
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
|
226
|
+
<url>
|
|
227
|
+
<loc>${escapeXml(baseUrl)}</loc>
|
|
228
|
+
<changefreq>daily</changefreq>
|
|
229
|
+
<priority>1.0</priority>
|
|
230
|
+
</url>
|
|
231
|
+
${posts.map((post) => {
|
|
232
|
+
const postUrl = `${baseUrl}${postPath}/${encodeURIComponent(post.slug)}`;
|
|
233
|
+
return `
|
|
234
|
+
<url>
|
|
235
|
+
<loc>${escapeXml(postUrl)}</loc>
|
|
236
|
+
${post.date ? `<lastmod>${new Date(post.date).toISOString().split("T")[0]}</lastmod>` : ""}
|
|
237
|
+
<changefreq>weekly</changefreq>
|
|
238
|
+
<priority>0.8</priority>
|
|
239
|
+
</url>`;
|
|
240
|
+
}).join("")}
|
|
241
|
+
</urlset>`;
|
|
242
|
+
}
|
|
243
|
+
function generateRobotsTxt(sitemapUrl, options) {
|
|
244
|
+
const lines = ["User-agent: *"];
|
|
245
|
+
if (options?.allowPaths) {
|
|
246
|
+
for (const path of options.allowPaths) {
|
|
247
|
+
lines.push(`Allow: ${path}`);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
if (options?.disallowPaths) {
|
|
251
|
+
for (const path of options.disallowPaths) {
|
|
252
|
+
lines.push(`Disallow: ${path}`);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
lines.push("", `Sitemap: ${sitemapUrl}`);
|
|
256
|
+
return lines.join("\n");
|
|
257
|
+
}
|
|
258
|
+
function metaTagsToHtml(tags) {
|
|
259
|
+
return tags.map((tag) => {
|
|
260
|
+
if (tag.title) return `<title>${escapeHtml(tag.title)}</title>`;
|
|
261
|
+
if (tag.tagName === "link") {
|
|
262
|
+
return `<link rel="${escapeHtml(tag.rel)}" href="${escapeHtml(tag.href)}">`;
|
|
263
|
+
}
|
|
264
|
+
if (tag.property) {
|
|
265
|
+
return `<meta property="${escapeHtml(tag.property)}" content="${escapeHtml(tag.content)}">`;
|
|
266
|
+
}
|
|
267
|
+
if (tag.name) {
|
|
268
|
+
return `<meta name="${escapeHtml(tag.name)}" content="${escapeHtml(tag.content)}">`;
|
|
269
|
+
}
|
|
270
|
+
return "";
|
|
271
|
+
}).filter(Boolean).join("\n");
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
export { createBreadcrumbJsonLd, createItemListJsonLd, createOrganizationJsonLd, createWebsiteJsonLd, generateJsonFeed, generateListMetaTags, generateMetaTags, generatePostMetaTags, generateRobotsTxt, generateRssFeed, generateSitemap, metaTagsToHtml, postToArticleJsonLd, serializeJsonLd };
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var chunkUCXXH2MP_cjs = require('../chunk-UCXXH2MP.cjs');
|
|
4
|
+
require('../chunk-OBGZSXTJ.cjs');
|
|
5
|
+
|
|
6
|
+
// src/server/index.ts
|
|
7
|
+
function createHandler(options) {
|
|
8
|
+
const { domain, basePath, fetch: fetchFn = globalThis.fetch } = options;
|
|
9
|
+
const baseUrl = chunkUCXXH2MP_cjs.normalizeBaseUrl(domain);
|
|
10
|
+
const prefix = basePath ? basePath.replace(/\/$/, "") : "";
|
|
11
|
+
return async (request) => {
|
|
12
|
+
const url = new URL(request.url);
|
|
13
|
+
let path = url.pathname;
|
|
14
|
+
if (prefix && path.startsWith(prefix)) {
|
|
15
|
+
path = path.slice(prefix.length) || "/";
|
|
16
|
+
}
|
|
17
|
+
const targetUrl = `${baseUrl}${path}${url.search}`;
|
|
18
|
+
const response = await fetchFn(targetUrl, {
|
|
19
|
+
method: request.method,
|
|
20
|
+
headers: {
|
|
21
|
+
Accept: request.headers.get("Accept") ?? "*/*",
|
|
22
|
+
"User-Agent": "folderblog-sdk/0.0.1"
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
return new Response(response.body, {
|
|
26
|
+
status: response.status,
|
|
27
|
+
statusText: response.statusText,
|
|
28
|
+
headers: response.headers
|
|
29
|
+
});
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
exports.createHandler = createHandler;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* folderblog/server - Server-side middleware for embedding folder.blog content
|
|
3
|
+
*
|
|
4
|
+
* Provides a Web Standard Request/Response handler that works with
|
|
5
|
+
* any framework (Hono, Express via adapter, Next.js, etc.)
|
|
6
|
+
*
|
|
7
|
+
* @example Hono
|
|
8
|
+
* ```ts
|
|
9
|
+
* import { Hono } from 'hono';
|
|
10
|
+
* import { createHandler } from 'folderblog/server';
|
|
11
|
+
*
|
|
12
|
+
* const app = new Hono();
|
|
13
|
+
* const handler = createHandler({ domain: 'yourname.folder.blog' });
|
|
14
|
+
*
|
|
15
|
+
* app.get('/blog/*', (c) => handler(c.req.raw));
|
|
16
|
+
* ```
|
|
17
|
+
*
|
|
18
|
+
* @example Node.js
|
|
19
|
+
* ```ts
|
|
20
|
+
* import { createHandler } from 'folderblog/server';
|
|
21
|
+
*
|
|
22
|
+
* const handler = createHandler({ domain: 'yourname.folder.blog' });
|
|
23
|
+
* // Use with any Request/Response compatible server
|
|
24
|
+
* ```
|
|
25
|
+
*
|
|
26
|
+
* @packageDocumentation
|
|
27
|
+
*/
|
|
28
|
+
interface FolderBlogHandlerOptions {
|
|
29
|
+
/** The folder.blog domain to proxy (e.g. 'yourname.folder.blog') */
|
|
30
|
+
domain: string;
|
|
31
|
+
/** Base path prefix to strip from incoming requests (e.g. '/blog') */
|
|
32
|
+
basePath?: string;
|
|
33
|
+
/** Custom fetch implementation */
|
|
34
|
+
fetch?: typeof fetch;
|
|
35
|
+
}
|
|
36
|
+
type FolderBlogHandler = (request: Request) => Promise<Response>;
|
|
37
|
+
/**
|
|
38
|
+
* Create a Web Standard handler that proxies requests to a folder.blog site.
|
|
39
|
+
*
|
|
40
|
+
* Maps incoming request paths to the blog's API and returns the response.
|
|
41
|
+
* Strips the basePath prefix if provided.
|
|
42
|
+
*
|
|
43
|
+
* @example
|
|
44
|
+
* ```ts
|
|
45
|
+
* const handler = createHandler({
|
|
46
|
+
* domain: 'yourname.folder.blog',
|
|
47
|
+
* basePath: '/blog',
|
|
48
|
+
* });
|
|
49
|
+
*
|
|
50
|
+
* // GET /blog/api/posts → proxied to yourname.folder.blog/api/posts
|
|
51
|
+
* const response = await handler(request);
|
|
52
|
+
* ```
|
|
53
|
+
*/
|
|
54
|
+
declare function createHandler(options: FolderBlogHandlerOptions): FolderBlogHandler;
|
|
55
|
+
|
|
56
|
+
export { type FolderBlogHandler, type FolderBlogHandlerOptions, createHandler };
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* folderblog/server - Server-side middleware for embedding folder.blog content
|
|
3
|
+
*
|
|
4
|
+
* Provides a Web Standard Request/Response handler that works with
|
|
5
|
+
* any framework (Hono, Express via adapter, Next.js, etc.)
|
|
6
|
+
*
|
|
7
|
+
* @example Hono
|
|
8
|
+
* ```ts
|
|
9
|
+
* import { Hono } from 'hono';
|
|
10
|
+
* import { createHandler } from 'folderblog/server';
|
|
11
|
+
*
|
|
12
|
+
* const app = new Hono();
|
|
13
|
+
* const handler = createHandler({ domain: 'yourname.folder.blog' });
|
|
14
|
+
*
|
|
15
|
+
* app.get('/blog/*', (c) => handler(c.req.raw));
|
|
16
|
+
* ```
|
|
17
|
+
*
|
|
18
|
+
* @example Node.js
|
|
19
|
+
* ```ts
|
|
20
|
+
* import { createHandler } from 'folderblog/server';
|
|
21
|
+
*
|
|
22
|
+
* const handler = createHandler({ domain: 'yourname.folder.blog' });
|
|
23
|
+
* // Use with any Request/Response compatible server
|
|
24
|
+
* ```
|
|
25
|
+
*
|
|
26
|
+
* @packageDocumentation
|
|
27
|
+
*/
|
|
28
|
+
interface FolderBlogHandlerOptions {
|
|
29
|
+
/** The folder.blog domain to proxy (e.g. 'yourname.folder.blog') */
|
|
30
|
+
domain: string;
|
|
31
|
+
/** Base path prefix to strip from incoming requests (e.g. '/blog') */
|
|
32
|
+
basePath?: string;
|
|
33
|
+
/** Custom fetch implementation */
|
|
34
|
+
fetch?: typeof fetch;
|
|
35
|
+
}
|
|
36
|
+
type FolderBlogHandler = (request: Request) => Promise<Response>;
|
|
37
|
+
/**
|
|
38
|
+
* Create a Web Standard handler that proxies requests to a folder.blog site.
|
|
39
|
+
*
|
|
40
|
+
* Maps incoming request paths to the blog's API and returns the response.
|
|
41
|
+
* Strips the basePath prefix if provided.
|
|
42
|
+
*
|
|
43
|
+
* @example
|
|
44
|
+
* ```ts
|
|
45
|
+
* const handler = createHandler({
|
|
46
|
+
* domain: 'yourname.folder.blog',
|
|
47
|
+
* basePath: '/blog',
|
|
48
|
+
* });
|
|
49
|
+
*
|
|
50
|
+
* // GET /blog/api/posts → proxied to yourname.folder.blog/api/posts
|
|
51
|
+
* const response = await handler(request);
|
|
52
|
+
* ```
|
|
53
|
+
*/
|
|
54
|
+
declare function createHandler(options: FolderBlogHandlerOptions): FolderBlogHandler;
|
|
55
|
+
|
|
56
|
+
export { type FolderBlogHandler, type FolderBlogHandlerOptions, createHandler };
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { normalizeBaseUrl } from '../chunk-Q6EXKX6K.js';
|
|
2
|
+
import '../chunk-3RG5ZIWI.js';
|
|
3
|
+
|
|
4
|
+
// src/server/index.ts
|
|
5
|
+
function createHandler(options) {
|
|
6
|
+
const { domain, basePath, fetch: fetchFn = globalThis.fetch } = options;
|
|
7
|
+
const baseUrl = normalizeBaseUrl(domain);
|
|
8
|
+
const prefix = basePath ? basePath.replace(/\/$/, "") : "";
|
|
9
|
+
return async (request) => {
|
|
10
|
+
const url = new URL(request.url);
|
|
11
|
+
let path = url.pathname;
|
|
12
|
+
if (prefix && path.startsWith(prefix)) {
|
|
13
|
+
path = path.slice(prefix.length) || "/";
|
|
14
|
+
}
|
|
15
|
+
const targetUrl = `${baseUrl}${path}${url.search}`;
|
|
16
|
+
const response = await fetchFn(targetUrl, {
|
|
17
|
+
method: request.method,
|
|
18
|
+
headers: {
|
|
19
|
+
Accept: request.headers.get("Accept") ?? "*/*",
|
|
20
|
+
"User-Agent": "folderblog-sdk/0.0.1"
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
return new Response(response.body, {
|
|
24
|
+
status: response.status,
|
|
25
|
+
statusText: response.statusText,
|
|
26
|
+
headers: response.headers
|
|
27
|
+
});
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export { createHandler };
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "folderblog",
|
|
3
|
-
"version": "0.0.
|
|
4
|
-
"description": "SDK for
|
|
3
|
+
"version": "0.0.3",
|
|
4
|
+
"description": "Official SDK for folder.blog — client, server middleware, and markdown processing",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
7
7
|
"module": "./dist/index.js",
|
|
@@ -11,7 +11,61 @@
|
|
|
11
11
|
"types": "./dist/index.d.ts",
|
|
12
12
|
"import": "./dist/index.js",
|
|
13
13
|
"require": "./dist/index.cjs"
|
|
14
|
-
}
|
|
14
|
+
},
|
|
15
|
+
"./local": {
|
|
16
|
+
"types": "./dist/local/index.d.ts",
|
|
17
|
+
"import": "./dist/local/index.js",
|
|
18
|
+
"require": "./dist/local/index.cjs"
|
|
19
|
+
},
|
|
20
|
+
"./processor": {
|
|
21
|
+
"types": "./dist/processor/index.d.ts",
|
|
22
|
+
"import": "./dist/processor/index.js",
|
|
23
|
+
"require": "./dist/processor/index.cjs"
|
|
24
|
+
},
|
|
25
|
+
"./processor/plugins": {
|
|
26
|
+
"types": "./dist/processor/plugins.d.ts",
|
|
27
|
+
"import": "./dist/processor/plugins.js",
|
|
28
|
+
"require": "./dist/processor/plugins.cjs"
|
|
29
|
+
},
|
|
30
|
+
"./processor/types": {
|
|
31
|
+
"types": "./dist/processor/types.d.ts",
|
|
32
|
+
"import": "./dist/processor/types.js",
|
|
33
|
+
"require": "./dist/processor/types.cjs"
|
|
34
|
+
},
|
|
35
|
+
"./seo": {
|
|
36
|
+
"types": "./dist/seo/index.d.ts",
|
|
37
|
+
"import": "./dist/seo/index.js",
|
|
38
|
+
"require": "./dist/seo/index.cjs"
|
|
39
|
+
},
|
|
40
|
+
"./server": {
|
|
41
|
+
"types": "./dist/server/index.d.ts",
|
|
42
|
+
"import": "./dist/server/index.js",
|
|
43
|
+
"require": "./dist/server/index.cjs"
|
|
44
|
+
},
|
|
45
|
+
"./cli": {
|
|
46
|
+
"types": "./dist/cli/index.d.ts",
|
|
47
|
+
"import": "./dist/cli/index.js",
|
|
48
|
+
"require": "./dist/cli/index.cjs"
|
|
49
|
+
},
|
|
50
|
+
"./plugins/embed-transformers": {
|
|
51
|
+
"types": "./dist/plugins/embed-transformers.d.ts",
|
|
52
|
+
"import": "./dist/plugins/embed-transformers.js",
|
|
53
|
+
"require": "./dist/plugins/embed-transformers.cjs"
|
|
54
|
+
},
|
|
55
|
+
"./plugins/embed-cloudflare-ai": {
|
|
56
|
+
"types": "./dist/plugins/embed-cloudflare-ai.d.ts",
|
|
57
|
+
"import": "./dist/plugins/embed-cloudflare-ai.js",
|
|
58
|
+
"require": "./dist/plugins/embed-cloudflare-ai.cjs"
|
|
59
|
+
},
|
|
60
|
+
"./plugins/similarity": {
|
|
61
|
+
"types": "./dist/plugins/similarity.d.ts",
|
|
62
|
+
"import": "./dist/plugins/similarity.js",
|
|
63
|
+
"require": "./dist/plugins/similarity.cjs"
|
|
64
|
+
},
|
|
65
|
+
"./package.json": "./package.json"
|
|
66
|
+
},
|
|
67
|
+
"bin": {
|
|
68
|
+
"folderblog": "./dist/cli/bin.js"
|
|
15
69
|
},
|
|
16
70
|
"files": [
|
|
17
71
|
"dist"
|
|
@@ -21,33 +75,66 @@
|
|
|
21
75
|
"dev": "tsup --watch",
|
|
22
76
|
"test": "vitest run",
|
|
23
77
|
"test:watch": "vitest",
|
|
24
|
-
"test:coverage": "vitest run --coverage",
|
|
25
78
|
"typecheck": "tsc --noEmit",
|
|
26
79
|
"prepublishOnly": "pnpm build"
|
|
27
80
|
},
|
|
81
|
+
"dependencies": {
|
|
82
|
+
"@sindresorhus/slugify": "^2.2.1",
|
|
83
|
+
"commander": "^13.0.0",
|
|
84
|
+
"github-slugger": "^2.0.0",
|
|
85
|
+
"gray-matter": "^4.0.3",
|
|
86
|
+
"hast-util-to-string": "^3.0.1",
|
|
87
|
+
"mdast-util-to-string": "^4.0.0",
|
|
88
|
+
"minisearch": "^7.2.0",
|
|
89
|
+
"rehype-autolink-headings": "^7.1.0",
|
|
90
|
+
"rehype-external-links": "^3.0.0",
|
|
91
|
+
"rehype-raw": "^7.0.0",
|
|
92
|
+
"rehype-slug": "6.0.0",
|
|
93
|
+
"rehype-stringify": "^10.0.1",
|
|
94
|
+
"remark-gfm": "^4.0.0",
|
|
95
|
+
"remark-obsidian-link": "^0.2.4",
|
|
96
|
+
"remark-parse": "^11.0.0",
|
|
97
|
+
"remark-rehype": "^11.1.1",
|
|
98
|
+
"unified": "^11.0.5",
|
|
99
|
+
"unist-util-visit": "^5.0.0",
|
|
100
|
+
"vfile": "^6.0.3"
|
|
101
|
+
},
|
|
102
|
+
"devDependencies": {
|
|
103
|
+
"@folderblog/processor": "workspace:*",
|
|
104
|
+
"@huggingface/transformers": "^3.8.1",
|
|
105
|
+
"@types/node": "^22.0.0",
|
|
106
|
+
"tsup": "^8.0.0",
|
|
107
|
+
"typescript": "^5.7.2",
|
|
108
|
+
"vitest": "^2.0.0"
|
|
109
|
+
},
|
|
28
110
|
"keywords": [
|
|
29
111
|
"folderblog",
|
|
112
|
+
"folder-blog",
|
|
30
113
|
"blog",
|
|
31
114
|
"cms",
|
|
32
115
|
"headless",
|
|
33
116
|
"api",
|
|
34
117
|
"sdk",
|
|
35
118
|
"markdown",
|
|
36
|
-
"static-site"
|
|
119
|
+
"static-site",
|
|
120
|
+
"processor"
|
|
37
121
|
],
|
|
38
122
|
"author": "",
|
|
39
123
|
"license": "MIT",
|
|
40
124
|
"homepage": "https://folder.blog",
|
|
41
125
|
"repository": {
|
|
42
126
|
"type": "git",
|
|
43
|
-
"url": "https://github.com/
|
|
127
|
+
"url": "https://github.com/iplanwebsites/folder-blog-mono"
|
|
44
128
|
},
|
|
45
|
-
"
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
"
|
|
129
|
+
"peerDependencies": {
|
|
130
|
+
"@huggingface/transformers": ">=3.0.0"
|
|
131
|
+
},
|
|
132
|
+
"peerDependenciesMeta": {
|
|
133
|
+
"@huggingface/transformers": {
|
|
134
|
+
"optional": true
|
|
135
|
+
}
|
|
50
136
|
},
|
|
137
|
+
"sideEffects": false,
|
|
51
138
|
"engines": {
|
|
52
139
|
"node": ">=18.0.0"
|
|
53
140
|
}
|