@zvndev/darkmown 2.2.0 → 2.3.1-next.1.g3a4a8dc

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.
Files changed (105) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/README.md +34 -2
  3. package/docs/spec-alignment.md +10 -0
  4. package/package.json +16 -11
  5. package/site/pages/docs/index.wd +14 -0
  6. package/site/pages/playground/index.js +47 -46
  7. package/src/catalog.js +16 -1
  8. package/src/compiler/actions.js +42 -22
  9. package/src/compiler/body.js +36 -14
  10. package/src/compiler/collections.js +16 -10
  11. package/src/compiler/context.js +39 -10
  12. package/src/compiler/fetch.js +39 -15
  13. package/src/compiler/format.js +5 -2
  14. package/src/compiler/forms.js +91 -26
  15. package/src/compiler/frontmatter.js +11 -2
  16. package/src/compiler/fs-reader.js +2 -1
  17. package/src/compiler/image-size.js +340 -0
  18. package/src/compiler/includes.js +8 -4
  19. package/src/compiler/interpolation.js +15 -7
  20. package/src/compiler/loops.js +63 -35
  21. package/src/compiler/markdown.js +4 -3
  22. package/src/compiler/media.js +17 -3
  23. package/src/compiler/page.js +8 -6
  24. package/src/compiler/predicates.js +49 -24
  25. package/src/compiler/reader.js +12 -2
  26. package/src/compiler/state.js +16 -2
  27. package/src/compiler/structure.js +24 -14
  28. package/src/deploy.js +8 -4
  29. package/src/errors.js +1156 -0
  30. package/src/router.js +7 -4
  31. package/src/scaffold.js +4 -2
  32. package/src/skin.js +17 -6
  33. package/types/api-runner.d.ts +94 -0
  34. package/types/builder.d.ts +141 -10
  35. package/types/catalog.d.ts +97 -0
  36. package/types/compiler/actions.d.ts +41 -0
  37. package/types/compiler/body.d.ts +11 -0
  38. package/types/compiler/collections.d.ts +76 -0
  39. package/types/compiler/context.d.ts +647 -0
  40. package/types/compiler/directives.d.ts +6 -0
  41. package/types/compiler/expr-ast.d.ts +20 -0
  42. package/types/compiler/fetch.d.ts +29 -0
  43. package/types/compiler/format.d.ts +49 -0
  44. package/types/compiler/forms.d.ts +94 -0
  45. package/types/compiler/frontmatter.d.ts +33 -0
  46. package/types/compiler/fs-reader.d.ts +7 -0
  47. package/types/compiler/image-size.d.ts +14 -0
  48. package/types/compiler/includes.d.ts +100 -0
  49. package/types/compiler/index.d.ts +5 -0
  50. package/types/compiler/interpolation.d.ts +115 -0
  51. package/types/compiler/loops.d.ts +36 -0
  52. package/types/compiler/markdown.d.ts +34 -0
  53. package/types/compiler/media.d.ts +32 -0
  54. package/types/compiler/page.d.ts +109 -0
  55. package/types/compiler/predicates.d.ts +50 -0
  56. package/types/compiler/reader.d.ts +48 -0
  57. package/types/compiler/state.d.ts +68 -0
  58. package/types/compiler/structure.d.ts +56 -0
  59. package/types/compiler.d.ts +35 -223
  60. package/types/dev.d.ts +47 -2
  61. package/types/errors.d.ts +100 -0
  62. package/types/feeds.d.ts +150 -0
  63. package/types/headers.d.ts +44 -0
  64. package/types/highlight.d.ts +24 -0
  65. package/types/router.d.ts +42 -3
  66. package/types/scaffold.d.ts +15 -3
  67. package/types/skin.d.ts +28 -1
  68. package/types/src/api-runner.d.ts +94 -0
  69. package/types/src/builder.d.ts +170 -0
  70. package/types/src/catalog.d.ts +97 -0
  71. package/types/src/compiler/actions.d.ts +41 -0
  72. package/types/src/compiler/body.d.ts +11 -0
  73. package/types/src/compiler/collections.d.ts +76 -0
  74. package/types/src/compiler/context.d.ts +647 -0
  75. package/types/src/compiler/directives.d.ts +6 -0
  76. package/types/src/compiler/expr-ast.d.ts +20 -0
  77. package/types/src/compiler/fetch.d.ts +29 -0
  78. package/types/src/compiler/format.d.ts +49 -0
  79. package/types/src/compiler/forms.d.ts +94 -0
  80. package/types/src/compiler/frontmatter.d.ts +33 -0
  81. package/types/src/compiler/fs-reader.d.ts +7 -0
  82. package/types/src/compiler/image-size.d.ts +14 -0
  83. package/types/src/compiler/includes.d.ts +100 -0
  84. package/types/src/compiler/index.d.ts +5 -0
  85. package/types/src/compiler/interpolation.d.ts +115 -0
  86. package/types/src/compiler/loops.d.ts +36 -0
  87. package/types/src/compiler/markdown.d.ts +34 -0
  88. package/types/src/compiler/media.d.ts +32 -0
  89. package/types/src/compiler/page.d.ts +109 -0
  90. package/types/src/compiler/predicates.d.ts +50 -0
  91. package/types/src/compiler/reader.d.ts +48 -0
  92. package/types/src/compiler/state.d.ts +68 -0
  93. package/types/src/compiler/structure.d.ts +56 -0
  94. package/types/src/compiler.d.ts +48 -0
  95. package/types/src/config.d.ts +45 -0
  96. package/types/src/dev.d.ts +58 -0
  97. package/types/src/errors.d.ts +100 -0
  98. package/types/src/feeds.d.ts +150 -0
  99. package/types/src/headers.d.ts +44 -0
  100. package/types/src/highlight.d.ts +24 -0
  101. package/types/src/router.d.ts +78 -0
  102. package/types/src/scaffold.d.ts +20 -0
  103. package/types/src/skin.d.ts +48 -0
  104. package/types/src/statics.d.ts +24 -0
  105. package/types/statics.d.ts +2 -1
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Parse a keyword-arg `:fetch` directive into a lifecycle-aware marker.
3
+ * Auto-declares four state keys (value/error/loading/empty), seeds them, and
4
+ * emits `data-wd-fetch-*` attributes (url/method/when/timeout/retry/headers/
5
+ * body/deps) consumed by the runtime's `startFetch`.
6
+ * @param {string} line
7
+ * @param {Ctx} ctx
8
+ * @param {number} index 0-based line index for `file:line` errors.
9
+ * @returns {string}
10
+ */
11
+ export function handleFetch(line: string, ctx: Ctx, index: number): string;
12
+ /**
13
+ * Validate a `:fetch` (or `refresh=`) URL's scheme at compile time. Mirrors the
14
+ * `:try` href guard: relative paths (`/`, `./`, `../`, bare), an `http(s)://`
15
+ * URL, or a leading `{ state }` interpolation are allowed; a protocol-relative
16
+ * `//host` or any non-http(s) scheme (`file:`, `data:`, `javascript:`, …) is
17
+ * rejected. Interpolated values are percent-encoded by the runtime, so a scheme
18
+ * cannot be injected through state at request time.
19
+ * @param {string} url
20
+ * @param {Ctx} ctx
21
+ * @param {string} [what] Option name for the error message.
22
+ * @returns {string}
23
+ */
24
+ export function validateFetchUrl(url: string, ctx: Ctx, what?: string): string;
25
+ /**
26
+ * @typedef {import("./context.js").Ctx} Ctx
27
+ */
28
+ export const FETCH_EXAMPLE: ":fetch todos from \"/api/todos.json\" when=visible";
29
+ export type Ctx = import("./context.js").Ctx;
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Parse the inside of a `{ … }` binding into its path and an ordered list of
3
+ * pipe stages. `{ price | money }` → `{ path: "price", stages: [{name:"money",args:[]}] }`.
4
+ * Stage validation (unknown formatter) happens in {@link validatePipes}.
5
+ * @param {string} inner
6
+ * @returns {{ path: string, stages: Stage[] }}
7
+ */
8
+ export function parsePipes(inner: string): {
9
+ path: string;
10
+ stages: Stage[];
11
+ };
12
+ /**
13
+ * Compile-time validation: every stage must name a known formatter. Throws a
14
+ * corrective compile error otherwise. Returns the parsed `{ path, stages }`.
15
+ * @param {string} inner
16
+ * @param {Ctx} ctx
17
+ * @returns {{ path: string, stages: Stage[] }}
18
+ */
19
+ export function validatePipes(inner: string, ctx: Ctx): {
20
+ path: string;
21
+ stages: Stage[];
22
+ };
23
+ /**
24
+ * Run a resolved value through a pipe chain at build time. Returns the final
25
+ * value (a string or number); the caller stringifies for output. Mirrors the
26
+ * runtime's `applyFmt`.
27
+ * @param {any} value
28
+ * @param {Stage[]} stages
29
+ * @returns {any}
30
+ */
31
+ export function applyPipeline(value: any, stages: Stage[]): any;
32
+ /** @type {Record<string, (list: any[], field?: string) => number>} */
33
+ export const AGGREGATES: Record<string, (list: any[], field?: string) => number>;
34
+ /**
35
+ * The whitelist. Each formatter is a pure function of (value, args) — no clock,
36
+ * no DOM, no globals beyond `Intl` — so a static value folds deterministically at
37
+ * build time and a reactive value formats identically in the browser.
38
+ * @type {Record<string, (value: any, args: any[]) => string | number>}
39
+ */
40
+ export const FORMATTERS: Record<string, (value: any, args: any[]) => string | number>;
41
+ /** Formatter names, for error messages and the runtime mirror check. */
42
+ export const FORMATTER_NAMES: string[];
43
+ export function fmtAttr(stages: Stage[]): string;
44
+ export function stagesFromAttr(json: string): Stage[];
45
+ export type Ctx = import("./context.js").Ctx;
46
+ export type Stage = {
47
+ name: string;
48
+ args: Array<string | number | boolean | null>;
49
+ };
@@ -0,0 +1,94 @@
1
+ /**
2
+ * @param {string} line
3
+ * @param {string[]} bodyLines
4
+ * @param {Ctx} ctx
5
+ * @param {number} index 0-based line index for `file:line` errors.
6
+ * @returns {string}
7
+ */
8
+ export function handleForm(line: string, bodyLines: string[], ctx: Ctx, index: number): string;
9
+ /**
10
+ * @param {string} line
11
+ * @param {Ctx} ctx
12
+ * @param {number} index 0-based line index for `file:line` errors.
13
+ * @returns {string}
14
+ */
15
+ export function handleInput(line: string, ctx: Ctx, index: number): string;
16
+ /**
17
+ * @param {string} line
18
+ * @param {Ctx} ctx
19
+ * @param {number} index 0-based line index for `file:line` errors.
20
+ * @returns {string}
21
+ */
22
+ export function handleBind(line: string, ctx: Ctx, index: number): string;
23
+ /**
24
+ * `:slider name [= value] [min=N] [max=N] [step=N] [aria-label="…"] [persist]` — a
25
+ * range input two-way bound to a NUMBER :state. With `= value` it declares the state
26
+ * inline (seeding it numeric) and may `persist`; without `=` it binds to an already-
27
+ * declared :state. Pure compile-time: it reuses the runtime's input binding (range
28
+ * values coerce to Number), so it ships NO behavior module.
29
+ * @param {string} line
30
+ * @param {Ctx} ctx
31
+ * @param {number} index 0-based line index for `file:line` errors.
32
+ * @returns {string}
33
+ */
34
+ export function handleSlider(line: string, ctx: Ctx, index: number): string;
35
+ /**
36
+ * @param {string} line
37
+ * @param {Ctx} ctx
38
+ * @param {number} index 0-based line index for `file:line` errors.
39
+ * @returns {string}
40
+ */
41
+ export function handleSubmit(line: string, ctx: Ctx, index: number): string;
42
+ /**
43
+ * `:textarea name [placeholder="…"] [rows=N] [required]` → a `<textarea>`. Like
44
+ * `:input`, it derives a non-visual aria-label from the placeholder (else the
45
+ * humanized name) when the author supplies none. Captured by the runtime's
46
+ * FormData exactly like `:input` — no runtime change needed.
47
+ * @param {string} line
48
+ * @param {Ctx} ctx
49
+ * @param {number} index 0-based line index for `file:line` errors.
50
+ * @returns {string}
51
+ */
52
+ export function handleTextarea(line: string, ctx: Ctx, index: number): string;
53
+ /**
54
+ * `:select name [required]` followed by `- Label` list lines → a `<select>` with
55
+ * one `<option>` per label (value === label). Derives an aria-label from the
56
+ * humanized name when none is given. Captured by FormData like the other fields.
57
+ * @param {string} line
58
+ * @param {string[]} optionLines The following `- Label` lines consumed by dispatch.
59
+ * @param {Ctx} ctx
60
+ * @param {number} index 0-based line index for `file:line` errors.
61
+ * @returns {string}
62
+ */
63
+ export function handleSelect(line: string, optionLines: string[], ctx: Ctx, index: number): string;
64
+ /**
65
+ * `:checkbox name [required]` / `:radio name [required]` followed by `- Label`
66
+ * lines → a labelled group of `<input type=checkbox|radio>`, each sharing `name`
67
+ * with value === label and wrapped in its own `<label>`. The group is a
68
+ * `role="group"` / `role="radiogroup"` container with an aria-label (explicit or
69
+ * humanized from the name). Checkbox groups are marked `data-wd-multi="name"` so
70
+ * the runtime captures every checked value as an array (`FormData.getAll`); radio
71
+ * groups capture a single value like `:input`. Flags: `required` (radio → the
72
+ * group; not emitted on checkboxes, where "at least one" has no native HTML form),
73
+ * `disabled` (whole group), `autofocus` (first control).
74
+ * @param {string} line
75
+ * @param {string[]} optionLines The following `- Label` lines consumed by dispatch.
76
+ * @param {Ctx} ctx
77
+ * @param {"checkbox" | "radio"} kind
78
+ * @param {number} index 0-based line index for `file:line` errors.
79
+ * @returns {string}
80
+ */
81
+ export function handleChoiceGroup(line: string, optionLines: string[], ctx: Ctx, kind: "checkbox" | "radio", index: number): string;
82
+ /**
83
+ * @typedef {import("./context.js").Ctx} Ctx
84
+ */
85
+ export const FORM_EXAMPLE: ":form into contact";
86
+ export const INPUT_EXAMPLE: ":input email type=email required";
87
+ export const TEXTAREA_EXAMPLE: ":textarea message placeholder=\"Your message\" rows=4";
88
+ export const SELECT_EXAMPLE: ":select topic";
89
+ export const CHECKBOX_EXAMPLE: ":checkbox toppings";
90
+ export const RADIO_EXAMPLE: ":radio size";
91
+ export const SUBMIT_EXAMPLE: ":submit \"Send\"";
92
+ export const BIND_EXAMPLE: ":bind query placeholder=\"Search\"";
93
+ export const SLIDER_EXAMPLE: ":slider volume = 50 min=0 max=100 step=1";
94
+ export type Ctx = import("./context.js").Ctx;
@@ -0,0 +1,33 @@
1
+ /**
2
+ * @typedef {import("./context.js").Meta} Meta
3
+ * @typedef {import("./context.js").FrontmatterValue} FrontmatterValue
4
+ * @typedef {import("./context.js").Compilation} Compilation
5
+ */
6
+ /**
7
+ * Split a raw file into its frontmatter `meta` and `body`. `bodyLine` is the
8
+ * 0-based file line index the body starts on (the number of lines the
9
+ * frontmatter block consumed) so compile errors can report true file lines.
10
+ * @param {string} raw Full file contents.
11
+ * @param {string} [file] Source path, used only for error messages.
12
+ * @returns {{ meta: Meta, body: string, bodyLine: number }}
13
+ */
14
+ export function parseFrontmatter(raw: string, file?: string): {
15
+ meta: Meta;
16
+ body: string;
17
+ bodyLine: number;
18
+ };
19
+ /**
20
+ * Warn (non-fatal) when a file looks like it MEANT to open frontmatter but forgot
21
+ * the leading `---`: the first content line is a `key: value` pair and a bare
22
+ * `---` fence appears within the opening lines. Conservative — a normal markdown
23
+ * body whose first line happens to contain a colon (with no early `---`) is left
24
+ * alone, since `---` is also a valid horizontal rule.
25
+ * @param {string} raw
26
+ * @param {string} file
27
+ * @param {Compilation} comp
28
+ * @returns {void}
29
+ */
30
+ export function warnLikelyFrontmatter(raw: string, file: string, comp: Compilation): void;
31
+ export type Meta = import("./context.js").Meta;
32
+ export type FrontmatterValue = import("./context.js").FrontmatterValue;
33
+ export type Compilation = import("./context.js").Compilation;
@@ -0,0 +1,7 @@
1
+ /**
2
+ * A {@link import("./reader.js").Reader} backed by `node:fs`. Each method is a
3
+ * thin pass-through to the synchronous fs call the compiler used before the
4
+ * reader abstraction, so an fs-backed compile stays byte-identical.
5
+ * @returns {import("./reader.js").Reader}
6
+ */
7
+ export function fsReader(): import("./reader.js").Reader;
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Read an image's intrinsic dimensions from its header bytes.
3
+ * @param {Uint8Array} bytes Raw file contents (a Node `Buffer` is a `Uint8Array`).
4
+ * @returns {Dimensions | null} The size, or null when the bytes are empty, of an
5
+ * unsupported format, or malformed. Never throws.
6
+ */
7
+ export function imageSize(bytes: Uint8Array): Dimensions | null;
8
+ /**
9
+ * Intrinsic pixel size.
10
+ */
11
+ export type Dimensions = {
12
+ width: number;
13
+ height: number;
14
+ };
@@ -0,0 +1,100 @@
1
+ /**
2
+ * @typedef {import("./context.js").Paths} Paths
3
+ * @typedef {import("./context.js").Assets} Assets
4
+ * @typedef {import("./context.js").Compilation} Compilation
5
+ */
6
+ /**
7
+ * Read a `.skin` file's first meaningful line (skipping `//` line comments,
8
+ * `/* … *\/` block comments, blanks, and decorative dividers — the same skip
9
+ * rules `compileSkin` applies) to decide whether it opts into scoping. A skin
10
+ * scopes its selectors ONLY when that first line is exactly `scoped`; otherwise
11
+ * it is global and untouched. This is the single source for "is this skin
12
+ * scoped + what is its id", so the CSS rewrite (builder) and the HTML stamp
13
+ * (page/include) always agree on both.
14
+ * @param {string} skinPath Absolute path to the colocated `.skin` file.
15
+ * @param {string} cwd Project root, for the path-based scope id.
16
+ * @param {import("./reader.js").Reader} reader Source reader for the skin file.
17
+ * @returns {{ scoped: boolean, scopeId: string }}
18
+ */
19
+ export function scopeInfoForSkin(skinPath: string, cwd: string, reader: import("./reader.js").Reader): {
20
+ scoped: boolean;
21
+ scopeId: string;
22
+ };
23
+ /**
24
+ * The scope info for a page/include's OWN colocated `.skin` sibling, or null
25
+ * when there is no sibling skin. Used by the HTML stamp sites: a route's own
26
+ * scoped skin → stamp the page body; an include's scoped skin → stamp the
27
+ * returned subtree. Resolution mirrors `collectColocatedAssets` (same stem,
28
+ * `.skin` extension).
29
+ * @param {string} file Absolute path to the page/include source file.
30
+ * @param {Paths} context
31
+ * @param {import("./reader.js").Reader} reader Source reader for the skin file.
32
+ * @returns {{ scoped: boolean, scopeId: string } | null}
33
+ */
34
+ export function scopedSkinFor(file: string, context: Paths, reader: import("./reader.js").Reader): {
35
+ scoped: boolean;
36
+ scopeId: string;
37
+ } | null;
38
+ /**
39
+ * Stamp `data-wd-scope="<id>"` onto every opening tag of a subtree — the HTML
40
+ * half of compile-time scoped styles, mirroring {@link enhanceImages}: a
41
+ * compile-time post-pass over assembled HTML, zero runtime JS. Every element
42
+ * start tag that doesn't already carry a scope attribute gets one, so the scoped
43
+ * stylesheet (whose selectors end in `[data-wd-scope="<id>"]`) matches inside
44
+ * this subtree and nowhere else. Void/self-closing tags are stamped too (the
45
+ * attribute is valid on any element). Closing tags, comments, doctype, and the
46
+ * `<!`/`<?` families are left alone. A tag that ALREADY has a `data-wd-scope`
47
+ * (e.g. a nested include with its own scope) keeps its own — first stamp wins.
48
+ *
49
+ * Raw-text / escapable-raw-text elements (`script`, `style`, `textarea`,
50
+ * `title`) are special-cased: their OPENING tag is stamped (valid), but their
51
+ * BODY is left byte-intact — `<`/`>` inside a `<script>` are JavaScript, not
52
+ * markup, so stamping there would corrupt the content. A small tokenizer walks
53
+ * tag-by-tag (quote-aware in attribute lists) and, on entering a raw-text
54
+ * element, copies through verbatim until the matching close tag.
55
+ * @param {string} html Subtree HTML to scope.
56
+ * @param {string} scopeId Scope id (from {@link import("../skin.js").scopeIdFor}).
57
+ * @returns {string}
58
+ */
59
+ export function stampScope(html: string, scopeId: string): string;
60
+ /**
61
+ * Register a page's colocated `.skin`/`.js` siblings as emitted assets.
62
+ * @param {string} file
63
+ * @param {Paths} context
64
+ * @param {Assets} assets
65
+ * @param {import("./reader.js").Reader} reader Source reader for sibling probing.
66
+ * @returns {void}
67
+ */
68
+ export function collectColocatedAssets(file: string, context: Paths, assets: Assets, reader: import("./reader.js").Reader): void;
69
+ /**
70
+ * Resolve an include spec to an absolute path inside `site/pages` or `site/_`.
71
+ * @param {string} spec Include target (may be quoted).
72
+ * @param {string} fromFile File requesting the include.
73
+ * @param {Paths} context
74
+ * @param {boolean} allowAny Allow non-page extensions (e.g. JSON for `@loop`).
75
+ * @param {string} loc Source location (`file:line`) for the error messages, so
76
+ * an unresolved/out-of-sandbox include reports the directive's line. Callers
77
+ * with a line index pass `at(ctx, index)`; pass `fromFile` for the file-only
78
+ * message.
79
+ * @param {import("./reader.js").Reader} reader Source reader for existence
80
+ * probing, threaded from `ctx.comp.reader`.
81
+ * @returns {string}
82
+ */
83
+ export function resolveInclude(spec: string, fromFile: string, context: Paths, allowAny: boolean, loc: string, reader: import("./reader.js").Reader): string;
84
+ /**
85
+ * @param {string} file
86
+ * @param {Paths} context
87
+ * @returns {boolean}
88
+ */
89
+ export function isAllowedInclude(file: string, context: Paths): boolean;
90
+ /**
91
+ * Warn when a plain `.md` file contains `.wd`-only syntax that stays inert.
92
+ * @param {string} body
93
+ * @param {string} file
94
+ * @param {Compilation} comp
95
+ * @returns {void}
96
+ */
97
+ export function scanMarkdownHints(body: string, file: string, comp: Compilation): void;
98
+ export type Paths = import("./context.js").Paths;
99
+ export type Assets = import("./context.js").Assets;
100
+ export type Compilation = import("./context.js").Compilation;
@@ -0,0 +1,5 @@
1
+ export { parseFrontmatter } from "./frontmatter.js";
2
+ export { stampScope } from "./includes.js";
3
+ export { escapeHtml } from "./interpolation.js";
4
+ export { loopKeyOf } from "./loops.js";
5
+ export { compileDocument, compileFromMemory, compilePage, enhanceImages } from "./page.js";
@@ -0,0 +1,115 @@
1
+ /**
2
+ * @typedef {import("./context.js").Scope} Scope
3
+ * @typedef {import("./context.js").Ctx} Ctx
4
+ */
5
+ /**
6
+ * Walk the scope chain for a top-level variable.
7
+ * @param {Scope} scope
8
+ * @param {string} name
9
+ * @returns {{ found: true, value: unknown } | { found: false }}
10
+ */
11
+ export function lookupVar(scope: Scope, name: string): {
12
+ found: true;
13
+ value: unknown;
14
+ } | {
15
+ found: false;
16
+ };
17
+ /**
18
+ * Resolve a dotted path against the static scope chain.
19
+ * @param {string} expr
20
+ * @param {Ctx} ctx
21
+ * @returns {{ found: true, value: unknown } | { found: false }}
22
+ */
23
+ export function lookupPath(expr: string, ctx: Ctx): {
24
+ found: true;
25
+ value: unknown;
26
+ } | {
27
+ found: false;
28
+ };
29
+ /**
30
+ * Safely read a dotted path off a value, rejecting prototype-pollution segments.
31
+ * @param {unknown} value
32
+ * @param {string[]} segments
33
+ * @returns {unknown}
34
+ */
35
+ export function getPath(value: unknown, segments: string[]): unknown;
36
+ /**
37
+ * Validate a dotted path shape, rejecting prototype-pollution segments at every
38
+ * level. Shared by actions/loops/fetch. Throws with a corrective `Use:` hint.
39
+ * @param {string} path Dotted path, e.g. `cart.items`.
40
+ * @param {Ctx} ctx
41
+ * @param {string} use Corrective suggestion for the error message.
42
+ * @returns {string[]} The validated segments.
43
+ */
44
+ export function validatePath(path: string, ctx: Ctx, use: string): string[];
45
+ /**
46
+ * Resolve a bare state name to its fully-qualified key, walking section scopes.
47
+ * @param {string} name
48
+ * @param {Ctx} ctx
49
+ * @returns {string | null}
50
+ */
51
+ export function resolveStateKey(name: string, ctx: Ctx): string | null;
52
+ /**
53
+ * Render an interpolated value as text. Arrays join with ", " (matching the
54
+ * documented `{ meta.tags }` behavior); a bare object is a mistake — fail
55
+ * loudly with a fix rather than emitting "[object Object]".
56
+ * @param {unknown} value
57
+ * @param {string} expr
58
+ * @param {Ctx} ctx
59
+ * @returns {string}
60
+ */
61
+ export function interpolateLeaf(value: unknown, expr: string, ctx: Ctx): string;
62
+ /**
63
+ * JSON-encode a value for an inline `<script>`, escaping `<` to stay HTML-safe.
64
+ * @param {unknown} value
65
+ * @returns {string}
66
+ */
67
+ export function safeScriptJson(value: unknown): string;
68
+ /**
69
+ * Parse a `:state`/`:store`/`:theme` value: quoted string, boolean, null, number,
70
+ * or JSON literal — falling back to the bare string. Multi-line array/object
71
+ * literals are joined into one line before reaching here (see `joinValueDirective`
72
+ * in body.js), so a value that opens a `[`/`{` but won't parse is genuinely
73
+ * unbalanced (a missing `]`/`}`) and throws, rather than being stored verbatim as
74
+ * the partial string `"["` — the silent-corruption footgun this guard replaces.
75
+ * @param {string} raw
76
+ * @param {string} [where] `file:line` for the error message.
77
+ * @returns {unknown}
78
+ */
79
+ export function parseStateValue(raw: string, where?: string): unknown;
80
+ /**
81
+ * @param {string} raw
82
+ * @returns {string | number | boolean}
83
+ */
84
+ export function parseScalar(raw: string): string | number | boolean;
85
+ /**
86
+ * @param {string} [value]
87
+ * @returns {string}
88
+ */
89
+ export function stripQuotes(value?: string): string;
90
+ /**
91
+ * Turn a field name / state key into a human-readable accessible name, e.g.
92
+ * `quest` → "Quest", `user-name` → "User name", `firstName` → "First name".
93
+ * Hyphens, underscores, and camelCase boundaries become spaces; the first letter
94
+ * is capitalized. Used to auto-derive aria-label on generated form inputs.
95
+ * @param {string} name
96
+ * @returns {string}
97
+ */
98
+ export function humanizeName(name: string): string;
99
+ /**
100
+ * Escape a value for safe inclusion in HTML text and attribute contexts.
101
+ * @param {unknown} value
102
+ * @returns {string}
103
+ */
104
+ export function escapeHtml(value: unknown): string;
105
+ /**
106
+ * Reverse {@link escapeHtml}. Used by the loop initial-paint fill to read a
107
+ * `data-wd-fmt` attribute (HTML-escaped JSON) back into pipe stages at build
108
+ * time — the browser does the same decode for the runtime via getAttribute.
109
+ * `&amp;` is decoded last so `&amp;lt;` round-trips to `&lt;`, not `<`.
110
+ * @param {string} value
111
+ * @returns {string}
112
+ */
113
+ export function unescapeHtml(value: string): string;
114
+ export type Scope = import("./context.js").Scope;
115
+ export type Ctx = import("./context.js").Ctx;
@@ -0,0 +1,36 @@
1
+ /**
2
+ * @param {string} line
3
+ * @param {string[]} bodyLines
4
+ * @param {string[] | null} emptyLines
5
+ * @param {Ctx} ctx
6
+ * @param {number} index 0-based line index of the `@loop` opener.
7
+ * @param {number} [emptyStart] 0-based index the empty branch starts at within
8
+ * the loop body, so its nested errors report the true file line.
9
+ * @returns {string}
10
+ */
11
+ export function handleLoop(line: string, bodyLines: string[], emptyLines: string[] | null, ctx: Ctx, index: number, emptyStart?: number): string;
12
+ /**
13
+ * @param {string} str
14
+ * @param {number} start
15
+ * @param {string} tag
16
+ * @returns {number}
17
+ */
18
+ export function matchElement(str: string, start: number, tag: string): number;
19
+ /**
20
+ * Stable per-render key for a loop row, disambiguating duplicates with `#n`.
21
+ * @param {unknown} item
22
+ * @param {Map<string, number>} counts Mutable seen-count accumulator.
23
+ * @returns {string}
24
+ */
25
+ export function loopKeyOf(item: unknown, counts: Map<string, number>): string;
26
+ /**
27
+ * @typedef {import("./context.js").Ctx} Ctx
28
+ * @typedef {import("./context.js").Predicate} Predicate
29
+ * @typedef {import("./context.js").NumArg} NumArg
30
+ * @typedef {import("./context.js").LoopOpts} LoopOpts
31
+ */
32
+ export const LOOP_EXAMPLE: "@loop /products.json into p where p.price < 50 sort by p.price asc";
33
+ export type Ctx = import("./context.js").Ctx;
34
+ export type Predicate = import("./context.js").Predicate;
35
+ export type NumArg = import("./context.js").NumArg;
36
+ export type LoopOpts = import("./context.js").LoopOpts;
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Pick the markdown-it instance for a page (raw HTML off by default, on with `html: true`).
3
+ * @param {Meta} [meta]
4
+ * @returns {MarkdownIt}
5
+ */
6
+ export function selectMd(meta?: Meta): MarkdownIt;
7
+ /**
8
+ * @param {string} text
9
+ * @param {Ctx} ctx
10
+ * @returns {string}
11
+ */
12
+ export function renderProse(text: string, ctx: Ctx): string;
13
+ /**
14
+ * The inline token an attr block attaches to: the immediately-preceding image
15
+ * (self-closing) or the open token matching the immediately-preceding close
16
+ * (link/em/strong/…). Returns null when nothing valid precedes. Exported so its
17
+ * contract — including the defensive unbalanced-close fallback that markdown-it's
18
+ * always-balanced token stream never triggers in practice — is unit-testable.
19
+ * @param {any[]} children markdown-it inline child tokens
20
+ * @param {number} i Index of the attr-block text token.
21
+ * @returns {any} the matching markdown-it Token, or null
22
+ */
23
+ export function attrTarget(children: any[], i: number): any;
24
+ /**
25
+ * GitHub-style heading slug: lowercase, punctuation stripped (letters, numbers,
26
+ * whitespace, `_`, and `-` survive), each whitespace character becomes a hyphen.
27
+ * Punctuation-only headings fall back to `"section"` so the id is never empty.
28
+ * @param {string} text Plain heading text.
29
+ * @returns {string}
30
+ */
31
+ export function slugify(text: string): string;
32
+ export type Meta = import("./context.js").Meta;
33
+ export type Ctx = import("./context.js").Ctx;
34
+ import MarkdownIt from "markdown-it";
@@ -0,0 +1,32 @@
1
+ /**
2
+ * `:video /clip.mp4 [poster=…] [width=…] [height=…] [preload=…] [controls] [autoplay]
3
+ * [loop] [muted] [playsinline]` and `:audio /track.mp3 [preload=…] [controls] …` →
4
+ * a hardened `<video>`/`<audio>`. Defaults: `preload="metadata"`, and `controls`
5
+ * unless the clip is an `autoplay` background (autoplay also implies `muted`, which
6
+ * browsers require). Author flags/attrs always win. URLs use the `:fetch` scheme
7
+ * guard (relative or http(s)).
8
+ * @param {string} line
9
+ * @param {"video" | "audio"} kind
10
+ * @param {Ctx} ctx
11
+ * @param {number} index 0-based line index for `file:line` errors.
12
+ * @returns {string}
13
+ */
14
+ export function handleMedia(line: string, kind: "video" | "audio", ctx: Ctx, index: number): string;
15
+ /**
16
+ * `:embed <url> [title="…"]` → a lazy, privacy-friendly responsive iframe. A
17
+ * YouTube or Vimeo URL is rewritten to its no-cookie / player embed; any other
18
+ * http(s) URL becomes a generic 16:9 iframe. Inline styles keep the wrapper
19
+ * self-contained (no framework CSS), so an embed-only page stays zero-JS/zero-CSS.
20
+ * @param {string} line
21
+ * @param {Ctx} ctx
22
+ * @param {number} index 0-based line index for `file:line` errors.
23
+ * @returns {string}
24
+ */
25
+ export function handleEmbed(line: string, ctx: Ctx, index: number): string;
26
+ /**
27
+ * @typedef {import("./context.js").Ctx} Ctx
28
+ */
29
+ export const VIDEO_EXAMPLE: ":video /demo.mp4 controls";
30
+ export const AUDIO_EXAMPLE: ":audio /theme.mp3 controls";
31
+ export const EMBED_EXAMPLE: ":embed https://youtu.be/dQw4w9WgXcQ title=\"Demo\"";
32
+ export type Ctx = import("./context.js").Ctx;