spec-up-t 1.4.1-beta.1 → 1.4.1-beta.3

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.
@@ -9,7 +9,7 @@ code[class*=language-],pre[class*=language-]{color:#000;background:0 0;text-shad
9
9
  @keyframes chartjs-render-animation{from{opacity:.99}to{opacity:1}}.chartjs-render-monitor{animation:chartjs-render-animation 1ms}.chartjs-size-monitor,.chartjs-size-monitor-expand,.chartjs-size-monitor-shrink{position:absolute;direction:ltr;left:0;top:0;right:0;bottom:0;overflow:hidden;pointer-events:none;visibility:hidden;z-index:-1}.chartjs-size-monitor-expand>div{position:absolute;width:1000000px;height:1000000px;left:0;top:0}.chartjs-size-monitor-shrink>div{position:absolute;width:200%;height:200%;left:0;top:0}
10
10
  #logo img{width:100px}.bi{display:inline-block;width:1.2rem;height:1.2rem}#header,#header *{background-color:var(--header-navbar-bg);color:var(--header-navbar-text)}[issue-count]:after{content:"(" attr(issue-count) ")";margin:0 0 0 .3em;padding:.1em 0 0}[issue-count=""][animate]{display:none;opacity:0}[issue-count][animate]:not([issue-count=""]){animation:display-show 1s}#header a:not(#logo),#header a:not(#logo) *,#header button,#header button *{background:#a9dde0}
11
11
  .container{min-height:100vh}@media (min-width:768px){.sidebar{position:sticky;top:0;height:100vh;overflow-y:auto;z-index:100}}ul.toc{padding:1em 0 1.75em;font-size:.85em}ul.toc,ul.toc ul{margin:0;padding:0;list-style:none}ul.toc li a{display:block;padding:.4em .8em;text-decoration:none;color:var(--toc-text);transition:background .2s ease,padding-left .2s ease,border-left .2s ease;padding-left:.8em;border-left:3px solid transparent;border-bottom:1px solid rgba(0,0,0,.03)}ul.toc ul{margin-left:1.2em!important;margin-top:.3em}ul.toc a:hover{background:#f9f9f9;padding-left:1em;border-left:3px solid #007bff}ul.toc>li>ul>li>a{padding-left:1.5em}ul.toc>li>ul>li>ul>li>a{padding-left:2em}ul.toc a:focus{outline:2px solid #007bff;outline-offset:-2px;box-shadow:0 0 0 3px rgba(0,123,255,.25);position:relative;z-index:1}.spec-footer{font-size:.7em;text-align:center}ul.toc li a.active,ul.toc li a[aria-current=page]{border-left:3px solid #007bff;font-weight:500;background:#f0f0f0}ul.toc li:last-child a{border-bottom:none}#toc ul li{position:relative}#toc ul li.has-children{position:relative}.collapse-toggle{position:absolute;top:.35em;right:0;width:28px;height:28px;background:0 0;border:1px solid transparent;border-radius:3px;padding:0;display:flex;align-items:center;justify-content:center;cursor:pointer;transition:background-color .2s,border-color .2s;z-index:2}.collapse-toggle:hover{background-color:rgba(0,0,0,.05)}.collapse-toggle:focus{outline:2px solid #007bff;outline-offset:1px;border-color:#007bff;box-shadow:0 0 0 3px rgba(0,123,255,.25)}.collapse-toggle::after{content:"";border-style:solid;border-width:.15em .15em 0 0;display:inline-block;height:.5em;width:.5em;position:relative;transform:rotate(135deg);transition:transform .2s ease}.collapse-toggle.collapsed::after{transform:rotate(45deg)}#toc ul li.has-children>ul{max-height:1000px;overflow:hidden;transition:max-height .3s ease-in-out,opacity .3s ease-in-out;opacity:1}#toc ul li.has-children.collapsed>ul{max-height:0;overflow:hidden;opacity:.6}.screen-reader-text{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.screen-reader-text:focus{width:auto;height:auto;clip:auto;display:block;padding:15px 23px 14px;background-color:#fff;color:#007bff;z-index:100000;font-size:1em;font-weight:700;text-decoration:none;line-height:normal;box-shadow:0 0 2px 2px rgba(0,0,0,.2)}
12
- h2,h3,h4,h5,h6{margin-top:1.5em!important}#terminology-section-utility-container{margin:1em auto;width:100%}@media (min-width:576px){#terminology-section-utility-container{position:sticky;top:3.5em;z-index:10}}.toc-anchor{font-size:.875em;color:#73c2df;text-decoration:none;transition:opacity .3s ease}.toc-anchor:hover{opacity:1}dl.terms-and-definitions-list{margin:0;padding:0;position:relative;z-index:1}dl.terms-and-definitions-list>dt{font-weight:700;margin:0;background-color:var(--card-bg-dt);border:1px solid var(--card-border);padding:.5rem 1.25rem;border-radius:.375rem .375rem 0 0;color:var(--card-text);position:relative;line-height:1.5;font-size:1.05em;display:flex;flex-wrap:wrap;align-items:center;gap:.5rem;min-height:2.5rem}dl.terms-and-definitions-list>dt>span{flex:1 1 auto;min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;transition:all .2s ease-in-out}dl.terms-and-definitions-list>dt:focus>span,dl.terms-and-definitions-list>dt:hover>span,dl.terms-and-definitions-list>dt>span:focus,dl.terms-and-definitions-list>dt>span:hover{white-space:normal;overflow:visible;text-overflow:initial;word-wrap:break-word;line-height:1.4}dl.terms-and-definitions-list>dt:active>span,dl.terms-and-definitions-list>dt>span:active{white-space:normal;overflow:visible;text-overflow:initial;word-wrap:break-word;line-height:1.4}dl.terms-and-definitions-list>dt .btn{font-size:1.2em!important}dl.terms-and-definitions-list>dt:hover{background-color:var(--card-hover-bg);transition:background-color .2s ease-in-out}dl.terms-and-definitions-list>dd{margin:0;background-color:var(--card-bg);border:1px solid var(--card-border);border-top:none;padding:.5rem 2rem;color:var(--card-text);width:100%;position:relative}dl.terms-and-definitions-list dd p{margin:0;color:var(--card-text);padding-left:.5rem}dl.terms-and-definitions-list>dd::before{display:none}dl.terms-and-definitions-list dd table{margin:.5em 0;width:100%;border-collapse:collapse}dl.terms-and-definitions-list dd table td,dl.terms-and-definitions-list dd table th{padding:.5em;border:1px solid #ddd}dl.terms-and-definitions-list dd table th{background-color:#f5f5f5}dl>dd:has(table).meta-info-content-wrapper{display:block;font-size:.7em;position:relative;max-height:none;height:auto;overflow:visible;transition:all .3s ease-out}dl>dd:has(table).meta-info-content-wrapper td,dl>dd:has(table).meta-info-content-wrapper th{padding:.3em}dl>dd:has(table).collapsed.meta-info-content-wrapper{max-height:0;height:0;padding-top:0;padding-bottom:0;margin-top:0;margin-bottom:0;overflow:hidden;transition:all .3s ease-out;line-height:0}dl>dd:has(table).meta-info-content-wrapper>div{display:block;padding:inherit}.meta-info-toggle-button{position:relative;cursor:pointer}dl.terms-and-definitions-list dt+dd{border-bottom:none}dl.terms-and-definitions-list dd+dd{border-top:none;border-bottom:none}dl.terms-and-definitions-list dd+dt{margin-top:1rem}dl.terms-and-definitions-list dd.last-dd{border-radius:0 0 .375rem .375rem;border-bottom:1px solid var(--card-border)}html.dense-info dl.terms-and-definitions-list>dt{padding:.1rem 1.25rem;line-height:inherit;font-size:inherit;min-height:auto}html.dense-info dl.terms-and-definitions-list dd+dt{margin-top:.3rem}html.dense-info dl.terms-and-definitions-list>dt::before{display:none}html.dense-info dl.terms-and-definitions-list>dt>span{flex:1 1 auto;min-width:0}.term-external{position:relative}dl.terms-and-definitions-list>dd.term-external,dl.terms-and-definitions-list>dt.term-external{background:#a9dde03b}.term-local{position:relative}dl.terms-and-definitions-list>dd.term-local,dl.terms-and-definitions-list>dt.term-local{background:#f0f8ff}dl.terms-and-definitions-list .term-local-original-term{font-style:italic}dl.terms-and-definitions-list .term-local-parenthetical-terms{font-weight:400}.btn{font-variant:small-caps;text-transform:uppercase;position:relative;z-index:5}.transclusion-heading{font-size:1.3em;font-weight:700;margin:.2em 0 .2em}dl.terms-and-definitions-list>dd.term-external-embedded{position:relative;padding-left:2.5rem;padding-top:.3rem;padding-bottom:.3rem}dl.terms-and-definitions-list span.term-external::after{content:"";position:absolute;top:50%;transform:translateY(-50%);background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' class='bi bi-box-arrow-right' viewBox='0 0 16 16' fill='currentColor'%3E%3Cpath fill-rule='evenodd' d='M10 12.5a.5.5 0 0 1-.5.5h-8a.5.5 0 0 1-.5-.5v-9a.5.5 0 0 1 .5-.5h8a.5.5 0 0 1 .5.5v2a.5.5 0 0 0 1 0v-2A1.5 1.5 0 0 0 9.5 2h-8A1.5 1.5 0 0 0 0 3.5v9A1.5 1.5 0 0 0 1.5 14h8a1.5 1.5 0 0 0 1.5-1.5v-2a.5.5 0 0 0-1 0v2z'/%3E%3Cpath fill-rule='evenodd' d='M15.854 8.354a.5.5 0 0 0 0-.708l-3-3a.5.5 0 0 0-.708.708L14.293 7.5H5.5a.5.5 0 0 0 0 1h8.793l-2.147 2.146a.5.5 0 0 0 .708.708l3-3z'/%3E%3C/svg%3E");background-size:contain;background-repeat:no-repeat;background-position:center;width:.8rem;height:.8rem;left:.2rem}dl.terms-and-definitions-list span.term-external{padding-left:1.5rem;display:inline-block}.placeholder-tref{display:none}.no-xref-found-message{background:#f8c481;color:#03365f;margin:0 .5em;padding:0 .8em;border-radius:.25rem;font-size:.8rem;font-variant:small-caps;text-transform:uppercase}
12
+ h2,h3,h4,h5,h6{margin-top:1.5em!important}#terminology-section-utility-container{margin:1em auto;width:100%}@media (min-width:576px){#terminology-section-utility-container{position:sticky;top:3.5em;z-index:10}}.toc-anchor{font-size:.875em;color:#73c2df;text-decoration:none;transition:opacity .3s ease}.toc-anchor:hover{opacity:1}dl.terms-and-definitions-list{margin:0;padding:0;position:relative;z-index:1}dl.terms-and-definitions-list>dt{font-weight:700;margin:0;background-color:var(--card-bg-dt);border:1px solid var(--card-border);padding:.5rem 1.25rem;border-radius:.375rem .375rem 0 0;color:var(--card-text);position:relative;line-height:1.5;font-size:1.05em;display:flex;flex-wrap:wrap;align-items:center;gap:.5rem;min-height:2.5rem}dl.terms-and-definitions-list>dt>span{flex:1 1 auto;min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;transition:all .2s ease-in-out}dl.terms-and-definitions-list>dt:focus>span,dl.terms-and-definitions-list>dt:hover>span,dl.terms-and-definitions-list>dt>span:focus,dl.terms-and-definitions-list>dt>span:hover{white-space:normal;overflow:visible;text-overflow:initial;word-wrap:break-word;line-height:1.4}dl.terms-and-definitions-list>dt:active>span,dl.terms-and-definitions-list>dt>span:active{white-space:normal;overflow:visible;text-overflow:initial;word-wrap:break-word;line-height:1.4}dl.terms-and-definitions-list>dt .btn{font-size:1.2em!important}dl.terms-and-definitions-list>dt:hover{background-color:var(--card-hover-bg);transition:background-color .2s ease-in-out}dl.terms-and-definitions-list>dd{margin:0;background-color:var(--card-bg);border:1px solid var(--card-border);border-top:none;padding:.5rem 2rem;color:var(--card-text);width:100%;position:relative}dl.terms-and-definitions-list dd p{margin:0;color:var(--card-text);padding-left:.5rem}dl.terms-and-definitions-list>dd::before{display:none}dl.terms-and-definitions-list dd table{margin:.5em 0;width:100%;border-collapse:collapse}dl.terms-and-definitions-list dd table td,dl.terms-and-definitions-list dd table th{padding:.5em;border:1px solid #ddd}dl.terms-and-definitions-list dd table th{background-color:#f5f5f5}dl>dd:has(table).meta-info-content-wrapper{display:block;font-size:.7em;position:relative;max-height:none;height:auto;overflow:visible;transition:all .3s ease-out}dl>dd:has(table).meta-info-content-wrapper td,dl>dd:has(table).meta-info-content-wrapper th{padding:.3em}dl>dd:has(table).collapsed.meta-info-content-wrapper{max-height:0;height:0;padding-top:0;padding-bottom:0;margin-top:0;margin-bottom:0;overflow:hidden;transition:all .3s ease-out;line-height:0}dl>dd:has(table).meta-info-content-wrapper>div{display:block;padding:inherit}.meta-info-toggle-button{position:relative;cursor:pointer}dl.terms-and-definitions-list dt+dd{border-bottom:none}dl.terms-and-definitions-list dd+dd{border-top:none;border-bottom:none}dl.terms-and-definitions-list dd+dt{margin-top:1rem}dl.terms-and-definitions-list dd.last-dd{border-radius:0 0 .375rem .375rem;border-bottom:1px solid var(--card-border)}html.dense-info dl.terms-and-definitions-list>dt{padding:.1rem 1.25rem;line-height:inherit;font-size:inherit;min-height:auto}html.dense-info dl.terms-and-definitions-list dd+dt{margin-top:.3rem}html.dense-info dl.terms-and-definitions-list>dt::before{display:none}html.dense-info dl.terms-and-definitions-list>dt>span{flex:1 1 auto;min-width:0}.term-external{position:relative}dl.terms-and-definitions-list>dd.term-external,dl.terms-and-definitions-list>dt.term-external{background:#a9dde03b}.term-local{position:relative}dl.terms-and-definitions-list>dd.term-local,dl.terms-and-definitions-list>dt.term-local{background:#f0f8ff}dl.terms-and-definitions-list .term-original-term{font-style:italic}dl.terms-and-definitions-list .term-external-parenthetical-terms{font-weight:400}dl.terms-and-definitions-list .term-local-original-term{font-style:italic}dl.terms-and-definitions-list .term-local-parenthetical-terms{font-weight:400}.btn{font-variant:small-caps;text-transform:uppercase;position:relative;z-index:5}.transclusion-heading{font-size:1.3em;font-weight:700;margin:.2em 0 .2em}dl.terms-and-definitions-list>dd.term-external-embedded{position:relative;padding-left:2.5rem;padding-top:.3rem;padding-bottom:.3rem}dl.terms-and-definitions-list span.term-external::after{content:"";position:absolute;top:50%;transform:translateY(-50%);background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' class='bi bi-box-arrow-right' viewBox='0 0 16 16' fill='currentColor'%3E%3Cpath fill-rule='evenodd' d='M10 12.5a.5.5 0 0 1-.5.5h-8a.5.5 0 0 1-.5-.5v-9a.5.5 0 0 1 .5-.5h8a.5.5 0 0 1 .5.5v2a.5.5 0 0 0 1 0v-2A1.5 1.5 0 0 0 9.5 2h-8A1.5 1.5 0 0 0 0 3.5v9A1.5 1.5 0 0 0 1.5 14h8a1.5 1.5 0 0 0 1.5-1.5v-2a.5.5 0 0 0-1 0v2z'/%3E%3Cpath fill-rule='evenodd' d='M15.854 8.354a.5.5 0 0 0 0-.708l-3-3a.5.5 0 0 0-.708.708L14.293 7.5H5.5a.5.5 0 0 0 0 1h8.793l-2.147 2.146a.5.5 0 0 0 .708.708l3-3z'/%3E%3C/svg%3E");background-size:contain;background-repeat:no-repeat;background-position:center;width:.8rem;height:.8rem;left:.2rem}dl.terms-and-definitions-list span.term-external{padding-left:1.5rem;display:inline-block}.placeholder-tref{display:none}.no-xref-found-message{background:#f8c481;color:#03365f;margin:0 .5em;padding:0 .8em;border-radius:.25rem;font-size:.8rem;font-variant:small-caps;text-transform:uppercase}
13
13
  .btn{font-size:.7em!important;margin:0 0 0 .5em!important}
14
14
  .highlight-matches-search{font-size:1em;outline:3px solid #1d6dae39;outline-offset:5px;transition:outline .3s ease-in-out}.highlight-matches-search.active{outline:3px solid #1d6dae;transition:outline .3s ease-in-out}.highlight-matches-DIF-search{background-color:#1d6dae11}.highlight-matches-ToIP-search{background-color:#a9dde0b0}.highlight-matches-BTC-search{background-color:#f19019a1}.highlight-matches-KERI-search{background-color:#b5d070d5}.highlight-matches-SSI-search{background-color:#ff0}.highlight-matches-GLEIF-search{background-color:#52dac6a6}
15
15
  .highlight-cfib41dyhcd99sm{background-color:#1d6dae11}
@@ -30,5 +30,5 @@ article a[href^="https://"]:not(.btn)::after,article a[href^=http]:not(.btn)::af
30
30
  #content .figure:has(.scrollHintImage){padding:0;margin:0;width:auto;height:auto}
31
31
  .highlight2{padding-left:1em;padding-right:1em;border:1px dashed #71bbe6;background:#a9dde03b}
32
32
  #toc ul.toc{counter-reset:toc-section}#toc ul.toc>li{counter-reset:toc-subsection}#toc ul.toc>li>ul>li{counter-reset:toc-subsubsection}#toc ul.toc>li>a::before{counter-increment:toc-section;content:counter(toc-section) ". ";font-weight:700;margin-right:.5em}#toc ul.toc>li>ul>li>a::before{counter-increment:toc-subsection;content:counter(toc-section) "." counter(toc-subsection) " ";font-weight:700;margin-right:.5em}#toc ul.toc>li>ul>li>ul>li>a::before{counter-increment:toc-subsubsection;content:counter(toc-section) "." counter(toc-subsection) "." counter(toc-subsubsection) " ";font-weight:700;margin-right:.5em}main article{counter-reset:main-section}main article h2{counter-reset:main-subsection}main article h3{counter-reset:main-subsubsection}main article h2 .toc-anchor::after{counter-increment:main-section;content:" " counter(main-section) ".";font-weight:700}main article h3 .toc-anchor::after{counter-increment:main-subsection;content:" " counter(main-section) "." counter(main-subsection);font-weight:700}main article h4 .toc-anchor::after{counter-increment:main-subsubsection;content:" " counter(main-section) "." counter(main-subsection) "." counter(main-subsubsection);font-weight:700}main article h5::before,main article h6::before{font-weight:700;margin-right:.5em}@media print{#toc ul.toc>li>a::before,#toc ul.toc>li>ul>li>a::before,#toc ul.toc>li>ul>li>ul>li>a::before,main article h2 .toc-anchor::after,main article h3 .toc-anchor::after,main article h4 .toc-anchor::after{color:#000!important}}
33
- .term-external-original-term{font-style:italic}.term-external-parenthetical-terms{font-weight:400}
33
+
34
34
  :target{scroll-margin-top:calc(50vh)}body:not([hashscroll]) :target{animation:highlight-target 3.5s .25s ease}body:not([hashscroll]) dt:has(:target){animation:highlight-target-parent-dt 3.5s .25s ease}dl.terms-and-definitions-list>.highlight{background-color:#ff0!important;transition:background-color .3s ease-in-out}svg[icon]{width:1.25em;height:1.25em;vertical-align:text-top}@keyframes highlight-target{50%{background-color:#ff0}}@keyframes highlight-target-parent-dt{50%{background-color:#ff0;border:1px solid #00c8ff}}#svg{display:none}main *{overflow-wrap:anywhere;word-break:normal}.table-responsive{overflow-x:auto!important;-webkit-overflow-scrolling:touch;width:100%;max-width:100%}.table-responsive *{overflow-wrap:normal!important;word-break:normal!important;white-space:nowrap!important}dd td img{max-width:30px}.notice{margin:1em 0;padding:.5em .9em .55em .65em;border-left:.5em solid}.notice p{margin:.4em 0 0}.note{background:#e9fbe9;border-color:#52e052}.note .notice-link{display:block;color:#178217}.issue{background:#e9f0fb;border-color:#527fe0}.issue .notice-link:before{display:block;color:#1e4cae}.warning{background:#fbe9e9;border-color:#e05252}.warning .notice-link{display:block;color:#ae1e1e}.example{color:#cebe00;background:#1a1e23;border-left:.5em solid}.example .notice-link{display:block;color:inherit;font-size:1.1em;font-family:Heebo,sans-serif}.example pre[class*=language-]{padding:0;border-radius:0}.todo{background:#fbe4ff;border-color:#9700e2}.todo .notice-link{display:block;color:#6d00a2}.alert-primary{background-color:#f9fcff;border-color:#b2ebf2;color:#00838f}.alert-secondary{background-color:#f5f5f5;border-color:#e0e0e0;color:#616161}.alert-success{background-color:#e8f5e9;border-color:#c8e6c9;color:#388e3c}.alert-danger{background-color:#ffebee;border-color:#ef9a9a;color:#d32f2f}.alert-warning{background-color:#fffde7;border-color:#fff9c4;color:#f9a825}.alert-info{background-color:#e1f5fe;border-color:#b3e5fc;color:#0288d1}.alert-light{background-color:#f9f9f9;border-color:#eee;color:#424242}.alert-dark{background-color:#e0e0e0;border-color:#bdbdbd;color:#212121}.alert a{color:inherit;font-weight:700}.alert-primary a{color:#006064}.alert-success a{color:#2e7d32}.alert-danger a{color:#b71c1c}.alert-warning a{color:#f57f17}.alert-info a{color:#01579b}.alert-dark a{color:#000}[data-bs-theme=dark] .alert-primary{background-color:#004d40;border-color:#00695c;color:#b2dfdb}[data-bs-theme=dark] .alert-secondary{background-color:#424242;border-color:#616161;color:#e0e0e0}[data-bs-theme=dark] .alert-success{background-color:#1b5e20;border-color:#2e7d32;color:#a5d6a7}[data-bs-theme=dark] .alert-danger{background-color:#b71c1c;border-color:#c62828;color:#ef9a9a}[data-bs-theme=dark] .alert-warning{background-color:#f57f17;border-color:#ffb300;color:#ffe082}[data-bs-theme=dark] .alert-info{background-color:#01579b;border-color:#0277bd;color:#81d4fa}[data-bs-theme=dark] .alert-light{background-color:#303030;border-color:#424242;color:#e0e0e0}[data-bs-theme=dark] .alert-dark{background-color:#212121;border-color:#424242;color:#bdbdbd}[data-bs-theme=dark] .alert a{color:inherit;font-weight:700}[data-bs-theme=dark] .alert-primary a{color:#80cbc4}[data-bs-theme=dark] .alert-success a{color:#81c784}[data-bs-theme=dark] .alert-danger a{color:#ef5350}[data-bs-theme=dark] .alert-warning a{color:#ffca28}[data-bs-theme=dark] .alert-info a{color:#4fc3f7}[data-bs-theme=dark] .alert-dark a{color:#9e9e9e}#offcanvasSettings .btn-menu-item{border-radius:unset;width:100%;text-align:left;border:1px solid #dee2e6;background-color:var(--card-bg);color:var(--card-text);padding:10px 15px;margin-bottom:5px;transition:background-color .2s ease}#offcanvasSettings .btn-menu-item:hover{background-color:#e9ecef}
@@ -1,7 +1 @@
1
- .term-external-original-term {
2
- font-style: italic;
3
- }
4
-
5
- .term-external-parenthetical-terms {
6
- font-weight: normal;
7
- }
1
+ /* Currently empty */
@@ -282,6 +282,15 @@ dl.terms-and-definitions-list>dd.term-local {
282
282
  background: #f0f8ff;
283
283
  }
284
284
 
285
+ /* Generic class for all original terms (both local and external) */
286
+ dl.terms-and-definitions-list .term-original-term {
287
+ font-style: italic;
288
+ }
289
+
290
+ dl.terms-and-definitions-list .term-external-parenthetical-terms {
291
+ font-weight: normal;
292
+ }
293
+
285
294
  /* Local term original term and parenthetical styling */
286
295
  dl.terms-and-definitions-list .term-local-original-term {
287
296
  font-style: italic;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "spec-up-t",
3
- "version": "1.4.1-beta.1",
3
+ "version": "1.4.1-beta.3",
4
4
  "description": "Technical specification drafting tool that generates rich specification documents from markdown. Forked from https://github.com/decentralized-identity/spec-up by Daniel Buchner (https://github.com/csuwildcat)",
5
5
  "main": "./index",
6
6
  "repository": {
@@ -44,3 +44,7 @@ const destFile = path.join(newVersionDir, 'index.html');
44
44
  fs.copyFileSync(sourceFile, destFile);
45
45
 
46
46
  Logger.success(`Created a freezed specification version in ${destFile}`);
47
+
48
+ // Update the versions index.html to include the newly created version
49
+ const createVersionsIndex = require('./pipeline/configuration/create-versions-index.js');
50
+ createVersionsIndex(outputPath);
package/src/freeze.js ADDED
@@ -0,0 +1,39 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * freeze.js
4
+ *
5
+ * Legacy wrapper to run the existing src/freeze-spec-data.js script.
6
+ * This file intentionally keeps logic minimal: it simply requires the
7
+ * implementation file so older tooling or CI jobs that call `freeze.js`
8
+ * continue to work without changing behaviour.
9
+ *
10
+ * Usage:
11
+ * node freeze.js
12
+ * or make it executable and run:
13
+ * ./freeze.js
14
+ *
15
+ * Rationale for keeping this file:
16
+ * - It provides a stable legacy entry point used by external consumers.
17
+ * - The real implementation remains in src/freeze-spec-data.js so maintenance
18
+ * and tests remain focused there.
19
+ */
20
+
21
+ const path = require('path');
22
+
23
+ // The implementation `freeze-spec-data.js` now lives in the same `src` folder
24
+ // as this wrapper, so require it directly from __dirname.
25
+ const scriptPath = path.join(__dirname, 'freeze-spec-data.js');
26
+
27
+ try {
28
+ // Require executes the implementation file. It is expected to run
29
+ // synchronously at top-level (the existing file performs its actions
30
+ // when loaded).
31
+ require(scriptPath);
32
+ } catch (err) {
33
+ // Keep error reporting minimal and consistent with CLI usage.
34
+ // Use stderr for messages and exit with non-zero code on failure.
35
+ // This keeps behaviour predictable for callers.
36
+ // eslint-disable-next-line no-console
37
+ console.error('Failed to execute freeze script:', err && err.message ? err.message : err);
38
+ process.exitCode = 1;
39
+ }
@@ -0,0 +1,306 @@
1
+ /**
2
+ * Template Tag Parser HTML Output Tests
3
+ *
4
+ * These tests verify that the HTML output contains all required CSS classes
5
+ * and structural elements that are essential for proper rendering and styling.
6
+ *
7
+ * These tests were added after discovering that shallow `.toContain()` assertions
8
+ * were insufficient to catch missing CSS classes that broke functionality.
9
+ */
10
+
11
+ const {
12
+ parseDef,
13
+ parseRef,
14
+ parseXref,
15
+ parseTref
16
+ } = require('./template-tag-parser');
17
+
18
+ describe('Template Tag Parser - HTML Output Validation', () => {
19
+ let mockConfig, mockGlobal;
20
+
21
+ beforeEach(() => {
22
+ mockConfig = {
23
+ specs: [{
24
+ external_specs: [{
25
+ external_spec: 'test-spec',
26
+ gh_page: 'https://example.com/spec'
27
+ }]
28
+ }]
29
+ };
30
+
31
+ mockGlobal = {
32
+ definitions: [],
33
+ references: [],
34
+ specGroups: {},
35
+ noticeTitles: {},
36
+ currentFile: 'test.md'
37
+ };
38
+
39
+ global.definitions = mockGlobal.definitions;
40
+ global.references = mockGlobal.references;
41
+ global.specGroups = mockGlobal.specGroups;
42
+ global.noticeTitles = mockGlobal.noticeTitles;
43
+ });
44
+
45
+ describe('parseDef - HTML structure validation', () => {
46
+ // Test: Does def output contain the required CSS class for styling?
47
+ test('should include term-original-term class when def has aliases', () => {
48
+ const mockToken = {
49
+ info: { args: ['test-term', 'alias1'] }
50
+ };
51
+
52
+ const result = parseDef(mockGlobal, mockToken, 'Test Term', 'test.md');
53
+
54
+ // Critical: This class is required for CSS styling
55
+ expect(result).toContain('term-original-term');
56
+ expect(result).toContain('term-local-original-term');
57
+ });
58
+
59
+ test('should include term-original-term class when def has no aliases', () => {
60
+ const mockToken = {
61
+ info: { args: ['test-term'] }
62
+ };
63
+
64
+ const result = parseDef(mockGlobal, mockToken, 'Test Term', 'test.md');
65
+
66
+ // Even without aliases, the original term should have proper class
67
+ expect(result).toContain('term-original-term');
68
+ expect(result).toContain('term-local-original-term');
69
+ });
70
+
71
+ test('should include term-local-parenthetical-terms class for multiple aliases', () => {
72
+ const mockToken = {
73
+ info: { args: ['test-term', 'alias1', 'alias2', 'alias3'] }
74
+ };
75
+
76
+ const result = parseDef(mockGlobal, mockToken, 'Test Term', 'test.md');
77
+
78
+ expect(result).toContain('term-local-parenthetical-terms');
79
+ });
80
+
81
+ test('should display primary alias first, then other aliases, then original term', () => {
82
+ const mockToken = {
83
+ info: { args: ['original-term', 'primary-alias', 'second-alias'] }
84
+ };
85
+
86
+ const result = parseDef(mockGlobal, mockToken, 'Test Term', 'test.md');
87
+
88
+ // Primary alias should appear first (not in parentheses) in the innermost span
89
+ expect(result).toMatch(/>primary-alias <span class='term-local-parenthetical-terms'>/);
90
+
91
+ // Second alias should appear in parentheses
92
+ expect(result).toContain('second-alias');
93
+
94
+ // Original term should appear last in parentheses with proper class
95
+ expect(result).toContain('<span class=\'term-local-original-term term-original-term\' title=\'original term\'>original-term</span>');
96
+ });
97
+
98
+ test('should have nested span structure with all IDs', () => {
99
+ const mockToken = {
100
+ info: { args: ['term1', 'alias1', 'alias2'] }
101
+ };
102
+
103
+ const result = parseDef(mockGlobal, mockToken, 'Test Term', 'test.md');
104
+
105
+ // Check all IDs are present
106
+ expect(result).toContain('id="term:term1"');
107
+ expect(result).toContain('id="term:alias1"');
108
+ expect(result).toContain('id="term:alias2"');
109
+
110
+ // Check nested structure (outer span has last alias ID)
111
+ expect(result).toMatch(/^<span id="term:alias2">/);
112
+ });
113
+ });
114
+
115
+ describe('parseTref - HTML structure validation', () => {
116
+ // Test: Does tref output contain the required CSS classes for styling?
117
+ test('should include term-external-original-term and term-original-term classes when tref has aliases', () => {
118
+ const mockToken = {
119
+ info: { args: ['external-spec', 'test-term', 'alias1'] }
120
+ };
121
+
122
+ const result = parseTref(mockToken);
123
+
124
+ // Critical: These classes are required for CSS styling
125
+ expect(result).toContain('term-original-term');
126
+ expect(result).toContain('term-external-original-term');
127
+ });
128
+
129
+ test('should include term-original-term class when tref has no aliases', () => {
130
+ const mockToken = {
131
+ info: { args: ['external-spec', 'test-term'] }
132
+ };
133
+
134
+ const result = parseTref(mockToken);
135
+
136
+ // Even without aliases, the original term should have proper class
137
+ expect(result).toContain('term-original-term');
138
+ expect(result).toContain('term-external-original-term');
139
+ });
140
+
141
+ test('should include term-external-parenthetical-terms class for multiple aliases', () => {
142
+ const mockToken = {
143
+ info: { args: ['external-spec', 'test-term', 'alias1', 'alias2', 'alias3'] }
144
+ };
145
+
146
+ const result = parseTref(mockToken);
147
+
148
+ expect(result).toContain('term-external-parenthetical-terms');
149
+ });
150
+
151
+ test('should include term-external class on outer span', () => {
152
+ const mockToken = {
153
+ info: { args: ['external-spec', 'test-term', 'alias1'] }
154
+ };
155
+
156
+ const result = parseTref(mockToken);
157
+
158
+ expect(result).toContain('class="term-external"');
159
+ });
160
+
161
+ test('should display primary alias first, then other aliases, then original term', () => {
162
+ const mockToken = {
163
+ info: { args: ['spec', 'original-term', 'primary-alias', 'second-alias'] }
164
+ };
165
+
166
+ const result = parseTref(mockToken);
167
+
168
+ // Primary alias should appear first (not in parentheses)
169
+ expect(result).toContain('>primary-alias <span');
170
+
171
+ // Second alias should appear in parentheses
172
+ expect(result).toContain('second-alias');
173
+
174
+ // Original term should appear last in parentheses with proper class
175
+ expect(result).toContain('<span class=\'term-external-original-term term-original-term\' title=\'original term\'>original-term</span>');
176
+ });
177
+
178
+ test('should have data-original-term attribute', () => {
179
+ const mockToken = {
180
+ info: { args: ['spec', 'test-term', 'alias1'] }
181
+ };
182
+
183
+ const result = parseTref(mockToken);
184
+
185
+ expect(result).toContain('data-original-term="test-term"');
186
+ });
187
+
188
+ test('should have proper title attribute on inner span when alias exists', () => {
189
+ const mockToken = {
190
+ info: { args: ['spec', 'test-term', 'alias1'] }
191
+ };
192
+
193
+ const result = parseTref(mockToken);
194
+
195
+ expect(result).toContain('title="Externally defined as test-term"');
196
+ });
197
+ });
198
+
199
+ describe('parseXref - HTML structure validation', () => {
200
+ test('should include x-term-reference and term-reference classes', () => {
201
+ const mockToken = {
202
+ info: { args: ['test-spec', 'test-term'] }
203
+ };
204
+
205
+ const result = parseXref(mockConfig, mockToken);
206
+
207
+ expect(result).toContain('class="x-term-reference term-reference"');
208
+ });
209
+
210
+ test('should display primary alias instead of term when alias provided', () => {
211
+ const mockToken = {
212
+ info: { args: ['test-spec', 'test-term', 'my-alias'] }
213
+ };
214
+
215
+ const result = parseXref(mockConfig, mockToken);
216
+
217
+ // Should show alias, not the term
218
+ expect(result).toContain('>my-alias</a>');
219
+ expect(result).not.toContain('>test-term</a>');
220
+ });
221
+
222
+ test('should display term when no alias provided', () => {
223
+ const mockToken = {
224
+ info: { args: ['test-spec', 'test-term'] }
225
+ };
226
+
227
+ const result = parseXref(mockConfig, mockToken);
228
+
229
+ expect(result).toContain('>test-term</a>');
230
+ });
231
+
232
+ test('should have both data-local-href and href attributes', () => {
233
+ const mockToken = {
234
+ info: { args: ['test-spec', 'test-term'] }
235
+ };
236
+
237
+ const result = parseXref(mockConfig, mockToken);
238
+
239
+ expect(result).toContain('data-local-href="#term:test-spec:test-term"');
240
+ expect(result).toContain('href="https://example.com/spec#term:test-term"');
241
+ });
242
+ });
243
+
244
+ describe('parseRef - HTML structure validation', () => {
245
+ test('should include term-reference class', () => {
246
+ const result = parseRef(mockGlobal, 'test-term');
247
+
248
+ expect(result).toContain('class="term-reference"');
249
+ });
250
+
251
+ test('should create proper href with term: prefix', () => {
252
+ const result = parseRef(mockGlobal, 'test-term');
253
+
254
+ expect(result).toContain('href="#term:test-term"');
255
+ });
256
+
257
+ test('should display the term as link text', () => {
258
+ const result = parseRef(mockGlobal, 'my-test-term');
259
+
260
+ expect(result).toContain('>my-test-term</a>');
261
+ });
262
+ });
263
+
264
+ describe('Regression tests - ensure critical classes are never removed', () => {
265
+ // These tests protect against the specific bug that was fixed
266
+ test('def: term-original-term class must always be present', () => {
267
+ const withAlias = { info: { args: ['term', 'alias'] } };
268
+ const withoutAlias = { info: { args: ['term'] } };
269
+
270
+ expect(parseDef(mockGlobal, withAlias, 'Test', 'test.md')).toContain('term-original-term');
271
+ expect(parseDef(mockGlobal, withoutAlias, 'Test', 'test.md')).toContain('term-original-term');
272
+ });
273
+
274
+ test('def: term-local-original-term class must always be present', () => {
275
+ const withAlias = { info: { args: ['term', 'alias'] } };
276
+ const withoutAlias = { info: { args: ['term'] } };
277
+
278
+ expect(parseDef(mockGlobal, withAlias, 'Test', 'test.md')).toContain('term-local-original-term');
279
+ expect(parseDef(mockGlobal, withoutAlias, 'Test', 'test.md')).toContain('term-local-original-term');
280
+ });
281
+
282
+ test('tref: term-original-term class must always be present', () => {
283
+ const withAlias = { info: { args: ['spec', 'term', 'alias'] } };
284
+ const withoutAlias = { info: { args: ['spec', 'term'] } };
285
+
286
+ expect(parseTref(withAlias)).toContain('term-original-term');
287
+ expect(parseTref(withoutAlias)).toContain('term-original-term');
288
+ });
289
+
290
+ test('tref: term-external-original-term class must always be present', () => {
291
+ const withAlias = { info: { args: ['spec', 'term', 'alias'] } };
292
+ const withoutAlias = { info: { args: ['spec', 'term'] } };
293
+
294
+ expect(parseTref(withAlias)).toContain('term-external-original-term');
295
+ expect(parseTref(withoutAlias)).toContain('term-external-original-term');
296
+ });
297
+
298
+ test('tref: term-external class must always be present on outer span', () => {
299
+ const withAlias = { info: { args: ['spec', 'term', 'alias'] } };
300
+ const withoutAlias = { info: { args: ['spec', 'term'] } };
301
+
302
+ expect(parseTref(withAlias)).toContain('class="term-external"');
303
+ expect(parseTref(withoutAlias)).toContain('class="term-external"');
304
+ });
305
+ });
306
+ });
@@ -89,15 +89,18 @@ function parseDef(globalState, token, primary, currentFile) {
89
89
  parentheticalContent.push(...aliases.slice(1));
90
90
  }
91
91
 
92
- // Add original term if it's different from the primary display term
93
- if (termName !== primaryDisplayTerm) {
94
- parentheticalContent.push(`<span class='term-local-original-term' title='original term'>${termName}</span>`);
95
- }
92
+ // Always add the original term if there are aliases, with special styling
93
+ // This ensures consistent behavior regardless of whether alias1 equals the term
94
+ // The original term should always be visible in the parenthetical list
95
+ parentheticalContent.push(`<span class='term-local-original-term term-original-term' title='original term'>${termName}</span>`);
96
96
 
97
97
  // Append parenthetical terms if any exist
98
98
  if (parentheticalContent.length > 0) {
99
99
  displayText += ` <span class='term-local-parenthetical-terms'>(${parentheticalContent.join(', ')})</span>`;
100
100
  }
101
+ } else {
102
+ // No aliases: wrap the term name itself as the original term
103
+ displayText = `<span class='term-local-original-term term-original-term' title='original term'>${termName}</span>`;
101
104
  }
102
105
 
103
106
  // Generate HTML spans for each term/alias combination
@@ -183,26 +186,36 @@ function parseTref(token) {
183
186
  parentheticalContent.push(...aliases.slice(1));
184
187
  }
185
188
 
186
- // Add original term if it's different from the primary display term
187
- if (termName !== primaryDisplayTerm) {
188
- parentheticalContent.push(`<span class='term-external-original-term' title='original term'>${termName}</span>`);
189
- }
189
+ // Always add the original term if there are aliases, with special styling
190
+ // This ensures consistent behavior regardless of whether alias1 equals the term
191
+ // The original term should always be visible in the parenthetical list
192
+ parentheticalContent.push(`<span class='term-external-original-term term-original-term' title='original term'>${termName}</span>`);
190
193
 
191
194
  // Append parenthetical terms if any exist
192
195
  if (parentheticalContent.length > 0) {
193
196
  displayText += ` <span class='term-external-parenthetical-terms'>(${parentheticalContent.join(', ')})</span>`;
194
197
  }
198
+ } else {
199
+ // No aliases: wrap the term name itself as the original term
200
+ displayText = `<span class='term-external-original-term term-original-term' title='original term'>${termName}</span>`;
195
201
  }
196
202
 
197
- const termId = `term:${termName.replace(whitespace.oneOrMore, '-').toLowerCase()}`;
198
- const primaryAliasId = aliases.length > 0 ? `term:${aliases[0].replace(whitespace.oneOrMore, '-').toLowerCase()}` : '';
203
+ // Generate HTML spans for each term/alias combination, similar to parseDef
204
+ // This creates anchor points that can be referenced by [[ref: ...]] links
205
+ // token.info.args for tref is [externalSpec, term, alias1, alias2, ...]
206
+ // We need to create IDs for the term and all aliases (skip the externalSpec at index 0)
207
+ const termsAndAliases = [termName, ...aliases];
199
208
 
200
- // Handle cases where we have aliases
201
- if (aliases.length > 0 && aliases[0] !== termName) {
202
- return `<span data-original-term="${termName}" class="term-external" id="${termId}"><span title="Externally defined as ${termName}" id="${primaryAliasId}">${displayText}</span></span>`;
203
- } else {
204
- return `<span title="Externally also defined as ${termName}" data-original-term="${termName}" class="term-external" id="${termId}">${displayText}</span>`;
205
- }
209
+ return termsAndAliases.reduce((acc, syn, index) => {
210
+ // Generate a unique term ID by normalizing the synonym: replace whitespace with hyphens and convert to lowercase
211
+ const termId = `term:${syn.replace(whitespace.oneOrMore, '-').toLowerCase()}`;
212
+ // Add title attribute to the innermost span (first in the array, which wraps the display text directly)
213
+ // This provides a tooltip showing which external term this alias refers to
214
+ const titleAttr = index === 0 && aliases.length > 0 ? ` title="Externally defined as ${termName}"` : '';
215
+ // Add class and data attributes only to the outermost span (last one created)
216
+ const outerAttrs = index === termsAndAliases.length - 1 ? ` data-original-term="${termName}" class="term-external"` : '';
217
+ return `<span id="${termId}"${outerAttrs}${titleAttr}>${acc}</span>`;
218
+ }, displayText);
206
219
  }
207
220
 
208
221
  /**
@@ -231,7 +244,7 @@ function processXTrefObject(xtref) {
231
244
 
232
245
  // Collect all aliases from parts after the term (index 1), trim and filter empties
233
246
  const allAliases = parts.slice(2).map(p => p.trim()).filter(Boolean);
234
-
247
+
235
248
  // Initialize both tref and xref alias arrays
236
249
  xtrefObject.trefAliases = [];
237
250
  xtrefObject.xrefAliases = [];
@@ -62,6 +62,33 @@ describe('Template Tag Parsing Functions', () => {
62
62
  });
63
63
  });
64
64
 
65
+ // Test: Does the parser handle the case when alias1 equals the term?
66
+ // Issue #194: When alias1 is the same as the term, the term should still appear in parentheses
67
+ test('should include term in parentheses even when alias1 equals term', () => {
68
+ const mockToken = {
69
+ info: { args: ['action', 'action', 'actions', 'act', 'acts'] }
70
+ };
71
+
72
+ const result = parseDef(mockGlobal, mockToken, 'Action', 'test.md');
73
+
74
+ // Should show: action (actions, act, acts, action)
75
+ // The term "action" should appear both as primary and in parenthetical list
76
+ expect(result).toContain('action');
77
+ expect(result).toContain('actions');
78
+ expect(result).toContain('act');
79
+ expect(result).toContain('acts');
80
+
81
+ // The original term should be in the parenthetical content with special styling
82
+ expect(result).toContain('term-local-original-term');
83
+ expect(result).toContain('term-local-parenthetical-terms');
84
+
85
+ // Verify all term IDs are present
86
+ expect(result).toContain('id="term:action"');
87
+ expect(result).toContain('id="term:actions"');
88
+ expect(result).toContain('id="term:act"');
89
+ expect(result).toContain('id="term:acts"');
90
+ });
91
+
65
92
  // Test: Can the system parse reference markup into proper links?
66
93
  test('should parse reference correctly', () => {
67
94
  const result = parseRef(mockGlobal, 'test-term');
@@ -118,22 +118,24 @@ async function mergeXrefTermsIntoAllXTrefs(xrefTerms, outputPathJSON, outputPath
118
118
  // Add xref terms to the allXTrefs structure
119
119
  // Mark them with source: 'xref' to distinguish from tref entries
120
120
  xrefTerms.forEach(xrefTerm => {
121
- // Check if this term already exists (avoid duplicates)
121
+ // Check if this term already exists (match by externalSpec and term only)
122
+ // Don't filter by source because entries from markdown scanning don't have source field
122
123
  const existingIndex = allXTrefs.xtrefs.findIndex(existing =>
123
124
  existing.externalSpec === xrefTerm.externalSpec &&
124
- existing.term === xrefTerm.term &&
125
- existing.source === 'xref'
125
+ existing.term === xrefTerm.term
126
126
  );
127
127
 
128
128
  if (existingIndex >= 0) {
129
- // Update existing entry
129
+ // Update existing entry - preserve the existing metadata but add/update content
130
130
  allXTrefs.xtrefs[existingIndex] = {
131
131
  ...allXTrefs.xtrefs[existingIndex],
132
- ...xrefTerm,
132
+ content: xrefTerm.content, // Update the content from fetched HTML
133
+ source: xrefTerm.source, // Add source field
134
+ termId: xrefTerm.termId, // Add termId if not present
133
135
  lastUpdated: new Date().toISOString()
134
136
  };
135
137
  } else {
136
- // Add new entry
138
+ // Add new entry (this term wasn't referenced in the markdown)
137
139
  allXTrefs.xtrefs.push({
138
140
  ...xrefTerm,
139
141
  lastUpdated: new Date().toISOString()
@@ -179,28 +181,53 @@ function extractTermsFromHtml(externalSpec, html) {
179
181
  batch.each((index, termElement) => {
180
182
  try {
181
183
  const $termElement = $(termElement);
182
- const termId = $termElement.attr('id');
183
184
 
184
- // Skip elements without an id attribute or with invalid id format
185
- if (!termId || !termId.includes('term:')) {
185
+ // The id can be on the dt element itself OR on span(s) inside it
186
+ // Some terms have multiple nested spans with different IDs (e.g., aliases)
187
+ // We need to extract ALL term IDs from the dt element
188
+ const termIds = [];
189
+
190
+ // First check if dt itself has an id
191
+ const dtId = $termElement.attr('id');
192
+ if (dtId && dtId.includes('term:')) {
193
+ termIds.push(dtId.replace('term:', ''));
194
+ }
195
+
196
+ // Then find all spans with ids that contain 'term:'
197
+ $termElement.find('span[id*="term:"]').each((i, span) => {
198
+ const spanId = $(span).attr('id');
199
+ if (spanId && spanId.includes('term:')) {
200
+ const termName = spanId.replace('term:', '');
201
+ if (!termIds.includes(termName)) {
202
+ termIds.push(termName);
203
+ }
204
+ }
205
+ });
206
+
207
+ // Skip if no valid term IDs found
208
+ if (termIds.length === 0) {
186
209
  return;
187
210
  }
188
211
 
189
- const termName = termId.replace('term:', '');
190
212
  const dd = $termElement.next('dd');
191
213
 
192
214
  if (dd.length > 0) {
193
- // Create term object compatible with allXTrefs structure
194
- const termObj = {
195
- externalSpec: externalSpec,
196
- term: termName,
197
- content: $.html(dd), // Store the complete DD content
198
- // Add metadata for consistency with tref structure
199
- source: 'xref', // Distinguish from tref entries
200
- termId: `term:${externalSpec}:${termName}`, // Fully qualified term ID
201
- };
202
-
203
- terms.push(termObj);
215
+ const ddContent = $.html(dd); // Store the complete DD content once
216
+
217
+ // Create a term object for each ID found in this dt element
218
+ // This handles cases where one term definition has multiple aliases
219
+ termIds.forEach(termName => {
220
+ const termObj = {
221
+ externalSpec: externalSpec,
222
+ term: termName,
223
+ content: ddContent,
224
+ // Add metadata for consistency with tref structure
225
+ source: 'xref', // Distinguish from tref entries
226
+ termId: `term:${externalSpec}:${termName}`, // Fully qualified term ID
227
+ };
228
+
229
+ terms.push(termObj);
230
+ });
204
231
  }
205
232
  } catch (termError) {
206
233
  Logger.warn(`Error processing term in ${externalSpec}:`, termError.message);
@@ -37,7 +37,10 @@ async function processXTrefsData(allXTrefs, GITHUB_API_TOKEN, outputPathJSON, ou
37
37
 
38
38
  for (const repoKey of Object.keys(xrefsByRepo)) {
39
39
  const repoGroup = xrefsByRepo[repoKey];
40
- Logger.process(`Processing repository: ${repoKey} (${repoGroup.xtrefs.length} terms)`);
40
+ // Build a repository URL for logging. Prefer an explicit repoUrl from
41
+ // an xtref, otherwise fall back to the canonical GitHub URL.
42
+ const repoUrl = repoGroup.xtrefs[0]?.repoUrl || `https://github.com/${repoKey}`;
43
+ Logger.process(`Processing repository: ${repoKey} (${repoGroup.xtrefs.length} terms) - ${repoUrl}`);
41
44
 
42
45
  const ghPageUrl = repoGroup.xtrefs[0]?.ghPageUrl;
43
46
  const allTermsData = await fetchAllTermsFromIndex(
@@ -48,7 +51,7 @@ async function processXTrefsData(allXTrefs, GITHUB_API_TOKEN, outputPathJSON, ou
48
51
  );
49
52
 
50
53
  if (!allTermsData) {
51
- Logger.error(`Could not fetch terms from repository ${repoKey}`);
54
+ Logger.error(`Could not fetch terms from repository ${repoKey} (${repoUrl})`);
52
55
  repoGroup.xtrefs.forEach(xtref => {
53
56
  xtref.commitHash = 'not found';
54
57
  xtref.content = 'This term was not found in the external repository.';
@@ -71,11 +74,30 @@ async function processXTrefsData(allXTrefs, GITHUB_API_TOKEN, outputPathJSON, ou
71
74
  xtref.commitHash = 'not found';
72
75
  xtref.content = 'This term was not found in the external repository.';
73
76
  xtref.avatarUrl = null;
74
- Logger.error(`Origin: ${xtref.sourceFile || xtref.sourceFiles.join(', ')} 👉 No match found for term: ${xtref.term} in ${xtref.externalSpec} (${repoKey})`);
77
+
78
+ // Build a readable list of source files for the error message.
79
+ // Two possible data structures exist:
80
+ // 1. xtref.sourceFile is a STRING like "primitive.md"
81
+ // 2. xtref.sourceFiles is an ARRAY OF OBJECTS like [{file: "primitive.md", type: "xref"}]
82
+ //
83
+ // The ternary operator works as follows:
84
+ // - If xtref.sourceFile exists (legacy case) → use it directly (it's already a string)
85
+ // - Otherwise → extract file names from the sourceFiles array:
86
+ // - .map(sf => sf.file) extracts just the filename from each object
87
+ // - .join(', ') combines them into a comma-separated string
88
+ const sourceFilesList = xtref.sourceFile
89
+ ? xtref.sourceFile
90
+ : (xtref.sourceFiles || []).map(sf => sf.file).join(', ');
91
+
92
+ // Prefer an explicit repo URL if provided on the xtref, otherwise
93
+ // build a standard GitHub URL from the owner/repo.
94
+ const githubUrl = xtref.repoUrl || `https://github.com/${repoKey}`;
95
+
96
+ Logger.error(`Origin: ${sourceFilesList} 👉 No match found for term: ${xtref.term} in ${xtref.externalSpec} (${githubUrl})`);
75
97
  }
76
98
  }
77
99
 
78
- Logger.success(`Finished processing repository: ${repoKey}`);
100
+ Logger.success(`Finished processing repository: ${repoKey} (${repoUrl})`);
79
101
  Logger.separator();
80
102
  }
81
103
 
@@ -51,7 +51,7 @@ class Logger {
51
51
  * Highlight important data - magenta
52
52
  */
53
53
  static highlight(message, ...args) {
54
- console.log(chalk.magenta('✨'), chalk.magenta(message), ...args);
54
+ console.log(chalk.blue('✨'), chalk.blue(message), ...args);
55
55
  }
56
56
 
57
57
  /**