@agentshouse/mdmodel 0.1.0-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +57 -0
- package/dist/definition.d.ts +36 -0
- package/dist/definition.d.ts.map +1 -0
- package/dist/definition.js +165 -0
- package/dist/definition.js.map +1 -0
- package/dist/document.d.ts +5 -0
- package/dist/document.d.ts.map +1 -0
- package/dist/document.js +154 -0
- package/dist/document.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/links.d.ts +2 -0
- package/dist/links.d.ts.map +1 -0
- package/dist/links.js +142 -0
- package/dist/links.js.map +1 -0
- package/dist/paths.d.ts +14 -0
- package/dist/paths.d.ts.map +1 -0
- package/dist/paths.js +54 -0
- package/dist/paths.js.map +1 -0
- package/dist/profile.d.ts +6 -0
- package/dist/profile.d.ts.map +1 -0
- package/dist/profile.js +7 -0
- package/dist/profile.js.map +1 -0
- package/dist/refuse.d.ts +6 -0
- package/dist/refuse.d.ts.map +1 -0
- package/dist/refuse.js +16 -0
- package/dist/refuse.js.map +1 -0
- package/dist/schema.d.ts +6 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/schema.js +70 -0
- package/dist/schema.js.map +1 -0
- package/dist/sections.d.ts +10 -0
- package/dist/sections.d.ts.map +1 -0
- package/dist/sections.js +79 -0
- package/dist/sections.js.map +1 -0
- package/dist/types.d.ts +48 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/dist/validate.d.ts +3 -0
- package/dist/validate.d.ts.map +1 -0
- package/dist/validate.js +174 -0
- package/dist/validate.js.map +1 -0
- package/dist/version.d.ts +2 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +3 -0
- package/dist/version.js.map +1 -0
- package/dist/yaml.d.ts +49 -0
- package/dist/yaml.d.ts.map +1 -0
- package/dist/yaml.js +427 -0
- package/dist/yaml.js.map +1 -0
- package/fixtures/README.md +65 -0
- package/fixtures/cases/any-without-type/expected.json +1 -0
- package/fixtures/cases/any-without-type/library/note.md +3 -0
- package/fixtures/cases/field-enum/expected.json +7 -0
- package/fixtures/cases/field-enum/library/item.md +9 -0
- package/fixtures/cases/field-enum/library/types/task.md +8 -0
- package/fixtures/cases/malformed-definition/expected.json +7 -0
- package/fixtures/cases/malformed-definition/library/types/task.md +5 -0
- package/fixtures/cases/missing-required-field/expected.json +7 -0
- package/fixtures/cases/missing-required-field/library/item.md +8 -0
- package/fixtures/cases/missing-required-field/library/types/task.md +8 -0
- package/fixtures/cases/prd-definition/expected.json +1 -0
- package/fixtures/cases/prd-definition/library/types/task.md +15 -0
- package/fixtures/cases/referenced-missing-file/expected.json +7 -0
- package/fixtures/cases/referenced-missing-file/library/a.md +9 -0
- package/fixtures/cases/referenced-missing-file/library/b.md +9 -0
- package/fixtures/cases/referenced-missing-file/library/types/person.md +5 -0
- package/fixtures/cases/referenced-missing-file/library/types/task.md +8 -0
- package/fixtures/cases/structured-section/expected.json +7 -0
- package/fixtures/cases/structured-section/library/item.md +12 -0
- package/fixtures/cases/structured-section/library/types/task.md +8 -0
- package/fixtures/cases/unclosed-frontmatter/expected.json +6 -0
- package/fixtures/cases/unclosed-frontmatter/library/note.md +3 -0
- package/fixtures/cases/unresolved-link/expected.json +6 -0
- package/fixtures/cases/unresolved-link/library/item.md +8 -0
- package/fixtures/cases/unresolved-link/library/types/task.md +7 -0
- package/fixtures/cases/valid-typed-document/expected.json +1 -0
- package/fixtures/cases/valid-typed-document/library/people/ada.md +4 -0
- package/fixtures/cases/valid-typed-document/library/tasks/one.md +17 -0
- package/fixtures/cases/valid-typed-document/library/types/person.md +5 -0
- package/fixtures/cases/valid-typed-document/library/types/task.md +12 -0
- package/markdown-syntax-profile.md +9 -0
- package/package.json +41 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Anton (trof3039)
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# `@agentshouse/mdmodel`
|
|
2
|
+
|
|
3
|
+
The standalone MD Model: the MD Type language, its parser, its validator, the
|
|
4
|
+
deterministic application of one structured change to Document content, the
|
|
5
|
+
Markdown syntax profile, and the conformance fixtures.
|
|
6
|
+
|
|
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.
|
|
9
|
+
|
|
10
|
+
## Install
|
|
11
|
+
|
|
12
|
+
```sh
|
|
13
|
+
npm install @agentshouse/mdmodel
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
The first prerelease is published after the User confirms.
|
|
17
|
+
|
|
18
|
+
## API
|
|
19
|
+
|
|
20
|
+
```ts
|
|
21
|
+
import {
|
|
22
|
+
apply,
|
|
23
|
+
MARKDOWN_SYNTAX_PROFILE,
|
|
24
|
+
parse,
|
|
25
|
+
serialize,
|
|
26
|
+
validate,
|
|
27
|
+
VERSION,
|
|
28
|
+
} from '@agentshouse/mdmodel';
|
|
29
|
+
|
|
30
|
+
validate({ 'note.md': '# Hello\n' });
|
|
31
|
+
const parsed = parse(content);
|
|
32
|
+
if (parsed.ok) {
|
|
33
|
+
const next = apply(parsed.document, {
|
|
34
|
+
kind: 'field',
|
|
35
|
+
name: 'title',
|
|
36
|
+
value: 'World',
|
|
37
|
+
prior: 'Hello',
|
|
38
|
+
});
|
|
39
|
+
if (next.outcome === 'applied') serialize(next.document);
|
|
40
|
+
}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
`validate` takes one Library state and returns acceptance or one refusal
|
|
44
|
+
naming the file, the field or section, the violated rule, and `VERSION`.
|
|
45
|
+
`apply` sets one typed field, including a list field as a whole, or replaces
|
|
46
|
+
one declared section body against that value's prior content. A field change
|
|
47
|
+
does not reformat the rest of the file.
|
|
48
|
+
|
|
49
|
+
## Fixtures
|
|
50
|
+
|
|
51
|
+
The conformance corpus ships in `fixtures/`. Its verdict format and rule ids
|
|
52
|
+
are documented in [`fixtures/README.md`](fixtures/README.md).
|
|
53
|
+
|
|
54
|
+
## Markdown syntax profile
|
|
55
|
+
|
|
56
|
+
GitHub Flavored Markdown Spec 0.29, as documentation, not as validation.
|
|
57
|
+
See [`markdown-syntax-profile.md`](markdown-syntax-profile.md).
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { Document, Refusal } from './types.js';
|
|
2
|
+
export type ScalarSpec = {
|
|
3
|
+
readonly kind: 'string';
|
|
4
|
+
readonly required: boolean;
|
|
5
|
+
} | {
|
|
6
|
+
readonly kind: 'number';
|
|
7
|
+
readonly required: boolean;
|
|
8
|
+
} | {
|
|
9
|
+
readonly kind: 'date';
|
|
10
|
+
readonly required: boolean;
|
|
11
|
+
} | {
|
|
12
|
+
readonly kind: 'enum';
|
|
13
|
+
readonly required: boolean;
|
|
14
|
+
readonly values: readonly string[];
|
|
15
|
+
} | {
|
|
16
|
+
readonly kind: 'reference';
|
|
17
|
+
readonly required: boolean;
|
|
18
|
+
readonly to: string;
|
|
19
|
+
};
|
|
20
|
+
export type FieldSpec = ScalarSpec | {
|
|
21
|
+
readonly kind: 'list';
|
|
22
|
+
readonly required: boolean;
|
|
23
|
+
readonly of: ScalarSpec;
|
|
24
|
+
};
|
|
25
|
+
export type SectionSpec = {
|
|
26
|
+
readonly kind: 'text' | 'list';
|
|
27
|
+
readonly required: boolean;
|
|
28
|
+
};
|
|
29
|
+
export type Definition = {
|
|
30
|
+
readonly name: string;
|
|
31
|
+
readonly file: string;
|
|
32
|
+
readonly fields: Readonly<Record<string, FieldSpec>>;
|
|
33
|
+
readonly sections: Readonly<Record<string, SectionSpec>>;
|
|
34
|
+
};
|
|
35
|
+
export declare function readDefinition(file: string, document: Document): Definition | Refusal;
|
|
36
|
+
//# sourceMappingURL=definition.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"definition.d.ts","sourceRoot":"","sources":["../src/definition.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAEpD,MAAM,MAAM,UAAU,GAClB;IAAE,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IAAC,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAA;CAAE,GACvD;IAAE,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IAAC,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAA;CAAE,GACvD;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAA;CAAE,GACrD;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAA;CAAE,GACzF;IAAE,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC;IAAC,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;CAAE,CAAC;AAEpF,MAAM,MAAM,SAAS,GACjB,UAAU,GACV;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,EAAE,EAAE,UAAU,CAAA;CAAE,CAAC;AAEnF,MAAM,MAAM,WAAW,GAAG;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IAAC,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAA;CAAE,CAAC;AAEzF,MAAM,MAAM,UAAU,GAAG;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC,CAAC;IACrD,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC;CAC1D,CAAC;AA4FF,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,GAAG,UAAU,GAAG,OAAO,CA6BrF"}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import { typeNameFromPath } from './paths.js';
|
|
2
|
+
import { refusal } from './refuse.js';
|
|
3
|
+
function refuse(file, rule, location) {
|
|
4
|
+
return location === undefined ? refusal(file, rule) : refusal(file, rule, {
|
|
5
|
+
location
|
|
6
|
+
});
|
|
7
|
+
}
|
|
8
|
+
function isRecord(value) {
|
|
9
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
10
|
+
}
|
|
11
|
+
function requiredFlag(value) {
|
|
12
|
+
if (value === undefined) return false;
|
|
13
|
+
if (typeof value === 'boolean') return value;
|
|
14
|
+
return undefined;
|
|
15
|
+
}
|
|
16
|
+
function stringList(value) {
|
|
17
|
+
if (!Array.isArray(value) || value.length === 0) return undefined;
|
|
18
|
+
if (!value.every((item)=>typeof item === 'string' && item !== '')) return undefined;
|
|
19
|
+
return value;
|
|
20
|
+
}
|
|
21
|
+
const SCALAR_KINDS = new Set([
|
|
22
|
+
'string',
|
|
23
|
+
'number',
|
|
24
|
+
'date',
|
|
25
|
+
'enum',
|
|
26
|
+
'reference'
|
|
27
|
+
]);
|
|
28
|
+
function parseScalar(file, location, spec, required) {
|
|
29
|
+
const kind = spec.kind;
|
|
30
|
+
if (kind === 'string') return {
|
|
31
|
+
kind: 'string',
|
|
32
|
+
required
|
|
33
|
+
};
|
|
34
|
+
if (kind === 'number') return {
|
|
35
|
+
kind: 'number',
|
|
36
|
+
required
|
|
37
|
+
};
|
|
38
|
+
if (kind === 'date') return {
|
|
39
|
+
kind: 'date',
|
|
40
|
+
required
|
|
41
|
+
};
|
|
42
|
+
if (kind === 'enum') {
|
|
43
|
+
const values = stringList(spec.values);
|
|
44
|
+
if (values === undefined) return refuse(file, 'definition-enum-values', location);
|
|
45
|
+
return {
|
|
46
|
+
kind: 'enum',
|
|
47
|
+
required,
|
|
48
|
+
values
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
if (kind === 'reference') {
|
|
52
|
+
if (typeof spec.to !== 'string' || spec.to === '') return refuse(file, 'definition-reference-to', location);
|
|
53
|
+
return {
|
|
54
|
+
kind: 'reference',
|
|
55
|
+
required,
|
|
56
|
+
to: spec.to
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
return refuse(file, 'definition-field-kind', location);
|
|
60
|
+
}
|
|
61
|
+
function extraKeys(spec, allowed) {
|
|
62
|
+
return Object.keys(spec).some((key)=>!allowed.includes(key));
|
|
63
|
+
}
|
|
64
|
+
function parseField(file, location, value) {
|
|
65
|
+
if (!isRecord(value) || typeof value.kind !== 'string') return refuse(file, 'definition-field', location);
|
|
66
|
+
const required = requiredFlag(value.required);
|
|
67
|
+
if (required === undefined) return refuse(file, 'definition-field', location);
|
|
68
|
+
if (value.kind === 'list') {
|
|
69
|
+
if (typeof value.of !== 'string' || !SCALAR_KINDS.has(value.of)) {
|
|
70
|
+
return refuse(file, 'definition-list-of', location);
|
|
71
|
+
}
|
|
72
|
+
const allowed = value.of === 'enum' ? [
|
|
73
|
+
'kind',
|
|
74
|
+
'required',
|
|
75
|
+
'of',
|
|
76
|
+
'values'
|
|
77
|
+
] : value.of === 'reference' ? [
|
|
78
|
+
'kind',
|
|
79
|
+
'required',
|
|
80
|
+
'of',
|
|
81
|
+
'to'
|
|
82
|
+
] : [
|
|
83
|
+
'kind',
|
|
84
|
+
'required',
|
|
85
|
+
'of'
|
|
86
|
+
];
|
|
87
|
+
if (extraKeys(value, allowed)) return refuse(file, 'definition-field', location);
|
|
88
|
+
const inner = parseScalar(file, location, {
|
|
89
|
+
...value,
|
|
90
|
+
kind: value.of
|
|
91
|
+
}, false);
|
|
92
|
+
if ('rule' in inner) return inner;
|
|
93
|
+
return {
|
|
94
|
+
kind: 'list',
|
|
95
|
+
required,
|
|
96
|
+
of: inner
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
const allowed = value.kind === 'enum' ? [
|
|
100
|
+
'kind',
|
|
101
|
+
'required',
|
|
102
|
+
'values'
|
|
103
|
+
] : value.kind === 'reference' ? [
|
|
104
|
+
'kind',
|
|
105
|
+
'required',
|
|
106
|
+
'to'
|
|
107
|
+
] : [
|
|
108
|
+
'kind',
|
|
109
|
+
'required'
|
|
110
|
+
];
|
|
111
|
+
if (extraKeys(value, allowed)) return refuse(file, 'definition-field', location);
|
|
112
|
+
return parseScalar(file, location, value, required);
|
|
113
|
+
}
|
|
114
|
+
function parseSection(file, location, value) {
|
|
115
|
+
if (!isRecord(value) || value.kind !== 'text' && value.kind !== 'list') {
|
|
116
|
+
return refuse(file, 'definition-section-kind', location);
|
|
117
|
+
}
|
|
118
|
+
const required = requiredFlag(value.required);
|
|
119
|
+
if (required === undefined || extraKeys(value, [
|
|
120
|
+
'kind',
|
|
121
|
+
'required'
|
|
122
|
+
])) {
|
|
123
|
+
return refuse(file, 'definition-section', location);
|
|
124
|
+
}
|
|
125
|
+
return {
|
|
126
|
+
kind: value.kind,
|
|
127
|
+
required
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
export function readDefinition(file, document) {
|
|
131
|
+
if (document.type !== 'definition') return refuse(file, 'definition-type');
|
|
132
|
+
const name = typeNameFromPath(file);
|
|
133
|
+
if (name === '') return refuse(file, 'definition-type');
|
|
134
|
+
for (const key of Object.keys(document.fields)){
|
|
135
|
+
if (key !== 'type' && key !== 'fields' && key !== 'sections') {
|
|
136
|
+
return refuse(file, 'definition-shape', key);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
const fieldsValue = document.fields.fields;
|
|
140
|
+
const sectionsValue = document.fields.sections;
|
|
141
|
+
if (fieldsValue !== undefined && !isRecord(fieldsValue)) return refuse(file, 'definition-fields', 'fields');
|
|
142
|
+
if (sectionsValue !== undefined && !isRecord(sectionsValue)) {
|
|
143
|
+
return refuse(file, 'definition-sections', 'sections');
|
|
144
|
+
}
|
|
145
|
+
const fields = {};
|
|
146
|
+
for (const [fieldName, spec] of Object.entries(fieldsValue ?? {})){
|
|
147
|
+
const parsed = parseField(file, fieldName, spec);
|
|
148
|
+
if ('rule' in parsed) return parsed;
|
|
149
|
+
fields[fieldName] = parsed;
|
|
150
|
+
}
|
|
151
|
+
const sections = {};
|
|
152
|
+
for (const [sectionName, spec] of Object.entries(sectionsValue ?? {})){
|
|
153
|
+
const parsed = parseSection(file, sectionName, spec);
|
|
154
|
+
if ('rule' in parsed) return parsed;
|
|
155
|
+
sections[sectionName] = parsed;
|
|
156
|
+
}
|
|
157
|
+
return {
|
|
158
|
+
name,
|
|
159
|
+
file,
|
|
160
|
+
fields,
|
|
161
|
+
sections
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
//# sourceMappingURL=definition.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["/Users/executor/code/agents.house/mdmodel/packages/mdmodel/src/definition.ts"],"sourcesContent":["import { typeNameFromPath } from './paths.js';\nimport { refusal } from './refuse.js';\nimport type { Document, Refusal } from './types.js';\n\nexport type ScalarSpec =\n | { readonly kind: 'string'; readonly required: boolean }\n | { readonly kind: 'number'; readonly required: boolean }\n | { readonly kind: 'date'; readonly required: boolean }\n | { readonly kind: 'enum'; readonly required: boolean; readonly values: readonly string[] }\n | { readonly kind: 'reference'; readonly required: boolean; readonly to: string };\n\nexport type FieldSpec =\n | ScalarSpec\n | { readonly kind: 'list'; readonly required: boolean; readonly of: ScalarSpec };\n\nexport type SectionSpec = { readonly kind: 'text' | 'list'; readonly required: boolean };\n\nexport type Definition = {\n readonly name: string;\n readonly file: string;\n readonly fields: Readonly<Record<string, FieldSpec>>;\n readonly sections: Readonly<Record<string, SectionSpec>>;\n};\n\nfunction refuse(file: string, rule: string, location?: string): Refusal {\n return location === undefined ? refusal(file, rule) : refusal(file, rule, { location });\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === 'object' && !Array.isArray(value);\n}\n\nfunction requiredFlag(value: unknown): boolean | undefined {\n if (value === undefined) return false;\n if (typeof value === 'boolean') return value;\n return undefined;\n}\n\nfunction stringList(value: unknown): string[] | undefined {\n if (!Array.isArray(value) || value.length === 0) return undefined;\n if (!value.every((item) => typeof item === 'string' && item !== '')) return undefined;\n return value;\n}\n\nconst SCALAR_KINDS = new Set(['string', 'number', 'date', 'enum', 'reference']);\n\nfunction parseScalar(\n file: string,\n location: string,\n spec: Record<string, unknown>,\n required: boolean,\n): ScalarSpec | Refusal {\n const kind = spec.kind;\n if (kind === 'string') return { kind: 'string', required };\n if (kind === 'number') return { kind: 'number', required };\n if (kind === 'date') return { kind: 'date', required };\n if (kind === 'enum') {\n const values = stringList(spec.values);\n if (values === undefined) return refuse(file, 'definition-enum-values', location);\n return { kind: 'enum', required, values };\n }\n if (kind === 'reference') {\n if (typeof spec.to !== 'string' || spec.to === '')\n return refuse(file, 'definition-reference-to', location);\n return { kind: 'reference', required, to: spec.to };\n }\n return refuse(file, 'definition-field-kind', location);\n}\n\nfunction extraKeys(spec: Record<string, unknown>, allowed: readonly string[]): boolean {\n return Object.keys(spec).some((key) => !allowed.includes(key));\n}\n\nfunction parseField(file: string, location: string, value: unknown): FieldSpec | Refusal {\n if (!isRecord(value) || typeof value.kind !== 'string')\n return refuse(file, 'definition-field', location);\n const required = requiredFlag(value.required);\n if (required === undefined) return refuse(file, 'definition-field', location);\n if (value.kind === 'list') {\n if (typeof value.of !== 'string' || !SCALAR_KINDS.has(value.of)) {\n return refuse(file, 'definition-list-of', location);\n }\n const allowed =\n value.of === 'enum'\n ? ['kind', 'required', 'of', 'values']\n : value.of === 'reference'\n ? ['kind', 'required', 'of', 'to']\n : ['kind', 'required', 'of'];\n if (extraKeys(value, allowed)) return refuse(file, 'definition-field', location);\n const inner = parseScalar(file, location, { ...value, kind: value.of }, false);\n if ('rule' in inner) return inner;\n return { kind: 'list', required, of: inner };\n }\n const allowed =\n value.kind === 'enum'\n ? ['kind', 'required', 'values']\n : value.kind === 'reference'\n ? ['kind', 'required', 'to']\n : ['kind', 'required'];\n if (extraKeys(value, allowed)) return refuse(file, 'definition-field', location);\n return parseScalar(file, location, value, required);\n}\n\nfunction parseSection(file: string, location: string, value: unknown): SectionSpec | Refusal {\n if (!isRecord(value) || (value.kind !== 'text' && value.kind !== 'list')) {\n return refuse(file, 'definition-section-kind', location);\n }\n const required = requiredFlag(value.required);\n if (required === undefined || extraKeys(value, ['kind', 'required'])) {\n return refuse(file, 'definition-section', location);\n }\n return { kind: value.kind, required };\n}\n\nexport function readDefinition(file: string, document: Document): Definition | Refusal {\n if (document.type !== 'definition') return refuse(file, 'definition-type');\n const name = typeNameFromPath(file);\n if (name === '') return refuse(file, 'definition-type');\n for (const key of Object.keys(document.fields)) {\n if (key !== 'type' && key !== 'fields' && key !== 'sections') {\n return refuse(file, 'definition-shape', key);\n }\n }\n const fieldsValue = document.fields.fields;\n const sectionsValue = document.fields.sections;\n if (fieldsValue !== undefined && !isRecord(fieldsValue))\n return refuse(file, 'definition-fields', 'fields');\n if (sectionsValue !== undefined && !isRecord(sectionsValue)) {\n return refuse(file, 'definition-sections', 'sections');\n }\n const fields: Record<string, FieldSpec> = {};\n for (const [fieldName, spec] of Object.entries(fieldsValue ?? {})) {\n const parsed = parseField(file, fieldName, spec);\n if ('rule' in parsed) return parsed;\n fields[fieldName] = parsed;\n }\n const sections: Record<string, SectionSpec> = {};\n for (const [sectionName, spec] of Object.entries(sectionsValue ?? {})) {\n const parsed = parseSection(file, sectionName, spec);\n if ('rule' in parsed) return parsed;\n sections[sectionName] = parsed;\n }\n return { name, file, fields, sections };\n}\n"],"names":["typeNameFromPath","refusal","refuse","file","rule","location","undefined","isRecord","value","Array","isArray","requiredFlag","stringList","length","every","item","SCALAR_KINDS","Set","parseScalar","spec","required","kind","values","to","extraKeys","allowed","Object","keys","some","key","includes","parseField","of","has","inner","parseSection","readDefinition","document","type","name","fields","fieldsValue","sectionsValue","sections","fieldName","entries","parsed","sectionName"],"mappings":"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"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { ApplyResult, Change, Document, ParseResult } from './types.js';
|
|
2
|
+
export declare function parse(content: string, file?: string): ParseResult;
|
|
3
|
+
export declare function apply(document: Document, change: Change): ApplyResult;
|
|
4
|
+
export declare function serialize(document: Document): string;
|
|
5
|
+
//# sourceMappingURL=document.d.ts.map
|
|
@@ -0,0 +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"}
|
package/dist/document.js
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { headingSpans, sectionsRecord } from './sections.js';
|
|
2
|
+
import { refusal } from './refuse.js';
|
|
3
|
+
import { bodyStartAfterYaml, equalValue, extractFrontmatter, jsValue, lineEnding, mapEntry, parseYamlMapping, serializeJs, serializeKey, YamlError } from './yaml.js';
|
|
4
|
+
const internals = new WeakMap();
|
|
5
|
+
function refuse(file, rule, location) {
|
|
6
|
+
return location === undefined ? refusal(file, rule) : refusal(file, rule, {
|
|
7
|
+
location
|
|
8
|
+
});
|
|
9
|
+
}
|
|
10
|
+
function freezeDocument(content, type, fields, sections, internal) {
|
|
11
|
+
const document = Object.freeze({
|
|
12
|
+
content,
|
|
13
|
+
type,
|
|
14
|
+
fields: Object.freeze(fields),
|
|
15
|
+
sections: Object.freeze(sections)
|
|
16
|
+
});
|
|
17
|
+
internals.set(document, internal);
|
|
18
|
+
return document;
|
|
19
|
+
}
|
|
20
|
+
function declaredType(fields) {
|
|
21
|
+
const value = fields.type;
|
|
22
|
+
if (value === undefined || value === null) return undefined;
|
|
23
|
+
if (typeof value !== 'string' || value === '') return undefined;
|
|
24
|
+
return value;
|
|
25
|
+
}
|
|
26
|
+
export function parse(content, file = '') {
|
|
27
|
+
const fence = extractFrontmatter(content);
|
|
28
|
+
if (fence.kind === 'none') {
|
|
29
|
+
const spans = headingSpans(content, 0);
|
|
30
|
+
return {
|
|
31
|
+
ok: true,
|
|
32
|
+
document: freezeDocument(content, undefined, {}, sectionsRecord(spans, content), {
|
|
33
|
+
root: null,
|
|
34
|
+
yamlEnd: 0,
|
|
35
|
+
bodyStart: 0
|
|
36
|
+
})
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
if (fence.kind === 'unclosed') return {
|
|
40
|
+
ok: false,
|
|
41
|
+
refusal: refuse(file, 'frontmatter-fence')
|
|
42
|
+
};
|
|
43
|
+
try {
|
|
44
|
+
const root = parseYamlMapping(content, fence.yamlStart, fence.yamlEnd);
|
|
45
|
+
const fields = jsValue(root);
|
|
46
|
+
if (fields === null || typeof fields !== 'object' || Array.isArray(fields)) {
|
|
47
|
+
return {
|
|
48
|
+
ok: false,
|
|
49
|
+
refusal: refuse(file, 'frontmatter-fence')
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
const bodyStart = bodyStartAfterYaml(content, fence.yamlEnd);
|
|
53
|
+
const spans = headingSpans(content, bodyStart);
|
|
54
|
+
const record = fields;
|
|
55
|
+
return {
|
|
56
|
+
ok: true,
|
|
57
|
+
document: freezeDocument(content, declaredType(record), record, sectionsRecord(spans, content), {
|
|
58
|
+
root,
|
|
59
|
+
yamlEnd: fence.yamlEnd,
|
|
60
|
+
bodyStart
|
|
61
|
+
})
|
|
62
|
+
};
|
|
63
|
+
} catch (error) {
|
|
64
|
+
if (error instanceof YamlError) return {
|
|
65
|
+
ok: false,
|
|
66
|
+
refusal: refuse(file, 'frontmatter-fence')
|
|
67
|
+
};
|
|
68
|
+
throw error;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
function splice(content, start, end, insert) {
|
|
72
|
+
return content.slice(0, start) + insert + content.slice(end);
|
|
73
|
+
}
|
|
74
|
+
function applyField(document, name, value, prior) {
|
|
75
|
+
const internal = internals.get(document);
|
|
76
|
+
if (internal === undefined || internal.root === null) {
|
|
77
|
+
return {
|
|
78
|
+
outcome: 'refuse',
|
|
79
|
+
refusal: refuse('', 'frontmatter-fence', name)
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
if (!equalValue(document.fields[name], prior)) {
|
|
83
|
+
return {
|
|
84
|
+
outcome: 'stale',
|
|
85
|
+
document
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
let nextContent;
|
|
89
|
+
const existing = mapEntry(internal.root, name);
|
|
90
|
+
if (existing !== undefined) {
|
|
91
|
+
nextContent = splice(document.content, existing.value.start, existing.value.end, serializeJs(value));
|
|
92
|
+
} else {
|
|
93
|
+
const nl = lineEnding(document.content);
|
|
94
|
+
const line = `${serializeKey(name)}: ${serializeJs(value)}${nl}`;
|
|
95
|
+
const before = document.content.slice(0, internal.yamlEnd);
|
|
96
|
+
const prefix = before.endsWith('\n') || before.endsWith('\r\n') ? '' : nl;
|
|
97
|
+
nextContent = splice(document.content, internal.yamlEnd, internal.yamlEnd, `${prefix}${line}`);
|
|
98
|
+
}
|
|
99
|
+
const parsed = parse(nextContent);
|
|
100
|
+
if (!parsed.ok) return {
|
|
101
|
+
outcome: 'refuse',
|
|
102
|
+
refusal: parsed.refusal
|
|
103
|
+
};
|
|
104
|
+
return {
|
|
105
|
+
outcome: 'applied',
|
|
106
|
+
document: parsed.document
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
function applySection(document, name, value, prior) {
|
|
110
|
+
const internal = internals.get(document);
|
|
111
|
+
if (internal === undefined) return {
|
|
112
|
+
outcome: 'refuse',
|
|
113
|
+
refusal: refuse('', 'required-section', name)
|
|
114
|
+
};
|
|
115
|
+
const spans = headingSpans(document.content, internal.bodyStart);
|
|
116
|
+
const span = spans.find((item)=>item.name === name);
|
|
117
|
+
if (span === undefined) return {
|
|
118
|
+
outcome: 'refuse',
|
|
119
|
+
refusal: refuse('', 'required-section', name)
|
|
120
|
+
};
|
|
121
|
+
const current = document.content.slice(span.bodyStart, span.bodyEnd);
|
|
122
|
+
if (current !== prior) return {
|
|
123
|
+
outcome: 'stale',
|
|
124
|
+
document
|
|
125
|
+
};
|
|
126
|
+
const parsed = parse(splice(document.content, span.bodyStart, span.bodyEnd, value));
|
|
127
|
+
if (!parsed.ok) return {
|
|
128
|
+
outcome: 'refuse',
|
|
129
|
+
refusal: parsed.refusal
|
|
130
|
+
};
|
|
131
|
+
return {
|
|
132
|
+
outcome: 'applied',
|
|
133
|
+
document: parsed.document
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
export function apply(document, change) {
|
|
137
|
+
try {
|
|
138
|
+
if (change.kind === 'field') return applyField(document, change.name, change.value, change.prior);
|
|
139
|
+
return applySection(document, change.name, change.value, change.prior);
|
|
140
|
+
} catch (error) {
|
|
141
|
+
if (error instanceof YamlError) {
|
|
142
|
+
return {
|
|
143
|
+
outcome: 'refuse',
|
|
144
|
+
refusal: refuse('', 'frontmatter-fence', change.name)
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
throw error;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
export function serialize(document) {
|
|
151
|
+
return document.content;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
//# sourceMappingURL=document.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["/Users/executor/code/agents.house/mdmodel/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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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { apply, parse, serialize } from './document.js';
|
|
2
|
+
export { MARKDOWN_SYNTAX_PROFILE } from './profile.js';
|
|
3
|
+
export type { ApplyResult, Change, Document, Library, ParseResult, Refusal, Validation, } from './types.js';
|
|
4
|
+
export { validate } from './validate.js';
|
|
5
|
+
export { VERSION } from './version.js';
|
|
6
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +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"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["/Users/executor/code/agents.house/mdmodel/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"}
|
package/dist/links.d.ts
ADDED
|
@@ -0,0 +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"}
|