glossarist 0.2.2 → 0.3.1

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.2",
3
+ "version": "0.3.1",
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,11 +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),
73
76
  domains: mc.data.domains,
74
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,
75
82
  raw: mc,
76
83
  });
77
84
  }
78
85
  }
79
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
+
80
105
  export const conceptParser = new ConceptParser();
@@ -1,4 +1,4 @@
1
- import type { Concept } from './models/index';
1
+ import type { Concept, Register } from './models/index';
2
2
 
3
3
  /** Read all concept YAML files from a directory. */
4
4
  export function readConcepts(dir: string): Concept[];
@@ -10,4 +10,4 @@ export function readConcept(dir: string, id: string): Concept | null;
10
10
  export function listConceptIds(dir: string, prefix?: string): string[];
11
11
 
12
12
  /** Read register.yaml from a dataset directory (if present). */
13
- export function readRegister(dir: string): Record<string, unknown> | null;
13
+ export function readRegister(dir: string): Register | null;
@@ -4,6 +4,7 @@ import yaml from 'js-yaml';
4
4
  import { conceptParser } from './concept-parser.js';
5
5
  import { naturalSort } from './sort.js';
6
6
  import { InvalidInputError } from './errors.js';
7
+ import { Register } from './models/register.js';
7
8
 
8
9
  function assertDir(dir, fnName) {
9
10
  if (typeof dir !== 'string' || dir.trim() === '') {
@@ -91,5 +92,6 @@ export function readRegister(dir) {
91
92
  assertDir(dir, 'readRegister');
92
93
  const p = path.join(dir, 'register.yaml');
93
94
  if (!fs.existsSync(p)) return null;
94
- return yaml.load(fs.readFileSync(p, 'utf8'));
95
+ const raw = yaml.load(fs.readFileSync(p, 'utf8'));
96
+ return Register.fromJSON(raw);
95
97
  }
@@ -43,6 +43,18 @@ export class ConceptSerializer {
43
43
  mainDoc.data.domains = concept.domains.map(d => d.toJSON());
44
44
  }
45
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
+
46
58
  const parts = [
47
59
  '---\n' + yaml.dump(mainDoc, DUMP_OPTS),
48
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
@@ -28,9 +28,11 @@ export {
28
28
 
29
29
  export {
30
30
  GlossaristModel,
31
+ Register, Section,
32
+ REGISTER_STATUSES, ORDERING_METHODS,
31
33
  Concept, LocalizedConcept,
32
34
  Designation, Expression, Abbreviation, Symbol, GraphicalSymbol,
33
- Citation, ConceptSource, RelatedConcept, ConceptReference, ConceptDate,
35
+ Citation, ConceptRef, ConceptSource, RelatedConcept, ConceptReference, ConceptDate,
34
36
  DetailedDefinition, NonVerbRep,
35
37
  GcrMetadata, GcrStatistics,
36
38
  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
+ }
@@ -10,14 +10,17 @@ export class Concept extends GlossaristModel {
10
10
  super();
11
11
  this.id = String(data.id ?? data.termid ?? '');
12
12
  this.term = data.term ?? null;
13
+ this.uri = data.uri ?? null;
13
14
  this._rawLocalizations = data.localizations ?? {};
14
15
  this._cache = {};
15
16
 
16
- this.relatedConcepts = _mapInstances(data.relatedConcepts ?? data.related_concepts ?? [], RelatedConcept);
17
+ this.relatedConcepts = _mapInstances(data.relatedConcepts ?? data.related ?? data.related_concepts ?? [], RelatedConcept);
17
18
  this.domains = _normalizeDomains(data.domains, data.groups);
19
+ this.tags = Array.isArray(data.tags) ? [...data.tags] : [];
18
20
  this.dates = _mapInstances(data.dates ?? [], ConceptDate);
19
21
  this.sources = _mapInstances(data.sources ?? [], ConceptSource);
20
22
  this.status = data.status ?? null;
23
+ this.schemaVersion = data.schemaVersion ?? data.schema_version ?? '3';
21
24
  this.raw = data.raw ?? null;
22
25
  }
23
26
 
@@ -68,6 +71,7 @@ export class Concept extends GlossaristModel {
68
71
  toJSON() {
69
72
  const obj = { id: this.id };
70
73
  if (this.term != null) obj.term = this.term;
74
+ if (this.uri != null) obj.uri = this.uri;
71
75
 
72
76
  if (Object.keys(this._rawLocalizations).length > 0) {
73
77
  obj.localizations = {};
@@ -84,11 +88,14 @@ export class Concept extends GlossaristModel {
84
88
  }
85
89
 
86
90
  if (this.relatedConcepts.length > 0) {
87
- obj.relatedConcepts = this.relatedConcepts.map(rc => rc.toJSON());
91
+ obj.related = this.relatedConcepts.map(rc => rc.toJSON());
88
92
  }
89
93
  if (this.domains.length > 0) {
90
94
  obj.domains = this.domains.map(d => d.toJSON());
91
95
  }
96
+ if (this.tags.length > 0) {
97
+ obj.tags = [...this.tags];
98
+ }
92
99
  if (this.dates.length > 0) {
93
100
  obj.dates = this.dates.map(d => d.toJSON());
94
101
  }
@@ -96,6 +103,7 @@ export class Concept extends GlossaristModel {
96
103
  obj.sources = this.sources.map(s => s.toJSON());
97
104
  }
98
105
  if (this.status != null) obj.status = this.status;
106
+ obj.schema_version = this.schemaVersion;
99
107
  return obj;
100
108
  }
101
109
 
@@ -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
+ }