@descent-vtt/spec-graph 0.2.0

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 (70) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +627 -0
  3. package/bin/spec-graph.js +24 -0
  4. package/dist/baseline.d.ts +93 -0
  5. package/dist/baseline.js +187 -0
  6. package/dist/baseline.js.map +1 -0
  7. package/dist/cli.d.ts +66 -0
  8. package/dist/cli.js +565 -0
  9. package/dist/cli.js.map +1 -0
  10. package/dist/config.d.ts +66 -0
  11. package/dist/config.js +183 -0
  12. package/dist/config.js.map +1 -0
  13. package/dist/directives.d.ts +56 -0
  14. package/dist/directives.js +125 -0
  15. package/dist/directives.js.map +1 -0
  16. package/dist/extract.d.ts +116 -0
  17. package/dist/extract.js +1043 -0
  18. package/dist/extract.js.map +1 -0
  19. package/dist/glob.d.ts +73 -0
  20. package/dist/glob.js +284 -0
  21. package/dist/glob.js.map +1 -0
  22. package/dist/graph.d.ts +55 -0
  23. package/dist/graph.js +201 -0
  24. package/dist/graph.js.map +1 -0
  25. package/dist/identity.d.ts +87 -0
  26. package/dist/identity.js +294 -0
  27. package/dist/identity.js.map +1 -0
  28. package/dist/index.d.ts +63 -0
  29. package/dist/index.js +45 -0
  30. package/dist/index.js.map +1 -0
  31. package/dist/lifecycle.d.ts +57 -0
  32. package/dist/lifecycle.js +258 -0
  33. package/dist/lifecycle.js.map +1 -0
  34. package/dist/markdown.d.ts +152 -0
  35. package/dist/markdown.js +820 -0
  36. package/dist/markdown.js.map +1 -0
  37. package/dist/paths.d.ts +22 -0
  38. package/dist/paths.js +58 -0
  39. package/dist/paths.js.map +1 -0
  40. package/dist/report.d.ts +89 -0
  41. package/dist/report.js +326 -0
  42. package/dist/report.js.map +1 -0
  43. package/dist/resolve.d.ts +61 -0
  44. package/dist/resolve.js +340 -0
  45. package/dist/resolve.js.map +1 -0
  46. package/dist/rules.d.ts +61 -0
  47. package/dist/rules.js +557 -0
  48. package/dist/rules.js.map +1 -0
  49. package/dist/runner.d.ts +108 -0
  50. package/dist/runner.js +186 -0
  51. package/dist/runner.js.map +1 -0
  52. package/dist/sections.d.ts +94 -0
  53. package/dist/sections.js +372 -0
  54. package/dist/sections.js.map +1 -0
  55. package/dist/select.d.ts +96 -0
  56. package/dist/select.js +413 -0
  57. package/dist/select.js.map +1 -0
  58. package/dist/source.d.ts +43 -0
  59. package/dist/source.js +109 -0
  60. package/dist/source.js.map +1 -0
  61. package/dist/state.d.ts +45 -0
  62. package/dist/state.js +330 -0
  63. package/dist/state.js.map +1 -0
  64. package/dist/types.d.ts +294 -0
  65. package/dist/types.js +52 -0
  66. package/dist/types.js.map +1 -0
  67. package/dist/yaml.d.ts +37 -0
  68. package/dist/yaml.js +240 -0
  69. package/dist/yaml.js.map +1 -0
  70. package/package.json +71 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"identity.js","sourceRoot":"","sources":["../src/identity.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,sFAAsF;AACtF,MAAM,kBAAkB,GAAqC;IAC3D,GAAG,EAAE,KAAK;IACV,IAAI,EAAE,KAAK;IACX,wBAAwB,EAAE,KAAK;IAC/B,kBAAkB,EAAE,KAAK;IACzB,SAAS,EAAE,KAAK;IAChB,GAAG,EAAE,KAAK;IACV,IAAI,EAAE,KAAK;IACX,GAAG,EAAE,KAAK;IACV,IAAI,EAAE,KAAK;IACX,YAAY,EAAE,KAAK;IACnB,QAAQ,EAAE,UAAU;IACpB,SAAS,EAAE,UAAU;IACrB,MAAM,EAAE,QAAQ;IAChB,OAAO,EAAE,QAAQ;IACjB,GAAG,EAAE,KAAK;IACV,IAAI,EAAE,KAAK;IACX,IAAI,EAAE,MAAM;IACZ,KAAK,EAAE,MAAM;CACd,CAAC;AAEF,oFAAoF;AACpF,MAAM,CAAC,MAAM,OAAO,GAAsB,CAAC,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC;AAEnH,+EAA+E;AAC/E,MAAM,WAAW,GAAG,kDAAkD,CAAC;AACvE,6DAA6D;AAC7D,MAAM,WAAW,GAAG,eAAe,CAAC;AAgCpC;;;;;;GAMG;AACH,MAAM,UAAU,QAAQ,CAAC,KAAoB;IAC3C,MAAM,IAAI,GAAG,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAClC,MAAM,eAAe,GAAG,cAAc,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACnD,MAAM,OAAO,GAAG,IAAI,GAAG,EAAU,CAAC;IAElC,MAAM,GAAG,GAAG,CAAC,KAAgC,EAAQ,EAAE;QACrD,IAAI,CAAC,KAAK;YAAE,OAAO;QACnB,MAAM,GAAG,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC;QAChC,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC;YAAE,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IACvC,CAAC,CAAC;IAEF,MAAM,UAAU,GAAsE,EAAE,CAAC;IAEzF,MAAM,QAAQ,GAAG,CAAC,GAAkB,EAAE,SAAkB,EAAQ,EAAE;QAChE,IAAI,CAAC,GAAG;YAAE,OAAO;QACjB,MAAM,OAAO,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC;QAC3B,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO;QACjC,MAAM,QAAQ,GAAG,WAAW,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAC3C,IAAI,QAAQ,EAAE,CAAC;YACb,UAAU,CAAC,IAAI,CAAC;gBACd,MAAM,EAAG,QAAQ,CAAC,CAAC,CAAY,CAAC,WAAW,EAAE;gBAC7C,MAAM,EAAE,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAW,EAAE,EAAE,CAAC;gBAClD,KAAK,EAAE,OAAO;aACf,CAAC,CAAC;YACH,OAAO;QACT,CAAC;QACD,MAAM,IAAI,GAAG,WAAW,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACvC,IAAI,IAAI,IAAI,SAAS,EAAE,CAAC;YACtB,UAAU,CAAC,IAAI,CAAC;gBACd,MAAM,EAAE,eAAe;gBACvB,MAAM,EAAE,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAW,EAAE,EAAE,CAAC;gBAC9C,KAAK,EAAE,OAAO;aACf,CAAC,CAAC;YACH,OAAO;QACT,CAAC;QACD,UAAU,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC;IAClE,CAAC,CAAC;IAEF,QAAQ,CAAC,KAAK,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC;IACjC,8EAA8E;IAC9E,iEAAiE;IACjE,QAAQ,CAAC,YAAY,CAAC,IAAI,CAAC,EAAE,IAAI,CAAC,CAAC;IACnC,QAAQ,CAAC,SAAS,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,KAAK,CAAC,CAAC;IAE1C,MAAM,QAAQ,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,IAAI,CAAC,CAAC;IAC3D,MAAM,MAAM,GAAG,QAAQ,EAAE,MAAM,IAAI,eAAe,CAAC;IACnD,MAAM,MAAM,GAAG,QAAQ,EAAE,MAAM,IAAI,IAAI,CAAC;IAExC,4EAA4E;IAC5E,8EAA8E;IAC9E,oEAAoE;IACpE,MAAM,SAAS,GAAG,CAAC,QAAQ,EAAE,KAAK,EAAE,KAAK,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC;IAE5D,IAAI,EAAU,CAAC;IACf,IAAI,MAAM,KAAK,IAAI,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;QACvC,EAAE,GAAG,GAAG,MAAM,IAAI,SAAS,CAAC,MAAM,EAAE,SAAS,CAAC,EAAE,CAAC;IACnD,CAAC;SAAM,IAAI,KAAK,CAAC,UAAU,EAAE,CAAC;QAC5B,EAAE,GAAG,KAAK,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC;IAC/B,CAAC;SAAM,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;QAC3B,4EAA4E;QAC5E,0EAA0E;QAC1E,mEAAmE;QACnE,EAAE,GAAG,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IAC/C,CAAC;SAAM,CAAC;QACN,EAAE,GAAG,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC;IAC3C,CAAC;IAED,GAAG,CAAC,EAAE,CAAC,CAAC;IACR,IAAI,KAAK,CAAC,kBAAkB,KAAK,KAAK,EAAE,CAAC;QACvC,GAAG,CAAC,IAAI,CAAC,CAAC;QACV,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAChB,GAAG,CAAC,cAAc,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;IAClC,CAAC;IACD,GAAG,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;IACtB,KAAK,MAAM,KAAK,IAAI,KAAK,CAAC,eAAe;QAAE,GAAG,CAAC,KAAK,CAAC,CAAC;IACtD,IAAI,KAAK,CAAC,OAAO;QAAE,GAAG,CAAC,SAAS,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC;IAEjD,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;QACpB,MAAM,SAAS,GAAG,eAAe,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;QACrD,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;YACpB,yEAAyE;YACzE,2EAA2E;YAC3E,mCAAmC;YACnC,KAAK,MAAM,MAAM,IAAI,SAAS;gBAAE,GAAG,CAAC,GAAG,MAAM,IAAI,MAAM,EAAE,CAAC,CAAC;QAC7D,CAAC;aAAM,CAAC;YACN,wEAAwE;YACxE,kDAAkD;YAClD,KAAK,MAAM,MAAM,IAAI,SAAS;gBAAE,GAAG,CAAC,MAAM,CAAC,CAAC;QAC9C,CAAC;IACH,CAAC;IAED,OAAO,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,GAAG,OAAO,CAAC,EAAE,CAAC;AACvD,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,YAAY,CAAC,KAAa;IACxC,OAAO,KAAK;SACT,IAAI,EAAE;SACN,WAAW,EAAE;SACb,OAAO,CAAC,wBAAwB,EAAE,EAAE,CAAC;SACrC,OAAO,CAAC,WAAW,EAAE,EAAE,CAAC;SACxB,IAAI,EAAE,CAAC;AACZ,CAAC;AAED,0CAA0C;AAC1C,MAAM,UAAU,WAAW,CAAC,MAAc;IACxC,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IACjC,IAAI,IAAI,KAAK,CAAC,CAAC;QAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;IACjD,2DAA2D;IAC3D,IAAI,IAAI,KAAK,CAAC;QAAE,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;IAC/D,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,KAAK,CAAC,IAAI,GAAG,CAAC,CAAC,EAAE,CAAC;AAC3E,CAAC;AAED,uEAAuE;AACvE,MAAM,UAAU,gBAAgB,CAAC,KAAa;IAC5C,MAAM,KAAK,GAAG,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC,CAAC;IACpE,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAC;IACxB,OAAO,EAAE,MAAM,EAAG,KAAK,CAAC,CAAC,CAAY,CAAC,WAAW,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAW,EAAE,EAAE,CAAC,EAAE,CAAC;AACzG,CAAC;AAED,uDAAuD;AACvD,MAAM,UAAU,YAAY,CAAC,KAAa;IACxC,MAAM,KAAK,GAAG,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;IAC7C,OAAO,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAW,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AAChE,CAAC;AAED,+EAA+E;AAC/E,MAAM,UAAU,aAAa,CAAC,MAAc;IAC1C,OAAO,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,kBAAkB,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;AACjE,CAAC;AAED,MAAM,kBAAkB,GAAG,6CAA6C,CAAC;AAEzE;;;;;;;GAOG;AACH,MAAM,UAAU,gBAAgB,CAAC,MAAc;IAC7C,MAAM,aAAa,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC;IACjD,MAAM,IAAI,GAAG,aAAa,CAAC,KAAK,CAAC,aAAa,CAAC,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;IACrE,MAAM,GAAG,GAAG,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;IAClC,2EAA2E;IAC3E,IAAI,GAAG,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IAC1B,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAClC,4EAA4E;IAC5E,uEAAuE;IACvE,IAAI,CAAC,8BAA8B,CAAC,IAAI,CAAC,SAAS,CAAC;QAAE,OAAO,IAAI,CAAC;IACjE,OAAO,kBAAkB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACvC,CAAC;AAED,kEAAkE;AAClE,MAAM,UAAU,UAAU,CAAC,MAAc;IACvC,OAAO,sBAAsB,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;AAC9E,CAAC;AAED,mEAAmE;AACnE,MAAM,UAAU,cAAc,CAAC,SAAiB;IAC9C,MAAM,QAAQ,GAAG,SAAS,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACpD,uEAAuE;IACvE,KAAK,IAAI,CAAC,GAAG,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;QACjD,MAAM,KAAK,GAAG,kBAAkB,CAAC,QAAQ,CAAC,CAAC,CAAW,CAAC,CAAC;QACxD,IAAI,KAAK;YAAE,OAAO,KAAK,CAAC;IAC1B,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,gFAAgF;AAChF,gFAAgF;AAChF,gFAAgF;AAEhF,SAAS,QAAQ,CAAC,SAAiB;IACjC,MAAM,IAAI,GAAG,SAAS,CAAC,KAAK,CAAC,SAAS,CAAC,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;IAC7D,MAAM,GAAG,GAAG,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;IAClC,MAAM,IAAI,GAAG,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IACjD,4EAA4E;IAC5E,6EAA6E;IAC7E,qBAAqB;IACrB,MAAM,KAAK,GAAG,SAAS,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;IACzC,IAAI,KAAK,GAAG,CAAC,IAAI,mBAAmB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QAChD,MAAM,MAAM,GAAG,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;QACzC,OAAO,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;IACnD,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,SAAS,cAAc,CAAC,SAAiB;IACvC,MAAM,GAAG,GAAG,SAAS,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;IACvC,MAAM,KAAK,GAAG,SAAS,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;IACzC,OAAO,GAAG,GAAG,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;AAC3D,CAAC;AAED,yFAAyF;AACzF,SAAS,YAAY,CAAC,IAAY;IAChC,MAAM,QAAQ,GAAG,+CAA+C,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC5E,IAAI,QAAQ;QAAE,OAAO,QAAQ,CAAC,CAAC,CAAW,CAAC;IAC3C,MAAM,IAAI,GAAG,yBAAyB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAClD,IAAI,IAAI;QAAE,OAAO,IAAI,CAAC,CAAC,CAAW,CAAC;IACnC,OAAO,IAAI,CAAC;AACd,CAAC;AAED,2DAA2D;AAC3D,SAAS,SAAS,CAAC,OAAsB;IACvC,IAAI,CAAC,OAAO;QAAE,OAAO,IAAI,CAAC;IAC1B,MAAM,KAAK,GAAG,uCAAuC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACpE,OAAO,KAAK,CAAC,CAAC,CAAE,KAAK,CAAC,CAAC,CAAY,CAAC,CAAC,CAAC,IAAI,CAAC;AAC7C,CAAC;AAED;;;;;;GAMG;AACH,SAAS,SAAS,CAAC,MAAc,EAAE,SAAiD;IAClF,MAAM,KAAK,GAAG,YAAY,CAAC,MAAM,EAAE,SAAS,CAAC,IAAI,MAAM,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC;IACvE,OAAO,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;AAC7C,CAAC;AAED,6EAA6E;AAC7E,SAAS,YAAY,CAAC,MAAc,EAAE,SAAiD;IACrF,KAAK,MAAM,OAAO,IAAI,SAAS,EAAE,CAAC;QAChC,IAAI,CAAC,OAAO;YAAE,SAAS;QACvB,MAAM,MAAM,GAAG,WAAW,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QAC9C,IAAI,MAAM,IAAI,MAAM,CAAC,QAAQ,CAAC,MAAM,EAAE,EAAE,CAAC,KAAK,MAAM;YAAE,OAAO,MAAM,CAAC,MAAM,CAAC;IAC7E,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,6DAA6D;AAC7D,SAAS,eAAe,CAAC,MAAc,EAAE,SAAiD;IACxF,MAAM,GAAG,GAAG,IAAI,GAAG,CAAS,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;IAC9C,MAAM,QAAQ,GAAG,YAAY,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;IACjD,IAAI,QAAQ,KAAK,IAAI;QAAE,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC,CAAC;IACvE,KAAK,MAAM,KAAK,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC;QAAE,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,CAAC;IACzE,OAAO,CAAC,GAAG,GAAG,CAAC,CAAC;AAClB,CAAC","sourcesContent":["/**\n * Document identity and reference normalisation.\n *\n * Foreign-key validation across a corpus of Markdown only works if `ADR-7`,\n * `ADR-0007`, `adr 7`, `[[0007-sharding]]` and `../adr/0007-sharding.md` are\n * understood to name the same document. Getting that wrong in either direction\n * is fatal: too strict and every real repository drowns in false \"broken\n * reference\" findings, too loose and `RFC 7` silently resolves to `ADR-7`.\n *\n * The resolution here is deliberately asymmetric. **Textual aliases** (`adr-7`)\n * are global, because a prefix already names the family. **Bare numbers** (`7`,\n * `#0007`) are resolved only within the citing document's own family, because a\n * repository with both `adr/0007` and `rfc/0007` is normal and guessing between\n * them would be worse than reporting nothing.\n */\n\n/** Directory names that name a specification family, mapped to a canonical prefix. */\nconst FAMILY_DIRECTORIES: Readonly<Record<string, string>> = {\n adr: 'ADR',\n adrs: 'ADR',\n 'architecture-decisions': 'ADR',\n 'decision-records': 'ADR',\n decisions: 'ADR',\n rfc: 'RFC',\n rfcs: 'RFC',\n kep: 'KEP',\n keps: 'KEP',\n enhancements: 'KEP',\n proposal: 'PROPOSAL',\n proposals: 'PROPOSAL',\n design: 'DESIGN',\n designs: 'DESIGN',\n prd: 'PRD',\n prds: 'PRD',\n spec: 'SPEC',\n specs: 'SPEC',\n};\n\n/** Front-matter keys that may carry an explicit identifier, in preference order. */\nexport const ID_KEYS: readonly string[] = ['id', 'adr', 'adr-id', 'rfc', 'rfc-id', 'kep-number', 'number', 'slug'];\n\n/** `ADR-0007`, `KEP 1234`, `rfc2119`: a family prefix followed by a number. */\nconst PREFIXED_ID = /^([A-Za-z][A-Za-z0-9_]{0,15}?)[\\s._-]*(\\d{1,6})$/;\n/** A bare number, optionally hash-prefixed: `7`, `#0007`. */\nconst BARE_NUMBER = /^#?(\\d{1,6})$/;\n\nexport interface DocumentIdentity {\n /** The canonical id: `ADR-0007`, or a path when nothing better exists. */\n readonly id: string;\n /** Family prefix, upper-cased. `null` when the document is not numbered. */\n readonly family: string | null;\n /** Sequence number without leading zeros. `null` when not numbered. */\n readonly number: number | null;\n /** Every lower-cased spelling that should resolve to this document. */\n readonly aliases: readonly string[];\n}\n\nexport interface IdentityInput {\n /** Repository-relative POSIX path. */\n readonly path: string;\n /** An id declared by a directive or front matter, if any. */\n readonly declaredId: string | null;\n /** Extra aliases declared by a directive or front matter. */\n readonly declaredAliases: readonly string[];\n /** The document H1, if any. */\n readonly heading: string | null;\n /**\n * Register the file path and stem as aliases. Default true.\n *\n * A specification that is a *region* of a file must not claim the file's\n * path: the file already answers to it, and two nodes answering to one path\n * would make every link to that file ambiguous.\n */\n readonly includePathAliases?: boolean | undefined;\n}\n\n/**\n * Derives a document's canonical id and every alias that should reach it.\n *\n * Evidence is used in order of how deliberate it is: an explicit declaration,\n * then the file name, then the H1. Whatever loses still contributes aliases, so\n * a reference written in any of those forms still resolves.\n */\nexport function identify(input: IdentityInput): DocumentIdentity {\n const stem = fileStem(input.path);\n const directoryFamily = familyFromPath(input.path);\n const aliases = new Set<string>();\n\n const add = (value: string | null | undefined): void => {\n if (!value) return;\n const key = normaliseRef(value);\n if (key.length > 0) aliases.add(key);\n };\n\n const candidates: { family: string | null; number: number | null; label: string }[] = [];\n\n const consider = (raw: string | null, allowBare: boolean): void => {\n if (!raw) return;\n const cleaned = raw.trim();\n if (cleaned.length === 0) return;\n const prefixed = PREFIXED_ID.exec(cleaned);\n if (prefixed) {\n candidates.push({\n family: (prefixed[1] as string).toUpperCase(),\n number: Number.parseInt(prefixed[2] as string, 10),\n label: cleaned,\n });\n return;\n }\n const bare = BARE_NUMBER.exec(cleaned);\n if (bare && allowBare) {\n candidates.push({\n family: directoryFamily,\n number: Number.parseInt(bare[1] as string, 10),\n label: cleaned,\n });\n return;\n }\n candidates.push({ family: null, number: null, label: cleaned });\n };\n\n consider(input.declaredId, true);\n // `0007-sharding-the-write-path.md` and `kep-1234-foo.md` both start with the\n // identifier; take the leading token rather than the whole stem.\n consider(leadingToken(stem), true);\n consider(headingId(input.heading), false);\n\n const numbered = candidates.find((c) => c.number !== null);\n const family = numbered?.family ?? directoryFamily;\n const number = numbered?.number ?? null;\n\n // Witnesses to the local zero-padding convention, most authoritative first.\n // The spelling that actually produced the number comes first: an id read from\n // the H1 of `sharding.md` must still render as ADR-0007, not ADR-7.\n const witnesses = [numbered?.label, input.declaredId, stem];\n\n let id: string;\n if (number !== null && family !== null) {\n id = `${family}-${padNumber(number, witnesses)}`;\n } else if (input.declaredId) {\n id = input.declaredId.trim();\n } else if (number !== null) {\n // Numbered but with no family to prefix - a Rust RFC in `text/0001-foo.md`,\n // say. The file stem is a far better identity than a bare \"1\": it is what\n // people actually type, and it stays unique across the repository.\n id = stem.length > 0 ? stem : String(number);\n } else {\n id = stem.length > 0 ? stem : input.path;\n }\n\n add(id);\n if (input.includePathAliases !== false) {\n add(stem);\n add(input.path);\n add(stripExtension(input.path));\n }\n add(input.declaredId);\n for (const alias of input.declaredAliases) add(alias);\n if (input.heading) add(headingId(input.heading));\n\n if (number !== null) {\n const spellings = numberSpellings(number, witnesses);\n if (family !== null) {\n // Every spelling a human might type. `normaliseRef` folds separators, so\n // `adr-7`, `adr 7` and `adr7` collapse to one key; the padding variants do\n // not, and each needs registering.\n for (const digits of spellings) add(`${family}-${digits}`);\n } else {\n // With no family there is only one number space in the repository, so a\n // bare `0001` is unambiguous and worth resolving.\n for (const digits of spellings) add(digits);\n }\n }\n\n return { id, family, number, aliases: [...aliases] };\n}\n\n/**\n * Folds a reference to its lookup key.\n *\n * Case, separators and surrounding punctuation carry no meaning in a citation,\n * so `ADR-0007`, `adr 0007` and `Adr_0007` all become `adr0007`.\n */\nexport function normaliseRef(value: string): string {\n return value\n .trim()\n .toLowerCase()\n .replace(/^[#<([]+|[)\\]>.,;:]+$/g, '')\n .replace(/[\\s._-]+/g, '')\n .trim();\n}\n\n/** Splits a `target#anchor` reference. */\nexport function splitAnchor(target: string): { target: string; anchor: string | null } {\n const hash = target.indexOf('#');\n if (hash === -1) return { target, anchor: null };\n // A leading `#` is an in-document anchor, not a separator.\n if (hash === 0) return { target: '', anchor: target.slice(1) };\n return { target: target.slice(0, hash), anchor: target.slice(hash + 1) };\n}\n\n/** Parses `ADR-0007` into its family and number, or returns `null`. */\nexport function parsePrefixedRef(value: string): { family: string; number: number } | null {\n const match = PREFIXED_ID.exec(value.trim().replace(/^[#([]+/, ''));\n if (!match) return null;\n return { family: (match[1] as string).toUpperCase(), number: Number.parseInt(match[2] as string, 10) };\n}\n\n/** Parses a bare `7` or `#0007`, or returns `null`. */\nexport function parseBareRef(value: string): number | null {\n const match = BARE_NUMBER.exec(value.trim());\n return match ? Number.parseInt(match[1] as string, 10) : null;\n}\n\n/** True when a reference target names a location rather than an identifier. */\nexport function looksLikePath(target: string): boolean {\n return target.includes('/') || DOCUMENT_EXTENSION.test(target);\n}\n\nconst DOCUMENT_EXTENSION = /\\.(md|markdown|mdx|txt|rst|adoc|asciidoc)$/i;\n\n/**\n * True when a path-like target could name a specification.\n *\n * A specification linking to `../../src/rules.ts` is citing source code, which\n * is a different tool's job. Treating it as a dangling specification reference\n * would report an error on one of the most ordinary things a design document\n * does, so targets carrying a non-document extension are not references at all.\n */\nexport function isDocumentTarget(target: string): boolean {\n const withoutAnchor = splitAnchor(target).target;\n const base = withoutAnchor.slice(withoutAnchor.lastIndexOf('/') + 1);\n const dot = base.lastIndexOf('.');\n // No extension: a directory, or an extensionless spec such as `rfcs/0007`.\n if (dot <= 0) return true;\n const extension = base.slice(dot);\n // Only a plausible file extension disqualifies a target. An identifier like\n // `v1.2` or `ADR-0007.1` keeps its dot and is still resolved as an id.\n if (!/^\\.[A-Za-z][A-Za-z0-9]{0,5}$/.test(extension)) return true;\n return DOCUMENT_EXTENSION.test(base);\n}\n\n/** True when a reference target points outside the repository. */\nexport function isExternal(target: string): boolean {\n return /^[a-z][a-z0-9+.-]*:/i.test(target) && !/^[a-z]:[\\\\/]/i.test(target);\n}\n\n/** The family prefix implied by a document's directory, if any. */\nexport function familyFromPath(posixPath: string): string | null {\n const segments = posixPath.toLowerCase().split('/');\n // Nearest enclosing directory wins: `docs/rfcs/adr/0007.md` is an ADR.\n for (let i = segments.length - 2; i >= 0; i -= 1) {\n const found = FAMILY_DIRECTORIES[segments[i] as string];\n if (found) return found;\n }\n return null;\n}\n\n/* -------------------------------------------------------------------------- */\n/* Internals */\n/* -------------------------------------------------------------------------- */\n\nfunction fileStem(posixPath: string): string {\n const base = posixPath.slice(posixPath.lastIndexOf('/') + 1);\n const dot = base.lastIndexOf('.');\n const stem = dot > 0 ? base.slice(0, dot) : base;\n // `docs/adr/0007-sharding/README.md` is identified by its directory - but a\n // README at the repository root has no directory to be named after, and must\n // keep its own name.\n const slash = posixPath.lastIndexOf('/');\n if (slash > 0 && /^(readme|index)$/i.test(stem)) {\n const parent = posixPath.slice(0, slash);\n return parent.slice(parent.lastIndexOf('/') + 1);\n }\n return stem;\n}\n\nfunction stripExtension(posixPath: string): string {\n const dot = posixPath.lastIndexOf('.');\n const slash = posixPath.lastIndexOf('/');\n return dot > slash ? posixPath.slice(0, dot) : posixPath;\n}\n\n/** `kep-1234-foo` -> `kep-1234`; `0007-sharding` -> `0007`; `sharding` -> `sharding`. */\nfunction leadingToken(stem: string): string {\n const prefixed = /^([A-Za-z]{1,15}[\\s._-]?\\d{1,6})(?:[\\s._-]|$)/.exec(stem);\n if (prefixed) return prefixed[1] as string;\n const bare = /^(\\d{1,6})(?:[\\s._-]|$)/.exec(stem);\n if (bare) return bare[1] as string;\n return stem;\n}\n\n/** `# ADR-0007: Sharding the write path` -> `ADR-0007`. */\nfunction headingId(heading: string | null): string | null {\n if (!heading) return null;\n const match = /^\\s*([A-Za-z]{1,15}[\\s._-]?\\d{1,6})\\b/.exec(heading);\n return match ? (match[1] as string) : null;\n}\n\n/**\n * Chooses the digit width of the canonical id.\n *\n * A repository that writes `0007` everywhere should see `ADR-0007` in reports,\n * not `ADR-7`. The file name is the most reliable witness of the local\n * convention, so it decides.\n */\nfunction padNumber(number: number, witnesses: readonly (string | null | undefined)[]): string {\n const width = paddingWidth(number, witnesses) ?? String(number).length;\n return String(number).padStart(width, '0');\n}\n\n/** The digit width of the first witness that actually spells this number. */\nfunction paddingWidth(number: number, witnesses: readonly (string | null | undefined)[]): number | null {\n for (const witness of witnesses) {\n if (!witness) continue;\n const digits = /(\\d{1,6})/.exec(witness)?.[1];\n if (digits && Number.parseInt(digits, 10) === number) return digits.length;\n }\n return null;\n}\n\n/** Zero-padded spellings a citation might reasonably use. */\nfunction numberSpellings(number: number, witnesses: readonly (string | null | undefined)[]): string[] {\n const out = new Set<string>([String(number)]);\n const observed = paddingWidth(number, witnesses);\n if (observed !== null) out.add(String(number).padStart(observed, '0'));\n for (const width of [3, 4]) out.add(String(number).padStart(width, '0'));\n return [...out];\n}\n"]}
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Programmatic API.
3
+ *
4
+ * The CLI is a shell over `analyse`; anything the CLI can do, a build script, a
5
+ * custom reporter or an editor extension can do too.
6
+ *
7
+ * ```ts
8
+ * import { analyse, formatReport } from '@descent-vtt/spec-graph';
9
+ *
10
+ * const result = await analyse({ root: process.cwd(), patterns: ['docs/**\/*.md'] });
11
+ * if (!result.ok) console.error(formatReport(result, { color: true }));
12
+ * ```
13
+ *
14
+ * For a corpus that is already in memory - a monorepo tool, a language server,
15
+ * a test - skip the filesystem entirely:
16
+ *
17
+ * ```ts
18
+ * import { analyseSources, query } from '@descent-vtt/spec-graph';
19
+ *
20
+ * const { graph } = analyseSources([{ path: 'adr/0001.md', text: '...' }]);
21
+ * const ghosts = query(graph, 'item[openness=open] -delegates-to-> document[phase=retired]');
22
+ * ```
23
+ */
24
+ export { applyBaseline, formatBaseline, fingerprintOf, parseBaseline, BASELINE_VERSION, EMPTY_BASELINE, } from './baseline.js';
25
+ export type { Baseline, BaselineEntry, BaselineOutcome, ParsedBaseline } from './baseline.js';
26
+ export { loadConfig, parseConfig, CONFIG_FILES, CONFIG_PACKAGE_KEY } from './config.js';
27
+ export type { LoadedConfig, SpecGraphConfig } from './config.js';
28
+ export { main, parseArgs, HELP, UsageError, EXIT_OK, EXIT_FAILED, EXIT_ERROR } from './cli.js';
29
+ export type { CliIO, CliOptions, Command } from './cli.js';
30
+ export { analyse, analyseSources, createFamilyFilter, createHistoryMatcher, withDiagnostics, DEFAULT_CONCURRENCY, DEFAULT_PATTERNS, } from './runner.js';
31
+ export type { AnalyseOptions, AnalyseSourcesOptions, AnalysisResult, Source } from './runner.js';
32
+ export { buildGraph, LOAD_BEARING_EDGES, OBLIGATION_EDGES } from './graph.js';
33
+ export type { SpecGraph } from './graph.js';
34
+ export { attributesOf, execute, matches, parseQuery, query, renderMatch, QueryError, DEFAULT_MATCH_LIMIT, } from './select.js';
35
+ export type { ExecuteOptions, Match, NodeMatcher, Operator, Predicate, QuerySpec, StepSpec, } from './select.js';
36
+ export { resolveStrict, runRules, sortDiagnostics, DEFAULT_SEVERITIES, RULE_DESCRIPTIONS, RULE_IDS, RULE_QUERIES, } from './rules.js';
37
+ export type { RuleOptions } from './rules.js';
38
+ export { createPainter, formatGraph, formatJson, formatReport, shouldUseAscii, shouldUseColor, } from './report.js';
39
+ export type { ColorEnvironment, GraphExportOptions, GraphFormat, Painter, ReporterOptions } from './report.js';
40
+ export { extractDocument, extractSpecifications, classifyReference, sectionPathAt } from './extract.js';
41
+ export { findSpecificationRegions, readRegionStatus, regionAt } from './sections.js';
42
+ export type { RegionStatus, SpecificationRegion } from './sections.js';
43
+ export type { Classification, ExtractedDocument, ExtractInput, ReferenceCandidate } from './extract.js';
44
+ export { resolveCorpus, documentOf } from './resolve.js';
45
+ export type { ResolvedCorpus, ResolveOptions } from './resolve.js';
46
+ export { scanMarkdown, slugify } from './markdown.js';
47
+ export type { FrontMatter, Heading, HtmlComment, Link, LinkForm, ListItem, Range, ScannedDocument, ScannedLine, } from './markdown.js';
48
+ export { parseDirectives, attr, attrList, directiveFor } from './directives.js';
49
+ export type { Attribute, Directive, DirectiveName } from './directives.js';
50
+ export { parseFrontMatter, toRecord, valuesOf } from './yaml.js';
51
+ export type { YamlEntry } from './yaml.js';
52
+ export { isRetired, isStatusHeading, normaliseStatus, phaseFromPath, phaseOf, receptivityOf, supersessionTargetsIn, STATUS_KEYS, } from './lifecycle.js';
53
+ export { resolveItemState, KNOWN_STATE_WORDS } from './state.js';
54
+ export type { ResolvedState, StateInput } from './state.js';
55
+ export { familyFromPath, identify, isDocumentTarget, isExternal, looksLikePath, normaliseRef, parseBareRef, parsePrefixedRef, splitAnchor, ID_KEYS, } from './identity.js';
56
+ export type { DocumentIdentity, IdentityInput } from './identity.js';
57
+ export { createGlobMatcher, createReferenceFilter, globBase, globToRegExp, isGlob, walkFiles, DEFAULT_IGNORED_DIRECTORIES, MAX_FILE_SIZE, } from './glob.js';
58
+ export type { GlobMatcher, WalkedFile, WalkOptions } from './glob.js';
59
+ export { compareRefs, createLineIndex, formatRef, refOf, spanOf } from './source.js';
60
+ export type { LineIndex } from './source.js';
61
+ export { basenamePosix, dirnamePosix, extnamePosix, joinPosix, normalisePosix, resolveFrom, toPosix, } from './paths.js';
62
+ export { DISPOSITIONS, EDGE_KINDS, EDGE_TRAITS, OPENNESS_OF } from './types.js';
63
+ export type { AnalysisSummary, DanglingReason, DanglingRef, Diagnostic, Disposition, DocumentNode, Edge, EdgeKind, EdgeOrigin, EdgeTraits, ItemNode, NodeKind, Openness, ParseProblem, Phase, Position, Receptivity, RelatedLocation, RuleId, Severity, SourceRef, Span, SpecNode, StateSignal, } from './types.js';
package/dist/index.js ADDED
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Programmatic API.
3
+ *
4
+ * The CLI is a shell over `analyse`; anything the CLI can do, a build script, a
5
+ * custom reporter or an editor extension can do too.
6
+ *
7
+ * ```ts
8
+ * import { analyse, formatReport } from '@descent-vtt/spec-graph';
9
+ *
10
+ * const result = await analyse({ root: process.cwd(), patterns: ['docs/**\/*.md'] });
11
+ * if (!result.ok) console.error(formatReport(result, { color: true }));
12
+ * ```
13
+ *
14
+ * For a corpus that is already in memory - a monorepo tool, a language server,
15
+ * a test - skip the filesystem entirely:
16
+ *
17
+ * ```ts
18
+ * import { analyseSources, query } from '@descent-vtt/spec-graph';
19
+ *
20
+ * const { graph } = analyseSources([{ path: 'adr/0001.md', text: '...' }]);
21
+ * const ghosts = query(graph, 'item[openness=open] -delegates-to-> document[phase=retired]');
22
+ * ```
23
+ */
24
+ export { applyBaseline, formatBaseline, fingerprintOf, parseBaseline, BASELINE_VERSION, EMPTY_BASELINE, } from './baseline.js';
25
+ export { loadConfig, parseConfig, CONFIG_FILES, CONFIG_PACKAGE_KEY } from './config.js';
26
+ export { main, parseArgs, HELP, UsageError, EXIT_OK, EXIT_FAILED, EXIT_ERROR } from './cli.js';
27
+ export { analyse, analyseSources, createFamilyFilter, createHistoryMatcher, withDiagnostics, DEFAULT_CONCURRENCY, DEFAULT_PATTERNS, } from './runner.js';
28
+ export { buildGraph, LOAD_BEARING_EDGES, OBLIGATION_EDGES } from './graph.js';
29
+ export { attributesOf, execute, matches, parseQuery, query, renderMatch, QueryError, DEFAULT_MATCH_LIMIT, } from './select.js';
30
+ export { resolveStrict, runRules, sortDiagnostics, DEFAULT_SEVERITIES, RULE_DESCRIPTIONS, RULE_IDS, RULE_QUERIES, } from './rules.js';
31
+ export { createPainter, formatGraph, formatJson, formatReport, shouldUseAscii, shouldUseColor, } from './report.js';
32
+ export { extractDocument, extractSpecifications, classifyReference, sectionPathAt } from './extract.js';
33
+ export { findSpecificationRegions, readRegionStatus, regionAt } from './sections.js';
34
+ export { resolveCorpus, documentOf } from './resolve.js';
35
+ export { scanMarkdown, slugify } from './markdown.js';
36
+ export { parseDirectives, attr, attrList, directiveFor } from './directives.js';
37
+ export { parseFrontMatter, toRecord, valuesOf } from './yaml.js';
38
+ export { isRetired, isStatusHeading, normaliseStatus, phaseFromPath, phaseOf, receptivityOf, supersessionTargetsIn, STATUS_KEYS, } from './lifecycle.js';
39
+ export { resolveItemState, KNOWN_STATE_WORDS } from './state.js';
40
+ export { familyFromPath, identify, isDocumentTarget, isExternal, looksLikePath, normaliseRef, parseBareRef, parsePrefixedRef, splitAnchor, ID_KEYS, } from './identity.js';
41
+ export { createGlobMatcher, createReferenceFilter, globBase, globToRegExp, isGlob, walkFiles, DEFAULT_IGNORED_DIRECTORIES, MAX_FILE_SIZE, } from './glob.js';
42
+ export { compareRefs, createLineIndex, formatRef, refOf, spanOf } from './source.js';
43
+ export { basenamePosix, dirnamePosix, extnamePosix, joinPosix, normalisePosix, resolveFrom, toPosix, } from './paths.js';
44
+ export { DISPOSITIONS, EDGE_KINDS, EDGE_TRAITS, OPENNESS_OF } from './types.js';
45
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,EACL,aAAa,EACb,cAAc,EACd,aAAa,EACb,aAAa,EACb,gBAAgB,EAChB,cAAc,GACf,MAAM,eAAe,CAAC;AAGvB,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAGxF,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAG/F,OAAO,EACL,OAAO,EACP,cAAc,EACd,kBAAkB,EAClB,oBAAoB,EACpB,eAAe,EACf,mBAAmB,EACnB,gBAAgB,GACjB,MAAM,aAAa,CAAC;AAGrB,OAAO,EAAE,UAAU,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAG9E,OAAO,EACL,YAAY,EACZ,OAAO,EACP,OAAO,EACP,UAAU,EACV,KAAK,EACL,WAAW,EACX,UAAU,EACV,mBAAmB,GACpB,MAAM,aAAa,CAAC;AAWrB,OAAO,EACL,aAAa,EACb,QAAQ,EACR,eAAe,EACf,kBAAkB,EAClB,iBAAiB,EACjB,QAAQ,EACR,YAAY,GACb,MAAM,YAAY,CAAC;AAGpB,OAAO,EACL,aAAa,EACb,WAAW,EACX,UAAU,EACV,YAAY,EACZ,cAAc,EACd,cAAc,GACf,MAAM,aAAa,CAAC;AAGrB,OAAO,EAAE,eAAe,EAAE,qBAAqB,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AACxG,OAAO,EAAE,wBAAwB,EAAE,gBAAgB,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAIrF,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAGzD,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAatD,OAAO,EAAE,eAAe,EAAE,IAAI,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAGhF,OAAO,EAAE,gBAAgB,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAGjE,OAAO,EACL,SAAS,EACT,eAAe,EACf,eAAe,EACf,aAAa,EACb,OAAO,EACP,aAAa,EACb,qBAAqB,EACrB,WAAW,GACZ,MAAM,gBAAgB,CAAC;AAExB,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAGjE,OAAO,EACL,cAAc,EACd,QAAQ,EACR,gBAAgB,EAChB,UAAU,EACV,aAAa,EACb,YAAY,EACZ,YAAY,EACZ,gBAAgB,EAChB,WAAW,EACX,OAAO,GACR,MAAM,eAAe,CAAC;AAGvB,OAAO,EACL,iBAAiB,EACjB,qBAAqB,EACrB,QAAQ,EACR,YAAY,EACZ,MAAM,EACN,SAAS,EACT,2BAA2B,EAC3B,aAAa,GACd,MAAM,WAAW,CAAC;AAGnB,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAGrF,OAAO,EACL,aAAa,EACb,YAAY,EACZ,YAAY,EACZ,SAAS,EACT,cAAc,EACd,WAAW,EACX,OAAO,GACR,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC","sourcesContent":["/**\n * Programmatic API.\n *\n * The CLI is a shell over `analyse`; anything the CLI can do, a build script, a\n * custom reporter or an editor extension can do too.\n *\n * ```ts\n * import { analyse, formatReport } from '@descent-vtt/spec-graph';\n *\n * const result = await analyse({ root: process.cwd(), patterns: ['docs/**\\/*.md'] });\n * if (!result.ok) console.error(formatReport(result, { color: true }));\n * ```\n *\n * For a corpus that is already in memory - a monorepo tool, a language server,\n * a test - skip the filesystem entirely:\n *\n * ```ts\n * import { analyseSources, query } from '@descent-vtt/spec-graph';\n *\n * const { graph } = analyseSources([{ path: 'adr/0001.md', text: '...' }]);\n * const ghosts = query(graph, 'item[openness=open] -delegates-to-> document[phase=retired]');\n * ```\n */\n\nexport {\n applyBaseline,\n formatBaseline,\n fingerprintOf,\n parseBaseline,\n BASELINE_VERSION,\n EMPTY_BASELINE,\n} from './baseline.js';\nexport type { Baseline, BaselineEntry, BaselineOutcome, ParsedBaseline } from './baseline.js';\n\nexport { loadConfig, parseConfig, CONFIG_FILES, CONFIG_PACKAGE_KEY } from './config.js';\nexport type { LoadedConfig, SpecGraphConfig } from './config.js';\n\nexport { main, parseArgs, HELP, UsageError, EXIT_OK, EXIT_FAILED, EXIT_ERROR } from './cli.js';\nexport type { CliIO, CliOptions, Command } from './cli.js';\n\nexport {\n analyse,\n analyseSources,\n createFamilyFilter,\n createHistoryMatcher,\n withDiagnostics,\n DEFAULT_CONCURRENCY,\n DEFAULT_PATTERNS,\n} from './runner.js';\nexport type { AnalyseOptions, AnalyseSourcesOptions, AnalysisResult, Source } from './runner.js';\n\nexport { buildGraph, LOAD_BEARING_EDGES, OBLIGATION_EDGES } from './graph.js';\nexport type { SpecGraph } from './graph.js';\n\nexport {\n attributesOf,\n execute,\n matches,\n parseQuery,\n query,\n renderMatch,\n QueryError,\n DEFAULT_MATCH_LIMIT,\n} from './select.js';\nexport type {\n ExecuteOptions,\n Match,\n NodeMatcher,\n Operator,\n Predicate,\n QuerySpec,\n StepSpec,\n} from './select.js';\n\nexport {\n resolveStrict,\n runRules,\n sortDiagnostics,\n DEFAULT_SEVERITIES,\n RULE_DESCRIPTIONS,\n RULE_IDS,\n RULE_QUERIES,\n} from './rules.js';\nexport type { RuleOptions } from './rules.js';\n\nexport {\n createPainter,\n formatGraph,\n formatJson,\n formatReport,\n shouldUseAscii,\n shouldUseColor,\n} from './report.js';\nexport type { ColorEnvironment, GraphExportOptions, GraphFormat, Painter, ReporterOptions } from './report.js';\n\nexport { extractDocument, extractSpecifications, classifyReference, sectionPathAt } from './extract.js';\nexport { findSpecificationRegions, readRegionStatus, regionAt } from './sections.js';\nexport type { RegionStatus, SpecificationRegion } from './sections.js';\nexport type { Classification, ExtractedDocument, ExtractInput, ReferenceCandidate } from './extract.js';\n\nexport { resolveCorpus, documentOf } from './resolve.js';\nexport type { ResolvedCorpus, ResolveOptions } from './resolve.js';\n\nexport { scanMarkdown, slugify } from './markdown.js';\nexport type {\n FrontMatter,\n Heading,\n HtmlComment,\n Link,\n LinkForm,\n ListItem,\n Range,\n ScannedDocument,\n ScannedLine,\n} from './markdown.js';\n\nexport { parseDirectives, attr, attrList, directiveFor } from './directives.js';\nexport type { Attribute, Directive, DirectiveName } from './directives.js';\n\nexport { parseFrontMatter, toRecord, valuesOf } from './yaml.js';\nexport type { YamlEntry } from './yaml.js';\n\nexport {\n isRetired,\n isStatusHeading,\n normaliseStatus,\n phaseFromPath,\n phaseOf,\n receptivityOf,\n supersessionTargetsIn,\n STATUS_KEYS,\n} from './lifecycle.js';\n\nexport { resolveItemState, KNOWN_STATE_WORDS } from './state.js';\nexport type { ResolvedState, StateInput } from './state.js';\n\nexport {\n familyFromPath,\n identify,\n isDocumentTarget,\n isExternal,\n looksLikePath,\n normaliseRef,\n parseBareRef,\n parsePrefixedRef,\n splitAnchor,\n ID_KEYS,\n} from './identity.js';\nexport type { DocumentIdentity, IdentityInput } from './identity.js';\n\nexport {\n createGlobMatcher,\n createReferenceFilter,\n globBase,\n globToRegExp,\n isGlob,\n walkFiles,\n DEFAULT_IGNORED_DIRECTORIES,\n MAX_FILE_SIZE,\n} from './glob.js';\nexport type { GlobMatcher, WalkedFile, WalkOptions } from './glob.js';\n\nexport { compareRefs, createLineIndex, formatRef, refOf, spanOf } from './source.js';\nexport type { LineIndex } from './source.js';\n\nexport {\n basenamePosix,\n dirnamePosix,\n extnamePosix,\n joinPosix,\n normalisePosix,\n resolveFrom,\n toPosix,\n} from './paths.js';\n\nexport { DISPOSITIONS, EDGE_KINDS, EDGE_TRAITS, OPENNESS_OF } from './types.js';\nexport type {\n AnalysisSummary,\n DanglingReason,\n DanglingRef,\n Diagnostic,\n Disposition,\n DocumentNode,\n Edge,\n EdgeKind,\n EdgeOrigin,\n EdgeTraits,\n ItemNode,\n NodeKind,\n Openness,\n ParseProblem,\n Phase,\n Position,\n Receptivity,\n RelatedLocation,\n RuleId,\n Severity,\n SourceRef,\n Span,\n SpecNode,\n StateSignal,\n} from './types.js';\n"]}
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Status vocabulary normalisation.
3
+ *
4
+ * Every specification ecosystem invented its own status words, and no two agree.
5
+ * MADR says `accepted`; Kubernetes KEPs say `implementable`; Rust RFCs say
6
+ * `merged`; a hundred internal wikis say `Live`, `Signed off`, or `DONE ✅`.
7
+ *
8
+ * spec-graph refuses to care. What it needs from a status is one bit - *can this
9
+ * document still absorb an obligation?* - and that bit is recoverable from all
10
+ * of those vocabularies at once. The tables below are the only place in the
11
+ * codebase that knows any ecosystem-specific word, which is what keeps the
12
+ * engine domain-agnostic: supporting a new convention is a table entry, never a
13
+ * rule change.
14
+ */
15
+ import type { Phase, Receptivity } from './types.js';
16
+ /** Front-matter keys that may carry a status, in order of preference. */
17
+ export declare const STATUS_KEYS: readonly string[];
18
+ /**
19
+ * Reduces a status string to lookup form.
20
+ *
21
+ * Strips Markdown emphasis, badge syntax, emoji and dates, so `**Accepted**`,
22
+ * `` `accepted` ``, `Accepted ✅` and `Accepted (2026-03-01)` all land on the
23
+ * same token.
24
+ */
25
+ export declare function normaliseStatus(raw: string): string;
26
+ /**
27
+ * Maps a raw status string onto a lifecycle phase.
28
+ *
29
+ * Returns `unknown` rather than guessing when nothing matches: a document with
30
+ * an unrecognised status is not evidence of anything, and inventing a phase for
31
+ * it would produce exactly the confident-but-wrong findings this tool exists to
32
+ * eliminate.
33
+ */
34
+ export declare function phaseOf(raw: string | null | undefined): Phase;
35
+ /** Whether a document in this phase can take on a new open obligation. */
36
+ export declare function receptivityOf(phase: Phase): Receptivity;
37
+ /** True when the phase means the document no longer binds anybody. */
38
+ export declare function isRetired(phase: Phase): boolean;
39
+ /** True when the heading names a section that declares the document status. */
40
+ export declare function isStatusHeading(text: string): boolean;
41
+ /**
42
+ * Infers retirement from where a file lives.
43
+ *
44
+ * Moving an ADR into `docs/adr/archive/` is the most common way a team retires a
45
+ * decision without touching its front matter, and a graph that misses it will
46
+ * happily let obligations be delegated into the archive.
47
+ */
48
+ export declare function phaseFromPath(posixPath: string): Phase;
49
+ /**
50
+ * Pulls a supersession target out of a status string.
51
+ *
52
+ * `Superseded by ADR-0009` is the single most common way a supersession is ever
53
+ * recorded, and it is written in the status field rather than as a link. Reading
54
+ * it here means the relation reaches the graph even in repositories that never
55
+ * adopted an explicit field for it.
56
+ */
57
+ export declare function supersessionTargetsIn(raw: string): string[];
@@ -0,0 +1,258 @@
1
+ /**
2
+ * Status vocabulary normalisation.
3
+ *
4
+ * Every specification ecosystem invented its own status words, and no two agree.
5
+ * MADR says `accepted`; Kubernetes KEPs say `implementable`; Rust RFCs say
6
+ * `merged`; a hundred internal wikis say `Live`, `Signed off`, or `DONE ✅`.
7
+ *
8
+ * spec-graph refuses to care. What it needs from a status is one bit - *can this
9
+ * document still absorb an obligation?* - and that bit is recoverable from all
10
+ * of those vocabularies at once. The tables below are the only place in the
11
+ * codebase that knows any ecosystem-specific word, which is what keeps the
12
+ * engine domain-agnostic: supporting a new convention is a table entry, never a
13
+ * rule change.
14
+ */
15
+ /**
16
+ * Recognised status words, most terminal first.
17
+ *
18
+ * Order matters. A status of "accepted, superseded by ADR-0009" is retired, not
19
+ * active: retirement is terminal, so a retirement word anywhere in the string
20
+ * wins over anything else in it.
21
+ */
22
+ const VOCABULARY = [
23
+ [
24
+ 'retired',
25
+ [
26
+ 'superseded',
27
+ // Misspelled in the wild often enough that omitting it would silently
28
+ // mis-classify real documents as active.
29
+ 'superceded',
30
+ 'supersedes-by',
31
+ 'replaced',
32
+ 'replaced-by',
33
+ 'deprecated',
34
+ 'obsolete',
35
+ 'obsoleted',
36
+ 'retired',
37
+ 'archived',
38
+ 'archive',
39
+ 'rejected',
40
+ 'declined',
41
+ 'withdrawn',
42
+ 'abandoned',
43
+ 'cancelled',
44
+ 'canceled',
45
+ 'dropped',
46
+ 'revoked',
47
+ 'reverted',
48
+ 'moved',
49
+ 'historical',
50
+ 'defunct',
51
+ 'inactive',
52
+ 'dead',
53
+ 'void',
54
+ 'closed',
55
+ 'postponed',
56
+ 'deferred',
57
+ 'not-planned',
58
+ 'wontfix',
59
+ ],
60
+ ],
61
+ [
62
+ 'frozen',
63
+ ['final', 'finalised', 'finalized', 'frozen', 'locked', 'ratified', 'immutable', 'sealed', 'standard', 'published'],
64
+ ],
65
+ [
66
+ 'active',
67
+ [
68
+ 'accepted',
69
+ 'active',
70
+ 'approved',
71
+ 'adopted',
72
+ 'agreed',
73
+ 'implementable',
74
+ 'implemented',
75
+ 'implementing',
76
+ 'current',
77
+ 'effective',
78
+ 'in-effect',
79
+ 'enforced',
80
+ 'stable',
81
+ 'merged',
82
+ 'released',
83
+ 'shipped',
84
+ 'live',
85
+ 'done',
86
+ 'complete',
87
+ 'completed',
88
+ 'signed-off',
89
+ 'committed',
90
+ ],
91
+ ],
92
+ [
93
+ 'draft',
94
+ [
95
+ 'draft',
96
+ 'drafting',
97
+ 'proposed',
98
+ 'proposal',
99
+ 'provisional',
100
+ 'prospective',
101
+ 'wip',
102
+ 'work-in-progress',
103
+ 'in-progress',
104
+ 'review',
105
+ 'in-review',
106
+ 'under-review',
107
+ 'reviewing',
108
+ 'discussion',
109
+ 'discussing',
110
+ 'pending',
111
+ 'idea',
112
+ 'exploratory',
113
+ 'candidate',
114
+ 'open',
115
+ 'new',
116
+ 'unreviewed',
117
+ 'rfc',
118
+ 'experimental',
119
+ 'alpha',
120
+ 'beta',
121
+ 'incubating',
122
+ 'provisionally-accepted',
123
+ ],
124
+ ],
125
+ ];
126
+ /** Path segments that retire every document beneath them. */
127
+ const RETIRING_DIRECTORIES = new Set([
128
+ 'archive',
129
+ 'archived',
130
+ 'attic',
131
+ 'deprecated',
132
+ 'graveyard',
133
+ 'historical',
134
+ 'obsolete',
135
+ 'rejected',
136
+ 'retired',
137
+ 'superseded',
138
+ 'superceded',
139
+ 'withdrawn',
140
+ ]);
141
+ /** Front-matter keys that may carry a status, in order of preference. */
142
+ export const STATUS_KEYS = ['status', 'state', 'stage', 'lifecycle', 'phase', 'adr-status'];
143
+ /** Heading names whose body declares the document status. */
144
+ const STATUS_HEADINGS = new Set(['status', 'state', 'stage', 'lifecycle', 'current status']);
145
+ /**
146
+ * Reduces a status string to lookup form.
147
+ *
148
+ * Strips Markdown emphasis, badge syntax, emoji and dates, so `**Accepted**`,
149
+ * `` `accepted` ``, `Accepted ✅` and `Accepted (2026-03-01)` all land on the
150
+ * same token.
151
+ */
152
+ export function normaliseStatus(raw) {
153
+ return raw
154
+ .replace(/<[^>]*>/g, ' ')
155
+ .replace(/[`*_~"'#]/g, '')
156
+ .replace(/\[([^\]]*)\]\([^)]*\)/g, '$1')
157
+ .replace(/\p{Extended_Pictographic}/gu, ' ')
158
+ .replace(/\d{4}-\d{2}-\d{2}/g, ' ')
159
+ .toLowerCase()
160
+ .replace(/[^\p{L}\p{N}]+/gu, ' ')
161
+ .trim();
162
+ }
163
+ /**
164
+ * Maps a raw status string onto a lifecycle phase.
165
+ *
166
+ * Returns `unknown` rather than guessing when nothing matches: a document with
167
+ * an unrecognised status is not evidence of anything, and inventing a phase for
168
+ * it would produce exactly the confident-but-wrong findings this tool exists to
169
+ * eliminate.
170
+ */
171
+ export function phaseOf(raw) {
172
+ if (raw === null || raw === undefined)
173
+ return 'unknown';
174
+ const text = normaliseStatus(raw);
175
+ if (text.length === 0)
176
+ return 'unknown';
177
+ const words = new Set(text.split(' '));
178
+ const hyphenated = text.replace(/ /g, '-');
179
+ for (const [phase, terms] of VOCABULARY) {
180
+ for (const term of terms) {
181
+ if (term.includes('-')) {
182
+ if (hyphenated.includes(term))
183
+ return phase;
184
+ }
185
+ else if (words.has(term)) {
186
+ return phase;
187
+ }
188
+ }
189
+ }
190
+ return 'unknown';
191
+ }
192
+ /** Whether a document in this phase can take on a new open obligation. */
193
+ export function receptivityOf(phase) {
194
+ switch (phase) {
195
+ case 'draft':
196
+ case 'active':
197
+ return 'receptive';
198
+ case 'frozen':
199
+ case 'retired':
200
+ // A record was never asking for an obligation, so it cannot take one on.
201
+ // Sealed for the same reason a retired document is, by a different route.
202
+ case 'record':
203
+ return 'sealed';
204
+ case 'unknown':
205
+ return 'unknown';
206
+ }
207
+ }
208
+ /** True when the phase means the document no longer binds anybody. */
209
+ export function isRetired(phase) {
210
+ return phase === 'retired';
211
+ }
212
+ /** True when the heading names a section that declares the document status. */
213
+ export function isStatusHeading(text) {
214
+ return STATUS_HEADINGS.has(text.trim().toLowerCase().replace(/[:*_`]/g, '').trim());
215
+ }
216
+ /**
217
+ * Infers retirement from where a file lives.
218
+ *
219
+ * Moving an ADR into `docs/adr/archive/` is the most common way a team retires a
220
+ * decision without touching its front matter, and a graph that misses it will
221
+ * happily let obligations be delegated into the archive.
222
+ */
223
+ export function phaseFromPath(posixPath) {
224
+ const segments = posixPath.toLowerCase().split('/');
225
+ // The file name itself is not a directory; a document called `archive.md` is
226
+ // not retired.
227
+ for (let i = 0; i < segments.length - 1; i += 1) {
228
+ if (RETIRING_DIRECTORIES.has(segments[i]))
229
+ return 'retired';
230
+ }
231
+ return 'unknown';
232
+ }
233
+ /**
234
+ * Pulls a supersession target out of a status string.
235
+ *
236
+ * `Superseded by ADR-0009` is the single most common way a supersession is ever
237
+ * recorded, and it is written in the status field rather than as a link. Reading
238
+ * it here means the relation reaches the graph even in repositories that never
239
+ * adopted an explicit field for it.
240
+ */
241
+ export function supersessionTargetsIn(raw) {
242
+ const out = [];
243
+ const pattern = /(?:superse[dc]ed|replaced|obsoleted)\s*(?:by|with|through)?\s*[:\-—]?\s*(.+)$/i;
244
+ const match = pattern.exec(raw.replace(/[`*_]/g, ''));
245
+ if (!match)
246
+ return out;
247
+ const tail = match[1];
248
+ for (const token of tail.split(/[,;]|\band\b/)) {
249
+ const cleaned = token
250
+ .replace(/\[([^\]]*)\]\([^)]*\)/g, '$1')
251
+ .replace(/[()<>"']/g, ' ')
252
+ .trim();
253
+ if (cleaned.length > 0 && cleaned.length < 200)
254
+ out.push(cleaned);
255
+ }
256
+ return out;
257
+ }
258
+ //# sourceMappingURL=lifecycle.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lifecycle.js","sourceRoot":"","sources":["../src/lifecycle.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Status vocabulary normalisation.\n *\n * Every specification ecosystem invented its own status words, and no two agree.\n * MADR says `accepted`; Kubernetes KEPs say `implementable`; Rust RFCs say\n * `merged`; a hundred internal wikis say `Live`, `Signed off`, or `DONE ✅`.\n *\n * spec-graph refuses to care. What it needs from a status is one bit - *can this\n * document still absorb an obligation?* - and that bit is recoverable from all\n * of those vocabularies at once. The tables below are the only place in the\n * codebase that knows any ecosystem-specific word, which is what keeps the\n * engine domain-agnostic: supporting a new convention is a table entry, never a\n * rule change.\n */\n\nimport type { Phase, Receptivity } from './types.js';\n\n/**\n * Recognised status words, most terminal first.\n *\n * Order matters. A status of \"accepted, superseded by ADR-0009\" is retired, not\n * active: retirement is terminal, so a retirement word anywhere in the string\n * wins over anything else in it.\n */\nconst VOCABULARY: readonly (readonly [Phase, readonly string[]])[] = [\n [\n 'retired',\n [\n 'superseded',\n // Misspelled in the wild often enough that omitting it would silently\n // mis-classify real documents as active.\n 'superceded',\n 'supersedes-by',\n 'replaced',\n 'replaced-by',\n 'deprecated',\n 'obsolete',\n 'obsoleted',\n 'retired',\n 'archived',\n 'archive',\n 'rejected',\n 'declined',\n 'withdrawn',\n 'abandoned',\n 'cancelled',\n 'canceled',\n 'dropped',\n 'revoked',\n 'reverted',\n 'moved',\n 'historical',\n 'defunct',\n 'inactive',\n 'dead',\n 'void',\n 'closed',\n 'postponed',\n 'deferred',\n 'not-planned',\n 'wontfix',\n ],\n ],\n [\n 'frozen',\n ['final', 'finalised', 'finalized', 'frozen', 'locked', 'ratified', 'immutable', 'sealed', 'standard', 'published'],\n ],\n [\n 'active',\n [\n 'accepted',\n 'active',\n 'approved',\n 'adopted',\n 'agreed',\n 'implementable',\n 'implemented',\n 'implementing',\n 'current',\n 'effective',\n 'in-effect',\n 'enforced',\n 'stable',\n 'merged',\n 'released',\n 'shipped',\n 'live',\n 'done',\n 'complete',\n 'completed',\n 'signed-off',\n 'committed',\n ],\n ],\n [\n 'draft',\n [\n 'draft',\n 'drafting',\n 'proposed',\n 'proposal',\n 'provisional',\n 'prospective',\n 'wip',\n 'work-in-progress',\n 'in-progress',\n 'review',\n 'in-review',\n 'under-review',\n 'reviewing',\n 'discussion',\n 'discussing',\n 'pending',\n 'idea',\n 'exploratory',\n 'candidate',\n 'open',\n 'new',\n 'unreviewed',\n 'rfc',\n 'experimental',\n 'alpha',\n 'beta',\n 'incubating',\n 'provisionally-accepted',\n ],\n ],\n];\n\n/** Path segments that retire every document beneath them. */\nconst RETIRING_DIRECTORIES: ReadonlySet<string> = new Set([\n 'archive',\n 'archived',\n 'attic',\n 'deprecated',\n 'graveyard',\n 'historical',\n 'obsolete',\n 'rejected',\n 'retired',\n 'superseded',\n 'superceded',\n 'withdrawn',\n]);\n\n/** Front-matter keys that may carry a status, in order of preference. */\nexport const STATUS_KEYS: readonly string[] = ['status', 'state', 'stage', 'lifecycle', 'phase', 'adr-status'];\n\n/** Heading names whose body declares the document status. */\nconst STATUS_HEADINGS: ReadonlySet<string> = new Set(['status', 'state', 'stage', 'lifecycle', 'current status']);\n\n/**\n * Reduces a status string to lookup form.\n *\n * Strips Markdown emphasis, badge syntax, emoji and dates, so `**Accepted**`,\n * `` `accepted` ``, `Accepted ✅` and `Accepted (2026-03-01)` all land on the\n * same token.\n */\nexport function normaliseStatus(raw: string): string {\n return raw\n .replace(/<[^>]*>/g, ' ')\n .replace(/[`*_~\"'#]/g, '')\n .replace(/\\[([^\\]]*)\\]\\([^)]*\\)/g, '$1')\n .replace(/\\p{Extended_Pictographic}/gu, ' ')\n .replace(/\\d{4}-\\d{2}-\\d{2}/g, ' ')\n .toLowerCase()\n .replace(/[^\\p{L}\\p{N}]+/gu, ' ')\n .trim();\n}\n\n/**\n * Maps a raw status string onto a lifecycle phase.\n *\n * Returns `unknown` rather than guessing when nothing matches: a document with\n * an unrecognised status is not evidence of anything, and inventing a phase for\n * it would produce exactly the confident-but-wrong findings this tool exists to\n * eliminate.\n */\nexport function phaseOf(raw: string | null | undefined): Phase {\n if (raw === null || raw === undefined) return 'unknown';\n const text = normaliseStatus(raw);\n if (text.length === 0) return 'unknown';\n const words = new Set(text.split(' '));\n const hyphenated = text.replace(/ /g, '-');\n\n for (const [phase, terms] of VOCABULARY) {\n for (const term of terms) {\n if (term.includes('-')) {\n if (hyphenated.includes(term)) return phase;\n } else if (words.has(term)) {\n return phase;\n }\n }\n }\n return 'unknown';\n}\n\n/** Whether a document in this phase can take on a new open obligation. */\nexport function receptivityOf(phase: Phase): Receptivity {\n switch (phase) {\n case 'draft':\n case 'active':\n return 'receptive';\n case 'frozen':\n case 'retired':\n // A record was never asking for an obligation, so it cannot take one on.\n // Sealed for the same reason a retired document is, by a different route.\n case 'record':\n return 'sealed';\n case 'unknown':\n return 'unknown';\n }\n}\n\n/** True when the phase means the document no longer binds anybody. */\nexport function isRetired(phase: Phase): boolean {\n return phase === 'retired';\n}\n\n/** True when the heading names a section that declares the document status. */\nexport function isStatusHeading(text: string): boolean {\n return STATUS_HEADINGS.has(text.trim().toLowerCase().replace(/[:*_`]/g, '').trim());\n}\n\n/**\n * Infers retirement from where a file lives.\n *\n * Moving an ADR into `docs/adr/archive/` is the most common way a team retires a\n * decision without touching its front matter, and a graph that misses it will\n * happily let obligations be delegated into the archive.\n */\nexport function phaseFromPath(posixPath: string): Phase {\n const segments = posixPath.toLowerCase().split('/');\n // The file name itself is not a directory; a document called `archive.md` is\n // not retired.\n for (let i = 0; i < segments.length - 1; i += 1) {\n if (RETIRING_DIRECTORIES.has(segments[i] as string)) return 'retired';\n }\n return 'unknown';\n}\n\n/**\n * Pulls a supersession target out of a status string.\n *\n * `Superseded by ADR-0009` is the single most common way a supersession is ever\n * recorded, and it is written in the status field rather than as a link. Reading\n * it here means the relation reaches the graph even in repositories that never\n * adopted an explicit field for it.\n */\nexport function supersessionTargetsIn(raw: string): string[] {\n const out: string[] = [];\n const pattern = /(?:superse[dc]ed|replaced|obsoleted)\\s*(?:by|with|through)?\\s*[:\\-—]?\\s*(.+)$/i;\n const match = pattern.exec(raw.replace(/[`*_]/g, ''));\n if (!match) return out;\n const tail = match[1] as string;\n for (const token of tail.split(/[,;]|\\band\\b/)) {\n const cleaned = token\n .replace(/\\[([^\\]]*)\\]\\([^)]*\\)/g, '$1')\n .replace(/[()<>\"']/g, ' ')\n .trim();\n if (cleaned.length > 0 && cleaned.length < 200) out.push(cleaned);\n }\n return out;\n}\n"]}