glossarist 0.2.0 → 0.2.2
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 +1 -1
- package/src/concept-collection.js +1 -2
- package/src/concept-parser.js +2 -0
- package/src/concept-serializer.js +4 -0
- package/src/index.d.ts +1 -1
- package/src/index.js +2 -2
- package/src/models/concept-reference.js +36 -0
- package/src/models/concept.js +17 -0
- package/src/models/index.d.ts +13 -0
- package/src/models/index.js +1 -0
- package/src/models/localized-concept.js +2 -0
- package/src/validators/gcr-validator.js +74 -0
- package/src/validators/index.js +7 -0
- package/src/validators/validation-result.js +34 -0
package/package.json
CHANGED
|
@@ -37,8 +37,7 @@ export class ConceptCollection {
|
|
|
37
37
|
|
|
38
38
|
byStatus(status) {
|
|
39
39
|
return new ConceptCollection(this[_items].filter(c => {
|
|
40
|
-
|
|
41
|
-
return langs.length > 0 && c.localization(langs[0])?.entryStatus === status;
|
|
40
|
+
return c.languages.some(lang => c.localization(lang)?.entryStatus === status);
|
|
42
41
|
}));
|
|
43
42
|
}
|
|
44
43
|
|
package/src/concept-parser.js
CHANGED
|
@@ -39,6 +39,10 @@ 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
|
+
|
|
42
46
|
const parts = [
|
|
43
47
|
'---\n' + yaml.dump(mainDoc, DUMP_OPTS),
|
|
44
48
|
...langDocs.map(d => '---\n' + yaml.dump(d, DUMP_OPTS)),
|
package/src/index.d.ts
CHANGED
|
@@ -26,7 +26,7 @@ export { ConceptCollection } from './concept-collection';
|
|
|
26
26
|
export { ManagedConceptCollection } from './managed-concept-collection';
|
|
27
27
|
|
|
28
28
|
// Validators
|
|
29
|
-
export { validateConcept, validateRegister, createConceptValidator, ValidationError, ValidationRule, RegisterValidator } from './validators/index';
|
|
29
|
+
export { validateConcept, validateRegister, validateGcrPackage, createConceptValidator, ValidationError, ValidationRule, ValidationResult, RegisterValidator, GcrValidator } from './validators/index';
|
|
30
30
|
|
|
31
31
|
// UUID
|
|
32
32
|
export { conceptUuid, localizedConceptUuid, uuidV5 } from './uuid';
|
package/src/index.js
CHANGED
|
@@ -5,7 +5,7 @@ export { writeConcept, writeConcepts } from './concept-writer.js';
|
|
|
5
5
|
export { createGcr, GcrWriter } from './gcr-writer.js';
|
|
6
6
|
export { ConceptCollection } from './concept-collection.js';
|
|
7
7
|
export { ManagedConceptCollection } from './managed-concept-collection.js';
|
|
8
|
-
export { validateConcept, validateRegister, createConceptValidator, ValidationError, ValidationRule, RegisterValidator } from './validators/index.js';
|
|
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
11
|
export { V1Reader, migrateV1ToV2 } from './v1-reader.js';
|
|
@@ -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, ConceptSource, RelatedConcept, ConceptReference, ConceptDate,
|
|
34
34
|
DetailedDefinition, NonVerbRep,
|
|
35
35
|
GcrMetadata, GcrStatistics,
|
|
36
36
|
RELATIONSHIP_TYPES, DATE_TYPES,
|
|
@@ -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
|
+
}
|
package/src/models/concept.js
CHANGED
|
@@ -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
|
|
|
@@ -13,6 +14,7 @@ export class Concept extends GlossaristModel {
|
|
|
13
14
|
this._cache = {};
|
|
14
15
|
|
|
15
16
|
this.relatedConcepts = _mapInstances(data.relatedConcepts ?? data.related_concepts ?? [], RelatedConcept);
|
|
17
|
+
this.domains = _normalizeDomains(data.domains, data.groups);
|
|
16
18
|
this.dates = _mapInstances(data.dates ?? [], ConceptDate);
|
|
17
19
|
this.sources = _mapInstances(data.sources ?? [], ConceptSource);
|
|
18
20
|
this.status = data.status ?? null;
|
|
@@ -84,6 +86,9 @@ export class Concept extends GlossaristModel {
|
|
|
84
86
|
if (this.relatedConcepts.length > 0) {
|
|
85
87
|
obj.relatedConcepts = this.relatedConcepts.map(rc => rc.toJSON());
|
|
86
88
|
}
|
|
89
|
+
if (this.domains.length > 0) {
|
|
90
|
+
obj.domains = this.domains.map(d => d.toJSON());
|
|
91
|
+
}
|
|
87
92
|
if (this.dates.length > 0) {
|
|
88
93
|
obj.dates = this.dates.map(d => d.toJSON());
|
|
89
94
|
}
|
|
@@ -102,3 +107,15 @@ export class Concept extends GlossaristModel {
|
|
|
102
107
|
function _mapInstances(arr, Cls) {
|
|
103
108
|
return arr.map(item => item instanceof Cls ? item : new Cls(item));
|
|
104
109
|
}
|
|
110
|
+
|
|
111
|
+
function _normalizeDomains(domains, groups) {
|
|
112
|
+
if (domains) {
|
|
113
|
+
return domains.map(d => d instanceof ConceptReference ? d : new ConceptReference(d));
|
|
114
|
+
}
|
|
115
|
+
if (groups) {
|
|
116
|
+
return groups.map(g => typeof g === 'string'
|
|
117
|
+
? ConceptReference.domain(g)
|
|
118
|
+
: new ConceptReference(g));
|
|
119
|
+
}
|
|
120
|
+
return [];
|
|
121
|
+
}
|
package/src/models/index.d.ts
CHANGED
|
@@ -13,6 +13,7 @@ export class Concept extends GlossaristModel {
|
|
|
13
13
|
readonly localizations: Record<string, any>;
|
|
14
14
|
readonly raw: Record<string, unknown> | null;
|
|
15
15
|
readonly relatedConcepts: RelatedConcept[];
|
|
16
|
+
readonly domains: ConceptReference[];
|
|
16
17
|
readonly dates: ConceptDate[];
|
|
17
18
|
readonly sources: ConceptSource[];
|
|
18
19
|
readonly status: string | null;
|
|
@@ -34,6 +35,7 @@ export class LocalizedConcept extends GlossaristModel {
|
|
|
34
35
|
readonly examples: { content: string }[];
|
|
35
36
|
readonly sources: ConceptSource[];
|
|
36
37
|
readonly entryStatus: string | null;
|
|
38
|
+
readonly domain: string | null;
|
|
37
39
|
readonly primaryDesignation: string | null;
|
|
38
40
|
readonly primaryDefinition: string | null;
|
|
39
41
|
static fromJSON(data: Record<string, unknown>): LocalizedConcept;
|
|
@@ -88,6 +90,17 @@ export class RelatedConcept extends GlossaristModel {
|
|
|
88
90
|
readonly ref: Citation | null;
|
|
89
91
|
}
|
|
90
92
|
|
|
93
|
+
export class ConceptReference extends GlossaristModel {
|
|
94
|
+
readonly conceptId: string | null;
|
|
95
|
+
readonly refType: string | null;
|
|
96
|
+
readonly source: string | null;
|
|
97
|
+
readonly urn: string | null;
|
|
98
|
+
readonly isLocal: boolean;
|
|
99
|
+
readonly isExternal: boolean;
|
|
100
|
+
static domain(conceptId: string): ConceptReference;
|
|
101
|
+
static fromJSON(data: Record<string, unknown>): ConceptReference;
|
|
102
|
+
}
|
|
103
|
+
|
|
91
104
|
export const DATE_TYPES: readonly string[];
|
|
92
105
|
export class ConceptDate extends GlossaristModel {
|
|
93
106
|
readonly date: string | null;
|
package/src/models/index.js
CHANGED
|
@@ -5,6 +5,7 @@ export { Designation, Expression, Abbreviation, Symbol, GraphicalSymbol } from '
|
|
|
5
5
|
export { Citation } from './citation.js';
|
|
6
6
|
export { ConceptSource } from './concept-source.js';
|
|
7
7
|
export { RelatedConcept, RELATIONSHIP_TYPES } from './related-concept.js';
|
|
8
|
+
export { ConceptReference } from './concept-reference.js';
|
|
8
9
|
export { ConceptDate, DATE_TYPES } from './concept-date.js';
|
|
9
10
|
export { DetailedDefinition } from './detailed-definition.js';
|
|
10
11
|
export { NonVerbRep } from './non-verb-rep.js';
|
|
@@ -15,6 +15,7 @@ export class LocalizedConcept extends GlossaristModel {
|
|
|
15
15
|
this.entryStatus = data.entry_status ?? data.entryStatus ?? null;
|
|
16
16
|
this.reviewType = data.review_type ?? data.reviewType ?? null;
|
|
17
17
|
this.classification = data.classification ?? null;
|
|
18
|
+
this.domain = data.domain ?? null;
|
|
18
19
|
|
|
19
20
|
this._terms = null;
|
|
20
21
|
this._definitions = null;
|
|
@@ -83,6 +84,7 @@ export class LocalizedConcept extends GlossaristModel {
|
|
|
83
84
|
if (this.entryStatus != null) obj.entry_status = this.entryStatus;
|
|
84
85
|
if (this.reviewType != null) obj.review_type = this.reviewType;
|
|
85
86
|
if (this.classification != null) obj.classification = this.classification;
|
|
87
|
+
if (this.domain != null) obj.domain = this.domain;
|
|
86
88
|
return obj;
|
|
87
89
|
}
|
|
88
90
|
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import yaml from 'js-yaml';
|
|
2
|
+
import { DATASET_ASSETS } from '../dataset-asset.js';
|
|
3
|
+
import { ValidationResult } from './validation-result.js';
|
|
4
|
+
|
|
5
|
+
export class GcrValidator {
|
|
6
|
+
async validate(pkg) {
|
|
7
|
+
const result = new ValidationResult();
|
|
8
|
+
await this._validateMetadata(pkg, result);
|
|
9
|
+
await this._validateConcepts(pkg, result);
|
|
10
|
+
await this._validateAssets(pkg, result);
|
|
11
|
+
return result;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
async _validateMetadata(pkg, result) {
|
|
15
|
+
const raw = await pkg._readText('metadata.yaml');
|
|
16
|
+
if (!raw) {
|
|
17
|
+
result.addError('metadata.yaml is missing');
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
let meta;
|
|
22
|
+
try {
|
|
23
|
+
meta = yaml.load(raw);
|
|
24
|
+
} catch (e) {
|
|
25
|
+
result.addError(`metadata.yaml: invalid YAML: ${e.message}`);
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (!meta.shortname) result.addError('metadata.yaml missing shortname');
|
|
30
|
+
if (!meta.version) result.addError('metadata.yaml missing version');
|
|
31
|
+
if (meta.concept_count == null) result.addError('metadata.yaml missing concept_count');
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async _validateConcepts(pkg, result) {
|
|
35
|
+
const ids = await pkg.conceptIds();
|
|
36
|
+
if (ids.length === 0) {
|
|
37
|
+
result.addError('No concept files found in concepts/');
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
async _validateAssets(pkg, result) {
|
|
42
|
+
for (const asset of DATASET_ASSETS) {
|
|
43
|
+
if (asset.type === 'file') {
|
|
44
|
+
await this._validateFileAsset(pkg, asset.path, result);
|
|
45
|
+
} else if (asset.type === 'directory') {
|
|
46
|
+
await this._validateDirectoryAsset(pkg, asset.path, result);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
async _validateFileAsset(pkg, path, result) {
|
|
52
|
+
const raw = await pkg._readText(path);
|
|
53
|
+
if (!raw) return;
|
|
54
|
+
try {
|
|
55
|
+
yaml.load(raw);
|
|
56
|
+
} catch (e) {
|
|
57
|
+
result.addError(`${path}: invalid YAML at line ${e.mark?.line ?? '?'}: ${e.message}`);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
async _validateDirectoryAsset(pkg, dirPath, result) {
|
|
62
|
+
let hasFiles = false;
|
|
63
|
+
let hasEntries = false;
|
|
64
|
+
pkg._zip.forEach((relativePath, entry) => {
|
|
65
|
+
if (relativePath.startsWith(`${dirPath}/`)) {
|
|
66
|
+
hasEntries = true;
|
|
67
|
+
if (!entry.dir) hasFiles = true;
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
if (hasEntries && !hasFiles) {
|
|
71
|
+
result.addWarning(`${dirPath}/ directory exists but is empty`);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
package/src/validators/index.js
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
export { ValidationError } from './validation-error.js';
|
|
2
2
|
export { ValidationRule } from './validation-rule.js';
|
|
3
|
+
export { ValidationResult } from './validation-result.js';
|
|
3
4
|
export { ConceptValidator, LanguageCodeRule, DesignationTypeRule, EntryStatusRule } from './concept-validator.js';
|
|
4
5
|
export { RegisterValidator } from './register-validator.js';
|
|
6
|
+
export { GcrValidator } from './gcr-validator.js';
|
|
5
7
|
|
|
6
8
|
import { ConceptValidator, LanguageCodeRule, DesignationTypeRule, EntryStatusRule } from './concept-validator.js';
|
|
7
9
|
import { RegisterValidator } from './register-validator.js';
|
|
10
|
+
import { GcrValidator } from './gcr-validator.js';
|
|
8
11
|
|
|
9
12
|
const _default = new ConceptValidator()
|
|
10
13
|
.addRule(new LanguageCodeRule())
|
|
@@ -22,3 +25,7 @@ export function createConceptValidator() {
|
|
|
22
25
|
export function validateRegister(register) {
|
|
23
26
|
return new RegisterValidator().validate(register);
|
|
24
27
|
}
|
|
28
|
+
|
|
29
|
+
export async function validateGcrPackage(pkg) {
|
|
30
|
+
return new GcrValidator().validate(pkg);
|
|
31
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export class ValidationResult {
|
|
2
|
+
constructor() {
|
|
3
|
+
this.errors = [];
|
|
4
|
+
this.warnings = [];
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
get valid() {
|
|
8
|
+
return this.errors.length === 0;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
addError(message) {
|
|
12
|
+
this.errors.push(message);
|
|
13
|
+
return this;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
addWarning(message) {
|
|
17
|
+
this.warnings.push(message);
|
|
18
|
+
return this;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
merge(other) {
|
|
22
|
+
for (const e of other.errors) this.errors.push(e);
|
|
23
|
+
for (const w of other.warnings) this.warnings.push(w);
|
|
24
|
+
return this;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
toJSON() {
|
|
28
|
+
return {
|
|
29
|
+
valid: this.valid,
|
|
30
|
+
errors: [...this.errors],
|
|
31
|
+
warnings: [...this.warnings],
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
}
|