@zvndev/darkmown 2.2.0 → 2.3.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 +13 -0
- package/README.md +34 -2
- package/docs/spec-alignment.md +10 -0
- package/package.json +16 -11
- package/site/pages/docs/index.wd +14 -0
- package/site/pages/playground/index.js +47 -46
- package/src/catalog.js +16 -1
- package/src/compiler/actions.js +42 -22
- package/src/compiler/body.js +36 -14
- package/src/compiler/collections.js +16 -10
- package/src/compiler/context.js +39 -10
- package/src/compiler/fetch.js +39 -15
- package/src/compiler/format.js +5 -2
- package/src/compiler/forms.js +91 -26
- package/src/compiler/frontmatter.js +11 -2
- package/src/compiler/fs-reader.js +2 -1
- package/src/compiler/image-size.js +340 -0
- package/src/compiler/includes.js +8 -4
- package/src/compiler/interpolation.js +15 -7
- package/src/compiler/loops.js +63 -35
- package/src/compiler/markdown.js +4 -3
- package/src/compiler/media.js +17 -3
- package/src/compiler/page.js +8 -6
- package/src/compiler/predicates.js +49 -24
- package/src/compiler/reader.js +12 -2
- package/src/compiler/state.js +16 -2
- package/src/compiler/structure.js +24 -14
- package/src/deploy.js +8 -4
- package/src/errors.js +1156 -0
- package/src/router.js +7 -4
- package/src/scaffold.js +4 -2
- package/src/skin.js +17 -6
- package/types/api-runner.d.ts +94 -0
- package/types/builder.d.ts +141 -10
- package/types/catalog.d.ts +97 -0
- package/types/compiler/actions.d.ts +41 -0
- package/types/compiler/body.d.ts +11 -0
- package/types/compiler/collections.d.ts +76 -0
- package/types/compiler/context.d.ts +661 -0
- package/types/compiler/directives.d.ts +6 -0
- package/types/compiler/expr-ast.d.ts +20 -0
- package/types/compiler/fetch.d.ts +29 -0
- package/types/compiler/format.d.ts +49 -0
- package/types/compiler/forms.d.ts +94 -0
- package/types/compiler/frontmatter.d.ts +33 -0
- package/types/compiler/fs-reader.d.ts +7 -0
- package/types/compiler/image-size.d.ts +14 -0
- package/types/compiler/includes.d.ts +100 -0
- package/types/compiler/index.d.ts +5 -0
- package/types/compiler/interpolation.d.ts +115 -0
- package/types/compiler/loops.d.ts +36 -0
- package/types/compiler/markdown.d.ts +34 -0
- package/types/compiler/media.d.ts +32 -0
- package/types/compiler/page.d.ts +109 -0
- package/types/compiler/predicates.d.ts +50 -0
- package/types/compiler/reader.d.ts +56 -0
- package/types/compiler/state.d.ts +68 -0
- package/types/compiler/structure.d.ts +56 -0
- package/types/compiler.d.ts +35 -223
- package/types/dev.d.ts +47 -2
- package/types/errors.d.ts +100 -0
- package/types/feeds.d.ts +150 -0
- package/types/headers.d.ts +44 -0
- package/types/highlight.d.ts +24 -0
- package/types/router.d.ts +42 -3
- package/types/scaffold.d.ts +15 -3
- package/types/skin.d.ts +28 -1
- package/types/statics.d.ts +2 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,19 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to Darkmown are documented here. Versions follow [semver](https://semver.org); pre-1.0 minor versions may contain breaking changes.
|
|
4
4
|
|
|
5
|
+
## 2.3.0 — 2026-08-09
|
|
6
|
+
|
|
7
|
+
A trust release. Every compile error now carries a stable, searchable code; the dependency tree carries zero known advisories; and CI proves the framework on three operating systems and three browser engines instead of one of each. The new coverage found real bugs, including one that had silently broken every Windows user since the project started. No runtime change (7654 B gzipped, unchanged) and no breaking changes.
|
|
8
|
+
|
|
9
|
+
- **Stable `WDxxx` compile-error codes.** All 138 author-facing compile errors now carry a permanent identifier — `[WD201] Malformed :state in site/pages/index.wd:1: …` — mirrored on the thrown error as `err.wd.code` alongside the existing `file`, `line`, `hint`, and `example`. Codes are grouped by subsystem (`WD1xx` loops, `WD4xx` forms, `WD5xx` fetch, and so on), and a shipped code is a public contract: never renumbered, never reused. The registry (`src/errors.js`) is the single source of truth, [`docs/errors.md`](docs/errors.md) is generated from it, and the catalog exposes it as data. Drift guards run in both directions — an uncoded author-facing throw, a duplicate code, or an undocumented one all fail the build. Genuine internal invariants (the AST parser, the reader contract) deliberately stay plain `Error`s, and that split is enforced by test.
|
|
10
|
+
- **Zero known advisories.** `npm audit --audit-level=high` now exits 0. `image-size` carried a high-severity advisory affecting *every* published version including the latest — no upgrade existed — so its one call site (reading `<img>` dimensions at build time) is now served by an inlined header reader for PNG, JPEG, GIF, WebP, and SVG. Its segment walkers are explicitly bounded, which is the exact failure class the advisory described. `linkify-it` and `esbuild` advisories cleared by upgrade. Runtime code dependencies drop from three to two.
|
|
11
|
+
- **BUGFIX: CRLF source files no longer lose their frontmatter.** `parseFrontmatter` tested for an LF-shaped `---\n` opener, so a file authored on Windows — or checked out anywhere with git's `core.autocrlf`, the default *on* Windows — silently had its entire frontmatter block reclassified as body text. No error was raised: the page just lost its title, layout, `draft` flag, and collection schema fields. Source text is now newline-normalized at the reader boundary and in `parseFrontmatter` itself. If you have ever edited a Darkmown page on Windows, this is the release that fixes it.
|
|
12
|
+
- **CI runs on Windows, macOS, and Linux, across Chromium, Firefox, and WebKit.** Previously ubuntu-only and Chromium-only. The browser matrix is sharded so three engines cost no more wall-clock than one, and browsers are cached by resolved Playwright version. The whole 19-job matrix completes in about two minutes. The Windows job found the CRLF bug on its first run.
|
|
13
|
+
- **TypeScript types actually resolve now.** The `exports` map had no `types` conditions, so consumers on `moduleResolution: node16` / `nodenext` / `bundler` — the default for new TypeScript projects — resolved *no types at all*, and the `./catalog` subpath shipped no declarations. Both entrypoints now carry `types` conditions, `types/catalog.d.ts` is authored, and `@types/markdown-it` moved to `dependencies` (types-only, no runtime code) so the root entrypoint stops erroring. Fixing this exposed that `npm run types` had been failing under TypeScript 6 and emitting nothing, leaving the committed declarations stale; generation is repaired and the output regenerated.
|
|
14
|
+
- **A canary channel.** Merges to `master` publish a prerelease to the `next` dist-tag, so a fix can be exercised before it becomes stable: `npm i @zvndev/darkmown@next`. The tag-triggered stable path is untouched, and the canary job asserts after publishing that it did not become `latest`.
|
|
15
|
+
- **Property tests for the skin compiler and the router,** in the seeded, reproducible style of the existing fuzz suite. They immediately found a real bug: `.skin` token modifiers quoted the attribute *value* but never validated the attribute *name*, so `tokens [{=v]` emitted `:root[{="v"]` — a stray brace that unbalances the stylesheet and makes a CSS parser swallow every rule after it. Fixed, with the case kept as a permanent regression guard.
|
|
16
|
+
- **Internal:** the VS Code extension's `.vsix` is now built and uploaded by CI on every push, so it can be installed from a run without a marketplace listing. `.gitattributes` pins the working tree to LF. The test suite grew from 892 to 989 tests, holding 100% `src` line coverage.
|
|
17
|
+
|
|
5
18
|
## 2.2.0 — 2026-07-12
|
|
6
19
|
|
|
7
20
|
AI authoring becomes a first-class framework surface: a machine-readable directive catalog, a generated prompt cheatsheet, a shipped GBNF grammar for constrained decoding, and compile errors that teach by example — all generated from the compiler's own whitelist tables, so none of it can drift from what actually compiles. Zero runtime impact (7654 B, unchanged).
|
package/README.md
CHANGED
|
@@ -39,6 +39,10 @@ npx darkmown dev
|
|
|
39
39
|
|
|
40
40
|
The package is `@zvndev/darkmown`; the command it installs is plain `darkmown`.
|
|
41
41
|
|
|
42
|
+
Every merge to `master` also publishes a prerelease to the `next` tag, so you can try a
|
|
43
|
+
fix before it ships stable: `npm install -D @zvndev/darkmown@next`. Use `@latest` (the
|
|
44
|
+
default) for anything you care about.
|
|
45
|
+
|
|
42
46
|
Prefer to see it run before you install? The [browser playground](https://darkmown.com/playground/)
|
|
43
47
|
compiles `.wd`/`.md` live in your browser — the same compiler the CLI ships — with
|
|
44
48
|
no install and no build step.
|
|
@@ -995,13 +999,41 @@ import { compileFromMemory } from "@zvndev/darkmown";
|
|
|
995
999
|
try {
|
|
996
1000
|
compileFromMemory({ "site/pages/index.wd": ":state x" }, "site/pages/index.wd");
|
|
997
1001
|
} catch (err) {
|
|
998
|
-
err.message; // "Malformed :state in …/index.wd:1: :state x. Use: :state name = value [persist] — e.g. :state count = 0"
|
|
999
|
-
err.wd; // { file: "…/index.wd", line: 1, hint: ":state name = value [persist] — e.g. …", example: ":state count = 0" }
|
|
1002
|
+
err.message; // "[WD201] Malformed :state in …/index.wd:1: :state x. Use: :state name = value [persist] — e.g. :state count = 0"
|
|
1003
|
+
err.wd; // { code: "WD201", file: "…/index.wd", line: 1, hint: ":state name = value [persist] — e.g. …", example: ":state count = 0" }
|
|
1000
1004
|
}
|
|
1001
1005
|
```
|
|
1002
1006
|
|
|
1003
1007
|
`example` is always a concrete, **compilable** line (never a `[placeholder]`), and every corrective `Use:` hint that contains bracket-placeholders ends with a matching `— e.g. <valid line>`. This is a deliberate affordance for small local models, which otherwise copy `[optional]`-style placeholders into source verbatim.
|
|
1004
1008
|
|
|
1009
|
+
## Compile error codes
|
|
1010
|
+
|
|
1011
|
+
Every author-facing compile error opens with a stable `WDxxx` code, so it is searchable, linkable, and matchable by tooling without parsing prose:
|
|
1012
|
+
|
|
1013
|
+
```
|
|
1014
|
+
[WD201] Malformed :state in site/pages/index.wd:1: :state x.
|
|
1015
|
+
Use: :state name = value [persist] — e.g. :state count = 0
|
|
1016
|
+
```
|
|
1017
|
+
|
|
1018
|
+
The full list lives in [`docs/errors.md`](docs/errors.md) — code, cause, fix, and a compilable example each. Codes are grouped by subsystem:
|
|
1019
|
+
|
|
1020
|
+
| Range | Subsystem |
|
|
1021
|
+
| --- | --- |
|
|
1022
|
+
| `WD0xx` | Source, frontmatter & block structure |
|
|
1023
|
+
| `WD1xx` | Loops & collections |
|
|
1024
|
+
| `WD2xx` | State & expressions |
|
|
1025
|
+
| `WD3xx` | Button, effect & timer actions |
|
|
1026
|
+
| `WD4xx` | Forms & form fields |
|
|
1027
|
+
| `WD5xx` | Data fetching & URL safety |
|
|
1028
|
+
| `WD6xx` | Includes & page structure |
|
|
1029
|
+
| `WD7xx` | Media & embeds |
|
|
1030
|
+
| `WD8xx` | Skins & styling |
|
|
1031
|
+
| `WD9xx` | Project, routing & CLI |
|
|
1032
|
+
|
|
1033
|
+
**A shipped code is a public contract:** it is never renumbered and a retired number is never reused, so an error found in an old log or issue always resolves to a meaningful entry. `docs/errors.md` is generated from the compiler's own registry, and drift guards fail the build if an author-facing error is added without a code, if two errors share one, or if a code goes undocumented.
|
|
1034
|
+
|
|
1035
|
+
Genuine *internal* invariants — the expression-AST parser reading the compiler's own validated output, the reader contract — deliberately stay plain `Error`s and carry no code, because they signal a framework bug rather than something you can fix in a page.
|
|
1036
|
+
|
|
1005
1037
|
## AI authoring
|
|
1006
1038
|
|
|
1007
1039
|
Darkmown ships its own description of the `.wd` language — the same tables the compiler validates against — so a tool can teach a model to write `.wd` and constrain its output to what compiles.
|
package/docs/spec-alignment.md
CHANGED
|
@@ -2,6 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
This audit compares the current Darkmown implementation with the original MARROW/WhateverDown vision and the follow-up decisions made on 2026-06-11.
|
|
4
4
|
|
|
5
|
+
## 2.3.0 additions — error codes, supply chain, platform coverage (2026-08-09)
|
|
6
|
+
|
|
7
|
+
- **Every author-facing compile error carries a stable `WDxxx` code.** The original vision treated compile errors as a first-class DX surface ("errors include file path + corrective suggestion"); this closes the last gap by making them *identifiable*. 138 codes, grouped by subsystem, mirrored on `err.wd.code` beside the existing `file`/`line`/`hint`/`example`. `src/errors.js` is the single source of truth, `docs/errors.md` is generated from it, and `directiveCatalog()` exposes it as data for tooling. Drift guards run both directions: an author-facing throw without a code, a duplicate code, or an undocumented code all fail the build. Internal invariants (the AST parser reading the compiler's own validated output, the reader contract) stay plain `Error`s by design — they signal a framework bug, not an authoring mistake — and that split is itself enforced by test.
|
|
8
|
+
- **Zero known advisories, and one fewer runtime dependency.** `image-size` carried a high-severity advisory affecting every published version with no upgrade path, so its single build-time call site is now served by an inlined header reader (PNG/JPEG/GIF/WebP/SVG) with explicitly bounded parse loops. This keeps the project's "few runtime dependencies" posture honest rather than deferring to an unfixable upstream.
|
|
9
|
+
- **BUGFIX — CRLF sources lost their frontmatter.** `parseFrontmatter` tested an LF-shaped `---\n` opener, so any file authored on Windows or checked out with `core.autocrlf` had its whole frontmatter block silently reclassified as body text: no error, but the page lost its title, layout, `draft` flag, and collection schema fields. Newline normalization now happens at the reader boundary and in `parseFrontmatter`. This had been latent since the first commit; it surfaced within minutes of adding a Windows CI job.
|
|
10
|
+
- **Platform coverage is now part of the contract.** CI runs the suite on Linux, Windows, and macOS, and the browser e2e suite against Chromium, Firefox, and WebKit (sharded, so three engines cost roughly one engine's wall-clock). The reactive runtime had only ever been proven in one engine; the CLI's path handling had never executed on Windows at all.
|
|
11
|
+
- **Types resolve for modern TypeScript consumers.** The `exports` map lacked `types` conditions, so `moduleResolution: node16`/`nodenext`/`bundler` consumers got no types from either entrypoint. Fixed, `types/catalog.d.ts` authored, and the declaration build itself repaired (it had been failing silently under TypeScript 6, leaving the committed `.d.ts` stale).
|
|
12
|
+
- **Property tests extended to the skin compiler and the router,** in the existing seeded/reproducible style. They found a real defect on the first run: `.skin` token modifiers quoted the attribute value but never validated the attribute *name*, letting `tokens [{=v]` emit an unbalanced `:root[{="v"]` selector that a CSS parser would use to swallow every following rule.
|
|
13
|
+
- **Release channels:** merges to `master` publish a prerelease to the `next` dist-tag; the tag-triggered stable path is unchanged and the canary job verifies after publishing that it did not take `latest`.
|
|
14
|
+
|
|
5
15
|
## 2.1.0 additions — eval-free expression interpreter (2026-07-11)
|
|
6
16
|
|
|
7
17
|
- **Reactive pages run under a strict CSP with no `'unsafe-eval'`.** This is the headline. Through 2.0.x, the runtime executed validated `:computed` / `@loop … where` / `.class when` / `:if` expressions by building a `new Function` — which forced `script-src 'unsafe-eval'` on every reactive page, the #1 objection from security-minded adopters. The grammar was already a **closed** whitelist (comparisons, `and`/`or`/`not`, arithmetic in `:computed`, the `sum`/`avg`/`min`/`max`/`count` aggregates, literals, and dotted paths with proto-pollution guards), so it is small enough to interpret directly. The compiler now serializes each already-validated expression into a compact **AST** (nested arrays, tags `L`/`S`/`I`/`C`/`A` + the fixed operators — see `src/compiler/expr-ast.js`) emitted into the `data-wd-*` attributes, and the runtime **walks** it with a tiny closed evaluator (`src/runtime.js`). No `new Function`, no `eval`, anywhere on the page. The evaluator applies the *actual* JavaScript operators, so semantics (loose `==`/`!=`, `NaN`/`null` handling, `&&`/`||` short-circuit, string coercion, division) are byte-identical to the old path — guarded by a parity battery (`tests/expr-ast.test.js`) that compares old-`new Function` vs new-AST results across a wide input matrix, plus the fuzz suite (`tests/fuzz.test.js`) which now proves accepted expressions carry only closed op tags (dangerous text can survive only as inert `["L", …]` data) and walk to a pure value.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zvndev/darkmown",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.3.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -12,8 +12,14 @@
|
|
|
12
12
|
},
|
|
13
13
|
"main": "./src/index.js",
|
|
14
14
|
"exports": {
|
|
15
|
-
".":
|
|
16
|
-
|
|
15
|
+
".": {
|
|
16
|
+
"types": "./types/index.d.ts",
|
|
17
|
+
"default": "./src/index.js"
|
|
18
|
+
},
|
|
19
|
+
"./catalog": {
|
|
20
|
+
"types": "./types/catalog.d.ts",
|
|
21
|
+
"default": "./src/catalog.js"
|
|
22
|
+
},
|
|
17
23
|
"./package.json": "./package.json"
|
|
18
24
|
},
|
|
19
25
|
"types": "./types/index.d.ts",
|
|
@@ -61,18 +67,17 @@
|
|
|
61
67
|
"node": ">=20"
|
|
62
68
|
},
|
|
63
69
|
"dependencies": {
|
|
70
|
+
"@types/markdown-it": "^14.1.2",
|
|
64
71
|
"highlight.js": "^11.11.1",
|
|
65
|
-
"
|
|
66
|
-
"markdown-it": "^14.2.0"
|
|
72
|
+
"markdown-it": "^14.3.0"
|
|
67
73
|
},
|
|
68
74
|
"devDependencies": {
|
|
69
|
-
"@biomejs/biome": "^2.5.
|
|
70
|
-
"@playwright/test": "^1.
|
|
71
|
-
"@types/
|
|
72
|
-
"
|
|
73
|
-
"esbuild": "^0.24.2",
|
|
75
|
+
"@biomejs/biome": "^2.5.7",
|
|
76
|
+
"@playwright/test": "^1.61.1",
|
|
77
|
+
"@types/node": "^26.1.1",
|
|
78
|
+
"esbuild": "^0.28.2",
|
|
74
79
|
"husky": "^9.1.7",
|
|
75
|
-
"lint-staged": "^
|
|
80
|
+
"lint-staged": "^17.0.0",
|
|
76
81
|
"typescript": "^6.0.3"
|
|
77
82
|
},
|
|
78
83
|
"repository": {
|
package/site/pages/docs/index.wd
CHANGED
|
@@ -47,6 +47,7 @@ Darkmown is a Markdown-native framework for mostly-static sites with tiny reacti
|
|
|
47
47
|
- [Syntax highlighting](#syntax-highlighting)
|
|
48
48
|
- [Trust boundary](#trust-boundary)
|
|
49
49
|
- [Colocation](#colocation)
|
|
50
|
+
- [Compile error codes](#compile-error-codes)
|
|
50
51
|
- [CLI reference](#cli-reference)
|
|
51
52
|
- [Spec status](#spec-status)
|
|
52
53
|
</nav>
|
|
@@ -618,6 +619,19 @@ darkmown catalog --llms > system-prompt.md
|
|
|
618
619
|
|
|
619
620
|
Compile errors help here too: every corrective `Use:` hint ends with a concrete `— e.g. <valid line>`, and each thrown error carries a structured `err.wd = { file, line, hint, example }` mirror alongside its message — so an edit-and-retry loop gets the fix without parsing prose.
|
|
620
621
|
|
|
622
|
+
## Compile error codes
|
|
623
|
+
|
|
624
|
+
Every author-facing compile error opens with a stable `WDxxx` code, so it is searchable and matchable by tooling without parsing prose:
|
|
625
|
+
|
|
626
|
+
```
|
|
627
|
+
[WD201] Malformed :state in site/pages/index.wd:1: :state x.
|
|
628
|
+
Use: :state name = value [persist] — e.g. :state count = 0
|
|
629
|
+
```
|
|
630
|
+
|
|
631
|
+
The code is mirrored on the thrown error as `err.wd.code`, alongside `file`, `line`, `hint`, and a compilable `example`. Codes are grouped by subsystem — `WD0xx` source and frontmatter, `WD1xx` loops, `WD2xx` state and expressions, `WD3xx` actions, `WD4xx` forms, `WD5xx` fetching, `WD6xx` includes and structure, `WD7xx` media, `WD8xx` skins, `WD9xx` project and CLI.
|
|
632
|
+
|
|
633
|
+
A shipped code is a public contract: never renumbered, never reused. The full reference — cause, fix, and an example per code — is generated from the compiler's own registry, so it cannot drift from what actually throws.
|
|
634
|
+
|
|
621
635
|
## CLI reference
|
|
622
636
|
|
|
623
637
|
The `darkmown` command (installed by `npx @zvndev/darkmown`) is the whole tool — every command is one word:
|