@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.
Files changed (53) hide show
  1. package/API.md +230 -216
  2. package/README.md +15 -18
  3. package/dist/blocks.d.ts.map +1 -1
  4. package/dist/blocks.js +7 -14
  5. package/dist/condition.d.ts +21 -8
  6. package/dist/condition.d.ts.map +1 -1
  7. package/dist/condition.js +208 -89
  8. package/dist/definition.d.ts +16 -1
  9. package/dist/definition.d.ts.map +1 -1
  10. package/dist/definition.js +255 -29
  11. package/dist/divide.d.ts.map +1 -1
  12. package/dist/divide.js +27 -17
  13. package/dist/document.d.ts +6 -3
  14. package/dist/document.d.ts.map +1 -1
  15. package/dist/document.js +315 -167
  16. package/dist/fields.d.ts +2 -0
  17. package/dist/fields.d.ts.map +1 -1
  18. package/dist/fields.js +7 -0
  19. package/dist/index.d.ts +14 -8
  20. package/dist/index.d.ts.map +1 -1
  21. package/dist/index.js +7 -5
  22. package/dist/links.d.ts +3 -0
  23. package/dist/links.d.ts.map +1 -1
  24. package/dist/links.js +66 -40
  25. package/dist/paths.d.ts +10 -1
  26. package/dist/paths.d.ts.map +1 -1
  27. package/dist/paths.js +115 -12
  28. package/dist/project.d.ts +1 -0
  29. package/dist/project.d.ts.map +1 -1
  30. package/dist/project.js +38 -15
  31. package/dist/references.d.ts +2 -2
  32. package/dist/references.d.ts.map +1 -1
  33. package/dist/references.js +6 -1
  34. package/dist/schema.js +2 -2
  35. package/dist/types.d.ts +49 -2
  36. package/dist/types.d.ts.map +1 -1
  37. package/dist/validate.d.ts +30 -2
  38. package/dist/validate.d.ts.map +1 -1
  39. package/dist/validate.js +290 -115
  40. package/dist/yaml.d.ts +7 -3
  41. package/dist/yaml.d.ts.map +1 -1
  42. package/dist/yaml.js +72 -43
  43. package/fixtures/README.md +6 -5
  44. package/fixtures/cases/cross-room-link/context.json +1 -0
  45. package/fixtures/cases/cross-room-link/expected.json +1 -0
  46. package/fixtures/cases/cross-room-link/library/clients/acme.md +6 -0
  47. package/fixtures/cases/cross-room-link/schema.yaml +3 -0
  48. package/fixtures/cases/cross-room-reference/context.json +1 -0
  49. package/fixtures/cases/cross-room-reference/expected.json +1 -0
  50. package/fixtures/cases/cross-room-reference/library/clients/acme.md +9 -0
  51. package/fixtures/cases/cross-room-reference/schema.yaml +9 -0
  52. package/fixtures/division.json +26 -26
  53. package/package.json +4 -2
package/dist/index.js CHANGED
@@ -1,11 +1,13 @@
1
- export { dated, factKind, facts, fires, holds, instant, itemFires, pointed, prosePaths, WRITE_DOORS, } 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 { headingLines } from './blocks.js';
2
3
  export { divide } from './divide.js';
3
- export { apply, bodyLinks, links, parse, relativize, relativizeBody, relinkBody, serialize, } from './document.js';
4
- export { MEMBER_FIELD, RECEIVED_FIELD, SITE_FIELD } from './fields.js';
5
- export { relative, retarget, target } from './paths.js';
4
+ export { apply, bodyLinks, links, parse, parseFrontmatter, relativize, relativizeBody, relinkBody, serialize, } from './document.js';
5
+ export { HOUSE_FIELDS, MEMBER_FIELD, RECEIVED_FIELD, SITE_FIELD, TYPE_FIELD } from './fields.js';
6
+ export { relative, retarget, roomPath, roomTarget, target } from './paths.js';
6
7
  export { MARKDOWN_SYNTAX_PROFILE } from './profile.js';
