glossarist 0.1.2 → 0.1.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/README.md +176 -104
- package/package.json +1 -1
- package/src/gcr-writer.js +1 -1
- package/src/index.d.ts +1 -1
- package/src/index.js +1 -1
- package/src/validators/concept-validator.js +0 -20
- package/src/validators/index.d.ts +18 -1
- package/src/validators/index.js +3 -2
- package/src/validators/register-validator.js +22 -0
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
[](https://www.npmjs.com/package/glossarist)
|
|
5
5
|
[](https://opensource.org/licenses/MIT)
|
|
6
6
|
|
|
7
|
-
JavaScript
|
|
7
|
+
JavaScript SDK for reading and writing [Glossarist](https://github.com/glossarist) GCR packages — manages terminology concepts with rich domain models, bidirectional YAML serialization, validation, and cross-reference resolution.
|
|
8
8
|
|
|
9
9
|
## Install
|
|
10
10
|
|
|
@@ -12,105 +12,213 @@ JavaScript library for reading [Glossarist](https://github.com/glossarist) GCR p
|
|
|
12
12
|
npm install glossarist
|
|
13
13
|
```
|
|
14
14
|
|
|
15
|
-
Requires Node.js
|
|
15
|
+
Requires Node.js 20+.
|
|
16
16
|
|
|
17
17
|
## Usage
|
|
18
18
|
|
|
19
|
-
###
|
|
19
|
+
### Read a GCR package
|
|
20
20
|
|
|
21
21
|
```js
|
|
22
22
|
import { loadGcr } from 'glossarist';
|
|
23
|
-
import fs from 'fs';
|
|
24
23
|
|
|
25
|
-
const
|
|
26
|
-
const pkg = await loadGcr(buf);
|
|
27
|
-
|
|
28
|
-
// Metadata
|
|
24
|
+
const pkg = await loadGcr(fs.readFileSync('my-dataset.gcr'));
|
|
29
25
|
const meta = await pkg.metadata();
|
|
30
|
-
console.log(meta.shortname, meta.version, meta.concept_count);
|
|
31
|
-
|
|
32
|
-
// List concept IDs
|
|
33
|
-
const ids = await pkg.conceptIds();
|
|
34
|
-
|
|
35
|
-
// Read a specific concept
|
|
36
|
-
const concept = await pkg.concept('3.1.1.1');
|
|
37
|
-
console.log(concept.termid);
|
|
38
|
-
console.log(concept.localizations.eng.terms[0].designation);
|
|
39
26
|
|
|
40
|
-
//
|
|
27
|
+
// Stream concepts (memory-efficient for large datasets)
|
|
41
28
|
await pkg.eachConcept((concept) => {
|
|
42
|
-
console.log(concept.
|
|
29
|
+
console.log(concept.id, concept.primaryDesignation('eng'));
|
|
43
30
|
});
|
|
44
31
|
```
|
|
45
32
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
### Reading concept YAML files from a directory
|
|
33
|
+
### Read from a directory
|
|
49
34
|
|
|
50
35
|
```js
|
|
51
|
-
import { readConcepts,
|
|
36
|
+
import { readConcepts, readRegister } from 'glossarist';
|
|
52
37
|
|
|
53
|
-
// Read all concepts
|
|
54
38
|
const concepts = readConcepts('./geolexica-v2/');
|
|
55
|
-
|
|
39
|
+
const register = readRegister('./geolexica-v2/');
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### Write a GCR package
|
|
43
|
+
|
|
44
|
+
```js
|
|
45
|
+
import { createGcr, ManagedConceptCollection, conceptParser } from 'glossarist';
|
|
46
|
+
|
|
47
|
+
const concept = conceptParser.parse(`
|
|
48
|
+
termid: "3.1.1.1"
|
|
49
|
+
eng:
|
|
50
|
+
terms:
|
|
51
|
+
- type: expression
|
|
52
|
+
designation: entity
|
|
53
|
+
definition:
|
|
54
|
+
- content: A concrete or abstract thing.
|
|
55
|
+
`);
|
|
56
|
+
|
|
57
|
+
const buf = await createGcr([concept], { shortname: 'test' });
|
|
58
|
+
fs.writeFileSync('out.gcr', buf);
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### Domain model
|
|
62
|
+
|
|
63
|
+
Every domain entity is a class instance with `toJSON()`, `fromJSON()`, `equals()`, and `clone()`:
|
|
64
|
+
|
|
65
|
+
```js
|
|
66
|
+
import { Concept, LocalizedConcept, Expression, DetailedDefinition } from 'glossarist/models';
|
|
67
|
+
|
|
68
|
+
const lc = new LocalizedConcept({
|
|
69
|
+
language_code: 'eng',
|
|
70
|
+
terms: [{ type: 'expression', designation: 'entity', normative_status: 'preferred' }],
|
|
71
|
+
definition: [{ content: 'A concrete or abstract thing.' }],
|
|
72
|
+
entry_status: 'valid',
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
const concept = new Concept({
|
|
76
|
+
id: '3.1.1.1',
|
|
77
|
+
localizations: { eng: lc.toJSON() },
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
console.log(concept.primaryDesignation('eng')); // 'entity'
|
|
81
|
+
console.log(concept.definition('eng')); // 'A concrete or abstract thing.'
|
|
56
82
|
|
|
57
|
-
//
|
|
58
|
-
const
|
|
83
|
+
// Round-trip invariant
|
|
84
|
+
const restored = Concept.fromJSON(concept.toJSON());
|
|
85
|
+
console.log(concept.equals(restored)); // true
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Validation
|
|
89
|
+
|
|
90
|
+
```js
|
|
91
|
+
import { validateConcept, validateRegister, createConceptValidator, ValidationRule } from 'glossarist';
|
|
92
|
+
|
|
93
|
+
// Built-in rules: language codes, designation types, entry status
|
|
94
|
+
const result = validateConcept(concept);
|
|
95
|
+
if (!result.valid) {
|
|
96
|
+
for (const err of result.errors) {
|
|
97
|
+
console.log(`[${err.severity}] ${err.path}: ${err.message}`);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Custom rules
|
|
102
|
+
class NoDuplicateTermsRule extends ValidationRule {
|
|
103
|
+
constructor() { super('no-duplicate-terms', 'warning'); }
|
|
104
|
+
validate(value, path) {
|
|
105
|
+
// check for duplicate designations...
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const validator = createConceptValidator().addRule(new NoDuplicateTermsRule());
|
|
110
|
+
validator.validate(concept);
|
|
59
111
|
|
|
60
|
-
//
|
|
61
|
-
|
|
112
|
+
// Register validation
|
|
113
|
+
validateRegister({ schema_version: '1', shortname: 'my-dataset' });
|
|
62
114
|
```
|
|
63
115
|
|
|
64
|
-
###
|
|
116
|
+
### UUID generation
|
|
65
117
|
|
|
66
|
-
|
|
118
|
+
Deterministic UUID v5 matching the Ruby glossarist gem:
|
|
67
119
|
|
|
68
|
-
```
|
|
69
|
-
|
|
70
|
-
import { loadGcr } from 'glossarist/gcr';
|
|
120
|
+
```js
|
|
121
|
+
import { conceptUuid, localizedConceptUuid } from 'glossarist';
|
|
71
122
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
const pkg = await loadGcr(buf);
|
|
75
|
-
const meta = await pkg.metadata();
|
|
76
|
-
</script>
|
|
123
|
+
conceptUuid('3.1.1.1'); // → UUID v5 (stable across runs)
|
|
124
|
+
localizedConceptUuid('3.1.1.1', 'eng'); // → different UUID v5
|
|
77
125
|
```
|
|
78
126
|
|
|
79
|
-
|
|
127
|
+
### Reference resolution
|
|
80
128
|
|
|
81
|
-
|
|
129
|
+
Extract and resolve cross-references between concepts:
|
|
82
130
|
|
|
83
131
|
```js
|
|
84
|
-
{
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
fra: { ... },
|
|
97
|
-
},
|
|
98
|
-
raw: { ... }, // original parsed YAML
|
|
132
|
+
import { referenceResolver } from 'glossarist';
|
|
133
|
+
import { ConceptCollection } from 'glossarist';
|
|
134
|
+
|
|
135
|
+
const collection = new ConceptCollection(allConcepts);
|
|
136
|
+
|
|
137
|
+
// Find all references in a concept
|
|
138
|
+
const refs = referenceResolver.extractReferences(concept);
|
|
139
|
+
|
|
140
|
+
// Resolve against a collection
|
|
141
|
+
const resolved = referenceResolver.resolveAll(concept, collection);
|
|
142
|
+
for (const [target, resolvedConcept] of resolved) {
|
|
143
|
+
if (!resolvedConcept) console.warn(`Broken reference: ${target}`);
|
|
99
144
|
}
|
|
100
145
|
```
|
|
101
146
|
|
|
102
|
-
|
|
147
|
+
### Managed collection lifecycle
|
|
148
|
+
|
|
149
|
+
```js
|
|
150
|
+
import { ManagedConceptCollection, conceptParser } from 'glossarist';
|
|
151
|
+
|
|
152
|
+
const mcc = new ManagedConceptCollection();
|
|
103
153
|
|
|
104
|
-
|
|
154
|
+
// Load from GCR
|
|
155
|
+
await mcc.loadFromGcr(fs.readFileSync('dataset.gcr'));
|
|
105
156
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
157
|
+
// Load from directory
|
|
158
|
+
mcc.loadFromDirectory('./concepts/');
|
|
159
|
+
|
|
160
|
+
// Add or replace a concept
|
|
161
|
+
mcc.add(newConcept);
|
|
162
|
+
|
|
163
|
+
// Save back
|
|
164
|
+
mcc.saveToDirectory('./out/');
|
|
165
|
+
const buf = await mcc.saveToGcr({ metadata: { shortname: 'test' } });
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### V1 format migration
|
|
169
|
+
|
|
170
|
+
```js
|
|
171
|
+
import { V1Reader, migrateV1ToV2 } from 'glossarist';
|
|
172
|
+
|
|
173
|
+
if (V1Reader.isV1Directory('./concepts-v1/')) {
|
|
174
|
+
const concepts = V1Reader.readAll('./concepts-v1/');
|
|
175
|
+
await migrateV1ToV2('./concepts-v1/', './concepts-v2/');
|
|
176
|
+
}
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
### Concept serialization
|
|
180
|
+
|
|
181
|
+
Serialize to canonical (single-doc) or managed (multi-doc) format:
|
|
182
|
+
|
|
183
|
+
```js
|
|
184
|
+
import { conceptSerializer } from 'glossarist';
|
|
185
|
+
|
|
186
|
+
conceptSerializer.toCanonicalYaml(concept); // single YAML doc with termid + lang keys
|
|
187
|
+
conceptSerializer.toManagedYaml(concept); // multi-doc YAML with data.identifier
|
|
188
|
+
conceptSerializer.toYaml(concept); // auto-detect: uses term for canonical, id for managed
|
|
189
|
+
conceptSerializer.toRegisterYaml(register); // register.yaml format
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
## Sub-path exports
|
|
193
|
+
|
|
194
|
+
```js
|
|
195
|
+
import 'glossarist'; // everything
|
|
196
|
+
import 'glossarist/gcr'; // browser-friendly GCR reader (no fs)
|
|
197
|
+
import 'glossarist/concept'; // Node.js filesystem reader
|
|
198
|
+
import 'glossarist/models'; // domain model classes
|
|
199
|
+
import 'glossarist/validators'; // validation framework
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
## Architecture
|
|
203
|
+
|
|
204
|
+
```
|
|
205
|
+
Public API (index.js)
|
|
206
|
+
├── Domain models → Concept, LocalizedConcept, Designation (Expression, Symbol, ...),
|
|
207
|
+
│ Citation, ConceptSource, RelatedConcept, DetailedDefinition, NonVerbRep
|
|
208
|
+
├── Parsing → ConceptParser (canonical + managed format detection)
|
|
209
|
+
├── Serialization → ConceptSerializer (canonical + managed YAML output)
|
|
210
|
+
├── I/O → loadGcr, readConcepts, createGcr, writeConcepts
|
|
211
|
+
├── Collections → ConceptCollection (Proxy-based, queryable), ManagedConceptCollection
|
|
212
|
+
├── Validation → ConceptValidator, RegisterValidator, ValidationRule (pluggable)
|
|
213
|
+
├── Utilities → conceptUuid, referenceResolver, V1Reader
|
|
214
|
+
└── Errors → GlossaristError, InvalidInputError, YamlParseError
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
Models are pure — no I/O, serialization, or filesystem dependencies. Serialization formats are pluggable. Validation rules are pluggable.
|
|
110
218
|
|
|
111
219
|
## Error handling
|
|
112
220
|
|
|
113
|
-
All public functions validate inputs and throw
|
|
221
|
+
All public functions validate inputs and throw typed errors:
|
|
114
222
|
|
|
115
223
|
```js
|
|
116
224
|
import { InvalidInputError, YamlParseError } from 'glossarist';
|
|
@@ -119,60 +227,26 @@ try {
|
|
|
119
227
|
await pkg.concept('3.1.1.1');
|
|
120
228
|
} catch (err) {
|
|
121
229
|
if (err instanceof YamlParseError) {
|
|
122
|
-
//
|
|
123
|
-
// err.
|
|
230
|
+
// Malformed YAML — err.cause chains the original error
|
|
231
|
+
// err.message includes the concept ID for easy location
|
|
124
232
|
} else if (err instanceof InvalidInputError) {
|
|
125
|
-
//
|
|
233
|
+
// Null, empty, or wrong-type arguments
|
|
126
234
|
}
|
|
127
235
|
}
|
|
128
236
|
```
|
|
129
237
|
|
|
130
|
-
Errors include the concept ID or filename in their message, making it easy to locate failures in large datasets.
|
|
131
|
-
|
|
132
|
-
- **`GlossaristError`** — base class for all library errors
|
|
133
|
-
- **`InvalidInputError`** — null, undefined, empty, or wrong-type arguments
|
|
134
|
-
- **`YamlParseError`** — malformed YAML with `cause` chaining the original error
|
|
135
|
-
|
|
136
238
|
## TypeScript
|
|
137
239
|
|
|
138
240
|
TypeScript declarations are included. No `@types/` package needed.
|
|
139
241
|
|
|
140
242
|
```ts
|
|
141
|
-
import { loadGcr,
|
|
243
|
+
import { loadGcr, type Concept, type GcrMetadata } from 'glossarist';
|
|
244
|
+
import { Concept, LocalizedConcept, Designation } from 'glossarist/models';
|
|
142
245
|
|
|
143
246
|
const pkg = await loadGcr(buffer);
|
|
144
247
|
const meta: GcrMetadata | null = await pkg.metadata();
|
|
145
248
|
```
|
|
146
249
|
|
|
147
|
-
## API
|
|
148
|
-
|
|
149
|
-
### GCR Package (`glossarist/gcr`)
|
|
150
|
-
|
|
151
|
-
- `loadGcr(input)` — Load a GCR ZIP from Buffer/ArrayBuffer/Uint8Array/Blob/base64 string. Returns `GcrPackage`.
|
|
152
|
-
- `GcrPackage#metadata()` — Parse `metadata.yaml`.
|
|
153
|
-
- `GcrPackage#register()` — Parse optional `register.yaml`.
|
|
154
|
-
- `GcrPackage#conceptIds()` — Array of concept IDs (natural-sorted).
|
|
155
|
-
- `GcrPackage#concept(id)` — Read and normalize a single concept.
|
|
156
|
-
- `GcrPackage#eachConcept(callback)` — Stream all concepts.
|
|
157
|
-
- `GcrPackage#allConcepts()` — Load all concepts into an array.
|
|
158
|
-
- `parseConceptYaml(raw, context?)` — Parse raw YAML string into normalized concept object. `context` is an optional concept ID or filename for error messages.
|
|
159
|
-
- `naturalSort(a, b)` — Natural sort comparator for concept IDs.
|
|
160
|
-
|
|
161
|
-
### Concept Directory Reader (`glossarist/concept`)
|
|
162
|
-
|
|
163
|
-
Node.js only (uses `fs`).
|
|
164
|
-
|
|
165
|
-
- `readConcepts(dir)` — Read all concept YAML files from a directory.
|
|
166
|
-
- `readConcept(dir, id)` — Read a single concept by ID.
|
|
167
|
-
- `listConceptIds(dir, prefix?)` — List concept IDs, optionally filtered by prefix.
|
|
168
|
-
- `readRegister(dir)` — Read `register.yaml` if present.
|
|
169
|
-
|
|
170
|
-
### Errors
|
|
171
|
-
|
|
172
|
-
- `GlossaristError` — base error class
|
|
173
|
-
- `InvalidInputError` — bad input arguments
|
|
174
|
-
- `YamlParseError` — YAML parse failures (has `cause`, includes concept context)
|
|
175
|
-
|
|
176
250
|
## Development
|
|
177
251
|
|
|
178
252
|
```bash
|
|
@@ -182,8 +256,6 @@ npm run lint # lint src/ and test/
|
|
|
182
256
|
npm run test:coverage # run with coverage report
|
|
183
257
|
```
|
|
184
258
|
|
|
185
|
-
See [CONTRIBUTING.md](./CONTRIBUTING.md) for full guidelines.
|
|
186
|
-
|
|
187
259
|
## License
|
|
188
260
|
|
|
189
261
|
[MIT](./LICENSE)
|
package/package.json
CHANGED
package/src/gcr-writer.js
CHANGED
|
@@ -4,7 +4,7 @@ import { InvalidInputError } from './errors.js';
|
|
|
4
4
|
|
|
5
5
|
export class GcrWriter {
|
|
6
6
|
static async createBuffer(options) {
|
|
7
|
-
if (!options || !
|
|
7
|
+
if (!options || !options.concepts || typeof options.concepts[Symbol.iterator] !== 'function') {
|
|
8
8
|
throw new InvalidInputError(
|
|
9
9
|
'GcrWriter requires { concepts: Concept[] }',
|
|
10
10
|
'object with concepts array',
|
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 } from './validators/index';
|
|
29
|
+
export { validateConcept, validateRegister, createConceptValidator, ValidationError, ValidationRule, RegisterValidator } from './validators/index';
|
|
30
30
|
|
|
31
31
|
// UUID
|
|
32
32
|
export { conceptUuid, localizedConceptUuid, uuidV5 } from './uuid';
|
package/src/index.js
CHANGED
|
@@ -4,7 +4,7 @@ export { writeConcept, writeConcepts } from './concept-writer.js';
|
|
|
4
4
|
export { createGcr, GcrWriter } from './gcr-writer.js';
|
|
5
5
|
export { ConceptCollection } from './concept-collection.js';
|
|
6
6
|
export { ManagedConceptCollection } from './managed-concept-collection.js';
|
|
7
|
-
export { validateConcept, validateRegister, createConceptValidator, ValidationError, ValidationRule } from './validators/index.js';
|
|
7
|
+
export { validateConcept, validateRegister, createConceptValidator, ValidationError, ValidationRule, RegisterValidator } from './validators/index.js';
|
|
8
8
|
export { conceptUuid, localizedConceptUuid, uuidV5 } from './uuid.js';
|
|
9
9
|
export { ReferenceResolver, Reference, referenceResolver } from './reference-resolver.js';
|
|
10
10
|
export { V1Reader, migrateV1ToV2 } from './v1-reader.js';
|
|
@@ -97,23 +97,3 @@ export class ConceptValidator {
|
|
|
97
97
|
}
|
|
98
98
|
}
|
|
99
99
|
|
|
100
|
-
export class RegisterValidator {
|
|
101
|
-
validate(register) {
|
|
102
|
-
const errors = [];
|
|
103
|
-
if (!register || typeof register !== 'object') {
|
|
104
|
-
errors.push(new ValidationError('', 'Register must be a non-null object'));
|
|
105
|
-
return { valid: false, errors, warnings: [] };
|
|
106
|
-
}
|
|
107
|
-
if (!register.schema_version) {
|
|
108
|
-
errors.push(new ValidationError('schema_version', 'Register must have a schema_version', 'warning'));
|
|
109
|
-
}
|
|
110
|
-
if (!register.shortname) {
|
|
111
|
-
errors.push(new ValidationError('shortname', 'Register should have a shortname', 'warning'));
|
|
112
|
-
}
|
|
113
|
-
return {
|
|
114
|
-
valid: errors.filter(e => e.severity === 'error').length === 0,
|
|
115
|
-
errors: errors.filter(e => e.severity === 'error'),
|
|
116
|
-
warnings: errors.filter(e => e.severity === 'warning'),
|
|
117
|
-
};
|
|
118
|
-
}
|
|
119
|
-
}
|
|
@@ -11,6 +11,23 @@ export class ValidationRule {
|
|
|
11
11
|
validate(value: any, path: string): ValidationError[];
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
+
export class ConceptValidator {
|
|
15
|
+
addRule(rule: ValidationRule): this;
|
|
16
|
+
validate(concept: any): {
|
|
17
|
+
valid: boolean;
|
|
18
|
+
errors: ValidationError[];
|
|
19
|
+
warnings: ValidationError[];
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export class RegisterValidator {
|
|
24
|
+
validate(register: any): {
|
|
25
|
+
valid: boolean;
|
|
26
|
+
errors: ValidationError[];
|
|
27
|
+
warnings: ValidationError[];
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
|
|
14
31
|
export function validateConcept(concept: any): {
|
|
15
32
|
valid: boolean;
|
|
16
33
|
errors: ValidationError[];
|
|
@@ -23,4 +40,4 @@ export function validateRegister(register: any): {
|
|
|
23
40
|
warnings: ValidationError[];
|
|
24
41
|
};
|
|
25
42
|
|
|
26
|
-
export function createConceptValidator():
|
|
43
|
+
export function createConceptValidator(): ConceptValidator;
|
package/src/validators/index.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
export { ValidationError } from './validation-error.js';
|
|
2
2
|
export { ValidationRule } from './validation-rule.js';
|
|
3
|
-
export { ConceptValidator,
|
|
3
|
+
export { ConceptValidator, LanguageCodeRule, DesignationTypeRule, EntryStatusRule } from './concept-validator.js';
|
|
4
|
+
export { RegisterValidator } from './register-validator.js';
|
|
4
5
|
|
|
5
6
|
import { ConceptValidator, LanguageCodeRule, DesignationTypeRule, EntryStatusRule } from './concept-validator.js';
|
|
6
|
-
import { RegisterValidator } from './
|
|
7
|
+
import { RegisterValidator } from './register-validator.js';
|
|
7
8
|
|
|
8
9
|
const _default = new ConceptValidator()
|
|
9
10
|
.addRule(new LanguageCodeRule())
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { ValidationError } from './validation-error.js';
|
|
2
|
+
|
|
3
|
+
export class RegisterValidator {
|
|
4
|
+
validate(register) {
|
|
5
|
+
const errors = [];
|
|
6
|
+
if (!register || typeof register !== 'object') {
|
|
7
|
+
errors.push(new ValidationError('', 'Register must be a non-null object'));
|
|
8
|
+
return { valid: false, errors, warnings: [] };
|
|
9
|
+
}
|
|
10
|
+
if (!register.schema_version) {
|
|
11
|
+
errors.push(new ValidationError('schema_version', 'Register must have a schema_version', 'warning'));
|
|
12
|
+
}
|
|
13
|
+
if (!register.shortname) {
|
|
14
|
+
errors.push(new ValidationError('shortname', 'Register should have a shortname', 'warning'));
|
|
15
|
+
}
|
|
16
|
+
return {
|
|
17
|
+
valid: errors.filter(e => e.severity === 'error').length === 0,
|
|
18
|
+
errors: errors.filter(e => e.severity === 'error'),
|
|
19
|
+
warnings: errors.filter(e => e.severity === 'warning'),
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
}
|