glossarist 0.2.1 → 0.3.0

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/README.md CHANGED
@@ -308,8 +308,10 @@ import 'glossarist/validators'; // validation framework
308
308
 
309
309
  ```
310
310
  Public API (index.js)
311
- ├── Domain models → Concept, LocalizedConcept, Designation (Expression, Symbol, ...),
312
- Citation, ConceptSource, RelatedConcept, DetailedDefinition, NonVerbRep
311
+ ├── Domain models → Concept, LocalizedConcept, Designation (Expression, Abbreviation, Symbol,
312
+ LetterSymbol, GraphicalSymbol), GrammarInfo, Pronunciation, Locality,
313
+ │ Citation, ConceptSource, RelatedConcept, ConceptReference, ConceptDate,
314
+ │ DetailedDefinition, NonVerbRep
313
315
  ├── Parsing → ConceptParser (canonical + managed format detection)
314
316
  ├── Serialization → ConceptSerializer (canonical + managed YAML output)
315
317
  ├── I/O → loadGcr, readConcepts, createGcr, writeConcepts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "glossarist",
3
- "version": "0.2.1",
3
+ "version": "0.3.0",
4
4
  "description": "JavaScript SDK for Glossarist GCR packages — read, write, validate, and manage terminology concepts",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
@@ -1,5 +1,7 @@
1
1
  import yaml from 'js-yaml';
2
2
  import { Concept } from './models/concept.js';
3
+ import { ConceptRef } from './models/concept-ref.js';
4
+ import { RelatedConcept } from './models/related-concept.js';
3
5
  import { InvalidInputError, YamlParseError } from './errors.js';
4
6
 
5
7
  const STRUCTURAL_KEYS = new Set(['termid', 'term']);
@@ -70,9 +72,34 @@ export class ConceptParser {
70
72
  id: String(mc.data.identifier),
71
73
  term: null,
72
74
  localizations,
75
+ related: _normalizeRelated(mc.related ?? mc.data?.related),
76
+ domains: mc.data.domains,
77
+ groups: mc.data.groups,
78
+ dates: mc.dates ?? mc.data?.dates,
79
+ sources: mc.sources ?? mc.data?.sources,
80
+ status: mc.status,
81
+ schemaVersion: mc.schema_version,
73
82
  raw: mc,
74
83
  });
75
84
  }
76
85
  }
77
86
 
87
+ function _normalizeRelated(arr) {
88
+ if (!arr || !Array.isArray(arr)) return [];
89
+ return arr.map(r => {
90
+ if (r instanceof RelatedConcept) return r;
91
+ const data = { ...r };
92
+ if (data.ref) {
93
+ if (typeof data.ref !== 'object' || data.ref === null) {
94
+ throw new InvalidInputError(
95
+ `RelatedConcept.ref must be an object { source, id }, got: ${typeof data.ref}`,
96
+ 'object',
97
+ );
98
+ }
99
+ data.ref = new ConceptRef(data.ref);
100
+ }
101
+ return new RelatedConcept(data);
102
+ });
103
+ }
104
+
78
105
  export const conceptParser = new ConceptParser();
@@ -39,6 +39,22 @@ export class ConceptSerializer {
39
39
  id: genId(),
40
40
  };
41
41
 
42
+ if (concept.domains.length > 0) {
43
+ mainDoc.data.domains = concept.domains.map(d => d.toJSON());
44
+ }
45
+
46
+ if (concept.relatedConcepts.length > 0) {
47
+ mainDoc.related = concept.relatedConcepts.map(rc => rc.toJSON());
48
+ }
49
+ if (concept.sources.length > 0) {
50
+ mainDoc.sources = concept.sources.map(s => s.toJSON());
51
+ }
52
+ if (concept.dates.length > 0) {
53
+ mainDoc.dates = concept.dates.map(d => d.toJSON());
54
+ }
55
+ if (concept.status) mainDoc.status = concept.status;
56
+ if (concept.schemaVersion) mainDoc.schema_version = concept.schemaVersion;
57
+
42
58
  const parts = [
43
59
  '---\n' + yaml.dump(mainDoc, DUMP_OPTS),
44
60
  ...langDocs.map(d => '---\n' + yaml.dump(d, DUMP_OPTS)),
package/src/gcr-reader.js CHANGED
@@ -26,7 +26,7 @@ const BASE64_RE = /^[A-Za-z0-9+/]{100,}={0,2}$/;
26
26
  /**
27
27
  * @typedef {Object} Source
28
28
  * @property {string} type - e.g. 'authoritative', 'adapted'
29
- * @property {{ ref: string }} [origin] - reference to the source standard
29
+ * @property {{ ref: { source: string, id?: string, version?: string } }} [origin] - reference to the source standard
30
30
  */
31
31
 
32
32
  /**
package/src/index.d.ts CHANGED
@@ -3,7 +3,7 @@ export {
3
3
  GlossaristModel,
4
4
  Concept, LocalizedConcept,
5
5
  Designation, Expression, Abbreviation, Symbol, GraphicalSymbol,
6
- Citation, ConceptSource, RelatedConcept, ConceptDate,
6
+ Citation, ConceptRef, ConceptSource, RelatedConcept, ConceptDate,
7
7
  DetailedDefinition, NonVerbRep,
8
8
  GcrMetadata, GcrStatistics,
9
9
  RELATIONSHIP_TYPES, DATE_TYPES,
package/src/index.js CHANGED
@@ -30,7 +30,7 @@ export {
30
30
  GlossaristModel,
31
31
  Concept, LocalizedConcept,
32
32
  Designation, Expression, Abbreviation, Symbol, GraphicalSymbol,
33
- Citation, ConceptSource, RelatedConcept, ConceptDate,
33
+ Citation, ConceptRef, ConceptSource, RelatedConcept, ConceptReference, ConceptDate,
34
34
  DetailedDefinition, NonVerbRep,
35
35
  GcrMetadata, GcrStatistics,
36
36
  RELATIONSHIP_TYPES, DATE_TYPES,
@@ -1,45 +1,35 @@
1
1
  import { GlossaristModel } from './base.js';
2
+ import { Locality } from './locality.js';
2
3
 
3
4
  export class Citation extends GlossaristModel {
4
- constructor(data) {
5
+ constructor(data = {}) {
5
6
  super();
6
- if (typeof data === 'string') {
7
- this.source = data;
8
- this.ref = null;
9
- this.id = null;
10
- this.version = null;
11
- this.clause = null;
12
- this.link = null;
13
- } else {
14
- const d = data ?? {};
15
- this.source = d.source ?? null;
16
- this.ref = d.ref ?? null;
17
- this.id = d.id ?? null;
18
- this.version = d.version ?? null;
19
- this.clause = d.clause ?? null;
20
- this.link = d.link ?? null;
21
- }
22
- }
23
-
24
- get isStructured() {
25
- return typeof this.source === 'object' && this.source !== null;
7
+ const d = data ?? {};
8
+
9
+ this.ref = d.ref
10
+ ? (d.ref instanceof Citation.Ref ? d.ref : new Citation.Ref(d.ref))
11
+ : null;
12
+
13
+ this.locality = d.locality
14
+ ? (d.locality instanceof Locality ? d.locality : new Locality(d.locality))
15
+ : null;
16
+ this.link = d.link ?? null;
17
+ this.original = d.original ?? null;
18
+ this.customLocality = d.custom_locality ?? d.customLocality ?? null;
26
19
  }
27
20
 
28
21
  toString() {
29
- if (this.ref) return this.ref;
30
- if (typeof this.source === 'string') return this.source;
31
- if (typeof this.source === 'object' && this.source !== null) return this.source.ref ?? '';
22
+ if (this.ref) return this.ref.toString();
32
23
  return '';
33
24
  }
34
25
 
35
26
  toJSON() {
36
27
  const obj = {};
37
- if (this.source != null) obj.source = this.source;
38
- if (this.ref != null) obj.ref = this.ref;
39
- if (this.id != null) obj.id = this.id;
40
- if (this.version != null) obj.version = this.version;
41
- if (this.clause != null) obj.clause = this.clause;
28
+ if (this.ref != null) obj.ref = this.ref.toJSON();
29
+ if (this.locality != null) obj.locality = this.locality.toJSON();
42
30
  if (this.link != null) obj.link = this.link;
31
+ if (this.original != null) obj.original = this.original;
32
+ if (this.customLocality != null) obj.custom_locality = this.customLocality;
43
33
  return obj;
44
34
  }
45
35
 
@@ -47,3 +37,32 @@ export class Citation extends GlossaristModel {
47
37
  return new Citation(data);
48
38
  }
49
39
  }
40
+
41
+ Citation.Ref = class Ref extends GlossaristModel {
42
+ constructor(data = {}) {
43
+ super();
44
+ const d = data ?? {};
45
+ this.source = d.source ?? null;
46
+ this.id = d.id ?? null;
47
+ this.version = d.version ?? null;
48
+ }
49
+
50
+ toString() {
51
+ const parts = [];
52
+ if (this.source) parts.push(this.source);
53
+ if (this.id) parts.push(this.id);
54
+ return parts.join(' ');
55
+ }
56
+
57
+ toJSON() {
58
+ const obj = {};
59
+ if (this.source != null) obj.source = this.source;
60
+ if (this.id != null) obj.id = this.id;
61
+ if (this.version != null) obj.version = this.version;
62
+ return obj;
63
+ }
64
+
65
+ static fromJSON(data) {
66
+ return new Citation.Ref(data);
67
+ }
68
+ };
@@ -1,7 +1,7 @@
1
1
  import { GlossaristModel } from './base.js';
2
2
 
3
3
  export const DATE_TYPES = Object.freeze([
4
- 'accepted', 'amended', 'published', 'withdrawn', 'created',
4
+ 'accepted', 'amended', 'retired',
5
5
  ]);
6
6
 
7
7
  export class ConceptDate extends GlossaristModel {
@@ -0,0 +1,25 @@
1
+ import { GlossaristModel } from './base.js';
2
+
3
+ export class ConceptRef extends GlossaristModel {
4
+ constructor(data = {}) {
5
+ super();
6
+ this.source = data.source ?? null;
7
+ this.id = data.id ?? null;
8
+ }
9
+
10
+ toString() {
11
+ if (this.source && this.id) return `${this.source} ${this.id}`;
12
+ return this.source ?? this.id ?? '';
13
+ }
14
+
15
+ toJSON() {
16
+ const obj = {};
17
+ if (this.source != null) obj.source = this.source;
18
+ if (this.id != null) obj.id = this.id;
19
+ return obj;
20
+ }
21
+
22
+ static fromJSON(data) {
23
+ return new ConceptRef(data);
24
+ }
25
+ }
@@ -0,0 +1,36 @@
1
+ import { GlossaristModel } from './base.js';
2
+
3
+ export class ConceptReference extends GlossaristModel {
4
+ constructor(data = {}) {
5
+ super();
6
+ this.conceptId = data.concept_id ?? data.conceptId ?? null;
7
+ this.refType = data.ref_type ?? data.refType ?? null;
8
+ this.source = data.source ?? null;
9
+ this.urn = data.urn ?? null;
10
+ }
11
+
12
+ get isLocal() {
13
+ return this.urn == null && this.source == null;
14
+ }
15
+
16
+ get isExternal() {
17
+ return !this.isLocal;
18
+ }
19
+
20
+ static domain(conceptId) {
21
+ return new ConceptReference({ concept_id: conceptId, ref_type: 'domain' });
22
+ }
23
+
24
+ toJSON() {
25
+ const obj = {};
26
+ if (this.conceptId != null) obj.concept_id = this.conceptId;
27
+ if (this.refType != null) obj.ref_type = this.refType;
28
+ if (this.source != null) obj.source = this.source;
29
+ if (this.urn != null) obj.urn = this.urn;
30
+ return obj;
31
+ }
32
+
33
+ static fromJSON(data) {
34
+ return new ConceptReference(data);
35
+ }
36
+ }
@@ -1,6 +1,7 @@
1
1
  import { GlossaristModel } from './base.js';
2
2
  import { LocalizedConcept } from './localized-concept.js';
3
3
  import { RelatedConcept } from './related-concept.js';
4
+ import { ConceptReference } from './concept-reference.js';
4
5
  import { ConceptDate } from './concept-date.js';
5
6
  import { ConceptSource } from './concept-source.js';
6
7
 
@@ -9,13 +10,16 @@ export class Concept extends GlossaristModel {
9
10
  super();
10
11
  this.id = String(data.id ?? data.termid ?? '');
11
12
  this.term = data.term ?? null;
13
+ this.uri = data.uri ?? null;
12
14
  this._rawLocalizations = data.localizations ?? {};
13
15
  this._cache = {};
14
16
 
15
- this.relatedConcepts = _mapInstances(data.relatedConcepts ?? data.related_concepts ?? [], RelatedConcept);
17
+ this.relatedConcepts = _mapInstances(data.relatedConcepts ?? data.related ?? data.related_concepts ?? [], RelatedConcept);
18
+ this.domains = _normalizeDomains(data.domains, data.groups);
16
19
  this.dates = _mapInstances(data.dates ?? [], ConceptDate);
17
20
  this.sources = _mapInstances(data.sources ?? [], ConceptSource);
18
21
  this.status = data.status ?? null;
22
+ this.schemaVersion = data.schemaVersion ?? data.schema_version ?? '3';
19
23
  this.raw = data.raw ?? null;
20
24
  }
21
25
 
@@ -66,6 +70,7 @@ export class Concept extends GlossaristModel {
66
70
  toJSON() {
67
71
  const obj = { id: this.id };
68
72
  if (this.term != null) obj.term = this.term;
73
+ if (this.uri != null) obj.uri = this.uri;
69
74
 
70
75
  if (Object.keys(this._rawLocalizations).length > 0) {
71
76
  obj.localizations = {};
@@ -82,7 +87,10 @@ export class Concept extends GlossaristModel {
82
87
  }
83
88
 
84
89
  if (this.relatedConcepts.length > 0) {
85
- obj.relatedConcepts = this.relatedConcepts.map(rc => rc.toJSON());
90
+ obj.related = this.relatedConcepts.map(rc => rc.toJSON());
91
+ }
92
+ if (this.domains.length > 0) {
93
+ obj.domains = this.domains.map(d => d.toJSON());
86
94
  }
87
95
  if (this.dates.length > 0) {
88
96
  obj.dates = this.dates.map(d => d.toJSON());
@@ -91,6 +99,7 @@ export class Concept extends GlossaristModel {
91
99
  obj.sources = this.sources.map(s => s.toJSON());
92
100
  }
93
101
  if (this.status != null) obj.status = this.status;
102
+ obj.schema_version = this.schemaVersion;
94
103
  return obj;
95
104
  }
96
105
 
@@ -102,3 +111,15 @@ export class Concept extends GlossaristModel {
102
111
  function _mapInstances(arr, Cls) {
103
112
  return arr.map(item => item instanceof Cls ? item : new Cls(item));
104
113
  }
114
+
115
+ function _normalizeDomains(domains, groups) {
116
+ if (domains) {
117
+ return domains.map(d => d instanceof ConceptReference ? d : new ConceptReference(d));
118
+ }
119
+ if (groups) {
120
+ return groups.map(g => typeof g === 'string'
121
+ ? ConceptReference.domain(g)
122
+ : new ConceptReference(g));
123
+ }
124
+ return [];
125
+ }
@@ -1,4 +1,8 @@
1
1
  import { GlossaristModel } from './base.js';
2
+ import { ConceptSource } from './concept-source.js';
3
+ import { Pronunciation } from './pronunciation.js';
4
+ import { GrammarInfo } from './grammar-info.js';
5
+ import { RelatedConcept } from './related-concept.js';
2
6
 
3
7
  export class Designation extends GlossaristModel {
4
8
  static _registry = new Map();
@@ -18,11 +22,41 @@ export class Designation extends GlossaristModel {
18
22
  this.designation = data.designation ?? '';
19
23
  this.type = data.type ?? 'expression';
20
24
  this.normativeStatus = data.normative_status ?? null;
25
+ this.absent = data.absent ?? null;
26
+ this.fieldOfApplication = data.field_of_application ?? null;
27
+ this.usageInfo = data.usage_info ?? null;
28
+ this.geographicalArea = data.geographical_area ?? null;
29
+ this.language = data.language ?? null;
30
+ this.script = data.script ?? null;
31
+ this.system = data.system ?? null;
32
+ this.international = data.international ?? null;
33
+ this.termType = data.term_type ?? null;
34
+ this.pronunciations = (data.pronunciation ?? []).map(
35
+ p => p instanceof Pronunciation ? p : new Pronunciation(p)
36
+ );
37
+ this.sources = (data.sources ?? []).map(
38
+ s => s instanceof ConceptSource ? s : new ConceptSource(s)
39
+ );
40
+ this.related = (data.related ?? []).map(
41
+ r => r instanceof RelatedConcept ? r : new RelatedConcept(r)
42
+ );
21
43
  }
22
44
 
23
45
  toJSON() {
24
46
  const obj = { type: this.type, designation: this.designation };
25
47
  if (this.normativeStatus != null) obj.normative_status = this.normativeStatus;
48
+ if (this.absent != null) obj.absent = this.absent;
49
+ if (this.fieldOfApplication != null) obj.field_of_application = this.fieldOfApplication;
50
+ if (this.usageInfo != null) obj.usage_info = this.usageInfo;
51
+ if (this.geographicalArea != null) obj.geographical_area = this.geographicalArea;
52
+ if (this.language != null) obj.language = this.language;
53
+ if (this.script != null) obj.script = this.script;
54
+ if (this.system != null) obj.system = this.system;
55
+ if (this.international != null) obj.international = this.international;
56
+ if (this.termType != null) obj.term_type = this.termType;
57
+ if (this.pronunciations.length > 0) obj.pronunciation = this.pronunciations.map(p => p.toJSON());
58
+ if (this.sources.length > 0) obj.sources = this.sources.map(s => s.toJSON());
59
+ if (this.related.length > 0) obj.related = this.related.map(r => r.toJSON());
26
60
  return obj;
27
61
  }
28
62
 
@@ -34,18 +68,16 @@ export class Designation extends GlossaristModel {
34
68
  export class Expression extends Designation {
35
69
  constructor(data = {}) {
36
70
  super(data);
37
- this.gender = data.gender ?? null;
38
- this.plurality = data.plurality ?? null;
39
- this.partOfSpeech = data.part_of_speech ?? null;
40
- this.geographicalArea = data.geographical_area ?? null;
71
+ this.prefix = data.prefix ?? null;
72
+ this.grammarInfo = (data.grammar_info ?? []).map(
73
+ g => g instanceof GrammarInfo ? g : new GrammarInfo(g)
74
+ );
41
75
  }
42
76
 
43
77
  toJSON() {
44
78
  const obj = super.toJSON();
45
- if (this.gender != null) obj.gender = this.gender;
46
- if (this.plurality != null) obj.plurality = this.plurality;
47
- if (this.partOfSpeech != null) obj.part_of_speech = this.partOfSpeech;
48
- if (this.geographicalArea != null) obj.geographical_area = this.geographicalArea;
79
+ if (this.prefix != null) obj.prefix = this.prefix;
80
+ if (this.grammarInfo.length > 0) obj.grammar_info = this.grammarInfo.map(g => g.toJSON());
49
81
  return obj;
50
82
  }
51
83
 
@@ -54,37 +86,60 @@ export class Expression extends Designation {
54
86
 
55
87
  Designation.register('expression', Expression);
56
88
 
57
- export class Abbreviation extends Designation {
89
+ export class Abbreviation extends Expression {
90
+ constructor(data = {}) {
91
+ super(data);
92
+ this.acronym = data.acronym ?? false;
93
+ this.initialism = data.initialism ?? false;
94
+ this.truncation = data.truncation ?? false;
95
+ }
96
+
97
+ toJSON() {
98
+ const obj = super.toJSON();
99
+ if (this.acronym) obj.acronym = true;
100
+ if (this.initialism) obj.initialism = true;
101
+ if (this.truncation) obj.truncation = true;
102
+ return obj;
103
+ }
104
+
58
105
  static fromJSON(data) { return new Abbreviation(data); }
59
106
  }
60
107
 
61
108
  Designation.register('abbreviation', Abbreviation);
62
109
 
63
110
  export class Symbol extends Designation {
111
+ static fromJSON(data) { return new Symbol(data); }
112
+ }
113
+
114
+ Designation.register('symbol', Symbol);
115
+
116
+ export class LetterSymbol extends Symbol {
64
117
  constructor(data = {}) {
65
118
  super(data);
66
- this.international = data.international ?? null;
119
+ this.text = data.text ?? null;
67
120
  }
68
121
 
69
122
  toJSON() {
70
123
  const obj = super.toJSON();
71
- if (this.international != null) obj.international = this.international;
124
+ if (this.text != null) obj.text = this.text;
72
125
  return obj;
73
126
  }
74
127
 
75
- static fromJSON(data) { return new Symbol(data); }
128
+ static fromJSON(data) { return new LetterSymbol(data); }
76
129
  }
77
130
 
78
- Designation.register('symbol', Symbol);
131
+ Designation.register('letter_symbol', LetterSymbol);
79
132
 
80
- export class GraphicalSymbol extends Designation {
133
+ export class GraphicalSymbol extends Symbol {
81
134
  constructor(data = {}) {
82
135
  super(data);
136
+ this.text = data.text ?? null;
83
137
  this.image = data.image ?? null;
84
138
  }
85
139
 
86
140
  toJSON() {
87
141
  const obj = super.toJSON();
142
+ if (this.text != null) obj.text = this.text;
88
143
  if (this.image != null) obj.image = this.image;
89
144
  return obj;
90
145
  }
@@ -0,0 +1,40 @@
1
+ import { GlossaristModel } from './base.js';
2
+
3
+ export const GRAMMAR_GENDERS = Object.freeze(['m', 'f', 'n', 'c']);
4
+ export const GRAMMAR_NUMBERS = Object.freeze(['singular', 'dual', 'plural']);
5
+ export const GRAMMAR_PARTS_OF_SPEECH = Object.freeze([
6
+ 'noun', 'verb', 'adj', 'adverb', 'preposition', 'participle',
7
+ ]);
8
+
9
+ export class GrammarInfo extends GlossaristModel {
10
+ constructor(data = {}) {
11
+ super();
12
+ this.gender = data.gender ?? null;
13
+ this.number = data.number ?? null;
14
+ this.partOfSpeech = data.part_of_speech ?? data.partOfSpeech ?? null;
15
+ this.noun = data.noun ?? false;
16
+ this.verb = data.verb ?? false;
17
+ this.adj = data.adj ?? false;
18
+ this.adverb = data.adverb ?? false;
19
+ this.preposition = data.preposition ?? false;
20
+ this.participle = data.participle ?? false;
21
+ }
22
+
23
+ toJSON() {
24
+ const obj = {};
25
+ if (this.gender != null) obj.gender = this.gender;
26
+ if (this.number != null) obj.number = this.number;
27
+ if (this.partOfSpeech != null) obj.part_of_speech = this.partOfSpeech;
28
+ if (this.noun) obj.noun = true;
29
+ if (this.verb) obj.verb = true;
30
+ if (this.adj) obj.adj = true;
31
+ if (this.adverb) obj.adverb = true;
32
+ if (this.preposition) obj.preposition = true;
33
+ if (this.participle) obj.participle = true;
34
+ return obj;
35
+ }
36
+
37
+ static fromJSON(data) {
38
+ return new GrammarInfo(data);
39
+ }
40
+ }