glossarist 0.3.3 → 0.3.4
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/src/concept-collection.js +11 -3
- package/src/concept-parser.js +6 -11
- package/src/index.js +2 -2
- package/src/models/concept-ref.js +9 -2
- package/src/models/designation-relationship.js +27 -0
- package/src/models/designation.js +6 -1
- package/src/models/index.d.ts +11 -1
- package/src/models/index.js +1 -0
- package/src/models/localized-concept.js +38 -82
- package/src/models/related-concept.js +0 -2
- package/src/validators/index.js +4 -1
- package/src/validators/relationship-type-rule.js +40 -0
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "glossarist",
|
|
3
|
-
"version": "0.3.
|
|
4
|
-
"description": "JavaScript SDK for Glossarist GCR packages
|
|
3
|
+
"version": "0.3.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",
|
|
7
7
|
"types": "src/index.d.ts",
|
|
@@ -62,14 +62,22 @@ export class ConceptCollection {
|
|
|
62
62
|
for (const t of lc.terms) {
|
|
63
63
|
if ((t.designation ?? '').toLowerCase().includes(q)) return true;
|
|
64
64
|
}
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
65
|
+
if (this._searchDefs(lc.definitions, q)) return true;
|
|
66
|
+
if (this._searchDefs(lc.notes, q)) return true;
|
|
67
|
+
if (this._searchDefs(lc.examples, q)) return true;
|
|
68
|
+
if (this._searchDefs(lc.annotations, q)) return true;
|
|
68
69
|
}
|
|
69
70
|
return false;
|
|
70
71
|
}));
|
|
71
72
|
}
|
|
72
73
|
|
|
74
|
+
_searchDefs(arr, q) {
|
|
75
|
+
for (const d of arr) {
|
|
76
|
+
if ((d.content ?? '').toLowerCase().includes(q)) return true;
|
|
77
|
+
}
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
|
|
73
81
|
allLanguages() {
|
|
74
82
|
const set = new Set();
|
|
75
83
|
for (const c of this[_items]) {
|
package/src/concept-parser.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import yaml from 'js-yaml';
|
|
2
2
|
import { Concept } from './models/concept.js';
|
|
3
|
-
import { ConceptRef } from './models/concept-ref.js';
|
|
4
3
|
import { RelatedConcept } from './models/related-concept.js';
|
|
5
4
|
import { InvalidInputError, YamlParseError } from './errors.js';
|
|
6
5
|
|
|
@@ -88,17 +87,13 @@ function _normalizeRelated(arr) {
|
|
|
88
87
|
if (!arr || !Array.isArray(arr)) return [];
|
|
89
88
|
return arr.map(r => {
|
|
90
89
|
if (r instanceof RelatedConcept) return r;
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
'object',
|
|
97
|
-
);
|
|
98
|
-
}
|
|
99
|
-
data.ref = new ConceptRef(data.ref);
|
|
90
|
+
if (r.ref != null && typeof r.ref !== 'object') {
|
|
91
|
+
throw new InvalidInputError(
|
|
92
|
+
`RelatedConcept.ref must be an object { source, id }, got: ${typeof r.ref}`,
|
|
93
|
+
'object',
|
|
94
|
+
);
|
|
100
95
|
}
|
|
101
|
-
return new RelatedConcept(
|
|
96
|
+
return new RelatedConcept(r);
|
|
102
97
|
});
|
|
103
98
|
}
|
|
104
99
|
|
package/src/index.js
CHANGED
|
@@ -32,8 +32,8 @@ export {
|
|
|
32
32
|
REGISTER_STATUSES, ORDERING_METHODS,
|
|
33
33
|
Concept, LocalizedConcept,
|
|
34
34
|
Designation, Expression, Abbreviation, Symbol, GraphicalSymbol,
|
|
35
|
-
Citation, ConceptRef, ConceptSource, RelatedConcept, ConceptReference, ConceptDate,
|
|
35
|
+
Citation, ConceptRef, ConceptSource, RelatedConcept, DesignationRelationship, ConceptReference, ConceptDate,
|
|
36
36
|
DetailedDefinition, NonVerbRep,
|
|
37
37
|
GcrMetadata, GcrStatistics,
|
|
38
|
-
RELATIONSHIP_TYPES, DATE_TYPES,
|
|
38
|
+
RELATIONSHIP_TYPES, DESIGNATION_RELATIONSHIP_TYPES, DATE_TYPES,
|
|
39
39
|
} from './models/index.js';
|
|
@@ -5,17 +5,24 @@ export class ConceptRef extends GlossaristModel {
|
|
|
5
5
|
super();
|
|
6
6
|
this.source = data.source ?? null;
|
|
7
7
|
this.id = data.id ?? null;
|
|
8
|
+
this.text = data.text ?? null;
|
|
8
9
|
}
|
|
9
10
|
|
|
10
11
|
toString() {
|
|
11
|
-
|
|
12
|
-
|
|
12
|
+
const parts = [];
|
|
13
|
+
if (this.source) parts.push(this.source);
|
|
14
|
+
if (this.id) parts.push(this.id);
|
|
15
|
+
const base = parts.join(' ');
|
|
16
|
+
if (this.text && base) return `${base} (${this.text})`;
|
|
17
|
+
if (this.text) return this.text;
|
|
18
|
+
return base;
|
|
13
19
|
}
|
|
14
20
|
|
|
15
21
|
toJSON() {
|
|
16
22
|
const obj = {};
|
|
17
23
|
if (this.source != null) obj.source = this.source;
|
|
18
24
|
if (this.id != null) obj.id = this.id;
|
|
25
|
+
if (this.text != null) obj.text = this.text;
|
|
19
26
|
return obj;
|
|
20
27
|
}
|
|
21
28
|
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { GlossaristModel } from './base.js';
|
|
2
|
+
|
|
3
|
+
export const DESIGNATION_RELATIONSHIP_TYPES = Object.freeze([
|
|
4
|
+
// TBX (ISO 30042) / ISO 12620 term-level relationships
|
|
5
|
+
'abbreviated_form_for', 'short_form_for',
|
|
6
|
+
]);
|
|
7
|
+
|
|
8
|
+
export class DesignationRelationship extends GlossaristModel {
|
|
9
|
+
constructor(data = {}) {
|
|
10
|
+
super();
|
|
11
|
+
this.type = data.type ?? null;
|
|
12
|
+
this.content = data.content ?? null;
|
|
13
|
+
this.target = data.target ?? null;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
toJSON() {
|
|
17
|
+
const obj = {};
|
|
18
|
+
if (this.type != null) obj.type = this.type;
|
|
19
|
+
if (this.content != null) obj.content = this.content;
|
|
20
|
+
if (this.target != null) obj.target = this.target;
|
|
21
|
+
return obj;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
static fromJSON(data) {
|
|
25
|
+
return new DesignationRelationship(data);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -3,6 +3,7 @@ import { ConceptSource } from './concept-source.js';
|
|
|
3
3
|
import { Pronunciation } from './pronunciation.js';
|
|
4
4
|
import { GrammarInfo } from './grammar-info.js';
|
|
5
5
|
import { RelatedConcept } from './related-concept.js';
|
|
6
|
+
import { DesignationRelationship, DESIGNATION_RELATIONSHIP_TYPES } from './designation-relationship.js';
|
|
6
7
|
|
|
7
8
|
export class Designation extends GlossaristModel {
|
|
8
9
|
static _registry = new Map();
|
|
@@ -38,7 +39,11 @@ export class Designation extends GlossaristModel {
|
|
|
38
39
|
s => s instanceof ConceptSource ? s : new ConceptSource(s)
|
|
39
40
|
);
|
|
40
41
|
this.related = (data.related ?? []).map(
|
|
41
|
-
r => r instanceof
|
|
42
|
+
r => (r instanceof DesignationRelationship || r instanceof RelatedConcept)
|
|
43
|
+
? r
|
|
44
|
+
: DESIGNATION_RELATIONSHIP_TYPES.includes(r.type)
|
|
45
|
+
? DesignationRelationship.fromJSON(r)
|
|
46
|
+
: RelatedConcept.fromJSON(r)
|
|
42
47
|
);
|
|
43
48
|
}
|
|
44
49
|
|
package/src/models/index.d.ts
CHANGED
|
@@ -88,6 +88,7 @@ export class LocalizedConcept extends GlossaristModel {
|
|
|
88
88
|
readonly definitions: DetailedDefinition[];
|
|
89
89
|
readonly definition: DetailedDefinition[];
|
|
90
90
|
readonly notes: DetailedDefinition[];
|
|
91
|
+
readonly annotations: DetailedDefinition[];
|
|
91
92
|
readonly examples: DetailedDefinition[];
|
|
92
93
|
readonly sources: ConceptSource[];
|
|
93
94
|
readonly dates: ConceptDate[];
|
|
@@ -113,7 +114,7 @@ export class Designation extends GlossaristModel {
|
|
|
113
114
|
readonly termType: string | null;
|
|
114
115
|
readonly pronunciations: Pronunciation[];
|
|
115
116
|
readonly sources: ConceptSource[];
|
|
116
|
-
readonly related: RelatedConcept[];
|
|
117
|
+
readonly related: (RelatedConcept | DesignationRelationship)[];
|
|
117
118
|
static register(type: string, cls: typeof Designation): void;
|
|
118
119
|
static fromData(data: Record<string, unknown>): Designation;
|
|
119
120
|
static fromJSON(data: Record<string, unknown>): Designation;
|
|
@@ -194,6 +195,7 @@ export namespace Citation {
|
|
|
194
195
|
export class ConceptRef extends GlossaristModel {
|
|
195
196
|
readonly source: string | null;
|
|
196
197
|
readonly id: string | null;
|
|
198
|
+
readonly text: string | null;
|
|
197
199
|
toString(): string;
|
|
198
200
|
static fromJSON(data: Record<string, unknown>): ConceptRef;
|
|
199
201
|
}
|
|
@@ -212,6 +214,14 @@ export class RelatedConcept extends GlossaristModel {
|
|
|
212
214
|
readonly ref: ConceptRef | null;
|
|
213
215
|
}
|
|
214
216
|
|
|
217
|
+
export const DESIGNATION_RELATIONSHIP_TYPES: readonly string[];
|
|
218
|
+
export class DesignationRelationship extends GlossaristModel {
|
|
219
|
+
readonly type: string | null;
|
|
220
|
+
readonly content: string | null;
|
|
221
|
+
readonly target: string | null;
|
|
222
|
+
static fromJSON(data: Record<string, unknown>): DesignationRelationship;
|
|
223
|
+
}
|
|
224
|
+
|
|
215
225
|
export class ConceptReference extends GlossaristModel {
|
|
216
226
|
readonly conceptId: string | null;
|
|
217
227
|
readonly refType: string | null;
|
package/src/models/index.js
CHANGED
|
@@ -8,6 +8,7 @@ export { Citation } from './citation.js';
|
|
|
8
8
|
export { ConceptRef } from './concept-ref.js';
|
|
9
9
|
export { ConceptSource } from './concept-source.js';
|
|
10
10
|
export { RelatedConcept, RELATIONSHIP_TYPES } from './related-concept.js';
|
|
11
|
+
export { DesignationRelationship, DESIGNATION_RELATIONSHIP_TYPES } from './designation-relationship.js';
|
|
11
12
|
export { ConceptReference } from './concept-reference.js';
|
|
12
13
|
export { ConceptDate, DATE_TYPES } from './concept-date.js';
|
|
13
14
|
export { DetailedDefinition } from './detailed-definition.js';
|
|
@@ -6,6 +6,10 @@ import { ConceptDate } from './concept-date.js';
|
|
|
6
6
|
import { NonVerbRep } from './non-verb-rep.js';
|
|
7
7
|
import { RelatedConcept } from './related-concept.js';
|
|
8
8
|
|
|
9
|
+
function wrapAs(Cls) {
|
|
10
|
+
return item => item instanceof Cls ? item : new Cls(item);
|
|
11
|
+
}
|
|
12
|
+
|
|
9
13
|
export class LocalizedConcept extends GlossaristModel {
|
|
10
14
|
constructor(data = {}) {
|
|
11
15
|
super();
|
|
@@ -30,6 +34,7 @@ export class LocalizedConcept extends GlossaristModel {
|
|
|
30
34
|
this._rawDefinition = data.definition ?? [];
|
|
31
35
|
this._rawSources = data.sources ?? [];
|
|
32
36
|
this._rawNotes = data.notes ?? [];
|
|
37
|
+
this._rawAnnotations = data.annotations ?? [];
|
|
33
38
|
this._rawExamples = data.examples ?? [];
|
|
34
39
|
this._rawDates = data.dates ?? [];
|
|
35
40
|
this._rawNonVerbal = data.non_verbal_rep ?? data.non_verb ?? [];
|
|
@@ -39,6 +44,7 @@ export class LocalizedConcept extends GlossaristModel {
|
|
|
39
44
|
this._definitions = null;
|
|
40
45
|
this._sources = null;
|
|
41
46
|
this._notes = null;
|
|
47
|
+
this._annotations = null;
|
|
42
48
|
this._examples = null;
|
|
43
49
|
this._dates = null;
|
|
44
50
|
this._nonVerbal = null;
|
|
@@ -46,19 +52,11 @@ export class LocalizedConcept extends GlossaristModel {
|
|
|
46
52
|
}
|
|
47
53
|
|
|
48
54
|
get terms() {
|
|
49
|
-
|
|
50
|
-
this._terms = this._rawTerms.map(t => Designation.fromData(t));
|
|
51
|
-
}
|
|
52
|
-
return this._terms;
|
|
55
|
+
return this._lazy('_terms', '_rawTerms', t => Designation.fromData(t));
|
|
53
56
|
}
|
|
54
57
|
|
|
55
58
|
get definitions() {
|
|
56
|
-
|
|
57
|
-
this._definitions = this._rawDefinition.map(
|
|
58
|
-
d => d instanceof DetailedDefinition ? d : new DetailedDefinition(d)
|
|
59
|
-
);
|
|
60
|
-
}
|
|
61
|
-
return this._definitions;
|
|
59
|
+
return this._lazy('_definitions', '_rawDefinition', wrapAs(DetailedDefinition));
|
|
62
60
|
}
|
|
63
61
|
|
|
64
62
|
get definition() {
|
|
@@ -66,57 +64,31 @@ export class LocalizedConcept extends GlossaristModel {
|
|
|
66
64
|
}
|
|
67
65
|
|
|
68
66
|
get sources() {
|
|
69
|
-
|
|
70
|
-
this._sources = this._rawSources.map(
|
|
71
|
-
s => s instanceof ConceptSource ? s : new ConceptSource(s)
|
|
72
|
-
);
|
|
73
|
-
}
|
|
74
|
-
return this._sources;
|
|
67
|
+
return this._lazy('_sources', '_rawSources', wrapAs(ConceptSource));
|
|
75
68
|
}
|
|
76
69
|
|
|
77
70
|
get notes() {
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
return this._notes;
|
|
71
|
+
return this._lazy('_notes', '_rawNotes', wrapAs(DetailedDefinition));
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
get annotations() {
|
|
75
|
+
return this._lazy('_annotations', '_rawAnnotations', wrapAs(DetailedDefinition));
|
|
84
76
|
}
|
|
85
77
|
|
|
86
78
|
get examples() {
|
|
87
|
-
|
|
88
|
-
this._examples = this._rawExamples.map(
|
|
89
|
-
e => e instanceof DetailedDefinition ? e : new DetailedDefinition(e)
|
|
90
|
-
);
|
|
91
|
-
}
|
|
92
|
-
return this._examples;
|
|
79
|
+
return this._lazy('_examples', '_rawExamples', wrapAs(DetailedDefinition));
|
|
93
80
|
}
|
|
94
81
|
|
|
95
82
|
get dates() {
|
|
96
|
-
|
|
97
|
-
this._dates = this._rawDates.map(
|
|
98
|
-
d => d instanceof ConceptDate ? d : new ConceptDate(d)
|
|
99
|
-
);
|
|
100
|
-
}
|
|
101
|
-
return this._dates;
|
|
83
|
+
return this._lazy('_dates', '_rawDates', wrapAs(ConceptDate));
|
|
102
84
|
}
|
|
103
85
|
|
|
104
86
|
get nonVerbalRep() {
|
|
105
|
-
|
|
106
|
-
this._nonVerbal = this._rawNonVerbal.map(
|
|
107
|
-
n => n instanceof NonVerbRep ? n : new NonVerbRep(n)
|
|
108
|
-
);
|
|
109
|
-
}
|
|
110
|
-
return this._nonVerbal;
|
|
87
|
+
return this._lazy('_nonVerbal', '_rawNonVerbal', wrapAs(NonVerbRep));
|
|
111
88
|
}
|
|
112
89
|
|
|
113
90
|
get related() {
|
|
114
|
-
|
|
115
|
-
this._related = this._rawRelated.map(
|
|
116
|
-
r => r instanceof RelatedConcept ? r : new RelatedConcept(r)
|
|
117
|
-
);
|
|
118
|
-
}
|
|
119
|
-
return this._related;
|
|
91
|
+
return this._lazy('_related', '_rawRelated', wrapAs(RelatedConcept));
|
|
120
92
|
}
|
|
121
93
|
|
|
122
94
|
get primaryDesignation() {
|
|
@@ -146,47 +118,31 @@ export class LocalizedConcept extends GlossaristModel {
|
|
|
146
118
|
if (this.reviewDecision) obj.review_decision = this.reviewDecision;
|
|
147
119
|
if (this.reviewDecisionNotes) obj.review_decision_notes = this.reviewDecisionNotes;
|
|
148
120
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
121
|
+
this._serialize(obj, 'terms', '_terms', '_rawTerms');
|
|
122
|
+
this._serialize(obj, 'definition', '_definitions', '_rawDefinition');
|
|
123
|
+
this._serialize(obj, 'notes', '_notes', '_rawNotes');
|
|
124
|
+
this._serialize(obj, 'annotations', '_annotations', '_rawAnnotations');
|
|
125
|
+
this._serialize(obj, 'examples', '_examples', '_rawExamples');
|
|
126
|
+
this._serialize(obj, 'sources', '_sources', '_rawSources');
|
|
127
|
+
this._serialize(obj, 'dates', '_dates', '_rawDates');
|
|
128
|
+
this._serialize(obj, 'non_verbal_rep', '_nonVerbal', '_rawNonVerbal');
|
|
129
|
+
this._serialize(obj, 'related', '_related', '_rawRelated');
|
|
153
130
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
obj.definition = defs.map(d => (typeof d.toJSON === 'function') ? d.toJSON() : d);
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
const notes = this._notes ?? (this._rawNotes.length > 0 ? this._rawNotes : []);
|
|
160
|
-
if (notes.length > 0) {
|
|
161
|
-
obj.notes = notes.map(n => (typeof n.toJSON === 'function') ? n.toJSON() : n);
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
const examples = this._examples ?? (this._rawExamples.length > 0 ? this._rawExamples : []);
|
|
165
|
-
if (examples.length > 0) {
|
|
166
|
-
obj.examples = examples.map(e => (typeof e.toJSON === 'function') ? e.toJSON() : e);
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
const sources = this._sources ?? (this._rawSources.length > 0 ? this._rawSources : []);
|
|
170
|
-
if (sources.length > 0) {
|
|
171
|
-
obj.sources = sources.map(s => (typeof s.toJSON === 'function') ? s.toJSON() : s);
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
const dates = this._dates ?? (this._rawDates.length > 0 ? this._rawDates : []);
|
|
175
|
-
if (dates.length > 0) {
|
|
176
|
-
obj.dates = dates.map(d => (typeof d.toJSON === 'function') ? d.toJSON() : d);
|
|
177
|
-
}
|
|
131
|
+
return obj;
|
|
132
|
+
}
|
|
178
133
|
|
|
179
|
-
|
|
180
|
-
if (
|
|
181
|
-
|
|
134
|
+
_lazy(cacheKey, rawKey, wrapFn) {
|
|
135
|
+
if (this[cacheKey] === null) {
|
|
136
|
+
this[cacheKey] = this[rawKey].map(wrapFn);
|
|
182
137
|
}
|
|
138
|
+
return this[cacheKey];
|
|
139
|
+
}
|
|
183
140
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
141
|
+
_serialize(obj, jsonKey, cacheKey, rawKey) {
|
|
142
|
+
const items = this[cacheKey] ?? (this[rawKey].length > 0 ? this[rawKey] : []);
|
|
143
|
+
if (items.length > 0) {
|
|
144
|
+
obj[jsonKey] = items.map(i => (typeof i.toJSON === 'function') ? i.toJSON() : i);
|
|
187
145
|
}
|
|
188
|
-
|
|
189
|
-
return obj;
|
|
190
146
|
}
|
|
191
147
|
|
|
192
148
|
static fromJSON(data) {
|
|
@@ -31,8 +31,6 @@ export const RELATIONSHIP_TYPES = Object.freeze([
|
|
|
31
31
|
'sequentially_related_concept', 'spatially_related_concept', 'temporally_related_concept',
|
|
32
32
|
// Lexical (ISO 12620 / TBX)
|
|
33
33
|
'homograph', 'false_friend',
|
|
34
|
-
// Designation-level
|
|
35
|
-
'abbreviated_form_for', 'short_form_for',
|
|
36
34
|
]);
|
|
37
35
|
|
|
38
36
|
export class RelatedConcept extends GlossaristModel {
|
package/src/validators/index.js
CHANGED
|
@@ -4,6 +4,7 @@ export { ValidationResult } from './validation-result.js';
|
|
|
4
4
|
export { ConceptValidator, LanguageCodeRule, DesignationTypeRule, EntryStatusRule } from './concept-validator.js';
|
|
5
5
|
export { RegisterValidator } from './register-validator.js';
|
|
6
6
|
export { GcrValidator } from './gcr-validator.js';
|
|
7
|
+
export { RelationshipTypeRule } from './relationship-type-rule.js';
|
|
7
8
|
export {
|
|
8
9
|
RefShapeRule,
|
|
9
10
|
LocalityCompletenessRule,
|
|
@@ -17,6 +18,7 @@ export {
|
|
|
17
18
|
import { ConceptValidator, LanguageCodeRule, DesignationTypeRule, EntryStatusRule } from './concept-validator.js';
|
|
18
19
|
import { RegisterValidator } from './register-validator.js';
|
|
19
20
|
import { GcrValidator } from './gcr-validator.js';
|
|
21
|
+
import { RelationshipTypeRule } from './relationship-type-rule.js';
|
|
20
22
|
import {
|
|
21
23
|
RefShapeRule,
|
|
22
24
|
LocalityCompletenessRule,
|
|
@@ -37,7 +39,8 @@ const _default = new ConceptValidator()
|
|
|
37
39
|
.addRule(new SchemaVersionRule())
|
|
38
40
|
.addRule(new DomainRefRule())
|
|
39
41
|
.addRule(new UuidFormatRule())
|
|
40
|
-
.addRule(new SourceUrnFormatRule())
|
|
42
|
+
.addRule(new SourceUrnFormatRule())
|
|
43
|
+
.addRule(new RelationshipTypeRule());
|
|
41
44
|
|
|
42
45
|
export function validateConcept(concept) {
|
|
43
46
|
return _default.validate(concept);
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { ValidationRule } from './validation-rule.js';
|
|
2
|
+
import { RELATIONSHIP_TYPES } from '../models/related-concept.js';
|
|
3
|
+
import { DESIGNATION_RELATIONSHIP_TYPES } from '../models/designation-relationship.js';
|
|
4
|
+
|
|
5
|
+
const KNOWN_CONCEPT_TYPES = new Set(RELATIONSHIP_TYPES);
|
|
6
|
+
const KNOWN_DESIGNATION_TYPES = new Set(DESIGNATION_RELATIONSHIP_TYPES);
|
|
7
|
+
|
|
8
|
+
export class RelationshipTypeRule extends ValidationRule {
|
|
9
|
+
constructor() { super('relationship-type', 'warning'); }
|
|
10
|
+
|
|
11
|
+
validate(value, path) {
|
|
12
|
+
const errors = [];
|
|
13
|
+
this._checkRelated(value.related, `${path}related`, KNOWN_CONCEPT_TYPES, errors);
|
|
14
|
+
|
|
15
|
+
if (value.localizations) {
|
|
16
|
+
for (const [lang, lc] of Object.entries(value.localizations)) {
|
|
17
|
+
this._checkRelated(lc.related, `${path}localizations.${lang}.related`, KNOWN_CONCEPT_TYPES, errors);
|
|
18
|
+
|
|
19
|
+
if (lc.terms) {
|
|
20
|
+
for (let ti = 0; ti < lc.terms.length; ti++) {
|
|
21
|
+
this._checkRelated(lc.terms[ti]?.related,
|
|
22
|
+
`${path}localizations.${lang}.terms[${ti}].related`, KNOWN_DESIGNATION_TYPES, errors);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return errors;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
_checkRelated(arr, basePath, knownTypes, errors) {
|
|
31
|
+
if (!arr) return;
|
|
32
|
+
for (let i = 0; i < arr.length; i++) {
|
|
33
|
+
const type = arr[i]?.type;
|
|
34
|
+
if (type && !knownTypes.has(type)) {
|
|
35
|
+
errors.push(...this.error(`${basePath}[${i}].type`,
|
|
36
|
+
`Unknown relationship type '${type}'`));
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|