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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (113) hide show
  1. package/README.md +60 -6
  2. package/dist/document.d.ts +2 -1
  3. package/dist/document.d.ts.map +1 -1
  4. package/dist/document.js +71 -1
  5. package/dist/document.js.map +1 -1
  6. package/dist/index.d.ts +2 -2
  7. package/dist/index.d.ts.map +1 -1
  8. package/dist/index.js +1 -1
  9. package/dist/index.js.map +1 -1
  10. package/dist/links.d.ts +7 -1
  11. package/dist/links.d.ts.map +1 -1
  12. package/dist/links.js +259 -48
  13. package/dist/links.js.map +1 -1
  14. package/dist/paths.d.ts +1 -0
  15. package/dist/paths.d.ts.map +1 -1
  16. package/dist/paths.js +5 -0
  17. package/dist/paths.js.map +1 -1
  18. package/dist/types.d.ts +11 -0
  19. package/dist/types.d.ts.map +1 -1
  20. package/dist/types.js.map +1 -1
  21. package/dist/validate.d.ts +2 -2
  22. package/dist/validate.d.ts.map +1 -1
  23. package/dist/validate.js +12 -8
  24. package/dist/validate.js.map +1 -1
  25. package/dist/version.d.ts +1 -1
  26. package/dist/version.js +1 -1
  27. package/dist/version.js.map +1 -1
  28. package/dist/yaml.d.ts +1 -0
  29. package/dist/yaml.d.ts.map +1 -1
  30. package/dist/yaml.js +5 -3
  31. package/dist/yaml.js.map +1 -1
  32. package/fixtures/README.md +12 -4
  33. package/fixtures/cases/any-without-type/context.json +1 -0
  34. package/fixtures/cases/binary-any-reference/context.json +5 -0
  35. package/fixtures/cases/binary-any-reference/expected.json +7 -0
  36. package/fixtures/cases/binary-any-reference/library/notes/draft.md +6 -0
  37. package/fixtures/cases/binary-any-reference/library/types/note.md +5 -0
  38. package/fixtures/cases/binary-document-collision/context.json +5 -0
  39. package/fixtures/cases/binary-document-collision/expected.json +6 -0
  40. package/fixtures/cases/binary-document-collision/library/notes/draft.md +3 -0
  41. package/fixtures/cases/binary-document-collision/library/types/note.md +3 -0
  42. package/fixtures/cases/binary-link-outside-collection/context.json +6 -0
  43. package/fixtures/cases/binary-link-outside-collection/expected.json +6 -0
  44. package/fixtures/cases/binary-link-outside-collection/library/notes/draft.md +5 -0
  45. package/fixtures/cases/binary-link-outside-collection/library/types/note.md +3 -0
  46. package/fixtures/cases/binary-prototype-reference/context.json +1 -0
  47. package/fixtures/cases/binary-prototype-reference/expected.json +7 -0
  48. package/fixtures/cases/binary-prototype-reference/library/notes/draft.md +6 -0
  49. package/fixtures/cases/binary-prototype-reference/library/types/note.md +5 -0
  50. package/fixtures/cases/binary-typed-reference/context.json +5 -0
  51. package/fixtures/cases/binary-typed-reference/expected.json +7 -0
  52. package/fixtures/cases/binary-typed-reference/library/notes/draft.md +6 -0
  53. package/fixtures/cases/binary-typed-reference/library/types/note.md +5 -0
  54. package/fixtures/cases/binary-typed-reference/library/types/person.md +3 -0
  55. package/fixtures/cases/bracketed-link-definition/context.json +1 -0
  56. package/fixtures/cases/bracketed-link-definition/expected.json +1 -0
  57. package/fixtures/cases/bracketed-link-definition/library/item.md +10 -0
  58. package/fixtures/cases/bracketed-link-definition/library/types/task.md +7 -0
  59. package/fixtures/cases/declared-binary-links/context.json +6 -0
  60. package/fixtures/cases/declared-binary-links/expected.json +1 -0
  61. package/fixtures/cases/declared-binary-links/library/notes/draft.md +7 -0
  62. package/fixtures/cases/declared-binary-links/library/types/note.md +3 -0
  63. package/fixtures/cases/duplicate-link-definition/context.json +1 -0
  64. package/fixtures/cases/duplicate-link-definition/expected.json +1 -0
  65. package/fixtures/cases/duplicate-link-definition/library/item.md +11 -0
  66. package/fixtures/cases/duplicate-link-definition/library/types/task.md +7 -0
  67. package/fixtures/cases/fenced-link-definition/context.json +1 -0
  68. package/fixtures/cases/fenced-link-definition/expected.json +1 -0
  69. package/fixtures/cases/fenced-link-definition/library/item.md +12 -0
  70. package/fixtures/cases/fenced-link-definition/library/types/task.md +7 -0
  71. package/fixtures/cases/field-enum/context.json +1 -0
  72. package/fixtures/cases/interrupted-link-definition/context.json +1 -0
  73. package/fixtures/cases/interrupted-link-definition/expected.json +1 -0
  74. package/fixtures/cases/interrupted-link-definition/library/notes/draft.md +8 -0
  75. package/fixtures/cases/interrupted-link-definition/library/types/note.md +3 -0
  76. package/fixtures/cases/long-fence-link-definition/context.json +1 -0
  77. package/fixtures/cases/long-fence-link-definition/expected.json +1 -0
  78. package/fixtures/cases/long-fence-link-definition/library/notes/draft.md +9 -0
  79. package/fixtures/cases/long-fence-link-definition/library/types/note.md +3 -0
  80. package/fixtures/cases/malformed-definition/context.json +1 -0
  81. package/fixtures/cases/missing-binary-image/context.json +6 -0
  82. package/fixtures/cases/missing-binary-image/expected.json +6 -0
  83. package/fixtures/cases/missing-binary-image/library/notes/draft.md +5 -0
  84. package/fixtures/cases/missing-binary-image/library/types/note.md +3 -0
  85. package/fixtures/cases/missing-binary-link/context.json +6 -0
  86. package/fixtures/cases/missing-binary-link/expected.json +6 -0
  87. package/fixtures/cases/missing-binary-link/library/notes/draft.md +5 -0
  88. package/fixtures/cases/missing-binary-link/library/types/note.md +3 -0
  89. package/fixtures/cases/missing-required-field/context.json +1 -0
  90. package/fixtures/cases/paragraph-link-definition/context.json +5 -0
  91. package/fixtures/cases/paragraph-link-definition/expected.json +1 -0
  92. package/fixtures/cases/paragraph-link-definition/library/notes/draft.md +10 -0
  93. package/fixtures/cases/paragraph-link-definition/library/types/note.md +3 -0
  94. package/fixtures/cases/prd-definition/context.json +1 -0
  95. package/fixtures/cases/quoted-link-definition/context.json +1 -0
  96. package/fixtures/cases/quoted-link-definition/expected.json +6 -0
  97. package/fixtures/cases/quoted-link-definition/library/notes/draft.md +7 -0
  98. package/fixtures/cases/quoted-link-definition/library/types/note.md +3 -0
  99. package/fixtures/cases/referenced-missing-file/context.json +1 -0
  100. package/fixtures/cases/structured-section/context.json +1 -0
  101. package/fixtures/cases/truncated-link-definition/context.json +1 -0
  102. package/fixtures/cases/truncated-link-definition/expected.json +6 -0
  103. package/fixtures/cases/truncated-link-definition/library/notes/draft.md +7 -0
  104. package/fixtures/cases/truncated-link-definition/library/notes/other.md +5 -0
  105. package/fixtures/cases/truncated-link-definition/library/types/note.md +3 -0
  106. package/fixtures/cases/unclosed-frontmatter/context.json +1 -0
  107. package/fixtures/cases/unresolved-link/context.json +1 -0
  108. package/fixtures/cases/valid-typed-document/context.json +1 -0
  109. package/fixtures/cases/wide-item-fence-link/context.json +1 -0
  110. package/fixtures/cases/wide-item-fence-link/expected.json +1 -0
  111. package/fixtures/cases/wide-item-fence-link/library/notes/draft.md +7 -0
  112. package/fixtures/cases/wide-item-fence-link/library/types/note.md +3 -0
  113. package/package.json +1 -1
