@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 +90 -3
- package/dist/document.d.ts.map +1 -1
- package/dist/document.js +21 -3
- package/dist/document.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/links.d.ts +17 -0
- package/dist/links.d.ts.map +1 -1
- package/dist/links.js +37 -10
- package/dist/links.js.map +1 -1
- package/dist/project.d.ts +3 -0
- package/dist/project.d.ts.map +1 -0
- package/dist/project.js +96 -0
- package/dist/project.js.map +1 -0
- package/dist/types.d.ts +16 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/version.js.map +1 -1
- package/fixtures/README.md +32 -0
- package/fixtures/preamble.json +107 -0
- package/fixtures/projection.json +156 -0
- package/package.json +2 -2
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.
|
|
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
|
|
80
|
-
Markdown link destination. A
|
|
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
|
package/dist/document.d.ts.map
CHANGED
|
@@ -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;
|
|
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);
|
package/dist/document.js.map
CHANGED
|
@@ -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
|
|
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
|
package/dist/index.d.ts.map
CHANGED
|
@@ -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
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;
|
|
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
|
package/dist/links.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"links.d.ts","sourceRoot":"","sources":["../src/links.ts"],"names":[],"mappings":"
|
|
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(
|
|
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)
|
|
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
|
|
323
|
-
const { fences, definitions } = scanBlocks(body);
|
|
324
|
-
const
|
|
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,
|
|
358
|
+
i = takeLink(body, i + 1, i, uses, definitions);
|
|
338
359
|
continue;
|
|
339
360
|
}
|
|
340
361
|
if (body[i] === '[') {
|
|
341
|
-
i = takeLink(body, i,
|
|
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
|
|
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 @@
|
|
|
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"}
|
package/dist/project.js
ADDED
|
@@ -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;
|
package/dist/types.d.ts.map
CHANGED
|
@@ -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":"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,iBAAiB,GAAG;IAC9B,QAAQ,CAAC,eAAe,EAAE,SAAS,MAAM,EAAE,CAAC;IAC5C,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC9B,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,MAAM,CAAC;IAC1B,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,UAAU,GAClB;IAAE,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAC5C;IAAE,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAAC;AAEtD,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,UAAU,CAAC;IAC1B,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"}
|
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":"
|
|
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.
|
|
1
|
+
export declare const VERSION = "0.1.0-alpha.6";
|
|
2
2
|
//# sourceMappingURL=version.d.ts.map
|
package/dist/version.js
CHANGED
package/dist/version.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/workspace/packages/mdmodel/src/version.ts"],"sourcesContent":["export const VERSION = '0.1.0-alpha.
|
|
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"}
|
package/fixtures/README.md
CHANGED
|
@@ -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 .\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 .\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 .\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.
|
|
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.
|
|
12
|
+
"node": ">=24.21.0"
|
|
13
13
|
},
|
|
14
14
|
"exports": {
|
|
15
15
|
".": {
|