@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.
Files changed (59) hide show
  1. package/API.md +231 -225
  2. package/README.md +16 -19
  3. package/dist/blocks.d.ts.map +1 -1
  4. package/dist/blocks.js +35 -20
  5. package/dist/condition.d.ts +41 -11
  6. package/dist/condition.d.ts.map +1 -1
  7. package/dist/condition.js +223 -87
  8. package/dist/definition.d.ts +17 -1
  9. package/dist/definition.d.ts.map +1 -1
  10. package/dist/definition.js +319 -43
  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 +326 -176
  16. package/dist/index.d.ts +13 -9
  17. package/dist/index.d.ts.map +1 -1
  18. package/dist/index.js +7 -6
  19. package/dist/links.d.ts +3 -0
  20. package/dist/links.d.ts.map +1 -1
  21. package/dist/links.js +108 -51
  22. package/dist/paths.d.ts +10 -1
  23. package/dist/paths.d.ts.map +1 -1
  24. package/dist/paths.js +118 -12
  25. package/dist/pattern.d.ts +10 -0
  26. package/dist/pattern.d.ts.map +1 -0
  27. package/dist/pattern.js +355 -0
  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.d.ts +1 -0
  35. package/dist/schema.d.ts.map +1 -1
  36. package/dist/schema.js +55 -4
  37. package/dist/types.d.ts +50 -2
  38. package/dist/types.d.ts.map +1 -1
  39. package/dist/validate.d.ts +30 -2
  40. package/dist/validate.d.ts.map +1 -1
  41. package/dist/validate.js +317 -114
  42. package/dist/yaml.d.ts.map +1 -1
  43. package/dist/yaml.js +66 -21
  44. package/fixtures/README.md +10 -5
  45. package/fixtures/cases/conditional-section/context.json +1 -0
  46. package/fixtures/cases/conditional-section/expected.json +6 -0
  47. package/fixtures/cases/conditional-section/library/questions/hosting.md +9 -0
  48. package/fixtures/cases/conditional-section/library/questions/payments.md +12 -0
  49. package/fixtures/cases/conditional-section/schema.yaml +14 -0
  50. package/fixtures/cases/cross-room-link/context.json +1 -0
  51. package/fixtures/cases/cross-room-link/expected.json +1 -0
  52. package/fixtures/cases/cross-room-link/library/clients/acme.md +6 -0
  53. package/fixtures/cases/cross-room-link/schema.yaml +3 -0
  54. package/fixtures/cases/cross-room-reference/context.json +1 -0
  55. package/fixtures/cases/cross-room-reference/expected.json +1 -0
  56. package/fixtures/cases/cross-room-reference/library/clients/acme.md +9 -0
  57. package/fixtures/cases/cross-room-reference/schema.yaml +9 -0
  58. package/fixtures/division.json +26 -26
  59. package/package.json +10 -3
