glossarist 0.3.3 → 0.3.5
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 +36 -28
- package/src/concept-parser.js +6 -11
- package/src/gcr-reader.js +32 -2
- package/src/index.d.ts +10 -0
- package/src/index.js +4 -2
- package/src/models/concept-ref.js +9 -2
- package/src/models/concept-source.js +2 -0
- package/src/models/concept.js +44 -0
- 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/reference-mention.js +88 -0
- package/src/reference-resolver.js +285 -26
- package/src/render-classification.js +106 -0
- package/src/validators/concept-validator.js +51 -44
- package/src/validators/gcr-validator.js +6 -6
- package/src/validators/index.js +7 -1
- package/src/validators/register-validator.js +7 -11
- package/src/validators/relationship-type-rule.js +39 -0
- package/src/validators/v3-rules.js +171 -107
- package/src/validators/validation-error.js +4 -0
- package/src/validators/validation-result.js +29 -11
- package/src/validators/validation-rule.js +7 -5
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.5",
|
|
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",
|
|
@@ -5,7 +5,6 @@ const _items = Symbol('items');
|
|
|
5
5
|
export class ConceptCollection {
|
|
6
6
|
constructor(concepts = []) {
|
|
7
7
|
this[_items] = Array.from(concepts);
|
|
8
|
-
return new Proxy(this, _handler);
|
|
9
8
|
}
|
|
10
9
|
|
|
11
10
|
get length() { return this[_items].length; }
|
|
@@ -23,10 +22,35 @@ export class ConceptCollection {
|
|
|
23
22
|
splice(...args) { return this[_items].splice(...args); }
|
|
24
23
|
set(index, item) { this[_items][index] = item; }
|
|
25
24
|
|
|
25
|
+
toArray() { return [...this[_items]]; }
|
|
26
|
+
|
|
26
27
|
byId(id) {
|
|
27
28
|
return this[_items].find(c => c.id === id || c.termid === id);
|
|
28
29
|
}
|
|
29
30
|
|
|
31
|
+
/**
|
|
32
|
+
* Find a concept by id and version.
|
|
33
|
+
*
|
|
34
|
+
* The version is matched against a top-level `version` field on
|
|
35
|
+
* the concept instance (e.g. `concept.version`). The deployment
|
|
36
|
+
* is responsible for setting this field on bibliographic records;
|
|
37
|
+
* the data model does not enforce its presence or type.
|
|
38
|
+
*
|
|
39
|
+
* If `version` is null, the method falls back to `byId(id)`. This
|
|
40
|
+
* supports the "version-agnostic" lookup for datasets where
|
|
41
|
+
* version is not tracked.
|
|
42
|
+
*
|
|
43
|
+
* @param {string} id
|
|
44
|
+
* @param {string | null} version
|
|
45
|
+
* @returns {Concept | null}
|
|
46
|
+
*/
|
|
47
|
+
byIdAnd(id, version) {
|
|
48
|
+
if (version == null) return this.byId(id);
|
|
49
|
+
return this[_items].find(c =>
|
|
50
|
+
(c.id === id || c.termid === id) && c.version === version
|
|
51
|
+
) ?? null;
|
|
52
|
+
}
|
|
53
|
+
|
|
30
54
|
byPrefix(prefix) {
|
|
31
55
|
return new ConceptCollection(this[_items].filter(c => c.id.startsWith(prefix)));
|
|
32
56
|
}
|
|
@@ -62,14 +86,22 @@ export class ConceptCollection {
|
|
|
62
86
|
for (const t of lc.terms) {
|
|
63
87
|
if ((t.designation ?? '').toLowerCase().includes(q)) return true;
|
|
64
88
|
}
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
89
|
+
if (this._searchDefs(lc.definitions, q)) return true;
|
|
90
|
+
if (this._searchDefs(lc.notes, q)) return true;
|
|
91
|
+
if (this._searchDefs(lc.examples, q)) return true;
|
|
92
|
+
if (this._searchDefs(lc.annotations, q)) return true;
|
|
68
93
|
}
|
|
69
94
|
return false;
|
|
70
95
|
}));
|
|
71
96
|
}
|
|
72
97
|
|
|
98
|
+
_searchDefs(arr, q) {
|
|
99
|
+
for (const d of arr) {
|
|
100
|
+
if ((d.content ?? '').toLowerCase().includes(q)) return true;
|
|
101
|
+
}
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
104
|
+
|
|
73
105
|
allLanguages() {
|
|
74
106
|
const set = new Set();
|
|
75
107
|
for (const c of this[_items]) {
|
|
@@ -82,27 +114,3 @@ export class ConceptCollection {
|
|
|
82
114
|
slice(...args) { return new ConceptCollection(this[_items].slice(...args)); }
|
|
83
115
|
concat(...args) { return new ConceptCollection(this[_items].concat(...args)); }
|
|
84
116
|
}
|
|
85
|
-
|
|
86
|
-
const _handler = {
|
|
87
|
-
get(target, prop, receiver) {
|
|
88
|
-
if (typeof prop === 'string' && /^\d+$/.test(prop)) {
|
|
89
|
-
return target[_items][Number(prop)];
|
|
90
|
-
}
|
|
91
|
-
if (prop === 'length') return target[_items].length;
|
|
92
|
-
const value = Reflect.get(target, prop, receiver);
|
|
93
|
-
return typeof value === 'function' ? value.bind(target) : value;
|
|
94
|
-
},
|
|
95
|
-
set(target, prop, value) {
|
|
96
|
-
if (typeof prop === 'string' && /^\d+$/.test(prop)) {
|
|
97
|
-
target[_items][Number(prop)] = value;
|
|
98
|
-
return true;
|
|
99
|
-
}
|
|
100
|
-
return Reflect.set(target, prop, value);
|
|
101
|
-
},
|
|
102
|
-
has(target, prop) {
|
|
103
|
-
if (typeof prop === 'string' && /^\d+$/.test(prop)) {
|
|
104
|
-
return Number(prop) in target[_items];
|
|
105
|
-
}
|
|
106
|
-
return Reflect.has(target, prop);
|
|
107
|
-
},
|
|
108
|
-
};
|
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/gcr-reader.js
CHANGED
|
@@ -367,12 +367,42 @@ export class GcrPackage {
|
|
|
367
367
|
return map;
|
|
368
368
|
}
|
|
369
369
|
|
|
370
|
-
/**
|
|
371
|
-
|
|
370
|
+
/**
|
|
371
|
+
* Read a text file from the package.
|
|
372
|
+
* @param {string} filePath
|
|
373
|
+
* @returns {Promise<string | null>} null if the file doesn't exist
|
|
374
|
+
*/
|
|
375
|
+
async readText(filePath) {
|
|
372
376
|
const entry = this._zip.file(filePath);
|
|
373
377
|
if (!entry) return null;
|
|
374
378
|
return entry.async('text');
|
|
375
379
|
}
|
|
380
|
+
|
|
381
|
+
/**
|
|
382
|
+
* List all entry paths in the package.
|
|
383
|
+
* @returns {Array<{ path: string, dir: boolean }>}
|
|
384
|
+
*/
|
|
385
|
+
entryPaths() {
|
|
386
|
+
const entries = [];
|
|
387
|
+
this._zip.forEach((relativePath, entry) => {
|
|
388
|
+
entries.push({ path: relativePath, dir: !!entry.dir });
|
|
389
|
+
});
|
|
390
|
+
return entries;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
/**
|
|
394
|
+
* Check whether a specific entry exists in the package.
|
|
395
|
+
* @param {string} filePath
|
|
396
|
+
* @returns {boolean}
|
|
397
|
+
*/
|
|
398
|
+
hasEntry(filePath) {
|
|
399
|
+
return this._zip.file(filePath) != null;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
/** @private @deprecated Use readText instead */
|
|
403
|
+
async _readText(filePath) {
|
|
404
|
+
return this.readText(filePath);
|
|
405
|
+
}
|
|
376
406
|
}
|
|
377
407
|
|
|
378
408
|
// --- Concept YAML parsing ---
|
package/src/index.d.ts
CHANGED
|
@@ -34,6 +34,16 @@ export { conceptUuid, localizedConceptUuid, uuidV5 } from './uuid';
|
|
|
34
34
|
// Reference resolution
|
|
35
35
|
export { ReferenceResolver, Reference, referenceResolver } from './reference-resolver';
|
|
36
36
|
|
|
37
|
+
export type MentionParseResult = {
|
|
38
|
+
kind: 'cite-ref' | 'numeric' | 'unresolved';
|
|
39
|
+
key?: string;
|
|
40
|
+
label?: string | null;
|
|
41
|
+
id?: string;
|
|
42
|
+
raw: string;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export function parseMention(raw: string): MentionParseResult;
|
|
46
|
+
|
|
37
47
|
// V1 support
|
|
38
48
|
export { V1Reader, migrateV1ToV2 } from './v1-reader';
|
|
39
49
|
|
package/src/index.js
CHANGED
|
@@ -8,6 +8,8 @@ export { ManagedConceptCollection } from './managed-concept-collection.js';
|
|
|
8
8
|
export { validateConcept, validateRegister, validateGcrPackage, createConceptValidator, ValidationError, ValidationRule, ValidationResult, RegisterValidator, GcrValidator } from './validators/index.js';
|
|
9
9
|
export { conceptUuid, localizedConceptUuid, uuidV5 } from './uuid.js';
|
|
10
10
|
export { ReferenceResolver, Reference, referenceResolver } from './reference-resolver.js';
|
|
11
|
+
export { parseMention } from './reference-mention.js';
|
|
12
|
+
export { ReferenceClassifier } from './render-classification.js';
|
|
11
13
|
export { V1Reader, migrateV1ToV2 } from './v1-reader.js';
|
|
12
14
|
export { GlossaristError, InvalidInputError, YamlParseError } from './errors.js';
|
|
13
15
|
|
|
@@ -32,8 +34,8 @@ export {
|
|
|
32
34
|
REGISTER_STATUSES, ORDERING_METHODS,
|
|
33
35
|
Concept, LocalizedConcept,
|
|
34
36
|
Designation, Expression, Abbreviation, Symbol, GraphicalSymbol,
|
|
35
|
-
Citation, ConceptRef, ConceptSource, RelatedConcept, ConceptReference, ConceptDate,
|
|
37
|
+
Citation, ConceptRef, ConceptSource, RelatedConcept, DesignationRelationship, ConceptReference, ConceptDate,
|
|
36
38
|
DetailedDefinition, NonVerbRep,
|
|
37
39
|
GcrMetadata, GcrStatistics,
|
|
38
|
-
RELATIONSHIP_TYPES, DATE_TYPES,
|
|
40
|
+
RELATIONSHIP_TYPES, DESIGNATION_RELATIONSHIP_TYPES, DATE_TYPES,
|
|
39
41
|
} 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
|
|
|
@@ -4,6 +4,7 @@ import { Citation } from './citation.js';
|
|
|
4
4
|
export class ConceptSource extends GlossaristModel {
|
|
5
5
|
constructor(data = {}) {
|
|
6
6
|
super();
|
|
7
|
+
this.id = data.id ?? null;
|
|
7
8
|
this.status = data.status ?? null;
|
|
8
9
|
this.type = data.type ?? null;
|
|
9
10
|
this.origin = data.origin
|
|
@@ -14,6 +15,7 @@ export class ConceptSource extends GlossaristModel {
|
|
|
14
15
|
|
|
15
16
|
toJSON() {
|
|
16
17
|
const obj = {};
|
|
18
|
+
if (this.id != null) obj.id = this.id;
|
|
17
19
|
if (this.status != null) obj.status = this.status;
|
|
18
20
|
if (this.type != null) obj.type = this.type;
|
|
19
21
|
if (this.origin != null) obj.origin = this.origin.toJSON();
|
package/src/models/concept.js
CHANGED
|
@@ -30,6 +30,11 @@ export class Concept extends GlossaristModel {
|
|
|
30
30
|
return Object.keys(this._rawLocalizations);
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
+
get languageCodes() {
|
|
34
|
+
return this.languages;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** @deprecated Use localization(lang) for model access, or toJSON().localizations for raw data */
|
|
33
38
|
get localizations() {
|
|
34
39
|
return this._rawLocalizations;
|
|
35
40
|
}
|
|
@@ -68,6 +73,45 @@ export class Concept extends GlossaristModel {
|
|
|
68
73
|
return lang in this._rawLocalizations;
|
|
69
74
|
}
|
|
70
75
|
|
|
76
|
+
/**
|
|
77
|
+
* Find a source by its local id within this concept.
|
|
78
|
+
*
|
|
79
|
+
* The lookup walks concept-level, localization-level, and
|
|
80
|
+
* designation-level sources in that order, returning the first
|
|
81
|
+
* source whose `id` matches. Sources without an `id` are
|
|
82
|
+
* skipped. The id is local to the concept; uniqueness is
|
|
83
|
+
* enforced by the validator (see CiteRefIntegrityRule).
|
|
84
|
+
*
|
|
85
|
+
* @param {string} id
|
|
86
|
+
* @returns {ConceptSource | null}
|
|
87
|
+
*/
|
|
88
|
+
findSourceById(id) {
|
|
89
|
+
if (typeof id !== 'string' || id.length === 0) return null;
|
|
90
|
+
|
|
91
|
+
// 1. Concept-level sources.
|
|
92
|
+
for (const source of this.sources) {
|
|
93
|
+
if (source.id === id) return source;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// 2. Localization-level sources.
|
|
97
|
+
for (const lang of this.languages) {
|
|
98
|
+
const lc = this.localization(lang);
|
|
99
|
+
if (!lc) continue;
|
|
100
|
+
for (const source of lc.sources) {
|
|
101
|
+
if (source.id === id) return source;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// 3. Designation-level sources.
|
|
105
|
+
for (const designation of lc.terms) {
|
|
106
|
+
for (const source of designation.sources) {
|
|
107
|
+
if (source.id === id) return source;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
|
|
71
115
|
toJSON() {
|
|
72
116
|
const obj = { id: this.id };
|
|
73
117
|
if (this.term != null) obj.term = this.term;
|
|
@@ -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 {
|