@agentshouse/mdmodel 0.1.0-ccf1367190878 → 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 +152 -142
- 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 -10
- package/dist/condition.d.ts.map +1 -1
- package/dist/condition.js +218 -85
- package/dist/definition.d.ts +3 -1
- package/dist/definition.d.ts.map +1 -1
- package/dist/definition.js +105 -21
- package/dist/divide.d.ts.map +1 -1
- package/dist/divide.js +27 -17
- package/dist/document.d.ts +5 -5
- package/dist/document.d.ts.map +1 -1
- package/dist/document.js +304 -187
- package/dist/index.d.ts +9 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -5
- package/dist/links.d.ts +2 -0
- package/dist/links.d.ts.map +1 -1
- package/dist/links.js +107 -50
- package/dist/paths.d.ts +9 -1
- package/dist/paths.d.ts.map +1 -1
- package/dist/paths.js +28 -2
- 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.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 +28 -4
- package/dist/types.d.ts.map +1 -1
- package/dist/validate.d.ts +27 -3
- package/dist/validate.d.ts.map +1 -1
- package/dist/validate.js +219 -70
- package/dist/yaml.d.ts.map +1 -1
- package/dist/yaml.js +36 -3
- package/fixtures/README.md +9 -4
- 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 +7 -2
package/dist/pattern.js
ADDED
|
@@ -0,0 +1,355 @@
|
|
|
1
|
+
const NativeRegExp = RegExp;
|
|
2
|
+
export const PATTERN_STATES = 1_000;
|
|
3
|
+
const VISIT_BUDGET = 100_000_000;
|
|
4
|
+
const HELD_PATTERNS = 512;
|
|
5
|
+
export class PatternTooCostly extends Error {
|
|
6
|
+
}
|
|
7
|
+
let scope = null;
|
|
8
|
+
const ASCII = 128;
|
|
9
|
+
class Unsupported extends Error {
|
|
10
|
+
}
|
|
11
|
+
const ATOM = 0;
|
|
12
|
+
const SPLIT = 1;
|
|
13
|
+
const ASSERT = 2;
|
|
14
|
+
const MATCH = 3;
|
|
15
|
+
class Atom {
|
|
16
|
+
literal;
|
|
17
|
+
ascii = new Int8Array(ASCII);
|
|
18
|
+
matcher;
|
|
19
|
+
constructor(source, literal) {
|
|
20
|
+
this.literal = literal;
|
|
21
|
+
this.matcher = new NativeRegExp(`^(?:${source})$`, 'u');
|
|
22
|
+
}
|
|
23
|
+
holds(point) {
|
|
24
|
+
if (this.literal !== null)
|
|
25
|
+
return point === this.literal;
|
|
26
|
+
if (point >= ASCII)
|
|
27
|
+
return this.matcher.test(String.fromCodePoint(point));
|
|
28
|
+
const known = this.ascii[point];
|
|
29
|
+
if (known !== 0)
|
|
30
|
+
return known > 0;
|
|
31
|
+
const held = this.matcher.test(String.fromCharCode(point));
|
|
32
|
+
this.ascii[point] = held ? 1 : -1;
|
|
33
|
+
return held;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
class Reader {
|
|
37
|
+
source;
|
|
38
|
+
at = 0;
|
|
39
|
+
constructor(source) {
|
|
40
|
+
this.source = source;
|
|
41
|
+
}
|
|
42
|
+
peek(offset = 0) {
|
|
43
|
+
return this.source[this.at + offset];
|
|
44
|
+
}
|
|
45
|
+
starts(text) {
|
|
46
|
+
return this.source.startsWith(text, this.at);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
function digits(reader) {
|
|
50
|
+
const from = reader.at;
|
|
51
|
+
while (/[0-9]/.test(reader.peek() ?? ''))
|
|
52
|
+
reader.at += 1;
|
|
53
|
+
return reader.at === from ? null : Number(reader.source.slice(from, reader.at));
|
|
54
|
+
}
|
|
55
|
+
function escapeEnd(source, at) {
|
|
56
|
+
const next = source[at + 1];
|
|
57
|
+
if (next === 'c')
|
|
58
|
+
return at + 3;
|
|
59
|
+
if (next === 'x')
|
|
60
|
+
return at + 4;
|
|
61
|
+
if ((next === 'u' && source[at + 2] === '{') || next === 'p' || next === 'P')
|
|
62
|
+
return source.indexOf('}', at + 2) + 1;
|
|
63
|
+
if (next !== 'u')
|
|
64
|
+
return at + 2;
|
|
65
|
+
const end = at + 6;
|
|
66
|
+
const lead = Number.parseInt(source.slice(at + 2, end), 16);
|
|
67
|
+
const trail = /^\\u([0-9a-fA-F]{4})$/.exec(source.slice(end, end + 6))?.[1];
|
|
68
|
+
const paired = lead >= 0xd800 &&
|
|
69
|
+
lead <= 0xdbff &&
|
|
70
|
+
trail !== undefined &&
|
|
71
|
+
Number.parseInt(trail, 16) >= 0xdc00 &&
|
|
72
|
+
Number.parseInt(trail, 16) <= 0xdfff;
|
|
73
|
+
return paired ? end + 6 : end;
|
|
74
|
+
}
|
|
75
|
+
function classEnd(source, at) {
|
|
76
|
+
let i = at + 1;
|
|
77
|
+
while (source[i] !== ']')
|
|
78
|
+
i += source[i] === '\\' ? 2 : 1;
|
|
79
|
+
return i + 1;
|
|
80
|
+
}
|
|
81
|
+
function escaped(reader) {
|
|
82
|
+
const next = reader.peek(1);
|
|
83
|
+
if (next === 'b' || next === 'B') {
|
|
84
|
+
reader.at += 2;
|
|
85
|
+
return { kind: 'assert', assertion: next === 'b' ? 'boundary' : 'inside' };
|
|
86
|
+
}
|
|
87
|
+
if (/[1-9k]/.test(next))
|
|
88
|
+
throw new Unsupported();
|
|
89
|
+
const end = escapeEnd(reader.source, reader.at);
|
|
90
|
+
const source = reader.source.slice(reader.at, end);
|
|
91
|
+
reader.at = end;
|
|
92
|
+
return { kind: 'atom', source, literal: false };
|
|
93
|
+
}
|
|
94
|
+
function group(reader) {
|
|
95
|
+
reader.at += 1;
|
|
96
|
+
if (reader.starts('?:'))
|
|
97
|
+
reader.at += 2;
|
|
98
|
+
else if (reader.starts('?<') && !reader.starts('?<=') && !reader.starts('?<!'))
|
|
99
|
+
reader.at = reader.source.indexOf('>', reader.at) + 1;
|
|
100
|
+
else if (reader.peek() === '?')
|
|
101
|
+
throw new Unsupported();
|
|
102
|
+
const alternatives = disjunction(reader);
|
|
103
|
+
reader.at += 1;
|
|
104
|
+
return { kind: 'group', alternatives };
|
|
105
|
+
}
|
|
106
|
+
function atom(reader) {
|
|
107
|
+
const character = reader.peek();
|
|
108
|
+
if (character === '^' || character === '$') {
|
|
109
|
+
reader.at += 1;
|
|
110
|
+
return { kind: 'assert', assertion: character === '^' ? 'start' : 'end' };
|
|
111
|
+
}
|
|
112
|
+
if (character === '\\')
|
|
113
|
+
return escaped(reader);
|
|
114
|
+
if (character === '(')
|
|
115
|
+
return group(reader);
|
|
116
|
+
if (character === '[' || character === '.') {
|
|
117
|
+
const end = character === '[' ? classEnd(reader.source, reader.at) : reader.at + 1;
|
|
118
|
+
const source = reader.source.slice(reader.at, end);
|
|
119
|
+
reader.at = end;
|
|
120
|
+
return { kind: 'atom', source, literal: false };
|
|
121
|
+
}
|
|
122
|
+
const source = String.fromCodePoint(reader.source.codePointAt(reader.at));
|
|
123
|
+
reader.at += source.length;
|
|
124
|
+
return { kind: 'atom', source, literal: true };
|
|
125
|
+
}
|
|
126
|
+
function quantified(reader, node) {
|
|
127
|
+
const sign = reader.peek();
|
|
128
|
+
let least = 1;
|
|
129
|
+
let most = 1;
|
|
130
|
+
if (sign === '*' || sign === '+' || sign === '?') {
|
|
131
|
+
reader.at += 1;
|
|
132
|
+
least = sign === '+' ? 1 : 0;
|
|
133
|
+
most = sign === '?' ? 1 : Infinity;
|
|
134
|
+
}
|
|
135
|
+
else if (sign === '{') {
|
|
136
|
+
reader.at += 1;
|
|
137
|
+
least = digits(reader);
|
|
138
|
+
most = least;
|
|
139
|
+
if (reader.peek() === ',') {
|
|
140
|
+
reader.at += 1;
|
|
141
|
+
most = digits(reader) ?? Infinity;
|
|
142
|
+
}
|
|
143
|
+
reader.at += 1;
|
|
144
|
+
}
|
|
145
|
+
else
|
|
146
|
+
return { node, least, most };
|
|
147
|
+
if (reader.peek() === '?')
|
|
148
|
+
reader.at += 1;
|
|
149
|
+
if (least > PATTERN_STATES || (Number.isFinite(most) && most > PATTERN_STATES))
|
|
150
|
+
throw new Unsupported();
|
|
151
|
+
return { node, least, most };
|
|
152
|
+
}
|
|
153
|
+
function disjunction(reader) {
|
|
154
|
+
const alternatives = [[]];
|
|
155
|
+
while (reader.at < reader.source.length && reader.peek() !== ')') {
|
|
156
|
+
if (reader.peek() === '|') {
|
|
157
|
+
reader.at += 1;
|
|
158
|
+
alternatives.push([]);
|
|
159
|
+
continue;
|
|
160
|
+
}
|
|
161
|
+
alternatives.at(-1).push(quantified(reader, atom(reader)));
|
|
162
|
+
}
|
|
163
|
+
return alternatives;
|
|
164
|
+
}
|
|
165
|
+
class Machine {
|
|
166
|
+
kinds = [];
|
|
167
|
+
outs = [];
|
|
168
|
+
alternates = [];
|
|
169
|
+
atoms = [];
|
|
170
|
+
assertions = [];
|
|
171
|
+
atomed = new Map();
|
|
172
|
+
state(kind, out, alternate = -1) {
|
|
173
|
+
if (this.kinds.length >= PATTERN_STATES)
|
|
174
|
+
throw new Unsupported();
|
|
175
|
+
this.kinds.push(kind);
|
|
176
|
+
this.outs.push(out);
|
|
177
|
+
this.alternates.push(alternate);
|
|
178
|
+
return this.kinds.length - 1;
|
|
179
|
+
}
|
|
180
|
+
atom(source, literal) {
|
|
181
|
+
const known = this.atomed.get(source);
|
|
182
|
+
if (known !== undefined)
|
|
183
|
+
return known;
|
|
184
|
+
this.atoms.push(new Atom(source, literal ? source.codePointAt(0) : null));
|
|
185
|
+
this.atomed.set(source, this.atoms.length - 1);
|
|
186
|
+
return this.atoms.length - 1;
|
|
187
|
+
}
|
|
188
|
+
node(node, next) {
|
|
189
|
+
if (node.kind === 'atom')
|
|
190
|
+
return this.state(ATOM, next, this.atom(node.source, node.literal));
|
|
191
|
+
if (node.kind === 'assert') {
|
|
192
|
+
this.assertions.push(node.assertion);
|
|
193
|
+
return this.state(ASSERT, next, this.assertions.length - 1);
|
|
194
|
+
}
|
|
195
|
+
return this.alternatives(node.alternatives, next);
|
|
196
|
+
}
|
|
197
|
+
alternatives(alternatives, next) {
|
|
198
|
+
let entry = this.sequence(alternatives.at(-1), next);
|
|
199
|
+
for (let index = alternatives.length - 2; index >= 0; index -= 1)
|
|
200
|
+
entry = this.state(SPLIT, this.sequence(alternatives[index], next), entry);
|
|
201
|
+
return entry;
|
|
202
|
+
}
|
|
203
|
+
sequence(terms, next) {
|
|
204
|
+
let entry = next;
|
|
205
|
+
for (let index = terms.length - 1; index >= 0; index -= 1)
|
|
206
|
+
entry = this.term(terms[index], entry);
|
|
207
|
+
return entry;
|
|
208
|
+
}
|
|
209
|
+
term({ node, least, most }, next) {
|
|
210
|
+
if (least === 1 && most === 1)
|
|
211
|
+
return this.node(node, next);
|
|
212
|
+
let entry = next;
|
|
213
|
+
if (most === Infinity) {
|
|
214
|
+
const loop = this.state(SPLIT, -1, next);
|
|
215
|
+
const body = this.node(node, loop);
|
|
216
|
+
if (body === loop)
|
|
217
|
+
return next;
|
|
218
|
+
this.outs[loop] = body;
|
|
219
|
+
entry = loop;
|
|
220
|
+
}
|
|
221
|
+
else
|
|
222
|
+
for (let copy = least; copy < most; copy += 1) {
|
|
223
|
+
const body = this.node(node, entry);
|
|
224
|
+
if (body === entry)
|
|
225
|
+
return next;
|
|
226
|
+
entry = this.state(SPLIT, body, entry);
|
|
227
|
+
}
|
|
228
|
+
for (let copy = 0; copy < least; copy += 1) {
|
|
229
|
+
const body = this.node(node, entry);
|
|
230
|
+
if (body === entry)
|
|
231
|
+
return next;
|
|
232
|
+
entry = body;
|
|
233
|
+
}
|
|
234
|
+
return entry;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
function word(point) {
|
|
238
|
+
return ((point >= 0x30 && point <= 0x39) ||
|
|
239
|
+
(point >= 0x41 && point <= 0x5a) ||
|
|
240
|
+
(point >= 0x61 && point <= 0x7a) ||
|
|
241
|
+
point === 0x5f);
|
|
242
|
+
}
|
|
243
|
+
function holds(assertion, at, point, across) {
|
|
244
|
+
if (assertion === 'start')
|
|
245
|
+
return at === 0;
|
|
246
|
+
if (assertion === 'end')
|
|
247
|
+
return point === -1;
|
|
248
|
+
return assertion === 'boundary' ? across : !across;
|
|
249
|
+
}
|
|
250
|
+
class Linear {
|
|
251
|
+
machine;
|
|
252
|
+
start;
|
|
253
|
+
marks;
|
|
254
|
+
generation = 0;
|
|
255
|
+
constructor(machine, start) {
|
|
256
|
+
this.machine = machine;
|
|
257
|
+
this.start = start;
|
|
258
|
+
this.marks = new Float64Array(machine.kinds.length);
|
|
259
|
+
}
|
|
260
|
+
test(text) {
|
|
261
|
+
const { kinds, outs, alternates, atoms, assertions } = this.machine;
|
|
262
|
+
const marks = this.marks;
|
|
263
|
+
const budget = scope ?? { left: VISIT_BUDGET };
|
|
264
|
+
let seeds = [];
|
|
265
|
+
let previous = -1;
|
|
266
|
+
for (let at = 0;;) {
|
|
267
|
+
const point = at < text.length ? text.codePointAt(at) : -1;
|
|
268
|
+
const across = word(previous) !== word(point);
|
|
269
|
+
const generation = (this.generation += 1);
|
|
270
|
+
const active = [];
|
|
271
|
+
seeds.push(this.start);
|
|
272
|
+
while (seeds.length > 0) {
|
|
273
|
+
const state = seeds.pop();
|
|
274
|
+
if (marks[state] === generation)
|
|
275
|
+
continue;
|
|
276
|
+
marks[state] = generation;
|
|
277
|
+
if ((budget.left -= 1) < 0)
|
|
278
|
+
throw new PatternTooCostly();
|
|
279
|
+
const kind = kinds[state];
|
|
280
|
+
if (kind === MATCH)
|
|
281
|
+
return true;
|
|
282
|
+
if (kind === ATOM)
|
|
283
|
+
active.push(state);
|
|
284
|
+
else if (kind === SPLIT)
|
|
285
|
+
seeds.push(alternates[state], outs[state]);
|
|
286
|
+
else if (holds(assertions[alternates[state]], at, point, across))
|
|
287
|
+
seeds.push(outs[state]);
|
|
288
|
+
}
|
|
289
|
+
if (point === -1)
|
|
290
|
+
return false;
|
|
291
|
+
for (const state of active)
|
|
292
|
+
if (atoms[alternates[state]].holds(point))
|
|
293
|
+
seeds.push(outs[state]);
|
|
294
|
+
previous = point;
|
|
295
|
+
at += point > 0xffff ? 2 : 1;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
function compiled(source) {
|
|
300
|
+
try {
|
|
301
|
+
new NativeRegExp(source, 'u');
|
|
302
|
+
}
|
|
303
|
+
catch {
|
|
304
|
+
return null;
|
|
305
|
+
}
|
|
306
|
+
try {
|
|
307
|
+
const alternatives = disjunction(new Reader(source));
|
|
308
|
+
const machine = new Machine();
|
|
309
|
+
const start = machine.alternatives(alternatives, machine.state(MATCH, -1));
|
|
310
|
+
return new Linear(machine, start);
|
|
311
|
+
}
|
|
312
|
+
catch (error) {
|
|
313
|
+
if (error instanceof Unsupported)
|
|
314
|
+
return null;
|
|
315
|
+
throw error;
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
const held = new Map();
|
|
319
|
+
export function linearPattern(source) {
|
|
320
|
+
if (held.has(source))
|
|
321
|
+
return held.get(source);
|
|
322
|
+
const made = compiled(source);
|
|
323
|
+
if (held.size >= HELD_PATTERNS)
|
|
324
|
+
held.delete(held.keys().next().value);
|
|
325
|
+
held.set(source, made);
|
|
326
|
+
return made;
|
|
327
|
+
}
|
|
328
|
+
export function withLinearRegExp(run) {
|
|
329
|
+
const native = globalThis.RegExp;
|
|
330
|
+
function LinearRegExp(source) {
|
|
331
|
+
new NativeRegExp(source, 'u');
|
|
332
|
+
return {
|
|
333
|
+
test(text) {
|
|
334
|
+
const pattern = linearPattern(source);
|
|
335
|
+
if (pattern === null)
|
|
336
|
+
throw new SyntaxError(`the pattern ${source} is not linear`);
|
|
337
|
+
return pattern.test(text);
|
|
338
|
+
},
|
|
339
|
+
};
|
|
340
|
+
}
|
|
341
|
+
globalThis.RegExp = LinearRegExp;
|
|
342
|
+
try {
|
|
343
|
+
return run();
|
|
344
|
+
}
|
|
345
|
+
finally {
|
|
346
|
+
globalThis.RegExp = native;
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
export function openPatternBudget() {
|
|
350
|
+
const outer = scope;
|
|
351
|
+
scope = outer ?? { left: VISIT_BUDGET };
|
|
352
|
+
return () => {
|
|
353
|
+
scope = outer;
|
|
354
|
+
};
|
|
355
|
+
}
|
package/dist/project.d.ts
CHANGED
|
@@ -15,6 +15,7 @@ export type InputResult = {
|
|
|
15
15
|
readonly ok: false;
|
|
16
16
|
readonly refusal: Refusal;
|
|
17
17
|
};
|
|
18
|
+
export declare function firstEditFrom(edits: readonly Edit[], start: number): number;
|
|
18
19
|
export declare function rangeText(content: string, range: Extent, edits: readonly Edit[]): string;
|
|
19
20
|
export declare function projectionInput(document: Document, file: string, context: ProjectionContext): InputResult;
|
|
20
21
|
export declare function project(document: Document, file: string, context?: ProjectionContext): Projection;
|
package/dist/project.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"project.d.ts","sourceRoot":"","sources":["../src/project.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,iBAAiB,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAG3F,MAAM,MAAM,KAAK,GAAG,MAAM,GAAG;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAEvD,MAAM,MAAM,IAAI,GAAG,KAAK,CAAC;AAEzB,MAAM,MAAM,KAAK,GAAG;IAClB,QAAQ,CAAC,MAAM,EAAE,SAAS,KAAK,EAAE,CAAC;IAClC,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC,QAAQ,CAAC,KAAK,EAAE,SAAS,IAAI,EAAE,CAAC;CACjC,CAAC;AAEF,MAAM,MAAM,WAAW,GACnB;IAAE,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAA;CAAE,GAC5C;IAAE,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"project.d.ts","sourceRoot":"","sources":["../src/project.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,iBAAiB,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAG3F,MAAM,MAAM,KAAK,GAAG,MAAM,GAAG;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAEvD,MAAM,MAAM,IAAI,GAAG,KAAK,CAAC;AAEzB,MAAM,MAAM,KAAK,GAAG;IAClB,QAAQ,CAAC,MAAM,EAAE,SAAS,KAAK,EAAE,CAAC;IAClC,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC,QAAQ,CAAC,KAAK,EAAE,SAAS,IAAI,EAAE,CAAC;CACjC,CAAC;AAEF,MAAM,MAAM,WAAW,GACnB;IAAE,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAA;CAAE,GAC5C;IAAE,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAAC;AA0CtD,wBAAgB,aAAa,CAAC,KAAK,EAAE,SAAS,IAAI,EAAE,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAS3E;AAED,wBAAgB,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,IAAI,EAAE,GAAG,MAAM,CAWxF;AA4BD,wBAAgB,eAAe,CAC7B,QAAQ,EAAE,QAAQ,EAClB,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,iBAAiB,GACzB,WAAW,CA+Bb;AAED,wBAAgB,OAAO,CACrB,QAAQ,EAAE,QAAQ,EAClB,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE,iBAA2C,GACnD,UAAU,CAMZ"}
|
package/dist/project.js
CHANGED
|
@@ -1,24 +1,32 @@
|
|
|
1
1
|
import { memberFacts } from './definition.js';
|
|
2
|
-
import { frontmatter } from './document.js';
|
|
3
|
-
import {
|
|
4
|
-
import { target } from './paths.js';
|
|
2
|
+
import { frontmatter, inventoryOf } from './document.js';
|
|
3
|
+
import { roomTarget, target } from './paths.js';
|
|
5
4
|
import { refusal } from './refuse.js';
|
|
6
5
|
import { headingSpans } from './sections.js';
|
|
7
6
|
import { bodyStartOf } from './yaml.js';
|
|
8
|
-
function internalEdits(content, bodyStart, file, documentTargets) {
|
|
9
|
-
const inventory = linkInventory(content.slice(bodyStart));
|
|
7
|
+
function internalEdits(content, inventory, bodyStart, file, documentTargets) {
|
|
10
8
|
const internal = new Set();
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
for (const use of uses) {
|
|
9
|
+
const uses = inventory.uses
|
|
10
|
+
.filter((use) => {
|
|
14
11
|
const resolved = target(file, use.destination.href);
|
|
15
|
-
|
|
16
|
-
|
|
12
|
+
return resolved === null
|
|
13
|
+
? roomTarget(use.destination.href) !== null
|
|
14
|
+
: documentTargets.has(resolved);
|
|
15
|
+
})
|
|
16
|
+
.sort((left, right) => left.start - right.start);
|
|
17
|
+
const edits = [];
|
|
18
|
+
for (let index = uses.length - 1; index >= 0; index -= 1) {
|
|
19
|
+
const use = uses[index];
|
|
17
20
|
internal.add(use.destination.start);
|
|
18
21
|
const label = { start: bodyStart + use.labelStart, end: bodyStart + use.labelEnd };
|
|
19
|
-
const inner =
|
|
20
|
-
|
|
21
|
-
|
|
22
|
+
const inner = [];
|
|
23
|
+
for (let nested = index + 1; nested < uses.length; nested += 1) {
|
|
24
|
+
const edit = edits[uses.length - 1 - nested];
|
|
25
|
+
if (edit.start >= label.end)
|
|
26
|
+
break;
|
|
27
|
+
if (edit.start >= label.start && edit.end <= label.end)
|
|
28
|
+
inner.push(edit);
|
|
29
|
+
}
|
|
22
30
|
edits.push({
|
|
23
31
|
start: bodyStart + use.start,
|
|
24
32
|
end: bodyStart + use.end,
|
|
@@ -32,10 +40,25 @@ function internalEdits(content, bodyStart, file, documentTargets) {
|
|
|
32
40
|
}
|
|
33
41
|
return edits.sort((left, right) => left.start - right.start);
|
|
34
42
|
}
|
|
43
|
+
export function firstEditFrom(edits, start) {
|
|
44
|
+
let low = 0;
|
|
45
|
+
let high = edits.length;
|
|
46
|
+
while (low < high) {
|
|
47
|
+
const middle = (low + high) >>> 1;
|
|
48
|
+
if (edits[middle].start < start)
|
|
49
|
+
low = middle + 1;
|
|
50
|
+
else
|
|
51
|
+
high = middle;
|
|
52
|
+
}
|
|
53
|
+
return low;
|
|
54
|
+
}
|
|
35
55
|
export function rangeText(content, range, edits) {
|
|
36
56
|
let text = '';
|
|
37
57
|
let cursor = range.start;
|
|
38
|
-
for (
|
|
58
|
+
for (let index = firstEditFrom(edits, range.start); index < edits.length; index += 1) {
|
|
59
|
+
const edit = edits[index];
|
|
60
|
+
if (edit.start > range.end)
|
|
61
|
+
break;
|
|
39
62
|
if (edit.start < cursor || edit.end > range.end)
|
|
40
63
|
continue;
|
|
41
64
|
text += content.slice(cursor, edit.start) + edit.text;
|
|
@@ -73,7 +96,7 @@ function freeTextValues(definition, document) {
|
|
|
73
96
|
export function projectionInput(document, file, context) {
|
|
74
97
|
const content = document.content;
|
|
75
98
|
const bodyStart = bodyStartOf(content);
|
|
76
|
-
const edits = internalEdits(content, bodyStart, file, new Set(context.documentTargets));
|
|
99
|
+
const edits = internalEdits(content, inventoryOf(document), bodyStart, file, new Set(context.documentTargets));
|
|
77
100
|
const declared = document.type;
|
|
78
101
|
if (declared === undefined || declared === 'any') {
|
|
79
102
|
return {
|
package/dist/references.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"references.d.ts","sourceRoot":"","sources":["../src/references.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,UAAU,EAEhB,MAAM,iBAAiB,CAAC;AACzB,OAAO,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"references.d.ts","sourceRoot":"","sources":["../src/references.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,UAAU,EAEhB,MAAM,iBAAiB,CAAC;AACzB,OAAO,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AA8BzD,wBAAgB,UAAU,CACxB,QAAQ,EAAE,IAAI,CAAC,WAAW,EAAE,QAAQ,CAAC,EACrC,UAAU,EAAE,UAAU,GACrB,SAAS,SAAS,EAAE,CAItB"}
|
package/dist/references.js
CHANGED
|
@@ -5,7 +5,12 @@ function collect(value, facts, pointer, found) {
|
|
|
5
5
|
return;
|
|
6
6
|
if (typeof value === 'string') {
|
|
7
7
|
if (facts.reference !== undefined)
|
|
8
|
-
found.push({
|
|
8
|
+
found.push({
|
|
9
|
+
pointer,
|
|
10
|
+
value,
|
|
11
|
+
to: facts.reference,
|
|
12
|
+
...(facts.crossRoom === true ? { crossRoom: true } : {}),
|
|
13
|
+
});
|
|
9
14
|
return;
|
|
10
15
|
}
|
|
11
16
|
if (Array.isArray(value)) {
|
package/dist/schema.d.ts
CHANGED
|
@@ -4,5 +4,6 @@ export type SchemaRefusal = {
|
|
|
4
4
|
readonly location: string;
|
|
5
5
|
};
|
|
6
6
|
export declare function frontmatterRefusal(definition: Definition, fields: Readonly<Record<string, unknown>>): SchemaRefusal | undefined;
|
|
7
|
+
export declare function conditionedSections(definition: Definition, fields: Readonly<Record<string, unknown>>): readonly string[];
|
|
7
8
|
export declare function fitsItem(definition: Definition, section: string, text: string): boolean;
|
|
8
9
|
//# sourceMappingURL=schema.d.ts.map
|
package/dist/schema.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AACA,OAAO,EAA4B,KAAK,UAAU,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AACA,OAAO,EAA4B,KAAK,UAAU,EAAE,MAAM,iBAAiB,CAAC;AA0B5E,MAAM,MAAM,aAAa,GAAG;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC;AA6NjF,wBAAgB,kBAAkB,CAChC,UAAU,EAAE,UAAU,EACtB,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GACxC,aAAa,GAAG,SAAS,CAI3B;AAYD,wBAAgB,mBAAmB,CACjC,UAAU,EAAE,UAAU,EACtB,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GACxC,SAAS,MAAM,EAAE,CAGnB;AAED,wBAAgB,QAAQ,CAAC,UAAU,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAMvF"}
|
package/dist/schema.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Validator } from '@cfworker/json-schema';
|
|
2
2
|
import { escapePointer, refTarget } from './definition.js';
|
|
3
3
|
import { MEMBER_FIELD, RECEIVED_FIELD, SITE_FIELD, TYPE_FIELD } from './fields.js';
|
|
4
|
+
import { withLinearRegExp } from './pattern.js';
|
|
4
5
|
import { interpretPlain, isRecord } from './yaml.js';
|
|
5
6
|
const STANDING_MAPS = ['properties', 'patternProperties'];
|
|
6
7
|
const STANDING = [
|
|
@@ -95,10 +96,49 @@ function closed(node, inPlace, defs, always = false) {
|
|
|
95
96
|
function copied(value) {
|
|
96
97
|
return JSON.parse(JSON.stringify(value));
|
|
97
98
|
}
|
|
99
|
+
function asks(asked) {
|
|
100
|
+
return asked.sections.length > 0 || asked.conditions.length > 0;
|
|
101
|
+
}
|
|
102
|
+
function branchOf(node, defs, closedDefs) {
|
|
103
|
+
if (!isRecord(node))
|
|
104
|
+
return { schema: node, asked: { sections: [], conditions: [] } };
|
|
105
|
+
const { sections, ...rest } = node;
|
|
106
|
+
const read = unconditioned(rest, defs, closedDefs);
|
|
107
|
+
const required = Object.entries(isRecord(sections) ? sections : {}).flatMap(([name, spec]) => isRecord(spec) && spec.required === true ? [name] : []);
|
|
108
|
+
return { schema: read.schema, asked: { sections: required, conditions: read.conditions } };
|
|
109
|
+
}
|
|
110
|
+
function unconditioned(node, defs, closedDefs) {
|
|
111
|
+
if (!isRecord(node))
|
|
112
|
+
return { schema: node, conditions: [] };
|
|
113
|
+
const schema = { ...node };
|
|
114
|
+
const conditions = [];
|
|
115
|
+
if (Array.isArray(node.allOf))
|
|
116
|
+
schema.allOf = node.allOf.map((entry) => {
|
|
117
|
+
const read = unconditioned(entry, defs, closedDefs);
|
|
118
|
+
conditions.push(...read.conditions);
|
|
119
|
+
return read.schema;
|
|
120
|
+
});
|
|
121
|
+
if (node.if === undefined)
|
|
122
|
+
return { schema, conditions };
|
|
123
|
+
const met = branchOf(node.then, defs, closedDefs);
|
|
124
|
+
const unmet = branchOf(node.else, defs, closedDefs);
|
|
125
|
+
if (node.then !== undefined)
|
|
126
|
+
schema.then = met.schema;
|
|
127
|
+
if (node.else !== undefined)
|
|
128
|
+
schema.else = unmet.schema;
|
|
129
|
+
if (!asks(met.asked) && !asks(unmet.asked))
|
|
130
|
+
return { schema, conditions };
|
|
131
|
+
const test = new Validator(isRecord(node.if)
|
|
132
|
+
? { ...closed(node.if, true, defs), $defs: closedDefs }
|
|
133
|
+
: node.if === true, '2020-12', false);
|
|
134
|
+
return { schema, conditions: [...conditions, { test, met: met.asked, unmet: unmet.asked }] };
|
|
135
|
+
}
|
|
98
136
|
function compile(definition) {
|
|
99
137
|
const defs = copied(definition.shape.defs);
|
|
100
|
-
const { sections, member_document: _mark, ...
|
|
138
|
+
const { sections, member_document: _mark, ...written } = copied(definition.shape.schema);
|
|
101
139
|
const closedDefs = mapped(defs, (held) => closed(held, true, defs));
|
|
140
|
+
const read = unconditioned(written, defs, closedDefs);
|
|
141
|
+
const root = read.schema;
|
|
102
142
|
const declared = isRecord(root.properties) ? root.properties : {};
|
|
103
143
|
const properties = {
|
|
104
144
|
[TYPE_FIELD]: true,
|
|
@@ -114,7 +154,7 @@ function compile(definition) {
|
|
|
114
154
|
continue;
|
|
115
155
|
lists.set(name, new Validator({ ...closed(spec.items, false, defs), $defs: closedDefs }, '2020-12', false));
|
|
116
156
|
}
|
|
117
|
-
return { frontmatter, sections: lists };
|
|
157
|
+
return { frontmatter, sections: lists, conditions: read.conditions };
|
|
118
158
|
}
|
|
119
159
|
function compiledOf(definition) {
|
|
120
160
|
const held = compiled.get(definition);
|
|
@@ -150,14 +190,25 @@ function refusalOf(errors) {
|
|
|
150
190
|
return { rule: 'schema', location: '' };
|
|
151
191
|
}
|
|
152
192
|
export function frontmatterRefusal(definition, fields) {
|
|
153
|
-
const
|
|
193
|
+
const { frontmatter } = compiledOf(definition);
|
|
194
|
+
const result = withLinearRegExp(() => frontmatter.validate(fields));
|
|
154
195
|
return result.valid ? undefined : refusalOf(result.errors);
|
|
155
196
|
}
|
|
197
|
+
function askedOf(conditions, fields) {
|
|
198
|
+
return conditions.flatMap((condition) => {
|
|
199
|
+
const asked = condition.test.validate(fields).valid ? condition.met : condition.unmet;
|
|
200
|
+
return [...asked.sections, ...askedOf(asked.conditions, fields)];
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
export function conditionedSections(definition, fields) {
|
|
204
|
+
const { conditions } = compiledOf(definition);
|
|
205
|
+
return withLinearRegExp(() => askedOf(conditions, fields));
|
|
206
|
+
}
|
|
156
207
|
export function fitsItem(definition, section, text) {
|
|
157
208
|
const spec = definition.sections[section];
|
|
158
209
|
const validator = compiledOf(definition).sections.get(section);
|
|
159
210
|
if (spec?.kind !== 'list' || validator === undefined)
|
|
160
211
|
return false;
|
|
161
212
|
const typed = spec.items.type === 'string' ? text : interpretPlain(text);
|
|
162
|
-
return validator.validate(typed).valid;
|
|
213
|
+
return withLinearRegExp(() => validator.validate(typed).valid);
|
|
163
214
|
}
|
package/dist/types.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import type { Definition } from './definition.js';
|
|
|
2
2
|
export type Library = Readonly<Record<string, string>>;
|
|
3
3
|
export type VerifiedModel = {
|
|
4
4
|
readonly types: Readonly<Record<string, Definition>>;
|
|
5
|
+
readonly nameable?: readonly string[];
|
|
5
6
|
};
|
|
6
7
|
export type ModelVerification = {
|
|
7
8
|
readonly verdict: 'accept';
|
|
@@ -13,13 +14,14 @@ export type ModelVerification = {
|
|
|
13
14
|
};
|
|
14
15
|
export type ValidationContext = {
|
|
15
16
|
readonly binaryTargets: readonly string[];
|
|
17
|
+
readonly anyRoom?: true;
|
|
16
18
|
};
|
|
17
19
|
export type ProjectionContext = {
|
|
18
20
|
readonly documentTargets: readonly string[];
|
|
19
21
|
readonly definition?: Definition;
|
|
20
22
|
};
|
|
21
23
|
export type DivisionContext = ProjectionContext & {
|
|
22
|
-
readonly
|
|
24
|
+
readonly bound: number;
|
|
23
25
|
};
|
|
24
26
|
export type Extent = {
|
|
25
27
|
readonly start: number;
|
|
@@ -60,6 +62,10 @@ export type Validation = {
|
|
|
60
62
|
} | ({
|
|
61
63
|
readonly verdict: 'refuse';
|
|
62
64
|
} & Refusal);
|
|
65
|
+
export type ValidationStep = {
|
|
66
|
+
readonly path: string;
|
|
67
|
+
readonly resolved: number;
|
|
68
|
+
};
|
|
63
69
|
export type Projection = {
|
|
64
70
|
readonly ok: true;
|
|
65
71
|
readonly text: string;
|
|
@@ -92,14 +98,31 @@ export type Link = {
|
|
|
92
98
|
readonly href: string;
|
|
93
99
|
readonly target: string;
|
|
94
100
|
};
|
|
95
|
-
export type LinkOccurrence = Link & {
|
|
96
|
-
readonly start: number;
|
|
97
|
-
};
|
|
98
101
|
export type Reference = {
|
|
99
102
|
readonly pointer: string;
|
|
100
103
|
readonly value: string;
|
|
101
104
|
readonly to: readonly string[];
|
|
105
|
+
readonly crossRoom?: true;
|
|
102
106
|
};
|
|
107
|
+
export type RoomReference = {
|
|
108
|
+
readonly handle: string;
|
|
109
|
+
readonly path: string;
|
|
110
|
+
} & ({
|
|
111
|
+
readonly kind: 'link';
|
|
112
|
+
readonly href: string;
|
|
113
|
+
} | {
|
|
114
|
+
readonly kind: 'field';
|
|
115
|
+
readonly pointer: string;
|
|
116
|
+
readonly value: string;
|
|
117
|
+
readonly to: readonly string[];
|
|
118
|
+
readonly crossRoom?: true;
|
|
119
|
+
} | {
|
|
120
|
+
readonly kind: 'item';
|
|
121
|
+
readonly section: string;
|
|
122
|
+
readonly href: string;
|
|
123
|
+
readonly to: readonly string[];
|
|
124
|
+
readonly crossRoom?: true;
|
|
125
|
+
});
|
|
103
126
|
export type Change = {
|
|
104
127
|
readonly kind: 'field';
|
|
105
128
|
readonly name: string;
|
|
@@ -130,6 +153,7 @@ export type ApplyResult = {
|
|
|
130
153
|
} | {
|
|
131
154
|
readonly outcome: 'stale';
|
|
132
155
|
readonly document: Document;
|
|
156
|
+
readonly stale: readonly number[];
|
|
133
157
|
} | {
|
|
134
158
|
readonly outcome: 'refuse';
|
|
135
159
|
readonly refusal: Refusal;
|