@glossarist/concept-browser 0.7.118 → 0.7.119

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": "@glossarist/concept-browser",
3
- "version": "0.7.118",
3
+ "version": "0.7.119",
4
4
  "description": "Vue SPA for browsing Glossarist terminology datasets with cross-reference resolution, graph visualization, and multi-language support",
5
5
  "type": "module",
6
6
  "bin": {
@@ -42,7 +42,7 @@
42
42
  "astro": "^7.0.7",
43
43
  "d3": "^7.9.0",
44
44
  "favicons": "^7.2.0",
45
- "glossarist": "^0.4.52",
45
+ "glossarist": "^0.4.54",
46
46
  "js-yaml": "^4.1.0",
47
47
  "jszip": "^3.10.1",
48
48
  "n3": "^1.26.0",
@@ -157,6 +157,16 @@ function extractRelated(concept, registerId, uriBase, urnMap) {
157
157
  const edges = [];
158
158
  const sourceUri = concept["@id"];
159
159
  for (const r of concept["gl:related"] || []) {
160
+ if (r["gl:target"]) {
161
+ edges.push({
162
+ source: sourceUri,
163
+ target: r["gl:target"],
164
+ type: r["gl:relationshipType"] || "references",
165
+ label: r["gl:term"] || void 0,
166
+ register: r["gl:target"].match(/\/([^/]+)\/concept\//)?.[1] || registerId
167
+ });
168
+ continue;
169
+ }
160
170
  const ref = r["gl:ref"];
161
171
  if (!ref) continue;
162
172
  const source = ref["gl:source"] || ref["source"];
@@ -382,6 +392,9 @@ function main() {
382
392
  const base = alias.endsWith("*") ? alias.slice(0, -1) : alias;
383
393
  if (base) urnMap.set(base, ds);
384
394
  }
395
+ if (manifest.ref) {
396
+ urnMap.set(manifest.ref, ds);
397
+ }
385
398
  } catch {
386
399
  }
387
400
  }
@@ -173,6 +173,19 @@ function extractRelated(concept, registerId, uriBase, urnMap) {
173
173
  const edges = [];
174
174
  const sourceUri = concept['@id'];
175
175
  for (const r of concept['gl:related'] || []) {
176
+ // Prefer gl:target — already resolved at generate-data build time
177
+ // via refPrefixMap (maps dataset ref labels to dataset IDs).
178
+ if (r['gl:target']) {
179
+ edges.push({
180
+ source: sourceUri,
181
+ target: r['gl:target'],
182
+ type: r['gl:relationshipType'] || 'references',
183
+ label: r['gl:term'] || undefined,
184
+ register: r['gl:target'].match(/\/([^/]+)\/concept\//)?.[1] || registerId,
185
+ });
186
+ continue;
187
+ }
188
+ // Fallback: resolve from gl:ref using urnMap
176
189
  const ref = r['gl:ref'];
177
190
  if (!ref) continue;
178
191
  const source = ref['gl:source'] || ref['source'];
@@ -458,6 +471,12 @@ for (const ds of datasets) {
458
471
  const base = alias.endsWith('*') ? alias.slice(0, -1) : alias;
459
472
  if (base) urnMap.set(base, ds);
460
473
  }
474
+ // Map the dataset's ref label (e.g. "CIE S 017:2020") to its dataset ID.
475
+ // This enables cross-dataset resolution for gl:ref entries that use
476
+ // source labels instead of URIs.
477
+ if (manifest.ref) {
478
+ urnMap.set(manifest.ref, ds);
479
+ }
461
480
  } catch {}
462
481
  }
463
482
  console.log(`URI resolution map: ${[...urnMap.entries()].map(([k,v]) => `${k}→${v}`).join(', ')}\n`);
@@ -0,0 +1,45 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { conceptFromJson } from '../../adapters/model-bridge';
3
+
4
+ function makeJsonLdWithRelated(target: string | null) {
5
+ const related = target
6
+ ? {
7
+ 'gl:relationshipType': 'superseded_by',
8
+ 'gl:target': target,
9
+ 'gl:ref': { 'gl:source': 'CIE S 017:2020', 'gl:id': '17-21-097' },
10
+ }
11
+ : {
12
+ 'gl:relationshipType': 'superseded_by',
13
+ 'gl:ref': { 'gl:source': 'CIE S 017:2020', 'gl:id': '17-21-097' },
14
+ };
15
+ return {
16
+ '@type': 'skos:Concept',
17
+ '@id': 'https://example.com/cie-2011/concept/17-50',
18
+ 'gl:identifier': '17-50',
19
+ 'gl:localizedConcept': {
20
+ eng: {
21
+ 'gl:languageCode': 'eng',
22
+ 'gl:designation': [{ '@type': 'gl:Expression', 'gl:term': 'test term', 'gl:normativeStatus': 'preferred' }],
23
+ 'gl:definition': [{ 'gl:content': 'test definition' }],
24
+ },
25
+ },
26
+ 'gl:related': [related],
27
+ };
28
+ }
29
+
30
+ describe('cross-dataset related concept resolution — gl:target flows through model-bridge natively', () => {
31
+ it('concept.relatedConcepts entries with gl:target have native target field', () => {
32
+ const json = makeJsonLdWithRelated('https://example.com/cie-2020/concept/17-21-097');
33
+ const concept = conceptFromJson(json as any);
34
+ expect(concept.relatedConcepts).toHaveLength(1);
35
+ expect(concept.relatedConcepts[0].type).toBe('superseded_by');
36
+ expect(concept.relatedConcepts[0].target).toBe('https://example.com/cie-2020/concept/17-21-097');
37
+ });
38
+
39
+ it('concept.relatedConcepts entries without gl:target have null target', () => {
40
+ const json = makeJsonLdWithRelated(null);
41
+ const concept = conceptFromJson(json as any);
42
+ expect(concept.relatedConcepts).toHaveLength(1);
43
+ expect(concept.relatedConcepts[0].target).toBeNull();
44
+ });
45
+ });
@@ -367,6 +367,13 @@ function mapRelatedFromJsonLd(r: JsonLdRelated): Record<string, unknown> {
367
367
  result.type = r[GL.RELATIONSHIP_TYPE];
368
368
  }
369
369
 
370
+ // gl:target is the resolved concept URI for cross-dataset relations
371
+ // (e.g., superseded_by → cie-2020 concept). Without this, the UI
372
+ // renders related concepts as plain text with no clickable link.
373
+ if (r[GL.TARGET]) {
374
+ result.target = r[GL.TARGET];
375
+ }
376
+
370
377
  if (r[GL.REF]) {
371
378
  const ref = mapRefFromJsonLd(r[GL.REF]);
372
379
  if (ref) result.ref = ref;