@glossarist/concept-browser 0.7.35 → 0.7.41
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 +2 -2
- package/scripts/build-edges.js +16 -8
- package/scripts/generate-data.mjs +284 -86
- package/src/__tests__/citation-display.test.ts +165 -3
- package/src/__tests__/cite-ref.test.ts +112 -0
- package/src/__tests__/concept-detail-interaction.test.ts +1 -1
- package/src/__tests__/{math.test.ts → content-renderer.test.ts} +113 -29
- package/src/__tests__/escape.test.ts +76 -0
- package/src/__tests__/graph-data-source.test.ts +155 -0
- package/src/__tests__/model-bridge-bridges.test.ts +150 -0
- package/src/__tests__/model-bridge-citation.test.ts +163 -0
- package/src/__tests__/reference-resolver-cite.test.ts +122 -0
- package/src/__tests__/reference-resolver.test.ts +12 -7
- package/src/__tests__/resolve-view.test.ts +1 -1
- package/src/__tests__/sidebar-nav-highlighting.test.ts +178 -0
- package/src/__tests__/source-refs.test.ts +9 -6
- package/src/__tests__/test-helpers.ts +20 -0
- package/src/__tests__/uri-router.test.ts +39 -12
- package/src/adapters/DatasetAdapter.ts +12 -0
- package/src/adapters/GraphDataSource.ts +3 -3
- package/src/adapters/ReferenceResolver.ts +85 -55
- package/src/adapters/UriRouter.ts +82 -10
- package/src/adapters/factory.ts +34 -10
- package/src/adapters/model-bridge.ts +121 -71
- package/src/adapters/types.ts +3 -0
- package/src/components/AppSidebar.vue +7 -4
- package/src/components/CitationDisplay.vue +86 -30
- package/src/components/ConceptDetail.vue +56 -20
- package/src/components/LanguageDetail.vue +6 -6
- package/src/composables/use-concept-content.ts +8 -8
- package/src/composables/use-concept-edges.ts +2 -1
- package/src/composables/use-render-options.ts +1 -1
- package/src/graph/GraphEngine.ts +3 -3
- package/src/stores/vocabulary.ts +2 -2
- package/src/style.css +29 -0
- package/src/utils/content-renderer.ts +312 -0
- package/src/utils/markdown-lite.ts +2 -2
- package/src/utils/math.ts +0 -189
|
@@ -1,34 +1,106 @@
|
|
|
1
1
|
import type { Manifest } from './types';
|
|
2
2
|
|
|
3
|
+
// ── URI pattern matching ────────────────────────────────────────────────────
|
|
4
|
+
|
|
5
|
+
function matchUriPattern(uri: string, pattern: string): boolean {
|
|
6
|
+
if (!pattern.endsWith('*')) return uri === pattern;
|
|
7
|
+
return uri.startsWith(pattern.slice(0, -1));
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function extractConceptId(uri: string, pattern: string): string | null {
|
|
11
|
+
if (!pattern.endsWith('*')) return null;
|
|
12
|
+
const base = pattern.slice(0, -1);
|
|
13
|
+
if (!uri.startsWith(base)) return null;
|
|
14
|
+
const remainder = uri.slice(base.length);
|
|
15
|
+
|
|
16
|
+
if (uri.startsWith('https://') || uri.startsWith('http://')) {
|
|
17
|
+
const match = remainder.match(/^\/?concept\/([^/?#]+)/);
|
|
18
|
+
return match ? match[1] : null;
|
|
19
|
+
}
|
|
20
|
+
if (uri.startsWith('urn:')) {
|
|
21
|
+
return remainder || null;
|
|
22
|
+
}
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// ── Static parse regex ──────────────────────────────────────────────────────
|
|
27
|
+
|
|
3
28
|
const URI_REGISTER_RE = /\/([^/]+)\/concept\/([^/]+)$/;
|
|
4
29
|
|
|
30
|
+
// ── UriRouter ───────────────────────────────────────────────────────────────
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Single source of truth for URI routing.
|
|
34
|
+
*
|
|
35
|
+
* Maps URIs to {registerId, conceptId} pairs using dataset-registered URI
|
|
36
|
+
* patterns. Supports wildcard patterns, URN prefix mapping, and URI construction.
|
|
37
|
+
*
|
|
38
|
+
* ReferenceResolver delegates URI matching here and adds its own concerns
|
|
39
|
+
* (routing table, source refs, citation classification) on top.
|
|
40
|
+
*/
|
|
5
41
|
export class UriRouter {
|
|
6
|
-
|
|
42
|
+
/** registerId → { baseUrl, uriBase, uriPatterns } */
|
|
43
|
+
private registerMap = new Map<string, { baseUrl: string; uriBase: string; uriPatterns: string[] }>();
|
|
7
44
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
45
|
+
/** URN prefix → registerId (extracted from uriPatterns at registration time) */
|
|
46
|
+
private urnMap = new Map<string, string>();
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Register a dataset's URI patterns for routing.
|
|
50
|
+
* URN-prefixed patterns are also indexed for fast URN → registerId lookup.
|
|
51
|
+
*/
|
|
52
|
+
registerDataset(registerId: string, baseUrl: string, uriBase: string, uriPatterns: string[]): void {
|
|
53
|
+
this.registerMap.set(registerId, { baseUrl, uriBase, uriPatterns });
|
|
54
|
+
|
|
55
|
+
for (const pattern of uriPatterns) {
|
|
56
|
+
const base = pattern.endsWith('*') ? pattern.slice(0, -1) : pattern;
|
|
57
|
+
if (base.startsWith('urn:')) {
|
|
58
|
+
// Store without trailing colon so prefix matching works naturally
|
|
59
|
+
const clean = base.endsWith(':') ? base.slice(0, -1) : base;
|
|
60
|
+
this.urnMap.set(clean, registerId);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
14
63
|
}
|
|
15
64
|
|
|
65
|
+
/**
|
|
66
|
+
* Resolve a URI to {registerId, conceptId} using registered patterns.
|
|
67
|
+
* Returns null if no registered dataset matches.
|
|
68
|
+
*/
|
|
16
69
|
resolveUri(uri: string): { registerId: string; conceptId: string } | null {
|
|
17
70
|
for (const [registerId, info] of this.registerMap) {
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
|
|
71
|
+
for (const pattern of info.uriPatterns) {
|
|
72
|
+
if (matchUriPattern(uri, pattern)) {
|
|
73
|
+
const conceptId = extractConceptId(uri, pattern);
|
|
74
|
+
if (conceptId) return { registerId, conceptId };
|
|
75
|
+
}
|
|
21
76
|
}
|
|
22
77
|
}
|
|
23
78
|
return null;
|
|
24
79
|
}
|
|
25
80
|
|
|
81
|
+
/** Resolve a URN prefix to a registerId. Matches by longest prefix. Returns null if unknown. */
|
|
82
|
+
resolveUrn(urn: string): string | null {
|
|
83
|
+
// Try exact match first, then progressively shorter prefixes
|
|
84
|
+
for (let len = urn.length; len > 0; len--) {
|
|
85
|
+
const prefix = urn.slice(0, len);
|
|
86
|
+
const match = this.urnMap.get(prefix);
|
|
87
|
+
if (match) return match;
|
|
88
|
+
}
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/** Get the uriBase for a register. Returns empty string if unknown. */
|
|
93
|
+
getUriBase(registerId: string): string {
|
|
94
|
+
return this.registerMap.get(registerId)?.uriBase ?? '';
|
|
95
|
+
}
|
|
96
|
+
|
|
26
97
|
/** Extract registerId and conceptId from any glossarist URI (no registration needed). */
|
|
27
98
|
static parseUri(uri: string): { registerId: string; conceptId: string } | null {
|
|
28
99
|
const m = uri.match(URI_REGISTER_RE);
|
|
29
100
|
return m ? { registerId: m[1], conceptId: m[2] } : null;
|
|
30
101
|
}
|
|
31
102
|
|
|
103
|
+
/** Construct a canonical URI for a concept. */
|
|
32
104
|
buildUri(registerId: string, conceptId: string): string {
|
|
33
105
|
const info = this.registerMap.get(registerId);
|
|
34
106
|
const uriBase = info?.uriBase ?? '';
|
package/src/adapters/factory.ts
CHANGED
|
@@ -2,15 +2,17 @@ import type { DatasetRegistry, Manifest, Resolution } from './types';
|
|
|
2
2
|
import type { RoutingEntry as ConfigRoutingEntry } from '../config/types';
|
|
3
3
|
import { DatasetAdapter } from './DatasetAdapter';
|
|
4
4
|
import { ReferenceResolver } from './ReferenceResolver';
|
|
5
|
+
import { UriRouter } from './UriRouter';
|
|
5
6
|
|
|
6
7
|
export class AdapterFactory {
|
|
7
8
|
private adapters = new Map<string, DatasetAdapter>();
|
|
8
|
-
private urnMap = new Map<string, string>();
|
|
9
|
-
readonly resolver: ReferenceResolver;
|
|
10
9
|
private crossRefIndex: Record<string, string[]> | null = null;
|
|
10
|
+
readonly uriRouter: UriRouter;
|
|
11
|
+
readonly resolver: ReferenceResolver;
|
|
11
12
|
|
|
12
13
|
constructor() {
|
|
13
|
-
this.
|
|
14
|
+
this.uriRouter = new UriRouter();
|
|
15
|
+
this.resolver = new ReferenceResolver(this.uriRouter);
|
|
14
16
|
}
|
|
15
17
|
|
|
16
18
|
async discoverDatasets(datasetsUrl: string): Promise<DatasetAdapter[]> {
|
|
@@ -79,16 +81,38 @@ export class AdapterFactory {
|
|
|
79
81
|
...(manifest.uriAliases ?? []),
|
|
80
82
|
manifest.uriBase ? `${manifest.uriBase}/${registerId}/*` : undefined,
|
|
81
83
|
].filter(Boolean) as string[];
|
|
82
|
-
this.resolver.registerDataset(registerId, uriPatterns);
|
|
83
84
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
85
|
+
this.uriRouter.registerDataset(
|
|
86
|
+
registerId,
|
|
87
|
+
manifest.baseUrl,
|
|
88
|
+
manifest.uriBase,
|
|
89
|
+
uriPatterns,
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
// Propagate URN map to all adapters for ref-target resolution
|
|
93
|
+
const urnMap = new Map<string, string>();
|
|
94
|
+
for (const id of this.uriRouter.getRegisteredIds()) {
|
|
95
|
+
const uriBase = this.uriRouter.getUriBase(id);
|
|
96
|
+
if (!uriBase) continue;
|
|
97
|
+
// Reconstruct URN map from uriRouter registrations
|
|
98
|
+
for (const pattern of [manifest.datasetUri, ...(manifest.uriAliases ?? [])]) {
|
|
99
|
+
if (!pattern) continue;
|
|
100
|
+
const base = pattern.endsWith('*') ? pattern.slice(0, -1) : pattern;
|
|
101
|
+
if (base.startsWith('urn:')) urnMap.set(base, registerId);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
// Include URNs from all previously registered datasets
|
|
105
|
+
for (const adapter of this.adapters.values()) {
|
|
106
|
+
const m = adapter.manifest;
|
|
107
|
+
if (!m) continue;
|
|
108
|
+
for (const pattern of [m.datasetUri, ...(m.uriAliases ?? [])]) {
|
|
109
|
+
if (!pattern) continue;
|
|
110
|
+
const base = pattern.endsWith('*') ? pattern.slice(0, -1) : pattern;
|
|
111
|
+
if (base.startsWith('urn:')) urnMap.set(base, adapter.registerId);
|
|
112
|
+
}
|
|
88
113
|
}
|
|
89
|
-
|
|
90
114
|
for (const adapter of this.adapters.values()) {
|
|
91
|
-
adapter.setUrnMap(
|
|
115
|
+
adapter.setUrnMap(urnMap);
|
|
92
116
|
}
|
|
93
117
|
}
|
|
94
118
|
|
|
@@ -73,18 +73,18 @@ interface JsonLdRef {
|
|
|
73
73
|
'gl:id'?: string;
|
|
74
74
|
'gl:version'?: string;
|
|
75
75
|
'gl:text'?: string;
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
76
|
+
source?: string;
|
|
77
|
+
id?: string;
|
|
78
|
+
version?: string;
|
|
79
79
|
}
|
|
80
80
|
|
|
81
81
|
interface JsonLdLocality {
|
|
82
82
|
'gl:localityType'?: string;
|
|
83
83
|
'gl:referenceFrom'?: string;
|
|
84
84
|
'gl:referenceTo'?: string;
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
85
|
+
type?: string;
|
|
86
|
+
reference_from?: string;
|
|
87
|
+
reference_to?: string;
|
|
88
88
|
}
|
|
89
89
|
|
|
90
90
|
interface JsonLdOrigin {
|
|
@@ -97,6 +97,7 @@ interface JsonLdOrigin {
|
|
|
97
97
|
}
|
|
98
98
|
|
|
99
99
|
interface JsonLdSource {
|
|
100
|
+
'gl:id'?: string;
|
|
100
101
|
'gl:sourceType'?: string;
|
|
101
102
|
'gl:sourceStatus'?: string;
|
|
102
103
|
'gl:modification'?: string;
|
|
@@ -109,6 +110,8 @@ interface JsonLdRelated {
|
|
|
109
110
|
'@id'?: string;
|
|
110
111
|
'gl:term'?: string;
|
|
111
112
|
'gl:target'?: string;
|
|
113
|
+
'gl:sourceId'?: string;
|
|
114
|
+
'gl:citation'?: JsonLdOrigin;
|
|
112
115
|
}
|
|
113
116
|
|
|
114
117
|
interface JsonLdDesignation {
|
|
@@ -192,10 +195,24 @@ export function getRefText(ref: ConceptRef): string | null {
|
|
|
192
195
|
return refTexts.get(ref) ?? null;
|
|
193
196
|
}
|
|
194
197
|
|
|
198
|
+
// RelatedConcept.sourceId: links a citation reference back to its source entry
|
|
199
|
+
const relatedSourceIds = new WeakMap<object, string>();
|
|
200
|
+
|
|
201
|
+
export function getRelatedSourceId(rc: object): string | null {
|
|
202
|
+
return relatedSourceIds.get(rc) ?? null;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// RelatedConcept.citation: embedded citation data for cite-ref references
|
|
206
|
+
const relatedCitations = new WeakMap<object, Record<string, unknown>>();
|
|
207
|
+
|
|
208
|
+
export function getRelatedCitation(rc: object): Record<string, unknown> | null {
|
|
209
|
+
return relatedCitations.get(rc) ?? null;
|
|
210
|
+
}
|
|
211
|
+
|
|
195
212
|
// Relationship types whose target is a designation string, not a concept ref.
|
|
196
213
|
const DESIGNATION_REL_TYPES = new Set(['abbreviated_form_for', 'short_form_for']);
|
|
197
214
|
|
|
198
|
-
function
|
|
215
|
+
function attachBridges(concept: Concept, localizations: Record<string, unknown>): void {
|
|
199
216
|
for (const lang of concept.languages) {
|
|
200
217
|
const lc = concept.localization(lang);
|
|
201
218
|
const raw = localizations[lang];
|
|
@@ -210,7 +227,7 @@ function attachAnnotations(concept: Concept, localizations: Record<string, unkno
|
|
|
210
227
|
));
|
|
211
228
|
}
|
|
212
229
|
|
|
213
|
-
// Designation-level relationship targets
|
|
230
|
+
// Designation-level relationship targets, ref text, sourceId, citation
|
|
214
231
|
const rawTerms = rawObj.terms;
|
|
215
232
|
if (Array.isArray(rawTerms)) {
|
|
216
233
|
for (const rawTerm of rawTerms) {
|
|
@@ -222,40 +239,56 @@ function attachAnnotations(concept: Concept, localizations: Record<string, unkno
|
|
|
222
239
|
if (!designation) continue;
|
|
223
240
|
const rawRelated = rawT.related;
|
|
224
241
|
if (!Array.isArray(rawRelated)) continue;
|
|
225
|
-
|
|
226
|
-
if (!rawRel || typeof rawRel !== 'object') continue;
|
|
227
|
-
const rel = rawRel as Record<string, unknown>;
|
|
228
|
-
const relType = rel.type as string | undefined;
|
|
229
|
-
const rc = designation.related.find(r => r.type === relType);
|
|
230
|
-
if (!rc) continue;
|
|
231
|
-
if (rel.target && typeof rel.target === 'string') {
|
|
232
|
-
designationTargets.set(rc as object, rel.target);
|
|
233
|
-
}
|
|
234
|
-
if ('ref' in rc && rc.ref) {
|
|
235
|
-
const rawRef = rel.ref as Record<string, unknown> | undefined;
|
|
236
|
-
if (rawRef?.text && typeof rawRef.text === 'string') {
|
|
237
|
-
refTexts.set(rc.ref, rawRef.text);
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
}
|
|
242
|
+
attachRelatedBridges(designation.related, rawRelated);
|
|
241
243
|
}
|
|
242
244
|
}
|
|
243
245
|
|
|
244
|
-
// Localization-level
|
|
246
|
+
// Localization-level related concepts
|
|
245
247
|
const rawRelated = rawObj.related;
|
|
246
248
|
if (Array.isArray(rawRelated)) {
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
249
|
+
attachRelatedBridges(lc.related, rawRelated);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Attach bridged fields (ref text, sourceId, citation) to RelatedConcept instances
|
|
256
|
+
* from the raw deserialized data. Called after Concept.fromJSON creates the model
|
|
257
|
+
* instances, since RelatedConcept.fromJSON only reads type/content/ref.
|
|
258
|
+
*/
|
|
259
|
+
function attachRelatedBridges(
|
|
260
|
+
modelRelated: Array<{ type?: string | null; content?: string | null; ref?: any; related?: any[] }>,
|
|
261
|
+
rawRelated: unknown[],
|
|
262
|
+
): void {
|
|
263
|
+
for (const rawRel of rawRelated) {
|
|
264
|
+
if (!rawRel || typeof rawRel !== 'object') continue;
|
|
265
|
+
const rel = rawRel as Record<string, unknown>;
|
|
266
|
+
const relType = rel.type as string | undefined;
|
|
267
|
+
const rc = relType ? modelRelated.find(r => r.type === relType) : undefined;
|
|
268
|
+
if (!rc) continue;
|
|
269
|
+
|
|
270
|
+
// Ref text
|
|
271
|
+
if (rc.ref) {
|
|
272
|
+
const rawRef = rel.ref as Record<string, unknown> | undefined;
|
|
273
|
+
if (rawRef?.text && typeof rawRef.text === 'string') {
|
|
274
|
+
refTexts.set(rc.ref, rawRef.text);
|
|
257
275
|
}
|
|
258
276
|
}
|
|
277
|
+
|
|
278
|
+
// Designation target
|
|
279
|
+
if (rel.target && typeof rel.target === 'string') {
|
|
280
|
+
designationTargets.set(rc, rel.target);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// Source ID — links citation reference back to the ConceptSource entry
|
|
284
|
+
if (rel.sourceId && typeof rel.sourceId === 'string') {
|
|
285
|
+
relatedSourceIds.set(rc, rel.sourceId);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// Citation — embedded origin data for cite-ref references
|
|
289
|
+
if (rel.citation && typeof rel.citation === 'object') {
|
|
290
|
+
relatedCitations.set(rc, rel.citation as Record<string, unknown>);
|
|
291
|
+
}
|
|
259
292
|
}
|
|
260
293
|
}
|
|
261
294
|
|
|
@@ -335,8 +368,37 @@ function mapDesignationFromJsonLd(d: JsonLdDesignation): Record<string, unknown>
|
|
|
335
368
|
return result;
|
|
336
369
|
}
|
|
337
370
|
|
|
371
|
+
function mapRefFromJsonLd(rawRef: JsonLdRef | string | undefined): Record<string, unknown> | null {
|
|
372
|
+
if (!rawRef) return null;
|
|
373
|
+
if (typeof rawRef === 'string') return { source: rawRef };
|
|
374
|
+
const refObj: Record<string, unknown> = {};
|
|
375
|
+
// gl:-prefixed keys take precedence over unprefixed keys
|
|
376
|
+
refObj.source = rawRef['gl:source'] ?? rawRef.source;
|
|
377
|
+
refObj.id = rawRef['gl:id'] ?? rawRef.id;
|
|
378
|
+
refObj.version = rawRef['gl:version'] ?? rawRef.version;
|
|
379
|
+
if (rawRef['gl:text']) refObj.text = rawRef['gl:text'];
|
|
380
|
+
return (refObj.source ?? refObj.id ?? refObj.version ?? refObj.text) != null
|
|
381
|
+
? refObj : null;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
/**
|
|
385
|
+
* Map JSON-LD locality to glossarist's snake_case format.
|
|
386
|
+
* Always uses snake_case (reference_from/reference_to) for consistency
|
|
387
|
+
* with the glossarist model.
|
|
388
|
+
*/
|
|
389
|
+
function mapLocalityFromJsonLd(rawLoc: JsonLdLocality | undefined): Record<string, unknown> | null {
|
|
390
|
+
if (!rawLoc) return null;
|
|
391
|
+
const locObj: Record<string, unknown> = {};
|
|
392
|
+
locObj.type = rawLoc['gl:localityType'] ?? rawLoc.type;
|
|
393
|
+
locObj.reference_from = rawLoc['gl:referenceFrom'] ?? rawLoc.reference_from;
|
|
394
|
+
locObj.reference_to = rawLoc['gl:referenceTo'] ?? rawLoc.reference_to;
|
|
395
|
+
return (locObj.type ?? locObj.reference_from ?? locObj.reference_to) != null
|
|
396
|
+
? locObj : null;
|
|
397
|
+
}
|
|
398
|
+
|
|
338
399
|
function mapSourceFromJsonLd(s: JsonLdSource): Record<string, unknown> {
|
|
339
400
|
const result: Record<string, unknown> = {};
|
|
401
|
+
if (s['gl:id']) result.id = s['gl:id'];
|
|
340
402
|
if (s['gl:sourceType']) result.type = s['gl:sourceType'];
|
|
341
403
|
if (s['gl:sourceStatus']) result.status = s['gl:sourceStatus'];
|
|
342
404
|
if (s['gl:modification']) result.modification = s['gl:modification'];
|
|
@@ -344,32 +406,10 @@ function mapSourceFromJsonLd(s: JsonLdSource): Record<string, unknown> {
|
|
|
344
406
|
if (s['gl:origin']) {
|
|
345
407
|
const origin: Record<string, unknown> = {};
|
|
346
408
|
const o = s['gl:origin'];
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
} else {
|
|
352
|
-
const refObj: Record<string, unknown> = {};
|
|
353
|
-
if (rawRef['gl:source']) refObj.source = rawRef['gl:source'];
|
|
354
|
-
if (rawRef['gl:id']) refObj.id = rawRef['gl:id'];
|
|
355
|
-
if (rawRef['gl:version']) refObj.version = rawRef['gl:version'];
|
|
356
|
-
if (rawRef['source']) refObj.source = rawRef['source'];
|
|
357
|
-
if (rawRef['id']) refObj.id = rawRef['id'];
|
|
358
|
-
if (rawRef['version']) refObj.version = rawRef['version'];
|
|
359
|
-
if (Object.keys(refObj).length > 0) origin.ref = refObj;
|
|
360
|
-
}
|
|
361
|
-
}
|
|
362
|
-
if (o['gl:locality']) {
|
|
363
|
-
const loc: Record<string, unknown> = {};
|
|
364
|
-
const rawLoc = o['gl:locality'];
|
|
365
|
-
if (rawLoc['gl:localityType']) loc.type = rawLoc['gl:localityType'];
|
|
366
|
-
if (rawLoc['gl:referenceFrom']) loc.reference_from = rawLoc['gl:referenceFrom'];
|
|
367
|
-
if (rawLoc['gl:referenceTo']) loc.reference_to = rawLoc['gl:referenceTo'];
|
|
368
|
-
if (rawLoc['type']) loc.type = rawLoc['type'];
|
|
369
|
-
if (rawLoc['reference_from']) loc.reference_from = rawLoc['reference_from'];
|
|
370
|
-
if (rawLoc['reference_to']) loc.reference_to = rawLoc['reference_to'];
|
|
371
|
-
origin.locality = loc;
|
|
372
|
-
}
|
|
409
|
+
const ref = mapRefFromJsonLd(o['gl:ref']);
|
|
410
|
+
if (ref) origin.ref = ref;
|
|
411
|
+
const loc = mapLocalityFromJsonLd(o['gl:locality']);
|
|
412
|
+
if (loc) origin.locality = loc;
|
|
373
413
|
if (o['gl:link']) origin.link = o['gl:link'];
|
|
374
414
|
if (o['gl:id']) origin.id = o['gl:id'];
|
|
375
415
|
if (o['gl:version']) origin.version = o['gl:version'];
|
|
@@ -388,14 +428,8 @@ function mapRelatedFromJsonLd(r: JsonLdRelated): Record<string, unknown> {
|
|
|
388
428
|
}
|
|
389
429
|
|
|
390
430
|
if (r['gl:ref']) {
|
|
391
|
-
const ref = r['gl:ref'];
|
|
392
|
-
|
|
393
|
-
if (ref['gl:source']) refObj.source = ref['gl:source'];
|
|
394
|
-
if (ref['gl:id']) refObj.id = ref['gl:id'];
|
|
395
|
-
if (ref['source']) refObj.source = ref['source'];
|
|
396
|
-
if (ref['id']) refObj.id = ref['id'];
|
|
397
|
-
if (ref['gl:text']) refObj.text = ref['gl:text'];
|
|
398
|
-
if (Object.keys(refObj).length > 0) result.ref = refObj;
|
|
431
|
+
const ref = mapRefFromJsonLd(r['gl:ref']);
|
|
432
|
+
if (ref) result.ref = ref;
|
|
399
433
|
}
|
|
400
434
|
|
|
401
435
|
if (!result.ref && r['@id']) {
|
|
@@ -406,6 +440,22 @@ function mapRelatedFromJsonLd(r: JsonLdRelated): Record<string, unknown> {
|
|
|
406
440
|
: { source: uri, id: null };
|
|
407
441
|
}
|
|
408
442
|
if (r['gl:term']) result.content = r['gl:term'];
|
|
443
|
+
|
|
444
|
+
// Bridged fields — stored in raw dict, extracted by attachBridges()
|
|
445
|
+
if (r['gl:sourceId']) result.sourceId = r['gl:sourceId'];
|
|
446
|
+
if (r['gl:citation']) {
|
|
447
|
+
const c = r['gl:citation'];
|
|
448
|
+
const citation: Record<string, unknown> = {};
|
|
449
|
+
if (c['gl:ref']) {
|
|
450
|
+
const cr = mapRefFromJsonLd(c['gl:ref']);
|
|
451
|
+
if (cr) citation.ref = cr;
|
|
452
|
+
}
|
|
453
|
+
const loc = mapLocalityFromJsonLd(c['gl:locality']);
|
|
454
|
+
if (loc) citation.locality = loc;
|
|
455
|
+
if (c['gl:link']) citation.link = c['gl:link'];
|
|
456
|
+
if (Object.keys(citation).length > 0) result.citation = citation;
|
|
457
|
+
}
|
|
458
|
+
|
|
409
459
|
return result;
|
|
410
460
|
}
|
|
411
461
|
|
|
@@ -493,7 +543,7 @@ function conceptFromJsonLd(doc: JsonLdConcept): Concept {
|
|
|
493
543
|
status: null,
|
|
494
544
|
});
|
|
495
545
|
|
|
496
|
-
|
|
546
|
+
attachBridges(concept, localizations);
|
|
497
547
|
return concept;
|
|
498
548
|
}
|
|
499
549
|
|
|
@@ -505,7 +555,7 @@ export function conceptFromJson(doc: Record<string, unknown>): Concept {
|
|
|
505
555
|
}
|
|
506
556
|
const concept = Concept.fromJSON(doc);
|
|
507
557
|
const locs = (doc as Record<string, unknown>).localizations as Record<string, unknown> | undefined;
|
|
508
|
-
if (locs)
|
|
558
|
+
if (locs) attachBridges(concept, locs);
|
|
509
559
|
return concept;
|
|
510
560
|
}
|
|
511
561
|
|
package/src/adapters/types.ts
CHANGED
|
@@ -32,6 +32,9 @@ export type {
|
|
|
32
32
|
export { RELATIONSHIP_TYPES, DATE_TYPES } from 'glossarist';
|
|
33
33
|
export { GRAMMAR_GENDERS, GRAMMAR_NUMBERS, GRAMMAR_PARTS_OF_SPEECH } from 'glossarist/models';
|
|
34
34
|
|
|
35
|
+
// Re-export citation classification from ReferenceResolver (single definition site)
|
|
36
|
+
export type { CitationClassification, CiteResolution } from './ReferenceResolver';
|
|
37
|
+
|
|
35
38
|
// ── Dataset metadata ──────────────────────────────────────────────────────
|
|
36
39
|
|
|
37
40
|
export interface ManifestSection {
|
|
@@ -114,8 +114,8 @@ const showDatasetNav = computed(() => !!currentManifest.value || !!siteConfig.va
|
|
|
114
114
|
const provenance = computed(() => {
|
|
115
115
|
const manifest = currentManifest.value;
|
|
116
116
|
return {
|
|
117
|
-
owner: manifest?.owner ||
|
|
118
|
-
ownerUrl:
|
|
117
|
+
owner: manifest?.owner || siteConfig.value?.branding?.ownerName,
|
|
118
|
+
ownerUrl: siteConfig.value?.branding?.ownerUrl,
|
|
119
119
|
ref: manifest?.ref,
|
|
120
120
|
status: manifest?.status,
|
|
121
121
|
lastUpdated: manifest?.lastUpdated,
|
|
@@ -149,14 +149,17 @@ function isActive(page: { route: string; datasetScoped?: boolean }): boolean {
|
|
|
149
149
|
const target = pageRoute(page);
|
|
150
150
|
if (route.path === target) return true;
|
|
151
151
|
if (page.datasetScoped) return route.name === page.route;
|
|
152
|
+
// Non-dataset-scoped page: only match if we're NOT inside a dataset route
|
|
153
|
+
const inDataset = 'registerId' in route.params;
|
|
154
|
+
if (inDataset) return false;
|
|
152
155
|
return route.name === page.route || route.name === `${page.route}-global`;
|
|
153
156
|
}
|
|
154
157
|
|
|
155
|
-
function navTitle(page: { route: string }): string {
|
|
158
|
+
function navTitle(page: { route: string; title?: string }): string {
|
|
156
159
|
const route = page.route || 'home';
|
|
157
160
|
const key = `nav.${route}`;
|
|
158
161
|
const translated = t(key);
|
|
159
|
-
return translated === key ? (page
|
|
162
|
+
return translated === key ? (page.title ?? route) : translated;
|
|
160
163
|
}
|
|
161
164
|
|
|
162
165
|
const expandedSectionNodes = ref<Set<string>>(new Set());
|