@zvndev/darkmown 2.4.1-next.7.ge25dbba → 2.5.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.
- package/CHANGELOG.md +10 -0
- package/README.md +33 -0
- package/docs/spec-alignment.md +12 -1
- package/package.json +5 -1
- package/site/pages/docs/index.wd +22 -0
- package/src/compiler/actions.js +25 -1
- package/src/compiler/body.js +5 -1
- package/src/compiler/context.js +107 -1
- package/src/compiler/fetch.js +2 -1
- package/src/compiler/forms.js +19 -1
- package/src/compiler/loops.js +14 -1
- package/src/compiler/markdown.js +2 -1
- package/src/compiler/page.js +3 -1
- package/src/compiler/state.js +9 -1
- package/src/compiler/structure.js +3 -1
- package/src/tools/apply.js +274 -0
- package/src/tools/grammar.js +171 -0
- package/src/tools/index.js +170 -0
- package/src/tools/outline.js +321 -0
- package/src/tools/parse.js +81 -0
- package/src/tools/validate.js +169 -0
- package/types/builder.d.ts +21 -0
- package/types/catalog.d.ts +80 -1
- package/types/compiler/context.d.ts +155 -0
- package/types/compiler/page.d.ts +33 -6
- package/types/compiler/schema.d.ts +88 -0
- package/types/compiler.d.ts +2 -1
- package/types/feeds.d.ts +47 -4
- package/types/index.d.ts +3 -0
- package/types/scaffold.d.ts +3 -1
- package/types/tools/apply.d.ts +68 -0
- package/types/tools/grammar.d.ts +44 -0
- package/types/tools/index.d.ts +75 -0
- package/types/tools/outline.d.ts +102 -0
- package/types/tools/parse.d.ts +26 -0
- package/types/tools/validate.d.ts +36 -0
package/types/catalog.d.ts
CHANGED
|
@@ -3,15 +3,86 @@
|
|
|
3
3
|
* @returns {DirectiveCatalog}
|
|
4
4
|
*/
|
|
5
5
|
export function directiveCatalog(): DirectiveCatalog;
|
|
6
|
+
/**
|
|
7
|
+
* One page of a built site, as it appears in the generated `llms.txt` index and
|
|
8
|
+
* `llms-full.txt` corpus.
|
|
9
|
+
* @typedef {object} SitePage
|
|
10
|
+
* @property {string} title
|
|
11
|
+
* @property {string} url Absolute URL when the site declared `site_url`, else the route path.
|
|
12
|
+
* @property {string} description "" when the page has no `description:`.
|
|
13
|
+
* @property {string} [body] The page's source body (frontmatter stripped). Only
|
|
14
|
+
* the corpus carries it; the index omits it.
|
|
15
|
+
*/
|
|
16
|
+
/**
|
|
17
|
+
* The site a generated llms file describes. Plain data, passed in by the builder
|
|
18
|
+
* (the only layer that knows the routes), so this module stays a pure renderer.
|
|
19
|
+
* @typedef {object} SiteCorpus
|
|
20
|
+
* @property {string} title
|
|
21
|
+
* @property {string} description
|
|
22
|
+
* @property {string} url Site origin, or "" when the home page set no `site_url`.
|
|
23
|
+
* @property {SitePage[]} pages Every emitted route, in route order.
|
|
24
|
+
*/
|
|
6
25
|
/**
|
|
7
26
|
* Render the catalog as a compact llms.txt-style markdown cheatsheet — the
|
|
8
27
|
* artifact an app pastes into a small model's system prompt. Generated entirely
|
|
9
28
|
* from {@link directiveCatalog}, so it can never disagree with what compiles.
|
|
29
|
+
*
|
|
30
|
+
* When a `site` is supplied (the build always supplies one) the cheatsheet is
|
|
31
|
+
* followed by an INDEX of the site's pages and a pointer to `llms-full.txt`,
|
|
32
|
+
* which is the llms.txt convention: a short index, with the complete corpus one
|
|
33
|
+
* fetch away.
|
|
34
|
+
* @param {SiteCorpus} [site] The built site to index.
|
|
10
35
|
* @returns {string}
|
|
11
36
|
*/
|
|
12
|
-
export function llmsText(): string;
|
|
37
|
+
export function llmsText(site?: SiteCorpus): string;
|
|
38
|
+
/**
|
|
39
|
+
* Render `llms-full.txt`: the COMPLETE corpus that `llms.txt` indexes. Where the
|
|
40
|
+
* cheatsheet is one line per directive, this carries the full syntax template,
|
|
41
|
+
* description, example, and reactivity for every one, plus every compile-error
|
|
42
|
+
* code with its cause and fix: and then the full source text of every page on
|
|
43
|
+
* the site. Same generator, same catalog, so index and corpus cannot disagree.
|
|
44
|
+
* @param {SiteCorpus} [site] The built site whose pages form the corpus.
|
|
45
|
+
* @returns {string}
|
|
46
|
+
*/
|
|
47
|
+
export function llmsFullText(site?: SiteCorpus): string;
|
|
13
48
|
/** The action tokens the catalog exposes — used by the drift guard. */
|
|
14
49
|
export const CATALOG_ACTION_TOKENS: string[];
|
|
50
|
+
/**
|
|
51
|
+
* One page of a built site, as it appears in the generated `llms.txt` index and
|
|
52
|
+
* `llms-full.txt` corpus.
|
|
53
|
+
*/
|
|
54
|
+
export type SitePage = {
|
|
55
|
+
title: string;
|
|
56
|
+
/**
|
|
57
|
+
* Absolute URL when the site declared `site_url`, else the route path.
|
|
58
|
+
*/
|
|
59
|
+
url: string;
|
|
60
|
+
/**
|
|
61
|
+
* "" when the page has no `description:`.
|
|
62
|
+
*/
|
|
63
|
+
description: string;
|
|
64
|
+
/**
|
|
65
|
+
* The page's source body (frontmatter stripped). Only
|
|
66
|
+
* the corpus carries it; the index omits it.
|
|
67
|
+
*/
|
|
68
|
+
body?: string | undefined;
|
|
69
|
+
};
|
|
70
|
+
/**
|
|
71
|
+
* The site a generated llms file describes. Plain data, passed in by the builder
|
|
72
|
+
* (the only layer that knows the routes), so this module stays a pure renderer.
|
|
73
|
+
*/
|
|
74
|
+
export type SiteCorpus = {
|
|
75
|
+
title: string;
|
|
76
|
+
description: string;
|
|
77
|
+
/**
|
|
78
|
+
* Site origin, or "" when the home page set no `site_url`.
|
|
79
|
+
*/
|
|
80
|
+
url: string;
|
|
81
|
+
/**
|
|
82
|
+
* Every emitted route, in route order.
|
|
83
|
+
*/
|
|
84
|
+
pages: SitePage[];
|
|
85
|
+
};
|
|
15
86
|
/**
|
|
16
87
|
* Whether the directive
|
|
17
88
|
* ships zero JS (static), needs the reactive runtime (reactive), or can be
|
|
@@ -84,6 +155,14 @@ export type DirectiveCatalog = {
|
|
|
84
155
|
* Logical joiners (`and`/`or`/`not`).
|
|
85
156
|
*/
|
|
86
157
|
predicateJoiners: string[];
|
|
158
|
+
/**
|
|
159
|
+
* Frontmatter keys the framework reads.
|
|
160
|
+
*/
|
|
161
|
+
frontmatterKeys: CatalogEntry[];
|
|
162
|
+
/**
|
|
163
|
+
* The `schema:` JSON-LD types the compiler can populate.
|
|
164
|
+
*/
|
|
165
|
+
schemaTypes: string[];
|
|
87
166
|
/**
|
|
88
167
|
* The `WDxxx` code blocks.
|
|
89
168
|
*/
|
|
@@ -66,6 +66,32 @@
|
|
|
66
66
|
* read threads through — the real filesystem by default, or an in-memory map
|
|
67
67
|
* for `compileFromMemory`. Set once by `createCompilation` and reached via
|
|
68
68
|
* `ctx.comp.reader` in the directive handlers (includes, loop data).
|
|
69
|
+
* @property {Symbol[]} symbols Every declared thing this compile saw, in source
|
|
70
|
+
* order, recorded by {@link recordSymbol} from the handler that parsed it.
|
|
71
|
+
* Surfaced on {@link CompiledPage} so a tool layer can answer "what is in this
|
|
72
|
+
* page?" without parsing `.wd` a second time.
|
|
73
|
+
*/
|
|
74
|
+
/**
|
|
75
|
+
* One declared thing in a page, as the compiler saw it.
|
|
76
|
+
*
|
|
77
|
+
* `kind` is the directive family; `name` is what an author would call the thing
|
|
78
|
+
* and is what `refs` matches on. `detail` is a short human string in the same
|
|
79
|
+
* vocabulary the model writes, never a serialized AST: the tool layer's job is
|
|
80
|
+
* to hand back text a model can nearly paste, and translation is where small
|
|
81
|
+
* models lose.
|
|
82
|
+
*
|
|
83
|
+
* @typedef {object} Symbol
|
|
84
|
+
* @property {"state" | "store" | "computed" | "theme" | "fetch" | "action" | "loop" | "form" | "field" | "if" | "include" | "read"} kind
|
|
85
|
+
* @property {string} name The symbol's own name, or the thing it targets.
|
|
86
|
+
* @property {string} [detail] Short source-shaped description, e.g. `cart append p`.
|
|
87
|
+
* @property {boolean} [reactive] For loops: whether it stays reactive at runtime.
|
|
88
|
+
* @property {string} [target] For actions: the state key written.
|
|
89
|
+
* @property {string} [op] For actions: the validated action token.
|
|
90
|
+
* @property {string} file Absolute path of the file the symbol was declared in.
|
|
91
|
+
* @property {number} [endLine] For a block directive (`@loop`, `:if`), the
|
|
92
|
+
* 1-based line its closer sits on, so a tool can address the whole block.
|
|
93
|
+
* @property {number | null} line 1-based line in that file, or null for a read
|
|
94
|
+
* recorded by {@link recordRead} (see there for why prose reads carry no line).
|
|
69
95
|
*/
|
|
70
96
|
/**
|
|
71
97
|
* A lexical scope chain for static interpolation values (include args, loop vars).
|
|
@@ -119,6 +145,8 @@
|
|
|
119
145
|
* `@loop` recorded, or null. The builder reads it to multiply routes.
|
|
120
146
|
* @property {Set<string>} deps Absolute source-file dependencies of this compile.
|
|
121
147
|
* @property {Set<string>} collectionsUsed Collection names this compile looped.
|
|
148
|
+
* @property {Symbol[]} symbols Every declared thing the compile saw, in
|
|
149
|
+
* source order. See {@link recordSymbol}.
|
|
122
150
|
*/
|
|
123
151
|
/**
|
|
124
152
|
* A compiled page: the document plus its full HTML shell.
|
|
@@ -131,6 +159,8 @@
|
|
|
131
159
|
* `@loop` recorded, or null. The builder reads it to multiply routes.
|
|
132
160
|
* @property {Set<string>} deps Absolute source-file dependencies of this compile.
|
|
133
161
|
* @property {Set<string>} collectionsUsed Collection names this compile looped.
|
|
162
|
+
* @property {Symbol[]} symbols Every declared thing the compile saw, in
|
|
163
|
+
* source order. See {@link recordSymbol}.
|
|
134
164
|
*/
|
|
135
165
|
/**
|
|
136
166
|
* Structured, machine-readable companion to a compile error's string message.
|
|
@@ -208,6 +238,68 @@ export function normalizeNewlines(text: string): string;
|
|
|
208
238
|
* @returns {Compilation}
|
|
209
239
|
*/
|
|
210
240
|
export function createCompilation(reader: import("./reader.js").Reader): Compilation;
|
|
241
|
+
/**
|
|
242
|
+
* Record one declared thing as the handler that owns it parses it.
|
|
243
|
+
*
|
|
244
|
+
* This is the compiler telling a tool layer what is in a page, rather than a
|
|
245
|
+
* tool layer parsing `.wd` a second time. A shadow parser drifts, and the day it
|
|
246
|
+
* disagrees with `compilePage` it tells a model something true about a file that
|
|
247
|
+
* will not compile.
|
|
248
|
+
*
|
|
249
|
+
* Called from the `handle*` functions, where the 0-based `index` is live, so
|
|
250
|
+
* every symbol carries a real `file:line`. Include bodies share the parent's
|
|
251
|
+
* `Compilation` but compile under their own `ctx.file`, so attribution stays
|
|
252
|
+
* correct with no extra plumbing.
|
|
253
|
+
*
|
|
254
|
+
* @param {Ctx} ctx
|
|
255
|
+
* @param {number} index 0-based line index into the current body slice.
|
|
256
|
+
* @param {Omit<Symbol, "file" | "line">} sym
|
|
257
|
+
* @returns {Symbol} The pushed record, so a caller whose reactive/static decision
|
|
258
|
+
* happens in a later branch can stamp it rather than duplicate that logic.
|
|
259
|
+
*/
|
|
260
|
+
export function recordSymbol(ctx: Ctx, index: number, sym: Omit<Symbol, "file" | "line">): Symbol;
|
|
261
|
+
/**
|
|
262
|
+
* Record where a block directive CLOSES, so a tool layer can address the whole
|
|
263
|
+
* construct rather than only its opening line.
|
|
264
|
+
*
|
|
265
|
+
* Measured need, not speculation: with only the opener line recorded, the 1.5B
|
|
266
|
+
* targeted a `@loop` for replacement and swapped out its header, leaving the
|
|
267
|
+
* `@endloop` behind. That produced `[WD010] Stray "@endloop"` as the single
|
|
268
|
+
* most common compile failure of the round-4 tool run. A `@loop` is three or
|
|
269
|
+
* more lines and a tool has to be able to say so.
|
|
270
|
+
*
|
|
271
|
+
* The dispatcher owns this because only it has the block's extent, from
|
|
272
|
+
* `scanBlock`/`scanConditional`. `at` is the index the block handler's own
|
|
273
|
+
* symbol was pushed to; nested symbols recorded while compiling the body land
|
|
274
|
+
* after it and are left alone.
|
|
275
|
+
*
|
|
276
|
+
* @param {Ctx} ctx
|
|
277
|
+
* @param {number} at Index into `ctx.comp.symbols` the block handler wrote to.
|
|
278
|
+
* @param {number} endIndex 0-based body index of the closing line.
|
|
279
|
+
* @returns {void}
|
|
280
|
+
*/
|
|
281
|
+
export function stampBlockEnd(ctx: Ctx, at: number, endIndex: number): void;
|
|
282
|
+
/**
|
|
283
|
+
* Record that a page READS a state key through an interpolation.
|
|
284
|
+
*
|
|
285
|
+
* Deliberately line-less. Prose is rendered a chunk at a time by markdown-it, so
|
|
286
|
+
* no 0-based body `index` is in scope at the point a `{ name }` binding resolves,
|
|
287
|
+
* and threading one through the markdown layer would mean tracking source
|
|
288
|
+
* positions inside a renderer that does not care about them.
|
|
289
|
+
*
|
|
290
|
+
* Instead the compiler records the two things only it can know: that the
|
|
291
|
+
* expression resolved to this state key, and the exact expression text. The tool
|
|
292
|
+
* layer locates the line by searching the file for that compiler-supplied
|
|
293
|
+
* string, which is a substring search for a known needle rather than a second
|
|
294
|
+
* parser. Duplicates (a static loop re-renders its body once per row) are the
|
|
295
|
+
* caller's to fold.
|
|
296
|
+
*
|
|
297
|
+
* @param {Ctx} ctx
|
|
298
|
+
* @param {string} key Resolved state key.
|
|
299
|
+
* @param {string} expr The binding expression as authored, e.g. `cart | count`.
|
|
300
|
+
* @returns {void}
|
|
301
|
+
*/
|
|
302
|
+
export function recordRead(ctx: Ctx, key: string, expr: string): void;
|
|
211
303
|
/**
|
|
212
304
|
* @param {Scope | null} parent
|
|
213
305
|
* @param {Record<string, unknown>} [vars]
|
|
@@ -396,6 +488,59 @@ export type Compilation = {
|
|
|
396
488
|
* `ctx.comp.reader` in the directive handlers (includes, loop data).
|
|
397
489
|
*/
|
|
398
490
|
reader: import("./reader.js").Reader;
|
|
491
|
+
/**
|
|
492
|
+
* Every declared thing this compile saw, in source
|
|
493
|
+
* order, recorded by {@link recordSymbol} from the handler that parsed it.
|
|
494
|
+
* Surfaced on {@link CompiledPage} so a tool layer can answer "what is in this
|
|
495
|
+
* page?" without parsing `.wd` a second time.
|
|
496
|
+
*/
|
|
497
|
+
symbols: Symbol[];
|
|
498
|
+
};
|
|
499
|
+
/**
|
|
500
|
+
* One declared thing in a page, as the compiler saw it.
|
|
501
|
+
*
|
|
502
|
+
* `kind` is the directive family; `name` is what an author would call the thing
|
|
503
|
+
* and is what `refs` matches on. `detail` is a short human string in the same
|
|
504
|
+
* vocabulary the model writes, never a serialized AST: the tool layer's job is
|
|
505
|
+
* to hand back text a model can nearly paste, and translation is where small
|
|
506
|
+
* models lose.
|
|
507
|
+
*/
|
|
508
|
+
export type Symbol = {
|
|
509
|
+
kind: "state" | "store" | "computed" | "theme" | "fetch" | "action" | "loop" | "form" | "field" | "if" | "include" | "read";
|
|
510
|
+
/**
|
|
511
|
+
* The symbol's own name, or the thing it targets.
|
|
512
|
+
*/
|
|
513
|
+
name: string;
|
|
514
|
+
/**
|
|
515
|
+
* Short source-shaped description, e.g. `cart append p`.
|
|
516
|
+
*/
|
|
517
|
+
detail?: string | undefined;
|
|
518
|
+
/**
|
|
519
|
+
* For loops: whether it stays reactive at runtime.
|
|
520
|
+
*/
|
|
521
|
+
reactive?: boolean | undefined;
|
|
522
|
+
/**
|
|
523
|
+
* For actions: the state key written.
|
|
524
|
+
*/
|
|
525
|
+
target?: string | undefined;
|
|
526
|
+
/**
|
|
527
|
+
* For actions: the validated action token.
|
|
528
|
+
*/
|
|
529
|
+
op?: string | undefined;
|
|
530
|
+
/**
|
|
531
|
+
* Absolute path of the file the symbol was declared in.
|
|
532
|
+
*/
|
|
533
|
+
file: string;
|
|
534
|
+
/**
|
|
535
|
+
* For a block directive (`@loop`, `:if`), the
|
|
536
|
+
* 1-based line its closer sits on, so a tool can address the whole block.
|
|
537
|
+
*/
|
|
538
|
+
endLine?: number | undefined;
|
|
539
|
+
/**
|
|
540
|
+
* 1-based line in that file, or null for a read
|
|
541
|
+
* recorded by {@link recordRead} (see there for why prose reads carry no line).
|
|
542
|
+
*/
|
|
543
|
+
line: number | null;
|
|
399
544
|
};
|
|
400
545
|
/**
|
|
401
546
|
* A lexical scope chain for static interpolation values (include args, loop vars).
|
|
@@ -517,6 +662,11 @@ export type CompiledDocument = {
|
|
|
517
662
|
* Collection names this compile looped.
|
|
518
663
|
*/
|
|
519
664
|
collectionsUsed: Set<string>;
|
|
665
|
+
/**
|
|
666
|
+
* Every declared thing the compile saw, in
|
|
667
|
+
* source order. See {@link recordSymbol}.
|
|
668
|
+
*/
|
|
669
|
+
symbols: Symbol[];
|
|
520
670
|
};
|
|
521
671
|
/**
|
|
522
672
|
* A compiled page: the document plus its full HTML shell.
|
|
@@ -539,6 +689,11 @@ export type CompiledPage = {
|
|
|
539
689
|
* Collection names this compile looped.
|
|
540
690
|
*/
|
|
541
691
|
collectionsUsed: Set<string>;
|
|
692
|
+
/**
|
|
693
|
+
* Every declared thing the compile saw, in
|
|
694
|
+
* source order. See {@link recordSymbol}.
|
|
695
|
+
*/
|
|
696
|
+
symbols: Symbol[];
|
|
542
697
|
};
|
|
543
698
|
/**
|
|
544
699
|
* Structured, machine-readable companion to a compile error's string message.
|
package/types/compiler/page.d.ts
CHANGED
|
@@ -2,13 +2,15 @@
|
|
|
2
2
|
* Compile a page source file into a full HTML document plus its assets.
|
|
3
3
|
* @param {string} file Absolute path to the source `.md`/`.wd` file.
|
|
4
4
|
* @param {Paths} context Resolved project paths.
|
|
5
|
-
* @param {{ feed?: { href: string, title: string }, collections?: Map<string, import("./collections.js").CollectionRow[]>, vars?: Record<string, unknown>, reader?: import("./reader.js").Reader }} [options]
|
|
5
|
+
* @param {{ feed?: { href: string, title: string }, site?: SiteContext, collections?: Map<string, import("./collections.js").CollectionRow[]>, vars?: Record<string, unknown>, reader?: import("./reader.js").Reader }} [options]
|
|
6
6
|
* When a site-wide RSS feed is emitted (the home page set `site_url` and the
|
|
7
7
|
* site has dated posts), `feed` carries its absolute href + title so every page
|
|
8
|
-
* links it. `
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
8
|
+
* links it. `site` carries the origin + this page's own route (and its resolved
|
|
9
|
+
* breadcrumb trail) so the shell can state a canonical URL. `collections` is the
|
|
10
|
+
* build-time collection index a bare-name `@loop` resolves against; `vars` seeds
|
|
11
|
+
* the document scope (the pager `page` object on a paginated route). `reader` is
|
|
12
|
+
* the source reader (fs-backed by default via `src/compiler.js`; in-memory for
|
|
13
|
+
* `compileFromMemory`).
|
|
12
14
|
* @returns {CompiledPage}
|
|
13
15
|
*/
|
|
14
16
|
export function compilePage(file: string, context: Paths, options?: {
|
|
@@ -16,6 +18,7 @@ export function compilePage(file: string, context: Paths, options?: {
|
|
|
16
18
|
href: string;
|
|
17
19
|
title: string;
|
|
18
20
|
};
|
|
21
|
+
site?: SiteContext;
|
|
19
22
|
collections?: Map<string, import("./collections.js").CollectionRow[]>;
|
|
20
23
|
vars?: Record<string, unknown>;
|
|
21
24
|
reader?: import("./reader.js").Reader;
|
|
@@ -86,7 +89,7 @@ export function compileFile(file: string, context: Paths, stack: string[], scope
|
|
|
86
89
|
* @param {Record<string, string> | Map<string, string>} files Project-relative
|
|
87
90
|
* path → source content.
|
|
88
91
|
* @param {string} entryPath Project-relative path of the page to compile.
|
|
89
|
-
* @param {{ feed?: { href: string, title: string }, collections?: Map<string, import("./collections.js").CollectionRow[]>, vars?: Record<string, unknown>, cwd?: string }} [options]
|
|
92
|
+
* @param {{ feed?: { href: string, title: string }, site?: SiteContext, collections?: Map<string, import("./collections.js").CollectionRow[]>, vars?: Record<string, unknown>, cwd?: string }} [options]
|
|
90
93
|
* Same shell options as {@link compilePage}; `cwd` overrides the virtual
|
|
91
94
|
* project root the relative keys resolve against (defaults to `/`).
|
|
92
95
|
* @returns {CompiledPage}
|
|
@@ -96,10 +99,34 @@ export function compileFromMemory(files: Record<string, string> | Map<string, st
|
|
|
96
99
|
href: string;
|
|
97
100
|
title: string;
|
|
98
101
|
};
|
|
102
|
+
site?: SiteContext;
|
|
99
103
|
collections?: Map<string, import("./collections.js").CollectionRow[]>;
|
|
100
104
|
vars?: Record<string, unknown>;
|
|
101
105
|
cwd?: string;
|
|
102
106
|
}): CompiledPage;
|
|
107
|
+
/**
|
|
108
|
+
* Where this page sits on a real, deployed site: the origin the home page
|
|
109
|
+
* declared with `site_url`, the concrete route this page is written to, and the
|
|
110
|
+
* breadcrumb trail the builder resolved from the routes that actually exist.
|
|
111
|
+
* Supplied by `src/builder.js`, the only layer that knows all three.
|
|
112
|
+
*/
|
|
113
|
+
export type SiteContext = {
|
|
114
|
+
/**
|
|
115
|
+
* Absolute site origin, e.g. `https://example.com`.
|
|
116
|
+
*/
|
|
117
|
+
url: string;
|
|
118
|
+
/**
|
|
119
|
+
* Public route path for THIS page (trailing-slashed).
|
|
120
|
+
*/
|
|
121
|
+
route: string;
|
|
122
|
+
/**
|
|
123
|
+
* Resolved crumb trail.
|
|
124
|
+
*/
|
|
125
|
+
breadcrumbs?: {
|
|
126
|
+
name: string;
|
|
127
|
+
url: string;
|
|
128
|
+
}[] | undefined;
|
|
129
|
+
};
|
|
103
130
|
export type Paths = import("./context.js").Paths;
|
|
104
131
|
export type Meta = import("./context.js").Meta;
|
|
105
132
|
export type Scope = import("./context.js").Scope;
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Whether a page's frontmatter declares an article-family `schema:` type. Used
|
|
3
|
+
* by the page shell to pick `og:type` (`article` vs `website`).
|
|
4
|
+
* @param {Meta} meta
|
|
5
|
+
* @returns {boolean}
|
|
6
|
+
*/
|
|
7
|
+
export function isArticleSchema(meta: Meta): boolean;
|
|
8
|
+
/**
|
|
9
|
+
* Everything the JSON-LD builders may read. Only page-derived facts: the shell's
|
|
10
|
+
* already-resolved title/description/image/lang, the page's frontmatter, and the
|
|
11
|
+
* absolute URL the builder resolved for it.
|
|
12
|
+
* @typedef {object} SchemaInput
|
|
13
|
+
* @property {Meta} meta Page frontmatter.
|
|
14
|
+
* @property {string} file Source path, for compile errors.
|
|
15
|
+
* @property {string} title Resolved page title.
|
|
16
|
+
* @property {string} description Resolved page description ("" when absent).
|
|
17
|
+
* @property {string} image Absolute social image URL ("" when absent).
|
|
18
|
+
* @property {string} lang Document language.
|
|
19
|
+
* @property {string} canonical Absolute canonical URL ("" when no `site_url`).
|
|
20
|
+
* @property {{ name: string, url: string }[]} breadcrumbs Resolved crumb trail.
|
|
21
|
+
*/
|
|
22
|
+
/**
|
|
23
|
+
* Build the page's `<script type="application/ld+json">` block, or "" when the
|
|
24
|
+
* page opts into nothing. Two independent inputs feed it: the `schema:`
|
|
25
|
+
* frontmatter type (or types), and a breadcrumb trail the builder resolved for a
|
|
26
|
+
* nested route. Two or more nodes are emitted as a JSON array in one script,
|
|
27
|
+
* which is valid JSON-LD and one fewer tag in the head.
|
|
28
|
+
* @param {SchemaInput} input
|
|
29
|
+
* @returns {string} The script tag, or "" when there is nothing to say.
|
|
30
|
+
*/
|
|
31
|
+
export function buildJsonLd(input: SchemaInput): string;
|
|
32
|
+
/**
|
|
33
|
+
* @typedef {import("./context.js").Meta} Meta
|
|
34
|
+
*/
|
|
35
|
+
/**
|
|
36
|
+
* The `schema:` types Darkmown knows how to populate from real page data:
|
|
37
|
+
* article-family types from a page's title/description/date/author, and the two
|
|
38
|
+
* site-identity types from what a home page already declares. Anything else is
|
|
39
|
+
* a compile error rather than a guess. `BreadcrumbList` is not listed because it
|
|
40
|
+
* is never opted into: it is derived automatically from routes that exist.
|
|
41
|
+
* @type {string[]}
|
|
42
|
+
*/
|
|
43
|
+
export const SCHEMA_TYPES: string[];
|
|
44
|
+
/** A concrete, copyable `schema:` frontmatter line for docs and error hints. */
|
|
45
|
+
export const SCHEMA_EXAMPLE: "schema: BlogPosting";
|
|
46
|
+
/**
|
|
47
|
+
* Everything the JSON-LD builders may read. Only page-derived facts: the shell's
|
|
48
|
+
* already-resolved title/description/image/lang, the page's frontmatter, and the
|
|
49
|
+
* absolute URL the builder resolved for it.
|
|
50
|
+
*/
|
|
51
|
+
export type SchemaInput = {
|
|
52
|
+
/**
|
|
53
|
+
* Page frontmatter.
|
|
54
|
+
*/
|
|
55
|
+
meta: Meta;
|
|
56
|
+
/**
|
|
57
|
+
* Source path, for compile errors.
|
|
58
|
+
*/
|
|
59
|
+
file: string;
|
|
60
|
+
/**
|
|
61
|
+
* Resolved page title.
|
|
62
|
+
*/
|
|
63
|
+
title: string;
|
|
64
|
+
/**
|
|
65
|
+
* Resolved page description ("" when absent).
|
|
66
|
+
*/
|
|
67
|
+
description: string;
|
|
68
|
+
/**
|
|
69
|
+
* Absolute social image URL ("" when absent).
|
|
70
|
+
*/
|
|
71
|
+
image: string;
|
|
72
|
+
/**
|
|
73
|
+
* Document language.
|
|
74
|
+
*/
|
|
75
|
+
lang: string;
|
|
76
|
+
/**
|
|
77
|
+
* Absolute canonical URL ("" when no `site_url`).
|
|
78
|
+
*/
|
|
79
|
+
canonical: string;
|
|
80
|
+
/**
|
|
81
|
+
* Resolved crumb trail.
|
|
82
|
+
*/
|
|
83
|
+
breadcrumbs: {
|
|
84
|
+
name: string;
|
|
85
|
+
url: string;
|
|
86
|
+
}[];
|
|
87
|
+
};
|
|
88
|
+
export type Meta = import("./context.js").Meta;
|
package/types/compiler.d.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* a different source (see `compileFromMemory`).
|
|
5
5
|
* @param {string} file Absolute path to the source `.md`/`.wd` file.
|
|
6
6
|
* @param {import("./config.js").Paths} context Resolved project paths.
|
|
7
|
-
* @param {{ feed?: { href: string, title: string }, collections?: Map<string, import("./compiler/collections.js").CollectionRow[]>, vars?: Record<string, unknown>, reader?: import("./compiler/reader.js").Reader }} [options]
|
|
7
|
+
* @param {{ feed?: { href: string, title: string }, site?: import("./compiler/page.js").SiteContext, collections?: Map<string, import("./compiler/collections.js").CollectionRow[]>, vars?: Record<string, unknown>, reader?: import("./compiler/reader.js").Reader }} [options]
|
|
8
8
|
* @returns {CompiledPage}
|
|
9
9
|
*/
|
|
10
10
|
export function compilePage(file: string, context: import("./config.js").Paths, options?: {
|
|
@@ -12,6 +12,7 @@ export function compilePage(file: string, context: import("./config.js").Paths,
|
|
|
12
12
|
href: string;
|
|
13
13
|
title: string;
|
|
14
14
|
};
|
|
15
|
+
site?: import("./compiler/page.js").SiteContext;
|
|
15
16
|
collections?: Map<string, import("./compiler/collections.js").CollectionRow[]>;
|
|
16
17
|
vars?: Record<string, unknown>;
|
|
17
18
|
reader?: import("./compiler/reader.js").Reader;
|
package/types/feeds.d.ts
CHANGED
|
@@ -109,13 +109,56 @@ export function buildRss(channel: {
|
|
|
109
109
|
feedUrl: string;
|
|
110
110
|
}, items: RssItem[]): string;
|
|
111
111
|
/**
|
|
112
|
-
* Build `robots.txt`.
|
|
113
|
-
* built, so there's nothing to `Disallow`)
|
|
114
|
-
*
|
|
112
|
+
* Build `robots.txt`. The wildcard group always allows everything (hidden/draft
|
|
113
|
+
* routes are never built, so there's nothing to `Disallow`), then every
|
|
114
|
+
* {@link AI_CRAWLERS} token is named explicitly with the site's declared policy.
|
|
115
|
+
* The `Sitemap:` line is included ONLY when `siteUrl` is set: a sitemap URL is
|
|
116
|
+
* only meaningful with an origin.
|
|
115
117
|
* @param {string} [siteUrl] Absolute origin, or "" / undefined to omit the line.
|
|
118
|
+
* @param {string} [policy] `"allow"` (default) or `"deny"`, from the home page's
|
|
119
|
+
* `ai_crawlers:` frontmatter. Validated by the caller ({@link aiCrawlerPolicy}).
|
|
116
120
|
* @returns {string} The robots.txt body (trailing newline).
|
|
117
121
|
*/
|
|
118
|
-
export function buildRobots(siteUrl?: string): string;
|
|
122
|
+
export function buildRobots(siteUrl?: string, policy?: string): string;
|
|
123
|
+
/**
|
|
124
|
+
* Validate the home page's `ai_crawlers:` frontmatter into a policy string.
|
|
125
|
+
* Absent means `allow` (the status quo the wildcard group already grants). A
|
|
126
|
+
* value that is neither `allow` nor `deny` throws rather than defaulting: the
|
|
127
|
+
* likely intent behind a typo like `ai_crawlers: block` is to opt OUT, and
|
|
128
|
+
* silently allowing instead would be exactly the wrong failure direction.
|
|
129
|
+
* @param {import("./compiler.js").Meta} meta Home page frontmatter.
|
|
130
|
+
* @param {string} [file] Home page source path, for the error message.
|
|
131
|
+
* @returns {string} One of {@link AI_CRAWLER_POLICIES}.
|
|
132
|
+
*/
|
|
133
|
+
export function aiCrawlerPolicy(meta: import("./compiler.js").Meta, file?: string): string;
|
|
134
|
+
/**
|
|
135
|
+
* The AI crawler and answer-engine robots.txt tokens Darkmown names explicitly.
|
|
136
|
+
*
|
|
137
|
+
* EVERY token here is copied from its own operator's published documentation
|
|
138
|
+
* (checked 2026-08-09): none are guessed, inferred from a log, or carried over
|
|
139
|
+
* from a third-party list. A wrong token is worse than a missing one: it looks
|
|
140
|
+
* like a policy the site is stating and is silently ignored by everybody.
|
|
141
|
+
*
|
|
142
|
+
* `Allow: /` for these is not a permission change (the `User-agent: *` group
|
|
143
|
+
* already allows them); it is an explicit, machine-readable statement of intent,
|
|
144
|
+
* which is what a publisher wants when the goal is to BE cited by answer engines.
|
|
145
|
+
* The same list is what `ai_crawlers: deny` turns into `Disallow: /`, which is
|
|
146
|
+
* the only form that actually changes anything.
|
|
147
|
+
*
|
|
148
|
+
* The `note` is written into the generated file next to the group. Search
|
|
149
|
+
* crawling, model training, and live user-triggered fetches are DIFFERENT
|
|
150
|
+
* permissions from the same operator, and a future reader editing robots.txt by
|
|
151
|
+
* hand needs to see that before collapsing a group.
|
|
152
|
+
* @type {{ operator: string, docs: string, note: string, agents: string[] }[]}
|
|
153
|
+
*/
|
|
154
|
+
export const AI_CRAWLERS: {
|
|
155
|
+
operator: string;
|
|
156
|
+
docs: string;
|
|
157
|
+
note: string;
|
|
158
|
+
agents: string[];
|
|
159
|
+
}[];
|
|
160
|
+
/** The accepted `ai_crawlers:` frontmatter values. */
|
|
161
|
+
export const AI_CRAWLER_POLICIES: string[];
|
|
119
162
|
/** Most-recent-first RSS post cap — a feed is a recent window, not an archive. */
|
|
120
163
|
export const RSS_ITEM_LIMIT: 20;
|
|
121
164
|
export type Route = import("./router.js").Route;
|
package/types/index.d.ts
CHANGED
|
@@ -5,6 +5,9 @@
|
|
|
5
5
|
export { buildSite } from "./builder.js";
|
|
6
6
|
export type { RouteManifestEntry, Paths, Assets } from "./builder.js";
|
|
7
7
|
|
|
8
|
+
export { directiveCatalog, llmsText } from "./catalog.js";
|
|
9
|
+
export type { DirectiveCatalog, DirectiveEntry, CatalogEntry, Reactivity } from "./catalog.js";
|
|
10
|
+
|
|
8
11
|
export {
|
|
9
12
|
compilePage,
|
|
10
13
|
compileDocument,
|
package/types/scaffold.d.ts
CHANGED
|
@@ -7,7 +7,9 @@ export function availableTemplates(): string[];
|
|
|
7
7
|
* Scaffold a new Darkmown project into `root` from a template, creating files
|
|
8
8
|
* that don't already exist (never overwriting). `package.json` is generated (so
|
|
9
9
|
* the name tracks the directory and the `@zvndev/darkmown` dep pins the installed
|
|
10
|
-
* version);
|
|
10
|
+
* version); `AGENTS.md` is copied from the package so coding agents find the
|
|
11
|
+
* directive reference at the project root; `CLAUDE.md` and `.gitignore` are
|
|
12
|
+
* generated; every other file is copied verbatim from the chosen template.
|
|
11
13
|
* @param {string} root Absolute path to the target project directory.
|
|
12
14
|
* @param {{ template?: string }} [options] `template` defaults to `"starter"`.
|
|
13
15
|
* @returns {{ root: string, template: string }}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolve one edit's target to a 1-based inclusive line range in `source`.
|
|
3
|
+
*
|
|
4
|
+
* Every rejection names what was wrong AND what would have worked, because a
|
|
5
|
+
* rejection a model cannot act on is just a failed attempt.
|
|
6
|
+
*
|
|
7
|
+
* @param {string} source
|
|
8
|
+
* @param {Edit} edit
|
|
9
|
+
* @param {import("../compiler/context.js").Symbol[]} symbols
|
|
10
|
+
* @returns {{ok: true, from: number, to: number} | {ok: false, error: string}}
|
|
11
|
+
*/
|
|
12
|
+
export function resolveTarget(source: string, edit: Edit, symbols: import("../compiler/context.js").Symbol[]): {
|
|
13
|
+
ok: true;
|
|
14
|
+
from: number;
|
|
15
|
+
to: number;
|
|
16
|
+
} | {
|
|
17
|
+
ok: false;
|
|
18
|
+
error: string;
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* TOOL ENTRY POINT.
|
|
22
|
+
*
|
|
23
|
+
* @param {Record<string, string>} files
|
|
24
|
+
* @param {string} entry File to edit, in the same key form as `files`.
|
|
25
|
+
* @param {Edit[]} [edits] Missing or empty is reported, not thrown.
|
|
26
|
+
* @returns {{ok: boolean, text: string, data: object}}
|
|
27
|
+
*/
|
|
28
|
+
export function apply(files: Record<string, string>, entry: string, edits?: Edit[]): {
|
|
29
|
+
ok: boolean;
|
|
30
|
+
text: string;
|
|
31
|
+
data: object;
|
|
32
|
+
};
|
|
33
|
+
/** The operations a caller may ask for. Closed, and validated per edit. */
|
|
34
|
+
export const OPS: Set<string>;
|
|
35
|
+
/**
|
|
36
|
+
* One edit, as a caller (or a model) writes it.
|
|
37
|
+
*
|
|
38
|
+
* Every field is optional because a malformed edit is REPORTED, not thrown: the
|
|
39
|
+
* whole point of this surface is that a model gets a sentence back telling it
|
|
40
|
+
* what to fix. Typing them as required would describe a contract this module
|
|
41
|
+
* deliberately does not enforce at the boundary.
|
|
42
|
+
*/
|
|
43
|
+
export type Edit = {
|
|
44
|
+
/**
|
|
45
|
+
* One of {@link OPS}.
|
|
46
|
+
*/
|
|
47
|
+
op?: string | undefined;
|
|
48
|
+
/**
|
|
49
|
+
* 1-based line, as printed by `outline`.
|
|
50
|
+
*/
|
|
51
|
+
line?: number | undefined;
|
|
52
|
+
/**
|
|
53
|
+
* A symbol target, e.g. `state:cart`.
|
|
54
|
+
*/
|
|
55
|
+
symbol?: string | undefined;
|
|
56
|
+
/**
|
|
57
|
+
* Exact source text to find.
|
|
58
|
+
*/
|
|
59
|
+
anchor?: string | undefined;
|
|
60
|
+
/**
|
|
61
|
+
* Replacement or inserted text.
|
|
62
|
+
*/
|
|
63
|
+
text?: string | undefined;
|
|
64
|
+
/**
|
|
65
|
+
* What the caller believed is at `line`, for staleness.
|
|
66
|
+
*/
|
|
67
|
+
expect?: string | undefined;
|
|
68
|
+
};
|