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