@glossarist/concept-browser 0.7.88 → 0.7.89

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.
@@ -29,6 +29,7 @@ import {
29
29
  isPartitivePresence,
30
30
  isPartitiveCount,
31
31
  } from '../utils/partitive-multiplicity';
32
+ import { GL } from './wire-keys';
32
33
  import {
33
34
  LetterSymbol,
34
35
  GrammarInfo,
@@ -43,165 +44,24 @@ import type { ConceptSummary } from './types';
43
44
  import { ConceptIdentity } from './concept-identity';
44
45
 
45
46
  // ── JSON-LD wire-format types ─────────────────────────────────────────────
46
-
47
- interface JsonLdContent {
48
- 'gl:content'?: string;
49
- }
50
-
51
- interface JsonLdDate {
52
- 'gl:date'?: string;
53
- 'gl:dateType'?: string;
54
- }
55
-
56
- interface JsonLdPronunciation {
57
- 'gl:content'?: string;
58
- 'gl:language'?: string;
59
- 'gl:script'?: string;
60
- 'gl:system'?: string;
61
- 'gl:country'?: string;
62
- }
63
-
64
- interface JsonLdGrammarInfo {
65
- 'gl:gender'?: string;
66
- 'gl:number'?: string;
67
- 'gl:partOfSpeech'?: string;
68
- 'gl:noun'?: boolean;
69
- 'gl:verb'?: boolean;
70
- 'gl:adj'?: boolean;
71
- 'gl:adverb'?: boolean;
72
- 'gl:preposition'?: boolean;
73
- 'gl:participle'?: boolean;
74
- }
75
-
76
- interface JsonLdRef {
77
- 'gl:source'?: string;
78
- 'gl:id'?: string;
79
- 'gl:version'?: string;
80
- 'gl:text'?: string;
81
- source?: string;
82
- id?: string;
83
- version?: string;
84
- }
85
-
86
- interface JsonLdLocality {
87
- 'gl:localityType'?: string;
88
- 'gl:referenceFrom'?: string;
89
- 'gl:referenceTo'?: string;
90
- type?: string;
91
- reference_from?: string;
92
- reference_to?: string;
93
- }
94
-
95
- interface JsonLdOrigin {
96
- 'gl:ref'?: string | JsonLdRef;
97
- 'gl:locality'?: JsonLdLocality;
98
- 'gl:link'?: string;
99
- 'gl:id'?: string;
100
- 'gl:version'?: string;
101
- 'gl:source'?: string;
102
- }
103
-
104
- interface JsonLdSource {
105
- 'gl:id'?: string;
106
- 'gl:sourceType'?: string;
107
- 'gl:sourceStatus'?: string;
108
- 'gl:modification'?: string;
109
- 'gl:origin'?: JsonLdOrigin;
110
- 'gl:sourcedFrom'?: JsonLdOrigin[];
111
- 'gl:sourced_from'?: JsonLdOrigin[];
112
- }
113
-
114
- interface JsonLdRelated {
115
- 'gl:relationshipType'?: string;
116
- 'gl:ref'?: JsonLdRef;
117
- '@id'?: string;
118
- 'gl:term'?: string;
119
- 'gl:target'?: string;
120
- 'gl:sourceId'?: string;
121
- 'gl:citation'?: JsonLdOrigin;
122
- }
123
-
124
- interface JsonLdDesignation {
125
- '@type'?: string;
126
- 'gl:term'?: string;
127
- 'gl:normativeStatus'?: string;
128
- 'gl:absent'?: unknown;
129
- 'gl:fieldOfApplication'?: string;
130
- 'gl:usageInfo'?: string;
131
- 'gl:geographicalArea'?: string;
132
- 'gl:language'?: string;
133
- 'gl:script'?: string;
134
- 'gl:system'?: string;
135
- 'gl:international'?: boolean;
136
- 'gl:termType'?: string;
137
- 'gl:pronunciation'?: JsonLdPronunciation[];
138
- 'gl:source'?: JsonLdSource[];
139
- 'gl:related'?: JsonLdRelated[];
140
- 'gl:prefix'?: string;
141
- 'gl:gender'?: string;
142
- 'gl:grammarInfo'?: JsonLdGrammarInfo[];
143
- }
144
-
145
- interface JsonLdLocalizedConcept {
146
- 'gl:languageCode'?: string;
147
- 'gl:entryStatus'?: string;
148
- 'gl:classification'?: string;
149
- 'gl:reviewType'?: string;
150
- 'gl:domain'?: string;
151
- 'gl:release'?: string;
152
- 'gl:lineageSourceSimilarity'?: number;
153
- 'gl:script'?: string;
154
- 'gl:system'?: string;
155
- 'gl:designation'?: JsonLdDesignation[];
156
- 'gl:definition'?: JsonLdContent[];
157
- 'gl:notes'?: JsonLdContent[];
158
- 'gl:annotations'?: JsonLdContent[];
159
- 'gl:examples'?: JsonLdContent[];
160
- 'gl:source'?: JsonLdSource[];
161
- 'gl:dates'?: JsonLdDate[];
162
- 'gl:references'?: JsonLdRelated[];
163
- 'gl:reviewDate'?: string;
164
- 'gl:reviewDecisionDate'?: string;
165
- 'gl:reviewDecisionEvent'?: string;
166
- 'gl:reviewStatus'?: string;
167
- 'gl:reviewDecision'?: string;
168
- 'gl:reviewDecisionNotes'?: string;
169
- }
170
-
171
- interface JsonLdPartitiveMember {
172
- 'gl:ref'?: JsonLdRef;
173
- /** ISO 704:2022 MECE: presence (required | optional) */
174
- 'gl:presence'?: string;
175
- /** ISO 704:2022 MECE: count (exactly_one | at_least_one | multiple) */
176
- 'gl:count'?: string;
177
- /** ISO 704:2022: delimiting part (3× stroke width in diagrams) */
178
- 'gl:isDelimiting'?: boolean;
179
- /** Legacy v3 field — one-string multiplicity, split into presence+count */
180
- 'gl:multiplicity'?: string;
181
- /** Legacy v2 field — migrated to presence+count */
182
- 'gl:certainty'?: string;
183
- }
184
-
185
- interface JsonLdPartitiveRelation {
186
- 'gl:comprehensive'?: JsonLdRef;
187
- 'gl:hasPartitive'?: JsonLdPartitiveMember[];
188
- 'gl:completeness'?: string;
189
- 'gl:criterion'?: Record<string, string> | string;
190
- }
191
-
192
- interface JsonLdConcept {
193
- '@type'?: string;
194
- '@id'?: string;
195
- 'gl:identifier'?: string | number;
196
- 'gl:term'?: string;
197
- 'gl:localizedConcept'?: Record<string, JsonLdLocalizedConcept>;
198
- 'gl:related'?: JsonLdRelated[];
199
- 'gl:partitiveRelations'?: JsonLdPartitiveRelation[];
200
- 'gl:tags'?: string[];
201
- 'gl:figureRef'?: unknown[];
202
- 'gl:tableRef'?: unknown[];
203
- 'gl:formulaRef'?: unknown[];
204
- }
47
+ // Extracted to model-bridge/jsonld-types.ts for scannability.
48
+
49
+ import type {
50
+ JsonLdContent,
51
+ JsonLdDate,
52
+ JsonLdPronunciation,
53
+ JsonLdGrammarInfo,
54
+ JsonLdRef,
55
+ JsonLdLocality,
56
+ JsonLdOrigin,
57
+ JsonLdSource,
58
+ JsonLdRelated,
59
+ JsonLdDesignation,
60
+ JsonLdLocalizedConcept,
61
+ JsonLdPartitiveMember,
62
+ JsonLdPartitiveRelation,
63
+ JsonLdConcept,
64
+ } from './model-bridge/jsonld-types';
205
65
 
206
66
  // ── Bridges for fields not yet in glossarist-js ────────────────────────────
207
67
  // Remove each bridge when glossarist-js publishes native support.
@@ -351,7 +211,7 @@ function attachRelatedBridges(
351
211
  // ── Detection ─────────────────────────────────────────────────────────────
352
212
 
353
213
  function isJsonLd(doc: Record<string, unknown>): boolean {
354
- return '@type' in doc && 'gl:localizedConcept' in doc;
214
+ return '@type' in doc && GL.LOCALIZED_CONCEPT in doc;
355
215
  }
356
216
 
357
217
  // ── JSON-LD → Glossarist native mapping ───────────────────────────────────
@@ -366,58 +226,62 @@ function mapDesignationFromJsonLd(d: JsonLdDesignation): Record<string, unknown>
366
226
  else if (rawType.includes('Symbol')) result.type = 'symbol';
367
227
  else result.type = 'expression';
368
228
 
369
- result.designation = d['gl:term'] ?? '';
370
- result.normative_status = d['gl:normativeStatus'] ?? null;
371
-
372
- if (d['gl:absent'] != null) result.absent = d['gl:absent'];
373
- if (d['gl:fieldOfApplication']) result.field_of_application = d['gl:fieldOfApplication'];
374
- if (d['gl:usageInfo']) result.usage_info = d['gl:usageInfo'];
375
- if (d['gl:geographicalArea']) result.geographical_area = d['gl:geographicalArea'];
376
- if (d['gl:language']) result.language = d['gl:language'];
377
- if (d['gl:script']) result.script = d['gl:script'];
378
- if (d['gl:system']) result.system = d['gl:system'];
379
- if (d['gl:international'] != null) result.international = d['gl:international'];
380
- if (d['gl:termType']) result.term_type = d['gl:termType'];
381
-
382
- if (d['gl:pronunciation']?.length) {
383
- result.pronunciation = d['gl:pronunciation'].map(p => ({
384
- content: p['gl:content'] ?? null,
385
- language: p['gl:language'] ?? null,
386
- script: p['gl:script'] ?? null,
387
- system: p['gl:system'] ?? null,
388
- country: p['gl:country'] ?? null,
229
+ result.designation = d[GL.TERM] ?? '';
230
+ result.normative_status = d[GL.NORMATIVE_STATUS] ?? null;
231
+
232
+ if (d[GL.ABSENT] != null) result.absent = d[GL.ABSENT];
233
+ if (d[GL.FIELD_OF_APPLICATION]) result.field_of_application = d[GL.FIELD_OF_APPLICATION];
234
+ if (d[GL.USAGE_INFO]) result.usage_info = d[GL.USAGE_INFO];
235
+ if (d[GL.GEOGRAPHICAL_AREA]) result.geographical_area = d[GL.GEOGRAPHICAL_AREA];
236
+ if (d[GL.LANGUAGE]) result.language = d[GL.LANGUAGE];
237
+ if (d[GL.SCRIPT]) result.script = d[GL.SCRIPT];
238
+ if (d[GL.SYSTEM]) result.system = d[GL.SYSTEM];
239
+ if (d[GL.INTERNATIONAL] != null) result.international = d[GL.INTERNATIONAL];
240
+ if (d[GL.TERM_TYPE]) result.term_type = d[GL.TERM_TYPE];
241
+
242
+ const pronunciation = d[GL.PRONUNCIATION];
243
+ if (pronunciation?.length) {
244
+ result.pronunciation = pronunciation.map(p => ({
245
+ content: p[GL.CONTENT] ?? null,
246
+ language: p[GL.LANGUAGE] ?? null,
247
+ script: p[GL.SCRIPT] ?? null,
248
+ system: p[GL.SYSTEM] ?? null,
249
+ country: p[GL.COUNTRY] ?? null,
389
250
  }));
390
251
  }
391
252
 
392
- if (d['gl:source']?.length) {
393
- result.sources = d['gl:source'].map(mapSourceFromJsonLd);
253
+ const sources = d[GL.SOURCE];
254
+ if (sources?.length) {
255
+ result.sources = sources.map(mapSourceFromJsonLd);
394
256
  }
395
257
 
396
- if (d['gl:related']?.length) {
397
- result.related = d['gl:related'].map(r => {
398
- const relType = r['gl:relationshipType'] ?? 'references';
399
- if (DESIGNATION_REL_TYPES.has(relType) && r['gl:target']) {
400
- return { type: relType, target: r['gl:target'] };
258
+ const related = d[GL.RELATED];
259
+ if (related?.length) {
260
+ result.related = related.map(r => {
261
+ const relType = r[GL.RELATIONSHIP_TYPE] ?? 'references';
262
+ if (DESIGNATION_REL_TYPES.has(relType) && r[GL.TARGET]) {
263
+ return { type: relType, target: r[GL.TARGET] };
401
264
  }
402
265
  return mapRelatedFromJsonLd(r);
403
266
  });
404
267
  }
405
268
 
406
- if (d['gl:prefix'] != null) result.prefix = d['gl:prefix'];
407
- if (d['gl:gender']) {
408
- result.grammar_info = [{ gender: d['gl:gender'] }];
269
+ if (d[GL.PREFIX] != null) result.prefix = d[GL.PREFIX];
270
+ if (d[GL.GENDER]) {
271
+ result.grammar_info = [{ gender: d[GL.GENDER] }];
409
272
  }
410
- if (d['gl:grammarInfo']?.length) {
411
- result.grammar_info = d['gl:grammarInfo'].map(gi => ({
412
- gender: gi['gl:gender'] ?? null,
413
- number: gi['gl:number'] ?? null,
414
- part_of_speech: gi['gl:partOfSpeech'] ?? null,
415
- noun: gi['gl:noun'] ?? false,
416
- verb: gi['gl:verb'] ?? false,
417
- adj: gi['gl:adj'] ?? false,
418
- adverb: gi['gl:adverb'] ?? false,
419
- preposition: gi['gl:preposition'] ?? false,
420
- participle: gi['gl:participle'] ?? false,
273
+ const grammarInfo = d[GL.GRAMMAR_INFO];
274
+ if (grammarInfo?.length) {
275
+ result.grammar_info = grammarInfo.map(gi => ({
276
+ gender: gi[GL.GENDER] ?? null,
277
+ number: gi[GL.NUMBER] ?? null,
278
+ part_of_speech: gi[GL.PART_OF_SPEECH] ?? null,
279
+ noun: gi[GL.NOUN] ?? false,
280
+ verb: gi[GL.VERB] ?? false,
281
+ adj: gi[GL.ADJ] ?? false,
282
+ adverb: gi[GL.ADVERB] ?? false,
283
+ preposition: gi[GL.PREPOSITION] ?? false,
284
+ participle: gi[GL.PARTICIPLE] ?? false,
421
285
  }));
422
286
  }
423
287
 
@@ -429,10 +293,10 @@ function mapRefFromJsonLd(rawRef: JsonLdRef | string | undefined): Record<string
429
293
  if (typeof rawRef === 'string') return { source: rawRef };
430
294
  const refObj: Record<string, unknown> = {};
431
295
  // gl:-prefixed keys take precedence over unprefixed keys
432
- refObj.source = rawRef['gl:source'] ?? rawRef.source;
433
- refObj.id = rawRef['gl:id'] ?? rawRef.id;
434
- refObj.version = rawRef['gl:version'] ?? rawRef.version;
435
- if (rawRef['gl:text']) refObj.text = rawRef['gl:text'];
296
+ refObj.source = rawRef[GL.SOURCE] ?? rawRef.source;
297
+ refObj.id = rawRef[GL.LOCAL_ID] ?? rawRef.id;
298
+ refObj.version = rawRef[GL.VERSION] ?? rawRef.version;
299
+ if (rawRef[GL.TEXT]) refObj.text = rawRef[GL.TEXT];
436
300
  return (refObj.source ?? refObj.id ?? refObj.version ?? refObj.text) != null
437
301
  ? refObj : null;
438
302
  }
@@ -445,46 +309,50 @@ function mapRefFromJsonLd(rawRef: JsonLdRef | string | undefined): Record<string
445
309
  function mapLocalityFromJsonLd(rawLoc: JsonLdLocality | undefined): Record<string, unknown> | null {
446
310
  if (!rawLoc) return null;
447
311
  const locObj: Record<string, unknown> = {};
448
- locObj.type = rawLoc['gl:localityType'] ?? rawLoc.type;
449
- locObj.reference_from = rawLoc['gl:referenceFrom'] ?? rawLoc.reference_from;
450
- locObj.reference_to = rawLoc['gl:referenceTo'] ?? rawLoc.reference_to;
312
+ locObj.type = rawLoc[GL.LOCALITY_TYPE] ?? rawLoc.type;
313
+ locObj.reference_from = rawLoc[GL.REFERENCE_FROM] ?? rawLoc.reference_from;
314
+ locObj.reference_to = rawLoc[GL.REFERENCE_TO] ?? rawLoc.reference_to;
451
315
  return (locObj.type ?? locObj.reference_from ?? locObj.reference_to) != null
452
316
  ? locObj : null;
453
317
  }
454
318
 
455
319
  function mapDetailedDefinitionFromJsonLd(d: any): Record<string, unknown> {
456
- const result: Record<string, unknown> = { content: d['gl:content'] ?? '' };
457
- if (d['gl:examples']?.length) {
458
- result.examples = d['gl:examples'].map(mapDetailedDefinitionFromJsonLd);
320
+ const result: Record<string, unknown> = { content: d[GL.CONTENT] ?? '' };
321
+ if (d[GL.EXAMPLES]?.length) {
322
+ result.examples = d[GL.EXAMPLES].map(mapDetailedDefinitionFromJsonLd);
459
323
  }
460
324
  return result;
461
325
  }
462
326
 
463
327
  function mapOriginFromJsonLd(o: JsonLdOrigin): Record<string, unknown> {
464
328
  const origin: Record<string, unknown> = {};
465
- const ref = mapRefFromJsonLd(o['gl:ref']);
329
+ const ref = mapRefFromJsonLd(o[GL.REF]);
466
330
  if (ref) origin.ref = ref;
467
- const loc = mapLocalityFromJsonLd(o['gl:locality']);
331
+ const loc = mapLocalityFromJsonLd(o[GL.LOCALITY]);
468
332
  if (loc) origin.locality = loc;
469
- if (o['gl:link']) origin.link = o['gl:link'];
470
- if (o['gl:id']) origin.id = o['gl:id'];
471
- if (o['gl:version']) origin.version = o['gl:version'];
472
- if (o['gl:source']) origin.source = o['gl:source'];
333
+ if (o[GL.LINK]) origin.link = o[GL.LINK];
334
+ if (o[GL.LOCAL_ID]) origin.id = o[GL.LOCAL_ID];
335
+ if (o[GL.VERSION]) origin.version = o[GL.VERSION];
336
+ if (o[GL.SOURCE]) origin.source = o[GL.SOURCE];
473
337
  return origin;
474
338
  }
475
339
 
476
340
  function mapSourceFromJsonLd(s: JsonLdSource): Record<string, unknown> {
477
341
  const result: Record<string, unknown> = {};
478
- if (s['gl:id']) result.id = s['gl:id'];
479
- if (s['gl:sourceType']) result.type = s['gl:sourceType'];
480
- if (s['gl:sourceStatus']) result.status = s['gl:sourceStatus'];
481
- if (s['gl:modification']) result.modification = s['gl:modification'];
482
-
483
- if (s['gl:origin']) {
484
- result.origin = mapOriginFromJsonLd(s['gl:origin']);
342
+ // Note: JsonLdSource uses GL.LOCAL_ID for the source's local id, which is
343
+ // distinct from JSON-LD's '@id' (the concept IRI). Keep this as a
344
+ // literal — adding a wire-key for an isolated one-off would be noise.
345
+ if (s[GL.LOCAL_ID]) result.id = s[GL.LOCAL_ID];
346
+ if (s[GL.SOURCE_TYPE]) result.type = s[GL.SOURCE_TYPE];
347
+ if (s[GL.SOURCE_STATUS]) result.status = s[GL.SOURCE_STATUS];
348
+ if (s[GL.MODIFICATION]) result.modification = s[GL.MODIFICATION];
349
+
350
+ const origin = s[GL.ORIGIN];
351
+ if (origin) {
352
+ result.origin = mapOriginFromJsonLd(origin);
485
353
  }
486
354
 
487
- const sf = s['gl:sourcedFrom'] ?? s['gl:sourced_from'];
355
+ const sf = s[GL.SOURCED_FROM] ?? s[GL.SOURCED_FROM_ALT];
488
356
  if (sf?.length) {
489
357
  result.sourced_from = sf.map(item => mapOriginFromJsonLd(item));
490
358
  }
@@ -495,12 +363,12 @@ function mapSourceFromJsonLd(s: JsonLdSource): Record<string, unknown> {
495
363
  function mapRelatedFromJsonLd(r: JsonLdRelated): Record<string, unknown> {
496
364
  const result: Record<string, unknown> = { type: 'references' };
497
365
 
498
- if (r['gl:relationshipType']) {
499
- result.type = r['gl:relationshipType'];
366
+ if (r[GL.RELATIONSHIP_TYPE]) {
367
+ result.type = r[GL.RELATIONSHIP_TYPE];
500
368
  }
501
369
 
502
- if (r['gl:ref']) {
503
- const ref = mapRefFromJsonLd(r['gl:ref']);
370
+ if (r[GL.REF]) {
371
+ const ref = mapRefFromJsonLd(r[GL.REF]);
504
372
  if (ref) result.ref = ref;
505
373
  }
506
374
 
@@ -511,20 +379,21 @@ function mapRelatedFromJsonLd(r: JsonLdRelated): Record<string, unknown> {
511
379
  ? { source: uri.split('/').slice(-3, -2)[0] || '', id: idMatch[1] }
512
380
  : { source: uri, id: null };
513
381
  }
514
- if (r['gl:term']) result.content = r['gl:term'];
382
+ if (r[GL.TERM]) result.content = r[GL.TERM];
515
383
 
516
384
  // Bridged fields — stored in raw dict, extracted by attachBridges()
517
- if (r['gl:sourceId']) result.sourceId = r['gl:sourceId'];
518
- if (r['gl:citation']) {
519
- const c = r['gl:citation'];
385
+ if (r[GL.SOURCE_ID]) result.sourceId = r[GL.SOURCE_ID];
386
+ const rawCitation = r[GL.CITATION];
387
+ if (rawCitation) {
388
+ const c = rawCitation;
520
389
  const citation: Record<string, unknown> = {};
521
- if (c['gl:ref']) {
522
- const cr = mapRefFromJsonLd(c['gl:ref']);
390
+ if (c[GL.REF]) {
391
+ const cr = mapRefFromJsonLd(c[GL.REF]);
523
392
  if (cr) citation.ref = cr;
524
393
  }
525
- const loc = mapLocalityFromJsonLd(c['gl:locality']);
394
+ const loc = mapLocalityFromJsonLd(c[GL.LOCALITY]);
526
395
  if (loc) citation.locality = loc;
527
- if (c['gl:link']) citation.link = c['gl:link'];
396
+ if (c[GL.LINK]) citation.link = c[GL.LINK];
528
397
  if (Object.keys(citation).length > 0) result.citation = citation;
529
398
  }
530
399
 
@@ -534,87 +403,95 @@ function mapRelatedFromJsonLd(r: JsonLdRelated): Record<string, unknown> {
534
403
  function mapLocalizedFromJsonLd(lc: JsonLdLocalizedConcept): Record<string, unknown> {
535
404
  const data: Record<string, unknown> = {};
536
405
 
537
- if (lc['gl:languageCode']) data.language_code = lc['gl:languageCode'];
538
- if (lc['gl:entryStatus']) data.entry_status = lc['gl:entryStatus'];
539
- if (lc['gl:classification']) data.classification = lc['gl:classification'];
540
- if (lc['gl:reviewType']) data.review_type = lc['gl:reviewType'];
541
- if (lc['gl:domain']) data.domain = lc['gl:domain'];
542
- if (lc['gl:release']) data.release = lc['gl:release'];
543
- if (lc['gl:lineageSourceSimilarity'] != null) data.lineage_source_similarity = lc['gl:lineageSourceSimilarity'];
544
- if (lc['gl:script']) data.script = lc['gl:script'];
545
- if (lc['gl:system']) data.system = lc['gl:system'];
546
-
547
- if (lc['gl:designation']?.length) {
548
- data.terms = lc['gl:designation'].map(mapDesignationFromJsonLd);
406
+ if (lc[GL.LANGUAGE_CODE]) data.language_code = lc[GL.LANGUAGE_CODE];
407
+ if (lc[GL.ENTRY_STATUS]) data.entry_status = lc[GL.ENTRY_STATUS];
408
+ if (lc[GL.CLASSIFICATION]) data.classification = lc[GL.CLASSIFICATION];
409
+ if (lc[GL.REVIEW_TYPE]) data.review_type = lc[GL.REVIEW_TYPE];
410
+ if (lc[GL.DOMAIN]) data.domain = lc[GL.DOMAIN];
411
+ if (lc[GL.RELEASE]) data.release = lc[GL.RELEASE];
412
+ if (lc[GL.LINEAGE_SOURCE_SIMILARITY] != null) data.lineage_source_similarity = lc[GL.LINEAGE_SOURCE_SIMILARITY];
413
+ if (lc[GL.SCRIPT]) data.script = lc[GL.SCRIPT];
414
+ if (lc[GL.SYSTEM]) data.system = lc[GL.SYSTEM];
415
+
416
+ const designation = lc[GL.DESIGNATION];
417
+ if (designation?.length) {
418
+ data.terms = designation.map(mapDesignationFromJsonLd);
549
419
  }
550
420
 
551
- if (lc['gl:definition']?.length) {
552
- data.definition = lc['gl:definition'].map(mapDetailedDefinitionFromJsonLd);
421
+ const definition = lc[GL.DEFINITION];
422
+ if (definition?.length) {
423
+ data.definition = definition.map(mapDetailedDefinitionFromJsonLd);
553
424
  }
554
425
 
555
- if (lc['gl:notes']?.length) {
556
- data.notes = lc['gl:notes'].map(mapDetailedDefinitionFromJsonLd);
426
+ const notes = lc[GL.NOTES];
427
+ if (notes?.length) {
428
+ data.notes = notes.map(mapDetailedDefinitionFromJsonLd);
557
429
  }
558
430
 
559
- if (lc['gl:annotations']?.length) {
560
- data.annotations = lc['gl:annotations'].map(mapDetailedDefinitionFromJsonLd);
431
+ const annotations = lc[GL.ANNOTATIONS];
432
+ if (annotations?.length) {
433
+ data.annotations = annotations.map(mapDetailedDefinitionFromJsonLd);
561
434
  }
562
435
 
563
- if (lc['gl:examples']?.length) {
564
- data.examples = lc['gl:examples'].map(mapDetailedDefinitionFromJsonLd);
436
+ const examples = lc[GL.EXAMPLES];
437
+ if (examples?.length) {
438
+ data.examples = examples.map(mapDetailedDefinitionFromJsonLd);
565
439
  }
566
440
 
567
- if (lc['gl:source']?.length) {
568
- data.sources = lc['gl:source'].map(mapSourceFromJsonLd);
441
+ const lcSources = lc[GL.SOURCE];
442
+ if (lcSources?.length) {
443
+ data.sources = lcSources.map(mapSourceFromJsonLd);
569
444
  }
570
445
 
571
- if (lc['gl:dates']?.length) {
572
- data.dates = lc['gl:dates'].map(d => ({
573
- date: d['gl:date'] ?? null,
574
- type: d['gl:dateType'] ?? null,
446
+ const dates = lc[GL.DATES];
447
+ if (dates?.length) {
448
+ data.dates = dates.map(d => ({
449
+ date: d[GL.DATE] ?? null,
450
+ type: d[GL.DATE_TYPE] ?? null,
575
451
  }));
576
452
  }
577
453
 
578
- if (lc['gl:references']?.length) {
579
- data.related = lc['gl:references'].map(mapRelatedFromJsonLd);
454
+ const lcReferences = lc[GL.REFERENCES];
455
+ if (lcReferences?.length) {
456
+ data.related = lcReferences.map(mapRelatedFromJsonLd);
580
457
  }
581
458
 
582
- if (lc['gl:reviewDate']) data.review_date = lc['gl:reviewDate'];
583
- if (lc['gl:reviewDecisionDate']) data.review_decision_date = lc['gl:reviewDecisionDate'];
584
- if (lc['gl:reviewDecisionEvent']) data.review_decision_event = lc['gl:reviewDecisionEvent'];
585
- if (lc['gl:reviewStatus']) data.review_status = lc['gl:reviewStatus'];
586
- if (lc['gl:reviewDecision']) data.review_decision = lc['gl:reviewDecision'];
587
- if (lc['gl:reviewDecisionNotes']) data.review_decision_notes = lc['gl:reviewDecisionNotes'];
459
+ if (lc[GL.REVIEW_DATE]) data.review_date = lc[GL.REVIEW_DATE];
460
+ if (lc[GL.REVIEW_DECISION_DATE]) data.review_decision_date = lc[GL.REVIEW_DECISION_DATE];
461
+ if (lc[GL.REVIEW_DECISION_EVENT]) data.review_decision_event = lc[GL.REVIEW_DECISION_EVENT];
462
+ if (lc[GL.REVIEW_STATUS]) data.review_status = lc[GL.REVIEW_STATUS];
463
+ if (lc[GL.REVIEW_DECISION]) data.review_decision = lc[GL.REVIEW_DECISION];
464
+ if (lc[GL.REVIEW_DECISION_NOTES]) data.review_decision_notes = lc[GL.REVIEW_DECISION_NOTES];
588
465
 
589
466
  return data;
590
467
  }
591
468
 
592
469
  function mapPartitiveRelationFromJsonLd(r: JsonLdPartitiveRelation): Record<string, unknown> | null {
593
- const comprehensive = r['gl:comprehensive'] ? mapRefFromJsonLd(r['gl:comprehensive']) : null;
470
+ const comprehensive = r[GL.COMPREHENSIVE] ? mapRefFromJsonLd(r[GL.COMPREHENSIVE]) : null;
594
471
  if (!comprehensive) return null;
595
472
 
596
- const partitives = (r['gl:hasPartitive'] ?? [])
473
+ const partitives = (r[GL.HAS_PARTITIVE] ?? [])
597
474
  .map((m): Record<string, unknown> | null => {
598
- const ref = m['gl:ref'] ? mapRefFromJsonLd(m['gl:ref']) : null;
475
+ const ref = m[GL.REF] ? mapRefFromJsonLd(m[GL.REF]) : null;
599
476
  if (!ref) return null;
600
477
  const out: Record<string, unknown> = { ref };
601
478
  // ISO 704:2022 MECE: prefer presence × count from JSON-LD. Fall back
602
479
  // to migrating the legacy one-string `multiplicity` or v2 `certainty`
603
480
  // so data in transit from older glossarist versions still loads.
604
- const presence = m['gl:presence'];
605
- const count = m['gl:count'];
481
+ const presence = m[GL.PRESENCE];
482
+ const count = m[GL.COUNT];
606
483
  if (isPartitivePresence(presence) && isPartitiveCount(count)) {
607
484
  out.presence = presence;
608
485
  out.count = count;
609
486
  } else {
610
- const raw = m['gl:multiplicity'] ?? splitLegacyCertainty(m['gl:certainty']);
487
+ const raw = m[GL.MULTIPLICITY] ?? splitLegacyCertainty(m[GL.CERTAINTY]);
611
488
  if (raw) {
612
489
  const parts = splitMultiplicity(raw);
613
490
  out.presence = parts.presence;
614
491
  out.count = parts.count;
615
492
  }
616
493
  }
617
- if (m['gl:isDelimiting'] === true) out.is_delimiting = true;
494
+ if (m[GL.IS_DELIMITING] === true) out.is_delimiting = true;
618
495
  return out;
619
496
  })
620
497
  .filter((m): m is Record<string, unknown> => m !== null);
@@ -628,12 +505,12 @@ function mapPartitiveRelationFromJsonLd(r: JsonLdPartitiveRelation): Record<stri
628
505
  partitives,
629
506
  };
630
507
 
631
- if (r['gl:completeness']) out.completeness = r['gl:completeness'];
508
+ if (r[GL.COMPLETENESS]) out.completeness = r[GL.COMPLETENESS];
632
509
 
633
- if (r['gl:criterion']) {
634
- out.criterion = typeof r['gl:criterion'] === 'string'
635
- ? { default: r['gl:criterion'] }
636
- : r['gl:criterion'];
510
+ if (r[GL.CRITERION]) {
511
+ out.criterion = typeof r[GL.CRITERION] === 'string'
512
+ ? { default: r[GL.CRITERION] }
513
+ : r[GL.CRITERION];
637
514
  }
638
515
 
639
516
  return out;
@@ -658,33 +535,34 @@ function splitMultiplicity(m: string): { presence: 'required' | 'optional'; coun
658
535
  }
659
536
 
660
537
  function conceptFromJsonLd(doc: JsonLdConcept): Concept {
661
- const id = String(doc['gl:identifier'] ?? doc['@id']?.split('/').pop() ?? '');
538
+ const id = String(doc[GL.IDENTIFIER] ?? doc['@id']?.split('/').pop() ?? '');
662
539
  const localizations: Record<string, unknown> = {};
663
540
 
664
- const rawLc = doc['gl:localizedConcept'] ?? {};
541
+ const rawLc = doc[GL.LOCALIZED_CONCEPT] ?? {};
665
542
  for (const [lang, lc] of Object.entries(rawLc)) {
666
543
  if (lc && typeof lc === 'object') {
667
544
  localizations[lang] = mapLocalizedFromJsonLd(lc);
668
545
  }
669
546
  }
670
547
 
671
- const related = (doc['gl:related'] ?? []).map(mapRelatedFromJsonLd);
672
- const partitiveRelations = (doc['gl:partitiveRelations'] ?? [])
548
+ const related = (doc[GL.RELATED] ?? []).map(mapRelatedFromJsonLd);
549
+ const partitiveRelations = (doc[GL.PARTITIVE_RELATIONS] ?? [])
673
550
  .map(mapPartitiveRelationFromJsonLd)
674
551
  .filter((r): r is Record<string, unknown> => r !== null);
675
- const tags = Array.isArray(doc['gl:tags']) ? [...doc['gl:tags']] : [];
552
+ const tagsArr = doc[GL.TAGS];
553
+ const tags = Array.isArray(tagsArr) ? [...tagsArr] : [];
676
554
 
677
555
  const concept = Concept.fromJSON({
678
556
  id,
679
- term: doc['gl:term'] ?? null,
557
+ term: doc[GL.TERM] ?? null,
680
558
  uri: doc['@id'] ?? null,
681
559
  localizations,
682
560
  related,
683
561
  partitive_relations: partitiveRelations,
684
562
  tags,
685
- figures: normalizeEntityRefs(doc['gl:figureRef']),
686
- tables: normalizeEntityRefs(doc['gl:tableRef']),
687
- formulas: normalizeEntityRefs(doc['gl:formulaRef']),
563
+ figures: normalizeEntityRefs(doc[GL.FIGURE_REF]),
564
+ tables: normalizeEntityRefs(doc[GL.TABLE_REF]),
565
+ formulas: normalizeEntityRefs(doc[GL.FORMULA_REF]),
688
566
  status: null,
689
567
  });
690
568
 
@@ -722,11 +600,8 @@ function normalizeOneEntityRef(entry: unknown): Record<string, string> | null {
722
600
  const entityId = (atId ? lastPathSegment(atId) : null) ?? explicitRef;
723
601
  if (!entityId) return null;
724
602
  const out: Record<string, string> = { ref: entityId };
725
- const display = typeof obj['gl:display'] === 'string' ? obj['gl:display']
726
- : typeof obj['gloss:display'] === 'string' ? obj['gloss:display']
727
- : typeof obj.display === 'string' ? obj.display
728
- : null;
729
- if (display) out.display = display;
603
+ const displayRaw = obj[GL.DISPLAY] ?? obj['gloss:display'] ?? obj.display;
604
+ if (typeof displayRaw === 'string') out.display = displayRaw;
730
605
  return out;
731
606
  }
732
607