@agentshouse/mdmodel 0.1.0-alpha.1 → 0.1.0-alpha.10

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 (156) hide show
  1. package/README.md +220 -7
  2. package/dist/blocks.d.ts +11 -0
  3. package/dist/blocks.d.ts.map +1 -0
  4. package/dist/blocks.js +63 -0
  5. package/dist/blocks.js.map +1 -0
  6. package/dist/definition.d.ts +8 -6
  7. package/dist/definition.d.ts.map +1 -1
  8. package/dist/definition.js +44 -38
  9. package/dist/definition.js.map +1 -1
  10. package/dist/divide.d.ts +3 -0
  11. package/dist/divide.d.ts.map +1 -0
  12. package/dist/divide.js +363 -0
  13. package/dist/divide.js.map +1 -0
  14. package/dist/document.d.ts +4 -1
  15. package/dist/document.d.ts.map +1 -1
  16. package/dist/document.js +95 -4
  17. package/dist/document.js.map +1 -1
  18. package/dist/index.d.ts +4 -2
  19. package/dist/index.d.ts.map +1 -1
  20. package/dist/index.js +3 -1
  21. package/dist/index.js.map +1 -1
  22. package/dist/links.d.ts +25 -1
  23. package/dist/links.d.ts.map +1 -1
  24. package/dist/links.js +289 -48
  25. package/dist/links.js.map +1 -1
  26. package/dist/paths.d.ts +1 -0
  27. package/dist/paths.d.ts.map +1 -1
  28. package/dist/paths.js +5 -0
  29. package/dist/paths.js.map +1 -1
  30. package/dist/profile.js.map +1 -1
  31. package/dist/project.d.ts +21 -0
  32. package/dist/project.d.ts.map +1 -0
  33. package/dist/project.js +158 -0
  34. package/dist/project.js.map +1 -0
  35. package/dist/refuse.js.map +1 -1
  36. package/dist/schema.d.ts.map +1 -1
  37. package/dist/schema.js +16 -38
  38. package/dist/schema.js.map +1 -1
  39. package/dist/sections.d.ts.map +1 -1
  40. package/dist/sections.js +14 -47
  41. package/dist/sections.js.map +1 -1
  42. package/dist/types.d.ts +44 -0
  43. package/dist/types.d.ts.map +1 -1
  44. package/dist/types.js.map +1 -1
  45. package/dist/validate.d.ts +2 -2
  46. package/dist/validate.d.ts.map +1 -1
  47. package/dist/validate.js +17 -8
  48. package/dist/validate.js.map +1 -1
  49. package/dist/version.d.ts +1 -1
  50. package/dist/version.d.ts.map +1 -1
  51. package/dist/version.js +1 -1
  52. package/dist/version.js.map +1 -1
  53. package/dist/yaml.d.ts +1 -0
  54. package/dist/yaml.d.ts.map +1 -1
  55. package/dist/yaml.js +6 -4
  56. package/dist/yaml.js.map +1 -1
  57. package/fixtures/README.md +87 -27
  58. package/fixtures/cases/any-without-type/context.json +1 -0
  59. package/fixtures/cases/binary-any-reference/context.json +5 -0
  60. package/fixtures/cases/binary-any-reference/expected.json +7 -0
  61. package/fixtures/cases/binary-any-reference/library/notes/draft.md +6 -0
  62. package/fixtures/cases/binary-any-reference/library/types/note.md +5 -0
  63. package/fixtures/cases/binary-document-collision/context.json +5 -0
  64. package/fixtures/cases/binary-document-collision/expected.json +6 -0
  65. package/fixtures/cases/binary-document-collision/library/notes/draft.md +3 -0
  66. package/fixtures/cases/binary-document-collision/library/types/note.md +3 -0
  67. package/fixtures/cases/binary-link-outside-collection/context.json +6 -0
  68. package/fixtures/cases/binary-link-outside-collection/expected.json +6 -0
  69. package/fixtures/cases/binary-link-outside-collection/library/notes/draft.md +5 -0
  70. package/fixtures/cases/binary-link-outside-collection/library/types/note.md +3 -0
  71. package/fixtures/cases/binary-prototype-reference/context.json +1 -0
  72. package/fixtures/cases/binary-prototype-reference/expected.json +7 -0
  73. package/fixtures/cases/binary-prototype-reference/library/notes/draft.md +6 -0
  74. package/fixtures/cases/binary-prototype-reference/library/types/note.md +5 -0
  75. package/fixtures/cases/binary-typed-reference/context.json +5 -0
  76. package/fixtures/cases/binary-typed-reference/expected.json +7 -0
  77. package/fixtures/cases/binary-typed-reference/library/notes/draft.md +6 -0
  78. package/fixtures/cases/binary-typed-reference/library/types/note.md +5 -0
  79. package/fixtures/cases/binary-typed-reference/library/types/person.md +3 -0
  80. package/fixtures/cases/bracketed-link-definition/context.json +1 -0
  81. package/fixtures/cases/bracketed-link-definition/expected.json +1 -0
  82. package/fixtures/cases/bracketed-link-definition/library/item.md +10 -0
  83. package/fixtures/cases/bracketed-link-definition/library/types/task.md +7 -0
  84. package/fixtures/cases/declared-binary-links/context.json +6 -0
  85. package/fixtures/cases/declared-binary-links/expected.json +1 -0
  86. package/fixtures/cases/declared-binary-links/library/notes/draft.md +7 -0
  87. package/fixtures/cases/declared-binary-links/library/types/note.md +3 -0
  88. package/fixtures/cases/duplicate-link-definition/context.json +1 -0
  89. package/fixtures/cases/duplicate-link-definition/expected.json +1 -0
  90. package/fixtures/cases/duplicate-link-definition/library/item.md +11 -0
  91. package/fixtures/cases/duplicate-link-definition/library/types/task.md +7 -0
  92. package/fixtures/cases/fenced-link-definition/context.json +1 -0
  93. package/fixtures/cases/fenced-link-definition/expected.json +1 -0
  94. package/fixtures/cases/fenced-link-definition/library/item.md +12 -0
  95. package/fixtures/cases/fenced-link-definition/library/types/task.md +7 -0
  96. package/fixtures/cases/field-enum/context.json +1 -0
  97. package/fixtures/cases/interrupted-link-definition/context.json +1 -0
  98. package/fixtures/cases/interrupted-link-definition/expected.json +1 -0
  99. package/fixtures/cases/interrupted-link-definition/library/notes/draft.md +8 -0
  100. package/fixtures/cases/interrupted-link-definition/library/types/note.md +3 -0
  101. package/fixtures/cases/long-fence-link-definition/context.json +1 -0
  102. package/fixtures/cases/long-fence-link-definition/expected.json +1 -0
  103. package/fixtures/cases/long-fence-link-definition/library/notes/draft.md +9 -0
  104. package/fixtures/cases/long-fence-link-definition/library/types/note.md +3 -0
  105. package/fixtures/cases/malformed-definition/context.json +1 -0
  106. package/fixtures/cases/missing-binary-image/context.json +6 -0
  107. package/fixtures/cases/missing-binary-image/expected.json +6 -0
  108. package/fixtures/cases/missing-binary-image/library/notes/draft.md +5 -0
  109. package/fixtures/cases/missing-binary-image/library/types/note.md +3 -0
  110. package/fixtures/cases/missing-binary-link/context.json +6 -0
  111. package/fixtures/cases/missing-binary-link/expected.json +6 -0
  112. package/fixtures/cases/missing-binary-link/library/notes/draft.md +5 -0
  113. package/fixtures/cases/missing-binary-link/library/types/note.md +3 -0
  114. package/fixtures/cases/missing-required-field/context.json +1 -0
  115. package/fixtures/cases/missing-section-item-kind/context.json +1 -0
  116. package/fixtures/cases/missing-section-item-kind/expected.json +7 -0
  117. package/fixtures/cases/missing-section-item-kind/library/types/task.md +7 -0
  118. package/fixtures/cases/paragraph-link-definition/context.json +5 -0
  119. package/fixtures/cases/paragraph-link-definition/expected.json +1 -0
  120. package/fixtures/cases/paragraph-link-definition/library/notes/draft.md +10 -0
  121. package/fixtures/cases/paragraph-link-definition/library/types/note.md +3 -0
  122. package/fixtures/cases/prd-definition/context.json +1 -0
  123. package/fixtures/cases/prd-definition/library/types/task.md +2 -1
  124. package/fixtures/cases/prototype-field/context.json +1 -0
  125. package/fixtures/cases/prototype-field/expected.json +7 -0
  126. package/fixtures/cases/prototype-field/library/item.md +9 -0
  127. package/fixtures/cases/prototype-field/library/types/task.md +7 -0
  128. package/fixtures/cases/quoted-link-definition/context.json +1 -0
  129. package/fixtures/cases/quoted-link-definition/expected.json +6 -0
  130. package/fixtures/cases/quoted-link-definition/library/notes/draft.md +7 -0
  131. package/fixtures/cases/quoted-link-definition/library/types/note.md +3 -0
  132. package/fixtures/cases/referenced-missing-file/context.json +1 -0
  133. package/fixtures/cases/structured-section/context.json +1 -0
  134. package/fixtures/cases/structured-section/library/types/task.md +1 -1
  135. package/fixtures/cases/truncated-link-definition/context.json +1 -0
  136. package/fixtures/cases/truncated-link-definition/expected.json +6 -0
  137. package/fixtures/cases/truncated-link-definition/library/notes/draft.md +7 -0
  138. package/fixtures/cases/truncated-link-definition/library/notes/other.md +5 -0
  139. package/fixtures/cases/truncated-link-definition/library/types/note.md +3 -0
  140. package/fixtures/cases/unclosed-frontmatter/context.json +1 -0
  141. package/fixtures/cases/undeclared-field/context.json +1 -0
  142. package/fixtures/cases/undeclared-field/expected.json +7 -0
  143. package/fixtures/cases/undeclared-field/library/item.md +9 -0
  144. package/fixtures/cases/undeclared-field/library/types/task.md +7 -0
  145. package/fixtures/cases/unresolved-link/context.json +1 -0
  146. package/fixtures/cases/valid-typed-document/context.json +1 -0
  147. package/fixtures/cases/valid-typed-document/library/tasks/one.md +1 -0
  148. package/fixtures/cases/valid-typed-document/library/types/task.md +2 -1
  149. package/fixtures/cases/wide-item-fence-link/context.json +1 -0
  150. package/fixtures/cases/wide-item-fence-link/expected.json +1 -0
  151. package/fixtures/cases/wide-item-fence-link/library/notes/draft.md +7 -0
  152. package/fixtures/cases/wide-item-fence-link/library/types/note.md +3 -0
  153. package/fixtures/division.json +415 -0
  154. package/fixtures/preamble.json +107 -0
  155. package/fixtures/projection.json +186 -0
  156. package/package.json +4 -5
