glossarist 0.4.1 → 0.4.3

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.
Files changed (39) hide show
  1. package/data/concept-model/README.md +44 -0
  2. package/data/concept-model/shapes/glossarist.shacl.ttl +704 -0
  3. package/package.json +24 -4
  4. package/src/concept-collection.js +57 -0
  5. package/src/concept-parser.js +1 -1
  6. package/src/concept-reader.js +1 -1
  7. package/src/concept-serializer.js +1 -1
  8. package/src/gcr-reader.js +1 -1
  9. package/src/index.js +15 -0
  10. package/src/models/bibliography-data.js +1 -1
  11. package/src/models/concept.js +6 -0
  12. package/src/models/dataset-color.js +79 -0
  13. package/src/models/designation.js +54 -0
  14. package/src/models/gcr-metadata.js +1 -1
  15. package/src/models/index.d.ts +121 -4
  16. package/src/models/index.js +13 -0
  17. package/src/models/register.js +99 -1
  18. package/src/models/relation-categories.js +151 -0
  19. package/src/models/relation-colors.js +78 -0
  20. package/src/rdf/deterministic-id.js +17 -0
  21. package/src/rdf/document-writer.js +87 -0
  22. package/src/rdf/gloss-concept.js +43 -0
  23. package/src/rdf/gloss-designation.js +72 -0
  24. package/src/rdf/gloss-detailed-definition.js +69 -0
  25. package/src/rdf/gloss-localized-concept.js +72 -0
  26. package/src/rdf/gloss-source.js +51 -0
  27. package/src/rdf/index.d.ts +107 -0
  28. package/src/rdf/index.js +27 -0
  29. package/src/rdf/normalize-enum.js +23 -0
  30. package/src/rdf/predicates.d.ts +443 -0
  31. package/src/rdf/predicates.js +245 -0
  32. package/src/rdf/prefixes.js +31 -0
  33. package/src/rdf/shacl.js +98 -0
  34. package/src/rdf/terms.js +15 -0
  35. package/src/transforms/concept-to-gloss.transform.js +75 -0
  36. package/src/transforms/index.d.ts +26 -0
  37. package/src/transforms/index.js +1 -0
  38. package/src/v1-reader.js +1 -1
  39. package/src/validators/gcr-validator.js +1 -1
package/package.json CHANGED
@@ -1,13 +1,15 @@
1
1
  {
2
2
  "name": "glossarist",
3
- "version": "0.4.1",
3
+ "version": "0.4.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",
8
8
  "sideEffects": false,
9
9
  "files": [
10
- "src"
10
+ "src",
11
+ "data/concept-model/shapes/glossarist.shacl.ttl",
12
+ "data/concept-model/README.md"
11
13
  ],
12
14
  "exports": {
13
15
  ".": {
@@ -34,10 +36,23 @@
34
36
  "types": "./src/validators/index.d.ts",
35
37
  "import": "./src/validators/index.js",
36
38
  "default": "./src/validators/index.js"
39
+ },
40
+ "./rdf": {
41
+ "types": "./src/rdf/index.d.ts",
42
+ "import": "./src/rdf/index.js",
43
+ "default": "./src/rdf/index.js"
44
+ },
45
+ "./transforms": {
46
+ "types": "./src/transforms/index.d.ts",
47
+ "import": "./src/transforms/index.js",
48
+ "default": "./src/transforms/index.js"
37
49
  }
38
50
  },
39
51
  "scripts": {
40
52
  "lint": "eslint src/ test/",
53
+ "sync:model": "node scripts/sync-concept-model.mjs",
54
+ "gen:predicates": "node scripts/gen-predicates.mjs",
55
+ "prebuild": "npm run gen:predicates",
41
56
  "pretest": "node test/fixtures/build-fixtures.js",
42
57
  "test": "find test -name '*.test.js' | sort | xargs node --test",
43
58
  "test:verbose": "find test -name '*.test.js' | sort | xargs node --test --test-reporter spec",
@@ -68,8 +83,13 @@
68
83
  "access": "public"
69
84
  },
70
85
  "dependencies": {
71
- "js-yaml": "^4.1.0",
72
- "jszip": "^3.10.1"
86
+ "@rdfjs/data-model": "^2.0.0",
87
+ "@rdfjs/dataset": "^2.0.0",
88
+ "js-yaml": "^5.0.0",
89
+ "jsonld": "^8.3.3",
90
+ "jszip": "^3.10.1",
91
+ "n3": "^1.17.0",
92
+ "rdf-validate-shacl": "^0.4.0"
73
93
  },
74
94
  "devDependencies": {
75
95
  "@eslint/js": "^10.0.1",
@@ -65,6 +65,43 @@ export class ConceptCollection {
65
65
  }));