7
8
  export { project } from './project.js';
8
9
  export { references } from './references.js';
9
10
  export { ARRAY_INDEX, ATTACHMENT, crossedFacts, escapePointer, itemFacts, memberFacts, pointerFacts, pointerSegments, } from './definition.js';
10
- export { validate, verify } from './validate.js';
11
+ export { assess, bodyRoomLinks, checkDocument, resolves, roomReferences, sectionReferences, validate, validating, verdicts, verify, } from './validate.js';
11
12
  export { VERSION } from './version.js';
13
+ export { bodyAfterFrontmatter, equalValue, frontmatterValue } from './yaml.js';
package/dist/links.d.ts CHANGED
@@ -1,3 +1,5 @@
1
+ export declare function escapes(text: string, index: number): boolean;
2
+ export declare function spanCursor(spans: readonly (readonly [number, number])[]): (index: number) => number | undefined;
1
3
  export type HrefOccurrence = {
2
4
  readonly href: string;
3
5
  readonly start: number;
@@ -22,5 +24,6 @@ export type LinkInventory = {
22
24
  };
23
25
  export declare function fenceSpans(text: string): readonly (readonly [number, number])[];
24
26
  export declare function linkInventory(body: string): LinkInventory;
27
+ export declare function destinations(inventory: LinkInventory): readonly HrefOccurrence[];
25
28
  export declare function hrefOccurrences(body: string): readonly HrefOccurrence[];
26
29
  //# sourceMappingURL=links.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"links.d.ts","sourceRoot":"","sources":["../src/links.ts"],"names":[],"mappings":"AA0sBA,MAAM,MAAM,cAAc,GAAG;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;CAC7B,CAAC;AAEF,MAAM,MAAM,OAAO,GAAG;IACpB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,WAAW,EAAE,cAAc,CAAC;CACtC,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,WAAW,EAAE,cAAc,CAAC;CACtC,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,QAAQ,CAAC,IAAI,EAAE,SAAS,OAAO,EAAE,CAAC;IAClC,QAAQ,CAAC,MAAM,EAAE,SAAS,eAAe,EAAE,CAAC;CAC7C,CAAC;AAEF,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,EAAE,CAe/E;AAYD,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,aAAa,CA2DzD;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,cAAc,EAAE,CAIvE"}
1
+ {"version":3,"file":"links.d.ts","sourceRoot":"","sources":["../src/links.ts"],"names":[],"mappings":"AA2hBA,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAE5D;AAyLD,wBAAgB,UAAU,CACxB,KAAK,EAAE,SAAS,CAAC,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,EAAE,GAC5C,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAOvC;AAED,MAAM,MAAM,cAAc,GAAG;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;CAC7B,CAAC;AAEF,MAAM,MAAM,OAAO,GAAG;IACpB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,WAAW,EAAE,cAAc,CAAC;CACtC,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,WAAW,EAAE,cAAc,CAAC;CACtC,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,QAAQ,CAAC,IAAI,EAAE,SAAS,OAAO,EAAE,CAAC;IAClC,QAAQ,CAAC,MAAM,EAAE,SAAS,eAAe,EAAE,CAAC;CAC7C,CAAC;AAEF,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,EAAE,CAe/E;AAaD,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,aAAa,CAoEzD;AAED,wBAAgB,YAAY,CAAC,SAAS,EAAE,aAAa,GAAG,SAAS,cAAc,EAAE,CAIhF;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,cAAc,EAAE,CAEvE"}
package/dist/links.js CHANGED
@@ -27,6 +27,15 @@ const INLINE_RAW = new RegExp([
27
27
  '<[A-Za-z][A-Za-z0-9+.-]{1,31}:[^\\x00-\\x20<>]*>',
28
28
  "<[A-Za-z0-9.!#$%&'*+/=?^_`{|}~-]+@[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?(?:\\.[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?)*>",
29
29
  ].join('|'), 'y');
30
+ const BACKSLASH = 0x5c;
31
+ const BACKTICK = 0x60;
32
+ const LINE_FEED = 0x0a;
33
+ const CARRIAGE_RETURN = 0x0d;
34
+ const LESS_THAN = 0x3c;
35
+ const OPEN_BRACKET = 0x5b;
36
+ const CLOSE_BRACKET = 0x5d;
37
+ const EXCLAMATION = 0x21;
38
+ const OPEN_PAREN = 0x28;
30
39
  const DELIMITER_ROW = /^\|?[ \t]*:?-+:?[ \t]*(?:\|[ \t]*:?-+:?[ \t]*)*\|?[ \t]*$/;
31
40
  const GAP = /^[ \t\v\f]+/;
32
41
  function endOfLine(text, from) {
@@ -155,28 +164,34 @@ function fenceSpan(text, line, open) {
155
164
  }
156
165
  return text.length;
157
166
  }
158
- function skipInlineCode(text, index, joins) {
167
+ function skipInlineCode(text, index, joins, unclosed) {
159
168
  let ticks = 0;
160
169
  while (text[index + ticks] === '`')
161
170
  ticks += 1;
171
+ if (index < unclosed.stop && (unclosed.runs.get(ticks) ?? -1) <= index)
172
+ return index + ticks;
173
+ const runs = new Map();
162
174
  let i = index + ticks;
163
175
  while (i < text.length) {
164
- const ch = text[i];
165
- if (ch === '`') {
176
+ const code = text.charCodeAt(i);
177
+ if (code === BACKTICK) {
166
178
  let run = 0;
167
- while (text[i + run] === '`')
179
+ while (text.charCodeAt(i + run) === BACKTICK)
168
180
  run += 1;
169
181
  if (run === ticks)
170
182
  return i + run;
183
+ runs.set(run, i);
171
184
  i += run;
172
185
  continue;
173
186
  }
174
- if (ch === '\r' && text[i + 1] !== '\n')
187
+ if (code === CARRIAGE_RETURN && text.charCodeAt(i + 1) !== LINE_FEED)
175
188
  break;
176
- if (ch === '\n' && !joins.has(i))
189
+ if (code === LINE_FEED && !joins.has(i))
177
190
  break;
178
191
  i += 1;
179
192
  }
193
+ unclosed.stop = i;
194
+ unclosed.runs = runs;
180
195
  return index + ticks;
181
196
  }
182
197
  function skipLinkSpace(text, start) {
@@ -194,21 +209,21 @@ function skipInlineSpace(text, start, joins) {
194
209
  return afterIndent(text, readLine(text, i + 1, endOfLine(text, i + 1)));
195
210
  }
196
211
  function closeEnd(text, start, open, joins) {
197
- const close = open === '(' ? ')' : open;
212
+ const close = (open === '(' ? ')' : open).charCodeAt(0);
198
213
  let i = start;
199
214
  while (i < text.length) {
200
- const ch = text[i];
215
+ const code = text.charCodeAt(i);
201
216
  if (escapes(text, i)) {
202
217
  i += 2;
203
218
  continue;
204
219
  }
205
- if (ch === close)
220
+ if (code === close)
206
221
  return i + 1;
207
- if (open === '(' && ch === '(')
222
+ if (open === '(' && code === OPEN_PAREN)
208
223
  return null;
209
- if (ch === '\r' && text[i + 1] !== '\n')
224
+ if (code === CARRIAGE_RETURN && text.charCodeAt(i + 1) !== LINE_FEED)
210
225
  return null;
211
- if (ch === '\n' && !joins.has(i))
226
+ if (code === LINE_FEED && !joins.has(i))
212
227
  return null;
213
228
  i += 1;
214
229
  }
@@ -504,24 +519,24 @@ function structure(text) {
504
519
  }
505
520
  return { joins, raw };
506
521
  }
507
- function escapes(text, index) {
508
- return text[index] === '\\' && PUNCTUATION.test(text[index + 1] ?? '');
522
+ export function escapes(text, index) {
523
+ return text.charCodeAt(index) === BACKSLASH && PUNCTUATION.test(text[index + 1] ?? '');
509
524
  }
510
525
  function referenceEnd(text, start, joins) {
511
526
  let i = start;
512
527
  while (i < text.length) {
513
- const ch = text[i];
528
+ const code = text.charCodeAt(i);
514
529
  if (escapes(text, i)) {
515
530
  i += 2;
516
531
  continue;
517
532
  }
518
- if (ch === ']')
533
+ if (code === CLOSE_BRACKET)
519
534
  return i + 1;
520
- if (ch === '[')
535
+ if (code === OPEN_BRACKET)
521
536
  return -1;
522
- if (ch === '\r' && text[i + 1] !== '\n')
537
+ if (code === CARRIAGE_RETURN && text.charCodeAt(i + 1) !== LINE_FEED)
523
538
  return -1;
524
- if (ch === '\n' && !joins.has(i))
539
+ if (code === LINE_FEED && !joins.has(i))
525
540
  return -1;
526
541
  i += 1;
527
542
  }
@@ -641,12 +656,13 @@ function endsBlock(content) {
641
656
  }
642
657
  function scanBlocks(text) {
643
658
  const { joins, raw } = structure(text);
659
+ const rawEnd = spanCursor(raw);
644
660
  const definitions = new Map();
645
661
  const blocks = [];
646
662
  let fresh = 0;
647
663
  let i = 0;
648
664
  while (i < text.length) {
649
- const skip = spanEndAt(raw, i);
665
+ const skip = rawEnd(i);
650
666
  if (skip !== undefined) {
651
667
  i = skip;
652
668
  continue;
@@ -667,12 +683,14 @@ function scanBlocks(text) {
667
683
  }
668
684
  return { raw, definitions, blocks, joins };
669
685
  }
670
- function spanEndAt(spans, index) {
671
- for (const [start, end] of spans) {
672
- if (index >= start && index < end)
673
- return end;
674
- }
675
- return undefined;
686
+ export function spanCursor(spans) {
687
+ let next = 0;
688
+ return (index) => {
689
+ while (next < spans.length && spans[next][1] <= index)
690
+ next += 1;
691
+ const span = spans[next];
692
+ return span !== undefined && span[0] <= index ? span[1] : undefined;
693
+ };
676
694
  }
677
695
  export function fenceSpans(text) {
678
696
  const fences = [];
@@ -693,9 +711,10 @@ export function fenceSpans(text) {
693
711
  function paragraphEnd(text, from, joins) {
694
712
  let i = from;
695
713
  while (i < text.length) {
696
- if (text[i] === '\n' && !joins.has(i))
714
+ const code = text.charCodeAt(i);
715
+ if (code === LINE_FEED && !joins.has(i))
697
716
  return i;
698
- if (text[i] === '\r' && text[i + 1] !== '\n')
717
+ if (code === CARRIAGE_RETURN && text.charCodeAt(i + 1) !== LINE_FEED)
699
718
  return i;
700
719
  i += 1;
701
720
  }
@@ -703,28 +722,30 @@ function paragraphEnd(text, from, joins) {
703
722
  }
704
723
  export function linkInventory(body) {
705
724
  const { raw, definitions, blocks, joins } = scanBlocks(body);
706
- const skipped = [...raw, ...blocks.map((block) => [block.start, block.end])];
725
+ const rawEnd = spanCursor(raw);
726
+ const blockEnd = spanCursor(blocks.map((block) => [block.start, block.end]));
707
727
  const uses = [];
708
728
  const openers = [];
729
+ const unclosed = { stop: 0, runs: new Map() };
709
730
  let paragraph = { start: 0, end: -1, text: '' };
710
731
  let i = 0;
711
732
  while (i < body.length) {
712
- const skipEnd = spanEndAt(skipped, i);
733
+ const skipEnd = rawEnd(i) ?? blockEnd(i);
713
734
  if (skipEnd !== undefined) {
714
735
  openers.length = 0;
715
736
  i = skipEnd;
716
737
  continue;
717
738
  }
718
- const ch = body[i];
739
+ const code = body.charCodeAt(i);
719
740
  if (escapes(body, i)) {
720
741
  i += 2;
721
742
  continue;
722
743
  }
723
- if (ch === '`') {
724
- i = skipInlineCode(body, i, joins);
744
+ if (code === BACKTICK) {
745
+ i = skipInlineCode(body, i, joins, unclosed);
725
746
  continue;
726
747
  }
727
- if (ch === '<') {
748
+ if (code === LESS_THAN) {
728
749
  if (i >= paragraph.end) {
729
750
  const end = paragraphEnd(body, i, joins);
730
751
  paragraph = { start: i, end, text: body.slice(i, end) };
@@ -736,18 +757,20 @@ export function linkInventory(body) {
736
757
  continue;
737
758
  }
738
759
  }
739
- if ((ch === '\n' && !joins.has(i)) || (ch === '\r' && body[i + 1] !== '\n'))
760
+ if ((code === LINE_FEED && !joins.has(i)) ||
761
+ (code === CARRIAGE_RETURN && body.charCodeAt(i + 1) !== LINE_FEED))
740
762
  openers.length = 0;
741
- if (ch === '[' || (ch === '!' && body[i + 1] === '[')) {
763
+ if (code === OPEN_BRACKET ||
764
+ (code === EXCLAMATION && body.charCodeAt(i + 1) === OPEN_BRACKET)) {
742
765
  const previous = openers.at(-1);
743
766
  if (previous !== undefined)
744
767
  previous.bracketAfter = true;
745
- const image = ch === '!';
768
+ const image = code === EXCLAMATION;
746
769
  openers.push({ at: image ? i + 1 : i, start: i, image, active: true, bracketAfter: false });
747
770
  i += image ? 2 : 1;
748
771
  continue;
749
772
  }
750
- if (ch === ']') {
773
+ if (code === CLOSE_BRACKET) {
751
774
  const opener = openers.pop();
752
775
  const use = opener?.active ? closeLink(body, opener, i, definitions, joins) : null;
753
776
  if (opener === undefined || use === null) {
@@ -766,9 +789,12 @@ export function linkInventory(body) {
766
789
  }
767
790
  return { uses, blocks };
768
791
  }
769
- export function hrefOccurrences(body) {
792
+ export function destinations(inventory) {
770
793
  const byStart = new Map();
771
- for (const use of linkInventory(body).uses)
794
+ for (const use of inventory.uses)
772
795
  byStart.set(use.destination.start, use.destination);
773
796
  return [...byStart.values()].sort((left, right) => left.start - right.start);
774
797
  }
798
+ export function hrefOccurrences(body) {
799
+ return destinations(linkInventory(body));
800
+ }
package/dist/paths.d.ts CHANGED
@@ -1,14 +1,23 @@
1
1
  export declare function dirname(path: string): string;
2
2
  export declare function join(base: string, relative: string): string;
3
3
  export declare function normalize(path: string): string | null;
4
+ export declare function destinationPath(href: string): string | null;
5
+ export type RoomTarget = {
6
+ readonly handle: string;
7
+ readonly path: string;
8
+ };
4
9
  export declare function classifyHref(from: string, href: string): {
5
10
  kind: 'skip';
6
11
  } | {
7
12
  kind: 'path';
8
13
  path: string;
9
- } | {
14
+ } | ({
15
+ kind: 'room';
16
+ } & RoomTarget) | {
10
17
  kind: 'invalid';
11
18
  };
19
+ export declare function roomTarget(href: string): RoomTarget | null;
20
+ export declare function roomPath(href: string): string | null;
12
21
  export declare function target(from: string, href: string): string | null;
13
22
  export declare function retarget(href: string, to: string): string;
14
23
  export declare function relative(from: string, target: string): string;
@@ -1 +1 @@
1
- {"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../src/paths.ts"],"names":[],"mappings":"AAAA,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAG5C;AAED,wBAAgB,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAG3D;AAED,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAYrD;AAED,wBAAgB,YAAY,CAC1B,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,GACX;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,SAAS,CAAA;CAAE,CAUzE;AAED,wBAAgB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAGhE;AAED,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,CAIzD;AAED,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAW7D;AAED,eAAO,MAAM,WAAW,YAAY,CAAC;AAErC,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAKvE"}
1
+ {"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../src/paths.ts"],"names":[],"mappings":"AAIA,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAG5C;AAED,wBAAgB,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAG3D;AAED,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAYrD;AAoDD,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAW3D;AAgBD,MAAM,MAAM,UAAU,GAAG;IAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAY5E,wBAAgB,YAAY,CAC1B,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,GAEV;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAChB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAC9B,CAAC;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG,UAAU,CAAC,GAC/B;IAAE,IAAI,EAAE,SAAS,CAAA;CAAE,CAatB;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI,CAG1D;AAED,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAGpD;AAED,wBAAgB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAGhE;AAED,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,CAGzD;AAED,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAW7D;AAED,eAAO,MAAM,WAAW,YAAY,CAAC;AAErC,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAMvE"}
package/dist/paths.js CHANGED
@@ -1,3 +1,6 @@
1
+ import { decodeNamedCharacterReference } from 'decode-named-character-reference';
2
+ import { decodeNumericCharacterReference } from 'micromark-util-decode-numeric-character-reference';
3
+ import { escapes } from './links.js';
1
4
  export function dirname(path) {
2
5
  const index = path.lastIndexOf('/');
3
6
  return index === -1 ? '' : path.slice(0, index);
@@ -22,30 +25,129 @@ export function normalize(path) {
22
25
  }
23
26
  return parts.join('/');
24
27
  }
28
+ const REFERENCE = /&(?:#[xX]([0-9a-fA-F]{1,6})|#([0-9]{1,7})|([A-Za-z][A-Za-z0-9]{0,31}));/y;
29
+ const PERCENT = /^%[0-9a-fA-F]{2}/;
30
+ function referenceAt(text, at) {
31
+ REFERENCE.lastIndex = at;
32
+ const match = REFERENCE.exec(text);
33
+ if (match === null)
34
+ return null;
35
+ const [whole, hex, decimal, name] = match;
36
+ const end = at + whole.length;
37
+ if (name !== undefined) {
38
+ const decoded = decodeNamedCharacterReference(name);
39
+ return decoded === false ? null : { text: decoded, end };
40
+ }
41
+ return {
42
+ text: decodeNumericCharacterReference(hex ?? decimal, hex === undefined ? 10 : 16),
43
+ end,
44
+ };
45
+ }
46
+ function unescaped(written) {
47
+ let base = '';
48
+ let i = 0;
49
+ while (i < written.length) {
50
+ const escaped = escapes(written, i);
51
+ const reference = escaped || written[i] !== '&' ? null : referenceAt(written, i);
52
+ const text = escaped ? written[i + 1] : (reference?.text ?? written[i]);
53
+ if (text.startsWith('?') || text.startsWith('#'))
54
+ return { base, suffix: i };
55
+ base += text;
56
+ i = escaped ? i + 2 : (reference?.end ?? i + 1);
57
+ }
58
+ return { base, suffix: written.length };
59
+ }
60
+ function located(href) {
61
+ const written = href.trim();
62
+ const { base, suffix } = unescaped(written);
63
+ const url = base.replace(/[\t\n\r]/g, '').replace(/^[\u0000-\u0020]+/, '');
64
+ return { base: suffix === written.length ? url.replace(/[\u0000-\u0020]+$/, '') : url, suffix };
65
+ }
66
+ const ENCODED = /(?:%[0-9a-fA-F]{2})+/g;
67
+ function decodedRun(run) {
68
+ try {
69
+ return decodeURIComponent(run);
70
+ }
71
+ catch {
72
+ return null;
73
+ }
74
+ }
75
+ export function destinationPath(href) {
76
+ const base = located(href).base;
77
+ let path = '';
78
+ let cursor = 0;
79
+ for (const match of base.matchAll(ENCODED)) {
80
+ const decoded = decodedRun(match[0]);
81
+ if (decoded === null)
82
+ return null;
83
+ path += base.slice(cursor, match.index) + decoded;
84
+ cursor = match.index + match[0].length;
85
+ }
86
+ return path + base.slice(cursor);
87
+ }
88
+ function written(path) {
89
+ let text = '';
90
+ for (let i = 0; i < path.length; i += 1) {
91
+ const char = path[i];
92
+ if (char === '?' || char === '#' || char < ' ')
93
+ text += encodeURIComponent(char);
94
+ else if (char === ' ' && (i === 0 || i === path.length - 1))
95
+ text += '%20';
96
+ else if (char === '%' && PERCENT.test(path.slice(i)))
97
+ text += '%25';
98
+ else if (escapes(path, i))
99
+ text += '%5C';
100
+ else if (char === '&' && referenceAt(path, i) !== null)
101
+ text += '%26';
102
+ else
103
+ text += char;
104
+ }
105
+ return text;
106
+ }
107
+ function roomTargetOf(path) {
108
+ if (!path.startsWith(SHELL_ROOMS))
109
+ return null;
110
+ const rest = path.slice(SHELL_ROOMS.length);
111
+ const slash = rest.indexOf('/');
112
+ const handle = rest.slice(0, slash);
113
+ if (slash <= 0 || handle === '.' || handle === '..')
114
+ return null;
115
+ const within = normalize(rest.slice(slash + 1));
116
+ return within === null || within === '' ? null : { handle, path: within };
117
+ }
25
118
  export function classifyHref(from, href) {
26
- const trimmed = href.trim();
27
- if (trimmed === '' || trimmed.startsWith('#'))
119
+ const base = located(href).base;
120
+ if (base === '')
28
121
  return { kind: 'skip' };
29
- if (/^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(trimmed))
122
+ if (/^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(base))
30
123
  return { kind: 'skip' };
31
- if (trimmed.startsWith('/'))
124
+ const path = destinationPath(href);
125
+ if (path === null)
32
126
  return { kind: 'invalid' };
33
- const bare = trimmed.replace(/[?#].*$/, '');
34
- if (bare === '')
35
- return { kind: 'skip' };
36
- const resolved = normalize(join(dirname(from), bare));
127
+ if (path.startsWith('/')) {
128
+ const room = roomTargetOf(path);
129
+ return room === null ? { kind: 'invalid' } : { kind: 'room', ...room };
130
+ }
131
+ const resolved = normalize(join(dirname(from), path));
37
132
  if (resolved === null || resolved === '')
38
133
  return { kind: 'invalid' };
39
134
  return { kind: 'path', path: resolved };
40
135
  }
136
+ export function roomTarget(href) {
137
+ const path = destinationPath(href);
138
+ return path === null ? null : roomTargetOf(path);
139
+ }
140
+ export function roomPath(href) {
141
+ const room = roomTarget(href);
142
+ return room === null ? null : `${SHELL_ROOMS}${room.handle}/${room.path}`;
143
+ }
41
144
  export function target(from, href) {
42
145
  const resolved = classifyHref(from, href);
43
146
  return resolved.kind === 'path' ? resolved.path : null;
44
147
  }
45
148
  export function retarget(href, to) {
46
149
  const trimmed = href.trim();
47
- const match = /[?#]/.exec(trimmed);
48
- return match === null ? to : to + trimmed.slice(match.index);
150
+ return written(to) + trimmed.slice(unescaped(trimmed).suffix);
49
151
  }
50
152
  export function relative(from, target) {
51
153
  const base = dirname(from)
@@ -62,8 +164,9 @@ export function relative(from, target) {
62
164
  export const SHELL_ROOMS = '/rooms/';
63
165
  export function shellTarget(href, handle) {
64
166
  const prefix = `${SHELL_ROOMS}${handle}/`;
65
- if (!href.startsWith(prefix))
167
+ const decoded = destinationPath(href);
168
+ if (decoded === null || !decoded.startsWith(prefix))
66
169
  return null;
67
- const path = normalize(href.slice(prefix.length).replace(/[?#].*$/s, ''));
170
+ const path = normalize(decoded.slice(prefix.length));
68
171
  return path === null || path === '' ? null : path;
69
172
  }
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;
@@ -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;AAmCtD,wBAAgB,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,IAAI,EAAE,GAAG,MAAM,CASxF;AA4BD,wBAAgB,eAAe,CAC7B,QAAQ,EAAE,QAAQ,EAClB,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,iBAAiB,GACzB,WAAW,CAyBb;AAED,wBAAgB,OAAO,CACrB,QAAQ,EAAE,QAAQ,EAClB,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE,iBAA2C,GACnD,UAAU,CAMZ"}
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 { linkInventory } from './links.js';
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 edits = [];
12
- const uses = [...inventory.uses].sort((left, right) => left.end - left.start - (right.end - right.start));
13
- for (const use of uses) {
9
+ const uses = inventory.uses
10
+ .filter((use) => {
14
11
  const resolved = target(file, use.destination.href);
15
- if (resolved === null || !documentTargets.has(resolved))
16
- continue;
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 = edits
20
- .filter((edit) => edit.start >= label.start && edit.end <= label.end)
21
- .sort((left, right) => left.start - right.start);
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 (const edit of edits) {
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 {
@@ -1,4 +1,4 @@
1
1
  import { type Definition } from './definition.js';
2
- import type { Document, Reference } from './types.js';
3
- export declare function references(document: Document, definition: Definition): readonly Reference[];
2
+ import type { Frontmatter, Reference } from './types.js';
3
+ export declare function references(document: Pick<Frontmatter, 'fields'>, definition: Definition): readonly Reference[];
4
4
  //# sourceMappingURL=references.d.ts.map
@@ -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,QAAQ,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAwBtD,wBAAgB,UAAU,CAAC,QAAQ,EAAE,QAAQ,EAAE,UAAU,EAAE,UAAU,GAAG,SAAS,SAAS,EAAE,CAI3F"}
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"}
@@ -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({ pointer, value, to: facts.reference });
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.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { Validator } from '@cfworker/json-schema';
2
2
  import { escapePointer, refTarget } from './definition.js';
3
- import { MEMBER_FIELD, RECEIVED_FIELD, SITE_FIELD } from './fields.js';
3
+ import { MEMBER_FIELD, RECEIVED_FIELD, SITE_FIELD, TYPE_FIELD } from './fields.js';
4
4
  import { interpretPlain, isRecord } from './yaml.js';
5
5
  const STANDING_MAPS = ['properties', 'patternProperties'];
6
6
  const STANDING = [
@@ -101,7 +101,7 @@ function compile(definition) {
101
101
  const closedDefs = mapped(defs, (held) => closed(held, true, defs));
102
102
  const declared = isRecord(root.properties) ? root.properties : {};
103
103
  const properties = {
104
- type: true,
104
+ [TYPE_FIELD]: true,
105
105
  [SITE_FIELD]: true,
106
106
  [RECEIVED_FIELD]: true,
107
107
  ...(definition.memberDocument ? { [MEMBER_FIELD]: { type: 'string' } } : {}),