@agentshouse/mdmodel 0.1.0-c64dbada0730d → 0.1.0-c679f403bf3fb
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/API.md +230 -216
- package/README.md +15 -18
- package/dist/blocks.d.ts.map +1 -1
- package/dist/blocks.js +7 -14
- package/dist/condition.d.ts +21 -8
- package/dist/condition.d.ts.map +1 -1
- package/dist/condition.js +208 -89
- package/dist/definition.d.ts +16 -1
- package/dist/definition.d.ts.map +1 -1
- package/dist/definition.js +255 -29
- package/dist/divide.d.ts.map +1 -1
- package/dist/divide.js +27 -17
- package/dist/document.d.ts +6 -3
- package/dist/document.d.ts.map +1 -1
- package/dist/document.js +315 -167
- package/dist/fields.d.ts +2 -0
- package/dist/fields.d.ts.map +1 -1
- package/dist/fields.js +7 -0
- package/dist/index.d.ts +14 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -5
- package/dist/links.d.ts +3 -0
- package/dist/links.d.ts.map +1 -1
- package/dist/links.js +66 -40
- package/dist/paths.d.ts +10 -1
- package/dist/paths.d.ts.map +1 -1
- package/dist/paths.js +115 -12
- package/dist/project.d.ts +1 -0
- package/dist/project.d.ts.map +1 -1
- package/dist/project.js +38 -15
- package/dist/references.d.ts +2 -2
- package/dist/references.d.ts.map +1 -1
- package/dist/references.js +6 -1
- package/dist/schema.js +2 -2
- package/dist/types.d.ts +49 -2
- package/dist/types.d.ts.map +1 -1
- package/dist/validate.d.ts +30 -2
- package/dist/validate.d.ts.map +1 -1
- package/dist/validate.js +290 -115
- package/dist/yaml.d.ts +7 -3
- package/dist/yaml.d.ts.map +1 -1
- package/dist/yaml.js +72 -43
- package/fixtures/README.md +6 -5
- package/fixtures/cases/cross-room-link/context.json +1 -0
- package/fixtures/cases/cross-room-link/expected.json +1 -0
- package/fixtures/cases/cross-room-link/library/clients/acme.md +6 -0
- package/fixtures/cases/cross-room-link/schema.yaml +3 -0
- package/fixtures/cases/cross-room-reference/context.json +1 -0
- package/fixtures/cases/cross-room-reference/expected.json +1 -0
- package/fixtures/cases/cross-room-reference/library/clients/acme.md +9 -0
- package/fixtures/cases/cross-room-reference/schema.yaml +9 -0
- package/fixtures/division.json +26 -26
- package/package.json +4 -2
package/dist/document.js
CHANGED
|
@@ -1,11 +1,20 @@
|
|
|
1
1
|
import { ARRAY_INDEX, pointerSegments } from './definition.js';
|
|
2
|
-
import { hrefOccurrences } from './links.js';
|
|
3
|
-
import { relative, retarget, SHELL_ROOMS, shellTarget, target } from './paths.js';
|
|
2
|
+
import { destinations, hrefOccurrences, linkInventory, } from './links.js';
|
|
3
|
+
import { destinationPath, relative, retarget, roomPath, roomTarget, SHELL_ROOMS, shellTarget, target, } from './paths.js';
|
|
4
4
|
import { references } from './references.js';
|
|
5
5
|
import { headingSpans, sectionsRecord } from './sections.js';
|
|
6
6
|
import { refusal } from './refuse.js';
|
|
7
|
-
import { bodyStartAfterYaml, bodyStartOf, equalValue, extractFrontmatter, jsValue, lineEnding, mapEntry, parseYamlMapping, serializeJs, serializeKey, YamlError, } from './yaml.js';
|
|
7
|
+
import { bodyStartAfterYaml, bodyStartOf, equalValue, extractFrontmatter, frontmatterValue, jsValue, lineEnding, mapEntry, parseYamlMapping, serializeJs, serializeKey, YamlError, } from './yaml.js';
|
|
8
8
|
const internals = new WeakMap();
|
|
9
|
+
const inventories = new WeakMap();
|
|
10
|
+
export function inventoryOf(document) {
|
|
11
|
+
const held = inventories.get(document);
|
|
12
|
+
if (held !== undefined)
|
|
13
|
+
return held;
|
|
14
|
+
const inventory = linkInventory(document.content.slice(bodyStartOf(document.content)));
|
|
15
|
+
inventories.set(document, inventory);
|
|
16
|
+
return inventory;
|
|
17
|
+
}
|
|
9
18
|
function refuse(file, rule, location) {
|
|
10
19
|
return location === undefined ? refusal(file, rule) : refusal(file, rule, { location });
|
|
11
20
|
}
|
|
@@ -28,19 +37,10 @@ function declaredType(fields) {
|
|
|
28
37
|
return undefined;
|
|
29
38
|
return value;
|
|
30
39
|
}
|
|
31
|
-
|
|
40
|
+
function opened(content, file) {
|
|
32
41
|
const fence = extractFrontmatter(content);
|
|
33
|
-
if (fence.kind === 'none')
|
|
34
|
-
|
|
35
|
-
return {
|
|
36
|
-
ok: true,
|
|
37
|
-
document: freezeDocument(content, undefined, {}, content.slice(0, spans[0]?.headingStart), sectionsRecord(spans, content), {
|
|
38
|
-
root: null,
|
|
39
|
-
yamlEnd: 0,
|
|
40
|
-
bodyStart: 0,
|
|
41
|
-
}),
|
|
42
|
-
};
|
|
43
|
-
}
|
|
42
|
+
if (fence.kind === 'none')
|
|
43
|
+
return { ok: true, root: null, fields: {}, yamlEnd: 0 };
|
|
44
44
|
if (fence.kind === 'unclosed')
|
|
45
45
|
return { ok: false, refusal: refuse(file, 'frontmatter-fence') };
|
|
46
46
|
try {
|
|
@@ -49,17 +49,7 @@ export function parse(content, file = '') {
|
|
|
49
49
|
if (fields === null || typeof fields !== 'object' || Array.isArray(fields)) {
|
|
50
50
|
return { ok: false, refusal: refuse(file, 'frontmatter-fence') };
|
|
51
51
|
}
|
|
52
|
-
|
|
53
|
-
const spans = headingSpans(content, bodyStart);
|
|
54
|
-
const record = fields;
|
|
55
|
-
return {
|
|
56
|
-
ok: true,
|
|
57
|
-
document: freezeDocument(content, declaredType(record), record, content.slice(bodyStart, spans[0]?.headingStart), sectionsRecord(spans, content), {
|
|
58
|
-
root,
|
|
59
|
-
yamlEnd: fence.yamlEnd,
|
|
60
|
-
bodyStart,
|
|
61
|
-
}),
|
|
62
|
-
};
|
|
52
|
+
return { ok: true, root, fields: fields, yamlEnd: fence.yamlEnd };
|
|
63
53
|
}
|
|
64
54
|
catch (error) {
|
|
65
55
|
if (error instanceof YamlError)
|
|
@@ -67,63 +57,29 @@ export function parse(content, file = '') {
|
|
|
67
57
|
throw error;
|
|
68
58
|
}
|
|
69
59
|
}
|
|
70
|
-
function
|
|
71
|
-
|
|
60
|
+
export function parseFrontmatter(content, file = '') {
|
|
61
|
+
const read = opened(content, file);
|
|
62
|
+
if (!read.ok)
|
|
63
|
+
return read;
|
|
64
|
+
return {
|
|
65
|
+
ok: true,
|
|
66
|
+
frontmatter: Object.freeze({
|
|
67
|
+
content,
|
|
68
|
+
type: declaredType(read.fields),
|
|
69
|
+
fields: Object.freeze(read.fields),
|
|
70
|
+
}),
|
|
71
|
+
};
|
|
72
72
|
}
|
|
73
|
-
function
|
|
74
|
-
const
|
|
75
|
-
if (
|
|
76
|
-
return
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
if (existing !== undefined) {
|
|
84
|
-
nextContent = splice(document.content, existing.value.start, existing.value.end, serializeJs(value));
|
|
85
|
-
}
|
|
86
|
-
else if (document.content[internal.root.start] === '{') {
|
|
87
|
-
nextContent = splice(document.content, internal.root.end - 1, internal.root.end - 1, `${internal.root.entries.length === 0 ? '' : ', '}${serializeKey(name)}: ${serializeJs(value)}`);
|
|
88
|
-
}
|
|
89
|
-
else {
|
|
90
|
-
const nl = lineEnding(document.content);
|
|
91
|
-
const lineStart = document.content.lastIndexOf('\n', internal.root.start - 1) + 1;
|
|
92
|
-
const indent = document.content.slice(lineStart, internal.root.start);
|
|
93
|
-
const line = `${indent}${serializeKey(name)}: ${serializeJs(value)}${nl}`;
|
|
94
|
-
const before = document.content.slice(0, internal.yamlEnd);
|
|
95
|
-
const prefix = before.endsWith('\n') || before.endsWith('\r\n') ? '' : nl;
|
|
96
|
-
nextContent = splice(document.content, internal.yamlEnd, internal.yamlEnd, `${prefix}${line}`);
|
|
97
|
-
}
|
|
98
|
-
const parsed = parse(nextContent);
|
|
99
|
-
if (!parsed.ok)
|
|
100
|
-
return { outcome: 'refuse', refusal: parsed.refusal };
|
|
101
|
-
return { outcome: 'applied', document: parsed.document };
|
|
102
|
-
}
|
|
103
|
-
function applySection(document, name, value, prior) {
|
|
104
|
-
const internal = internals.get(document);
|
|
105
|
-
if (internal === undefined)
|
|
106
|
-
return { outcome: 'refuse', refusal: refuse('', 'required-section', name) };
|
|
107
|
-
const spans = headingSpans(document.content, internal.bodyStart);
|
|
108
|
-
const span = spans.find((item) => item.name === name);
|
|
109
|
-
if (span === undefined)
|
|
110
|
-
return { outcome: 'refuse', refusal: refuse('', 'required-section', name) };
|
|
111
|
-
const current = document.content.slice(span.bodyStart, span.bodyEnd);
|
|
112
|
-
if (current !== prior)
|
|
113
|
-
return { outcome: 'stale', document };
|
|
114
|
-
const parsed = parse(splice(document.content, span.bodyStart, span.bodyEnd, value));
|
|
115
|
-
if (!parsed.ok)
|
|
116
|
-
return { outcome: 'refuse', refusal: parsed.refusal };
|
|
117
|
-
return { outcome: 'applied', document: parsed.document };
|
|
118
|
-
}
|
|
119
|
-
function applyPreamble(document, value, prior) {
|
|
120
|
-
if (document.preamble !== prior)
|
|
121
|
-
return { outcome: 'stale', document };
|
|
122
|
-
const start = bodyStartOf(document.content);
|
|
123
|
-
const parsed = parse(splice(document.content, start, start + document.preamble.length, value));
|
|
124
|
-
if (!parsed.ok)
|
|
125
|
-
return { outcome: 'refuse', refusal: parsed.refusal };
|
|
126
|
-
return { outcome: 'applied', document: parsed.document };
|
|
73
|
+
export function parse(content, file = '') {
|
|
74
|
+
const read = opened(content, file);
|
|
75
|
+
if (!read.ok)
|
|
76
|
+
return read;
|
|
77
|
+
const bodyStart = read.root === null ? 0 : bodyStartAfterYaml(content, read.yamlEnd);
|
|
78
|
+
const spans = headingSpans(content, bodyStart);
|
|
79
|
+
return {
|
|
80
|
+
ok: true,
|
|
81
|
+
document: freezeDocument(content, declaredType(read.fields), read.fields, content.slice(bodyStart, spans[0]?.headingStart), sectionsRecord(spans, content), { root: read.root, yamlEnd: read.yamlEnd, bodyStart }),
|
|
82
|
+
};
|
|
127
83
|
}
|
|
128
84
|
function fitsBare(to) {
|
|
129
85
|
if (to === '' || to.startsWith('<'))
|
|
@@ -152,58 +108,34 @@ function destinationText(to, bracketed) {
|
|
|
152
108
|
return `<${to}>`;
|
|
153
109
|
return null;
|
|
154
110
|
}
|
|
155
|
-
function
|
|
156
|
-
const
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
for (const match of hrefOccurrences(content.slice(bodyStart))) {
|
|
160
|
-
if (match.href !== href)
|
|
161
|
-
continue;
|
|
162
|
-
const text = destinationText(written, match.bracketed);
|
|
163
|
-
if (text === null)
|
|
164
|
-
return refuse('', 'unwritable-link', written);
|
|
165
|
-
rewritten += content.slice(cursor, bodyStart + match.start);
|
|
166
|
-
rewritten += text;
|
|
167
|
-
cursor = bodyStart + match.end;
|
|
168
|
-
}
|
|
169
|
-
return rewritten + content.slice(cursor);
|
|
170
|
-
}
|
|
171
|
-
function shellRelinked(content, bodyStart, file, handle) {
|
|
172
|
-
const hrefs = new Set(hrefOccurrences(content.slice(bodyStart)).map(({ href }) => href));
|
|
173
|
-
let held = content;
|
|
174
|
-
for (const href of hrefs) {
|
|
111
|
+
function shellRelinked(body, file, handle) {
|
|
112
|
+
const occurrences = hrefOccurrences(body);
|
|
113
|
+
const written = new Map();
|
|
114
|
+
for (const href of new Set(occurrences.map((occurrence) => occurrence.href))) {
|
|
175
115
|
const path = shellTarget(href, handle);
|
|
176
116
|
if (path === null)
|
|
177
117
|
continue;
|
|
178
|
-
const
|
|
179
|
-
if (
|
|
180
|
-
return
|
|
181
|
-
|
|
118
|
+
const to = retarget(href, relative(file, path));
|
|
119
|
+
if (destinationText(to, false) === null)
|
|
120
|
+
return refuse('', 'unwritable-link', to);
|
|
121
|
+
written.set(href, to);
|
|
182
122
|
}
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
return { outcome: 'applied', document: parsed.document };
|
|
194
|
-
}
|
|
195
|
-
function applyLink(document, href, to) {
|
|
196
|
-
const content = document.content;
|
|
197
|
-
const bodyStart = bodyStartOf(content);
|
|
198
|
-
if (!hrefOccurrences(content.slice(bodyStart)).some((occurrence) => occurrence.href === href))
|
|
199
|
-
return { outcome: 'stale', document };
|
|
200
|
-
return reparsed(document, relinked(content, bodyStart, href, to));
|
|
123
|
+
let rewritten = '';
|
|
124
|
+
let cursor = 0;
|
|
125
|
+
for (const match of occurrences) {
|
|
126
|
+
const to = written.get(match.href);
|
|
127
|
+
if (to === undefined)
|
|
128
|
+
continue;
|
|
129
|
+
rewritten += body.slice(cursor, match.start) + destinationText(to, match.bracketed);
|
|
130
|
+
cursor = match.end;
|
|
131
|
+
}
|
|
132
|
+
return rewritten + body.slice(cursor);
|
|
201
133
|
}
|
|
202
|
-
function nodeAt(root, pointer) {
|
|
134
|
+
function nodeAt(root, pointer, entryOf = mapEntry) {
|
|
203
135
|
let node = root;
|
|
204
136
|
for (const key of pointerSegments(pointer)) {
|
|
205
137
|
if (node?.kind === 'map')
|
|
206
|
-
node =
|
|
138
|
+
node = entryOf(node, key)?.value;
|
|
207
139
|
else if (node?.kind === 'seq' && ARRAY_INDEX.test(key))
|
|
208
140
|
node = node.items[Number(key)];
|
|
209
141
|
else
|
|
@@ -218,37 +150,36 @@ function scalarText(source, value) {
|
|
|
218
150
|
return JSON.stringify(value);
|
|
219
151
|
return serializeJs(value);
|
|
220
152
|
}
|
|
221
|
-
function
|
|
222
|
-
const
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
const
|
|
227
|
-
const parsed = parse(splice(document.content, node.start, node.end, text));
|
|
228
|
-
if (!parsed.ok)
|
|
229
|
-
return { outcome: 'refuse', refusal: parsed.refusal };
|
|
230
|
-
return { outcome: 'applied', document: parsed.document };
|
|
231
|
-
}
|
|
232
|
-
export function relativize(document, file, handle, definition) {
|
|
233
|
-
let held = document;
|
|
153
|
+
export function relativize(document, file, handle, definition, kept = new Set()) {
|
|
154
|
+
const relativized = (href) => {
|
|
155
|
+
const path = shellTarget(href, handle);
|
|
156
|
+
return path === null || kept.has(`${SHELL_ROOMS}${handle}/${path}`) ? null : path;
|
|
157
|
+
};
|
|
158
|
+
const changes = [];
|
|
234
159
|
for (const { pointer, value } of definition === undefined
|
|
235
160
|
? []
|
|
236
161
|
: references(document, definition)) {
|
|
237
|
-
const path =
|
|
238
|
-
if (path
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
162
|
+
const path = relativized(value);
|
|
163
|
+
if (path !== null)
|
|
164
|
+
changes.push({
|
|
165
|
+
kind: 'reference',
|
|
166
|
+
pointer,
|
|
167
|
+
value: retarget(value, relative(file, path)),
|
|
168
|
+
prior: value,
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
for (const href of new Set(destinations(inventoryOf(document)).map(({ href }) => href))) {
|
|
172
|
+
const path = relativized(href);
|
|
173
|
+
if (path !== null)
|
|
174
|
+
changes.push({ kind: 'link', href, to: relative(file, path) });
|
|
244
175
|
}
|
|
245
|
-
return
|
|
176
|
+
return apply(document, changes);
|
|
246
177
|
}
|
|
247
178
|
export function relativizeBody(body, file, handle) {
|
|
248
|
-
const relinkedBody = shellRelinked(body,
|
|
179
|
+
const relinkedBody = shellRelinked(body, file, handle);
|
|
249
180
|
if (typeof relinkedBody !== 'string')
|
|
250
181
|
return { outcome: 'refuse', refusal: relinkedBody };
|
|
251
|
-
const elsewhere = hrefOccurrences(relinkedBody).find(({ href }) => href
|
|
182
|
+
const elsewhere = hrefOccurrences(relinkedBody).find(({ href }) => destinationPath(href)?.startsWith(SHELL_ROOMS) === true && roomTarget(href) === null);
|
|
252
183
|
if (elsewhere !== undefined)
|
|
253
184
|
return { outcome: 'refuse', refusal: refuse(file, 'unresolved-link', elsewhere.href) };
|
|
254
185
|
return { outcome: 'applied', body: relinkedBody };
|
|
@@ -257,11 +188,11 @@ export function relinkBody(body, file, moves) {
|
|
|
257
188
|
let rewritten = '';
|
|
258
189
|
let cursor = 0;
|
|
259
190
|
for (const match of hrefOccurrences(body)) {
|
|
260
|
-
const from = target(file, match.href);
|
|
191
|
+
const from = target(file, match.href) ?? roomPath(match.href);
|
|
261
192
|
const to = from === null ? undefined : moves.get(from);
|
|
262
193
|
if (to === undefined)
|
|
263
194
|
continue;
|
|
264
|
-
const written = retarget(match.href, relative(file, to));
|
|
195
|
+
const written = retarget(match.href, from.startsWith(SHELL_ROOMS) || to.startsWith(SHELL_ROOMS) ? to : relative(file, to));
|
|
265
196
|
const text = destinationText(written, match.bracketed);
|
|
266
197
|
if (text === null)
|
|
267
198
|
return { outcome: 'refuse', refusal: refuse(file, 'unwritable-link', written) };
|
|
@@ -270,10 +201,10 @@ export function relinkBody(body, file, moves) {
|
|
|
270
201
|
}
|
|
271
202
|
return { outcome: 'applied', body: rewritten + body.slice(cursor) };
|
|
272
203
|
}
|
|
273
|
-
function linksIn(
|
|
204
|
+
function linksIn(occurrences, file) {
|
|
274
205
|
const found = [];
|
|
275
206
|
const seen = new Set();
|
|
276
|
-
for (const occurrence of
|
|
207
|
+
for (const occurrence of occurrences) {
|
|
277
208
|
if (seen.has(occurrence.href))
|
|
278
209
|
continue;
|
|
279
210
|
const resolved = target(file, occurrence.href);
|
|
@@ -285,29 +216,246 @@ function linksIn(text, file) {
|
|
|
285
216
|
return found;
|
|
286
217
|
}
|
|
287
218
|
export function links(document, file) {
|
|
288
|
-
return linksIn(
|
|
219
|
+
return linksIn(destinations(inventoryOf(document)), file);
|
|
289
220
|
}
|
|
290
221
|
export function bodyLinks(body, file) {
|
|
291
|
-
return linksIn(body, file);
|
|
222
|
+
return linksIn(hrefOccurrences(body), file);
|
|
223
|
+
}
|
|
224
|
+
function internalOf(document) {
|
|
225
|
+
const held = internals.get(document);
|
|
226
|
+
if (held !== undefined)
|
|
227
|
+
return held;
|
|
228
|
+
const read = opened(document.content, '');
|
|
229
|
+
if (!read.ok)
|
|
230
|
+
return read.refusal;
|
|
231
|
+
return {
|
|
232
|
+
root: read.root,
|
|
233
|
+
yamlEnd: read.yamlEnd,
|
|
234
|
+
bodyStart: read.root === null ? 0 : bodyStartAfterYaml(document.content, read.yamlEnd),
|
|
235
|
+
};
|
|
292
236
|
}
|
|
293
|
-
|
|
237
|
+
function partOf(change) {
|
|
238
|
+
if (change.kind === 'field' || change.kind === 'section')
|
|
239
|
+
return `${change.kind} ${change.name}`;
|
|
294
240
|
if (change.kind === 'link')
|
|
295
|
-
return
|
|
241
|
+
return `link ${change.href}`;
|
|
296
242
|
if (change.kind === 'reference')
|
|
297
|
-
return
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
243
|
+
return `reference ${change.pointer}`;
|
|
244
|
+
return 'preamble';
|
|
245
|
+
}
|
|
246
|
+
function planner(document, internal) {
|
|
247
|
+
const content = document.content;
|
|
248
|
+
const keyed = new Map();
|
|
249
|
+
let spans;
|
|
250
|
+
let occurrences;
|
|
251
|
+
let ending;
|
|
252
|
+
let added = 0;
|
|
253
|
+
const entryOf = (map, key) => {
|
|
254
|
+
let index = keyed.get(map);
|
|
255
|
+
if (index === undefined) {
|
|
256
|
+
index = new Map();
|
|
257
|
+
for (const entry of map.entries)
|
|
258
|
+
if (!index.has(entry.key))
|
|
259
|
+
index.set(entry.key, entry);
|
|
260
|
+
keyed.set(map, index);
|
|
261
|
+
}
|
|
262
|
+
return index.get(key);
|
|
263
|
+
};
|
|
264
|
+
const field = (name, value, prior, at) => {
|
|
265
|
+
const root = internal.root;
|
|
266
|
+
if (root === null)
|
|
267
|
+
return refuse('', 'frontmatter-fence', name);
|
|
268
|
+
if (!equalValue(document.fields[name], prior))
|
|
269
|
+
return 'stale';
|
|
270
|
+
if (!frontmatterValue(value))
|
|
271
|
+
return refuse('', 'frontmatter-fence', name);
|
|
272
|
+
const existing = entryOf(root, name);
|
|
273
|
+
if (existing !== undefined) {
|
|
274
|
+
const held = existing.value;
|
|
275
|
+
const inline = held.start > existing.after && !/[\r\n]/.test(content.slice(existing.after, held.start));
|
|
276
|
+
return inline
|
|
277
|
+
? [{ start: held.start, end: held.end, text: serializeJs(value), at }]
|
|
278
|
+
: [{ start: existing.after, end: held.end, text: ` ${serializeJs(value)}`, at }];
|
|
279
|
+
}
|
|
280
|
+
const entry = `${serializeKey(name)}: ${serializeJs(value)}`;
|
|
281
|
+
added += 1;
|
|
282
|
+
if (content[root.start] === '{') {
|
|
283
|
+
const separator = root.entries.length === 0 && added === 1 ? '' : ', ';
|
|
284
|
+
return [{ start: root.end - 1, end: root.end - 1, text: `${separator}${entry}`, at }];
|
|
285
|
+
}
|
|
286
|
+
const lineStart = content.lastIndexOf('\n', root.start - 1) + 1;
|
|
287
|
+
ending ??= lineEnding(content);
|
|
288
|
+
const line = `${content.slice(lineStart, root.start)}${entry}${ending}`;
|
|
289
|
+
return [{ start: internal.yamlEnd, end: internal.yamlEnd, text: line, at, entry: true }];
|
|
290
|
+
};
|
|
291
|
+
const section = (name, value, prior, at) => {
|
|
292
|
+
if (spans === undefined) {
|
|
293
|
+
spans = new Map();
|
|
294
|
+
for (const span of headingSpans(content, internal.bodyStart))
|
|
295
|
+
if (!spans.has(span.name))
|
|
296
|
+
spans.set(span.name, span);
|
|
297
|
+
}
|
|
298
|
+
const span = spans.get(name);
|
|
299
|
+
if (span === undefined)
|
|
300
|
+
return refuse('', 'required-section', name);
|
|
301
|
+
if (content.slice(span.bodyStart, span.bodyEnd) !== prior)
|
|
302
|
+
return 'stale';
|
|
303
|
+
return [{ start: span.bodyStart, end: span.bodyEnd, text: value, at }];
|
|
304
|
+
};
|
|
305
|
+
const link = (href, to, at) => {
|
|
306
|
+
if (occurrences === undefined) {
|
|
307
|
+
occurrences = new Map();
|
|
308
|
+
for (const occurrence of destinations(inventoryOf(document))) {
|
|
309
|
+
const same = occurrences.get(occurrence.href);
|
|
310
|
+
if (same === undefined)
|
|
311
|
+
occurrences.set(occurrence.href, [occurrence]);
|
|
312
|
+
else
|
|
313
|
+
same.push(occurrence);
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
const found = occurrences.get(href);
|
|
317
|
+
if (found === undefined)
|
|
318
|
+
return 'stale';
|
|
319
|
+
const written = retarget(href, to);
|
|
320
|
+
if (destinationText(written, false) === null)
|
|
321
|
+
return refuse('', 'unwritable-link', written);
|
|
322
|
+
return found.map((occurrence) => ({
|
|
323
|
+
start: internal.bodyStart + occurrence.start,
|
|
324
|
+
end: internal.bodyStart + occurrence.end,
|
|
325
|
+
text: destinationText(written, occurrence.bracketed),
|
|
326
|
+
at,
|
|
327
|
+
}));
|
|
328
|
+
};
|
|
329
|
+
const reference = (pointer, value, prior, at) => {
|
|
330
|
+
const node = internal.root === null ? undefined : nodeAt(internal.root, pointer, entryOf);
|
|
331
|
+
if (node?.kind !== 'scalar' || node.value !== prior)
|
|
332
|
+
return 'stale';
|
|
333
|
+
return [
|
|
334
|
+
{ start: node.start, end: node.end, text: scalarText(content[node.start], value), at },
|
|
335
|
+
];
|
|
336
|
+
};
|
|
337
|
+
return (change, at) => {
|
|
301
338
|
if (change.kind === 'field')
|
|
302
|
-
return
|
|
303
|
-
|
|
339
|
+
return field(change.name, change.value, change.prior, at);
|
|
340
|
+
if (change.kind === 'section')
|
|
341
|
+
return section(change.name, change.value, change.prior, at);
|
|
342
|
+
if (change.kind === 'link')
|
|
343
|
+
return link(change.href, change.to, at);
|
|
344
|
+
if (change.kind === 'reference')
|
|
345
|
+
return reference(change.pointer, change.value, change.prior, at);
|
|
346
|
+
if (document.preamble !== change.prior)
|
|
347
|
+
return 'stale';
|
|
348
|
+
const start = internal.bodyStart;
|
|
349
|
+
return [{ start, end: start + document.preamble.length, text: change.value, at }];
|
|
350
|
+
};
|
|
351
|
+
}
|
|
352
|
+
function overlapping(edits, stale) {
|
|
353
|
+
let reach = -1;
|
|
354
|
+
let owner = -1;
|
|
355
|
+
for (const edit of edits) {
|
|
356
|
+
if (edit.start < reach)
|
|
357
|
+
stale.add(Math.max(edit.at, owner));
|
|
358
|
+
if (edit.end > reach) {
|
|
359
|
+
reach = edit.end;
|
|
360
|
+
owner = edit.at;
|
|
361
|
+
}
|
|
304
362
|
}
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
363
|
+
}
|
|
364
|
+
function firstHeadings(content, bodyStart) {
|
|
365
|
+
const first = new Map();
|
|
366
|
+
for (const span of headingSpans(content, bodyStart)) {
|
|
367
|
+
if (!first.has(span.name))
|
|
368
|
+
first.set(span.name, span.headingStart);
|
|
369
|
+
}
|
|
370
|
+
return first;
|
|
371
|
+
}
|
|
372
|
+
function hidden(before, bodyStart, after, shifts) {
|
|
373
|
+
const placed = firstHeadings(after, bodyStartOf(after));
|
|
374
|
+
let shift = 0;
|
|
375
|
+
let index = 0;
|
|
376
|
+
for (const [name, at] of firstHeadings(before, bodyStart)) {
|
|
377
|
+
while (index < shifts.length && shifts[index][0] <= at) {
|
|
378
|
+
shift += shifts[index][1];
|
|
379
|
+
index += 1;
|
|
308
380
|
}
|
|
309
|
-
|
|
381
|
+
if (placed.get(name) !== at + shift)
|
|
382
|
+
return name;
|
|
310
383
|
}
|
|
384
|
+
return undefined;
|
|
385
|
+
}
|
|
386
|
+
function spliced(source, edits) {
|
|
387
|
+
const shifts = [];
|
|
388
|
+
const pieces = [];
|
|
389
|
+
let cursor = 0;
|
|
390
|
+
let entered = false;
|
|
391
|
+
let ended = false;
|
|
392
|
+
for (const edit of edits) {
|
|
393
|
+
const kept = source.slice(cursor, edit.start);
|
|
394
|
+
pieces.push(kept);
|
|
395
|
+
if (kept !== '')
|
|
396
|
+
ended = kept.endsWith('\n');
|
|
397
|
+
let text = edit.text;
|
|
398
|
+
if (edit.entry === true && !entered) {
|
|
399
|
+
entered = true;
|
|
400
|
+
if (!ended)
|
|
401
|
+
text = `${lineEnding(source)}${text}`;
|
|
402
|
+
}
|
|
403
|
+
pieces.push(text);
|
|
404
|
+
if (text !== '')
|
|
405
|
+
ended = text.endsWith('\n');
|
|
406
|
+
shifts.push([edit.start, text.length - (edit.end - edit.start)]);
|
|
407
|
+
cursor = edit.end;
|
|
408
|
+
}
|
|
409
|
+
pieces.push(source.slice(cursor));
|
|
410
|
+
return { content: pieces.join(''), shifts };
|
|
411
|
+
}
|
|
412
|
+
export function apply(document, changes) {
|
|
413
|
+
const internal = internalOf(document);
|
|
414
|
+
if (!('bodyStart' in internal))
|
|
415
|
+
return { outcome: 'refuse', refusal: internal };
|
|
416
|
+
const plan = planner(document, internal);
|
|
417
|
+
const edits = [];
|
|
418
|
+
const stale = new Set();
|
|
419
|
+
const named = new Set();
|
|
420
|
+
for (const [at, change] of changes.entries()) {
|
|
421
|
+
const planned = plan(change, at);
|
|
422
|
+
if (planned === 'stale') {
|
|
423
|
+
stale.add(at);
|
|
424
|
+
continue;
|
|
425
|
+
}
|
|
426
|
+
if (!Array.isArray(planned))
|
|
427
|
+
return { outcome: 'refuse', refusal: planned };
|
|
428
|
+
const part = partOf(change);
|
|
429
|
+
if (named.has(part)) {
|
|
430
|
+
stale.add(at);
|
|
431
|
+
continue;
|
|
432
|
+
}
|
|
433
|
+
named.add(part);
|
|
434
|
+
for (const edit of planned)
|
|
435
|
+
edits.push(edit);
|
|
436
|
+
}
|
|
437
|
+
edits.sort((left, right) => left.start - right.start || left.end - right.end || left.at - right.at);
|
|
438
|
+
overlapping(edits, stale);
|
|
439
|
+
if (stale.size > 0)
|
|
440
|
+
return { outcome: 'stale', document, stale: [...stale].sort((left, right) => left - right) };
|
|
441
|
+
const source = document.content;
|
|
442
|
+
const { content } = spliced(source, edits);
|
|
443
|
+
if (content === source)
|
|
444
|
+
return { outcome: 'applied', document };
|
|
445
|
+
const replaced = edits.filter((edit) => {
|
|
446
|
+
const kind = changes[edit.at].kind;
|
|
447
|
+
return kind === 'section' || kind === 'preamble';
|
|
448
|
+
});
|
|
449
|
+
if (replaced.length > 0) {
|
|
450
|
+
const checked = spliced(source, replaced);
|
|
451
|
+
const name = hidden(source, internal.bodyStart, checked.content, checked.shifts);
|
|
452
|
+
if (name !== undefined)
|
|
453
|
+
return { outcome: 'refuse', refusal: refuse('', 'overlapping-section', name) };
|
|
454
|
+
}
|
|
455
|
+
const parsed = parse(content);
|
|
456
|
+
if (!parsed.ok)
|
|
457
|
+
return { outcome: 'refuse', refusal: parsed.refusal };
|
|
458
|
+
return { outcome: 'applied', document: parsed.document };
|
|
311
459
|
}
|
|
312
460
|
export function serialize(document) {
|
|
313
461
|
return document.content;
|
package/dist/fields.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
export declare const TYPE_FIELD = "type";
|
|
1
2
|
export declare const MEMBER_FIELD = "member";
|
|
2
3
|
export declare const SITE_FIELD = "site";
|
|
3
4
|
export declare const RECEIVED_FIELD = "received";
|
|
5
|
+
export declare const HOUSE_FIELDS: readonly string[];
|
|
4
6
|
//# sourceMappingURL=fields.d.ts.map
|
package/dist/fields.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fields.d.ts","sourceRoot":"","sources":["../src/fields.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,YAAY,WAAW,CAAC;AAErC,eAAO,MAAM,UAAU,SAAS,CAAC;AAEjC,eAAO,MAAM,cAAc,aAAa,CAAC"}
|
|
1
|
+
{"version":3,"file":"fields.d.ts","sourceRoot":"","sources":["../src/fields.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,UAAU,SAAS,CAAC;AAEjC,eAAO,MAAM,YAAY,WAAW,CAAC;AAErC,eAAO,MAAM,UAAU,SAAS,CAAC;AAEjC,eAAO,MAAM,cAAc,aAAa,CAAC;AAEzC,eAAO,MAAM,YAAY,EAAE,SAAS,MAAM,EAKzC,CAAC"}
|
package/dist/fields.js
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -1,15 +1,21 @@
|
|
|
1
|
-
export { dated, factKind, facts, fires, holds, instant, itemFires, pointed, prosePaths, WRITE_DOORS, } from './condition.js';
|
|
2
|
-
export type { Actor, Condition, ConditionScope, ConditionTest, ConditionValue, CronCondition, DateCondition, DocumentCondition, DocumentEvent, DocumentFacts, Fact, FactKind, ItemCondition, ItemFacts, Resolved, WriteDoor, } from './condition.js';
|
|
1
|
+
export { ACTOR_PATHS, actorOperators, changeFields, CONDITION_OPERATORS, dated, datePointers, DOCUMENT_PATHS, factKind, facts, fires, holds, instant, ITEM_PATHS, itemFires, itemKind, itemOperators, memberPointers, members, pointed, prosePaths, testOperators, valuedChange, WRITE_DOORS, } from './condition.js';
|
|
2
|
+
export type { Actor, Condition, ConditionOperator, ConditionScope, ConditionTest, ConditionValue, CronCondition, DateCondition, DocumentCondition, DocumentEvent, DocumentFacts, Fact, FactKind, ItemCondition, ItemFacts, Resolved, WriteDoor, } from './condition.js';
|
|
3
|
+
export { headingLines } from './blocks.js';
|
|
4
|
+
export type { HeadingLine } from './blocks.js';
|
|
3
5
|
export { divide } from './divide.js';
|
|
4
|
-
export { apply, bodyLinks, links, parse, relativize, relativizeBody, relinkBody, serialize, } from './document.js';
|
|
5
|
-
export { MEMBER_FIELD, RECEIVED_FIELD, SITE_FIELD } from './fields.js';
|
|
6
|
-
export { relative, retarget, target } from './paths.js';
|
|
6
|
+
export { apply, bodyLinks, links, parse, parseFrontmatter, relativize, relativizeBody, relinkBody, serialize, } from './document.js';
|
|
7
|
+
export { HOUSE_FIELDS, MEMBER_FIELD, RECEIVED_FIELD, SITE_FIELD, TYPE_FIELD } from './fields.js';
|
|
8
|
+
export { relative, retarget, roomPath, roomTarget, target } from './paths.js';
|
|
9
|
+
export type { RoomTarget } from './paths.js';
|
|
7
10
|
export { MARKDOWN_SYNTAX_PROFILE } from './profile.js';
|
|
8
11
|
export { project } from './project.js';
|
|
9
12
|
export { references } from './references.js';
|
|
10
13
|
export { ARRAY_INDEX, ATTACHMENT, crossedFacts, escapePointer, itemFacts, memberFacts, pointerFacts, pointerSegments, } from './definition.js';
|
|
11
|
-
export type { Definition, JsonType, PatternFacts, PointerFacts, SectionSpec, Shape, ValueFacts, } from './definition.js';
|
|
12
|
-
export type { ApplyResult, BodyResult, Change, Division, DivisionContext, Document, Library, ModelVerification, Link, ParseResult, Projection, ProjectionContext, Reference, Refusal, Span, Validation, ValidationContext, VerifiedModel, } from './types.js';
|
|
13
|
-
export { validate, verify } from './validate.js';
|
|
14
|
+
export type { Definition, FieldRules, JsonType, PatternFacts, PointerFacts, RuleValue, SectionSpec, Shape, Transition, ValueFacts, } from './definition.js';
|
|
15
|
+
export type { ApplyResult, BodyResult, Change, Division, DivisionContext, Document, DocumentCheck, Frontmatter, FrontmatterResult, Library, ModelVerification, Link, ParseResult, Projection, ProjectionContext, Reached, Reference, Refusal, RoomReference, Span, Validation, ValidationContext, ValidationStep, VerifiedModel, } from './types.js';
|
|
16
|
+
export { assess, bodyRoomLinks, checkDocument, resolves, roomReferences, sectionReferences, validate, validating, verdicts, verify, } from './validate.js';
|
|
17
|
+
export type { Assessment, Verdicts } from './validate.js';
|
|
14
18
|
export { VERSION } from './version.js';
|
|
19
|
+
export { bodyAfterFrontmatter, equalValue, frontmatterValue } from './yaml.js';
|
|
20
|
+
export type { FrontmatterValue } from './yaml.js';
|
|
15
21
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,EACL,QAAQ,EACR,KAAK,EACL,KAAK,EACL,KAAK,EACL,OAAO,EACP,SAAS,EACT,OAAO,EACP,UAAU,EACV,WAAW,GACZ,MAAM,gBAAgB,CAAC;AACxB,YAAY,EACV,KAAK,EACL,SAAS,EACT,cAAc,EACd,aAAa,EACb,cAAc,EACd,aAAa,EACb,aAAa,EACb,iBAAiB,EACjB,aAAa,EACb,aAAa,EACb,IAAI,EACJ,QAAQ,EACR,aAAa,EACb,SAAS,EACT,QAAQ,EACR,SAAS,GACV,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EACL,KAAK,EACL,SAAS,EACT,KAAK,EACL,KAAK,EACL,UAAU,EACV,cAAc,EACd,UAAU,EACV,SAAS,GACV,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,WAAW,EACX,cAAc,EACd,YAAY,EACZ,mBAAmB,EACnB,KAAK,EACL,YAAY,EACZ,cAAc,EACd,QAAQ,EACR,KAAK,EACL,KAAK,EACL,KAAK,EACL,OAAO,EACP,UAAU,EACV,SAAS,EACT,QAAQ,EACR,aAAa,EACb,cAAc,EACd,OAAO,EACP,OAAO,EACP,UAAU,EACV,aAAa,EACb,YAAY,EACZ,WAAW,GACZ,MAAM,gBAAgB,CAAC;AACxB,YAAY,EACV,KAAK,EACL,SAAS,EACT,iBAAiB,EACjB,cAAc,EACd,aAAa,EACb,cAAc,EACd,aAAa,EACb,aAAa,EACb,iBAAiB,EACjB,aAAa,EACb,aAAa,EACb,IAAI,EACJ,QAAQ,EACR,aAAa,EACb,SAAS,EACT,QAAQ,EACR,SAAS,GACV,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,YAAY,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EACL,KAAK,EACL,SAAS,EACT,KAAK,EACL,KAAK,EACL,gBAAgB,EAChB,UAAU,EACV,cAAc,EACd,UAAU,EACV,SAAS,GACV,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,cAAc,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACjG,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAC9E,YAAY,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAC;AACvD,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EACL,WAAW,EACX,UAAU,EACV,YAAY,EACZ,aAAa,EACb,SAAS,EACT,WAAW,EACX,YAAY,EACZ,eAAe,GAChB,MAAM,iBAAiB,CAAC;AACzB,YAAY,EACV,UAAU,EACV,UAAU,EACV,QAAQ,EACR,YAAY,EACZ,YAAY,EACZ,SAAS,EACT,WAAW,EACX,KAAK,EACL,UAAU,EACV,UAAU,GACX,MAAM,iBAAiB,CAAC;AACzB,YAAY,EACV,WAAW,EACX,UAAU,EACV,MAAM,EACN,QAAQ,EACR,eAAe,EACf,QAAQ,EACR,aAAa,EACb,WAAW,EACX,iBAAiB,EACjB,OAAO,EACP,iBAAiB,EACjB,IAAI,EACJ,WAAW,EACX,UAAU,EACV,iBAAiB,EACjB,OAAO,EACP,SAAS,EACT,OAAO,EACP,aAAa,EACb,IAAI,EACJ,UAAU,EACV,iBAAiB,EACjB,cAAc,EACd,aAAa,GACd,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,MAAM,EACN,aAAa,EACb,aAAa,EACb,QAAQ,EACR,cAAc,EACd,iBAAiB,EACjB,QAAQ,EACR,UAAU,EACV,QAAQ,EACR,MAAM,GACP,MAAM,eAAe,CAAC;AACvB,YAAY,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAC1D,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EAAE,oBAAoB,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAC/E,YAAY,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC"}
|