spec-up-t 1.4.1-beta.2 → 1.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "spec-up-t",
3
- "version": "1.4.1-beta.2",
3
+ "version": "1.4.1",
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
+ }
@@ -200,15 +200,22 @@ function parseTref(token) {
200
200
  displayText = `<span class='term-external-original-term term-original-term' title='original term'>${termName}</span>`;
201
201
  }
202
202
 
203
- const termId = `term:${termName.replace(whitespace.oneOrMore, '-').toLowerCase()}`;
204
- const primaryAliasId = aliases.length > 0 ? `term:${aliases[0].replace(whitespace.oneOrMore, '-').toLowerCase()}` : '';
205
-
206
- // Handle cases where we have aliases
207
- if (aliases.length > 0 && aliases[0] !== termName) {
208
- return `<span data-original-term="${termName}" class="term-external" id="${termId}"><span title="Externally defined as ${termName}" id="${primaryAliasId}">${displayText}</span></span>`;
209
- } else {
210
- return `<span title="Externally also defined as ${termName}" data-original-term="${termName}" class="term-external" id="${termId}">${displayText}</span>`;
211
- }
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];
208
+
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);
212
219
  }
213
220
 
214
221
  /**
@@ -237,7 +244,7 @@ function processXTrefObject(xtref) {
237
244
 
238
245
  // Collect all aliases from parts after the term (index 1), trim and filter empties
239
246
  const allAliases = parts.slice(2).map(p => p.trim()).filter(Boolean);
240
-
247
+
241
248
  // Initialize both tref and xref alias arrays
242
249
  xtrefObject.trefAliases = [];
243
250
  xtrefObject.xrefAliases = [];
@@ -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);