package/README.md CHANGED
@@ -5,7 +5,8 @@ deterministic application of one structured change to Document content, the
5
5
  Markdown syntax profile, and the conformance fixtures.
6
6
 
7
7
  The package runs in a browser and in Node. It makes no network call and reads
8
- no filesystem on its own. Callers pass Library state as path-to-content.
8
+ no filesystem on its own. Callers pass text Library state as path-to-content and declare retained binary
9
+ link targets separately in a `ValidationContext`.
9
10
  The public root exposes one ESM artifact. Node 24.20.0 and later can load it
10
11
  through either `import` or `require`; browser bundlers consume the same artifact.
11
12
 
@@ -22,6 +23,7 @@ Package releases are published after the User confirms.
22
23
  ```ts
23
24
  import {
24
25
  apply,
26
+ links,
25
27
  MARKDOWN_SYNTAX_PROFILE,
26
28
  parse,
27
29
  serialize,
@@ -30,6 +32,13 @@ import {
30
32
  } from '@agentshouse/mdmodel';
31
33
 
32
34
  validate({ 'note.md': '# Hello\n' });
35
+ validate(
36
+ {
37
+ 'types/note.md': '---\ntype: definition\n---\n',
38
+ 'note.md': '---\ntype: note\n---\n![Figure](assets/figure.png)\n',
39
+ },
40
+ { binaryTargets: ['assets/figure.png'] },
41
+ );
33
42
  const parsed = parse(content);
34
43
  if (parsed.ok) {
35
44
  const next = apply(parsed.document, {
@@ -39,14 +48,59 @@ if (parsed.ok) {
39
48
  prior: 'Hello',
40
49
  });
41
50
  if (next.outcome === 'applied') serialize(next.document);
51
+ for (const link of links(parsed.document, 'tasks/two.md')) {
52
+ if (link.target !== 'data/export.csv') continue;
53
+ const moved = apply(parsed.document, {
54
+ kind: 'link',
55
+ href: link.href,
56
+ to: '../files/export.csv',
57
+ });
58
+ if (moved.outcome === 'applied') serialize(moved.document);
59
+ }
42
60
  }
43
61
  ```
