markdown-schema 0.2.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/.claude/skills/markdown-schema/SKILL.md +282 -0
- package/.claude/skills/markdown-schema/markdown-schema.guideline.md +419 -0
- package/LICENSE +21 -0
- package/README.md +720 -0
- package/dist/bin/validate-md.js +1127 -0
- package/dist/bin/validate-md.js.map +1 -0
- package/dist/index.d.ts +296 -0
- package/dist/index.js +1039 -0
- package/dist/index.js.map +1 -0
- package/dist/node.d.ts +17 -0
- package/dist/node.js +18 -0
- package/dist/node.js.map +1 -0
- package/package.json +72 -0
|
@@ -0,0 +1,419 @@
|
|
|
1
|
+
# Markdown Template Authoring Guide
|
|
2
|
+
|
|
3
|
+
A practical guide for authoring and validating markdown templates that
|
|
4
|
+
pair with `markdown-schema` schemas.
|
|
5
|
+
|
|
6
|
+
## How Templates Work
|
|
7
|
+
|
|
8
|
+
Templates use `<!-- TEMPLATE-ONLY: ... -->` HTML comments to carry both
|
|
9
|
+
machine-readable schema directives and author-facing prose. These comments
|
|
10
|
+
are invisible in rendered markdown until replaced with real content.
|
|
11
|
+
|
|
12
|
+
**Key principle:** Fixed structural scaffolding (headings, section titles, stable
|
|
13
|
+
labels like `Type:`, table headers) stays as plain markdown outside the comment
|
|
14
|
+
tags. Everything that changes per instance — placeholders, sample values,
|
|
15
|
+
schema constraints, and authoring guidance — goes inside `<!-- TEMPLATE-ONLY: -->`
|
|
16
|
+
blocks. Constants outside, variables inside.
|
|
17
|
+
|
|
18
|
+
**The "survives filling" test.** For every span of text in the template, ask:
|
|
19
|
+
_will this exact text still be present, character-for-character, in every
|
|
20
|
+
correctly filled instance?_ If yes, it's a constant — leave it outside. If no,
|
|
21
|
+
it's a variable — wrap it in `<!-- TEMPLATE-ONLY: -->`. Apply the test at the
|
|
22
|
+
finest granularity that still reads naturally: usually a label, a column
|
|
23
|
+
header, or a heading number, but never a whole line if only part of it varies.
|
|
24
|
+
|
|
25
|
+
**Common trap — enum placeholders.** A list of allowed values like
|
|
26
|
+
`Root | Layout | Container | View` _looks_ like fixed scaffolding because it
|
|
27
|
+
contains no `[brackets]`, but the author is meant to pick exactly one and
|
|
28
|
+
delete the rest. It fails the survives-filling test and therefore belongs
|
|
29
|
+
inside `<!-- TEMPLATE-ONLY: -->`. The same applies to any choice list, sample
|
|
30
|
+
value, or example identifier, even when no brackets are used.
|
|
31
|
+
|
|
32
|
+
**Anti-example:**
|
|
33
|
+
|
|
34
|
+
```markdown
|
|
35
|
+
<!-- WRONG — the enum is a placeholder, not a constant -->
|
|
36
|
+
|
|
37
|
+
- Type: Root | Layout | Container | View | Provider | Primitive
|
|
38
|
+
|
|
39
|
+
<!-- RIGHT — label outside, value inside -->
|
|
40
|
+
|
|
41
|
+
- Type: <!-- TEMPLATE-ONLY: enum: Root | Layout | Container | View | Provider | Primitive; required -->
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Schema Pairing Rule
|
|
45
|
+
|
|
46
|
+
The **template is the source of truth**. `validate-md` derives the schema from
|
|
47
|
+
the template's `<!-- TEMPLATE-ONLY: -->` directives at runtime — no separate
|
|
48
|
+
TypeScript schema file is needed. An optional `*.refine.ts` sibling adds
|
|
49
|
+
cross-section invariants that can't be expressed as directives:
|
|
50
|
+
|
|
51
|
+
```
|
|
52
|
+
component.template.md ← source of truth; directives encode the schema
|
|
53
|
+
component.refine.ts ← (optional) cross-section Zod refinements
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
To validate a filled document:
|
|
57
|
+
|
|
58
|
+
```sh
|
|
59
|
+
validate-md --template component.template.md example.md
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Two Kinds of Directives
|
|
63
|
+
|
|
64
|
+
Directives come in two structural shapes — **inline** and **block** — and the
|
|
65
|
+
distinction is not cosmetic; it's enforced by the parser.
|
|
66
|
+
|
|
67
|
+
### Inline directives
|
|
68
|
+
|
|
69
|
+
Sit _inside_ a heading, list item, or paragraph. They **must close on the same
|
|
70
|
+
line as the opener** (single-line). Multi-line bodies are a hard error.
|
|
71
|
+
|
|
72
|
+
```markdown
|
|
73
|
+
- Stage: <!-- TEMPLATE-ONLY: enum: Alpha | Beta | GA; required -->
|
|
74
|
+
- SKU: <!-- TEMPLATE-ONLY: string; regex `^[A-Z]{3}-\d{4}$`; required -->
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
The two inline kinds are `string` (with optional modifiers including `regex`)
|
|
78
|
+
and `enum:`. See _Directive Reference_ below.
|
|
79
|
+
|
|
80
|
+
### Block directives
|
|
81
|
+
|
|
82
|
+
Sit on their own line, opener at column ≤ 3 (CommonMark block-HTML rule),
|
|
83
|
+
closer (`-->`) on its own line. Bodies span multiple lines. **Body lines must
|
|
84
|
+
start at column 0** (no leading whitespace).
|
|
85
|
+
|
|
86
|
+
```markdown
|
|
87
|
+
<!-- TEMPLATE-ONLY: row; min-rows: 1
|
|
88
|
+
Prop Name: string; regex `^[a-z][a-zA-Z0-9]*$`; required
|
|
89
|
+
Type: string; required
|
|
90
|
+
Required: enum: Yes | No; required
|
|
91
|
+
-->
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Modifiers on the **opener** line are semicolon-separated (same shape as inline
|
|
95
|
+
directives). The body holds whatever the directive kind allows: column specs
|
|
96
|
+
for `row`, `//` prose lines for `guide`, nothing for `section` or `freetext`.
|
|
97
|
+
|
|
98
|
+
The block kinds are `freetext`, `row`, `section`, and `guide`.
|
|
99
|
+
|
|
100
|
+
## Directive Reference
|
|
101
|
+
|
|
102
|
+
### `string` — inline scalar field
|
|
103
|
+
|
|
104
|
+
```markdown
|
|
105
|
+
- Parent Component: <!-- TEMPLATE-ONLY: string; required -->
|
|
106
|
+
- Source: <!-- TEMPLATE-ONLY: string; optional; only-if Type=Primitive -->
|
|
107
|
+
- SKU: <!-- TEMPLATE-ONLY: string; regex `^[A-Z]{3}-\d{4}$`; required -->
|
|
108
|
+
- Currency: <!-- TEMPLATE-ONLY: string; optional; default=USD -->
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Modifiers (semicolon-separated):
|
|
112
|
+
|
|
113
|
+
- `required` / `optional` — presence constraint (default: required)
|
|
114
|
+
- `regex` — pattern validated by `new RegExp(pattern)`, written between backticks in the directive. Note: `regex` is a **modifier** of `string`, not a type of its own. A bare `regex` without a leading `string;` is a hard error.
|
|
115
|
+
- `default=<value>` — fallback when the value is blank
|
|
116
|
+
- `only-if <Key>=<Value>` — field is permitted only when the named sibling
|
|
117
|
+
field equals the given value (enforced as a `superRefine` on the section)
|
|
118
|
+
|
|
119
|
+
### `enum:` — inline choice list
|
|
120
|
+
|
|
121
|
+
```markdown
|
|
122
|
+
- Type: <!-- TEMPLATE-ONLY: enum: Root | Layout | Container | View | Provider | Primitive; required -->
|
|
123
|
+
- Tier: <!-- TEMPLATE-ONLY: enum: Free | Pro | Enterprise; optional -->
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Choices are split on `|` and trimmed. The field schema becomes `z.enum([...])`.
|
|
127
|
+
|
|
128
|
+
**Escapes** — for choices containing `|` or `\`:
|
|
129
|
+
|
|
130
|
+
- `\|` → literal `|`
|
|
131
|
+
- `\\` → literal `\`
|
|
132
|
+
- Other `\x` → emitted as literal `\x` (lenient).
|
|
133
|
+
|
|
134
|
+
```markdown
|
|
135
|
+
- Logical Op: <!-- TEMPLATE-ONLY: enum: AND | OR | A \| B (either) | C \\ D; required -->
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
The third choice is the literal string `A | B (either)`; the fourth is
|
|
139
|
+
`C \ D`. Modifiers (`required`, `optional`) work the same as `string`.
|
|
140
|
+
|
|
141
|
+
### H1 placeholder
|
|
142
|
+
|
|
143
|
+
The H1 carries an inline directive that validates the document title:
|
|
144
|
+
|
|
145
|
+
```markdown
|
|
146
|
+
# Release Notes: <!-- TEMPLATE-ONLY: string; required -->
|
|
147
|
+
|
|
148
|
+
# Component Spec: <!-- TEMPLATE-ONLY: string; regex `^[A-Z][A-Za-z0-9]*$`; required -->
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
The title in the filled document is matched as `<prefix> <value>`, where
|
|
152
|
+
`prefix` is the literal H1 text up to the directive and `value` is validated
|
|
153
|
+
by the directive.
|
|
154
|
+
|
|
155
|
+
### H3 directive (in repeated sections)
|
|
156
|
+
|
|
157
|
+
Sections that contain repeated H3 sub-groups can validate each H3's heading
|
|
158
|
+
text. Place an inline directive inside the H3:
|
|
159
|
+
|
|
160
|
+
```markdown
|
|
161
|
+
## Changes
|
|
162
|
+
|
|
163
|
+
### <!-- TEMPLATE-ONLY: string; regex `^(add|fix|chore): .+$`; required -->
|
|
164
|
+
|
|
165
|
+
Description of the change.
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
After filling, an H3 reads e.g. `### add: loadPlugin()`. Only `string` and
|
|
169
|
+
`enum:` directives are allowed in H3s.
|
|
170
|
+
|
|
171
|
+
**H2 directives are not supported.** The H2 heading text is the JSON section
|
|
172
|
+
key (derived via `headingToKey`) — keys must be fixed.
|
|
173
|
+
|
|
174
|
+
### `row` — table row schema (block directive)
|
|
175
|
+
|
|
176
|
+
Placed between the header-separator row and the sample data row:
|
|
177
|
+
|
|
178
|
+
```markdown
|
|
179
|
+
| Prop Name | Type | Default | Required |
|
|
180
|
+
| --------- | ---- | ------- | -------- |
|
|
181
|
+
|
|
182
|
+
<!-- TEMPLATE-ONLY: row; min-rows: 1; max-rows: 50
|
|
183
|
+
Prop Name: string; regex `^[a-z][a-zA-Z0-9]*$`; required
|
|
184
|
+
Type: string; required
|
|
185
|
+
Default: string; default=—
|
|
186
|
+
Required: enum: Yes | No; required
|
|
187
|
+
-->
|
|
188
|
+
|
|
189
|
+
| propName | string | — | Yes |
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
- **Opener modifiers**: `min-rows: N`, `max-rows: N`. Semicolon-separated.
|
|
193
|
+
- **Body**: one column spec per line. Each spec is `Header: <field-modifiers>`,
|
|
194
|
+
where field modifiers are the same grammar as inline `string` / `enum:`
|
|
195
|
+
directives. Column header text matches the GFM table header literally
|
|
196
|
+
(preserves spaces and casing).
|
|
197
|
+
|
|
198
|
+
### `section` — section-level constraints (block directive)
|
|
199
|
+
|
|
200
|
+
Placed immediately under the section's H2. In most cases it fits on one line:
|
|
201
|
+
|
|
202
|
+
```markdown
|
|
203
|
+
## 7. Internal State
|
|
204
|
+
|
|
205
|
+
<!-- TEMPLATE-ONLY: section; optional; remove-if Type=View -->
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
Modifiers (opener-line, semicolon-separated):
|
|
209
|
+
|
|
210
|
+
- `optional` — the H2 may be absent in the filled doc; the section's JSON
|
|
211
|
+
value is `undefined`.
|
|
212
|
+
- `remove-if <Key>=<Value>` — the section must be absent when the named
|
|
213
|
+
sibling field equals the value. Present when it shouldn't be → hard error.
|
|
214
|
+
- `min-groups: N` / `max-groups: N` — for repeated sections (H3 sub-groups),
|
|
215
|
+
enforces the count of groups.
|
|
216
|
+
|
|
217
|
+
### `freetext` — free-form markdown section (block directive)
|
|
218
|
+
|
|
219
|
+
Placed immediately under the section's H2 to opt the section into free-form
|
|
220
|
+
mode. The section's JSON value is the body serialized back to a markdown
|
|
221
|
+
string (all node types preserved: paragraphs, fenced code, lists, tables,
|
|
222
|
+
blockquotes, thematic breaks, H3+ headings).
|
|
223
|
+
|
|
224
|
+
```markdown
|
|
225
|
+
## 1. Summary
|
|
226
|
+
|
|
227
|
+
<!-- TEMPLATE-ONLY: freetext; required -->
|
|
228
|
+
|
|
229
|
+
A paragraph, a code block, a list — any markdown content.
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
Modifiers (opener-line, semicolon-separated):
|
|
233
|
+
|
|
234
|
+
- `required` — the section body must be non-empty (default).
|
|
235
|
+
- `optional` — the H2 may be absent; JSON value is `undefined`.
|
|
236
|
+
|
|
237
|
+
**Authoring rules for free-text sections:**
|
|
238
|
+
|
|
239
|
+
- Use H3+ for in-body structure; H2 always terminates the section.
|
|
240
|
+
- No `<!-- TEMPLATE-ONLY: -->` directives of any kind inside the body —
|
|
241
|
+
including `guide` blocks. Place `guide` blocks _above_ the H2.
|
|
242
|
+
- Sections with no recognized shape and no `freetext` directive are a
|
|
243
|
+
hard parse error.
|
|
244
|
+
- **Serialization normalizations:** the body is round-tripped through
|
|
245
|
+
`mdast-util-to-markdown`. Bare URLs become autolink literals
|
|
246
|
+
(`https://…` → `<https://…>`). Thematic breaks may be normalized to
|
|
247
|
+
`***`. Treat the JSON value as a markdown string, not as the original
|
|
248
|
+
source bytes.
|
|
249
|
+
|
|
250
|
+
### `guide` — author-facing prose (block directive)
|
|
251
|
+
|
|
252
|
+
Free-form prose, intended for the human filling the template. The parser
|
|
253
|
+
ignores it entirely — it has no schema effect. Body lines must start with
|
|
254
|
+
`//` (after optional leading whitespace) or be blank.
|
|
255
|
+
|
|
256
|
+
```markdown
|
|
257
|
+
<!-- TEMPLATE-ONLY: guide
|
|
258
|
+
// One concise sentence per bullet. Keep under 80 chars.
|
|
259
|
+
// If you need to call out a non-obvious constraint, do it here.
|
|
260
|
+
-->
|
|
261
|
+
|
|
262
|
+
- Highlights: <!-- TEMPLATE-ONLY: string; required -->
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
**Authoring convention:** place a `guide` block immediately _before_ the
|
|
266
|
+
field, list, table, sub-heading, or section it documents. A `guide` at the
|
|
267
|
+
top of a section documents the whole section; one at the top of the file
|
|
268
|
+
documents the whole template. The parser doesn't enforce placement, but
|
|
269
|
+
authors read top-to-bottom and expect explanation before the thing being
|
|
270
|
+
explained.
|
|
271
|
+
|
|
272
|
+
A good `guide` answers at least one of:
|
|
273
|
+
|
|
274
|
+
- **What** is this field, in everyday words? (Not "string; required" but
|
|
275
|
+
"the customer-facing name of the release.")
|
|
276
|
+
- **Why** does this constraint exist? (Not "regex `^v\d+\.\d+\.\d+$`" but
|
|
277
|
+
"must match SemVer because our changelog tooling sorts by it.")
|
|
278
|
+
- **When** should this section/field appear, in the author's terms? (Not
|
|
279
|
+
"only-if Type=Primitive" but "fill this only if you picked 'Primitive'
|
|
280
|
+
in the Type field above.")
|
|
281
|
+
- **Example** of a good answer, when the format is unobvious.
|
|
282
|
+
|
|
283
|
+
Aim each `guide` at the author who will fill the template, not at a fellow
|
|
284
|
+
engineer reading the template's source.
|
|
285
|
+
|
|
286
|
+
**What to avoid:**
|
|
287
|
+
|
|
288
|
+
- **Restating the grammar.** `// string; required` adds no information.
|
|
289
|
+
- **Engineer-speak the author won't share.** "PascalCase identifier
|
|
290
|
+
conforming to `[A-Z][A-Za-z0-9]*$`" → say "starts with a capital
|
|
291
|
+
letter, e.g. `Card`."
|
|
292
|
+
- **Comments that go stale faster than the template.** Reference the
|
|
293
|
+
_intent_ of a constraint, not the current implementation that enforces it.
|
|
294
|
+
|
|
295
|
+
## Section Extractor Inference
|
|
296
|
+
|
|
297
|
+
The parser infers the extractor from the section body's structure:
|
|
298
|
+
|
|
299
|
+
| Body shape | Extractor | Returns |
|
|
300
|
+
| ------------------------------------------- | ------------ | ------------------------------------------------- |
|
|
301
|
+
| `freetext` directive present | `freetext` | `string` (markdown source, round-tripped) |
|
|
302
|
+
| Sub-headings (H3 inside H2) | `repeated` | `{ heading: string; items: string[] }[]` |
|
|
303
|
+
| GFM table (with `row` directive) | `table` | `Record<string, string>[]` |
|
|
304
|
+
| Labeled bullet list (`- Key: value`) | `bulletList` | `Record<string, string \| undefined>` |
|
|
305
|
+
| GFM task list (`- [ ]` / `- [x]`) | `taskList` | `{ checked: boolean; text: string }[]` |
|
|
306
|
+
| Plain unordered bullet list | `bulletList` | `string[]` |
|
|
307
|
+
| None of the above (no `freetext` directive) | **error** | "no recognized shape; add a `freetext` directive" |
|
|
308
|
+
|
|
309
|
+
The H2 heading text becomes the JSON key (via `headingToKey`, e.g.
|
|
310
|
+
`## 1. Internal State` → `internalState`). The section's value is whatever
|
|
311
|
+
the inferred extractor produces.
|
|
312
|
+
|
|
313
|
+
## Cross-Section Invariants (`*.refine.ts`)
|
|
314
|
+
|
|
315
|
+
Rules that span more than one section belong in a `*.refine.ts` sibling.
|
|
316
|
+
The CLI auto-discovers it; no directive is required to enable it.
|
|
317
|
+
|
|
318
|
+
```ts
|
|
319
|
+
// component.refine.ts
|
|
320
|
+
import type { z } from "zod";
|
|
321
|
+
|
|
322
|
+
export const refine = (doc: any, ctx: z.core.$RefinementCtx): void => {
|
|
323
|
+
const classification = doc.classification as Record<
|
|
324
|
+
string,
|
|
325
|
+
string | undefined
|
|
326
|
+
>;
|
|
327
|
+
if (
|
|
328
|
+
classification["Type"] === "View" &&
|
|
329
|
+
Array.isArray(doc.internalState) &&
|
|
330
|
+
doc.internalState.length > 0
|
|
331
|
+
) {
|
|
332
|
+
ctx.addIssue({
|
|
333
|
+
code: "custom",
|
|
334
|
+
path: ["internalState"],
|
|
335
|
+
message: "View components must not have internal state",
|
|
336
|
+
});
|
|
337
|
+
}
|
|
338
|
+
};
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
Document each invariant in a `guide` block under the H1 so authors filling
|
|
342
|
+
the template know what cross-section rules apply.
|
|
343
|
+
|
|
344
|
+
## Authoring Workflow
|
|
345
|
+
|
|
346
|
+
1. Copy the `*.template.md` file to a new document.
|
|
347
|
+
2. Read every `<!-- TEMPLATE-ONLY: guide ... -->` block — that's where the
|
|
348
|
+
template author put guidance for you.
|
|
349
|
+
3. Read every other `<!-- TEMPLATE-ONLY: -->` directive to understand each
|
|
350
|
+
section's structure and constraints.
|
|
351
|
+
4. Fill in each section:
|
|
352
|
+
- **inline directive** (`string`, `enum:`): write the value, delete the
|
|
353
|
+
`<!-- TEMPLATE-ONLY: ... -->` comment.
|
|
354
|
+
- **`row` directive**: replace the sample row(s) with real data. Delete
|
|
355
|
+
the `row` directive block (it sits between separator and sample row,
|
|
356
|
+
not in the filled doc).
|
|
357
|
+
- **`section` directive**: add content for the section; delete the entire
|
|
358
|
+
section's H2 + body if the directive marks it `optional` or
|
|
359
|
+
`remove-if` says to remove it.
|
|
360
|
+
- **`guide` block**: delete it (or leave it — it's invisible in rendered
|
|
361
|
+
markdown either way, but cleaner without).
|
|
362
|
+
5. Run `validate-md --template component.template.md example.md` and confirm `OK`.
|
|
363
|
+
6. Grep for leftover directives: `grep '<!-- TEMPLATE-ONLY' example.md` must
|
|
364
|
+
return empty.
|
|
365
|
+
|
|
366
|
+
## Filling Checklist
|
|
367
|
+
|
|
368
|
+
- [ ] All `<!-- TEMPLATE-ONLY: -->` comment blocks replaced or removed
|
|
369
|
+
- [ ] No placeholder text remains outside comment blocks
|
|
370
|
+
- [ ] `enum:` choices collapsed to a single value (with escapes preserved if
|
|
371
|
+
the choice contained `|` or `\`)
|
|
372
|
+
- [ ] All required fields and table columns are populated
|
|
373
|
+
- [ ] Optional sections removed if not applicable (or populated if present)
|
|
374
|
+
- [ ] Cross-section invariants (per the H1 `guide` block and `*.refine.ts`)
|
|
375
|
+
are satisfied
|
|
376
|
+
- [ ] `validate-md --template <template> <doc>` prints `OK`
|
|
377
|
+
- [ ] `grep '<!-- TEMPLATE-ONLY' <doc>` returns empty
|
|
378
|
+
|
|
379
|
+
## Validation Error Interpretation
|
|
380
|
+
|
|
381
|
+
`validate-md` prints three kinds of errors:
|
|
382
|
+
|
|
383
|
+
**Directive errors** (`DirectiveError`): the _template_ itself is malformed
|
|
384
|
+
— unknown keyword, multi-line inline directive, indented body line, etc.
|
|
385
|
+
Fix the template, not the filled doc. Includes file/line/column.
|
|
386
|
+
|
|
387
|
+
```
|
|
388
|
+
component.template.md:18:3: unknown TEMPLATE-ONLY directive kind: "pik one"
|
|
389
|
+
component.template.md:42:1: inline TEMPLATE-ONLY directive must close on the same line; "-->" not found before line end
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
**Structural errors** (`Error.message`): the filled document is missing a
|
|
393
|
+
required heading, a required table, or has a malformed structure.
|
|
394
|
+
|
|
395
|
+
```
|
|
396
|
+
example.md: schema validation failed
|
|
397
|
+
- inputs: must contain a table
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
**Zod issue list** (`path: message`): the document has the right structure
|
|
401
|
+
but a value violates a directive constraint or refine rule. Fix the value.
|
|
402
|
+
|
|
403
|
+
```
|
|
404
|
+
example.md: schema validation failed
|
|
405
|
+
- classification.Type: Invalid option: expected one of "Root", "Layout", ...
|
|
406
|
+
- internalState: View components must not have internal state
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
When an issue path doesn't map to a single section's schema (e.g. path is
|
|
410
|
+
`internalState` but the error mentions another section's field), suspect a
|
|
411
|
+
cross-section refine. Check the `*.refine.ts` source.
|
|
412
|
+
|
|
413
|
+
**Common false alarms:**
|
|
414
|
+
|
|
415
|
+
- Leftover `<!-- TEMPLATE-ONLY: -->` blocks in the filled document — grep for them.
|
|
416
|
+
- Heading text drift (changed heading after filling) — compare to template.
|
|
417
|
+
- Smart quotes from copy-paste (`"` instead of `"`) — replace with straight quotes.
|
|
418
|
+
- A choice that contains `|` written without `\|` escape — read the directive's
|
|
419
|
+
expected choices carefully.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Gergely Szerovay
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|