@ferrule-io/ok-fine 0.0.0-stage → 0.3.7
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/LICENSE +202 -0
- package/README.md +93 -2
- package/dist/auth/discovery.js +67 -0
- package/dist/auth/http-auth.js +50 -0
- package/dist/auth/verifier.js +176 -0
- package/dist/cli-options.js +99 -0
- package/dist/cli.js +115 -0
- package/dist/config.js +320 -0
- package/dist/data-dir-lock.js +60 -0
- package/dist/errors.js +13 -0
- package/dist/http/mcp-route.js +32 -0
- package/dist/http/rest.js +243 -0
- package/dist/http/ui.js +118 -0
- package/dist/main.js +32 -0
- package/dist/mcp/server.js +203 -0
- package/dist/okf/concept.js +99 -0
- package/dist/okf/frontmatter.js +180 -0
- package/dist/okf/index-file.js +74 -0
- package/dist/okf/lint.js +480 -0
- package/dist/okf/log-file.js +74 -0
- package/dist/okf/markdown.js +106 -0
- package/dist/okf/paths.js +99 -0
- package/dist/okf/repository.js +53 -0
- package/dist/okf/semantics.js +121 -0
- package/dist/server.js +343 -0
- package/dist/service/feedback.js +40 -0
- package/dist/service/knowledge-service.js +819 -0
- package/dist/service/principal.js +22 -0
- package/dist/store/archive.js +108 -0
- package/dist/store/backend.js +4 -0
- package/dist/store/bundle.js +250 -0
- package/dist/store/catalog.js +295 -0
- package/dist/store/fs-util.js +120 -0
- package/dist/store/git-backend.js +713 -0
- package/dist/store/git.js +274 -0
- package/dist/store/mutex.js +19 -0
- package/dist/store/path-index.js +54 -0
- package/dist/ui/assets/bash-BiGZg9Om.js +1 -0
- package/dist/ui/assets/css-BGIxQoYs.js +1 -0
- package/dist/ui/assets/css-DUjzNfgv.js +1 -0
- package/dist/ui/assets/diff-woXpYk--.js +1 -0
- package/dist/ui/assets/dockerfile-IyjqRm3v.js +1 -0
- package/dist/ui/assets/geist-cyrillic-ext-wght-normal-DjL33-gN.woff2 +0 -0
- package/dist/ui/assets/geist-cyrillic-wght-normal-BEAKL7Jp.woff2 +0 -0
- package/dist/ui/assets/geist-latin-ext-wght-normal-DC-KSUi6.woff2 +0 -0
- package/dist/ui/assets/geist-latin-wght-normal-BgDaEnEv.woff2 +0 -0
- package/dist/ui/assets/geist-mono-cyrillic-ext-wght-normal-X_5orZeX.woff2 +0 -0
- package/dist/ui/assets/geist-mono-cyrillic-wght-normal-DiZS0aHC.woff2 +0 -0
- package/dist/ui/assets/geist-mono-latin-ext-wght-normal-Bwz-egvJ.woff2 +0 -0
- package/dist/ui/assets/geist-mono-latin-wght-normal-XN7g48iV.woff2 +0 -0
- package/dist/ui/assets/geist-mono-symbols2-wght-normal-CO5SzqOn.woff2 +0 -0
- package/dist/ui/assets/geist-mono-vietnamese-wght-normal-DadHysG0.woff2 +0 -0
- package/dist/ui/assets/geist-vietnamese-wght-normal-6IgcOCM7.woff2 +0 -0
- package/dist/ui/assets/github-dark-default-DXG-b-1a.js +1 -0
- package/dist/ui/assets/github-light-default-BXViO-2h.js +1 -0
- package/dist/ui/assets/go-rLFTqkRN.js +1 -0
- package/dist/ui/assets/highlight-CW19vWvw.js +153 -0
- package/dist/ui/assets/html-BMn1ogTm.js +1 -0
- package/dist/ui/assets/html-BlpXnbzK.js +1 -0
- package/dist/ui/assets/index-0b2NNRC5.js +127 -0
- package/dist/ui/assets/index-CFuTAZQM.css +1 -0
- package/dist/ui/assets/java-BtIQNnN0.js +1 -0
- package/dist/ui/assets/java-DxlWrXwe.js +1 -0
- package/dist/ui/assets/javascript-BzbQS41l.js +1 -0
- package/dist/ui/assets/javascript-Yelw-5ZN.js +1 -0
- package/dist/ui/assets/json-qhed-kSA.js +1 -0
- package/dist/ui/assets/jsx-D3KKoC5L.js +1 -0
- package/dist/ui/assets/jsx-hEbJ82HF.js +1 -0
- package/dist/ui/assets/markdown-BYOwaDjH.js +1 -0
- package/dist/ui/assets/python-gzcpVVnB.js +1 -0
- package/dist/ui/assets/ruby-Di5fYh6S.js +1 -0
- package/dist/ui/assets/rust-DpiIf5JO.js +1 -0
- package/dist/ui/assets/shellscript-CqHdAlCp.js +1 -0
- package/dist/ui/assets/sql-BOsskAmu.js +1 -0
- package/dist/ui/assets/sql-CNu3Or2W.js +1 -0
- package/dist/ui/assets/toml-CcmNWLt0.js +1 -0
- package/dist/ui/assets/tsx-5f3ulCja.js +1 -0
- package/dist/ui/assets/tsx-BOvsFOI6.js +1 -0
- package/dist/ui/assets/typescript-Utq2Cl8c.js +1 -0
- package/dist/ui/assets/typescript-j_1H8WHN.js +1 -0
- package/dist/ui/assets/yaml-CxbubiML.js +1 -0
- package/dist/ui/assets/yaml-DijMHRDA.js +1 -0
- package/dist/ui/favicon.svg +11 -0
- package/dist/ui/index.html +16 -0
- package/dist/ui/theme-init.js +10 -0
- package/dist/version.js +2 -0
- package/package.json +77 -4
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { parseFrontmatter, splitFrontmatter } from "./frontmatter.js";
|
|
2
|
+
import { extractLinks } from "./markdown.js";
|
|
3
|
+
import { blobRevision } from "./paths.js";
|
|
4
|
+
import { displayTitle, effectiveStatus, generatedAt, generatedBy, lastVerifiedAt, staleAfter, trustTier, } from "./semantics.js";
|
|
5
|
+
export function parseConcept(project, id, file, buf) {
|
|
6
|
+
const text = buf.toString("utf8");
|
|
7
|
+
const revision = blobRevision(buf);
|
|
8
|
+
const split = splitFrontmatter(text);
|
|
9
|
+
if (!split) {
|
|
10
|
+
return {
|
|
11
|
+
project,
|
|
12
|
+
id,
|
|
13
|
+
file,
|
|
14
|
+
revision,
|
|
15
|
+
frontmatter: null,
|
|
16
|
+
body: text,
|
|
17
|
+
parseError: "missing_frontmatter",
|
|
18
|
+
title: displayTitle(null, id),
|
|
19
|
+
description: null,
|
|
20
|
+
type: null,
|
|
21
|
+
tags: [],
|
|
22
|
+
status: "stable",
|
|
23
|
+
trustTier: "unverified",
|
|
24
|
+
staleAfter: null,
|
|
25
|
+
generatedAt: null,
|
|
26
|
+
generatedBy: null,
|
|
27
|
+
lastVerifiedAt: null,
|
|
28
|
+
outbound: [],
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
const parsed = parseFrontmatter(split.yaml);
|
|
32
|
+
if ("error" in parsed) {
|
|
33
|
+
return {
|
|
34
|
+
project,
|
|
35
|
+
id,
|
|
36
|
+
file,
|
|
37
|
+
revision,
|
|
38
|
+
frontmatter: null,
|
|
39
|
+
body: split.body,
|
|
40
|
+
parseError: parsed.error,
|
|
41
|
+
title: displayTitle(null, id),
|
|
42
|
+
description: null,
|
|
43
|
+
type: null,
|
|
44
|
+
tags: [],
|
|
45
|
+
status: "stable",
|
|
46
|
+
trustTier: "unverified",
|
|
47
|
+
staleAfter: null,
|
|
48
|
+
generatedAt: null,
|
|
49
|
+
generatedBy: null,
|
|
50
|
+
lastVerifiedAt: null,
|
|
51
|
+
outbound: [],
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
const { data } = parsed;
|
|
55
|
+
if (typeof data.type !== "string" || data.type.trim().length === 0) {
|
|
56
|
+
return {
|
|
57
|
+
project,
|
|
58
|
+
id,
|
|
59
|
+
file,
|
|
60
|
+
revision,
|
|
61
|
+
frontmatter: null,
|
|
62
|
+
body: split.body,
|
|
63
|
+
parseError: "missing_type",
|
|
64
|
+
title: displayTitle(data, id),
|
|
65
|
+
description: typeof data.description === "string" ? data.description : null,
|
|
66
|
+
type: null,
|
|
67
|
+
tags: [],
|
|
68
|
+
status: effectiveStatus(data),
|
|
69
|
+
trustTier: trustTier(data),
|
|
70
|
+
staleAfter: staleAfter(data),
|
|
71
|
+
generatedAt: generatedAt(data),
|
|
72
|
+
generatedBy: generatedBy(data),
|
|
73
|
+
lastVerifiedAt: lastVerifiedAt(data),
|
|
74
|
+
outbound: extractLinks(split.body, id),
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
const tags = Array.isArray(data.tags) ? data.tags.filter((t) => typeof t === "string") : [];
|
|
78
|
+
return {
|
|
79
|
+
project,
|
|
80
|
+
id,
|
|
81
|
+
file,
|
|
82
|
+
revision,
|
|
83
|
+
frontmatter: data,
|
|
84
|
+
body: split.body,
|
|
85
|
+
parseError: null,
|
|
86
|
+
title: displayTitle(data, id),
|
|
87
|
+
description: typeof data.description === "string" ? data.description : null,
|
|
88
|
+
type: data.type,
|
|
89
|
+
tags,
|
|
90
|
+
status: effectiveStatus(data),
|
|
91
|
+
trustTier: trustTier(data),
|
|
92
|
+
staleAfter: staleAfter(data),
|
|
93
|
+
generatedAt: generatedAt(data),
|
|
94
|
+
generatedBy: generatedBy(data),
|
|
95
|
+
lastVerifiedAt: lastVerifiedAt(data),
|
|
96
|
+
outbound: extractLinks(split.body, id),
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
//# sourceMappingURL=concept.js.map
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import { isDeepStrictEqual } from "node:util";
|
|
2
|
+
import { Document, isMap, parseDocument } from "yaml";
|
|
3
|
+
export function splitFrontmatter(text) {
|
|
4
|
+
if (text.charCodeAt(0) === 0xfeff) {
|
|
5
|
+
text = text.slice(1);
|
|
6
|
+
}
|
|
7
|
+
const match = /^---\r?\n/.exec(text);
|
|
8
|
+
if (!match) {
|
|
9
|
+
return null;
|
|
10
|
+
}
|
|
11
|
+
const yamlStart = match[0].length;
|
|
12
|
+
const closingRegex = /\r?\n---\r?\n|\r?\n---$/g;
|
|
13
|
+
closingRegex.lastIndex = yamlStart;
|
|
14
|
+
const closeMatch = closingRegex.exec(text);
|
|
15
|
+
if (!closeMatch) {
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
const yamlEnd = closeMatch.index;
|
|
19
|
+
const bodyStart = closeMatch.index + closeMatch[0].length;
|
|
20
|
+
const yaml = text.slice(yamlStart, yamlEnd);
|
|
21
|
+
let body = text.slice(bodyStart);
|
|
22
|
+
body = body.replace(/^(\r?\n)+/, "");
|
|
23
|
+
return { yaml, body };
|
|
24
|
+
}
|
|
25
|
+
export function parseFrontmatter(yaml) {
|
|
26
|
+
const doc = parseDocument(yaml);
|
|
27
|
+
if (doc.errors.length > 0) {
|
|
28
|
+
return {
|
|
29
|
+
error: "invalid_yaml",
|
|
30
|
+
message: doc.errors.map((e) => e.message).join("; "),
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
if (!doc.contents || !isMap(doc.contents)) {
|
|
34
|
+
return {
|
|
35
|
+
error: "frontmatter_not_mapping",
|
|
36
|
+
message: "frontmatter must be a YAML mapping",
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
const data = (doc.toJS() ?? {});
|
|
40
|
+
return { doc, data };
|
|
41
|
+
}
|
|
42
|
+
export function serializeConcept(doc, body) {
|
|
43
|
+
const normalizedBody = body.replace(/\r\n/g, "\n").replace(/^\n+/, "").replace(/\s*$/, "");
|
|
44
|
+
return `---\n${doc.toString({ lineWidth: 0 })}---\n\n${normalizedBody}\n`;
|
|
45
|
+
}
|
|
46
|
+
function createFormattedNode(doc, key, val) {
|
|
47
|
+
if (key === "tags" && Array.isArray(val)) {
|
|
48
|
+
return doc.createNode(val, { flow: true });
|
|
49
|
+
}
|
|
50
|
+
if (key === "usage_window" && val && typeof val === "object" && !Array.isArray(val)) {
|
|
51
|
+
return doc.createNode(val, { flow: true });
|
|
52
|
+
}
|
|
53
|
+
if (key === "parameters" && Array.isArray(val)) {
|
|
54
|
+
return doc.createNode(val.map((item) => item && typeof item === "object" && !Array.isArray(item) ? doc.createNode(item, { flow: true }) : item));
|
|
55
|
+
}
|
|
56
|
+
if (key === "generated" && val && typeof val === "object") {
|
|
57
|
+
return doc.createNode(val, { flow: true });
|
|
58
|
+
}
|
|
59
|
+
return val;
|
|
60
|
+
}
|
|
61
|
+
export function applyFrontmatter(existing, input, server) {
|
|
62
|
+
const ignoredKeys = [];
|
|
63
|
+
if (existing !== null) {
|
|
64
|
+
const existingJs = (existing.toJS() ?? {});
|
|
65
|
+
if ("generated" in input && !isDeepStrictEqual(input.generated, existingJs.generated)) {
|
|
66
|
+
ignoredKeys.push("generated");
|
|
67
|
+
}
|
|
68
|
+
if ("verified" in input && !isDeepStrictEqual(input.verified, existingJs.verified)) {
|
|
69
|
+
ignoredKeys.push("verified");
|
|
70
|
+
}
|
|
71
|
+
const cleanInput = {};
|
|
72
|
+
for (const [k, v] of Object.entries(input)) {
|
|
73
|
+
if (k !== "generated" && k !== "verified") {
|
|
74
|
+
cleanInput[k] = v;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
const keysToDelete = [];
|
|
78
|
+
if (isMap(existing.contents)) {
|
|
79
|
+
for (const item of existing.contents.items) {
|
|
80
|
+
if (!item || typeof item !== "object" || !("key" in item) || item.key == null) {
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
let keyName = "";
|
|
84
|
+
const rawKey = item.key;
|
|
85
|
+
if (typeof rawKey === "string") {
|
|
86
|
+
keyName = rawKey;
|
|
87
|
+
}
|
|
88
|
+
else if (typeof rawKey === "object" && "value" in rawKey && typeof rawKey.value === "string") {
|
|
89
|
+
keyName = rawKey.value;
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
keyName = String(rawKey);
|
|
93
|
+
}
|
|
94
|
+
if (keyName && keyName !== "generated" && keyName !== "verified" && !(keyName in cleanInput)) {
|
|
95
|
+
keysToDelete.push(keyName);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
for (const k of keysToDelete) {
|
|
100
|
+
existing.delete(k);
|
|
101
|
+
}
|
|
102
|
+
for (const [k, v] of Object.entries(cleanInput)) {
|
|
103
|
+
if (existing.has(k)) {
|
|
104
|
+
if (!isDeepStrictEqual(existingJs[k], v)) {
|
|
105
|
+
existing.set(k, createFormattedNode(existing, k, v));
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
existing.set(k, createFormattedNode(existing, k, v));
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
existing.set("generated", existing.createNode(server.generated, { flow: true }));
|
|
113
|
+
return { doc: existing, ignoredKeys };
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
if ("generated" in input) {
|
|
117
|
+
ignoredKeys.push("generated");
|
|
118
|
+
}
|
|
119
|
+
if ("verified" in input) {
|
|
120
|
+
ignoredKeys.push("verified");
|
|
121
|
+
}
|
|
122
|
+
const cleanInput = {};
|
|
123
|
+
for (const [k, v] of Object.entries(input)) {
|
|
124
|
+
if (k !== "generated" && k !== "verified") {
|
|
125
|
+
cleanInput[k] = v;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
const doc = new Document();
|
|
129
|
+
const firstKeys = ["type", "title", "description", "resource", "tags", "status"];
|
|
130
|
+
const endKeys = ["stale_after", "sources", "usage_window"];
|
|
131
|
+
for (const k of firstKeys) {
|
|
132
|
+
if (k in cleanInput) {
|
|
133
|
+
doc.set(k, createFormattedNode(doc, k, cleanInput[k]));
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
for (const [k, v] of Object.entries(cleanInput)) {
|
|
137
|
+
if (!firstKeys.includes(k) && !endKeys.includes(k)) {
|
|
138
|
+
doc.set(k, createFormattedNode(doc, k, v));
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
doc.set("generated", doc.createNode(server.generated, { flow: true }));
|
|
142
|
+
for (const k of endKeys) {
|
|
143
|
+
if (k in cleanInput) {
|
|
144
|
+
doc.set(k, createFormattedNode(doc, k, cleanInput[k]));
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return { doc, ignoredKeys };
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
export function appendVerification(doc, entry) {
|
|
151
|
+
const js = (doc.toJS() ?? {});
|
|
152
|
+
const raw = js.verified;
|
|
153
|
+
let list = [];
|
|
154
|
+
if (raw && typeof raw === "object") {
|
|
155
|
+
if (Array.isArray(raw)) {
|
|
156
|
+
list = raw.filter((item) => Boolean(item && typeof item === "object" && typeof item.by === "string" && item.by.length > 0));
|
|
157
|
+
}
|
|
158
|
+
else {
|
|
159
|
+
if ("by" in raw && typeof raw.by === "string" && raw.by.length > 0) {
|
|
160
|
+
const atVal = "at" in raw && typeof raw.at === "string" ? raw.at : undefined;
|
|
161
|
+
list = [{ by: raw.by, at: atVal }];
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
list.push(entry);
|
|
166
|
+
if (list.length === 1) {
|
|
167
|
+
doc.set("verified", doc.createNode(list[0], { flow: true }));
|
|
168
|
+
}
|
|
169
|
+
else {
|
|
170
|
+
doc.set("verified", doc.createNode(list.map((e) => doc.createNode(e, { flow: true }))));
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
export function nowIso() {
|
|
174
|
+
return new Date().toISOString().replace(/\.\d{3}Z$/, "Z");
|
|
175
|
+
}
|
|
176
|
+
export function isoAfterDays(now, days) {
|
|
177
|
+
const d = new Date(now.getTime() + days * 86_400_000);
|
|
178
|
+
return d.toISOString().replace(/\.\d{3}Z$/, "Z");
|
|
179
|
+
}
|
|
180
|
+
//# sourceMappingURL=frontmatter.js.map
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
export function renderIndex(listing) {
|
|
2
|
+
const sections = [];
|
|
3
|
+
// Group concepts by type
|
|
4
|
+
const byType = new Map();
|
|
5
|
+
for (const c of listing.concepts) {
|
|
6
|
+
const typeKey = c.type && c.type.length > 0 ? c.type : "Other";
|
|
7
|
+
let list = byType.get(typeKey);
|
|
8
|
+
if (!list) {
|
|
9
|
+
list = [];
|
|
10
|
+
byType.set(typeKey, list);
|
|
11
|
+
}
|
|
12
|
+
list.push({ file: c.file, title: c.title, description: c.description });
|
|
13
|
+
}
|
|
14
|
+
const sortedTypes = Array.from(byType).sort(([a], [b]) => a.localeCompare(b, "en"));
|
|
15
|
+
for (const [type, items] of sortedTypes) {
|
|
16
|
+
items.sort((a, b) => {
|
|
17
|
+
const titleCmp = a.title.toLowerCase().localeCompare(b.title.toLowerCase(), "en");
|
|
18
|
+
if (titleCmp !== 0) {
|
|
19
|
+
return titleCmp;
|
|
20
|
+
}
|
|
21
|
+
return a.file.localeCompare(b.file, "en");
|
|
22
|
+
});
|
|
23
|
+
const lines = [`# ${type}`];
|
|
24
|
+
for (const item of items) {
|
|
25
|
+
const uri = encodeURI(item.file);
|
|
26
|
+
if (item.description != null) {
|
|
27
|
+
const collapsedDesc = item.description.replace(/\s+/g, " ").trim();
|
|
28
|
+
lines.push(`* [${item.title}](${uri}) - ${collapsedDesc}`);
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
lines.push(`* [${item.title}](${uri})`);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
sections.push(lines.join("\n"));
|
|
35
|
+
}
|
|
36
|
+
// Non-markdown files
|
|
37
|
+
if (listing.files.length > 0) {
|
|
38
|
+
const sortedFiles = listing.files.slice().sort((a, b) => a.localeCompare(b, "en"));
|
|
39
|
+
const lines = ["# Files"];
|
|
40
|
+
for (const f of sortedFiles) {
|
|
41
|
+
lines.push(`* [${f}](${encodeURI(f)})`);
|
|
42
|
+
}
|
|
43
|
+
sections.push(lines.join("\n"));
|
|
44
|
+
}
|
|
45
|
+
// Directories
|
|
46
|
+
if (listing.dirs.length > 0) {
|
|
47
|
+
const sortedDirs = listing.dirs.slice().sort((a, b) => a.name.localeCompare(b.name, "en"));
|
|
48
|
+
const lines = ["# Directories"];
|
|
49
|
+
for (const d of sortedDirs) {
|
|
50
|
+
const uri = `${encodeURI(d.name)}/`;
|
|
51
|
+
let countText;
|
|
52
|
+
if (d.conceptCount > 0) {
|
|
53
|
+
countText = d.conceptCount === 1 ? "1 concept" : `${d.conceptCount} concepts`;
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
countText = d.fileCount === 1 ? "1 file" : `${d.fileCount} files`;
|
|
57
|
+
}
|
|
58
|
+
lines.push(`* [${d.name}](${uri}) - ${countText}`);
|
|
59
|
+
}
|
|
60
|
+
sections.push(lines.join("\n"));
|
|
61
|
+
}
|
|
62
|
+
let body;
|
|
63
|
+
if (sections.length === 0) {
|
|
64
|
+
body = "# Concepts\n";
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
body = `${sections.join("\n\n")}\n`;
|
|
68
|
+
}
|
|
69
|
+
if (listing.isRoot) {
|
|
70
|
+
return `---\nokf_version: "0.2"\n---\n\n${body}`;
|
|
71
|
+
}
|
|
72
|
+
return body;
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=index-file.js.map
|