66
66
  }
67
67
 
68
+ /**
69
+ * Cascading section membership filter. Returns concepts whose
70
+ * section closure includes `sectionId`.
71
+ *
72
+ * A concept in section "3.1.1" matches `bySection('3.1')` and
73
+ * `bySection('3')` because the section closure walks ancestors
74
+ * transitively (mirrors owl:TransitiveProperty on
75
+ * gloss:hasParentSection in the concept-model ontology).
76
+ *
77
+ * Two calling conventions:
78
+ *
79
+ * bySection('3.1', { register }) // register expands each concept's section closure
80
+ * bySection(['3.1', '3']) // pre-expanded target set (any of these must match)
81
+ *
82
+ * Concept-side membership is read from `concept.sections` if set,
83
+ * otherwise from `concept.groups` (a flat list of section IDs).
84
+ */
85
+ bySection(sectionId, options = {}) {
86
+ if (Array.isArray(sectionId)) {
87
+ const targetSet = new Set(sectionId);
88
+ return new ConceptCollection(this[_items].filter(c => {
89
+ const ids = options.register
90
+ ? options.register.conceptSectionIds(c)
91
+ : _flatConceptSectionIds(c);
92
+ return ids.some(id => targetSet.has(id));
93
+ }));
94
+ }
95
+ if (!options.register) {
96
+ throw new Error('bySection(sectionId) requires { register } to expand the concept section closures');
97
+ }
98
+ const target = sectionId;
99
+ return new ConceptCollection(this[_items].filter(c => {
100
+ const ids = options.register.conceptSectionIds(c);
101
+ return ids.includes(target);
102
+ }));
103
+ }
104
+
68
105
  index() {
69
106
  const map = new Map();
70
107
  for (const c of this[_items]) map.set(c.id, c);
@@ -106,3 +143,23 @@ export class ConceptCollection {
106
143
  slice(...args) { return new ConceptCollection(this[_items].slice(...args)); }
107
144
  concat(...args) { return new ConceptCollection(this[_items].concat(...args)); }
108
145
  }
146
+
147
+ // Standalone helper for the array-closure case where no register is
148
+ // available. Mirrors the lookup done inside Register#conceptSectionIds
149
+ // but without ancestor expansion (the closure is already provided).
150
+ function _flatConceptSectionIds(concept) {
151
+ if (!concept) return [];
152
+ const out = [];
153
+ for (const source of [concept.sections, concept.groups]) {
154
+ if (!Array.isArray(source)) continue;
155
+ for (const entry of source) {
156
+ if (typeof entry === 'string') {
157
+ out.push(entry);
158
+ } else if (entry && typeof entry === 'object') {
159
+ const id = entry.id ?? entry.sectionId ?? entry.ref?.id;
160
+ if (id) out.push(String(id));
161
+ }
162
+ }
163
+ }
164
+ return out;
165
+ }
@@ -1,4 +1,4 @@
1
- import yaml from 'js-yaml';
1
+ import * as yaml from 'js-yaml';
2
2
  import { Concept } from './models/concept.js';
3
3
  import { RelatedConcept } from './models/related-concept.js';
4
4
  import { InvalidInputError, YamlParseError } from './errors.js';
@@ -1,6 +1,6 @@
1
1
  import fs from 'fs';
2
2
  import path from 'path';
3
- import yaml from 'js-yaml';
3
+ import * as 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';
@@ -1,4 +1,4 @@
1
- import yaml from 'js-yaml';
1
+ import * as yaml from 'js-yaml';
2
2
 
3
3
  const DUMP_OPTS = { lineWidth: -1, noRefs: true, sortKeys: false, skipInvalid: true };
4
4
 
package/src/gcr-reader.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import JSZip from 'jszip';
2
- import yaml from 'js-yaml';
2
+ import * as yaml from 'js-yaml';
3
3
  import { conceptParser } from './concept-parser.js';
4
4
  import { InvalidInputError } from './errors.js';
5
5
  import { COMPILED_FORMATS, parseCompiledPath, compiledPath } from './compiled-format.js';
package/src/index.js CHANGED
@@ -56,3 +56,18 @@ export {
56
56
  isKnownEntityType,
57
57
  parseEntityPath,
58
58
  } from './entity-directory.js';
59
+
60
+ // RDF serialization layer (WS C). Mirrors lib/glossarist/rdf/.
61
+ export {
62
+ PRED, PREFIXES, SKOSXL, WELL_KNOWN,
63
+ deterministicId, deterministicBnode,
64
+ conceptUri, conceptToQuads,
65
+ localizedConceptUri, localizedConceptToQuads,
66
+ designationToQuads, skosLabelPredicate, skosxlLabelPredicate,
67
+ detailedDefinitionToQuads,
68
+ conceptSourceToQuads,
69
+ collectQuads, writeTurtle, writeNTriples, writeJsonld, sortQuads,
70
+ validateShacl, loadShapes, quadsToDataset,
71
+ } from './rdf/index.js';
72
+
73
+ export { ConceptToGlossTransform } from './transforms/concept-to-gloss.transform.js';
@@ -1,4 +1,4 @@
1
- import yaml from 'js-yaml';
1
+ import * as yaml from 'js-yaml';
2
2
  import { GlossaristModel } from './base.js';
3
3
  import { BibliographyEntry } from './bibliography-entry.js';
4
4
 
@@ -19,6 +19,12 @@ export class Concept extends GlossaristModel {
19
19
 
20
20
  this.relatedConcepts = _mapInstances(data.relatedConcepts ?? data.related ?? data.related_concepts ?? [], RelatedConcept);
21
21
  this.domains = _normalizeDomains(data.domains, data.groups);
22
+ this.groups = Array.isArray(data.groups)
23
+ ? data.groups.map(g => typeof g === 'string' ? g : (g?.id ?? g?.sectionId ?? null)).filter(Boolean)
24
+ : [];
25
+ this.sections = Array.isArray(data.sections)
26
+ ? data.sections.map(s => typeof s === 'string' ? s : (s?.id ?? s?.sectionId ?? null)).filter(Boolean)
27
+ : [];
22
28
  this.tags = Array.isArray(data.tags) ? [...data.tags] : [];
23
29
  this.dates = _mapInstances(data.dates ?? [], ConceptDate);
24
30
  this.sources = _mapInstances(data.sources ?? [], ConceptSource);
@@ -0,0 +1,79 @@
1
+ // Per-dataset color spec, light/dark variants.
2
+ //
3
+ // A DatasetColor may be:
4
+ // - a single hex string (legacy, applied to both modes)
5
+ // - an explicit { light, dark } pair
6
+ //
7
+ // Round-trips through Register.toJSON unchanged so the data lives in
8
+ // the GCR package. Consumers call resolvedColor(mode) to pick the
9
+ // right hex for the current UI mode.
10
+
11
+ /**
12
+ * @typedef {string} DatasetColorSpec
13
+ * @typedef {{ light: string, dark: string }} DatasetColorPair
14
+ * @typedef {DatasetColorSpec | DatasetColorPair} DatasetColor
15
+ */
16
+
17
+ export const COLOR_MODES = Object.freeze(['light', 'dark']);
18
+
19
+ /**
20
+ * Returns the right hex for the given mode. Falls back to the single
21
+ * hex when the spec is a string. Returns null when the spec is null
22
+ * or when the requested mode is missing from a pair.
23
+ *
24
+ * @param {DatasetColor | null | undefined} color
25
+ * @param {'light' | 'dark'} mode
26
+ * @returns {string | null}
27
+ */
28
+ export function resolveColor(color, mode) {
29
+ if (color == null) return null;
30
+ if (typeof color === 'string') return color;
31
+ if (typeof color === 'object') {
32
+ if (!mode) return color.light ?? color.dark ?? null;
33
+ return color[mode] ?? null;
34
+ }
35
+ return null;
36
+ }
37
+
38
+ /**
39
+ * True when the spec declares an explicit {light, dark} object.
40
+ * Lets callers distinguish "single hex" from "per-mode pair" without
41
+ * repeating the typeof check.
42
+ */
43
+ export function isColorPair(color) {
44
+ return color != null && typeof color === 'object';
45
+ }
46
+
47
+ /**
48
+ * Validates a color spec. A valid spec is either:
49
+ * - a string matching /^#[0-9a-fA-F]{3,8}$/
50
+ * - an object with `light` and `dark` string fields, each matching
51
+ * the same pattern
52
+ *
53
+ * Returns null when valid, or an error message describing the issue.
54
+ */
55
+ export function validateColor(color) {
56
+ if (color == null) return null;
57
+ if (typeof color === 'string') return hexError(color);
58
+ if (typeof color === 'object') {
59
+ if (color.light == null && color.dark == null) {
60
+ return 'color pair must have at least one of `light` or `dark`';
61
+ }
62
+ if (color.light != null) {
63
+ const e = hexError(color.light);
64
+ if (e) return `color.light: ${e}`;
65
+ }
66
+ if (color.dark != null) {
67
+ const e = hexError(color.dark);
68
+ if (e) return `color.dark: ${e}`;
69
+ }
70
+ return null;
71
+ }
72
+ return `unexpected color type ${typeof color}`;
73
+ }
74
+
75
+ const HEX_RE = /^#[0-9a-fA-F]{3,8}$/;
76
+ function hexError(s) {
77
+ if (typeof s !== 'string') return `expected string, got ${typeof s}`;
78
+ return HEX_RE.test(s) ? null : `not a valid hex color: ${JSON.stringify(s)}`;
79
+ }
@@ -5,6 +5,16 @@ import { GrammarInfo } from './grammar-info.js';
5
5
  import { RelatedConcept } from './related-concept.js';
6
6
  import { DesignationRelationship, DESIGNATION_RELATIONSHIP_TYPES } from './designation-relationship.js';
7
7
 
8
+ // Maps a normative-status local-name to the matching SKOS/SKOS-XL label
9
+ // predicate local-name. Lives on the model so a new status requires a
10
+ // single edit here, not edits in every emitter.
11
+ const SKOS_LABEL_BY_NORMATIVE_STATUS = Object.freeze({
12
+ preferred: 'prefLabel',
13
+ deprecated: 'hiddenLabel',
14
+ admitted: 'altLabel',
15
+ deprecated_: 'altLabel',
16
+ });
17
+
8
18
  export class Designation extends RegistrableModel {
9
19
  constructor(data = {}) {
10
20
  super();
@@ -53,6 +63,34 @@ export class Designation extends RegistrableModel {
53
63
  return obj;
54
64
  }
55
65
 
66
+ // RDF class local-name for this designation subtype. Override in
67
+ // subclasses that map to a different ontology class.
68
+ rdfClass() {
69
+ return 'Expression';
70
+ }
71
+
72
+ // SKOS label predicate URI (skos:prefLabel / skos:altLabel / skos:hiddenLabel)
73
+ // appropriate for this designation's normative status. Falls back to
74
+ // skos:altLabel for unknown statuses.
75
+ skosLabelPredicate(skosNs) {
76
+ const label = this._skosLabelLocalName();
77
+ return `${skosNs}${label}`;
78
+ }
79
+
80
+ // SKOS-XL label predicate URI (skosxl:prefLabel / etc.).
81
+ skosxlLabelPredicate(skosxlNs) {
82
+ const label = this._skosLabelLocalName();
83
+ return `${skosxlNs}${label}`;
84
+ }
85
+
86
+ _skosLabelLocalName() {
87
+ const status = String(this.normativeStatus ?? '')
88
+ .split(/[/#]/)
89
+ .pop()
90
+ .trim();
91
+ return SKOS_LABEL_BY_NORMATIVE_STATUS[status] ?? 'altLabel';
92
+ }
93
+
56
94
  static fromJSON(data) {
57
95
  return Designation.fromData(data);
58
96
  }
@@ -87,6 +125,10 @@ export class Abbreviation extends Expression {
87
125
  this.truncation = data.truncation ?? false;
88
126
  }
89
127
 
128
+ rdfClass() {
129
+ return 'Abbreviation';
130
+ }
131
+
90
132
  toJSON() {
91
133
  const obj = super.toJSON();
92
134
  if (this.acronym) obj.acronym = true;
@@ -101,6 +143,10 @@ export class Abbreviation extends Expression {
101
143
  Designation.register('abbreviation', Abbreviation);
102
144
 
103
145
  export class Symbol extends Designation {
146
+ rdfClass() {
147
+ return 'Symbol';
148
+ }
149
+
104
150
  static fromJSON(data) { return new Symbol(data); }
105
151
  }
106
152
 
@@ -112,6 +158,10 @@ export class LetterSymbol extends Symbol {
112
158
  this.text = data.text ?? null;
113
159
  }
114
160
 
161
+ rdfClass() {
162
+ return 'LetterSymbol';
163
+ }
164
+
115
165
  toJSON() {
116
166
  const obj = super.toJSON();
117
167
  if (this.text != null) obj.text = this.text;
@@ -130,6 +180,10 @@ export class GraphicalSymbol extends Symbol {
130
180
  this.image = data.image ?? null;
131
181
  }
132
182
 
183
+ rdfClass() {
184
+ return 'GraphicalSymbol';
185
+ }
186
+
133
187
  toJSON() {
134
188
  const obj = super.toJSON();
135
189
  if (this.text != null) obj.text = this.text;
@@ -1,4 +1,4 @@
1
- import yaml from 'js-yaml';
1
+ import * as yaml from 'js-yaml';
2
2
  import { GlossaristModel } from './base.js';
3
3
  import { GcrStatistics } from './gcr-statistics.js';
4
4
 
@@ -42,6 +42,8 @@ export class GlossaristModel {
42
42
  static fromJSON(data: Record<string, unknown>): GlossaristModel;
43
43
  equals(other: GlossaristModel): boolean;
44
44
  clone(): GlossaristModel;
45
+ protected _lazy<T>(cacheKey: string, rawKey: string, wrapFn: (item: any) => T): T[];
46
+ protected _serialize(obj: Record<string, unknown>, jsonKey: string, cacheKey: string, rawKey: string): void;
45
47
  }
46
48
 
47
49
  export class Concept extends GlossaristModel {
@@ -117,6 +119,9 @@ export class Designation extends GlossaristModel {
117
119
  readonly pronunciations: Pronunciation[];
118
120
  readonly sources: ConceptSource[];
119
121
  readonly related: (RelatedConcept | DesignationRelationship)[];
122
+ rdfClass(): string;
123
+ skosLabelPredicate(skosNs: string): string;
124
+ skosxlLabelPredicate(skosxlNs: string): string;
120
125
  static register(type: string, cls: typeof Designation): void;
121
126
  static fromData(data: Record<string, unknown>): Designation;
122
127
  static fromJSON(data: Record<string, unknown>): Designation;
@@ -250,13 +255,125 @@ export class DetailedDefinition extends GlossaristModel {
250
255
  static fromJSON(data: Record<string, unknown>): DetailedDefinition;
251
256
  }
252
257
 
253
- export class NonVerbRep extends GlossaristModel {
254
- readonly type: string | null;
255
- readonly ref: string | null;
256
- readonly text: string | null;
258
+ export class RegistrableModel extends GlossaristModel {
259
+ static register(type: string, cls: typeof RegistrableModel): void;
260
+ static fromData(data: Record<string, unknown>): RegistrableModel;
261
+ }
262
+
263
+ export class FigureImage extends GlossaristModel {
264
+ constructor(data?: Record<string, unknown>);
265
+ readonly src: string | null;
266
+ readonly format: string | null;
267
+ readonly role: string | null;
268
+ readonly width: number | null;
269
+ readonly height: number | null;
270
+ readonly scale: number | null;
271
+ static fromJSON(data: Record<string, unknown>): FigureImage;
272
+ }
273
+
274
+ export class NonVerbalEntity extends RegistrableModel {
275
+ constructor(data?: Record<string, unknown>);
276
+ readonly caption: Record<string, string> | null;
277
+ readonly description: Record<string, string> | null;
278
+ readonly alt: Record<string, string> | null;
257
279
  readonly sources: ConceptSource[];
280
+ findById(targetId: string): NonVerbalEntity | null;
281
+ allIds(): string[];
282
+ static fromJSON(data: Record<string, unknown>): NonVerbalEntity;
283
+ }
284
+
285
+ export class SharedNonVerbalEntity extends NonVerbalEntity {
286
+ constructor(data?: Record<string, unknown>);
287
+ readonly id: string | null;
288
+ readonly identifier: string | null;
289
+ findById(targetId: string): SharedNonVerbalEntity | null;
290
+ allIds(): string[];
291
+ static fromJSON(data: Record<string, unknown>): SharedNonVerbalEntity;
292
+ }
293
+
294
+ export class Figure extends SharedNonVerbalEntity {
295
+ constructor(data?: Record<string, unknown>);
296
+ readonly images: FigureImage[];
297
+ readonly subfigures: Figure[];
298
+ findById(targetId: string): Figure | null;
299
+ allIds(): string[];
300
+ static fromJSON(data: Record<string, unknown>): Figure;
301
+ }
302
+
303
+ export class Table extends SharedNonVerbalEntity {
304
+ constructor(data?: Record<string, unknown>);
305
+ readonly content: Record<string, unknown> | null;
306
+ readonly format: string | null;
307
+ static fromJSON(data: Record<string, unknown>): Table;
308
+ }
309
+
310
+ export class Formula extends SharedNonVerbalEntity {
311
+ constructor(data?: Record<string, unknown>);
312
+ readonly expression: Record<string, string> | null;
313
+ readonly notation: string | null;
314
+ static fromJSON(data: Record<string, unknown>): Formula;
315
+ }
316
+
317
+ export const NON_VERBAL_TYPES: readonly string[];
318
+
319
+ export class NonVerbRep extends NonVerbalEntity {
320
+ readonly type: string | null;
321
+ readonly images: FigureImage[];
322
+ static fromJSON(data: Record<string, unknown>): NonVerbRep;
258
323
  }
259
324
 
325
+ export class NonVerbalReference extends RegistrableModel {
326
+ constructor(data?: Record<string, unknown>);
327
+ readonly entityId: string | null;
328
+ readonly display: string | null;
329
+ readonly dedupKey: readonly [string, string | null];
330
+ static fromJSON(data: Record<string, unknown> | string): NonVerbalReference;
331
+ static register(type: string, cls: typeof NonVerbalReference): void;
332
+ }
333
+
334
+ export class FigureReference extends NonVerbalReference {
335
+ static fromJSON(data: Record<string, unknown> | string): FigureReference;
336
+ }
337
+
338
+ export class TableReference extends NonVerbalReference {
339
+ static fromJSON(data: Record<string, unknown> | string): TableReference;
340
+ }
341
+
342
+ export class FormulaReference extends NonVerbalReference {
343
+ static fromJSON(data: Record<string, unknown> | string): FormulaReference;
344
+ }
345
+
346
+ export class BibliographyEntry extends GlossaristModel {
347
+ constructor(data?: Record<string, unknown>);
348
+ readonly id: string | null;
349
+ readonly reference: string | null;
350
+ readonly title: string | null;
351
+ readonly link: string | null;
352
+ readonly type: string | null;
353
+ static fromJSON(data: Record<string, unknown>): BibliographyEntry;
354
+ }
355
+
356
+ export class BibliographyData extends GlossaristModel {
357
+ constructor(data?: Record<string, unknown>);
358
+ readonly entries: BibliographyEntry[];
359
+ find(id: string): BibliographyEntry | null;
360
+ readonly keys: string[];
361
+ toYAML(): string;
362
+ toJSON(): { bibliography: BibliographyEntry[] };
363
+ static fromYAML(yamlString: string): BibliographyData;
364
+ static fromJSON(data: Record<string, unknown>): BibliographyData;
365
+ }
366
+
367
+ export function fetchLocalizedString(
368
+ hash: Record<string, string> | null,
369
+ lang: string,
370
+ fallback?: string | null,
371
+ ): string | null;
372
+
373
+ export function localizedStringIsEmpty(hash: Record<string, string> | null): boolean;
374
+
375
+ export function localizedStringIsPresent(hash: Record<string, string> | null): boolean;
376
+
260
377
  export class GcrStatistics extends GlossaristModel {
261
378
  readonly totalConcepts: number;
262
379
  readonly conceptsWithDefinitions: number;
@@ -1,5 +1,18 @@
1
1
  export { Register, REGISTER_STATUSES } from './register.js';
2
2
  export { Section, ORDERING_METHODS } from './section.js';
3
+ export { resolveColor, isColorPair, validateColor, COLOR_MODES } from './dataset-color.js';
4
+ export {
5
+ RELATION_CATEGORIES,
6
+ categoryOf,
7
+ categoryDefinition,
8
+ uncategorizedTypes,
9
+ duplicatedTypes,
10
+ } from './relation-categories.js';
11
+ export {
12
+ RELATION_COLOR_DEFAULTS,
13
+ resolveRelationColor,
14
+ categoryColorPair,
15
+ } from './relation-colors.js';
3
16
  export { GlossaristModel } from './base.js';
4
17
  export { Concept } from './concept.js';
5
18
  export { LocalizedConcept } from './localized-concept.js';