@agentshouse/mdmodel 0.1.0-alpha.4 → 0.1.0-alpha.6

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/README.md CHANGED
@@ -7,7 +7,7 @@ Markdown syntax profile, and the conformance fixtures.
7
7
  The package runs in a browser and in Node. It makes no network call and reads
8
8
  no filesystem on its own. Callers pass text Library state as path-to-content and declare retained binary
9
9
  link targets separately in a `ValidationContext`.
10
- The public root exposes one ESM artifact. Node 24.20.0 and later can load it
10
+ The public root exposes one ESM artifact. Node 24.21.0 and later can load it
11
11
  through either `import` or `require`; browser bundlers consume the same artifact.
12
12
 
13
13
  ## Install
@@ -26,6 +26,7 @@ import {
26
26
  links,
27
27
  MARKDOWN_SYNTAX_PROFILE,
28
28
  parse,
29
+ project,
29
30
  serialize,
30
31
  validate,
31
32
  VERSION,
@@ -76,8 +77,46 @@ a resulting collection supplies that collection's context to this same
76
77
  `validate` function. `parse`, `apply`, and `serialize` operate on a single text
77
78
  Document; they do not validate collection membership or consume binary bytes.
78
79
  `apply` sets one typed field, including a list field as a whole, replaces
79
- one declared section body against that value's prior content, or rewrites one
80
- Markdown link destination. A field change does not reformat the rest of the file.
80
+ one existing section body or the preamble against that value's prior content,
81
+ or rewrites one Markdown link destination. A section need not be declared by
82
+ the Document's MD Type to be replaced. A field change does not reformat the rest of the file.
83
+
84
+ `Document.preamble` is a readonly string containing the exact source after
85
+ frontmatter and before the first section heading recognized by the existing
86
+ section parser. Without a section it is the entire post-frontmatter body;
87
+ without frontmatter it starts at the beginning of the Document. An empty span
88
+ is `''`. It includes whitespace, newlines, a leading H1, and any fenced text
89
+ before the first section. The closing frontmatter delimiter and its line ending
90
+ belong to frontmatter. Section recognition and the syntax profile are unchanged.
91
+
92
+ ```ts
93
+ const parsed = parse(content);
94
+ if (parsed.ok) {
95
+ const change = {
96
+ kind: 'preamble' as const,
97
+ prior: parsed.document.preamble,
98
+ value: 'Updated introduction.\n\n',
99
+ };
100
+ const result = apply(parsed.document, change);
101
+ if (result.outcome === 'applied') serialize(result.document);
102
+ }
103
+ ```
104
+
105
+ `apply(document, { kind: 'preamble', prior, value })` compares `prior` to the
106
+ current preamble exactly. A mismatch returns `{ outcome: 'stale', document }`
107
+ with the unchanged current Document. A match replaces only that source span,
108
+ then parses a new immutable Document; the original is unchanged. Frontmatter
109
+ and section source outside the span retain their exact bytes, including newline
110
+ and heading syntax. As with section replacement, `value` is literal source:
111
+ no newline or separator is inserted, and the resulting source is parsed again.
112
+ An invalid frontmatter result receives the existing parse refusal. Independent
113
+ field, section, and preamble edits keep their own prior values valid.
114
+
115
+ For a moved link, use `links` and the `link` change below to obtain the rewritten
116
+ Document, then compare its public `preamble` and `sections` to the original.
117
+ Submit the changed values with their original priors. Consumers need no source
118
+ span calculation or private Model import. Collection validation remains a
119
+ separate call against the resulting Library.
81
120
 
82
121
  `links(document, file)` is the inventory of that Document's ordinary link and
83
122
  image destinations that resolve to a collection Path, each with the destination
@@ -102,6 +141,54 @@ whitespace, does not open with `<`, and balances its parentheses, otherwise
102
141
  inside angle brackets, which the original form is kept in when it still fits. A
103
142
  destination that neither form can carry is refused as `unwritable-link`.
104
143
 
144
+ `project(document, file, context?)` is the Embedding projection input of one
145
+ Document: the prose a consumer embeds, with every internal Markdown-document
146
+ link reduced to the label a reader sees. A Document that declares no MD type
147
+ contributes its body after the frontmatter fence. A Document that declares one
148
+ contributes the H2 sections its MD type declares as free text, each with its own
149
+ heading line and every occurrence of a repeated name, in the order the Document
150
+ carries them. Its Path, its frontmatter fields, its preamble, the sections it
151
+ carries that its MD type does not declare, and the declared structured-list
152
+ sections are outside that input.
153
+
154
+ ```ts
155
+ const parsed = parse(content);
156
+ if (parsed.ok) {
157
+ const projected = project(parsed.document, 'tasks/two.md', {
158
+ documentTargets: ['plans/q3.md', 'people/ana.md'],
159
+ definition: types['types/task.md'],
160
+ });
161
+ if (projected.ok) embed(projected.text);
162
+ }
163
+ ```
164
+
165
+ `ProjectionContext` carries `documentTargets`, the collection-relative Paths the
166
+ caller holds as Markdown documents, and `definition`, the source of the MD Type
167
+ definition Document the Document's `type` names. A Document that declares a type
168
+ the caller supplies no definition for is refused as `unknown-type`, and a
169
+ definition source the model cannot read carries its own refusal naming
170
+ `types/<type>.md`. `documentTargets` is what makes a destination internal: a
171
+ destination resolving to a supplied Path is internal, and every other
172
+ destination stays authored text, a retained binary target and a Path nobody
173
+ supplied alike. The caller supplies that membership, as it does for `validate`;
174
+ the model reads no bytes, resolves no authority, and classifies nothing it was
175
+ not given.
176
+
177
+ An internal link contributes its visible label and no destination, fragment, or
178
+ title, in the inline `[label](path)`, full `[label][id]`, and collapsed
179
+ `[label][]` forms, and the link reference definition an enumerated use resolves
180
+ through leaves the projection with it. An image at a supplied document target
181
+ contributes its alt text the same way, because a move rewrites that destination
182
+ too. External URLs, retained binary targets, unsupplied destinations, bare
183
+ fragments, code spans, fenced code, and escaped syntax stay exactly as authored,
184
+ read through the same syntax profile `links` and `validate` read. `project`
185
+ returns text and never rewrites the Document.
186
+
187
+ Because a moved Path changes destinations alone, applying a `link` change leaves
188
+ the projection of every Document it rewrites byte-identical, while a changed
189
+ visible label changes it. `fixtures/projection.json` carries every move row of
190
+ that corpus; the label change is proven at this package's own `project` seam.
191
+
105
192
  ## Fixtures
106
193
 
107
194
  The conformance corpus ships in `fixtures/`. Its verdict format and rule ids
@@ -1 +1 @@
1
- {"version":3,"file":"document.d.ts","sourceRoot":"","sources":["../src/document.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAoDnF,wBAAgB,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,SAAK,GAAG,WAAW,CA0C7D;AAiGD,wBAAgB,KAAK,CAAC,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,GAAG,SAAS,IAAI,EAAE,CAWvE;AAED,wBAAgB,KAAK,CAAC,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,GAAG,WAAW,CAYrE;AAED,wBAAgB,SAAS,CAAC,QAAQ,EAAE,QAAQ,GAAG,MAAM,CAEpD"}
1
+ {"version":3,"file":"document.d.ts","sourceRoot":"","sources":["../src/document.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAsDnF,wBAAgB,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,SAAK,GAAG,WAAW,CAkD7D;AAyGD,wBAAgB,KAAK,CAAC,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,GAAG,SAAS,IAAI,EAAE,CAWvE;AAED,wBAAgB,KAAK,CAAC,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,GAAG,WAAW,CAarE;AAED,wBAAgB,SAAS,CAAC,QAAQ,EAAE,QAAQ,GAAG,MAAM,CAEpD"}
package/dist/document.js CHANGED
@@ -9,11 +9,12 @@ function refuse(file, rule, location) {
9
9
  location
10
10
  });
11
11
  }
12
- function freezeDocument(content, type, fields, sections, internal) {
12
+ function freezeDocument(content, type, fields, preamble, sections, internal) {
13
13
  const document = Object.freeze({
14
14
  content,
15
15
  type,
16
16
  fields: Object.freeze(fields),
17
+ preamble,
17
18
  sections: Object.freeze(sections)
18
19
  });
19
20
  internals.set(document, internal);
@@ -31,7 +32,7 @@ export function parse(content, file = '') {
31
32
  const spans = headingSpans(content, 0);
32
33
  return {
33
34
  ok: true,
34
- document: freezeDocument(content, undefined, {}, sectionsRecord(spans, content), {
35
+ document: freezeDocument(content, undefined, {}, content.slice(0, spans[0]?.headingStart), sectionsRecord(spans, content), {
35
36
  root: null,
36
37
  yamlEnd: 0,
37
38
  bodyStart: 0
@@ -56,7 +57,7 @@ export function parse(content, file = '') {
56
57
  const record = fields;
57
58
  return {
58
59
  ok: true,
59
- document: freezeDocument(content, declaredType(record), record, sectionsRecord(spans, content), {
60
+ document: freezeDocument(content, declaredType(record), record, content.slice(bodyStart, spans[0]?.headingStart), sectionsRecord(spans, content), {
60
61
  root,
61
62
  yamlEnd: fence.yamlEnd,
62
63
  bodyStart
@@ -135,6 +136,22 @@ function applySection(document, name, value, prior) {
135
136
  document: parsed.document
136
137
  };
137
138
  }
139
+ function applyPreamble(document, value, prior) {
140
+ if (document.preamble !== prior) return {
141
+ outcome: 'stale',
142
+ document
143
+ };
144
+ const start = bodyStartOf(document.content);
145
+ const parsed = parse(splice(document.content, start, start + document.preamble.length, value));
146
+ if (!parsed.ok) return {
147
+ outcome: 'refuse',
148
+ refusal: parsed.refusal
149
+ };
150
+ return {
151
+ outcome: 'applied',
152
+ document: parsed.document
153
+ };
154
+ }
138
155
  function fitsBare(to) {
139
156
  if (to === '' || to.startsWith('<')) return false;
140
157
  let depth = 0;
@@ -204,6 +221,7 @@ export function links(document, file) {
204
221
  }
205
222
  export function apply(document, change) {
206
223
  if (change.kind === 'link') return applyLink(document, change.href, change.to);
224
+ if (change.kind === 'preamble') return applyPreamble(document, change.value, change.prior);
207
225
  try {
208
226
  if (change.kind === 'field') return applyField(document, change.name, change.value, change.prior);
209
227
  return applySection(document, change.name, change.value, change.prior);
@@ -1 +1 @@
1
- {"version":3,"sources":["/workspace/packages/mdmodel/src/document.ts"],"sourcesContent":["import { hrefOccurrences } from './links.js';\nimport { classifyHref, hrefSuffix } from './paths.js';\nimport { headingSpans, sectionsRecord } from './sections.js';\nimport { refusal } from './refuse.js';\nimport type { ApplyResult, Change, Document, Link, ParseResult } from './types.js';\nimport {\n bodyStartAfterYaml,\n bodyStartOf,\n equalValue,\n extractFrontmatter,\n jsValue,\n lineEnding,\n mapEntry,\n parseYamlMapping,\n serializeJs,\n serializeKey,\n YamlError,\n type YamlMap,\n} from './yaml.js';\n\ntype Internal = {\n readonly root: YamlMap | null;\n readonly yamlEnd: number;\n readonly bodyStart: number;\n};\n\nconst internals = new WeakMap<Document, Internal>();\n\nfunction refuse(file: string, rule: string, location?: string) {\n return location === undefined ? refusal(file, rule) : refusal(file, rule, { location });\n}\n\nfunction freezeDocument(\n content: string,\n type: string | undefined,\n fields: Record<string, unknown>,\n sections: Record<string, string>,\n internal: Internal,\n): Document {\n const document: Document = Object.freeze({\n content,\n type,\n fields: Object.freeze(fields),\n sections: Object.freeze(sections),\n });\n internals.set(document, internal);\n return document;\n}\n\nfunction declaredType(fields: Record<string, unknown>): string | undefined {\n const value = fields.type;\n if (value === undefined || value === null) return undefined;\n if (typeof value !== 'string' || value === '') return undefined;\n return value;\n}\n\nexport function parse(content: string, file = ''): ParseResult {\n const fence = extractFrontmatter(content);\n if (fence.kind === 'none') {\n const spans = headingSpans(content, 0);\n return {\n ok: true,\n document: freezeDocument(content, undefined, {}, sectionsRecord(spans, content), {\n root: null,\n yamlEnd: 0,\n bodyStart: 0,\n }),\n };\n }\n if (fence.kind === 'unclosed') return { ok: false, refusal: refuse(file, 'frontmatter-fence') };\n try {\n const root = parseYamlMapping(content, fence.yamlStart, fence.yamlEnd);\n const fields = jsValue(root);\n if (fields === null || typeof fields !== 'object' || Array.isArray(fields)) {\n return { ok: false, refusal: refuse(file, 'frontmatter-fence') };\n }\n const bodyStart = bodyStartAfterYaml(content, fence.yamlEnd);\n const spans = headingSpans(content, bodyStart);\n const record = fields as Record<string, unknown>;\n return {\n ok: true,\n document: freezeDocument(\n content,\n declaredType(record),\n record,\n sectionsRecord(spans, content),\n {\n root,\n yamlEnd: fence.yamlEnd,\n bodyStart,\n },\n ),\n };\n } catch (error) {\n if (error instanceof YamlError)\n return { ok: false, refusal: refuse(file, 'frontmatter-fence') };\n throw error;\n }\n}\n\nfunction splice(content: string, start: number, end: number, insert: string): string {\n return content.slice(0, start) + insert + content.slice(end);\n}\n\nfunction applyField(document: Document, name: string, value: unknown, prior: unknown): ApplyResult {\n const internal = internals.get(document);\n if (internal === undefined || internal.root === null) {\n return { outcome: 'refuse', refusal: refuse('', 'frontmatter-fence', name) };\n }\n if (!equalValue(document.fields[name], prior)) {\n return { outcome: 'stale', document };\n }\n let nextContent: string;\n const existing = mapEntry(internal.root, name);\n if (existing !== undefined) {\n nextContent = splice(\n document.content,\n existing.value.start,\n existing.value.end,\n serializeJs(value),\n );\n } else {\n const nl = lineEnding(document.content);\n const line = `${serializeKey(name)}: ${serializeJs(value)}${nl}`;\n const before = document.content.slice(0, internal.yamlEnd);\n const prefix = before.endsWith('\\n') || before.endsWith('\\r\\n') ? '' : nl;\n nextContent = splice(document.content, internal.yamlEnd, internal.yamlEnd, `${prefix}${line}`);\n }\n const parsed = parse(nextContent);\n if (!parsed.ok) return { outcome: 'refuse', refusal: parsed.refusal };\n return { outcome: 'applied', document: parsed.document };\n}\n\nfunction applySection(document: Document, name: string, value: string, prior: string): ApplyResult {\n const internal = internals.get(document);\n if (internal === undefined)\n return { outcome: 'refuse', refusal: refuse('', 'required-section', name) };\n const spans = headingSpans(document.content, internal.bodyStart);\n const span = spans.find((item) => item.name === name);\n if (span === undefined)\n return { outcome: 'refuse', refusal: refuse('', 'required-section', name) };\n const current = document.content.slice(span.bodyStart, span.bodyEnd);\n if (current !== prior) return { outcome: 'stale', document };\n const parsed = parse(splice(document.content, span.bodyStart, span.bodyEnd, value));\n if (!parsed.ok) return { outcome: 'refuse', refusal: parsed.refusal };\n return { outcome: 'applied', document: parsed.document };\n}\n\nfunction fitsBare(to: string): boolean {\n if (to === '' || to.startsWith('<')) return false;\n let depth = 0;\n for (const character of to) {\n if (/[\\s\\u0000-\\u001f\\u007f]/.test(character)) return false;\n if (character === '(') depth += 1;\n else if (character === ')') {\n if (depth === 0) return false;\n depth -= 1;\n }\n }\n return depth === 0;\n}\n\nfunction fitsBrackets(to: string): boolean {\n return to !== '' && !/[<>\\n\\r]/.test(to);\n}\n\nfunction destinationText(to: string, bracketed: boolean): string | null {\n if (fitsBare(to)) return bracketed && fitsBrackets(to) ? `<${to}>` : to;\n if (fitsBrackets(to)) return `<${to}>`;\n return null;\n}\n\nfunction applyLink(document: Document, href: string, to: string): ApplyResult {\n const content = document.content;\n const bodyStart = bodyStartOf(content);\n const matches = hrefOccurrences(content.slice(bodyStart)).filter(\n (occurrence) => occurrence.href === href,\n );\n if (matches.length === 0) return { outcome: 'stale', document };\n const written = to + hrefSuffix(href);\n let rewritten = '';\n let cursor = 0;\n for (const match of matches) {\n const text = destinationText(written, match.bracketed);\n if (text === null)\n return { outcome: 'refuse', refusal: refuse('', 'unwritable-link', written) };\n rewritten += content.slice(cursor, bodyStart + match.start);\n rewritten += text;\n cursor = bodyStart + match.end;\n }\n const parsed = parse(rewritten + content.slice(cursor));\n if (!parsed.ok) return { outcome: 'refuse', refusal: parsed.refusal };\n return { outcome: 'applied', document: parsed.document };\n}\n\nexport function links(document: Document, file: string): readonly Link[] {\n const found: Link[] = [];\n const seen = new Set<string>();\n for (const occurrence of hrefOccurrences(document.content.slice(bodyStartOf(document.content)))) {\n if (seen.has(occurrence.href)) continue;\n const resolved = classifyHref(file, occurrence.href);\n if (resolved.kind !== 'path') continue;\n seen.add(occurrence.href);\n found.push({ href: occurrence.href, target: resolved.path });\n }\n return found;\n}\n\nexport function apply(document: Document, change: Change): ApplyResult {\n if (change.kind === 'link') return applyLink(document, change.href, change.to);\n try {\n if (change.kind === 'field')\n return applyField(document, change.name, change.value, change.prior);\n return applySection(document, change.name, change.value, change.prior);\n } catch (error) {\n if (error instanceof YamlError) {\n return { outcome: 'refuse', refusal: refuse('', 'frontmatter-fence', change.name) };\n }\n throw error;\n }\n}\n\nexport function serialize(document: Document): string {\n return document.content;\n}\n"],"names":["hrefOccurrences","classifyHref","hrefSuffix","headingSpans","sectionsRecord","refusal","bodyStartAfterYaml","bodyStartOf","equalValue","extractFrontmatter","jsValue","lineEnding","mapEntry","parseYamlMapping","serializeJs","serializeKey","YamlError","internals","WeakMap","refuse","file","rule","location","undefined","freezeDocument","content","type","fields","sections","internal","document","Object","freeze","set","declaredType","value","parse","fence","kind","spans","ok","root","yamlEnd","bodyStart","yamlStart","Array","isArray","record","error","splice","start","end","insert","slice","applyField","name","prior","get","outcome","nextContent","existing","nl","line","before","prefix","endsWith","parsed","applySection","span","find","item","current","bodyEnd","fitsBare","to","startsWith","depth","character","test","fitsBrackets","destinationText","bracketed","applyLink","href","matches","filter","occurrence","length","written","rewritten","cursor","match","text","links","found","seen","Set","has","resolved","add","push","target","path","apply","change","serialize"],"mappings":"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"}
1
+ {"version":3,"sources":["/workspace/packages/mdmodel/src/document.ts"],"sourcesContent":["import { hrefOccurrences } from './links.js';\nimport { classifyHref, hrefSuffix } from './paths.js';\nimport { headingSpans, sectionsRecord } from './sections.js';\nimport { refusal } from './refuse.js';\nimport type { ApplyResult, Change, Document, Link, ParseResult } from './types.js';\nimport {\n bodyStartAfterYaml,\n bodyStartOf,\n equalValue,\n extractFrontmatter,\n jsValue,\n lineEnding,\n mapEntry,\n parseYamlMapping,\n serializeJs,\n serializeKey,\n YamlError,\n type YamlMap,\n} from './yaml.js';\n\ntype Internal = {\n readonly root: YamlMap | null;\n readonly yamlEnd: number;\n readonly bodyStart: number;\n};\n\nconst internals = new WeakMap<Document, Internal>();\n\nfunction refuse(file: string, rule: string, location?: string) {\n return location === undefined ? refusal(file, rule) : refusal(file, rule, { location });\n}\n\nfunction freezeDocument(\n content: string,\n type: string | undefined,\n fields: Record<string, unknown>,\n preamble: string,\n sections: Record<string, string>,\n internal: Internal,\n): Document {\n const document: Document = Object.freeze({\n content,\n type,\n fields: Object.freeze(fields),\n preamble,\n sections: Object.freeze(sections),\n });\n internals.set(document, internal);\n return document;\n}\n\nfunction declaredType(fields: Record<string, unknown>): string | undefined {\n const value = fields.type;\n if (value === undefined || value === null) return undefined;\n if (typeof value !== 'string' || value === '') return undefined;\n return value;\n}\n\nexport function parse(content: string, file = ''): ParseResult {\n const fence = extractFrontmatter(content);\n if (fence.kind === 'none') {\n const spans = headingSpans(content, 0);\n return {\n ok: true,\n document: freezeDocument(\n content,\n undefined,\n {},\n content.slice(0, spans[0]?.headingStart),\n sectionsRecord(spans, content),\n {\n root: null,\n yamlEnd: 0,\n bodyStart: 0,\n },\n ),\n };\n }\n if (fence.kind === 'unclosed') return { ok: false, refusal: refuse(file, 'frontmatter-fence') };\n try {\n const root = parseYamlMapping(content, fence.yamlStart, fence.yamlEnd);\n const fields = jsValue(root);\n if (fields === null || typeof fields !== 'object' || Array.isArray(fields)) {\n return { ok: false, refusal: refuse(file, 'frontmatter-fence') };\n }\n const bodyStart = bodyStartAfterYaml(content, fence.yamlEnd);\n const spans = headingSpans(content, bodyStart);\n const record = fields as Record<string, unknown>;\n return {\n ok: true,\n document: freezeDocument(\n content,\n declaredType(record),\n record,\n content.slice(bodyStart, spans[0]?.headingStart),\n sectionsRecord(spans, content),\n {\n root,\n yamlEnd: fence.yamlEnd,\n bodyStart,\n },\n ),\n };\n } catch (error) {\n if (error instanceof YamlError)\n return { ok: false, refusal: refuse(file, 'frontmatter-fence') };\n throw error;\n }\n}\n\nfunction splice(content: string, start: number, end: number, insert: string): string {\n return content.slice(0, start) + insert + content.slice(end);\n}\n\nfunction applyField(document: Document, name: string, value: unknown, prior: unknown): ApplyResult {\n const internal = internals.get(document);\n if (internal === undefined || internal.root === null) {\n return { outcome: 'refuse', refusal: refuse('', 'frontmatter-fence', name) };\n }\n if (!equalValue(document.fields[name], prior)) {\n return { outcome: 'stale', document };\n }\n let nextContent: string;\n const existing = mapEntry(internal.root, name);\n if (existing !== undefined) {\n nextContent = splice(\n document.content,\n existing.value.start,\n existing.value.end,\n serializeJs(value),\n );\n } else {\n const nl = lineEnding(document.content);\n const line = `${serializeKey(name)}: ${serializeJs(value)}${nl}`;\n const before = document.content.slice(0, internal.yamlEnd);\n const prefix = before.endsWith('\\n') || before.endsWith('\\r\\n') ? '' : nl;\n nextContent = splice(document.content, internal.yamlEnd, internal.yamlEnd, `${prefix}${line}`);\n }\n const parsed = parse(nextContent);\n if (!parsed.ok) return { outcome: 'refuse', refusal: parsed.refusal };\n return { outcome: 'applied', document: parsed.document };\n}\n\nfunction applySection(document: Document, name: string, value: string, prior: string): ApplyResult {\n const internal = internals.get(document);\n if (internal === undefined)\n return { outcome: 'refuse', refusal: refuse('', 'required-section', name) };\n const spans = headingSpans(document.content, internal.bodyStart);\n const span = spans.find((item) => item.name === name);\n if (span === undefined)\n return { outcome: 'refuse', refusal: refuse('', 'required-section', name) };\n const current = document.content.slice(span.bodyStart, span.bodyEnd);\n if (current !== prior) return { outcome: 'stale', document };\n const parsed = parse(splice(document.content, span.bodyStart, span.bodyEnd, value));\n if (!parsed.ok) return { outcome: 'refuse', refusal: parsed.refusal };\n return { outcome: 'applied', document: parsed.document };\n}\n\nfunction applyPreamble(document: Document, value: string, prior: string): ApplyResult {\n if (document.preamble !== prior) return { outcome: 'stale', document };\n const start = bodyStartOf(document.content);\n const parsed = parse(splice(document.content, start, start + document.preamble.length, value));\n if (!parsed.ok) return { outcome: 'refuse', refusal: parsed.refusal };\n return { outcome: 'applied', document: parsed.document };\n}\n\nfunction fitsBare(to: string): boolean {\n if (to === '' || to.startsWith('<')) return false;\n let depth = 0;\n for (const character of to) {\n if (/[\\s\\u0000-\\u001f\\u007f]/.test(character)) return false;\n if (character === '(') depth += 1;\n else if (character === ')') {\n if (depth === 0) return false;\n depth -= 1;\n }\n }\n return depth === 0;\n}\n\nfunction fitsBrackets(to: string): boolean {\n return to !== '' && !/[<>\\n\\r]/.test(to);\n}\n\nfunction destinationText(to: string, bracketed: boolean): string | null {\n if (fitsBare(to)) return bracketed && fitsBrackets(to) ? `<${to}>` : to;\n if (fitsBrackets(to)) return `<${to}>`;\n return null;\n}\n\nfunction applyLink(document: Document, href: string, to: string): ApplyResult {\n const content = document.content;\n const bodyStart = bodyStartOf(content);\n const matches = hrefOccurrences(content.slice(bodyStart)).filter(\n (occurrence) => occurrence.href === href,\n );\n if (matches.length === 0) return { outcome: 'stale', document };\n const written = to + hrefSuffix(href);\n let rewritten = '';\n let cursor = 0;\n for (const match of matches) {\n const text = destinationText(written, match.bracketed);\n if (text === null)\n return { outcome: 'refuse', refusal: refuse('', 'unwritable-link', written) };\n rewritten += content.slice(cursor, bodyStart + match.start);\n rewritten += text;\n cursor = bodyStart + match.end;\n }\n const parsed = parse(rewritten + content.slice(cursor));\n if (!parsed.ok) return { outcome: 'refuse', refusal: parsed.refusal };\n return { outcome: 'applied', document: parsed.document };\n}\n\nexport function links(document: Document, file: string): readonly Link[] {\n const found: Link[] = [];\n const seen = new Set<string>();\n for (const occurrence of hrefOccurrences(document.content.slice(bodyStartOf(document.content)))) {\n if (seen.has(occurrence.href)) continue;\n const resolved = classifyHref(file, occurrence.href);\n if (resolved.kind !== 'path') continue;\n seen.add(occurrence.href);\n found.push({ href: occurrence.href, target: resolved.path });\n }\n return found;\n}\n\nexport function apply(document: Document, change: Change): ApplyResult {\n if (change.kind === 'link') return applyLink(document, change.href, change.to);\n if (change.kind === 'preamble') return applyPreamble(document, change.value, change.prior);\n try {\n if (change.kind === 'field')\n return applyField(document, change.name, change.value, change.prior);\n return applySection(document, change.name, change.value, change.prior);\n } catch (error) {\n if (error instanceof YamlError) {\n return { outcome: 'refuse', refusal: refuse('', 'frontmatter-fence', change.name) };\n }\n throw error;\n }\n}\n\nexport function serialize(document: Document): string {\n return document.content;\n}\n"],"names":["hrefOccurrences","classifyHref","hrefSuffix","headingSpans","sectionsRecord","refusal","bodyStartAfterYaml","bodyStartOf","equalValue","extractFrontmatter","jsValue","lineEnding","mapEntry","parseYamlMapping","serializeJs","serializeKey","YamlError","internals","WeakMap","refuse","file","rule","location","undefined","freezeDocument","content","type","fields","preamble","sections","internal","document","Object","freeze","set","declaredType","value","parse","fence","kind","spans","ok","slice","headingStart","root","yamlEnd","bodyStart","yamlStart","Array","isArray","record","error","splice","start","end","insert","applyField","name","prior","get","outcome","nextContent","existing","nl","line","before","prefix","endsWith","parsed","applySection","span","find","item","current","bodyEnd","applyPreamble","length","fitsBare","to","startsWith","depth","character","test","fitsBrackets","destinationText","bracketed","applyLink","href","matches","filter","occurrence","written","rewritten","cursor","match","text","links","found","seen","Set","has","resolved","add","push","target","path","apply","change","serialize"],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  export { apply, links, parse, serialize } from './document.js';
2
2
  export { MARKDOWN_SYNTAX_PROFILE } from './profile.js';
3
- export type { ApplyResult, Change, Document, Library, Link, ParseResult, Refusal, Validation, ValidationContext, } from './types.js';
3
+ export { project } from './project.js';
4
+ export type { ApplyResult, Change, Document, Library, Link, ParseResult, Projection, ProjectionContext, Refusal, Validation, ValidationContext, } from './types.js';
4
5
  export { validate } from './validate.js';
5
6
  export { VERSION } from './version.js';
6
7
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAC/D,OAAO,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAC;AACvD,YAAY,EACV,WAAW,EACX,MAAM,EACN,QAAQ,EACR,OAAO,EACP,IAAI,EACJ,WAAW,EACX,OAAO,EACP,UAAU,EACV,iBAAiB,GAClB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAC/D,OAAO,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAC;AACvD,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,YAAY,EACV,WAAW,EACX,MAAM,EACN,QAAQ,EACR,OAAO,EACP,IAAI,EACJ,WAAW,EACX,UAAU,EACV,iBAAiB,EACjB,OAAO,EACP,UAAU,EACV,iBAAiB,GAClB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC"}
package/dist/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  export { apply, links, parse, serialize } from './document.js';
2
2
  export { MARKDOWN_SYNTAX_PROFILE } from './profile.js';
3
+ export { project } from './project.js';
3
4
  export { validate } from './validate.js';
4
5
  export { VERSION } from './version.js';
5
6
 
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["/workspace/packages/mdmodel/src/index.ts"],"sourcesContent":["export { apply, links, parse, serialize } from './document.js';\nexport { MARKDOWN_SYNTAX_PROFILE } from './profile.js';\nexport type {\n ApplyResult,\n Change,\n Document,\n Library,\n Link,\n ParseResult,\n Refusal,\n Validation,\n ValidationContext,\n} from './types.js';\nexport { validate } from './validate.js';\nexport { VERSION } from './version.js';\n"],"names":["apply","links","parse","serialize","MARKDOWN_SYNTAX_PROFILE","validate","VERSION"],"mappings":"AAAA,SAASA,KAAK,EAAEC,KAAK,EAAEC,KAAK,EAAEC,SAAS,QAAQ,gBAAgB;AAC/D,SAASC,uBAAuB,QAAQ,eAAe;AAYvD,SAASC,QAAQ,QAAQ,gBAAgB;AACzC,SAASC,OAAO,QAAQ,eAAe"}
1
+ {"version":3,"sources":["/workspace/packages/mdmodel/src/index.ts"],"sourcesContent":["export { apply, links, parse, serialize } from './document.js';\nexport { MARKDOWN_SYNTAX_PROFILE } from './profile.js';\nexport { project } from './project.js';\nexport type {\n ApplyResult,\n Change,\n Document,\n Library,\n Link,\n ParseResult,\n Projection,\n ProjectionContext,\n Refusal,\n Validation,\n ValidationContext,\n} from './types.js';\nexport { validate } from './validate.js';\nexport { VERSION } from './version.js';\n"],"names":["apply","links","parse","serialize","MARKDOWN_SYNTAX_PROFILE","project","validate","VERSION"],"mappings":"AAAA,SAASA,KAAK,EAAEC,KAAK,EAAEC,KAAK,EAAEC,SAAS,QAAQ,gBAAgB;AAC/D,SAASC,uBAAuB,QAAQ,eAAe;AACvD,SAASC,OAAO,QAAQ,eAAe;AAcvC,SAASC,QAAQ,QAAQ,gBAAgB;AACzC,SAASC,OAAO,QAAQ,eAAe"}
package/dist/links.d.ts CHANGED
@@ -4,5 +4,22 @@ export type HrefOccurrence = {
4
4
  readonly end: number;
5
5
  readonly bracketed: boolean;
6
6
  };
7
+ export type LinkUse = {
8
+ readonly start: number;
9
+ readonly end: number;
10
+ readonly labelStart: number;
11
+ readonly labelEnd: number;
12
+ readonly destination: HrefOccurrence;
13
+ };
14
+ export type DefinitionBlock = {
15
+ readonly start: number;
16
+ readonly end: number;
17
+ readonly destination: HrefOccurrence;
18
+ };
19
+ export type LinkInventory = {
20
+ readonly uses: readonly LinkUse[];
21
+ readonly blocks: readonly DefinitionBlock[];
22
+ };
23
+ export declare function linkInventory(body: string): LinkInventory;
7
24
  export declare function hrefOccurrences(body: string): readonly HrefOccurrence[];
8
25
  //# sourceMappingURL=links.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"links.d.ts","sourceRoot":"","sources":["../src/links.ts"],"names":[],"mappings":"AA4VA,MAAM,MAAM,cAAc,GAAG;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;CAC7B,CAAC;AAEF,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,cAAc,EAAE,CA2BvE"}
1
+ {"version":3,"file":"links.d.ts","sourceRoot":"","sources":["../src/links.ts"],"names":[],"mappings":"AA8WA,MAAM,MAAM,cAAc,GAAG;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;CAC7B,CAAC;AAEF,MAAM,MAAM,OAAO,GAAG;IACpB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,WAAW,EAAE,cAAc,CAAC;CACtC,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,WAAW,EAAE,cAAc,CAAC;CACtC,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,QAAQ,CAAC,IAAI,EAAE,SAAS,OAAO,EAAE,CAAC;IAClC,QAAQ,CAAC,MAAM,EAAE,SAAS,eAAe,EAAE,CAAC;CAC7C,CAAC;AAEF,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,aAAa,CAyBzD;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,cAAc,EAAE,CAIvE"}
package/dist/links.js CHANGED
@@ -207,7 +207,7 @@ function labelEnd(text, start) {
207
207
  }
208
208
  return depth === 0 ? i : -1;
209
209
  }
210
- function takeLink(text, index, found, defs) {
210
+ function takeLink(text, index, start, found, defs) {
211
211
  const end = labelEnd(text, index + 1);
212
212
  if (end === -1) return index + 1;
213
213
  const label = text.slice(index + 1, end - 1);
@@ -215,7 +215,13 @@ function takeLink(text, index, found, defs) {
215
215
  if (text[i] === '(') {
216
216
  const dest = destination(text, i);
217
217
  if (dest === null) return index + 1;
218
- found.push(dest.occurrence);
218
+ found.push({
219
+ start,
220
+ end: dest.end,
221
+ labelStart: index + 1,
222
+ labelEnd: end - 1,
223
+ destination: dest.occurrence
224
+ });
219
225
  return dest.end;
220
226
  }
221
227
  if (text[i] === '[') {
@@ -223,7 +229,15 @@ function takeLink(text, index, found, defs) {
223
229
  if (close === -1) return index + 1;
224
230
  const id = (text.slice(i + 1, close - 1) || label).trim().toLowerCase();
225
231
  const defined = defs.get(id);
226
- if (defined !== undefined) found.push(defined);
232
+ if (defined !== undefined) {
233
+ found.push({
234
+ start,
235
+ end: close,
236
+ labelStart: index + 1,
237
+ labelEnd: end - 1,
238
+ destination: defined
239
+ });
240
+ }
227
241
  return close;
228
242
  }
229
243
  return i;
@@ -275,6 +289,7 @@ function endsBlock(content) {
275
289
  function scanBlocks(text) {
276
290
  const fences = [];
277
291
  const definitions = new Map();
292
+ const blocks = [];
278
293
  let paragraph = false;
279
294
  let depth = 0;
280
295
  let i = 0;
@@ -302,6 +317,11 @@ function scanBlocks(text) {
302
317
  const definition = paragraph ? null : definitionAt(text, line);
303
318
  if (definition !== null) {
304
319
  if (!definitions.has(definition.id)) definitions.set(definition.id, definition.occurrence);
320
+ blocks.push({
321
+ start: line.start,
322
+ end: definition.next,
323
+ destination: definition.occurrence
324
+ });
305
325
  i = definition.next;
306
326
  continue;
307
327
  }
@@ -310,7 +330,8 @@ function scanBlocks(text) {
310
330
  }
311
331
  return {
312
332
  fences,
313
- definitions
333
+ definitions,
334
+ blocks
314
335
  };
315
336
  }
316
337
  function fenceEndAt(spans, index) {
@@ -319,9 +340,9 @@ function fenceEndAt(spans, index) {
319
340
  }
320
341
  return undefined;
321
342
  }
322
- export function hrefOccurrences(body) {
323
- const { fences, definitions } = scanBlocks(body);
324
- const found = [];
343
+ export function linkInventory(body) {
344
+ const { fences, definitions, blocks } = scanBlocks(body);
345
+ const uses = [];
325
346
  let i = 0;
326
347
  while(i < body.length){
327
348
  const fenceEnd = fenceEndAt(fences, i);
@@ -334,17 +355,23 @@ export function hrefOccurrences(body) {
334
355
  continue;
335
356
  }
336
357
  if (body[i] === '!' && body[i + 1] === '[') {
337
- i = takeLink(body, i + 1, found, definitions);
358
+ i = takeLink(body, i + 1, i, uses, definitions);
338
359
  continue;
339
360
  }
340
361
  if (body[i] === '[') {
341
- i = takeLink(body, i, found, definitions);
362
+ i = takeLink(body, i, i, uses, definitions);
342
363
  continue;
343
364
  }
344
365
  i += 1;
345
366
  }
367
+ return {
368
+ uses,
369
+ blocks
370
+ };
371
+ }
372
+ export function hrefOccurrences(body) {
346
373
  const byStart = new Map();
347
- for (const occurrence of found)byStart.set(occurrence.start, occurrence);
374
+ for (const use of linkInventory(body).uses)byStart.set(use.destination.start, use.destination);
348
375
  return [
349
376
  ...byStart.values()
350
377
  ].sort((left, right)=>left.start - right.start);
package/dist/links.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["/workspace/packages/mdmodel/src/links.ts"],"sourcesContent":["type Fence = {\n readonly char: string;\n readonly length: number;\n readonly depth: number;\n readonly column: number;\n readonly item: boolean;\n};\n\ntype Line = {\n readonly start: number;\n readonly end: number;\n readonly content: number;\n readonly depth: number;\n readonly item: boolean;\n readonly inner: number;\n};\n\nconst TAB_STOP = 4;\nconst CLOSING_FENCE_INDENT = 3;\n\nconst HEADING = /^#{1,6}(?:[ \\t]|$)/;\nconst BREAK = /^(?:(?:\\*[ \\t]*){3,}|(?:-[ \\t]*){3,}|(?:_[ \\t]*){3,})$/;\nconst SETEXT = /^(?:=+|-+)[ \\t]*$/;\nconst LABEL = /^\\[((?:[^\\\\\\]\\n]|\\\\.)+)\\]:[ \\t]*/;\nconst DESTINATION = /^(?:(<[^<>\\n\\r]*>)|([^\\s<][^\\s]*))/d;\nconst TITLE = /^(?:\"[^\"\\n]*\"|'[^'\\n]*'|\\([^()\\n]*\\))[ \\t]*$/;\n\nfunction endOfLine(text: string, from: number): number {\n const nl = text.indexOf('\\n', from);\n return nl === -1 ? text.length : nl;\n}\n\nfunction afterLine(text: string, end: number): number {\n return end < text.length ? end + 1 : text.length;\n}\n\nfunction itemMarker(text: string, index: number, end: number): number | null {\n let i = index;\n const bullet = text[i];\n if (bullet === '-' || bullet === '*' || bullet === '+') i += 1;\n else {\n let digits = 0;\n while (digits < 9 && i + digits < end && text[i + digits]! >= '0' && text[i + digits]! <= '9')\n digits += 1;\n const delimiter = text[i + digits];\n if (digits === 0 || (delimiter !== '.' && delimiter !== ')')) return null;\n i += digits + 1;\n }\n if (i >= end) return i;\n if (text[i] !== ' ' && text[i] !== '\\t') return null;\n return i + 1;\n}\n\nfunction readLine(text: string, start: number, end: number): Line {\n let i = start;\n let depth = 0;\n let item = false;\n let inner = start;\n for (;;) {\n let spaces = 0;\n while (spaces < 3 && i < end && text[i] === ' ') {\n i += 1;\n spaces += 1;\n }\n if (i < end && text[i] === '>') {\n depth += 1;\n i += 1;\n if (i < end && text[i] === ' ') i += 1;\n inner = i;\n continue;\n }\n const after = i < end ? itemMarker(text, i, end) : null;\n if (after === null) break;\n item = true;\n i = after;\n }\n return { start, end, content: i, depth, item, inner };\n}\n\nfunction widthOf(text: string, from: number, to: number): number {\n let width = 0;\n for (let i = from; i < to; i += 1) width += text[i] === '\\t' ? TAB_STOP - (width % TAB_STOP) : 1;\n return width;\n}\n\nfunction afterIndent(text: string, line: Line): number {\n let i = line.inner;\n while (i < line.end && (text[i] === ' ' || text[i] === '\\t')) i += 1;\n return i;\n}\n\nfunction indentWidth(text: string, line: Line): number {\n return widthOf(text, line.inner, afterIndent(text, line));\n}\n\nfunction lineText(text: string, line: Line): string {\n const raw = text.slice(line.content, line.end);\n return raw.endsWith('\\r') ? raw.slice(0, -1) : raw;\n}\n\nfunction fenceRun(\n text: string,\n at: number,\n end: number,\n): { readonly char: string; readonly length: number } | null {\n const char = text[at];\n if (char !== '`' && char !== '~') return null;\n let length = 0;\n while (at + length < end && text[at + length] === char) length += 1;\n return length < 3 ? null : { char, length };\n}\n\nfunction fenceAt(text: string, line: Line): Fence | null {\n const run = fenceRun(text, line.content, line.end);\n if (run === null) return null;\n return {\n char: run.char,\n length: run.length,\n depth: line.depth,\n column: line.item ? widthOf(text, line.inner, line.content) : 0,\n item: line.item,\n };\n}\n\nfunction closesFence(text: string, line: Line, open: Fence): boolean {\n if (line.depth !== open.depth || line.item) return false;\n const indent = indentWidth(text, line);\n if (indent < open.column || indent > open.column + CLOSING_FENCE_INDENT) return false;\n const at = afterIndent(text, line);\n const run = fenceRun(text, at, line.end);\n if (run === null || run.char !== open.char || run.length < open.length) return false;\n return text.slice(at + run.length, line.end).trim() === '';\n}\n\nfunction leavesContainer(text: string, line: Line, open: Fence): boolean {\n if (line.depth < open.depth) return true;\n if (!open.item || lineText(text, line).trim() === '') return false;\n return indentWidth(text, line) < open.column;\n}\n\nfunction fenceSpan(text: string, line: Line, open: Fence): number {\n let cursor = afterLine(text, line.end);\n while (cursor < text.length) {\n const end = endOfLine(text, cursor);\n const next = readLine(text, cursor, end);\n if (leavesContainer(text, next, open)) return cursor;\n if (closesFence(text, next, open)) return afterLine(text, end);\n cursor = afterLine(text, end);\n }\n return text.length;\n}\n\nfunction skipInlineCode(text: string, index: number): number {\n let ticks = 0;\n while (text[index + ticks] === '`') ticks += 1;\n const close = text.indexOf('`'.repeat(ticks), index + ticks);\n if (close === -1) return text.length;\n return close + ticks;\n}\n\nfunction skipLinkSpace(text: string, start: number): number {\n let i = start;\n while (text[i] === ' ' || text[i] === '\\t') i += 1;\n return i;\n}\n\nfunction skipTitle(text: string, start: number): number | null {\n const open = text[start];\n if (open !== '\"' && open !== \"'\" && open !== '(') return start;\n const close = open === '(' ? ')' : open;\n let i = start + 1;\n while (i < text.length) {\n const ch = text[i]!;\n if (ch === close) return i + 1;\n if (ch === '\\n' || ch === '\\r') return null;\n i += 1;\n }\n return null;\n}\n\nfunction destination(\n text: string,\n start: number,\n): { occurrence: HrefOccurrence; end: number } | null {\n let i = skipLinkSpace(text, start + 1);\n let occurrence: HrefOccurrence;\n if (text[i] === '<') {\n const close = text.indexOf('>', i + 1);\n if (close === -1) return null;\n occurrence = { href: text.slice(i + 1, close), start: i, end: close + 1, bracketed: true };\n i = close + 1;\n } else {\n const from = i;\n let depth = 0;\n while (i < text.length) {\n const ch = text[i]!;\n if (ch === ' ' || ch === '\\t' || ch === '\\n' || ch === '\\r') break;\n if (ch === '(') depth += 1;\n else if (ch === ')') {\n if (depth === 0) break;\n depth -= 1;\n }\n i += 1;\n }\n occurrence = { href: text.slice(from, i), start: from, end: i, bracketed: false };\n }\n i = skipLinkSpace(text, i);\n const afterTitle = skipTitle(text, i);\n if (afterTitle === null) return null;\n i = skipLinkSpace(text, afterTitle);\n if (text[i] !== ')') return null;\n return { occurrence, end: i + 1 };\n}\n\nfunction labelEnd(text: string, start: number): number {\n let i = start;\n let depth = 1;\n while (i < text.length && depth > 0) {\n const ch = text[i]!;\n if (ch === '\\\\') {\n i += 2;\n continue;\n }\n if (ch === '[') depth += 1;\n else if (ch === ']') depth -= 1;\n else if (ch === '\\n' || ch === '\\r') return -1;\n i += 1;\n }\n return depth === 0 ? i : -1;\n}\n\nfunction takeLink(\n text: string,\n index: number,\n found: HrefOccurrence[],\n defs: ReadonlyMap<string, HrefOccurrence>,\n): number {\n const end = labelEnd(text, index + 1);\n if (end === -1) return index + 1;\n const label = text.slice(index + 1, end - 1);\n let i = end;\n if (text[i] === '(') {\n const dest = destination(text, i);\n if (dest === null) return index + 1;\n found.push(dest.occurrence);\n return dest.end;\n }\n if (text[i] === '[') {\n const close = labelEnd(text, i + 1);\n if (close === -1) return index + 1;\n const id = (text.slice(i + 1, close - 1) || label).trim().toLowerCase();\n const defined = defs.get(id);\n if (defined !== undefined) found.push(defined);\n return close;\n }\n return i;\n}\n\nfunction definitionDestination(text: string, from: number, to: number): HrefOccurrence | null {\n let i = from;\n while (i < to && (text[i] === ' ' || text[i] === '\\t')) i += 1;\n const match = DESTINATION.exec(text.slice(i, to));\n if (match === null) return null;\n const bracketed = match[1];\n const span = match.indices?.[1] ?? match.indices?.[2];\n const href = bracketed === undefined ? match[2] : bracketed.slice(1, -1);\n if (href === undefined || span === undefined) return null;\n return { href, start: i + span[0], end: i + span[1], bracketed: bracketed !== undefined };\n}\n\nfunction definitionAt(\n text: string,\n line: Line,\n): { id: string; occurrence: HrefOccurrence; next: number } | null {\n const head = LABEL.exec(lineText(text, line));\n if (head === null) return null;\n const id = head[1]?.trim().toLowerCase();\n if (id === undefined || id === '') return null;\n const from = line.content + head[0].length;\n let occurrence = definitionDestination(text, from, line.end);\n let next = afterLine(text, line.end);\n if (occurrence === null) {\n if (text.slice(from, line.end).trim() !== '' || next >= text.length) return null;\n const end = endOfLine(text, next);\n occurrence = definitionDestination(text, readLine(text, next, end).content, end);\n if (occurrence === null) return null;\n next = afterLine(text, end);\n }\n if (next < text.length) {\n const end = endOfLine(text, next);\n if (TITLE.test(lineText(text, readLine(text, next, end)))) next = afterLine(text, end);\n }\n return { id, occurrence, next };\n}\n\nfunction endsBlock(content: string): boolean {\n return HEADING.test(content) || BREAK.test(content) || SETEXT.test(content);\n}\n\nfunction scanBlocks(text: string): {\n readonly fences: readonly (readonly [number, number])[];\n readonly definitions: ReadonlyMap<string, HrefOccurrence>;\n} {\n const fences: [number, number][] = [];\n const definitions = new Map<string, HrefOccurrence>();\n let paragraph = false;\n let depth = 0;\n let i = 0;\n while (i < text.length) {\n const line = readLine(text, i, endOfLine(text, i));\n if (line.depth !== depth || line.item) paragraph = false;\n depth = line.depth;\n const open = fenceAt(text, line);\n if (open !== null) {\n const stop = fenceSpan(text, line, open);\n fences.push([i, stop]);\n paragraph = false;\n i = stop;\n continue;\n }\n const content = lineText(text, line);\n if (content.trim() === '') {\n paragraph = false;\n i = afterLine(text, line.end);\n continue;\n }\n const definition = paragraph ? null : definitionAt(text, line);\n if (definition !== null) {\n if (!definitions.has(definition.id)) definitions.set(definition.id, definition.occurrence);\n i = definition.next;\n continue;\n }\n paragraph = !endsBlock(content);\n i = afterLine(text, line.end);\n }\n return { fences, definitions };\n}\n\nfunction fenceEndAt(\n spans: readonly (readonly [number, number])[],\n index: number,\n): number | undefined {\n for (const [start, end] of spans) {\n if (index >= start && index < end) return end;\n }\n return undefined;\n}\n\nexport type HrefOccurrence = {\n readonly href: string;\n readonly start: number;\n readonly end: number;\n readonly bracketed: boolean;\n};\n\nexport function hrefOccurrences(body: string): readonly HrefOccurrence[] {\n const { fences, definitions } = scanBlocks(body);\n const found: HrefOccurrence[] = [];\n let i = 0;\n while (i < body.length) {\n const fenceEnd = fenceEndAt(fences, i);\n if (fenceEnd !== undefined) {\n i = fenceEnd;\n continue;\n }\n if (body[i] === '`') {\n i = skipInlineCode(body, i);\n continue;\n }\n if (body[i] === '!' && body[i + 1] === '[') {\n i = takeLink(body, i + 1, found, definitions);\n continue;\n }\n if (body[i] === '[') {\n i = takeLink(body, i, found, definitions);\n continue;\n }\n i += 1;\n }\n const byStart = new Map<number, HrefOccurrence>();\n for (const occurrence of found) byStart.set(occurrence.start, occurrence);\n return [...byStart.values()].sort((left, right) => left.start - right.start);\n}\n"],"names":["TAB_STOP","CLOSING_FENCE_INDENT","HEADING","BREAK","SETEXT","LABEL","DESTINATION","TITLE","endOfLine","text","from","nl","indexOf","length","afterLine","end","itemMarker","index","i","bullet","digits","delimiter","readLine","start","depth","item","inner","spaces","after","content","widthOf","to","width","afterIndent","line","indentWidth","lineText","raw","slice","endsWith","fenceRun","at","char","fenceAt","run","column","closesFence","open","indent","trim","leavesContainer","fenceSpan","cursor","next","skipInlineCode","ticks","close","repeat","skipLinkSpace","skipTitle","ch","destination","occurrence","href","bracketed","afterTitle","labelEnd","takeLink","found","defs","label","dest","push","id","toLowerCase","defined","get","undefined","definitionDestination","match","exec","span","indices","definitionAt","head","test","endsBlock","scanBlocks","fences","definitions","Map","paragraph","stop","definition","has","set","fenceEndAt","spans","hrefOccurrences","body","fenceEnd","byStart","values","sort","left","right"],"mappings":"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"}
1
+ {"version":3,"sources":["/workspace/packages/mdmodel/src/links.ts"],"sourcesContent":["type Fence = {\n readonly char: string;\n readonly length: number;\n readonly depth: number;\n readonly column: number;\n readonly item: boolean;\n};\n\ntype Line = {\n readonly start: number;\n readonly end: number;\n readonly content: number;\n readonly depth: number;\n readonly item: boolean;\n readonly inner: number;\n};\n\nconst TAB_STOP = 4;\nconst CLOSING_FENCE_INDENT = 3;\n\nconst HEADING = /^#{1,6}(?:[ \\t]|$)/;\nconst BREAK = /^(?:(?:\\*[ \\t]*){3,}|(?:-[ \\t]*){3,}|(?:_[ \\t]*){3,})$/;\nconst SETEXT = /^(?:=+|-+)[ \\t]*$/;\nconst LABEL = /^\\[((?:[^\\\\\\]\\n]|\\\\.)+)\\]:[ \\t]*/;\nconst DESTINATION = /^(?:(<[^<>\\n\\r]*>)|([^\\s<][^\\s]*))/d;\nconst TITLE = /^(?:\"[^\"\\n]*\"|'[^'\\n]*'|\\([^()\\n]*\\))[ \\t]*$/;\n\nfunction endOfLine(text: string, from: number): number {\n const nl = text.indexOf('\\n', from);\n return nl === -1 ? text.length : nl;\n}\n\nfunction afterLine(text: string, end: number): number {\n return end < text.length ? end + 1 : text.length;\n}\n\nfunction itemMarker(text: string, index: number, end: number): number | null {\n let i = index;\n const bullet = text[i];\n if (bullet === '-' || bullet === '*' || bullet === '+') i += 1;\n else {\n let digits = 0;\n while (digits < 9 && i + digits < end && text[i + digits]! >= '0' && text[i + digits]! <= '9')\n digits += 1;\n const delimiter = text[i + digits];\n if (digits === 0 || (delimiter !== '.' && delimiter !== ')')) return null;\n i += digits + 1;\n }\n if (i >= end) return i;\n if (text[i] !== ' ' && text[i] !== '\\t') return null;\n return i + 1;\n}\n\nfunction readLine(text: string, start: number, end: number): Line {\n let i = start;\n let depth = 0;\n let item = false;\n let inner = start;\n for (;;) {\n let spaces = 0;\n while (spaces < 3 && i < end && text[i] === ' ') {\n i += 1;\n spaces += 1;\n }\n if (i < end && text[i] === '>') {\n depth += 1;\n i += 1;\n if (i < end && text[i] === ' ') i += 1;\n inner = i;\n continue;\n }\n const after = i < end ? itemMarker(text, i, end) : null;\n if (after === null) break;\n item = true;\n i = after;\n }\n return { start, end, content: i, depth, item, inner };\n}\n\nfunction widthOf(text: string, from: number, to: number): number {\n let width = 0;\n for (let i = from; i < to; i += 1) width += text[i] === '\\t' ? TAB_STOP - (width % TAB_STOP) : 1;\n return width;\n}\n\nfunction afterIndent(text: string, line: Line): number {\n let i = line.inner;\n while (i < line.end && (text[i] === ' ' || text[i] === '\\t')) i += 1;\n return i;\n}\n\nfunction indentWidth(text: string, line: Line): number {\n return widthOf(text, line.inner, afterIndent(text, line));\n}\n\nfunction lineText(text: string, line: Line): string {\n const raw = text.slice(line.content, line.end);\n return raw.endsWith('\\r') ? raw.slice(0, -1) : raw;\n}\n\nfunction fenceRun(\n text: string,\n at: number,\n end: number,\n): { readonly char: string; readonly length: number } | null {\n const char = text[at];\n if (char !== '`' && char !== '~') return null;\n let length = 0;\n while (at + length < end && text[at + length] === char) length += 1;\n return length < 3 ? null : { char, length };\n}\n\nfunction fenceAt(text: string, line: Line): Fence | null {\n const run = fenceRun(text, line.content, line.end);\n if (run === null) return null;\n return {\n char: run.char,\n length: run.length,\n depth: line.depth,\n column: line.item ? widthOf(text, line.inner, line.content) : 0,\n item: line.item,\n };\n}\n\nfunction closesFence(text: string, line: Line, open: Fence): boolean {\n if (line.depth !== open.depth || line.item) return false;\n const indent = indentWidth(text, line);\n if (indent < open.column || indent > open.column + CLOSING_FENCE_INDENT) return false;\n const at = afterIndent(text, line);\n const run = fenceRun(text, at, line.end);\n if (run === null || run.char !== open.char || run.length < open.length) return false;\n return text.slice(at + run.length, line.end).trim() === '';\n}\n\nfunction leavesContainer(text: string, line: Line, open: Fence): boolean {\n if (line.depth < open.depth) return true;\n if (!open.item || lineText(text, line).trim() === '') return false;\n return indentWidth(text, line) < open.column;\n}\n\nfunction fenceSpan(text: string, line: Line, open: Fence): number {\n let cursor = afterLine(text, line.end);\n while (cursor < text.length) {\n const end = endOfLine(text, cursor);\n const next = readLine(text, cursor, end);\n if (leavesContainer(text, next, open)) return cursor;\n if (closesFence(text, next, open)) return afterLine(text, end);\n cursor = afterLine(text, end);\n }\n return text.length;\n}\n\nfunction skipInlineCode(text: string, index: number): number {\n let ticks = 0;\n while (text[index + ticks] === '`') ticks += 1;\n const close = text.indexOf('`'.repeat(ticks), index + ticks);\n if (close === -1) return text.length;\n return close + ticks;\n}\n\nfunction skipLinkSpace(text: string, start: number): number {\n let i = start;\n while (text[i] === ' ' || text[i] === '\\t') i += 1;\n return i;\n}\n\nfunction skipTitle(text: string, start: number): number | null {\n const open = text[start];\n if (open !== '\"' && open !== \"'\" && open !== '(') return start;\n const close = open === '(' ? ')' : open;\n let i = start + 1;\n while (i < text.length) {\n const ch = text[i]!;\n if (ch === close) return i + 1;\n if (ch === '\\n' || ch === '\\r') return null;\n i += 1;\n }\n return null;\n}\n\nfunction destination(\n text: string,\n start: number,\n): { occurrence: HrefOccurrence; end: number } | null {\n let i = skipLinkSpace(text, start + 1);\n let occurrence: HrefOccurrence;\n if (text[i] === '<') {\n const close = text.indexOf('>', i + 1);\n if (close === -1) return null;\n occurrence = { href: text.slice(i + 1, close), start: i, end: close + 1, bracketed: true };\n i = close + 1;\n } else {\n const from = i;\n let depth = 0;\n while (i < text.length) {\n const ch = text[i]!;\n if (ch === ' ' || ch === '\\t' || ch === '\\n' || ch === '\\r') break;\n if (ch === '(') depth += 1;\n else if (ch === ')') {\n if (depth === 0) break;\n depth -= 1;\n }\n i += 1;\n }\n occurrence = { href: text.slice(from, i), start: from, end: i, bracketed: false };\n }\n i = skipLinkSpace(text, i);\n const afterTitle = skipTitle(text, i);\n if (afterTitle === null) return null;\n i = skipLinkSpace(text, afterTitle);\n if (text[i] !== ')') return null;\n return { occurrence, end: i + 1 };\n}\n\nfunction labelEnd(text: string, start: number): number {\n let i = start;\n let depth = 1;\n while (i < text.length && depth > 0) {\n const ch = text[i]!;\n if (ch === '\\\\') {\n i += 2;\n continue;\n }\n if (ch === '[') depth += 1;\n else if (ch === ']') depth -= 1;\n else if (ch === '\\n' || ch === '\\r') return -1;\n i += 1;\n }\n return depth === 0 ? i : -1;\n}\n\nfunction takeLink(\n text: string,\n index: number,\n start: number,\n found: LinkUse[],\n defs: ReadonlyMap<string, HrefOccurrence>,\n): number {\n const end = labelEnd(text, index + 1);\n if (end === -1) return index + 1;\n const label = text.slice(index + 1, end - 1);\n let i = end;\n if (text[i] === '(') {\n const dest = destination(text, i);\n if (dest === null) return index + 1;\n found.push({\n start,\n end: dest.end,\n labelStart: index + 1,\n labelEnd: end - 1,\n destination: dest.occurrence,\n });\n return dest.end;\n }\n if (text[i] === '[') {\n const close = labelEnd(text, i + 1);\n if (close === -1) return index + 1;\n const id = (text.slice(i + 1, close - 1) || label).trim().toLowerCase();\n const defined = defs.get(id);\n if (defined !== undefined) {\n found.push({\n start,\n end: close,\n labelStart: index + 1,\n labelEnd: end - 1,\n destination: defined,\n });\n }\n return close;\n }\n return i;\n}\n\nfunction definitionDestination(text: string, from: number, to: number): HrefOccurrence | null {\n let i = from;\n while (i < to && (text[i] === ' ' || text[i] === '\\t')) i += 1;\n const match = DESTINATION.exec(text.slice(i, to));\n if (match === null) return null;\n const bracketed = match[1];\n const span = match.indices?.[1] ?? match.indices?.[2];\n const href = bracketed === undefined ? match[2] : bracketed.slice(1, -1);\n if (href === undefined || span === undefined) return null;\n return { href, start: i + span[0], end: i + span[1], bracketed: bracketed !== undefined };\n}\n\nfunction definitionAt(\n text: string,\n line: Line,\n): { id: string; occurrence: HrefOccurrence; next: number } | null {\n const head = LABEL.exec(lineText(text, line));\n if (head === null) return null;\n const id = head[1]?.trim().toLowerCase();\n if (id === undefined || id === '') return null;\n const from = line.content + head[0].length;\n let occurrence = definitionDestination(text, from, line.end);\n let next = afterLine(text, line.end);\n if (occurrence === null) {\n if (text.slice(from, line.end).trim() !== '' || next >= text.length) return null;\n const end = endOfLine(text, next);\n occurrence = definitionDestination(text, readLine(text, next, end).content, end);\n if (occurrence === null) return null;\n next = afterLine(text, end);\n }\n if (next < text.length) {\n const end = endOfLine(text, next);\n if (TITLE.test(lineText(text, readLine(text, next, end)))) next = afterLine(text, end);\n }\n return { id, occurrence, next };\n}\n\nfunction endsBlock(content: string): boolean {\n return HEADING.test(content) || BREAK.test(content) || SETEXT.test(content);\n}\n\nfunction scanBlocks(text: string): {\n readonly fences: readonly (readonly [number, number])[];\n readonly definitions: ReadonlyMap<string, HrefOccurrence>;\n readonly blocks: readonly DefinitionBlock[];\n} {\n const fences: [number, number][] = [];\n const definitions = new Map<string, HrefOccurrence>();\n const blocks: DefinitionBlock[] = [];\n let paragraph = false;\n let depth = 0;\n let i = 0;\n while (i < text.length) {\n const line = readLine(text, i, endOfLine(text, i));\n if (line.depth !== depth || line.item) paragraph = false;\n depth = line.depth;\n const open = fenceAt(text, line);\n if (open !== null) {\n const stop = fenceSpan(text, line, open);\n fences.push([i, stop]);\n paragraph = false;\n i = stop;\n continue;\n }\n const content = lineText(text, line);\n if (content.trim() === '') {\n paragraph = false;\n i = afterLine(text, line.end);\n continue;\n }\n const definition = paragraph ? null : definitionAt(text, line);\n if (definition !== null) {\n if (!definitions.has(definition.id)) definitions.set(definition.id, definition.occurrence);\n blocks.push({ start: line.start, end: definition.next, destination: definition.occurrence });\n i = definition.next;\n continue;\n }\n paragraph = !endsBlock(content);\n i = afterLine(text, line.end);\n }\n return { fences, definitions, blocks };\n}\n\nfunction fenceEndAt(\n spans: readonly (readonly [number, number])[],\n index: number,\n): number | undefined {\n for (const [start, end] of spans) {\n if (index >= start && index < end) return end;\n }\n return undefined;\n}\n\nexport type HrefOccurrence = {\n readonly href: string;\n readonly start: number;\n readonly end: number;\n readonly bracketed: boolean;\n};\n\nexport type LinkUse = {\n readonly start: number;\n readonly end: number;\n readonly labelStart: number;\n readonly labelEnd: number;\n readonly destination: HrefOccurrence;\n};\n\nexport type DefinitionBlock = {\n readonly start: number;\n readonly end: number;\n readonly destination: HrefOccurrence;\n};\n\nexport type LinkInventory = {\n readonly uses: readonly LinkUse[];\n readonly blocks: readonly DefinitionBlock[];\n};\n\nexport function linkInventory(body: string): LinkInventory {\n const { fences, definitions, blocks } = scanBlocks(body);\n const uses: LinkUse[] = [];\n let i = 0;\n while (i < body.length) {\n const fenceEnd = fenceEndAt(fences, i);\n if (fenceEnd !== undefined) {\n i = fenceEnd;\n continue;\n }\n if (body[i] === '`') {\n i = skipInlineCode(body, i);\n continue;\n }\n if (body[i] === '!' && body[i + 1] === '[') {\n i = takeLink(body, i + 1, i, uses, definitions);\n continue;\n }\n if (body[i] === '[') {\n i = takeLink(body, i, i, uses, definitions);\n continue;\n }\n i += 1;\n }\n return { uses, blocks };\n}\n\nexport function hrefOccurrences(body: string): readonly HrefOccurrence[] {\n const byStart = new Map<number, HrefOccurrence>();\n for (const use of linkInventory(body).uses) byStart.set(use.destination.start, use.destination);\n return [...byStart.values()].sort((left, right) => left.start - right.start);\n}\n"],"names":["TAB_STOP","CLOSING_FENCE_INDENT","HEADING","BREAK","SETEXT","LABEL","DESTINATION","TITLE","endOfLine","text","from","nl","indexOf","length","afterLine","end","itemMarker","index","i","bullet","digits","delimiter","readLine","start","depth","item","inner","spaces","after","content","widthOf","to","width","afterIndent","line","indentWidth","lineText","raw","slice","endsWith","fenceRun","at","char","fenceAt","run","column","closesFence","open","indent","trim","leavesContainer","fenceSpan","cursor","next","skipInlineCode","ticks","close","repeat","skipLinkSpace","skipTitle","ch","destination","occurrence","href","bracketed","afterTitle","labelEnd","takeLink","found","defs","label","dest","push","labelStart","id","toLowerCase","defined","get","undefined","definitionDestination","match","exec","span","indices","definitionAt","head","test","endsBlock","scanBlocks","fences","definitions","Map","blocks","paragraph","stop","definition","has","set","fenceEndAt","spans","linkInventory","body","uses","fenceEnd","hrefOccurrences","byStart","use","values","sort","left","right"],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { Document, Projection, ProjectionContext } from './types.js';
2
+ export declare function project(document: Document, file: string, context?: ProjectionContext): Projection;
3
+ //# sourceMappingURL=project.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"project.d.ts","sourceRoot":"","sources":["../src/project.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,QAAQ,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AA8C1E,wBAAgB,OAAO,CACrB,QAAQ,EAAE,QAAQ,EAClB,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE,iBAA2C,GACnD,UAAU,CA0BZ"}
@@ -0,0 +1,96 @@
1
+ import { readDefinition } from './definition.js';
2
+ import { parse } from './document.js';
3
+ import { linkInventory } from './links.js';
4
+ import { classifyHref } from './paths.js';
5
+ import { refusal } from './refuse.js';
6
+ import { headingSpans } from './sections.js';
7
+ import { bodyStartOf } from './yaml.js';
8
+ function internalEdits(body, file, documentTargets) {
9
+ const inventory = linkInventory(body);
10
+ const internal = new Set();
11
+ const edits = [];
12
+ for (const use of inventory.uses){
13
+ const resolved = classifyHref(file, use.destination.href);
14
+ if (resolved.kind !== 'path' || !documentTargets.has(resolved.path)) continue;
15
+ internal.add(use.destination.start);
16
+ edits.push({
17
+ start: use.start,
18
+ end: use.end,
19
+ text: body.slice(use.labelStart, use.labelEnd)
20
+ });
21
+ }
22
+ for (const block of inventory.blocks){
23
+ if (!internal.has(block.destination.start)) continue;
24
+ edits.push({
25
+ start: block.start,
26
+ end: block.end,
27
+ text: ''
28
+ });
29
+ }
30
+ return edits.sort((left, right)=>left.start - right.start);
31
+ }
32
+ function projected(body, spans, edits) {
33
+ let text = '';
34
+ for (const [from, to] of spans){
35
+ let cursor = from;
36
+ for (const edit of edits){
37
+ if (edit.start < cursor || edit.end > to) continue;
38
+ text += body.slice(cursor, edit.start) + edit.text;
39
+ cursor = edit.end;
40
+ }
41
+ text += body.slice(cursor, to);
42
+ }
43
+ return text;
44
+ }
45
+ export function project(document, file, context = {
46
+ documentTargets: []
47
+ }) {
48
+ const bodyStart = bodyStartOf(document.content);
49
+ const body = document.content.slice(bodyStart);
50
+ const edits = internalEdits(body, file, new Set(context.documentTargets));
51
+ const declared = document.type;
52
+ if (declared === undefined) {
53
+ return {
54
+ ok: true,
55
+ text: projected(body, [
56
+ [
57
+ 0,
58
+ body.length
59
+ ]
60
+ ], edits)
61
+ };
62
+ }
63
+ const source = context.definition;
64
+ if (source === undefined) {
65
+ return {
66
+ ok: false,
67
+ refusal: refusal(file, 'unknown-type', {
68
+ location: 'type'
69
+ })
70
+ };
71
+ }
72
+ const definitionFile = `types/${declared}.md`;
73
+ const parsed = parse(source, definitionFile);
74
+ if (!parsed.ok) return {
75
+ ok: false,
76
+ refusal: parsed.refusal
77
+ };
78
+ const definition = readDefinition(definitionFile, parsed.document);
79
+ if ('rule' in definition) return {
80
+ ok: false,
81
+ refusal: definition
82
+ };
83
+ const free = new Set(Object.entries(definition.sections).flatMap(([name, spec])=>spec.kind === 'text' ? [
84
+ name
85
+ ] : []));
86
+ const spans = headingSpans(document.content, bodyStart).filter((span)=>free.has(span.name)).map((span)=>[
87
+ span.headingStart - bodyStart,
88
+ span.bodyEnd - bodyStart
89
+ ]);
90
+ return {
91
+ ok: true,
92
+ text: projected(body, spans, edits)
93
+ };
94
+ }
95
+
96
+ //# sourceMappingURL=project.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["/workspace/packages/mdmodel/src/project.ts"],"sourcesContent":["import { readDefinition } from './definition.js';\nimport { parse } from './document.js';\nimport { linkInventory } from './links.js';\nimport { classifyHref } from './paths.js';\nimport { refusal } from './refuse.js';\nimport { headingSpans } from './sections.js';\nimport type { Document, Projection, ProjectionContext } from './types.js';\nimport { bodyStartOf } from './yaml.js';\n\ntype Span = readonly [number, number];\n\ntype Edit = {\n readonly start: number;\n readonly end: number;\n readonly text: string;\n};\n\nfunction internalEdits(body: string, file: string, documentTargets: ReadonlySet<string>): Edit[] {\n const inventory = linkInventory(body);\n const internal = new Set<number>();\n const edits: Edit[] = [];\n for (const use of inventory.uses) {\n const resolved = classifyHref(file, use.destination.href);\n if (resolved.kind !== 'path' || !documentTargets.has(resolved.path)) continue;\n internal.add(use.destination.start);\n edits.push({\n start: use.start,\n end: use.end,\n text: body.slice(use.labelStart, use.labelEnd),\n });\n }\n for (const block of inventory.blocks) {\n if (!internal.has(block.destination.start)) continue;\n edits.push({ start: block.start, end: block.end, text: '' });\n }\n return edits.sort((left, right) => left.start - right.start);\n}\n\nfunction projected(body: string, spans: readonly Span[], edits: readonly Edit[]): string {\n let text = '';\n for (const [from, to] of spans) {\n let cursor = from;\n for (const edit of edits) {\n if (edit.start < cursor || edit.end > to) continue;\n text += body.slice(cursor, edit.start) + edit.text;\n cursor = edit.end;\n }\n text += body.slice(cursor, to);\n }\n return text;\n}\n\nexport function project(\n document: Document,\n file: string,\n context: ProjectionContext = { documentTargets: [] },\n): Projection {\n const bodyStart = bodyStartOf(document.content);\n const body = document.content.slice(bodyStart);\n const edits = internalEdits(body, file, new Set(context.documentTargets));\n const declared = document.type;\n if (declared === undefined) {\n return { ok: true, text: projected(body, [[0, body.length]], edits) };\n }\n const source = context.definition;\n if (source === undefined) {\n return { ok: false, refusal: refusal(file, 'unknown-type', { location: 'type' }) };\n }\n const definitionFile = `types/${declared}.md`;\n const parsed = parse(source, definitionFile);\n if (!parsed.ok) return { ok: false, refusal: parsed.refusal };\n const definition = readDefinition(definitionFile, parsed.document);\n if ('rule' in definition) return { ok: false, refusal: definition };\n const free = new Set(\n Object.entries(definition.sections).flatMap(([name, spec]) =>\n spec.kind === 'text' ? [name] : [],\n ),\n );\n const spans = headingSpans(document.content, bodyStart)\n .filter((span) => free.has(span.name))\n .map((span): Span => [span.headingStart - bodyStart, span.bodyEnd - bodyStart]);\n return { ok: true, text: projected(body, spans, edits) };\n}\n"],"names":["readDefinition","parse","linkInventory","classifyHref","refusal","headingSpans","bodyStartOf","internalEdits","body","file","documentTargets","inventory","internal","Set","edits","use","uses","resolved","destination","href","kind","has","path","add","start","push","end","text","slice","labelStart","labelEnd","block","blocks","sort","left","right","projected","spans","from","to","cursor","edit","project","document","context","bodyStart","content","declared","type","undefined","ok","length","source","definition","location","definitionFile","parsed","free","Object","entries","sections","flatMap","name","spec","filter","span","map","headingStart","bodyEnd"],"mappings":"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"}
package/dist/types.d.ts CHANGED
@@ -2,10 +2,15 @@ export type Library = Readonly<Record<string, string>>;
2
2
  export type ValidationContext = {
3
3
  readonly binaryTargets: readonly string[];
4
4
  };
5
+ export type ProjectionContext = {
6
+ readonly documentTargets: readonly string[];
7
+ readonly definition?: string;
8
+ };
5
9
  export type Document = {
6
10
  readonly content: string;
7
11
  readonly type: string | undefined;
8
12
  readonly fields: Readonly<Record<string, unknown>>;
13
+ readonly preamble: string;
9
14
  readonly sections: Readonly<Record<string, string>>;
10
15
  };
11
16
  export type Refusal = {
@@ -20,6 +25,13 @@ export type Validation = {
20
25
  } | ({
21
26
  readonly verdict: 'refuse';
22
27
  } & Refusal);
28
+ export type Projection = {
29
+ readonly ok: true;
30
+ readonly text: string;
31
+ } | {
32
+ readonly ok: false;
33
+ readonly refusal: Refusal;
34
+ };
23
35
  export type ParseResult = {
24
36
  readonly ok: true;
25
37
  readonly document: Document;
@@ -41,6 +53,10 @@ export type Change = {
41
53
  readonly name: string;
42
54
  readonly value: string;
43
55
  readonly prior: string;
56
+ } | {
57
+ readonly kind: 'preamble';
58
+ readonly value: string;
59
+ readonly prior: string;
44
60
  } | {
45
61
  readonly kind: 'link';
46
62
  readonly href: string;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,OAAO,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;AAEvD,MAAM,MAAM,iBAAiB,GAAG;IAC9B,QAAQ,CAAC,aAAa,EAAE,SAAS,MAAM,EAAE,CAAC;CAC3C,CAAC;AAEF,MAAM,MAAM,QAAQ,GAAG;IACrB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;IAClC,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IACnD,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;CACrD,CAAC;AAEF,MAAM,MAAM,OAAO,GAAG;IACpB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,SAAS,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CACxC,CAAC;AAEF,MAAM,MAAM,UAAU,GAClB;IAAE,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAA;CAAE,GAC9B,CAAC;IAAE,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAA;CAAE,GAAG,OAAO,CAAC,CAAC;AAE/C,MAAM,MAAM,WAAW,GACnB;IAAE,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC;IAAC,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAA;CAAE,GAClD;IAAE,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAAC;AAEtD,MAAM,MAAM,IAAI,GAAG;IACjB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF,MAAM,MAAM,MAAM,GACd;IACE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;CACzB,GACD;IACE,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB,GACD;IACE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;CACrB,CAAC;AAEN,MAAM,MAAM,WAAW,GACnB;IAAE,QAAQ,CAAC,OAAO,EAAE,SAAS,CAAC;IAAC,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAA;CAAE,GAC5D;IAAE,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAA;CAAE,GAC1D;IAAE,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAAC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/dist/types.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["/workspace/packages/mdmodel/src/types.ts"],"sourcesContent":["export type Library = Readonly<Record<string, string>>;\n\nexport type ValidationContext = {\n readonly binaryTargets: readonly string[];\n};\n\nexport type Document = {\n readonly content: string;\n readonly type: string | undefined;\n readonly fields: Readonly<Record<string, unknown>>;\n readonly sections: Readonly<Record<string, string>>;\n};\n\nexport type Refusal = {\n readonly file: string;\n readonly rule: string;\n readonly version: string;\n readonly location?: string;\n readonly referrers?: readonly string[];\n};\n\nexport type Validation =\n | { readonly verdict: 'accept' }\n | ({ readonly verdict: 'refuse' } & Refusal);\n\nexport type ParseResult =\n | { readonly ok: true; readonly document: Document }\n | { readonly ok: false; readonly refusal: Refusal };\n\nexport type Link = {\n readonly href: string;\n readonly target: string;\n};\n\nexport type Change =\n | {\n readonly kind: 'field';\n readonly name: string;\n readonly value: unknown;\n readonly prior: unknown;\n }\n | {\n readonly kind: 'section';\n readonly name: string;\n readonly value: string;\n readonly prior: string;\n }\n | {\n readonly kind: 'link';\n readonly href: string;\n readonly to: string;\n };\n\nexport type ApplyResult =\n | { readonly outcome: 'applied'; readonly document: Document }\n | { readonly outcome: 'stale'; readonly document: Document }\n | { readonly outcome: 'refuse'; readonly refusal: Refusal };\n"],"names":[],"mappings":"AAqDA,WAG8D"}
1
+ {"version":3,"sources":["/workspace/packages/mdmodel/src/types.ts"],"sourcesContent":["export type Library = Readonly<Record<string, string>>;\n\nexport type ValidationContext = {\n readonly binaryTargets: readonly string[];\n};\n\nexport type ProjectionContext = {\n readonly documentTargets: readonly string[];\n readonly definition?: string;\n};\n\nexport type Document = {\n readonly content: string;\n readonly type: string | undefined;\n readonly fields: Readonly<Record<string, unknown>>;\n readonly preamble: string;\n readonly sections: Readonly<Record<string, string>>;\n};\n\nexport type Refusal = {\n readonly file: string;\n readonly rule: string;\n readonly version: string;\n readonly location?: string;\n readonly referrers?: readonly string[];\n};\n\nexport type Validation =\n | { readonly verdict: 'accept' }\n | ({ readonly verdict: 'refuse' } & Refusal);\n\nexport type Projection =\n | { readonly ok: true; readonly text: string }\n | { readonly ok: false; readonly refusal: Refusal };\n\nexport type ParseResult =\n | { readonly ok: true; readonly document: Document }\n | { readonly ok: false; readonly refusal: Refusal };\n\nexport type Link = {\n readonly href: string;\n readonly target: string;\n};\n\nexport type Change =\n | {\n readonly kind: 'field';\n readonly name: string;\n readonly value: unknown;\n readonly prior: unknown;\n }\n | {\n readonly kind: 'section';\n readonly name: string;\n readonly value: string;\n readonly prior: string;\n }\n | {\n readonly kind: 'preamble';\n readonly value: string;\n readonly prior: string;\n }\n | {\n readonly kind: 'link';\n readonly href: string;\n readonly to: string;\n };\n\nexport type ApplyResult =\n | { readonly outcome: 'applied'; readonly document: Document }\n | { readonly outcome: 'stale'; readonly document: Document }\n | { readonly outcome: 'refuse'; readonly refusal: Refusal };\n"],"names":[],"mappings":"AAoEA,WAG8D"}
package/dist/version.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- export declare const VERSION = "0.1.0-alpha.4";
1
+ export declare const VERSION = "0.1.0-alpha.6";
2
2
  //# sourceMappingURL=version.d.ts.map
package/dist/version.js CHANGED
@@ -1,3 +1,3 @@
1
- export const VERSION = '0.1.0-alpha.4';
1
+ export const VERSION = '0.1.0-alpha.6';
2
2
 
3
3
  //# sourceMappingURL=version.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["/workspace/packages/mdmodel/src/version.ts"],"sourcesContent":["export const VERSION = '0.1.0-alpha.4';\n"],"names":["VERSION"],"mappings":"AAAA,OAAO,MAAMA,UAAU,gBAAgB"}
1
+ {"version":3,"sources":["/workspace/packages/mdmodel/src/version.ts"],"sourcesContent":["export const VERSION = '0.1.0-alpha.6';\n"],"names":["VERSION"],"mappings":"AAAA,OAAO,MAAMA,UAAU,gBAAgB"}
@@ -7,6 +7,38 @@ compare `validate(library, context)` against `expected.json`. Text-only cases
7
7
  carry `{ "binaryTargets": [] }`. Binary targets are membership declarations;
8
8
  the corpus supplies no binary bytes or placeholder Documents.
9
9
 
10
+ ## Preamble operations
11
+
12
+ `preamble.json` exercises the public parse/apply/serialize seam separately from
13
+ the validation Libraries. Each row carries `name`, exact `source`, expected
14
+ `preamble`, replacement `value`, and exact serialized `expected` output. Parse
15
+ the source, compare `Document.preamble`, apply
16
+ `{ kind: 'preamble', prior: preamble, value }`, and compare the serialized result.
17
+ These cases cover empty and heading-free Documents, frontmatter boundaries,
18
+ fenced headings, newline/source preservation, and the Runtime moved-export
19
+ example from its [approved candidate](https://github.com/agentshouse/mdruntime/blob/ed0a6f0b95317d9ade846ec1f2871a3997519bc4/test/rooms.ts).
20
+ The package check runs every row in ESM, CommonJS, and the browser-target VM
21
+ bundle, also checking stale priors, immutable Documents, and conversion of the
22
+ Runtime link rewrite into a preamble operation through public APIs.
23
+
24
+ ## Projection
25
+
26
+ `projection.json` exercises the public `project` seam separately from the
27
+ validation Libraries. It carries `definitions`, the MD Type definition sources
28
+ keyed by a label, and `cases`. Each case carries `name`, the `file` the Document
29
+ sits at, exact `source`, the `documentTargets` the collection holds as Markdown
30
+ documents, the `definition` label or `null`, an optional `move`, and `expected`.
31
+ A case carrying a `move` lists both the Path its target sits at and the Path the
32
+ move takes it to, because the collection holds that target either way.
33
+
34
+ Parse the source, call `project(document, file, context)` with
35
+ `{ documentTargets, definition }`, and compare against `expected`, replacing the
36
+ `<package-version>` token in a refusal the same way `cases/` does. Then check
37
+ that the source is unchanged. Where `move` is present, apply
38
+ `{ kind: 'link', href, to }`, confirm the serialized Document changed, and
39
+ confirm the projection of the moved Document is still exactly `expected`: a
40
+ moved Path rewrites destinations and leaves the projection alone.
41
+
10
42
  ## Verdict format
11
43
 
12
44
  Acceptance:
@@ -0,0 +1,107 @@
1
+ [
2
+ {
3
+ "name": "CRLF frontmatter and H2 closing syntax",
4
+ "source": "---\r\ntype: note\r\ntitle: \"Kept\" # syntax\r\n---\r\n\r\n# Title\r\nIntro.\r\n\r\n## Body ###\r\nKept.\r\n",
5
+ "preamble": "\r\n# Title\r\nIntro.\r\n\r\n",
6
+ "value": "\r\nNew intro.\r\n\r\n",
7
+ "expected": "---\r\ntype: note\r\ntitle: \"Kept\" # syntax\r\n---\r\n\r\nNew intro.\r\n\r\n## Body ###\r\nKept.\r\n"
8
+ },
9
+ {
10
+ "name": "untyped document without sections",
11
+ "source": "# Title\nAll body text.",
12
+ "preamble": "# Title\nAll body text.",
13
+ "value": "Replaced without final newline.",
14
+ "expected": "Replaced without final newline."
15
+ },
16
+ {
17
+ "name": "typed document without sections",
18
+ "source": "---\ntype: note\n---\n# Title\nAll body text.",
19
+ "preamble": "# Title\nAll body text.",
20
+ "value": "New body.\n",
21
+ "expected": "---\ntype: note\n---\nNew body.\n"
22
+ },
23
+ {
24
+ "name": "empty document",
25
+ "source": "",
26
+ "preamble": "",
27
+ "value": "First text.\n",
28
+ "expected": "First text.\n"
29
+ },
30
+ {
31
+ "name": "frontmatter only",
32
+ "source": "---\ntype: note\n---\n",
33
+ "preamble": "",
34
+ "value": "First text.\n",
35
+ "expected": "---\ntype: note\n---\nFirst text.\n"
36
+ },
37
+ {
38
+ "name": "frontmatter delimiter at EOF",
39
+ "source": "---\ntype: note\n---",
40
+ "preamble": "",
41
+ "value": "\nFirst text.",
42
+ "expected": "---\ntype: note\n---\nFirst text."
43
+ },
44
+ {
45
+ "name": "section starts at offset zero",
46
+ "source": "## Body\nKept.\n",
47
+ "preamble": "",
48
+ "value": "Intro.\n\n",
49
+ "expected": "Intro.\n\n## Body\nKept.\n"
50
+ },
51
+ {
52
+ "name": "section immediately after frontmatter",
53
+ "source": "---\ntype: note\n---\n## Body\nKept.\n",
54
+ "preamble": "",
55
+ "value": "Intro.\n",
56
+ "expected": "---\ntype: note\n---\nIntro.\n## Body\nKept.\n"
57
+ },
58
+ {
59
+ "name": "backtick fence hides a heading",
60
+ "source": "```md\n## Example\n```\n\n## Body\nKept.\n",
61
+ "preamble": "```md\n## Example\n```\n\n",
62
+ "value": "Example removed.\n",
63
+ "expected": "Example removed.\n## Body\nKept.\n"
64
+ },
65
+ {
66
+ "name": "tilde fence hides a heading",
67
+ "source": " ~~~md\n## Example\n ~~~\n\n## Body\nKept.",
68
+ "preamble": " ~~~md\n## Example\n ~~~\n\n",
69
+ "value": "Example removed.\n",
70
+ "expected": "Example removed.\n## Body\nKept."
71
+ },
72
+ {
73
+ "name": "unclosed fence carries the entire body",
74
+ "source": "---\ntype: note\n---\n```\n## Example\n",
75
+ "preamble": "```\n## Example\n",
76
+ "value": "Plain replacement.\n",
77
+ "expected": "---\ntype: note\n---\nPlain replacement.\n"
78
+ },
79
+ {
80
+ "name": "existing section parser ignores other heading forms",
81
+ "source": "# Title\n### Subheading\n> ## Quoted\n ## Indented\nSetext\n------\n\n## Actual ##\nKept.\n",
82
+ "preamble": "# Title\n### Subheading\n> ## Quoted\n ## Indented\nSetext\n------\n\n",
83
+ "value": "Intro.\n",
84
+ "expected": "Intro.\n## Actual ##\nKept.\n"
85
+ },
86
+ {
87
+ "name": "Unicode and mixed newlines",
88
+ "source": "---\r\ntype: note\r\n---\r\n🙂 Вступ\r\n\n## Body\nKept.\r\n",
89
+ "preamble": "🙂 Вступ\r\n\n",
90
+ "value": "Новий 🙂\n\n",
91
+ "expected": "---\r\ntype: note\r\n---\r\nНовий 🙂\n\n## Body\nKept.\r\n"
92
+ },
93
+ {
94
+ "name": "heading at EOF and empty replacement",
95
+ "source": "Intro.\n\n## Body",
96
+ "preamble": "Intro.\n\n",
97
+ "value": "",
98
+ "expected": "## Body"
99
+ },
100
+ {
101
+ "name": "Runtime moved export link",
102
+ "source": "---\ntype: note\ntitle: Filed note\n---\n\nFiled beside [the export](../data/export.csv).\n\n## Body\n\nThe destination above sits outside every section.\n",
103
+ "preamble": "\nFiled beside [the export](../data/export.csv).\n\n",
104
+ "value": "\nFiled beside [the export](../files/export.csv).\n\n",
105
+ "expected": "---\ntype: note\ntitle: Filed note\n---\n\nFiled beside [the export](../files/export.csv).\n\n## Body\n\nThe destination above sits outside every section.\n"
106
+ }
107
+ ]
@@ -0,0 +1,156 @@
1
+ {
2
+ "definitions": {
3
+ "task": "---\ntype: definition\nfields:\n title: { kind: string, required: true }\n status: { kind: enum, values: [open, done], required: true }\n owner: { kind: reference, to: person }\nsections:\n Description: { kind: text, required: true }\n Acceptance: { kind: list }\n---\n",
4
+ "unreadable-task": "---\ntype: definition\nsections:\n Description: { kind: prose }\n---\n"
5
+ },
6
+ "cases": [
7
+ {
8
+ "name": "any body keeps labels and leaves every unsupplied destination authored",
9
+ "file": "notes/one.md",
10
+ "source": "---\ntitle: Note\n---\n\nRead [the plan](../plans/q3.md#goals \"Plan\") and ![the figure](../assets/figure.png).\nSee the [site](https://example.com/x.md) and this [anchor](#here).\nAn [unheld draft](../plans/draft.md) keeps the destination nobody supplied.\nEscaped \\[the plan\\](../plans/q3.md) stays, and so does `[the plan](../plans/q3.md)`.\n\n```sh\ncat [the plan](../plans/q3.md)\n```\n",
11
+ "documentTargets": [
12
+ "plans/q3.md",
13
+ "plans/2026-q3.md"
14
+ ],
15
+ "definition": null,
16
+ "move": {
17
+ "href": "../plans/q3.md#goals",
18
+ "to": "../plans/2026-q3.md"
19
+ },
20
+ "expected": {
21
+ "ok": true,
22
+ "text": "\nRead the plan and ![the figure](../assets/figure.png).\nSee the [site](https://example.com/x.md) and this [anchor](#here).\nAn [unheld draft](../plans/draft.md) keeps the destination nobody supplied.\nEscaped \\[the plan\\](../plans/q3.md) stays, and so does `[the plan](../plans/q3.md)`.\n\n```sh\ncat [the plan](../plans/q3.md)\n```\n"
23
+ }
24
+ },
25
+ {
26
+ "name": "any body without frontmatter projects from its first character",
27
+ "file": "notes/two.md",
28
+ "source": "# Title\n\nSee [the plan](plans/q3.md).\n",
29
+ "documentTargets": [
30
+ "notes/plans/q3.md",
31
+ "notes/plans/2026-q3.md"
32
+ ],
33
+ "definition": null,
34
+ "move": {
35
+ "href": "plans/q3.md",
36
+ "to": "plans/2026-q3.md"
37
+ },
38
+ "expected": {
39
+ "ok": true,
40
+ "text": "# Title\n\nSee the plan.\n"
41
+ }
42
+ },
43
+ {
44
+ "name": "typed Document takes its declared free-text sections alone",
45
+ "file": "tasks/one.md",
46
+ "source": "---\ntype: task\ntitle: Ship it\nstatus: open\nowner: ../people/ana.md\n---\n\nPreamble prose that mentions [the plan](../plans/q3.md).\n\n## Description\n\nFollow [the plan][plan] and [the plan][plan] again, then [read it][].\n\n[plan]: ../plans/q3.md \"Plan\"\n[read it]: ../plans/q3.md\n\n## Acceptance\n\n- [the plan](../plans/q3.md) is linked\n\n## Notes\n\nUndeclared prose about [the plan](../plans/q3.md).\n",
47
+ "documentTargets": [
48
+ "plans/q3.md",
49
+ "plans/2026-q3.md"
50
+ ],
51
+ "definition": "task",
52
+ "move": {
53
+ "href": "../plans/q3.md",
54
+ "to": "../plans/2026-q3.md"
55
+ },
56
+ "expected": {
57
+ "ok": true,
58
+ "text": "## Description\n\nFollow the plan and the plan again, then read it.\n\n\n"
59
+ }
60
+ },
61
+ {
62
+ "name": "a link definition outside the projected sections still resolves its use",
63
+ "file": "tasks/two.md",
64
+ "source": "---\ntype: task\ntitle: Ship it\nstatus: open\n---\n\n## Description\n\nFollow [the plan][plan].\n\n## Notes\n\n[plan]: ../plans/q3.md\n",
65
+ "documentTargets": [
66
+ "plans/q3.md",
67
+ "plans/2026-q3.md"
68
+ ],
69
+ "definition": "task",
70
+ "move": {
71
+ "href": "../plans/q3.md",
72
+ "to": "../plans/2026-q3.md"
73
+ },
74
+ "expected": {
75
+ "ok": true,
76
+ "text": "## Description\n\nFollow the plan.\n\n"
77
+ }
78
+ },
79
+ {
80
+ "name": "every occurrence of a declared free-text section is projected",
81
+ "file": "tasks/three.md",
82
+ "source": "---\ntype: task\ntitle: Ship it\nstatus: open\n---\n\n## Description\n\nFirst [plan](../plans/q3.md).\n\n## Description\n\nSecond note.\n",
83
+ "documentTargets": [
84
+ "plans/q3.md",
85
+ "plans/2026-q3.md"
86
+ ],
87
+ "definition": "task",
88
+ "move": {
89
+ "href": "../plans/q3.md",
90
+ "to": "../plans/2026-q3.md"
91
+ },
92
+ "expected": {
93
+ "ok": true,
94
+ "text": "## Description\n\nFirst plan.\n\n## Description\n\nSecond note.\n"
95
+ }
96
+ },
97
+ {
98
+ "name": "a declared type the caller does not supply is refused",
99
+ "file": "tasks/one.md",
100
+ "source": "---\ntype: task\ntitle: Ship it\nstatus: open\nowner: ../people/ana.md\n---\n\nPreamble prose that mentions [the plan](../plans/q3.md).\n\n## Description\n\nFollow [the plan][plan] and [the plan][plan] again, then [read it][].\n\n[plan]: ../plans/q3.md \"Plan\"\n[read it]: ../plans/q3.md\n\n## Acceptance\n\n- [the plan](../plans/q3.md) is linked\n\n## Notes\n\nUndeclared prose about [the plan](../plans/q3.md).\n",
101
+ "documentTargets": [
102
+ "plans/q3.md",
103
+ "plans/2026-q3.md"
104
+ ],
105
+ "definition": null,
106
+ "move": null,
107
+ "expected": {
108
+ "ok": false,
109
+ "refusal": {
110
+ "file": "tasks/one.md",
111
+ "rule": "unknown-type",
112
+ "version": "<package-version>",
113
+ "location": "type"
114
+ }
115
+ }
116
+ },
117
+ {
118
+ "name": "a definition the model cannot read is refused at that definition",
119
+ "file": "tasks/one.md",
120
+ "source": "---\ntype: task\ntitle: Ship it\nstatus: open\nowner: ../people/ana.md\n---\n\nPreamble prose that mentions [the plan](../plans/q3.md).\n\n## Description\n\nFollow [the plan][plan] and [the plan][plan] again, then [read it][].\n\n[plan]: ../plans/q3.md \"Plan\"\n[read it]: ../plans/q3.md\n\n## Acceptance\n\n- [the plan](../plans/q3.md) is linked\n\n## Notes\n\nUndeclared prose about [the plan](../plans/q3.md).\n",
121
+ "documentTargets": [
122
+ "plans/q3.md",
123
+ "plans/2026-q3.md"
124
+ ],
125
+ "definition": "unreadable-task",
126
+ "move": null,
127
+ "expected": {
128
+ "ok": false,
129
+ "refusal": {
130
+ "file": "types/task.md",
131
+ "rule": "definition-section-kind",
132
+ "version": "<package-version>",
133
+ "location": "Description"
134
+ }
135
+ }
136
+ },
137
+ {
138
+ "name": "an image at a supplied Markdown document target is projected like its link",
139
+ "file": "notes/four.md",
140
+ "source": "Read [the plan](../plans/q3.md) and ![the chart](../plans/q3.md).\nAlso [the plan][plan] and ![the chart][plan].\n\n[plan]: ../plans/q3.md\n",
141
+ "documentTargets": [
142
+ "plans/q3.md",
143
+ "plans/2026-q3.md"
144
+ ],
145
+ "definition": null,
146
+ "move": {
147
+ "href": "../plans/q3.md",
148
+ "to": "../plans/2026-q3.md"
149
+ },
150
+ "expected": {
151
+ "ok": true,
152
+ "text": "Read the plan and the chart.\nAlso the plan and the chart.\n\n"
153
+ }
154
+ }
155
+ ]
156
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentshouse/mdmodel",
3
- "version": "0.1.0-alpha.4",
3
+ "version": "0.1.0-alpha.6",
4
4
  "description": "MD Type language, parser, validator, structured change application, Markdown syntax profile, and conformance fixtures",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -9,7 +9,7 @@
9
9
  "access": "public"
10
10
  },
11
11
  "engines": {
12
- "node": ">=24.20.0"
12
+ "node": ">=24.21.0"
13
13
  },
14
14
  "exports": {
15
15
  ".": {