@glossarist/concept-browser 0.7.101 → 0.7.103

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.
Files changed (45) hide show
  1. package/cli/index.mjs +6 -28
  2. package/cli/index.ts +289 -0
  3. package/package.json +2 -2
  4. package/src/__tests__/__fixtures__/concepts.ts +3 -3
  5. package/src/__tests__/composables/use-concept-edges-v2.test.ts +1 -0
  6. package/src/__tests__/designation-relationship.test.ts +2 -2
  7. package/src/__tests__/perf/serialization-perf.test.ts +1 -0
  8. package/src/__tests__/use-rdf-document.test.ts +1 -0
  9. package/src/adapters/bibliography-adapter.ts +1 -1
  10. package/src/adapters/model-bridge.ts +15 -15
  11. package/src/adapters/non-verbal/figure-bridge.ts +2 -2
  12. package/src/adapters/non-verbal/formula-bridge.ts +2 -2
  13. package/src/adapters/non-verbal/table-bridge.ts +2 -2
  14. package/src/components/CitationDisplay.vue +1 -1
  15. package/src/components/ConceptDetail.vue +2 -2
  16. package/src/components/ConceptDiffModal.vue +2 -2
  17. package/src/components/ConceptDiffView.vue +1 -1
  18. package/src/components/DesignationList.vue +4 -4
  19. package/src/components/NavIcon.vue +1 -0
  20. package/src/components/figure/FigureDisplay.vue +5 -5
  21. package/src/components/formula/FormulaDisplay.vue +4 -4
  22. package/src/components/table/TableDisplay.vue +3 -3
  23. package/src/components/use-rdf-document.ts +16 -7
  24. package/src/composables/use-concept-content.ts +3 -3
  25. package/src/composables/use-concept-diff.ts +2 -2
  26. package/src/composables/use-concept-edges.ts +3 -3
  27. package/src/config/page-types.ts +1 -1
  28. package/src/i18n/locales/eng.yml +12 -0
  29. package/src/i18n/locales/fra.yml +12 -0
  30. package/src/layouts/Default.astro +32 -2
  31. package/src/utils/content-renderer.ts +1 -1
  32. package/src/utils/partitive-multiplicity.ts +3 -2
  33. package/src/views/ConceptView.vue +3 -3
  34. package/src/views/learn/LearnRelationshipsView.vue +63 -0
  35. package/tailwind.config.js +3 -3
  36. package/tsconfig.json +1 -1
  37. package/src/adapters/model-bridge/bridges.ts +0 -159
  38. package/src/adapters/model-bridge/concept.ts +0 -97
  39. package/src/adapters/model-bridge/entity-refs.ts +0 -49
  40. package/src/adapters/model-bridge/hyperedge.ts +0 -103
  41. package/src/adapters/model-bridge/index.ts +0 -46
  42. package/src/adapters/model-bridge/mappers.ts +0 -277
  43. package/src/adapters/model-bridge/partitive.ts +0 -79
  44. package/src/adapters/non-verbal/glossarist-augment.d.ts +0 -339
  45. /package/src/adapters/{model-bridge/jsonld-types.ts → jsonld-types.ts} +0 -0
