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
package/README.md
CHANGED
|
@@ -1,87 +1,148 @@
|
|
|
1
1
|
# folderblog
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
Zero dependencies. Works in Node.js, Deno, Bun, and browsers.
|
|
3
|
+
Official SDK for [folder.blog](https://folder.blog) — API client, server middleware, markdown processor, and CLI in one package.
|
|
6
4
|
|
|
7
5
|
## Install
|
|
8
6
|
|
|
9
7
|
```bash
|
|
10
|
-
npm
|
|
8
|
+
npm i folderblog
|
|
11
9
|
```
|
|
12
10
|
|
|
13
|
-
##
|
|
11
|
+
## API Client
|
|
12
|
+
|
|
13
|
+
Read posts from any folder.blog site:
|
|
14
14
|
|
|
15
|
-
```
|
|
16
|
-
import { folderBlog } from
|
|
15
|
+
```ts
|
|
16
|
+
import { folderBlog } from "folderblog";
|
|
17
17
|
|
|
18
|
-
const blog = folderBlog(
|
|
18
|
+
const blog = folderBlog("yourname.folder.blog");
|
|
19
19
|
|
|
20
20
|
const posts = await blog.posts.list();
|
|
21
|
-
const post = await blog.posts.get(
|
|
21
|
+
const post = await blog.posts.get("hello-world");
|
|
22
22
|
const site = await blog.site.get();
|
|
23
|
+
const tags = await blog.tags.list();
|
|
24
|
+
const rss = await blog.feed.rss();
|
|
23
25
|
```
|
|
24
26
|
|
|
25
|
-
##
|
|
27
|
+
## Markdown Processor
|
|
26
28
|
|
|
27
|
-
|
|
29
|
+
Process a folder of markdown files into structured JSON:
|
|
28
30
|
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
+
```ts
|
|
32
|
+
import { processFolder } from "folderblog";
|
|
33
|
+
|
|
34
|
+
const result = await processFolder({
|
|
35
|
+
dir: { input: "./content", output: "./dist" },
|
|
36
|
+
});
|
|
31
37
|
|
|
32
|
-
posts
|
|
33
|
-
posts[0].title // "Hello World"
|
|
34
|
-
posts[0].date // "2024-01-15"
|
|
35
|
-
posts[0].tags // ["intro"]
|
|
36
|
-
posts[0].excerpt // "My first post..."
|
|
37
|
-
posts[0].url // "/api/posts/hello-world"
|
|
38
|
+
console.log(`${result.posts.length} posts processed`);
|
|
38
39
|
```
|
|
39
40
|
|
|
40
|
-
|
|
41
|
+
Enable wiki-link resolution (Obsidian-style `[[links]]`):
|
|
42
|
+
|
|
43
|
+
```ts
|
|
44
|
+
const result = await processFolder({
|
|
45
|
+
dir: { input: "./vault", output: "./dist" },
|
|
46
|
+
pipeline: { wikiLinks: true },
|
|
47
|
+
});
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### Using the Processor class directly
|
|
51
|
+
|
|
52
|
+
```ts
|
|
53
|
+
import { Processor } from "folderblog";
|
|
41
54
|
|
|
42
|
-
|
|
43
|
-
|
|
55
|
+
const processor = new Processor({
|
|
56
|
+
config: {
|
|
57
|
+
dir: { input: "./content", output: "./dist" },
|
|
58
|
+
pipeline: { gfm: true, allowRawHtml: true },
|
|
59
|
+
},
|
|
60
|
+
});
|
|
44
61
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
62
|
+
await processor.initialize();
|
|
63
|
+
const result = await processor.process();
|
|
64
|
+
await processor.dispose();
|
|
48
65
|
```
|
|
49
66
|
|
|
50
|
-
###
|
|
67
|
+
### Markdown utilities
|
|
51
68
|
|
|
52
|
-
```
|
|
53
|
-
|
|
69
|
+
```ts
|
|
70
|
+
import { processMarkdown, parseFrontmatter, toSlug } from "folderblog";
|
|
54
71
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
site.postsCount // 12
|
|
58
|
-
site.url // "https://yourname.folder.blog"
|
|
72
|
+
const { html, frontmatter } = await processMarkdown("# Hello\n\nWorld");
|
|
73
|
+
const slug = toSlug("My Blog Post"); // 'my-blog-post'
|
|
59
74
|
```
|
|
60
75
|
|
|
61
|
-
##
|
|
76
|
+
## Server Middleware
|
|
62
77
|
|
|
63
|
-
|
|
64
|
-
import { folderBlog, NotFoundError } from 'folderblog';
|
|
78
|
+
Proxy a folder.blog into your app. Works with any Web Standard Request/Response framework:
|
|
65
79
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
}
|
|
80
|
+
```ts
|
|
81
|
+
import { createHandler } from "folderblog/server";
|
|
82
|
+
|
|
83
|
+
const handler = createHandler({
|
|
84
|
+
domain: "yourname.folder.blog",
|
|
85
|
+
basePath: "/blog",
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
// Hono
|
|
89
|
+
app.get("/blog/*", (c) => handler(c.req.raw));
|
|
90
|
+
|
|
91
|
+
// Node.js / any Web Standard server
|
|
92
|
+
const response = await handler(request);
|
|
73
93
|
```
|
|
74
94
|
|
|
75
|
-
##
|
|
95
|
+
## CLI
|
|
76
96
|
|
|
77
|
-
```
|
|
78
|
-
|
|
97
|
+
```bash
|
|
98
|
+
npx folderblog build # Auto-detect config
|
|
99
|
+
npx folderblog build -i ./content # Specify input dir
|
|
100
|
+
npx folderblog build -o ./dist # Specify output dir
|
|
101
|
+
npx folderblog build -c my.config.js # Custom config file
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Config file (auto-detected as `folderblog.config.{js,mjs,ts}`):
|
|
105
|
+
|
|
106
|
+
```js
|
|
107
|
+
export default {
|
|
108
|
+
dir: { input: "./content", output: "./dist" },
|
|
109
|
+
pipeline: { gfm: true, wikiLinks: true },
|
|
110
|
+
};
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## Sub-path Imports
|
|
114
|
+
|
|
115
|
+
For tree-shaking, import from specific sub-paths:
|
|
116
|
+
|
|
117
|
+
```ts
|
|
118
|
+
import { Processor } from "folderblog/processor";
|
|
119
|
+
import { PluginManager } from "folderblog/processor/plugins";
|
|
120
|
+
import type { ProcessConfig, ProcessedPost } from "folderblog/processor/types";
|
|
121
|
+
import { createHandler } from "folderblog/server";
|
|
122
|
+
import { build } from "folderblog/cli";
|
|
79
123
|
```
|
|
80
124
|
|
|
81
|
-
|
|
125
|
+
Everything is also available from the main `'folderblog'` entry point.
|
|
82
126
|
|
|
83
|
-
|
|
84
|
-
|
|
127
|
+
## Error Handling
|
|
128
|
+
|
|
129
|
+
```ts
|
|
130
|
+
import { folderBlog, NotFoundError, ApiError, NetworkError } from "folderblog";
|
|
131
|
+
|
|
132
|
+
const blog = folderBlog("yourname.folder.blog");
|
|
133
|
+
|
|
134
|
+
try {
|
|
135
|
+
const post = await blog.posts.get("missing");
|
|
136
|
+
} catch (err) {
|
|
137
|
+
if (err instanceof NotFoundError) {
|
|
138
|
+
console.log("Post not found");
|
|
139
|
+
} else if (err instanceof ApiError) {
|
|
140
|
+
console.log(`API error: ${err.status}`);
|
|
141
|
+
} else if (err instanceof NetworkError) {
|
|
142
|
+
console.log("Network failure", err.cause);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
```
|
|
85
146
|
|
|
86
147
|
## License
|
|
87
148
|
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// ../processor/src/types/config.ts
|
|
4
|
+
var DEFAULT_IMAGE_SIZES = [
|
|
5
|
+
{ width: 320, height: null, suffix: "xs" },
|
|
6
|
+
{ width: 640, height: null, suffix: "sm" },
|
|
7
|
+
{ width: 1024, height: null, suffix: "md" },
|
|
8
|
+
{ width: 1920, height: null, suffix: "lg" },
|
|
9
|
+
{ width: 3840, height: null, suffix: "xl" }
|
|
10
|
+
];
|
|
11
|
+
var withDefaults = (config) => ({
|
|
12
|
+
dir: config.dir,
|
|
13
|
+
plugins: config.plugins ?? {},
|
|
14
|
+
media: {
|
|
15
|
+
optimize: true,
|
|
16
|
+
sizes: DEFAULT_IMAGE_SIZES,
|
|
17
|
+
format: "webp",
|
|
18
|
+
quality: 80,
|
|
19
|
+
useHash: true,
|
|
20
|
+
useSharding: false,
|
|
21
|
+
pathPrefix: "/_media",
|
|
22
|
+
...config.media
|
|
23
|
+
},
|
|
24
|
+
content: {
|
|
25
|
+
notePathPrefix: "/content",
|
|
26
|
+
processAllFiles: false,
|
|
27
|
+
ignoreFiles: ["CONTRIBUTING.md", "README.md", "readme.md", "LICENSE.md"],
|
|
28
|
+
exportPosts: false,
|
|
29
|
+
trackRelationships: false,
|
|
30
|
+
includeSlugTracking: false,
|
|
31
|
+
slugConflictStrategy: "number",
|
|
32
|
+
slugScope: "global",
|
|
33
|
+
...config.content
|
|
34
|
+
},
|
|
35
|
+
mermaid: {
|
|
36
|
+
enabled: true,
|
|
37
|
+
strategy: "inline-svg",
|
|
38
|
+
dark: false,
|
|
39
|
+
...config.mermaid
|
|
40
|
+
},
|
|
41
|
+
pipeline: {
|
|
42
|
+
gfm: true,
|
|
43
|
+
allowRawHtml: true,
|
|
44
|
+
syntaxHighlighting: true,
|
|
45
|
+
parseFormulas: false,
|
|
46
|
+
removeDeadLinks: false,
|
|
47
|
+
...config.pipeline
|
|
48
|
+
},
|
|
49
|
+
similarity: {
|
|
50
|
+
topN: 5,
|
|
51
|
+
threshold: 0,
|
|
52
|
+
...config.similarity
|
|
53
|
+
},
|
|
54
|
+
debug: {
|
|
55
|
+
level: 0,
|
|
56
|
+
timing: false,
|
|
57
|
+
...config.debug
|
|
58
|
+
},
|
|
59
|
+
cache: config.cache
|
|
60
|
+
});
|
|
61
|
+
var getOutputDir = (config) => config.dir.output ?? `${config.dir.input}/dist`;
|
|
62
|
+
var getMediaOutputDir = (config) => {
|
|
63
|
+
const outputDir = getOutputDir(config);
|
|
64
|
+
const mediaSubdir = config.dir.mediaOutput ?? "_media";
|
|
65
|
+
return `${outputDir}/${mediaSubdir}`;
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
// ../processor/src/types/output.ts
|
|
69
|
+
var OUTPUT_FILES = {
|
|
70
|
+
POSTS: "posts.json",
|
|
71
|
+
POSTS_SLUG_MAP: "posts-slug-map.json",
|
|
72
|
+
POSTS_PATH_MAP: "posts-path-map.json",
|
|
73
|
+
MEDIAS: "media.json",
|
|
74
|
+
MEDIA_PATH_MAP: "media-path-map.json",
|
|
75
|
+
GRAPH: "graph.json",
|
|
76
|
+
TEXT_EMBEDDINGS: "posts-embedding-hash-map.json",
|
|
77
|
+
IMAGE_EMBEDDINGS: "media-embedding-hash-map.json",
|
|
78
|
+
SIMILARITY: "similarity.json",
|
|
79
|
+
DATABASE: "repo.db",
|
|
80
|
+
ISSUES: "processor-issues.json"
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
// ../processor/src/types/issues.ts
|
|
84
|
+
var isBrokenLinkIssue = (issue) => issue.category === "broken-link";
|
|
85
|
+
var isMissingMediaIssue = (issue) => issue.category === "missing-media";
|
|
86
|
+
var isMediaProcessingIssue = (issue) => issue.category === "media-processing";
|
|
87
|
+
var isSlugConflictIssue = (issue) => issue.category === "slug-conflict";
|
|
88
|
+
var isMermaidErrorIssue = (issue) => issue.category === "mermaid-error";
|
|
89
|
+
var isEmbeddingErrorIssue = (issue) => issue.category === "embedding-error";
|
|
90
|
+
var isPluginErrorIssue = (issue) => issue.category === "plugin-error";
|
|
91
|
+
|
|
92
|
+
// ../processor/src/types/cache.ts
|
|
93
|
+
var createEmptyCacheStats = () => ({
|
|
94
|
+
mediaCacheHits: 0,
|
|
95
|
+
mediaCacheMisses: 0,
|
|
96
|
+
textEmbeddingCacheHits: 0,
|
|
97
|
+
textEmbeddingCacheMisses: 0,
|
|
98
|
+
imageEmbeddingCacheHits: 0,
|
|
99
|
+
imageEmbeddingCacheMisses: 0
|
|
100
|
+
});
|
|
101
|
+
function buildMediaCacheFromManifest(medias) {
|
|
102
|
+
const cache = /* @__PURE__ */ new Map();
|
|
103
|
+
for (const media of medias) {
|
|
104
|
+
const hash = media.metadata?.hash;
|
|
105
|
+
if (!hash || !media.metadata?.width || !media.metadata?.height) continue;
|
|
106
|
+
cache.set(hash, {
|
|
107
|
+
width: media.metadata.width,
|
|
108
|
+
height: media.metadata.height,
|
|
109
|
+
format: media.metadata.format ?? "webp",
|
|
110
|
+
size: media.metadata.size ?? 0,
|
|
111
|
+
originalSize: media.metadata.originalSize,
|
|
112
|
+
outputPath: media.outputPath,
|
|
113
|
+
sizes: (media.sizes ?? []).map((s) => ({
|
|
114
|
+
suffix: s.suffix,
|
|
115
|
+
outputPath: s.outputPath,
|
|
116
|
+
width: s.width,
|
|
117
|
+
height: s.height,
|
|
118
|
+
size: s.size
|
|
119
|
+
}))
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
return cache;
|
|
123
|
+
}
|
|
124
|
+
function buildEmbeddingCacheFromManifest(embeddingMap) {
|
|
125
|
+
const cache = /* @__PURE__ */ new Map();
|
|
126
|
+
for (const [hash, embedding] of Object.entries(embeddingMap)) {
|
|
127
|
+
if (Array.isArray(embedding) && embedding.length > 0) {
|
|
128
|
+
cache.set(hash, embedding);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
return cache;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
exports.DEFAULT_IMAGE_SIZES = DEFAULT_IMAGE_SIZES;
|
|
135
|
+
exports.OUTPUT_FILES = OUTPUT_FILES;
|
|
136
|
+
exports.buildEmbeddingCacheFromManifest = buildEmbeddingCacheFromManifest;
|
|
137
|
+
exports.buildMediaCacheFromManifest = buildMediaCacheFromManifest;
|
|
138
|
+
exports.createEmptyCacheStats = createEmptyCacheStats;
|
|
139
|
+
exports.getMediaOutputDir = getMediaOutputDir;
|
|
140
|
+
exports.getOutputDir = getOutputDir;
|
|
141
|
+
exports.isBrokenLinkIssue = isBrokenLinkIssue;
|
|
142
|
+
exports.isEmbeddingErrorIssue = isEmbeddingErrorIssue;
|
|
143
|
+
exports.isMediaProcessingIssue = isMediaProcessingIssue;
|
|
144
|
+
exports.isMermaidErrorIssue = isMermaidErrorIssue;
|
|
145
|
+
exports.isMissingMediaIssue = isMissingMediaIssue;
|
|
146
|
+
exports.isPluginErrorIssue = isPluginErrorIssue;
|
|
147
|
+
exports.isSlugConflictIssue = isSlugConflictIssue;
|
|
148
|
+
exports.withDefaults = withDefaults;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
2
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
3
|
+
}) : x)(function(x) {
|
|
4
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
5
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
export { __require };
|