package/dist/index.js CHANGED
@@ -1,12 +1,13 @@
1
- export { dated, datePointers, 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, MESSAGE_PATHS, messageFires, messageOperators, notificationFires, pointed, prosePaths, testOperators, TEXT_FIELD, 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 { apply, bodyLinks, links, parse, parseFrontmatter, relativize, relativizeBody, relinkBody, serialize, } from './document.js';
4
5
  export { HOUSE_FIELDS, MEMBER_FIELD, RECEIVED_FIELD, SITE_FIELD, TYPE_FIELD } from './fields.js';
5
- export { relative, retarget, target } from './paths.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
- export { ARRAY_INDEX, ATTACHMENT, crossedFacts, escapePointer, itemFacts, memberFacts, pointerFacts, pointerSegments, } from './definition.js';
10
- export { validate, verify } from './validate.js';
10
+ export { ARRAY_INDEX, ATTACHMENT, crossedFacts, escapePointer, itemFacts, JSON_POINTER, memberFacts, pointerFacts, pointerSegments, } from './definition.js';
11
+ export { assess, bodyRoomLinks, checkDocument, resolves, roomReferences, sectionReferences, validate, validating, verdicts, verify, } from './validate.js';
11
12
  export { VERSION } from './version.js';
12
- export { equalValue, frontmatterValue } from './yaml.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":"AAgiBA,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;AA+CD,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,aAAa,CAmEzD;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
@@ -17,17 +17,28 @@ const HTML_TAG_LINE = /^(?:<[A-Za-z][A-Za-z0-9-]*(?:[ \t\v\f]+[A-Za-z_:][A-Za-z0
17
17
  const ORDERED = /^(\d{1,9})[.)]/;
18
18
  const LABEL_LENGTH = 999;
19
19
  const PUNCTUATION = /^[!-/:-@[-`{-~]$/;
20
- const INLINE_RAW = new RegExp([
20
+ const INLINE_TAG = new RegExp([
21
21
  '<[A-Za-z][A-Za-z0-9-]*(?:[ \\t\\n\\v\\f\\r]+[A-Za-z_:][A-Za-z0-9_.:-]*(?:[ \\t\\n\\v\\f\\r]*=[ \\t\\n\\v\\f\\r]*(?:[^ \\t\\n\\v\\f\\r"\'=<>`]+|\'[^\']*\'|"[^"]*"))?)*[ \\t\\n\\v\\f\\r]*\\/?>',
22
22
  '<\\/[A-Za-z][A-Za-z0-9-]*[ \\t\\n\\v\\f\\r]*>',
23
23
  '<!--(?!-?>)(?:-?[^-])*-->',
24
- '<\\?[\\s\\S]*?\\?>',
25
- '<![A-Z]+[ \\t\\n\\v\\f\\r]+[^>]*>',
26
- '<!\\[CDATA\\[[\\s\\S]*?\\]\\]>',
24
+ ].join('|'), 'y');
25
+ const AUTOLINK = new RegExp([
27
26
  '<[A-Za-z][A-Za-z0-9+.-]{1,31}:[^\\x00-\\x20<>]*>',
28
27
  "<[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
28
  ].join('|'), 'y');
30
- const DELIMITER_ROW = /^\|?[ \t]*:?-+:?[ \t]*(?:\|[ \t]*:?-+:?[ \t]*)*\|?[ \t]*$/;
29
+ const DECLARATION = /<![A-Z]+[ \t\n\v\f\r]/y;
30
+ const INSTRUCTION = '<?';
31
+ const CDATA = '<![CDATA[';
32
+ const BACKSLASH = 0x5c;
33
+ const BACKTICK = 0x60;
34
+ const LINE_FEED = 0x0a;
35
+ const CARRIAGE_RETURN = 0x0d;
36
+ const LESS_THAN = 0x3c;
37
+ const OPEN_BRACKET = 0x5b;
38
+ const CLOSE_BRACKET = 0x5d;
39
+ const EXCLAMATION = 0x21;
40
+ const OPEN_PAREN = 0x28;
41
+ const DELIMITER_ROW = /^\|?[ \t]*:?-+:?(?:[ \t]*\|[ \t]*:?-+:?)*(?:[ \t]*\|)?[ \t]*$/;
31
42
  const GAP = /^[ \t\v\f]+/;
32
43
  function endOfLine(text, from) {
33
44
  const nl = text.indexOf('\n', from);
@@ -155,28 +166,34 @@ function fenceSpan(text, line, open) {
155
166
  }
156
167
  return text.length;
157
168
  }
158
- function skipInlineCode(text, index, joins) {
169
+ function skipInlineCode(text, index, joins, unclosed) {
159
170
  let ticks = 0;
160
171
  while (text[index + ticks] === '`')
161
172
  ticks += 1;
173
+ if (index < unclosed.stop && (unclosed.runs.get(ticks) ?? -1) <= index)
174
+ return index + ticks;
175
+ const runs = new Map();
162
176
  let i = index + ticks;
163
177
  while (i < text.length) {
164
- const ch = text[i];
165
- if (ch === '`') {
178
+ const code = text.charCodeAt(i);
179
+ if (code === BACKTICK) {
166
180
  let run = 0;
167
- while (text[i + run] === '`')
181
+ while (text.charCodeAt(i + run) === BACKTICK)
168
182
  run += 1;
169
183
  if (run === ticks)
170
184
  return i + run;
185
+ runs.set(run, i);
171
186
  i += run;
172
187
  continue;
173
188
  }
174
- if (ch === '\r' && text[i + 1] !== '\n')
189
+ if (code === CARRIAGE_RETURN && text.charCodeAt(i + 1) !== LINE_FEED)
175
190
  break;
176
- if (ch === '\n' && !joins.has(i))
191
+ if (code === LINE_FEED && !joins.has(i))
177
192
  break;
178
193
  i += 1;
179
194
  }
195
+ unclosed.stop = i;
196
+ unclosed.runs = runs;
180
197
  return index + ticks;
181
198
  }
182
199
  function skipLinkSpace(text, start) {
@@ -194,21 +211,21 @@ function skipInlineSpace(text, start, joins) {
194
211
  return afterIndent(text, readLine(text, i + 1, endOfLine(text, i + 1)));
195
212
  }
196
213
  function closeEnd(text, start, open, joins) {
197
- const close = open === '(' ? ')' : open;
214
+ const close = (open === '(' ? ')' : open).charCodeAt(0);
198
215
  let i = start;
199
216
  while (i < text.length) {
200
- const ch = text[i];
217
+ const code = text.charCodeAt(i);
201
218
  if (escapes(text, i)) {
202
219
  i += 2;
203
220
  continue;
204
221
  }
205
- if (ch === close)
222
+ if (code === close)
206
223
  return i + 1;
207
- if (open === '(' && ch === '(')
224
+ if (open === '(' && code === OPEN_PAREN)
208
225
  return null;
209
- if (ch === '\r' && text[i + 1] !== '\n')
226
+ if (code === CARRIAGE_RETURN && text.charCodeAt(i + 1) !== LINE_FEED)
210
227
  return null;
211
- if (ch === '\n' && !joins.has(i))
228
+ if (code === LINE_FEED && !joins.has(i))
212
229
  return null;
213
230
  i += 1;
214
231
  }
@@ -504,24 +521,24 @@ function structure(text) {
504
521
  }
505
522
  return { joins, raw };
506
523
  }
507
- function escapes(text, index) {
508
- return text[index] === '\\' && PUNCTUATION.test(text[index + 1] ?? '');
524
+ export function escapes(text, index) {
525
+ return text.charCodeAt(index) === BACKSLASH && PUNCTUATION.test(text[index + 1] ?? '');
509
526
  }
510
527
  function referenceEnd(text, start, joins) {
511
528
  let i = start;
512
529
  while (i < text.length) {
513
- const ch = text[i];
530
+ const code = text.charCodeAt(i);
514
531
  if (escapes(text, i)) {
515
532
  i += 2;
516
533
  continue;
517
534
  }
518
- if (ch === ']')
535
+ if (code === CLOSE_BRACKET)
519
536
  return i + 1;
520
- if (ch === '[')
537
+ if (code === OPEN_BRACKET)
521
538
  return -1;
522
- if (ch === '\r' && text[i + 1] !== '\n')
539
+ if (code === CARRIAGE_RETURN && text.charCodeAt(i + 1) !== LINE_FEED)
523
540
  return -1;
524
- if (ch === '\n' && !joins.has(i))
541
+ if (code === LINE_FEED && !joins.has(i))
525
542
  return -1;
526
543
  i += 1;
527
544
  }
@@ -641,12 +658,13 @@ function endsBlock(content) {
641
658
  }
642
659
  function scanBlocks(text) {
643
660
  const { joins, raw } = structure(text);
661
+ const rawEnd = spanCursor(raw);
644
662
  const definitions = new Map();
645
663
  const blocks = [];
646
664
  let fresh = 0;
647
665
  let i = 0;
648
666
  while (i < text.length) {
649
- const skip = spanEndAt(raw, i);
667
+ const skip = rawEnd(i);
650
668
  if (skip !== undefined) {
651
669
  i = skip;
652
670
  continue;
@@ -667,12 +685,14 @@ function scanBlocks(text) {
667
685
  }
668
686
  return { raw, definitions, blocks, joins };
669
687
  }
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;
688
+ export function spanCursor(spans) {
689
+ let next = 0;
690
+ return (index) => {
691
+ while (next < spans.length && spans[next][1] <= index)
692
+ next += 1;
693
+ const span = spans[next];
694
+ return span !== undefined && span[0] <= index ? span[1] : undefined;
695
+ };
676
696
  }
677
697
  export function fenceSpans(text) {
678
698
  const fences = [];
@@ -693,61 +713,95 @@ export function fenceSpans(text) {
693
713
  function paragraphEnd(text, from, joins) {
694
714
  let i = from;
695
715
  while (i < text.length) {
696
- if (text[i] === '\n' && !joins.has(i))
716
+ const code = text.charCodeAt(i);
717
+ if (code === LINE_FEED && !joins.has(i))
697
718
  return i;
698
- if (text[i] === '\r' && text[i + 1] !== '\n')
719
+ if (code === CARRIAGE_RETURN && text.charCodeAt(i + 1) !== LINE_FEED)
699
720
  return i;
700
721
  i += 1;
701
722
  }
702
723
  return i;
703
724
  }
725
+ function closerAt(text, closer, from, closers) {
726
+ const held = closers.get(closer);
727
+ if (held !== undefined && from >= held[0] && (held[1] === -1 || from <= held[1]))
728
+ return held[1];
729
+ const at = text.indexOf(closer, from);
730
+ closers.set(closer, [from, at]);
731
+ return at;
732
+ }
733
+ function closedAfter(text, closer, from, closers) {
734
+ const at = closerAt(text, closer, from, closers);
735
+ return at === -1 ? -1 : at + closer.length;
736
+ }
737
+ function stuck(pattern, text, at) {
738
+ pattern.lastIndex = at;
739
+ return pattern.test(text) ? pattern.lastIndex : -1;
740
+ }
741
+ function markupEnd(text, at, closers) {
742
+ if (text.startsWith(INSTRUCTION, at))
743
+ return closedAfter(text, '?>', at + INSTRUCTION.length, closers);
744
+ if (text.startsWith(CDATA, at))
745
+ return closedAfter(text, ']]>', at + CDATA.length, closers);
746
+ const declared = stuck(DECLARATION, text, at);
747
+ if (declared !== -1)
748
+ return closedAfter(text, '>', declared, closers);
749
+ return stuck(INLINE_TAG, text, at);
750
+ }
751
+ function inlineRawEnd(text, at, closers) {
752
+ const end = markupEnd(text, at, closers);
753
+ return end === -1 ? stuck(AUTOLINK, text, at) : end;
754
+ }
704
755
  export function linkInventory(body) {
705
756
  const { raw, definitions, blocks, joins } = scanBlocks(body);
706
- const skipped = [...raw, ...blocks.map((block) => [block.start, block.end])];
757
+ const rawEnd = spanCursor(raw);
758
+ const blockEnd = spanCursor(blocks.map((block) => [block.start, block.end]));
707
759
  const uses = [];
708
760
  const openers = [];
709
- let paragraph = { start: 0, end: -1, text: '' };
761
+ const unclosed = { stop: 0, runs: new Map() };
762
+ let paragraph = { start: 0, end: -1, text: '', closers: new Map() };
710
763
  let i = 0;
711
764
  while (i < body.length) {
712
- const skipEnd = spanEndAt(skipped, i);
765
+ const skipEnd = rawEnd(i) ?? blockEnd(i);
713
766
  if (skipEnd !== undefined) {
714
767
  openers.length = 0;
715
768
  i = skipEnd;
716
769
  continue;
717
770
  }
718
- const ch = body[i];
771
+ const code = body.charCodeAt(i);
719
772
  if (escapes(body, i)) {
720
773
  i += 2;
721
774
  continue;
722
775
  }
723
- if (ch === '`') {
724
- i = skipInlineCode(body, i, joins);
776
+ if (code === BACKTICK) {
777
+ i = skipInlineCode(body, i, joins, unclosed);
725
778
  continue;
726
779
  }
727
- if (ch === '<') {
780
+ if (code === LESS_THAN) {
728
781
  if (i >= paragraph.end) {
729
782
  const end = paragraphEnd(body, i, joins);
730
- paragraph = { start: i, end, text: body.slice(i, end) };
783
+ paragraph = { start: i, end, text: body.slice(i, end), closers: new Map() };
731
784
  }
732
- INLINE_RAW.lastIndex = i - paragraph.start;
733
- const raw = INLINE_RAW.exec(paragraph.text);
734
- if (raw !== null) {
735
- i += raw[0].length;
785
+ const raw = inlineRawEnd(paragraph.text, i - paragraph.start, paragraph.closers);
786
+ if (raw !== -1) {
787
+ i = paragraph.start + raw;
736
788
  continue;
737
789
  }
738
790
  }
739
- if ((ch === '\n' && !joins.has(i)) || (ch === '\r' && body[i + 1] !== '\n'))
791
+ if ((code === LINE_FEED && !joins.has(i)) ||
792
+ (code === CARRIAGE_RETURN && body.charCodeAt(i + 1) !== LINE_FEED))
740
793
  openers.length = 0;
741
- if (ch === '[' || (ch === '!' && body[i + 1] === '[')) {
794
+ if (code === OPEN_BRACKET ||
795
+ (code === EXCLAMATION && body.charCodeAt(i + 1) === OPEN_BRACKET)) {
742
796
  const previous = openers.at(-1);
743
797
  if (previous !== undefined)
744
798
  previous.bracketAfter = true;
745
- const image = ch === '!';
799
+ const image = code === EXCLAMATION;
746
800
  openers.push({ at: image ? i + 1 : i, start: i, image, active: true, bracketAfter: false });
747
801
  i += image ? 2 : 1;
748
802
  continue;
749
803
  }
750
- if (ch === ']') {
804
+ if (code === CLOSE_BRACKET) {
751
805
  const opener = openers.pop();
752
806
  const use = opener?.active ? closeLink(body, opener, i, definitions, joins) : null;
753
807
  if (opener === undefined || use === null) {
@@ -766,9 +820,12 @@ export function linkInventory(body) {
766
820
  }
767
821
  return { uses, blocks };
768
822
  }
769
- export function hrefOccurrences(body) {
823
+ export function destinations(inventory) {
770
824
  const byStart = new Map();
771
- for (const use of linkInventory(body).uses)
825
+ for (const use of inventory.uses)
772
826
  byStart.set(use.destination.start, use.destination);
773
827
  return [...byStart.values()].sort((left, right) => left.start - right.start);
774
828
  }
829
+ export function hrefOccurrences(body) {
830
+ return destinations(linkInventory(body));
831
+ }
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;AAwDD,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,132 @@ 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 {
65
+ base: suffix === written.length ? url.replace(/(?<![\u0000-\u0020])[\u0000-\u0020]+$/, '') : url,
66
+ suffix,
67
+ };
68
+ }
69
+ const ENCODED = /(?:%[0-9a-fA-F]{2})+/g;
70
+ function decodedRun(run) {
71
+ try {
72
+ return decodeURIComponent(run);
73
+ }
74
+ catch {
75
+ return null;
76
+ }
77
+ }
78
+ export function destinationPath(href) {
79
+ const base = located(href).base;
80
+ let path = '';
81
+ let cursor = 0;
82
+ for (const match of base.matchAll(ENCODED)) {
83
+ const decoded = decodedRun(match[0]);
84
+ if (decoded === null)
85
+ return null;
86
+ path += base.slice(cursor, match.index) + decoded;
87
+ cursor = match.index + match[0].length;
88
+ }
89
+ return path + base.slice(cursor);
90
+ }
91
+ function written(path) {
92
+ let text = '';
93
+ for (let i = 0; i < path.length; i += 1) {
94
+ const char = path[i];
95
+ if (char === '?' || char === '#' || char < ' ')
96
+ text += encodeURIComponent(char);
97
+ else if (char === ' ' && (i === 0 || i === path.length - 1))
98
+ text += '%20';
99
+ else if (char === '%' && PERCENT.test(path.slice(i)))
100
+ text += '%25';
101
+ else if (escapes(path, i))
102
+ text += '%5C';
103
+ else if (char === '&' && referenceAt(path, i) !== null)
104
+ text += '%26';
105
+ else
106
+ text += char;
107
+ }
108
+ return text;
109
+ }
110
+ function roomTargetOf(path) {
111
+ if (!path.startsWith(SHELL_ROOMS))
112
+ return null;
113
+ const rest = path.slice(SHELL_ROOMS.length);
114
+ const slash = rest.indexOf('/');
115
+ const handle = rest.slice(0, slash);
116
+ if (slash <= 0 || handle === '.' || handle === '..')
117
+ return null;
118
+ const within = normalize(rest.slice(slash + 1));
119
+ return within === null || within === '' ? null : { handle, path: within };
120
+ }
25
121
  export function classifyHref(from, href) {
26
- const trimmed = href.trim();
27
- if (trimmed === '' || trimmed.startsWith('#'))
122
+ const base = located(href).base;
123
+ if (base === '')
28
124
  return { kind: 'skip' };
29
- if (/^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(trimmed))
125
+ if (/^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(base))
30
126
  return { kind: 'skip' };
31
- if (trimmed.startsWith('/'))
127
+ const path = destinationPath(href);
128
+ if (path === null)
32
129
  return { kind: 'invalid' };
33
- const bare = trimmed.replace(/[?#].*$/, '');
34
- if (bare === '')
35
- return { kind: 'skip' };
36
- const resolved = normalize(join(dirname(from), bare));
130
+ if (path.startsWith('/')) {
131
+ const room = roomTargetOf(path);
132
+ return room === null ? { kind: 'invalid' } : { kind: 'room', ...room };
133
+ }
134
+ const resolved = normalize(join(dirname(from), path));
37
135
  if (resolved === null || resolved === '')
38
136
  return { kind: 'invalid' };
39
137
  return { kind: 'path', path: resolved };
40
138
  }
139
+ export function roomTarget(href) {
140
+ const path = destinationPath(href);
141
+ return path === null ? null : roomTargetOf(path);
142
+ }
143
+ export function roomPath(href) {
144
+ const room = roomTarget(href);
145
+ return room === null ? null : `${SHELL_ROOMS}${room.handle}/${room.path}`;
146
+ }
41
147
  export function target(from, href) {
42
148
  const resolved = classifyHref(from, href);
43
149
  return resolved.kind === 'path' ? resolved.path : null;
44
150
  }
45
151
  export function retarget(href, to) {
46
152
  const trimmed = href.trim();
47
- const match = /[?#]/.exec(trimmed);
48
- return match === null ? to : to + trimmed.slice(match.index);
153
+ return written(to) + trimmed.slice(unescaped(trimmed).suffix);
49
154
  }
50
155
  export function relative(from, target) {
51
156
  const base = dirname(from)
@@ -62,8 +167,9 @@ export function relative(from, target) {
62
167
  export const SHELL_ROOMS = '/rooms/';
63
168
  export function shellTarget(href, handle) {
64
169
  const prefix = `${SHELL_ROOMS}${handle}/`;
65
- if (!href.startsWith(prefix))
170
+ const decoded = destinationPath(href);
171
+ if (decoded === null || !decoded.startsWith(prefix))
66
172
  return null;
67
- const path = normalize(href.slice(prefix.length).replace(/[?#].*$/s, ''));
173
+ const path = normalize(decoded.slice(prefix.length));
68
174
  return path === null || path === '' ? null : path;
69
175
  }
@@ -0,0 +1,10 @@
1
+ export declare const PATTERN_STATES = 1000;
2
+ export declare class PatternTooCostly extends Error {
3
+ }
4
+ export type Pattern = {
5
+ readonly test: (text: string) => boolean;
6
+ };
7
+ export declare function linearPattern(source: string): Pattern | null;
8
+ export declare function withLinearRegExp<T>(run: () => T): T;
9
+ export declare function openPatternBudget(): () => void;
10
+ //# sourceMappingURL=pattern.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pattern.d.ts","sourceRoot":"","sources":["../src/pattern.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,cAAc,OAAQ,CAAC;AAMpC,qBAAa,gBAAiB,SAAQ,KAAK;CAAG;AAQ9C,MAAM,MAAM,OAAO,GAAG;IAAE,QAAQ,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAA;CAAE,CAAC;AAiUnE,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,GAAG,IAAI,CAM5D;AAED,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,CAAC,GAAG,CAAC,CAkBnD;AAED,wBAAgB,iBAAiB,IAAI,MAAM,IAAI,CAM9C"}