@@ -1,159 +0,0 @@
1
- /**
2
- * WeakMap bridges for fields not yet in glossarist-js's published d.ts.
3
- *
4
- * Each bridge attaches data the model doesn't expose via typed
5
- * properties. Getters retrieve it; attachBridges() populates it after
6
- * Concept.fromJSON creates the model instances.
7
- *
8
- * Remove each bridge when glossarist-js publishes native support
9
- * (tracked by PR glossarist/glossarist-js#31 and BRIDGES.md).
10
- */
11
- import {
12
- Concept,
13
- LocalizedConcept,
14
- ConceptRef,
15
- Citation,
16
- DetailedDefinition,
17
- } from 'glossarist';
18
-
19
- // Annotations: LocalizedConcept.annotations
20
- const extraAnnotations = new WeakMap<LocalizedConcept, DetailedDefinition[]>();
21
-
22
- export function getAnnotations(lc: LocalizedConcept): DetailedDefinition[] {
23
- return extraAnnotations.get(lc) ?? [];
24
- }
25
-
26
- // Designation relationship targets: RelatedConcept.target (string)
27
- const designationTargets = new WeakMap<object, string>();
28
-
29
- export function getDesignationTarget(rc: { type?: string | null; content?: string | null; target?: string | null; ref?: any }): string | null {
30
- return designationTargets.get(rc) ?? rc.target ?? null;
31
- }
32
-
33
- // ConceptRef text: human-readable label alongside source/id
34
- const refTexts = new WeakMap<ConceptRef, string>();
35
-
36
- export function getRefText(ref: ConceptRef): string | null {
37
- return refTexts.get(ref) ?? null;
38
- }
39
-
40
- // RelatedConcept.sourceId: links a citation reference back to its source entry
41
- const relatedSourceIds = new WeakMap<object, string>();
42
-
43
- export function getRelatedSourceId(rc: object): string | null {
44
- return relatedSourceIds.get(rc) ?? null;
45
- }
46
-
47
- // RelatedConcept.citation: embedded citation data for cite-ref references
48
- const relatedCitations = new WeakMap<object, Record<string, unknown>>();
49
-
50
- export function getRelatedCitation(rc: object): Record<string, unknown> | null {
51
- return relatedCitations.get(rc) ?? null;
52
- }
53
-
54
- // Relationship types whose target is a designation string, not a concept ref.
55
- export const DESIGNATION_REL_TYPES = new Set(['abbreviated_form_for', 'short_form_for']);
56
-
57
- export function attachSourcedFromToSources(
58
- sources: readonly { sourced_from?: unknown }[],
59
- rawSources: unknown,
60
- ): void {
61
- if (!Array.isArray(rawSources)) return;
62
- for (let i = 0; i < sources.length && i < rawSources.length; i++) {
63
- const raw = rawSources[i] as Record<string, unknown> | undefined;
64
- if (raw?.sourced_from && Array.isArray(raw.sourced_from)) {
65
- (sources[i] as { sourced_from: unknown }).sourced_from = raw.sourced_from.map(
66
- (sf: unknown) => sf instanceof Citation ? sf : Citation.fromJSON(sf as Record<string, unknown>)
67
- );
68
- }
69
- }
70
- }
71
-
72
- /**
73
- * Attach bridged fields (ref text, sourceId, citation) to RelatedConcept instances
74
- * from the raw deserialized data. Called after Concept.fromJSON creates the model
75
- * instances, since RelatedConcept.fromJSON only reads type/content/ref.
76
- */
77
- function attachRelatedBridges(
78
- modelRelated: Array<{ type?: string | null; content?: Record<string, string> | string | null; ref?: any; related?: any[] }>,
79
- rawRelated: unknown[],
80
- ): void {
81
- for (const rawRel of rawRelated) {
82
- if (!rawRel || typeof rawRel !== 'object') continue;
83
- const rel = rawRel as Record<string, unknown>;
84
- const relType = rel.type as string | undefined;
85
- const rc = relType ? modelRelated.find(r => r.type === relType) : undefined;
86
- if (!rc) continue;
87
-
88
- // Ref text
89
- if (rc.ref) {
90
- const rawRef = rel.ref as Record<string, unknown> | undefined;
91
- if (rawRef?.text && typeof rawRef.text === 'string') {
92
- refTexts.set(rc.ref, rawRef.text);
93
- }
94
- }
95
-
96
- // Designation target
97
- if (rel.target && typeof rel.target === 'string') {
98
- designationTargets.set(rc, rel.target);
99
- }
100
-
101
- // Source ID — links citation reference back to the ConceptSource entry
102
- if (rel.sourceId && typeof rel.sourceId === 'string') {
103
- relatedSourceIds.set(rc, rel.sourceId);
104
- }
105
-
106
- // Citation — embedded origin data for cite-ref references
107
- if (rel.citation && typeof rel.citation === 'object') {
108
- relatedCitations.set(rc, rel.citation as Record<string, unknown>);
109
- }
110
- }
111
- }
112
-
113
- export function attachBridges(concept: Concept, localizations: Record<string, unknown>): void {
114
- for (const lang of concept.languages) {
115
- const lc = concept.localization(lang);
116
- const raw = localizations[lang];
117
- if (!lc || !raw || typeof raw !== 'object') continue;
118
- const rawObj = raw as Record<string, unknown>;
119
-
120
- // Attach sourced_from to localization-level sources
121
- attachSourcedFromToSources(lc.sources as unknown as { sourced_from?: unknown }[], rawObj.sources);
122
-
123
- // Annotations
124
- const annList = rawObj.annotations;
125
- if (Array.isArray(annList) && annList.length > 0) {
126
- extraAnnotations.set(lc, annList.map((a: Record<string, unknown>) =>
127
- DetailedDefinition.fromJSON({ content: (a.content as string) ?? '' }) as DetailedDefinition,
128
- ));
129
- }
130
-
131
- // Designation-level relationship targets, ref text, sourceId, citation
132
- const rawTerms = rawObj.terms;
133
- if (Array.isArray(rawTerms)) {
134
- for (let ti = 0; ti < rawTerms.length; ti++) {
135
- const rawTerm = rawTerms[ti];
136
- if (!rawTerm || typeof rawTerm !== 'object') continue;
137
- const rawT = rawTerm as Record<string, unknown>;
138
- const rawDesignation = rawT.designation as string | undefined;
139
- if (!rawDesignation) continue;
140
- const designation = lc.terms.find(d => d.designation === rawDesignation);
141
- if (!designation) continue;
142
- const rawRelated = rawT.related;
143
- if (!Array.isArray(rawRelated)) continue;
144
- attachRelatedBridges(designation.related, rawRelated);
145
- // Attach sourced_from to designation-level sources
146
- attachSourcedFromToSources(
147
- designation.sources as unknown as { sourced_from?: unknown }[],
148
- rawT.sources,
149
- );
150
- }
151
- }
152
-
153
- // Localization-level related concepts
154
- const rawRelated = rawObj.related;
155
- if (Array.isArray(rawRelated)) {
156
- attachRelatedBridges(lc.related, rawRelated);
157
- }
158
- }
159
- }
@@ -1,97 +0,0 @@
1
- /**
2
- * Top-level Concept loading: JSON-LD detection, Concept.fromJSON
3
- * orchestration, summary projection, URI construction.
4
- *
5
- * This module is the orchestration layer — mappers, bridges, and
6
- * partitive mapping live in their respective sibling modules.
7
- */
8
- import { Concept } from 'glossarist';
9
- import type { ConceptSummary } from '../types';
10
- import { ConceptIdentity } from '../concept-identity';
11
- import { GL } from '../wire-keys';
12
- import { isJsonLd, mapLocalizedFromJsonLd, mapRelatedFromJsonLd } from './mappers';
13
- import { mapHyperedgeFromJsonLd } from './hyperedge';
14
- import { normalizeEntityRefs } from './entity-refs';
15
- import { attachBridges, attachSourcedFromToSources } from './bridges';
16
- import type { JsonLdConcept } from './jsonld-types';
17
-
18
- function conceptFromJsonLd(doc: JsonLdConcept): Concept {
19
- const id = String(doc[GL.IDENTIFIER] ?? doc['@id']?.split('/').pop() ?? '');
20
- const localizations: Record<string, unknown> = {};
21
-
22
- const rawLc = doc[GL.LOCALIZED_CONCEPT] ?? {};
23
- for (const [lang, lc] of Object.entries(rawLc)) {
24
- if (lc && typeof lc === 'object') {
25
- localizations[lang] = mapLocalizedFromJsonLd(lc);
26
- }
27
- }
28
-
29
- const related = (doc[GL.RELATED] ?? []).map(mapRelatedFromJsonLd);
30
-
31
- // Unified hyperedge mapping: both partitive and generic relations
32
- // share the same JSON-LD wire shape. glossarist-js dispatches by
33
- // wire key (`partitive_relations` / `generic_relations`).
34
- const partitiveRelations = (doc[GL.PARTITIVE_RELATIONS] ?? [])
35
- .map((r: any) => mapHyperedgeFromJsonLd(r, 'gl:hasPartitive'))
36
- .filter((r): r is Record<string, unknown> => r !== null);
37
-
38
- const genericRelations = (doc[GL.GENERIC_RELATIONS] ?? [])
39
- .map((r: any) => mapHyperedgeFromJsonLd(r, 'gl:hasGeneric'))
40
- .filter((r): r is Record<string, unknown> => r !== null);
41
-
42
- const tagsArr = doc[GL.TAGS];
43
- const tags = Array.isArray(tagsArr) ? [...tagsArr] : [];
44
-
45
- const concept = Concept.fromJSON({
46
- id,
47
- term: doc[GL.TERM] ?? null,
48
- uri: doc['@id'] ?? null,
49
- localizations,
50
- related,
51
- partitive_relations: partitiveRelations,
52
- generic_relations: genericRelations,
53
- tags,
54
- figures: normalizeEntityRefs(doc[GL.FIGURE_REF]),
55
- tables: normalizeEntityRefs(doc[GL.TABLE_REF]),
56
- formulas: normalizeEntityRefs(doc[GL.FORMULA_REF]),
57
- status: null,
58
- });
59
-
60
- attachBridges(concept, localizations);
61
- return concept;
62
- }
63
-
64
- export function conceptFromJson(doc: Record<string, unknown>): Concept {
65
- if (isJsonLd(doc)) {
66
- return conceptFromJsonLd(doc as JsonLdConcept);
67
- }
68
- const concept = Concept.fromJSON(doc);
69
- const locs = (doc as Record<string, unknown>).localizations as Record<string, unknown> | undefined;
70
- if (locs) attachBridges(concept, locs);
71
- attachSourcedFromToSources(
72
- concept.sources as unknown as { sourced_from?: unknown }[],
73
- (doc as Record<string, unknown>).sources,
74
- );
75
- return concept;
76
- }
77
-
78
- export function conceptToSummary(concept: Concept): ConceptSummary {
79
- const designations: Record<string, string> = {};
80
- for (const lang of concept.languages) {
81
- const lc = concept.localization(lang);
82
- if (lc?.primaryDesignation) {
83
- designations[lang] = lc.primaryDesignation;
84
- }
85
- }
86
- return {
87
- id: concept.id,
88
- designations,
89
- eng: designations.eng || Object.values(designations)[0] || '',
90
- status: concept.status ?? 'valid',
91
- };
92
- }
93
-
94
- export function conceptUri(concept: Concept, registerId: string, uriBase: string): string {
95
- if (concept.uri) return concept.uri;
96
- return new ConceptIdentity(concept.id, registerId, uriBase).uri;
97
- }
@@ -1,49 +0,0 @@
1
- /**
2
- * Non-verbal entity reference normalization.
3
- *
4
- * Normalizes JSON-LD structural entity refs (`gl:figureRef` /
5
- * `gl:tableRef` / `gl:formulaRef`) into the shape
6
- * `NonVerbalReference.fromJSON` expects.
7
- *
8
- * Accepts three wire forms:
9
- * - bare string ID
10
- * - `{ "@id": "../kind/foo" }`
11
- * - `{ "@id": "../kind/foo", "gl:display": "Figure 3" }`
12
- *
13
- * Emits the canonical `{ ref, display? }` shape. The path's last
14
- * segment is the entity id; the field name (`figureRef` vs `tableRef`
15
- * vs `formulaRef`) is the kind discriminator upstream.
16
- */
17
- import { GL } from '../wire-keys';
18
-
19
- export function normalizeEntityRefs(raw: unknown): unknown[] {
20
- if (!Array.isArray(raw)) return [];
21
- return raw.map(normalizeOneEntityRef).filter((v): v is Record<string, string> => v !== null);
22
- }
23
-
24
- function normalizeOneEntityRef(entry: unknown): Record<string, string> | null {
25
- if (typeof entry === 'string') {
26
- const trimmed = entry.trim();
27
- return trimmed ? { ref: trimmed } : null;
28
- }
29
- if (!entry || typeof entry !== 'object') return null;
30
- const obj = entry as Record<string, unknown>;
31
- const atId = typeof obj['@id'] === 'string' ? (obj['@id'] as string) : null;
32
- const explicitRef = typeof obj.ref === 'string' ? obj.ref
33
- : typeof obj.entityId === 'string' ? obj.entityId
34
- : typeof obj.entity_id === 'string' ? obj.entity_id
35
- : null;
36
- const entityId = (atId ? lastPathSegment(atId) : null) ?? explicitRef;
37
- if (!entityId) return null;
38
- const out: Record<string, string> = { ref: entityId };
39
- const displayRaw = obj[GL.DISPLAY] ?? obj['gloss:display'] ?? obj.display;
40
- if (typeof displayRaw === 'string') out.display = displayRaw;
41
- return out;
42
- }
43
-
44
- function lastPathSegment(p: string): string | null {
45
- const cleaned = p.replace(/[?#].*$/, '').replace(/\/+$/, '');
46
- const segments = cleaned.split('/');
47
- const last = segments[segments.length - 1];
48
- return last ? decodeURIComponent(last) : null;
49
- }
@@ -1,103 +0,0 @@
1
- /**
2
- * Hyperedge (n-ary relation) mapping: JSON-LD → glossarist-js model.
3
- *
4
- * Handles ISO 704:2022 n-ary relations — both PartitiveHyperedge and
5
- * GenericHyperedge share the same JSON-LD wire shape. The mapper is
6
- * identical for both; the glossarist-js Concept constructor dispatches
7
- * by wire key (`partitive_relations` vs `generic_relations`).
8
- *
9
- * MECE migration: reads presence × count directly (ISO 704:2022 v3).
10
- * Falls back to migrating the legacy 5-value `multiplicity` or v2
11
- * `certainty` field.
12
- */
13
- import { GL } from '../wire-keys';
14
- import { isPartitivePresence, isPartitiveCount } from '../../utils/partitive-multiplicity';
15
- import { mapRefFromJsonLd } from './mappers';
16
- import type { JsonLdPartitiveRelation } from './jsonld-types';
17
-
18
- /**
19
- * Map a JSON-LD relation entry to the raw dict shape that
20
- * glossarist-js's AbstractHyperedge subclasses accept.
21
- *
22
- * Works for both PartitiveHyperedge and GenericHyperedge — the only
23
- * difference at the wire level is which wire key the parent dict
24
- * uses (`partitive_relations` vs `generic_relations`).
25
- *
26
- * Reads `gl:hasPartitive`, `gl:hasGeneric`, or `gl:members` for
27
- * the member array (in order of preference).
28
- */
29
- export function mapHyperedgeFromJsonLd(
30
- r: JsonLdPartitiveRelation,
31
- memberKey: 'gl:hasPartitive' | 'gl:hasGeneric' | 'gl:members' = 'gl:hasPartitive',
32
- ): Record<string, unknown> | null {
33
- const comprehensive = r[GL.COMPREHENSIVE] ? mapRefFromJsonLd(r[GL.COMPREHENSIVE]) : null;
34
- if (!comprehensive) return null;
35
-
36
- const rawMembers = r[GL.HAS_PARTITIVE] ?? r[GL.HAS_GENERIC] ?? r[GL.HAS_MEMBER] ?? r[memberKey] ?? [];
37
- const partitives = (rawMembers as any[])
38
- .map((m): Record<string, unknown> | null => {
39
- const ref = m[GL.REF] ? mapRefFromJsonLd(m[GL.REF]) : null;
40
- if (!ref) return null;
41
- const out: Record<string, unknown> = { ref };
42
- const presence = m[GL.PRESENCE];
43
- const count = m[GL.COUNT];
44
- if (isPartitivePresence(presence) && isPartitiveCount(count)) {
45
- out.presence = presence;
46
- out.count = count;
47
- } else {
48
- const raw = m[GL.MULTIPLICITY] ?? splitLegacyCertainty(m[GL.CERTAINTY]);
49
- if (raw) {
50
- const parts = splitMultiplicity(raw);
51
- out.presence = parts.presence;
52
- out.count = parts.count;
53
- }
54
- }
55
- if (m[GL.IS_DELIMITING] === true) out.is_delimiting = true;
56
- // ISO 704:2022 §5.5.4.2.1 — GenericMember carries a required
57
- // delimitingCharacteristic (the intension difference between
58
- // this specific concept and its coordinate concepts).
59
- const dc = m[GL.DELIMITING_CHARACTERISTIC];
60
- if (dc) {
61
- out.delimitingCharacteristic = typeof dc === 'string'
62
- ? { default: dc }
63
- : dc;
64
- }
65
- return out;
66
- })
67
- .filter((m): m is Record<string, unknown> => m !== null);
68
-
69
- if (partitives.length < 2) return null;
70
-
71
- const out: Record<string, unknown> = {
72
- comprehensive,
73
- members: partitives,
74
- };
75
-
76
- if (r[GL.COMPLETENESS]) out.completeness = r[GL.COMPLETENESS];
77
-
78
- if (r[GL.CRITERION]) {
79
- out.criterion = typeof r[GL.CRITERION] === 'string'
80
- ? { default: r[GL.CRITERION] }
81
- : r[GL.CRITERION];
82
- }
83
-
84
- return out;
85
- }
86
-
87
- /** Migrate legacy v2 certainty → old v3 multiplicity string. */
88
- function splitLegacyCertainty(certainty: string | undefined): string | null {
89
- if (!certainty) return null;
90
- return certainty === 'possible' ? 'optional' : 'compulsory';
91
- }
92
-
93
- /** Split the legacy one-string multiplicity into MECE presence × count. */
94
- function splitMultiplicity(m: string): { presence: 'required' | 'optional'; count: 'exactly_one' | 'at_least_one' | 'multiple' } {
95
- const LEGACY_MAP: Record<string, { presence: 'required' | 'optional'; count: 'exactly_one' | 'at_least_one' | 'multiple' }> = {
96
- compulsory: { presence: 'required', count: 'exactly_one' },
97
- optional: { presence: 'optional', count: 'exactly_one' },
98
- compulsory_multiple: { presence: 'required', count: 'multiple' },
99
- optional_multiple: { presence: 'optional', count: 'multiple' },
100
- compulsory_at_least_one: { presence: 'required', count: 'at_least_one' },
101
- };
102
- return LEGACY_MAP[m] ?? { presence: 'required', count: 'exactly_one' };
103
- }
@@ -1,46 +0,0 @@
1
- /**
2
- * Model bridge: converts between wire-format JSON and glossarist-js
3
- * model instances.
4
- *
5
- * Supports two input formats:
6
- * 1. JSON-LD (gl:-prefixed) — format produced by generate-data.mjs
7
- * 2. Glossarist native — snake_case format from glossarist-js Concept.toJSON()
8
- *
9
- * Public API is re-exported here. Internal organization:
10
- * - jsonld-types.ts — wire-format interface declarations
11
- * - bridges.ts — WeakMap bridges for fields not yet in glossarist-js
12
- * - mappers.ts — JSON-LD → snake_case mappers
13
- * - partitive.ts — PartitiveRelation mapping + MECE migration
14
- * - entity-refs.ts — non-verbal entity reference normalization
15
- * - concept.ts — top-level Concept orchestration + URI construction
16
- */
17
- export {
18
- conceptFromJson,
19
- conceptToSummary,
20
- conceptUri,
21
- } from './concept';
22
-
23
- export {
24
- getAnnotations,
25
- getDesignationTarget,
26
- getRefText,
27
- getRelatedSourceId,
28
- getRelatedCitation,
29
- } from './bridges';
30
-
31
- export type {
32
- JsonLdConcept,
33
- JsonLdLocalizedConcept,
34
- JsonLdPartitiveMember,
35
- JsonLdPartitiveRelation,
36
- JsonLdDesignation,
37
- JsonLdSource,
38
- JsonLdOrigin,
39
- JsonLdRelated,
40
- JsonLdRef,
41
- JsonLdLocality,
42
- JsonLdDate,
43
- JsonLdContent,
44
- JsonLdPronunciation,
45
- JsonLdGrammarInfo,
46
- } from './jsonld-types';