44
62
 
45
- `validate` takes one Library state and returns acceptance or one refusal
46
- naming the file, the field or section, the violated rule, and `VERSION`.
47
- `apply` sets one typed field, including a list field as a whole, or replaces
48
- one declared section body against that value's prior content. A field change
49
- does not reformat the rest of the file.
63
+ `validate(library, context?)` takes the resulting text Library and one
64
+ `ValidationContext` containing `binaryTargets: readonly string[]`. Omitting the
65
+ context declares no binary targets. Library keys and binary targets use the
66
+ same collection-relative path namespace; callers supply canonical paths, and
67
+ a path declared as both text and binary is refused. Ordinary links and images
68
+ in typed Documents may resolve to either membership, including relative
69
+ normalization from the source Document. A binary target supplies no Document
70
+ or MD Type and cannot satisfy a typed reference, including one to `any`.
71
+ The existing `any` Document rules are unchanged.
72
+
73
+ Validation returns acceptance or one refusal naming the file, the field or
74
+ section, the violated rule, and `VERSION`. Every caller operation that validates
75
+ a resulting collection supplies that collection's context to this same
76
+ `validate` function. `parse`, `apply`, and `serialize` operate on a single text
77
+ Document; they do not validate collection membership or consume binary bytes.
78
+ `apply` sets one typed field, including a list field as a whole, replaces
79
+ one declared section body against that value's prior content, or rewrites one
80
+ Markdown link destination. A field change does not reformat the rest of the file.
81
+
82
+ `links(document, file)` is the inventory of that Document's ordinary link and
83
+ image destinations that resolve to a collection Path, each with the destination
84
+ `href` exactly as written and the `target` Path it resolves to relative to
85
+ `file`. A `?query` or `#fragment` the destination carries is part of `href` and
86
+ not part of `target`. Entries are ordered by the position of the destination
87
+ text a rewrite would change, and one destination written the same way appears
88
+ once however many times it occurs. A destination that the syntax profile does not read as a
89
+ link, one that is empty, a bare fragment, or carries a URL scheme, and one that
90
+ leaves the collection are not in the inventory. Frontmatter field values,
91
+ including typed references, are not link destinations and are never enumerated.
92
+
93
+ `apply(document, { kind: 'link', href, to })` rewrites every body occurrence of
94
+ that written destination and changes nothing else, so a caller that moves a Path
95
+ rewrites its referring Documents without a second Markdown parser. `to` is the
96
+ Path the destination must resolve to instead; the `?query` or `#fragment` the
97
+ written destination carried is not part of that Path and is kept. A Document
98
+ that does not carry the destination is `stale`. A reference definition is
99
+ rewritten once however many uses name it. The destination is
100
+ written in the form the syntax profile reads back: bare when it carries no
101
+ whitespace, does not open with `<`, and balances its parentheses, otherwise
102
+ inside angle brackets, which the original form is kept in when it still fits. A
103
+ destination that neither form can carry is refused as `unwritable-link`.
50
104
 