@@ -0,0 +1,158 @@
1
+ import { readDefinition } from './definition.js';
2
+ import { frontmatter, 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, mapEntry } from './yaml.js';
8
+ function internalEdits(content, bodyStart, file, documentTargets) {
9
+ const inventory = linkInventory(content.slice(bodyStart));
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: bodyStart + use.start,
18
+ end: bodyStart + use.end,
19
+ text: content.slice(bodyStart + use.labelStart, bodyStart + use.labelEnd)
20
+ });
21
+ }
22
+ for (const block of inventory.blocks){
23
+ if (!internal.has(block.destination.start)) continue;
24
+ edits.push({
25
+ start: bodyStart + block.start,
26
+ end: bodyStart + block.end,
27
+ text: ''
28
+ });
29
+ }
30
+ return edits.sort((left, right)=>left.start - right.start);
31
+ }
32
+ export function rangeText(content, range, edits) {
33
+ let text = '';
34
+ let cursor = range.start;
35
+ for (const edit of edits){
36
+ if (edit.start < cursor || edit.end > range.end) continue;
37
+ text += content.slice(cursor, edit.start) + edit.text;
38
+ cursor = edit.end;
39
+ }
40
+ return text + content.slice(cursor, range.end);
41
+ }
42
+ function structural(spec) {
43
+ return spec.kind === 'list' && spec.of.kind !== 'text';
44
+ }
45
+ function freeTextValues(definition, document) {
46
+ const root = frontmatter(document);
47
+ if (root === null) return [];
48
+ const values = [];
49
+ for (const [name, spec] of Object.entries(definition.fields)){
50
+ const entry = mapEntry(root, name);
51
+ if (entry === undefined) continue;
52
+ const node = entry.value;
53
+ if (spec.kind === 'text') {
54
+ if (node.kind === 'scalar' && typeof node.value === 'string') {
55
+ values.push({
56
+ start: node.start,
57
+ end: node.end,
58
+ text: `${node.value}\n\n`
59
+ });
60
+ }
61
+ continue;
62
+ }
63
+ if (spec.kind !== 'list' || spec.of.kind !== 'text' || node.kind !== 'seq') continue;
64
+ for (const item of node.items){
65
+ if (item.kind === 'scalar' && typeof item.value === 'string') {
66
+ values.push({
67
+ start: item.start,
68
+ end: item.end,
69
+ text: `${item.value}\n\n`
70
+ });
71
+ }
72
+ }
73
+ }
74
+ return values;
75
+ }
76
+ export function projectionInput(document, file, context) {
77
+ const content = document.content;
78
+ const bodyStart = bodyStartOf(content);
79
+ const edits = internalEdits(content, bodyStart, file, new Set(context.documentTargets));
80
+ const declared = document.type;
81
+ if (declared === undefined || declared === 'any') {
82
+ return {
83
+ ok: true,
84
+ input: {
85
+ values: [],
86
+ ranges: [
87
+ {
88
+ start: bodyStart,
89
+ end: content.length
90
+ }
91
+ ],
92
+ edits
93
+ }
94
+ };
95
+ }
96
+ const source = context.definition;
97
+ if (source === undefined) {
98
+ return {
99
+ ok: false,
100
+ refusal: refusal(file, 'unknown-type', {
101
+ location: 'type'
102
+ })
103
+ };
104
+ }
105
+ const definitionFile = `types/${declared}.md`;
106
+ const parsed = parse(source, definitionFile);
107
+ if (!parsed.ok) return {
108
+ ok: false,
109
+ refusal: parsed.refusal
110
+ };
111
+ const definition = readDefinition(definitionFile, parsed.document);
112
+ if ('rule' in definition) return {
113
+ ok: false,
114
+ refusal: definition
115
+ };
116
+ const structure = new Set(Object.entries(definition.sections).flatMap(([name, spec])=>structural(spec) ? [
117
+ name
118
+ ] : []));
119
+ const headings = headingSpans(content, bodyStart);
120
+ const ranges = [
121
+ {
122
+ start: bodyStart,
123
+ end: headings[0]?.headingStart ?? content.length
124
+ }
125
+ ];
126
+ for (const heading of headings){
127
+ if (structure.has(heading.name)) continue;
128
+ ranges.push({
129
+ start: heading.headingStart,
130
+ end: heading.bodyEnd
131
+ });
132
+ }
133
+ return {
134
+ ok: true,
135
+ input: {
136
+ values: freeTextValues(definition, document),
137
+ ranges,
138
+ edits
139
+ }
140
+ };
141
+ }
142
+ export function project(document, file, context = {
143
+ documentTargets: []
144
+ }) {
145
+ const result = projectionInput(document, file, context);
146
+ if (!result.ok) return {
147
+ ok: false,
148
+ refusal: result.refusal
149
+ };
150
+ const { values, ranges, edits } = result.input;
151
+ const prose = ranges.map((range)=>rangeText(document.content, range, edits)).join('');
152
+ return {
153
+ ok: true,
154
+ text: values.map((value)=>value.text).join('') + prose
155
+ };
156
+ }
157
+
158
+ //# sourceMappingURL=project.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["/workspace/packages/mdmodel/src/project.ts"],"sourcesContent":["import { readDefinition, type Definition, type SectionSpec } from './definition.js';\nimport { frontmatter, 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, Extent, Projection, ProjectionContext, Refusal } from './types.js';\nimport { bodyStartOf, mapEntry } from './yaml.js';\n\nexport type Piece = Extent & { readonly text: string };\n\nexport type Edit = Piece;\n\nexport type Input = {\n readonly values: readonly Piece[];\n readonly ranges: readonly Extent[];\n readonly edits: readonly Edit[];\n};\n\nexport type InputResult =\n | { readonly ok: true; readonly input: Input }\n | { readonly ok: false; readonly refusal: Refusal };\n\nfunction internalEdits(\n content: string,\n bodyStart: number,\n file: string,\n documentTargets: ReadonlySet<string>,\n): Edit[] {\n const inventory = linkInventory(content.slice(bodyStart));\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: bodyStart + use.start,\n end: bodyStart + use.end,\n text: content.slice(bodyStart + use.labelStart, bodyStart + use.labelEnd),\n });\n }\n for (const block of inventory.blocks) {\n if (!internal.has(block.destination.start)) continue;\n edits.push({ start: bodyStart + block.start, end: bodyStart + block.end, text: '' });\n }\n return edits.sort((left, right) => left.start - right.start);\n}\n\nexport function rangeText(content: string, range: Extent, edits: readonly Edit[]): string {\n let text = '';\n let cursor = range.start;\n for (const edit of edits) {\n if (edit.start < cursor || edit.end > range.end) continue;\n text += content.slice(cursor, edit.start) + edit.text;\n cursor = edit.end;\n }\n return text + content.slice(cursor, range.end);\n}\n\nfunction structural(spec: SectionSpec): boolean {\n return spec.kind === 'list' && spec.of.kind !== 'text';\n}\n\nfunction freeTextValues(definition: Definition, document: Document): Piece[] {\n const root = frontmatter(document);\n if (root === null) return [];\n const values: Piece[] = [];\n for (const [name, spec] of Object.entries(definition.fields)) {\n const entry = mapEntry(root, name);\n if (entry === undefined) continue;\n const node = entry.value;\n if (spec.kind === 'text') {\n if (node.kind === 'scalar' && typeof node.value === 'string') {\n values.push({ start: node.start, end: node.end, text: `${node.value}\\n\\n` });\n }\n continue;\n }\n if (spec.kind !== 'list' || spec.of.kind !== 'text' || node.kind !== 'seq') continue;\n for (const item of node.items) {\n if (item.kind === 'scalar' && typeof item.value === 'string') {\n values.push({ start: item.start, end: item.end, text: `${item.value}\\n\\n` });\n }\n }\n }\n return values;\n}\n\nexport function projectionInput(\n document: Document,\n file: string,\n context: ProjectionContext,\n): InputResult {\n const content = document.content;\n const bodyStart = bodyStartOf(content);\n const edits = internalEdits(content, bodyStart, file, new Set(context.documentTargets));\n const declared = document.type;\n if (declared === undefined || declared === 'any') {\n return {\n ok: true,\n input: { values: [], ranges: [{ start: bodyStart, end: content.length }], edits },\n };\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 structure = new Set(\n Object.entries(definition.sections).flatMap(([name, spec]) => (structural(spec) ? [name] : [])),\n );\n const headings = headingSpans(content, bodyStart);\n const ranges: Extent[] = [{ start: bodyStart, end: headings[0]?.headingStart ?? content.length }];\n for (const heading of headings) {\n if (structure.has(heading.name)) continue;\n ranges.push({ start: heading.headingStart, end: heading.bodyEnd });\n }\n return { ok: true, input: { values: freeTextValues(definition, document), ranges, edits } };\n}\n\nexport function project(\n document: Document,\n file: string,\n context: ProjectionContext = { documentTargets: [] },\n): Projection {\n const result = projectionInput(document, file, context);\n if (!result.ok) return { ok: false, refusal: result.refusal };\n const { values, ranges, edits } = result.input;\n const prose = ranges.map((range) => rangeText(document.content, range, edits)).join('');\n return { ok: true, text: values.map((value) => value.text).join('') + prose };\n}\n"],"names":["readDefinition","frontmatter","parse","linkInventory","classifyHref","refusal","headingSpans","bodyStartOf","mapEntry","internalEdits","content","bodyStart","file","documentTargets","inventory","slice","internal","Set","edits","use","uses","resolved","destination","href","kind","has","path","add","start","push","end","text","labelStart","labelEnd","block","blocks","sort","left","right","rangeText","range","cursor","edit","structural","spec","of","freeTextValues","definition","document","root","values","name","Object","entries","fields","entry","undefined","node","value","item","items","projectionInput","context","declared","type","ok","input","ranges","length","source","location","definitionFile","parsed","structure","sections","flatMap","headings","headingStart","heading","bodyEnd","project","result","prose","map","join"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/executor/code/agents.house/mdmodel/packages/mdmodel/src/refuse.ts"],"sourcesContent":["import type { Refusal } from './types.js';\nimport { VERSION } from './version.js';\n\nexport function refusal(\n file: string,\n rule: string,\n extra?: { location?: string; referrers?: readonly string[] },\n): Refusal {\n return {\n file,\n rule,\n version: VERSION,\n ...(extra?.location === undefined ? {} : { location: extra.location }),\n ...(extra?.referrers === undefined ? {} : { referrers: extra.referrers }),\n };\n}\n"],"names":["VERSION","refusal","file","rule","extra","version","location","undefined","referrers"],"mappings":"AACA,SAASA,OAAO,QAAQ,eAAe;AAEvC,OAAO,SAASC,QACdC,IAAY,EACZC,IAAY,EACZC,KAA4D;IAE5D,OAAO;QACLF;QACAC;QACAE,SAASL;QACT,GAAII,OAAOE,aAAaC,YAAY,CAAC,IAAI;YAAED,UAAUF,MAAME,QAAQ;QAAC,CAAC;QACrE,GAAIF,OAAOI,cAAcD,YAAY,CAAC,IAAI;YAAEC,WAAWJ,MAAMI,SAAS;QAAC,CAAC;IAC1E;AACF"}
1
+ {"version":3,"sources":["/workspace/packages/mdmodel/src/refuse.ts"],"sourcesContent":["import type { Refusal } from './types.js';\nimport { VERSION } from './version.js';\n\nexport function refusal(\n file: string,\n rule: string,\n extra?: { location?: string; referrers?: readonly string[] },\n): Refusal {\n return {\n file,\n rule,\n version: VERSION,\n ...(extra?.location === undefined ? {} : { location: extra.location }),\n ...(extra?.referrers === undefined ? {} : { referrers: extra.referrers }),\n };\n}\n"],"names":["VERSION","refusal","file","rule","extra","version","location","undefined","referrers"],"mappings":"AACA,SAASA,OAAO,QAAQ,eAAe;AAEvC,OAAO,SAASC,QACdC,IAAY,EACZC,IAAY,EACZC,KAA4D;IAE5D,OAAO;QACLF;QACAC;QACAE,SAASL;QACT,GAAII,OAAOE,aAAaC,YAAY,CAAC,IAAI;YAAED,UAAUF,MAAME,QAAQ;QAAC,CAAC;QACrE,GAAIF,OAAOI,cAAcD,YAAY,CAAC,IAAI;YAAEC,WAAWJ,MAAMI,SAAS;QAAC,CAAC;IAC1E;AACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAc,MAAM,iBAAiB,CAAC;AAgC7D,wBAAgB,cAAc,CAC5B,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC,EAC3C,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GACxC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,gBAAgB,GAAG,YAAY,GAAG,YAAY,CAAA;CAAE,GAAG,SAAS,CAgBrF"}
1
+ {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAc,MAAM,iBAAiB,CAAC;AAmB7D,wBAAgB,cAAc,CAC5B,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC,EAC3C,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GACxC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,gBAAgB,GAAG,YAAY,GAAG,YAAY,CAAA;CAAE,GAAG,SAAS,CAUrF"}
package/dist/schema.js CHANGED
@@ -1,16 +1,6 @@
1
- import { Ajv } from 'ajv';
2
- import formatsPlugin from 'ajv-formats';
3
- const addFormats = formatsPlugin;
4
- const ajv = new Ajv({
5
- allErrors: true,
6
- strict: false,
7
- validateFormats: true
8
- });
9
- addFormats(ajv, [
10
- 'date'
11
- ]);
1
+ import { Validator } from '@cfworker/json-schema';
12
2
  function scalarSchema(spec) {
13
- if (spec.kind === 'string' || spec.kind === 'reference') return {
3
+ if (spec.kind === 'text' || spec.kind === 'string' || spec.kind === 'reference') return {
14
4
  type: 'string'
15
5
  };
16
6
  if (spec.kind === 'number') return {
@@ -35,36 +25,24 @@ function fieldSchema(spec) {
35
25
  return scalarSchema(spec);
36
26
  }
37
27
  function ruleFor(error) {
38
- if (error.keyword === 'required') return 'required-field';
39
- if (error.keyword === 'enum') return 'field-enum';
40
- return 'field-kind';
41
- }
42
- function locationFor(error) {
43
- if (error.keyword === 'required' && typeof error.params.missingProperty === 'string') {
44
- return error.params.missingProperty;
45
- }
46
- const path = error.instancePath.replace(/^\//, '').split('/');
47
- return path[0] ?? '';
28
+ return error.keyword === 'enum' ? 'field-enum' : 'field-kind';
48
29
  }
49
30
  export function validateFields(fields, values) {
50
- const properties = {};
51
- const required = [];
52
31
  for (const [name, spec] of Object.entries(fields)){
53
- properties[name] = fieldSchema(spec);
54
- if (spec.required) required.push(name);
32
+ if (spec.required && values[name] === undefined) return {
33
+ field: name,
34
+ rule: 'required-field'
35
+ };
55
36
  }
56
- const validate = ajv.compile({
57
- type: 'object',
58
- properties,
59
- required,
60
- additionalProperties: true
61
- });
62
- if (validate(values)) return undefined;
63
- const error = validate.errors[0];
64
- return {
65
- field: locationFor(error),
66
- rule: ruleFor(error)
67
- };
37
+ for (const [name, spec] of Object.entries(fields)){
38
+ if (values[name] === undefined) continue;
39
+ const result = new Validator(fieldSchema(spec), '2020-12', false).validate(values[name]);
40
+ if (!result.valid) return {
41
+ field: name,
42
+ rule: ruleFor(result.errors[0])
43
+ };
44
+ }
45
+ return undefined;
68
46
  }
69
47
 
70
48
  //# sourceMappingURL=schema.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/executor/code/agents.house/mdmodel/packages/mdmodel/src/schema.ts"],"sourcesContent":["import { Ajv, type ErrorObject } from 'ajv';\nimport formatsPlugin from 'ajv-formats';\nimport type { FieldSpec, ScalarSpec } from './definition.js';\n\nconst addFormats = formatsPlugin as unknown as (validator: Ajv, names?: string[]) => Ajv;\nconst ajv = new Ajv({ allErrors: true, strict: false, validateFormats: true });\naddFormats(ajv, ['date']);\n\nfunction scalarSchema(spec: ScalarSpec): Record<string, unknown> {\n if (spec.kind === 'string' || spec.kind === 'reference') return { type: 'string' };\n if (spec.kind === 'number') return { type: 'number' };\n if (spec.kind === 'date') return { type: 'string', format: 'date' };\n return { type: 'string', enum: [...spec.values] };\n}\n\nfunction fieldSchema(spec: FieldSpec): Record<string, unknown> {\n if (spec.kind === 'list') return { type: 'array', items: scalarSchema(spec.of) };\n return scalarSchema(spec);\n}\n\nfunction ruleFor(error: ErrorObject): 'required-field' | 'field-enum' | 'field-kind' {\n if (error.keyword === 'required') return 'required-field';\n if (error.keyword === 'enum') return 'field-enum';\n return 'field-kind';\n}\n\nfunction locationFor(error: ErrorObject): string {\n if (error.keyword === 'required' && typeof error.params.missingProperty === 'string') {\n return error.params.missingProperty;\n }\n const path = error.instancePath.replace(/^\\//, '').split('/');\n return path[0] ?? '';\n}\n\nexport function validateFields(\n fields: Readonly<Record<string, FieldSpec>>,\n values: Readonly<Record<string, unknown>>,\n): { field: string; rule: 'required-field' | 'field-enum' | 'field-kind' } | undefined {\n const properties: Record<string, Record<string, unknown>> = {};\n const required: string[] = [];\n for (const [name, spec] of Object.entries(fields)) {\n properties[name] = fieldSchema(spec);\n if (spec.required) required.push(name);\n }\n const validate = ajv.compile({\n type: 'object',\n properties,\n required,\n additionalProperties: true,\n });\n if (validate(values)) return undefined;\n const error = validate.errors![0]!;\n return { field: locationFor(error), rule: ruleFor(error) };\n}\n"],"names":["Ajv","formatsPlugin","addFormats","ajv","allErrors","strict","validateFormats","scalarSchema","spec","kind","type","format","enum","values","fieldSchema","items","of","ruleFor","error","keyword","locationFor","params","missingProperty","path","instancePath","replace","split","validateFields","fields","properties","required","name","Object","entries","push","validate","compile","additionalProperties","undefined","errors","field","rule"],"mappings":"AAAA,SAASA,GAAG,QAA0B,MAAM;AAC5C,OAAOC,mBAAmB,cAAc;AAGxC,MAAMC,aAAaD;AACnB,MAAME,MAAM,IAAIH,IAAI;IAAEI,WAAW;IAAMC,QAAQ;IAAOC,iBAAiB;AAAK;AAC5EJ,WAAWC,KAAK;IAAC;CAAO;AAExB,SAASI,aAAaC,IAAgB;IACpC,IAAIA,KAAKC,IAAI,KAAK,YAAYD,KAAKC,IAAI,KAAK,aAAa,OAAO;QAAEC,MAAM;IAAS;IACjF,IAAIF,KAAKC,IAAI,KAAK,UAAU,OAAO;QAAEC,MAAM;IAAS;IACpD,IAAIF,KAAKC,IAAI,KAAK,QAAQ,OAAO;QAAEC,MAAM;QAAUC,QAAQ;IAAO;IAClE,OAAO;QAAED,MAAM;QAAUE,MAAM;eAAIJ,KAAKK,MAAM;SAAC;IAAC;AAClD;AAEA,SAASC,YAAYN,IAAe;IAClC,IAAIA,KAAKC,IAAI,KAAK,QAAQ,OAAO;QAAEC,MAAM;QAASK,OAAOR,aAAaC,KAAKQ,EAAE;IAAE;IAC/E,OAAOT,aAAaC;AACtB;AAEA,SAASS,QAAQC,KAAkB;IACjC,IAAIA,MAAMC,OAAO,KAAK,YAAY,OAAO;IACzC,IAAID,MAAMC,OAAO,KAAK,QAAQ,OAAO;IACrC,OAAO;AACT;AAEA,SAASC,YAAYF,KAAkB;IACrC,IAAIA,MAAMC,OAAO,KAAK,cAAc,OAAOD,MAAMG,MAAM,CAACC,eAAe,KAAK,UAAU;QACpF,OAAOJ,MAAMG,MAAM,CAACC,eAAe;IACrC;IACA,MAAMC,OAAOL,MAAMM,YAAY,CAACC,OAAO,CAAC,OAAO,IAAIC,KAAK,CAAC;IACzD,OAAOH,IAAI,CAAC,EAAE,IAAI;AACpB;AAEA,OAAO,SAASI,eACdC,MAA2C,EAC3Cf,MAAyC;IAEzC,MAAMgB,aAAsD,CAAC;IAC7D,MAAMC,WAAqB,EAAE;IAC7B,KAAK,MAAM,CAACC,MAAMvB,KAAK,IAAIwB,OAAOC,OAAO,CAACL,QAAS;QACjDC,UAAU,CAACE,KAAK,GAAGjB,YAAYN;QAC/B,IAAIA,KAAKsB,QAAQ,EAAEA,SAASI,IAAI,CAACH;IACnC;IACA,MAAMI,WAAWhC,IAAIiC,OAAO,CAAC;QAC3B1B,MAAM;QACNmB;QACAC;QACAO,sBAAsB;IACxB;IACA,IAAIF,SAAStB,SAAS,OAAOyB;IAC7B,MAAMpB,QAAQiB,SAASI,MAAM,AAAC,CAAC,EAAE;IACjC,OAAO;QAAEC,OAAOpB,YAAYF;QAAQuB,MAAMxB,QAAQC;IAAO;AAC3D"}
1
+ {"version":3,"sources":["/workspace/packages/mdmodel/src/schema.ts"],"sourcesContent":["import { Validator, type OutputUnit } from '@cfworker/json-schema';\nimport type { FieldSpec, ScalarSpec } from './definition.js';\n\nfunction scalarSchema(spec: ScalarSpec): Record<string, unknown> {\n if (spec.kind === 'text' || spec.kind === 'string' || spec.kind === 'reference')\n return { type: 'string' };\n if (spec.kind === 'number') return { type: 'number' };\n if (spec.kind === 'date') return { type: 'string', format: 'date' };\n return { type: 'string', enum: [...spec.values] };\n}\n\nfunction fieldSchema(spec: FieldSpec): Record<string, unknown> {\n if (spec.kind === 'list') return { type: 'array', items: scalarSchema(spec.of) };\n return scalarSchema(spec);\n}\n\nfunction ruleFor(error: OutputUnit): 'field-enum' | 'field-kind' {\n return error.keyword === 'enum' ? 'field-enum' : 'field-kind';\n}\n\nexport function validateFields(\n fields: Readonly<Record<string, FieldSpec>>,\n values: Readonly<Record<string, unknown>>,\n): { field: string; rule: 'required-field' | 'field-enum' | 'field-kind' } | undefined {\n for (const [name, spec] of Object.entries(fields)) {\n if (spec.required && values[name] === undefined) return { field: name, rule: 'required-field' };\n }\n for (const [name, spec] of Object.entries(fields)) {\n if (values[name] === undefined) continue;\n const result = new Validator(fieldSchema(spec), '2020-12', false).validate(values[name]);\n if (!result.valid) return { field: name, rule: ruleFor(result.errors[0]!) };\n }\n return undefined;\n}\n"],"names":["Validator","scalarSchema","spec","kind","type","format","enum","values","fieldSchema","items","of","ruleFor","error","keyword","validateFields","fields","name","Object","entries","required","undefined","field","rule","result","validate","valid","errors"],"mappings":"AAAA,SAASA,SAAS,QAAyB,wBAAwB;AAGnE,SAASC,aAAaC,IAAgB;IACpC,IAAIA,KAAKC,IAAI,KAAK,UAAUD,KAAKC,IAAI,KAAK,YAAYD,KAAKC,IAAI,KAAK,aAClE,OAAO;QAAEC,MAAM;IAAS;IAC1B,IAAIF,KAAKC,IAAI,KAAK,UAAU,OAAO;QAAEC,MAAM;IAAS;IACpD,IAAIF,KAAKC,IAAI,KAAK,QAAQ,OAAO;QAAEC,MAAM;QAAUC,QAAQ;IAAO;IAClE,OAAO;QAAED,MAAM;QAAUE,MAAM;eAAIJ,KAAKK,MAAM;SAAC;IAAC;AAClD;AAEA,SAASC,YAAYN,IAAe;IAClC,IAAIA,KAAKC,IAAI,KAAK,QAAQ,OAAO;QAAEC,MAAM;QAASK,OAAOR,aAAaC,KAAKQ,EAAE;IAAE;IAC/E,OAAOT,aAAaC;AACtB;AAEA,SAASS,QAAQC,KAAiB;IAChC,OAAOA,MAAMC,OAAO,KAAK,SAAS,eAAe;AACnD;AAEA,OAAO,SAASC,eACdC,MAA2C,EAC3CR,MAAyC;IAEzC,KAAK,MAAM,CAACS,MAAMd,KAAK,IAAIe,OAAOC,OAAO,CAACH,QAAS;QACjD,IAAIb,KAAKiB,QAAQ,IAAIZ,MAAM,CAACS,KAAK,KAAKI,WAAW,OAAO;YAAEC,OAAOL;YAAMM,MAAM;QAAiB;IAChG;IACA,KAAK,MAAM,CAACN,MAAMd,KAAK,IAAIe,OAAOC,OAAO,CAACH,QAAS;QACjD,IAAIR,MAAM,CAACS,KAAK,KAAKI,WAAW;QAChC,MAAMG,SAAS,IAAIvB,UAAUQ,YAAYN,OAAO,WAAW,OAAOsB,QAAQ,CAACjB,MAAM,CAACS,KAAK;QACvF,IAAI,CAACO,OAAOE,KAAK,EAAE,OAAO;YAAEJ,OAAOL;YAAMM,MAAMX,QAAQY,OAAOG,MAAM,CAAC,EAAE;QAAG;IAC5E;IACA,OAAON;AACT"}
@@ -1 +1 @@
1
- {"version":3,"file":"sections.d.ts","sourceRoot":"","sources":["../src/sections.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,WAAW,GAAG;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B,CAAC;AAqBF,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,WAAW,EAAE,CAwB9E;AAED,wBAAgB,cAAc,CAC5B,KAAK,EAAE,SAAS,WAAW,EAAE,EAC7B,OAAO,EAAE,MAAM,GACd,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAOxB;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAiBtD"}
1
+ {"version":3,"file":"sections.d.ts","sourceRoot":"","sources":["../src/sections.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,WAAW,GAAG;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B,CAAC;AAIF,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,WAAW,EAAE,CAc9E;AAED,wBAAgB,cAAc,CAC5B,KAAK,EAAE,SAAS,WAAW,EAAE,EAC7B,OAAO,EAAE,MAAM,GACd,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAOxB;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAiBtD"}
package/dist/sections.js CHANGED
@@ -1,57 +1,24 @@
1
- const H2 = /^##(?!#)[ \t]+(.+?)[ \t]*#*[ \t]*$/;
1
+ import { headingLines } from './blocks.js';
2
2
  const ITEM = /^([ \t]*)([-*+]|\d+[.)])[ \t]+/;
3
- function lineRange(content, from) {
4
- const nl = content.indexOf('\n', from);
5
- const end = nl === -1 ? content.length : nl;
6
- let line = content.slice(from, end);
7
- if (line.endsWith('\r')) line = line.slice(0, -1);
8
- return {
9
- line,
10
- start: from,
11
- next: nl === -1 ? content.length : nl + 1
12
- };
13
- }
14
- function fenceMarker(line) {
15
- let i = 0;
16
- while(i < 3 && (line[i] === ' ' || line[i] === '\t'))i += 1;
17
- if (line.startsWith('```', i)) return '```';
18
- if (line.startsWith('~~~', i)) return '~~~';
19
- return null;
20
- }
21
3
  export function headingSpans(content, bodyStart) {
22
4
  const found = [];
23
- let i = bodyStart;
24
- let fence = null;
25
- while(i < content.length){
26
- const { line, start, next } = lineRange(content, i);
27
- const marker = fenceMarker(line);
28
- if (fence !== null) {
29
- if (marker === fence) fence = null;
30
- } else if (marker !== null) {
31
- fence = marker;
32
- } else {
33
- const match = H2.exec(line);
34
- if (match !== null && match[1] !== undefined) {
35
- const name = match[1].trim();
36
- if (found.length > 0) found[found.length - 1] = {
37
- ...found[found.length - 1],
38
- bodyEnd: start
39
- };
40
- found.push({
41
- name,
42
- headingStart: start,
43
- bodyStart: next,
44
- bodyEnd: content.length
45
- });
46
- }
47
- }
48
- if (next === i) break;
49
- i = next;
5
+ for (const heading of headingLines(content, bodyStart, content.length)){
6
+ if (heading.level !== 2) continue;
7
+ if (found.length > 0) found[found.length - 1] = {
8
+ ...found[found.length - 1],
9
+ bodyEnd: heading.start
10
+ };
11
+ found.push({
12
+ name: heading.name,
13
+ headingStart: heading.start,
14
+ bodyStart: heading.next,
15
+ bodyEnd: content.length
16
+ });
50
17
  }
51
18
  return found;
52
19
  }
53
20
  export function sectionsRecord(spans, content) {
54
- const record = {};
21
+ const record = Object.create(null);
55
22
  for (const span of spans){
56
23
  if (record[span.name] === undefined) record[span.name] = content.slice(span.bodyStart, span.bodyEnd);
57
24
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/executor/code/agents.house/mdmodel/packages/mdmodel/src/sections.ts"],"sourcesContent":["export type SectionSpan = {\n readonly name: string;\n readonly headingStart: number;\n readonly bodyStart: number;\n readonly bodyEnd: number;\n};\n\nconst H2 = /^##(?!#)[ \\t]+(.+?)[ \\t]*#*[ \\t]*$/;\nconst ITEM = /^([ \\t]*)([-*+]|\\d+[.)])[ \\t]+/;\n\nfunction lineRange(content: string, from: number): { line: string; next: number; start: number } {\n const nl = content.indexOf('\\n', from);\n const end = nl === -1 ? content.length : nl;\n let line = content.slice(from, end);\n if (line.endsWith('\\r')) line = line.slice(0, -1);\n return { line, start: from, next: nl === -1 ? content.length : nl + 1 };\n}\n\nfunction fenceMarker(line: string): '```' | '~~~' | null {\n let i = 0;\n while (i < 3 && (line[i] === ' ' || line[i] === '\\t')) i += 1;\n if (line.startsWith('```', i)) return '```';\n if (line.startsWith('~~~', i)) return '~~~';\n return null;\n}\n\nexport function headingSpans(content: string, bodyStart: number): SectionSpan[] {\n const found: SectionSpan[] = [];\n let i = bodyStart;\n let fence: '```' | '~~~' | null = null;\n while (i < content.length) {\n const { line, start, next } = lineRange(content, i);\n const marker = fenceMarker(line);\n if (fence !== null) {\n if (marker === fence) fence = null;\n } else if (marker !== null) {\n fence = marker;\n } else {\n const match = H2.exec(line);\n if (match !== null && match[1] !== undefined) {\n const name = match[1].trim();\n if (found.length > 0)\n found[found.length - 1] = { ...found[found.length - 1]!, bodyEnd: start };\n found.push({ name, headingStart: start, bodyStart: next, bodyEnd: content.length });\n }\n }\n if (next === i) break;\n i = next;\n }\n return found;\n}\n\nexport function sectionsRecord(\n spans: readonly SectionSpan[],\n content: string,\n): Record<string, string> {\n const record: Record<string, string> = {};\n for (const span of spans) {\n if (record[span.name] === undefined)\n record[span.name] = content.slice(span.bodyStart, span.bodyEnd);\n }\n return record;\n}\n\nexport function parseListSection(body: string): boolean {\n const lines = body.split(/\\r?\\n/);\n while (lines.length > 0 && lines[0]!.trim() === '') lines.shift();\n while (lines.length > 0 && lines[lines.length - 1]!.trim() === '') lines.pop();\n if (lines.length === 0) return true;\n const first = ITEM.exec(lines[0]!);\n if (first === null || first[1] === undefined) return false;\n const indent = first[1].length;\n for (const line of lines) {\n if (line.trim() === '') continue;\n const match = ITEM.exec(line);\n if (match !== null && match[1]?.length === indent) continue;\n const leading = /^\\s*/.exec(line)?.[0].length ?? 0;\n if (leading > indent) continue;\n return false;\n }\n return true;\n}\n"],"names":["H2","ITEM","lineRange","content","from","nl","indexOf","end","length","line","slice","endsWith","start","next","fenceMarker","i","startsWith","headingSpans","bodyStart","found","fence","marker","match","exec","undefined","name","trim","bodyEnd","push","headingStart","sectionsRecord","spans","record","span","parseListSection","body","lines","split","shift","pop","first","indent","leading"],"mappings":"AAOA,MAAMA,KAAK;AACX,MAAMC,OAAO;AAEb,SAASC,UAAUC,OAAe,EAAEC,IAAY;IAC9C,MAAMC,KAAKF,QAAQG,OAAO,CAAC,MAAMF;IACjC,MAAMG,MAAMF,OAAO,CAAC,IAAIF,QAAQK,MAAM,GAAGH;IACzC,IAAII,OAAON,QAAQO,KAAK,CAACN,MAAMG;IAC/B,IAAIE,KAAKE,QAAQ,CAAC,OAAOF,OAAOA,KAAKC,KAAK,CAAC,GAAG,CAAC;IAC/C,OAAO;QAAED;QAAMG,OAAOR;QAAMS,MAAMR,OAAO,CAAC,IAAIF,QAAQK,MAAM,GAAGH,KAAK;IAAE;AACxE;AAEA,SAASS,YAAYL,IAAY;IAC/B,IAAIM,IAAI;IACR,MAAOA,IAAI,KAAMN,CAAAA,IAAI,CAACM,EAAE,KAAK,OAAON,IAAI,CAACM,EAAE,KAAK,IAAG,EAAIA,KAAK;IAC5D,IAAIN,KAAKO,UAAU,CAAC,OAAOD,IAAI,OAAO;IACtC,IAAIN,KAAKO,UAAU,CAAC,OAAOD,IAAI,OAAO;IACtC,OAAO;AACT;AAEA,OAAO,SAASE,aAAad,OAAe,EAAEe,SAAiB;IAC7D,MAAMC,QAAuB,EAAE;IAC/B,IAAIJ,IAAIG;IACR,IAAIE,QAA8B;IAClC,MAAOL,IAAIZ,QAAQK,MAAM,CAAE;QACzB,MAAM,EAAEC,IAAI,EAAEG,KAAK,EAAEC,IAAI,EAAE,GAAGX,UAAUC,SAASY;QACjD,MAAMM,SAASP,YAAYL;QAC3B,IAAIW,UAAU,MAAM;YAClB,IAAIC,WAAWD,OAAOA,QAAQ;QAChC,OAAO,IAAIC,WAAW,MAAM;YAC1BD,QAAQC;QACV,OAAO;YACL,MAAMC,QAAQtB,GAAGuB,IAAI,CAACd;YACtB,IAAIa,UAAU,QAAQA,KAAK,CAAC,EAAE,KAAKE,WAAW;gBAC5C,MAAMC,OAAOH,KAAK,CAAC,EAAE,CAACI,IAAI;gBAC1B,IAAIP,MAAMX,MAAM,GAAG,GACjBW,KAAK,CAACA,MAAMX,MAAM,GAAG,EAAE,GAAG;oBAAE,GAAGW,KAAK,CAACA,MAAMX,MAAM,GAAG,EAAE;oBAAGmB,SAASf;gBAAM;gBAC1EO,MAAMS,IAAI,CAAC;oBAAEH;oBAAMI,cAAcjB;oBAAOM,WAAWL;oBAAMc,SAASxB,QAAQK,MAAM;gBAAC;YACnF;QACF;QACA,IAAIK,SAASE,GAAG;QAChBA,IAAIF;IACN;IACA,OAAOM;AACT;AAEA,OAAO,SAASW,eACdC,KAA6B,EAC7B5B,OAAe;IAEf,MAAM6B,SAAiC,CAAC;IACxC,KAAK,MAAMC,QAAQF,MAAO;QACxB,IAAIC,MAAM,CAACC,KAAKR,IAAI,CAAC,KAAKD,WACxBQ,MAAM,CAACC,KAAKR,IAAI,CAAC,GAAGtB,QAAQO,KAAK,CAACuB,KAAKf,SAAS,EAAEe,KAAKN,OAAO;IAClE;IACA,OAAOK;AACT;AAEA,OAAO,SAASE,iBAAiBC,IAAY;IAC3C,MAAMC,QAAQD,KAAKE,KAAK,CAAC;IACzB,MAAOD,MAAM5B,MAAM,GAAG,KAAK4B,KAAK,CAAC,EAAE,CAAEV,IAAI,OAAO,GAAIU,MAAME,KAAK;IAC/D,MAAOF,MAAM5B,MAAM,GAAG,KAAK4B,KAAK,CAACA,MAAM5B,MAAM,GAAG,EAAE,CAAEkB,IAAI,OAAO,GAAIU,MAAMG,GAAG;IAC5E,IAAIH,MAAM5B,MAAM,KAAK,GAAG,OAAO;IAC/B,MAAMgC,QAAQvC,KAAKsB,IAAI,CAACa,KAAK,CAAC,EAAE;IAChC,IAAII,UAAU,QAAQA,KAAK,CAAC,EAAE,KAAKhB,WAAW,OAAO;IACrD,MAAMiB,SAASD,KAAK,CAAC,EAAE,CAAChC,MAAM;IAC9B,KAAK,MAAMC,QAAQ2B,MAAO;QACxB,IAAI3B,KAAKiB,IAAI,OAAO,IAAI;QACxB,MAAMJ,QAAQrB,KAAKsB,IAAI,CAACd;QACxB,IAAIa,UAAU,QAAQA,KAAK,CAAC,EAAE,EAAEd,WAAWiC,QAAQ;QACnD,MAAMC,UAAU,OAAOnB,IAAI,CAACd,OAAO,CAAC,EAAE,CAACD,UAAU;QACjD,IAAIkC,UAAUD,QAAQ;QACtB,OAAO;IACT;IACA,OAAO;AACT"}
1
+ {"version":3,"sources":["/workspace/packages/mdmodel/src/sections.ts"],"sourcesContent":["import { headingLines } from './blocks.js';\n\nexport type SectionSpan = {\n readonly name: string;\n readonly headingStart: number;\n readonly bodyStart: number;\n readonly bodyEnd: number;\n};\n\nconst ITEM = /^([ \\t]*)([-*+]|\\d+[.)])[ \\t]+/;\n\nexport function headingSpans(content: string, bodyStart: number): SectionSpan[] {\n const found: SectionSpan[] = [];\n for (const heading of headingLines(content, bodyStart, content.length)) {\n if (heading.level !== 2) continue;\n if (found.length > 0)\n found[found.length - 1] = { ...found[found.length - 1]!, bodyEnd: heading.start };\n found.push({\n name: heading.name,\n headingStart: heading.start,\n bodyStart: heading.next,\n bodyEnd: content.length,\n });\n }\n return found;\n}\n\nexport function sectionsRecord(\n spans: readonly SectionSpan[],\n content: string,\n): Record<string, string> {\n const record: Record<string, string> = Object.create(null);\n for (const span of spans) {\n if (record[span.name] === undefined)\n record[span.name] = content.slice(span.bodyStart, span.bodyEnd);\n }\n return record;\n}\n\nexport function parseListSection(body: string): boolean {\n const lines = body.split(/\\r?\\n/);\n while (lines.length > 0 && lines[0]!.trim() === '') lines.shift();\n while (lines.length > 0 && lines[lines.length - 1]!.trim() === '') lines.pop();\n if (lines.length === 0) return true;\n const first = ITEM.exec(lines[0]!);\n if (first === null || first[1] === undefined) return false;\n const indent = first[1].length;\n for (const line of lines) {\n if (line.trim() === '') continue;\n const match = ITEM.exec(line);\n if (match !== null && match[1]?.length === indent) continue;\n const leading = /^\\s*/.exec(line)?.[0].length ?? 0;\n if (leading > indent) continue;\n return false;\n }\n return true;\n}\n"],"names":["headingLines","ITEM","headingSpans","content","bodyStart","found","heading","length","level","bodyEnd","start","push","name","headingStart","next","sectionsRecord","spans","record","Object","create","span","undefined","slice","parseListSection","body","lines","split","trim","shift","pop","first","exec","indent","line","match","leading"],"mappings":"AAAA,SAASA,YAAY,QAAQ,cAAc;AAS3C,MAAMC,OAAO;AAEb,OAAO,SAASC,aAAaC,OAAe,EAAEC,SAAiB;IAC7D,MAAMC,QAAuB,EAAE;IAC/B,KAAK,MAAMC,WAAWN,aAAaG,SAASC,WAAWD,QAAQI,MAAM,EAAG;QACtE,IAAID,QAAQE,KAAK,KAAK,GAAG;QACzB,IAAIH,MAAME,MAAM,GAAG,GACjBF,KAAK,CAACA,MAAME,MAAM,GAAG,EAAE,GAAG;YAAE,GAAGF,KAAK,CAACA,MAAME,MAAM,GAAG,EAAE;YAAGE,SAASH,QAAQI,KAAK;QAAC;QAClFL,MAAMM,IAAI,CAAC;YACTC,MAAMN,QAAQM,IAAI;YAClBC,cAAcP,QAAQI,KAAK;YAC3BN,WAAWE,QAAQQ,IAAI;YACvBL,SAASN,QAAQI,MAAM;QACzB;IACF;IACA,OAAOF;AACT;AAEA,OAAO,SAASU,eACdC,KAA6B,EAC7Bb,OAAe;IAEf,MAAMc,SAAiCC,OAAOC,MAAM,CAAC;IACrD,KAAK,MAAMC,QAAQJ,MAAO;QACxB,IAAIC,MAAM,CAACG,KAAKR,IAAI,CAAC,KAAKS,WACxBJ,MAAM,CAACG,KAAKR,IAAI,CAAC,GAAGT,QAAQmB,KAAK,CAACF,KAAKhB,SAAS,EAAEgB,KAAKX,OAAO;IAClE;IACA,OAAOQ;AACT;AAEA,OAAO,SAASM,iBAAiBC,IAAY;IAC3C,MAAMC,QAAQD,KAAKE,KAAK,CAAC;IACzB,MAAOD,MAAMlB,MAAM,GAAG,KAAKkB,KAAK,CAAC,EAAE,CAAEE,IAAI,OAAO,GAAIF,MAAMG,KAAK;IAC/D,MAAOH,MAAMlB,MAAM,GAAG,KAAKkB,KAAK,CAACA,MAAMlB,MAAM,GAAG,EAAE,CAAEoB,IAAI,OAAO,GAAIF,MAAMI,GAAG;IAC5E,IAAIJ,MAAMlB,MAAM,KAAK,GAAG,OAAO;IAC/B,MAAMuB,QAAQ7B,KAAK8B,IAAI,CAACN,KAAK,CAAC,EAAE;IAChC,IAAIK,UAAU,QAAQA,KAAK,CAAC,EAAE,KAAKT,WAAW,OAAO;IACrD,MAAMW,SAASF,KAAK,CAAC,EAAE,CAACvB,MAAM;IAC9B,KAAK,MAAM0B,QAAQR,MAAO;QACxB,IAAIQ,KAAKN,IAAI,OAAO,IAAI;QACxB,MAAMO,QAAQjC,KAAK8B,IAAI,CAACE;QACxB,IAAIC,UAAU,QAAQA,KAAK,CAAC,EAAE,EAAE3B,WAAWyB,QAAQ;QACnD,MAAMG,UAAU,OAAOJ,IAAI,CAACE,OAAO,CAAC,EAAE,CAAC1B,UAAU;QACjD,IAAI4B,UAAUH,QAAQ;QACtB,OAAO;IACT;IACA,OAAO;AACT"}
package/dist/types.d.ts CHANGED
@@ -1,8 +1,26 @@
1
1
  export type Library = Readonly<Record<string, string>>;
2
+ export type ValidationContext = {
3
+ readonly binaryTargets: readonly string[];
4
+ };
5
+ export type ProjectionContext = {
6
+ readonly documentTargets: readonly string[];
7
+ readonly definition?: string;
8
+ };
9
+ export type DivisionContext = ProjectionContext & {
10
+ readonly budget: number;
11
+ };
12
+ export type Extent = {
13
+ readonly start: number;
14
+ readonly end: number;
15
+ };
16
+ export type Span = Extent & {
17
+ readonly text: string;
18
+ };
2
19
  export type Document = {
3
20
  readonly content: string;
4
21
  readonly type: string | undefined;
5
22
  readonly fields: Readonly<Record<string, unknown>>;
23
+ readonly preamble: string;
6
24
  readonly sections: Readonly<Record<string, string>>;
7
25
  };
8
26
  export type Refusal = {
@@ -17,6 +35,20 @@ export type Validation = {
17
35
  } | ({
18
36
  readonly verdict: 'refuse';
19
37
  } & Refusal);
38
+ export type Projection = {
39
+ readonly ok: true;
40
+ readonly text: string;
41
+ } | {
42
+ readonly ok: false;
43
+ readonly refusal: Refusal;
44
+ };
45
+ export type Division = {
46
+ readonly ok: true;
47
+ readonly spans: readonly Span[];
48
+ } | {
49
+ readonly ok: false;
50
+ readonly refusal: Refusal;
51
+ };
20
52
  export type ParseResult = {
21
53
  readonly ok: true;
22
54
  readonly document: Document;
@@ -24,6 +56,10 @@ export type ParseResult = {
24
56
  readonly ok: false;
25
57
  readonly refusal: Refusal;
26
58
  };
59
+ export type Link = {
60
+ readonly href: string;
61
+ readonly target: string;
62
+ };
27
63
  export type Change = {
28
64
  readonly kind: 'field';
29
65
  readonly name: string;
@@ -34,6 +70,14 @@ export type Change = {
34
70
  readonly name: string;
35
71
  readonly value: string;
36
72
  readonly prior: string;
73
+ } | {
74
+ readonly kind: 'preamble';
75
+ readonly value: string;
76
+ readonly prior: string;
77
+ } | {
78
+ readonly kind: 'link';
79
+ readonly href: string;
80
+ readonly to: string;
37
81
  };
38
82
  export type ApplyResult = {
39
83
  readonly outcome: 'applied';
@@ -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,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,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,CAAC;AAEN,MAAM,MAAM,WAAW,GACnB;IAAE,QAAQ,CAAC,OAAO,EAAE,SAAS,CAAC;IAAC,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAA;CAAE,GAC5D;IAAE,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAA;CAAE,GAC1D;IAAE,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAAC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/dist/types.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/executor/code/agents.house/mdmodel/packages/mdmodel/src/types.ts"],"sourcesContent":["export type Library = Readonly<Record<string, string>>;\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 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\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":"AAuCA,WAG8D"}
1
+ {"version":3,"sources":["/workspace/packages/mdmodel/src/types.ts"],"sourcesContent":["export type Library = Readonly<Record<string, string>>;\n\nexport type ValidationContext = {\n readonly binaryTargets: readonly string[];\n};\n\nexport type ProjectionContext = {\n readonly documentTargets: readonly string[];\n readonly definition?: string;\n};\n\nexport type DivisionContext = ProjectionContext & {\n readonly budget: number;\n};\n\nexport type Extent = {\n readonly start: number;\n readonly end: number;\n};\n\nexport type Span = Extent & {\n readonly text: 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 Division =\n | { readonly ok: true; readonly spans: readonly Span[] }\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":"AAqFA,WAG8D"}
@@ -1,3 +1,3 @@
1
- import type { Library, Validation } from './types.js';
2
- export declare function validate(library: Library): Validation;
1
+ import type { Library, Validation, ValidationContext } from './types.js';
2
+ export declare function validate(library: Library, context?: ValidationContext): Validation;
3
3
  //# sourceMappingURL=validate.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../src/validate.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAY,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAuChE,wBAAgB,QAAQ,CAAC,OAAO,EAAE,OAAO,GAAG,UAAU,CA6GrD"}
1
+ {"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../src/validate.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAY,OAAO,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAuCnF,wBAAgB,QAAQ,CACtB,OAAO,EAAE,OAAO,EAChB,OAAO,GAAE,iBAAyC,GACjD,UAAU,CAuHZ"}
package/dist/validate.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { readDefinition } from './definition.js';
2
2
  import { parse } from './document.js';
3
- import { extractRelativeHrefs } from './links.js';
3
+ import { hrefOccurrences } from './links.js';
4
4
  import { classifyHref, isTypesPath } from './paths.js';
5
5
  import { refusal } from './refuse.js';
6
6
  import { parseListSection } from './sections.js';
@@ -38,10 +38,14 @@ function declaredTypeOf(content) {
38
38
  if (!parsed.ok) return undefined;
39
39
  return parsed.document.type ?? 'any';
40
40
  }
41
- export function validate(library) {
41
+ export function validate(library, context = {
42
+ binaryTargets: []
43
+ }) {
44
+ const binaryTargets = new Set(context.binaryTargets);
42
45
  const files = Object.keys(library).sort();
43
46
  const documents = new Map();
44
47
  for (const file of files){
48
+ if (binaryTargets.has(file)) return refuse(file, 'binary-target-document');
45
49
  const parsed = parse(library[file], file);
46
50
  documents.set(file, parsed);
47
51
  if (!parsed.ok) return {
@@ -86,6 +90,11 @@ export function validate(library) {
86
90
  location: fieldFailure.field
87
91
  });
88
92
  }
93
+ const declaredFields = new Set(Object.keys(definition.fields));
94
+ const undeclared = Object.keys(parsed.document.fields).find((name)=>name !== 'type' && !declaredFields.has(name));
95
+ if (undeclared !== undefined) return refuse(file, 'undeclared-field', {
96
+ location: undeclared
97
+ });
89
98
  for (const [name, spec] of Object.entries(definition.sections)){
90
99
  const body = parsed.document.sections[name];
91
100
  if (body === undefined) {
@@ -114,10 +123,10 @@ export function validate(library) {
114
123
  if (expected === null) continue;
115
124
  for (const target of targets){
116
125
  const resolved = classifyHref(file, target);
117
- if (resolved.kind !== 'path') return refuse(file, 'unresolved-reference', {
126
+ if (resolved.kind !== 'path' || binaryTargets.has(resolved.path)) return refuse(file, 'unresolved-reference', {
118
127
  location: name
119
128
  });
120
- if (!(resolved.path in library)) continue;
129
+ if (!documents.has(resolved.path)) continue;
121
130
  const targetType = declaredTypeOf(library[resolved.path]);
122
131
  if (targetType !== expected) return refuse(file, 'unresolved-reference', {
123
132
  location: name
@@ -125,10 +134,10 @@ export function validate(library) {
125
134
  }
126
135
  }
127
136
  const body = bodyAfterFrontmatter(library[file]);
128
- for (const href of extractRelativeHrefs(body)){
129
- const resolved = classifyHref(file, href);
137
+ for (const occurrence of hrefOccurrences(body)){
138
+ const resolved = classifyHref(file, occurrence.href);
130
139
  if (resolved.kind === 'skip') continue;
131
- if (resolved.kind === 'invalid' || !(resolved.path in library)) {
140
+ if (resolved.kind === 'invalid' || !documents.has(resolved.path) && !binaryTargets.has(resolved.path)) {
132
141
  return refuse(file, 'unresolved-link');
133
142
  }
134
143
  }
@@ -147,7 +156,7 @@ export function validate(library) {
147
156
  for (const target of referenceTargets(spec, parsed.document.fields[name])){
148
157
  const resolved = classifyHref(file, target);
149
158
  if (resolved.kind !== 'path') continue;
150
- if (resolved.path in library) continue;
159
+ if (documents.has(resolved.path)) continue;
151
160
  const referrers = missing.get(resolved.path) ?? [];
152
161
  referrers.push(file);
153
162
  missing.set(resolved.path, referrers);