@voltro/content 0.53.0

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.
@@ -0,0 +1,161 @@
1
+ import { Schema } from 'effect';
2
+
3
+ export declare const buildFeed: <A>(entries: ReadonlyArray<A>, options: BuildFeedOptions<A>) => string;
4
+
5
+ export declare interface BuildFeedOptions<A> {
6
+ readonly title: string;
7
+ readonly siteUrl: string;
8
+ readonly description?: string;
9
+ /** Feed path (`/rss.xml`) — becomes the atom:link self href. */
10
+ readonly feedPath?: string;
11
+ /** Map one entry to its feed item. Return null to exclude it. */
12
+ readonly item: (entry: A) => FeedItemInput | null;
13
+ }
14
+
15
+ export declare interface CollectionDefinition<A = Record<string, unknown>> {
16
+ readonly name: string;
17
+ /** App-root-relative content directory (`content/posts`). */
18
+ readonly directory: string;
19
+ /** Frontmatter schema (markdown collections) or file schema (data
20
+ * collections). Violations are BUILD/BOOT errors naming file + field. */
21
+ readonly schema: Schema.Schema<A, any, never>;
22
+ /** `'markdown'` (default): every `.md` under the directory, frontmatter +
23
+ * rendered body. `'data'`: every `.json`, decoded whole — the
24
+ * authors.json case, no render path. */
25
+ readonly kind?: 'markdown' | 'data';
26
+ readonly i18n?: CollectionLocales;
27
+ /** Render options for this collection's markdown bodies (link rewriting,
28
+ * a code-fence hook, …) — applied wherever the SERVER renders an entry. */
29
+ readonly markdown?: RenderMarkdownOptions;
30
+ }
31
+
32
+ export declare interface CollectionLocales {
33
+ /** The locale trees this collection carries (first path segment). */
34
+ readonly locales: ReadonlyArray<string>;
35
+ /**
36
+ * What a missing translation does: `'fallback'` (default) serves the
37
+ * defaultLocale's entry under the requested locale; `'missing'` omits it
38
+ * (the page 404s). Incomplete translations are the normal case — decide
39
+ * per collection instead of improvising per page.
40
+ */
41
+ readonly missing?: 'fallback' | 'missing';
42
+ readonly defaultLocale: string;
43
+ }
44
+
45
+ export declare const contentArtifactPath: (collection: string, locale: string | undefined, slug?: string) => string;
46
+
47
+ export declare interface ContentEntry<A = Record<string, unknown>> {
48
+ readonly slug: string;
49
+ /** The locale tree this entry came from (localized collections only). When
50
+ * the missing-policy substituted the default locale's entry, `locale` is
51
+ * the REQUESTED locale and `fallback` is true. */
52
+ readonly locale?: string;
53
+ readonly fallback?: boolean;
54
+ readonly data: A;
55
+ /** Markdown collections: the rendered HTML (shiki included, server-rendered). */
56
+ readonly html?: string;
57
+ readonly headings?: ReadonlyArray<ContentHeading>;
58
+ /** Raw markdown body (server side; omitted from client artifacts by default). */
59
+ readonly body?: string;
60
+ }
61
+
62
+ declare interface ContentHeading {
63
+ readonly depth: number;
64
+ readonly slug: string;
65
+ readonly text: string;
66
+ }
67
+
68
+ export declare const contentRoot: () => string | null;
69
+
70
+ export declare const defineCollection: <A>(definition: CollectionDefinition<A>) => CollectionDefinition<A>;
71
+
72
+ export declare interface FeedItemInput {
73
+ readonly title: string;
74
+ readonly link: string;
75
+ readonly date: string | Date;
76
+ readonly description?: string;
77
+ readonly guid?: string;
78
+ /** `<category>` tags on the item. */
79
+ readonly categories?: ReadonlyArray<string>;
80
+ }
81
+
82
+ /** Every entry of a collection (sorted by slug). Server: filesystem + render;
83
+ * client: the build artifact. The `data` type is the schema's decoded type. */
84
+ export declare const getCollection: <A = Record<string, unknown>>(name: string, options?: GetCollectionOptions) => Promise<ReadonlyArray<ContentEntry<A>>>;
85
+
86
+ export declare interface GetCollectionOptions {
87
+ readonly locale?: string;
88
+ }
89
+
90
+ /** One entry by slug, or null. */
91
+ export declare const getEntry: <A = Record<string, unknown>>(name: string, slug: string, options?: GetCollectionOptions) => Promise<ContentEntry<A> | null>;
92
+
93
+ /** Dev watch invalidation + test seam. */
94
+ export declare const invalidateContentCache: (collection?: string) => void;
95
+
96
+ export declare const reference: (collection: string) => Schema.Schema<string>;
97
+
98
+ /** Every reference field declared by a collection's schema (top level):
99
+ * field name → target collection. */
100
+ export declare const referenceFieldsOf: (definition: CollectionDefinition) => ReadonlyMap<string, string>;
101
+
102
+ /**
103
+ * `reference('<collection>')` — a frontmatter field that names an entry of
104
+ * another collection by slug (`author: jane` against an `authors` data
105
+ * collection). Decodes as the slug string; the BUILD validates every
106
+ * reference against the target collection, so a dangling one is a named
107
+ * build error, not a runtime null. Resolve it with
108
+ * `getEntry(<collection>, value)`.
109
+ */
110
+ export declare const ReferenceTarget: unique symbol;
111
+
112
+ /** The referenced collection of one schema property, if it is a reference. */
113
+ export declare const referenceTargetOf: (ast: {
114
+ readonly annotations?: Readonly<Record<symbol, unknown>>;
115
+ }) => string | undefined;
116
+
117
+ export declare const registeredCollection: (name: string) => CollectionDefinition | undefined;
118
+
119
+ export declare const registeredCollections: () => ReadonlyArray<CollectionDefinition>;
120
+
121
+ declare interface RenderMarkdownOptions {
122
+ /** Rewrite relative links (`./sibling.md`, `../other/page.md`) into route
123
+ * paths. Receives the raw href; return the replacement (or the input). */
124
+ readonly resolveLink?: (href: string) => string;
125
+ /** Highlight code fences with shiki (node only; a browser call renders
126
+ * plain `<pre>`). Default true. */
127
+ readonly highlight?: boolean;
128
+ /**
129
+ * Post-process one rendered code fence. Receives the raw code, the resolved
130
+ * language (first info-string token, lowercased), the FULL info string
131
+ * (` ```ts preview=data-table ` ⇒ `'ts preview=data-table'`), and the
132
+ * rendered html. Return the replacement html — e.g. append a live-demo
133
+ * mount point derived from the info string.
134
+ */
135
+ readonly codeFence?: (fence: {
136
+ readonly text: string;
137
+ readonly lang: string;
138
+ readonly info: string;
139
+ readonly html: string;
140
+ }) => string;
141
+ }
142
+
143
+ /** Test seam. */
144
+ export declare const resetCollectionsForTest: () => void;
145
+
146
+ export declare const setContentRoot: (root: string | null) => void;
147
+
148
+ /** Slug from the path relative to the collection directory — nested dirs
149
+ * stay in the slug, the extension is stripped, locale segment removed when
150
+ * the collection is localized. */
151
+ export declare const slugFromRelativePath: (relativePath: string) => string;
152
+
153
+ /**
154
+ * Validate every `reference('<collection>')` field across all registered
155
+ * collections: each value must name an existing entry of the target
156
+ * collection. The BUILD calls this after loading — a dangling reference is a
157
+ * named build error, not a runtime null.
158
+ */
159
+ export declare const validateReferences: () => Promise<void>;
160
+
161
+ export { }
package/dist/index.js ADDED
@@ -0,0 +1,132 @@
1
+ import { Schema as e } from "effect";
2
+ //#region \0rolldown/runtime.js
3
+ var t = Object.defineProperty, n = /* @__PURE__ */ ((e, n) => {
4
+ let r = {};
5
+ for (var i in e) t(r, i, {
6
+ get: e[i],
7
+ enumerable: !0
8
+ });
9
+ return n || t(r, Symbol.toStringTag, { value: "Module" }), r;
10
+ })({
11
+ ReferenceTarget: () => u,
12
+ defineCollection: () => a,
13
+ reference: () => d,
14
+ referenceFieldsOf: () => p,
15
+ referenceTargetOf: () => f,
16
+ registeredCollection: () => s,
17
+ registeredCollections: () => o,
18
+ resetCollectionsForTest: () => c,
19
+ slugFromRelativePath: () => l
20
+ }), r = Symbol.for("@voltro/content:collections"), i = () => {
21
+ let e = globalThis;
22
+ return e[r] ??= /* @__PURE__ */ new Map();
23
+ }, a = (e) => {
24
+ let t = i().get(e.name);
25
+ if (t !== void 0 && t.directory !== e.directory) throw Error(`defineCollection('${e.name}') is declared twice with different directories ('${t.directory}' vs '${e.directory}') — collection names are global.`);
26
+ return i().set(e.name, e), e;
27
+ }, o = () => [...i().values()], s = (e) => i().get(e), c = () => {
28
+ i().clear();
29
+ }, l = (e) => e.replace(/\\/g, "/").replace(/\.(md|mdx|json)$/i, ""), u = Symbol.for("@voltro/content:reference-target"), d = (t) => e.String.annotations({ [u]: t }), f = (e) => {
30
+ let t = e.annotations?.[u];
31
+ return typeof t == "string" ? t : void 0;
32
+ }, p = (e) => {
33
+ let t = /* @__PURE__ */ new Map(), n = e.schema.ast;
34
+ for (let e of n?.propertySignatures ?? []) {
35
+ let n = [e.type, ...e.type.types ?? []];
36
+ for (let r of n) {
37
+ let n = f(r);
38
+ n !== void 0 && typeof e.name == "string" && t.set(e.name, n);
39
+ }
40
+ }
41
+ return t;
42
+ }, m = typeof globalThis.process?.versions?.node == "string", h = Symbol.for("@voltro/content:root"), g = (e) => {
43
+ let t = globalThis;
44
+ t[h] = e;
45
+ }, _ = () => globalThis[h] ?? null, v = Symbol.for("@voltro/content:cache"), y = () => {
46
+ let e = globalThis;
47
+ return e[v] ??= /* @__PURE__ */ new Map();
48
+ }, b = (e) => {
49
+ if (e === void 0) {
50
+ y().clear();
51
+ return;
52
+ }
53
+ for (let t of [...y().keys()]) (t === e || t.startsWith(`${e}|`)) && y().delete(t);
54
+ }, x = (e, t) => {
55
+ let n = t === void 0 ? e.name : `${e.name}|${t}`, r = y().get(n);
56
+ return r === void 0 &&
57
+ // @vite-ignore keeps the whole render pipeline (marked + shiki grammars)
58
+ (r = import(
59
+ /* @vite-ignore */
60
+ "./serverLoad"
61
+ ).then(({ loadServerCollection: n }) => n(e, t)), y().set(n, r)), r;
62
+ }, S = (e, t, n) => {
63
+ let r = t === void 0 ? e : `${e}.${t}`;
64
+ return n === void 0 ? `/assets/content/${r}/index.json` : `/assets/content/${r}/${n}.json`;
65
+ }, C = async (e) => {
66
+ let t = await fetch(e);
67
+ return t.ok ? await t.json() : null;
68
+ }, w = (e, t) => {
69
+ let n = s(e);
70
+ return n?.i18n === void 0 ? t : t ?? n.i18n.defaultLocale;
71
+ }, T = async (e, t = {}) => {
72
+ if (m) {
73
+ let n = s(e);
74
+ if (n === void 0) throw Error(`getCollection('${e}'): no such collection is defined — export a defineCollection({ name: '${e}', … }) from a *.collection.ts.`);
75
+ return (await x(n, t.locale)).entries;
76
+ }
77
+ return await C(S(e, w(e, t.locale))) ?? [];
78
+ }, E = async (e, t, n = {}) => {
79
+ if (m) {
80
+ let r = s(e);
81
+ if (r === void 0) throw Error(`getEntry('${e}'): no such collection is defined.`);
82
+ return (await x(r, n.locale)).bySlug.get(t) ?? null;
83
+ }
84
+ return await C(S(e, w(e, n.locale), t));
85
+ }, D = async () => {
86
+ let { referenceFieldsOf: e, registeredCollections: t } = await Promise.resolve().then(() => n), r = [];
87
+ for (let n of t()) {
88
+ let t = e(n);
89
+ if (t.size === 0) continue;
90
+ let i = n.i18n?.locales ?? [void 0];
91
+ for (let e of i) {
92
+ let i = await T(n.name, e === void 0 ? {} : { locale: e });
93
+ for (let e of i) for (let [i, a] of t) {
94
+ let t = e.data[i];
95
+ typeof t == "string" && t !== "" && await E(a, t) === null && r.push(`collection '${n.name}', entry '${e.slug}': field '${i}' references '${t}' in collection '${a}' — no such entry.`);
96
+ }
97
+ }
98
+ }
99
+ if (r.length > 0) throw Error(`dangling content reference(s):\n${r.join("\n")}`);
100
+ }, O = (e) => e.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&apos;"), k = (e) => {
101
+ let t = e instanceof Date ? e : new Date(e);
102
+ return Number.isNaN(t.getTime()) ? String(e) : t.toUTCString();
103
+ }, A = (e, t) => {
104
+ let n = t.siteUrl.replace(/\/+$/, ""), r = e.map((e) => t.item(e)).filter((e) => e !== null).sort((e, t) => new Date(t.date).getTime() - new Date(e.date).getTime()).map((e) => {
105
+ let t = e.link.startsWith("http") ? e.link : `${n}/${e.link.replace(/^\/+/, "")}`;
106
+ return [
107
+ " <item>",
108
+ ` <title>${O(e.title)}</title>`,
109
+ ` <link>${O(t)}</link>`,
110
+ ` <guid isPermaLink="${e.guid === void 0 ? "true" : "false"}">${O(e.guid ?? t)}</guid>`,
111
+ ` <pubDate>${O(k(e.date))}</pubDate>`,
112
+ ...e.description === void 0 ? [] : [` <description>${O(e.description)}</description>`],
113
+ ...(e.categories ?? []).map((e) => ` <category>${O(e)}</category>`),
114
+ " </item>"
115
+ ].join("\n");
116
+ }).join("\n");
117
+ return [
118
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>",
119
+ "<rss version=\"2.0\" xmlns:atom=\"http://www.w3.org/2005/Atom\">",
120
+ " <channel>",
121
+ ` <title>${O(t.title)}</title>`,
122
+ ` <link>${O(n)}</link>`,
123
+ ...t.description === void 0 ? [" <description></description>"] : [` <description>${O(t.description)}</description>`],
124
+ ` <atom:link href="${O(`${n}${t.feedPath ?? "/rss.xml"}`)}" rel="self" type="application/rss+xml" />`,
125
+ ...r === "" ? [] : [r],
126
+ " </channel>",
127
+ "</rss>",
128
+ ""
129
+ ].join("\n");
130
+ };
131
+ //#endregion
132
+ export { u as ReferenceTarget, A as buildFeed, S as contentArtifactPath, _ as contentRoot, a as defineCollection, T as getCollection, E as getEntry, b as invalidateContentCache, d as reference, p as referenceFieldsOf, f as referenceTargetOf, s as registeredCollection, o as registeredCollections, c as resetCollectionsForTest, g as setContentRoot, l as slugFromRelativePath, D as validateReferences };
@@ -0,0 +1,70 @@
1
+ export declare interface ContentHeading {
2
+ readonly depth: number;
3
+ readonly slug: string;
4
+ readonly text: string;
5
+ }
6
+
7
+ /**
8
+ * Heading data (depth, slug, text) straight from markdown SOURCE — no render
9
+ * pass, browser-safe, sync. Slugs and dedup counters are computed by the SAME
10
+ * code `renderMarkdown` uses, so a TOC built from this always matches the
11
+ * rendered `id=` attributes.
12
+ */
13
+ export declare const extractHeadings: (body: string) => ReadonlyArray<ContentHeading>;
14
+
15
+ /**
16
+ * Highlight one raw code string to dual-theme shiki HTML — the SAME path a
17
+ * markdown fence takes, for callers rendering code outside markdown (a
18
+ * demo-source panel, say). Node only: in a browser (or when shiki is not
19
+ * installed) it returns an escaped plain `<pre>`.
20
+ */
21
+ export declare const highlightCode: (code: string, lang: string | undefined) => Promise<string>;
22
+
23
+ export declare interface ParsedFrontmatter {
24
+ readonly data: Readonly<Record<string, string | ReadonlyArray<string>>>;
25
+ readonly body: string;
26
+ }
27
+
28
+ /** `key: scalar` lines between `---` markers — the only shapes our content
29
+ * needs, parsed without a YAML dependency. */
30
+ export declare const parseFrontmatter: (source: string) => ParsedFrontmatter;
31
+
32
+ export declare interface RenderedMarkdown {
33
+ readonly html: string;
34
+ readonly headings: ReadonlyArray<ContentHeading>;
35
+ }
36
+
37
+ /**
38
+ * Render a markdown body (GFM) to HTML with heading ids/anchors and the
39
+ * `headings[]` TOC data. When `highlight` (default) and running on node, code
40
+ * fences are shiki-highlighted with light+dark themes; the dark variant
41
+ * activates via the standard `.dark .shiki` / `data-theme` CSS convention
42
+ * shiki's dual-theme output uses.
43
+ */
44
+ export declare const renderMarkdown: (body: string, options?: RenderMarkdownOptions) => Promise<RenderedMarkdown>;
45
+
46
+ export declare interface RenderMarkdownOptions {
47
+ /** Rewrite relative links (`./sibling.md`, `../other/page.md`) into route
48
+ * paths. Receives the raw href; return the replacement (or the input). */
49
+ readonly resolveLink?: (href: string) => string;
50
+ /** Highlight code fences with shiki (node only; a browser call renders
51
+ * plain `<pre>`). Default true. */
52
+ readonly highlight?: boolean;
53
+ /**
54
+ * Post-process one rendered code fence. Receives the raw code, the resolved
55
+ * language (first info-string token, lowercased), the FULL info string
56
+ * (` ```ts preview=data-table ` ⇒ `'ts preview=data-table'`), and the
57
+ * rendered html. Return the replacement html — e.g. append a live-demo
58
+ * mount point derived from the info string.
59
+ */
60
+ readonly codeFence?: (fence: {
61
+ readonly text: string;
62
+ readonly lang: string;
63
+ readonly info: string;
64
+ readonly html: string;
65
+ }) => string;
66
+ }
67
+
68
+ export declare const slugifyHeading: (value: string) => string;
69
+
70
+ export { }
@@ -0,0 +1,118 @@
1
+ import { marked as e } from "marked";
2
+ //#region src/markdown.ts
3
+ var t = typeof globalThis.process?.versions?.node == "string", n = (e) => {
4
+ let t = e.trim();
5
+ return t.startsWith("[") && t.endsWith("]") ? t.slice(1, -1).split(",").map((e) => e.trim().replace(/^['"]|['"]$/g, "")).filter((e) => e.length > 0) : t.replace(/^['"]|['"]$/g, "");
6
+ }, r = (e) => {
7
+ let t = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/.exec(e);
8
+ if (!t) return {
9
+ data: {},
10
+ body: e
11
+ };
12
+ let [, r, i] = t, a = {};
13
+ for (let e of (r ?? "").split(/\r?\n/)) {
14
+ let t = e.indexOf(":");
15
+ if (t < 0) continue;
16
+ let r = e.slice(0, t).trim();
17
+ r && (a[r] = n(e.slice(t + 1)));
18
+ }
19
+ return {
20
+ data: a,
21
+ body: i ?? ""
22
+ };
23
+ }, i = (e) => e.map((e) => "text" in e && typeof e.text == "string" ? e.text : "").join(""), a = (e) => e.toLowerCase().trim().replace(/<[^>]+>/g, "").replace(/[^\p{Letter}\p{Number}]+/gu, "-").replace(/^-+|-+$/g, ""), o = null, s = [
24
+ "ts",
25
+ "tsx",
26
+ "typescript",
27
+ "js",
28
+ "jsx",
29
+ "json",
30
+ "bash",
31
+ "sh",
32
+ "shell",
33
+ "css",
34
+ "html",
35
+ "sql",
36
+ "yaml",
37
+ "toml",
38
+ "md",
39
+ "mdx",
40
+ "diff",
41
+ "text",
42
+ "txt"
43
+ ], c = () => t ? (o ??= import("shiki").then((e) => e.createHighlighter({
44
+ themes: ["github-light", "github-dark-dimmed"],
45
+ langs: [...s]
46
+ })).catch(() => null), o) : Promise.resolve(null), l = async (t, n = {}) => {
47
+ let r = [], o = /* @__PURE__ */ new Map(), l = n.highlight === !1 ? null : await c(), u = new e.Renderer();
48
+ if (u.heading = ({ depth: t, tokens: n }) => {
49
+ let s = e.Parser.parseInline(n), c = i(n), l = a(c), u = o.get(l) ?? 0;
50
+ o.set(l, u + 1);
51
+ let d = u === 0 ? l : `${l}-${u}`;
52
+ return r.push({
53
+ depth: t,
54
+ slug: d,
55
+ text: c
56
+ }), `<h${t} id="${d}"><a class="heading-anchor" href="#${d}" aria-hidden="true">#</a>${s}</h${t}>\n`;
57
+ }, n.resolveLink !== void 0) {
58
+ let t = n.resolveLink;
59
+ u.link = ({ href: n, title: r, tokens: i }) => {
60
+ let a = e.Parser.parseInline(i);
61
+ return `<a href="${t(n)}"${r ? ` title="${r}"` : ""}>${a}</a>`;
62
+ };
63
+ }
64
+ return u.code = ({ text: e, lang: t }) => {
65
+ let r = (t ?? "").trim(), i = (r.split(/\s+/)[0] ?? "").toLowerCase(), a;
66
+ if (l !== null && s.includes(i)) try {
67
+ a = l.codeToHtml(e, {
68
+ lang: i,
69
+ themes: {
70
+ light: "github-light",
71
+ dark: "github-dark-dimmed"
72
+ }
73
+ });
74
+ } catch {
75
+ a = `<pre><code>${e.replace(/&/g, "&amp;").replace(/</g, "&lt;")}</code></pre>\n`;
76
+ }
77
+ else a = `<pre><code>${e.replace(/&/g, "&amp;").replace(/</g, "&lt;")}</code></pre>\n`;
78
+ return n.codeFence === void 0 ? a : n.codeFence({
79
+ text: e,
80
+ lang: i,
81
+ info: r,
82
+ html: a
83
+ });
84
+ }, {
85
+ html: await e.parse(t, {
86
+ async: !0,
87
+ gfm: !0,
88
+ renderer: u
89
+ }),
90
+ headings: r
91
+ };
92
+ }, u = async (e, t) => {
93
+ let n = await c(), r = (t ?? "").trim().split(/\s+/)[0]?.toLowerCase() ?? "";
94
+ if (n !== null && s.includes(r)) try {
95
+ return n.codeToHtml(e, {
96
+ lang: r,
97
+ themes: {
98
+ light: "github-light",
99
+ dark: "github-dark-dimmed"
100
+ }
101
+ });
102
+ } catch {}
103
+ return `<pre><code>${e.replace(/&/g, "&amp;").replace(/</g, "&lt;")}</code></pre>`;
104
+ }, d = (t) => {
105
+ let n = [], r = /* @__PURE__ */ new Map();
106
+ for (let o of e.lexer(t)) {
107
+ if (o.type !== "heading") continue;
108
+ let e = o, t = i(e.tokens), s = a(t), c = r.get(s) ?? 0;
109
+ r.set(s, c + 1), n.push({
110
+ depth: e.depth,
111
+ slug: c === 0 ? s : `${s}-${c}`,
112
+ text: t
113
+ });
114
+ }
115
+ return n;
116
+ };
117
+ //#endregion
118
+ export { d as extractHeadings, u as highlightCode, r as parseFrontmatter, l as renderMarkdown, a as slugifyHeading };
package/package.json ADDED
@@ -0,0 +1,49 @@
1
+ {
2
+ "name": "@voltro/content",
3
+ "version": "0.53.0",
4
+ "description": "Content collections — file-based, schema-typed markdown content (defineCollection over content/<name>/**/*.md with effect/Schema frontmatter), an isomorphic getCollection/getEntry (server: FS + rendered HTML with node-gated shiki; client: fetches the build-emitted JSON artifacts), heading extraction, locale trees with fallback, data collections, and a generic feed builder.",
5
+ "keywords": [
6
+ "voltro",
7
+ "typescript",
8
+ "framework"
9
+ ],
10
+ "license": "SEE LICENSE IN LICENSE",
11
+ "homepage": "https://voltro.dev",
12
+ "bugs": {
13
+ "email": "support@voltro.dev"
14
+ },
15
+ "author": {
16
+ "name": "Voltro UG",
17
+ "url": "https://voltro.dev"
18
+ },
19
+ "type": "module",
20
+ "exports": {
21
+ ".": {
22
+ "types": "./dist/index.d.ts",
23
+ "import": "./dist/index.js",
24
+ "default": "./dist/index.js"
25
+ },
26
+ "./markdown": {
27
+ "types": "./dist/markdown.d.ts",
28
+ "import": "./dist/markdown.js",
29
+ "default": "./dist/markdown.js"
30
+ },
31
+ "./package.json": "./package.json"
32
+ },
33
+ "sideEffects": false,
34
+ "engines": {
35
+ "node": ">=24.0.0"
36
+ },
37
+ "dependencies": {
38
+ "marked": "^18.0.9"
39
+ },
40
+ "optionalDependencies": {
41
+ "shiki": "^4.4.3"
42
+ },
43
+ "peerDependencies": {
44
+ "effect": "^3.22.0"
45
+ },
46
+ "publishConfig": {
47
+ "access": "public"
48
+ }
49
+ }