51
105
  ## Fixtures
52
106
 
@@ -1,5 +1,6 @@
1
- import type { ApplyResult, Change, Document, ParseResult } from './types.js';
1
+ import type { ApplyResult, Change, Document, Link, ParseResult } from './types.js';
2
2
  export declare function parse(content: string, file?: string): ParseResult;
3
+ export declare function links(document: Document, file: string): readonly Link[];
3
4
  export declare function apply(document: Document, change: Change): ApplyResult;
4
5
  export declare function serialize(document: Document): string;
5
6
  //# sourceMappingURL=document.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"document.d.ts","sourceRoot":"","sources":["../src/document.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAmD7E,wBAAgB,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,SAAK,GAAG,WAAW,CA0C7D;AAkDD,wBAAgB,KAAK,CAAC,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,GAAG,WAAW,CAWrE;AAED,wBAAgB,SAAS,CAAC,QAAQ,EAAE,QAAQ,GAAG,MAAM,CAEpD"}
1
+ {"version":3,"file":"document.d.ts","sourceRoot":"","sources":["../src/document.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAoDnF,wBAAgB,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,SAAK,GAAG,WAAW,CA0C7D;AAiGD,wBAAgB,KAAK,CAAC,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,GAAG,SAAS,IAAI,EAAE,CAWvE;AAED,wBAAgB,KAAK,CAAC,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,GAAG,WAAW,CAYrE;AAED,wBAAgB,SAAS,CAAC,QAAQ,EAAE,QAAQ,GAAG,MAAM,CAEpD"}
package/dist/document.js CHANGED
@@ -1,6 +1,8 @@
1
+ import { hrefOccurrences } from './links.js';
2
+ import { classifyHref, hrefSuffix } from './paths.js';
1
3
  import { headingSpans, sectionsRecord } from './sections.js';
2
4
  import { refusal } from './refuse.js';
3
- import { bodyStartAfterYaml, equalValue, extractFrontmatter, jsValue, lineEnding, mapEntry, parseYamlMapping, serializeJs, serializeKey, YamlError } from './yaml.js';
5
+ import { bodyStartAfterYaml, bodyStartOf, equalValue, extractFrontmatter, jsValue, lineEnding, mapEntry, parseYamlMapping, serializeJs, serializeKey, YamlError } from './yaml.js';
4
6
  const internals = new WeakMap();
5
7
  function refuse(file, rule, location) {
6
8
  return location === undefined ? refusal(file, rule) : refusal(file, rule, {
@@ -133,7 +135,75 @@ function applySection(document, name, value, prior) {
133
135
  document: parsed.document
134
136
  };
135
137
  }
138
+ function fitsBare(to) {
139
+ if (to === '' || to.startsWith('<')) return false;
140
+ let depth = 0;
141
+ for (const character of to){
142
+ if (/[\s\u0000-\u001f\u007f]/.test(character)) return false;
143
+ if (character === '(') depth += 1;
144
+ else if (character === ')') {
145
+ if (depth === 0) return false;
146
+ depth -= 1;
147
+ }
148
+ }
149
+ return depth === 0;
150
+ }
151
+ function fitsBrackets(to) {
152
+ return to !== '' && !/[<>\n\r]/.test(to);
153
+ }
154
+ function destinationText(to, bracketed) {
155
+ if (fitsBare(to)) return bracketed && fitsBrackets(to) ? `<${to}>` : to;
156
+ if (fitsBrackets(to)) return `<${to}>`;
157
+ return null;
158
+ }
159
+ function applyLink(document, href, to) {
160
+ const content = document.content;
161
+ const bodyStart = bodyStartOf(content);
162
+ const matches = hrefOccurrences(content.slice(bodyStart)).filter((occurrence)=>occurrence.href === href);
163
+ if (matches.length === 0) return {
164
+ outcome: 'stale',
165
+ document
166
+ };
167
+ const written = to + hrefSuffix(href);
168
+ let rewritten = '';
169
+ let cursor = 0;
170
+ for (const match of matches){
171
+ const text = destinationText(written, match.bracketed);
172
+ if (text === null) return {
173
+ outcome: 'refuse',
174
+ refusal: refuse('', 'unwritable-link', written)
175
+ };
176
+ rewritten += content.slice(cursor, bodyStart + match.start);
177
+ rewritten += text;
178
+ cursor = bodyStart + match.end;
179
+ }
180
+ const parsed = parse(rewritten + content.slice(cursor));
181
+ if (!parsed.ok) return {
182
+ outcome: 'refuse',
183
+ refusal: parsed.refusal
184
+ };
185
+ return {
186
+ outcome: 'applied',
187
+ document: parsed.document
188
+ };
189
+ }
190
+ export function links(document, file) {
191
+ const found = [];
192
+ const seen = new Set();
193
+ for (const occurrence of hrefOccurrences(document.content.slice(bodyStartOf(document.content)))){
194
+ if (seen.has(occurrence.href)) continue;
195
+ const resolved = classifyHref(file, occurrence.href);
196
+ if (resolved.kind !== 'path') continue;
197
+ seen.add(occurrence.href);
198
+ found.push({
199
+ href: occurrence.href,
200
+ target: resolved.path
201
+ });
202
+ }
203
+ return found;
204
+ }
136
205
  export function apply(document, change) {
206
+ if (change.kind === 'link') return applyLink(document, change.href, change.to);
137
207
  try {
138
208
  if (change.kind === 'field') return applyField(document, change.name, change.value, change.prior);
139
209
  return applySection(document, change.name, change.value, change.prior);
@@ -1 +1 @@
1
- {"version":3,"sources":["/workspace/packages/mdmodel/src/document.ts"],"sourcesContent":["import { headingSpans, sectionsRecord } from './sections.js';\nimport { refusal } from './refuse.js';\nimport type { ApplyResult, Change, Document, ParseResult } from './types.js';\nimport {\n bodyStartAfterYaml,\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\nexport function apply(document: Document, change: Change): ApplyResult {\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":["headingSpans","sectionsRecord","refusal","bodyStartAfterYaml","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","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 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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"}
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
- export { apply, parse, serialize } from './document.js';
1
+ export { apply, links, parse, serialize } from './document.js';
2
2
  export { MARKDOWN_SYNTAX_PROFILE } from './profile.js';
3
- export type { ApplyResult, Change, Document, Library, ParseResult, Refusal, Validation, } from './types.js';
3
+ export type { ApplyResult, Change, Document, Library, Link, ParseResult, Refusal, Validation, ValidationContext, } from './types.js';
4
4
  export { validate } from './validate.js';
5
5
  export { VERSION } from './version.js';
6
6
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AACxD,OAAO,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAC;AACvD,YAAY,EACV,WAAW,EACX,MAAM,EACN,QAAQ,EACR,OAAO,EACP,WAAW,EACX,OAAO,EACP,UAAU,GACX,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,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"}
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- export { apply, parse, serialize } from './document.js';
1
+ export { apply, links, parse, serialize } from './document.js';
2
2
  export { MARKDOWN_SYNTAX_PROFILE } from './profile.js';
3
3
  export { validate } from './validate.js';
4
4
  export { VERSION } from './version.js';
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["/workspace/packages/mdmodel/src/index.ts"],"sourcesContent":["export { apply, parse, serialize } from './document.js';\nexport { MARKDOWN_SYNTAX_PROFILE } from './profile.js';\nexport type {\n ApplyResult,\n Change,\n Document,\n Library,\n ParseResult,\n Refusal,\n Validation,\n} from './types.js';\nexport { validate } from './validate.js';\nexport { VERSION } from './version.js';\n"],"names":["apply","parse","serialize","MARKDOWN_SYNTAX_PROFILE","validate","VERSION"],"mappings":"AAAA,SAASA,KAAK,EAAEC,KAAK,EAAEC,SAAS,QAAQ,gBAAgB;AACxD,SAASC,uBAAuB,QAAQ,eAAe;AAUvD,SAASC,QAAQ,QAAQ,gBAAgB;AACzC,SAASC,OAAO,QAAQ,eAAe"}
1
+ {"version":3,"sources":["/workspace/packages/mdmodel/src/index.ts"],"sourcesContent":["export { apply, links, parse, serialize } from './document.js';\nexport { MARKDOWN_SYNTAX_PROFILE } from './profile.js';\nexport type {\n ApplyResult,\n Change,\n Document,\n Library,\n Link,\n ParseResult,\n Refusal,\n Validation,\n ValidationContext,\n} from './types.js';\nexport { validate } from './validate.js';\nexport { VERSION } from './version.js';\n"],"names":["apply","links","parse","serialize","MARKDOWN_SYNTAX_PROFILE","validate","VERSION"],"mappings":"AAAA,SAASA,KAAK,EAAEC,KAAK,EAAEC,KAAK,EAAEC,SAAS,QAAQ,gBAAgB;AAC/D,SAASC,uBAAuB,QAAQ,eAAe;AAYvD,SAASC,QAAQ,QAAQ,gBAAgB;AACzC,SAASC,OAAO,QAAQ,eAAe"}
package/dist/links.d.ts CHANGED
@@ -1,2 +1,8 @@
1
- export declare function extractRelativeHrefs(body: string): string[];
1
+ export type HrefOccurrence = {
2
+ readonly href: string;
3
+ readonly start: number;
4
+ readonly end: number;
5
+ readonly bracketed: boolean;
6
+ };
7
+ export declare function hrefOccurrences(body: string): readonly HrefOccurrence[];
2
8
  //# sourceMappingURL=links.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"links.d.ts","sourceRoot":"","sources":["../src/links.ts"],"names":[],"mappings":"AAoHA,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CA4B3D"}
1
+ {"version":3,"file":"links.d.ts","sourceRoot":"","sources":["../src/links.ts"],"names":[],"mappings":"AA4VA,MAAM,MAAM,cAAc,GAAG;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;CAC7B,CAAC;AAEF,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,cAAc,EAAE,CA2BvE"}