glossarist 0.4.40 → 0.4.41

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.
@@ -1,420 +0,0 @@
1
- import { GlossaristModel } from './base.js';
2
- import { LocalizedConcept } from './localized-concept.js';
3
- import { RelatedConcept } from './related-concept.js';
4
- import { PartitiveHyperedge } from './partitive-hyperedge.js';
5
- import { AbstractHyperedge } from './abstract-hyperedge.js';
6
- import { HyperedgeRegistry, groupHyperedgesByWireKey } from './hyperedge-registry.js';
7
- import { migrateHyperedgeToRelation } from '../migration/hyperedge-migrator.js';
8
- import { ConceptReference } from './concept-reference.js';
9
- import { ConceptDate } from './concept-date.js';
10
- import { ConceptSource } from './concept-source.js';
11
- import { FigureReference } from './non-verbal-references.js';
12
- import { TableReference } from './non-verbal-references.js';
13
- import { FormulaReference } from './non-verbal-references.js';
14
- import { diffConcepts } from '../diff/concept-diff.js';
15
-
16
- export class Concept extends GlossaristModel {
17
- // Scalar fields the diff layer tracks at the concept level. Adding
18
- // a new scalar metadata field requires only appending its name here;
19
- // diff detection, patch application, and similarity scoring all pick
20
- // it up automatically. (Invariant N2 — TODO.hyperedges-v2/07.)
21
- static get DIFF_FIELDS() {
22
- return Object.freeze(['status', 'term', 'uri', 'schemaVersion']);
23
- }
24
-
25
- static wireNameFor(field) {
26
- return CONCEPT_WIRE_NAMES[field] ?? field;
27
- }
28
-
29
- constructor(data = {}) {
30
- super();
31
- this.id = String(data.id ?? data.termid ?? '');
32
- this.term = data.term ?? null;
33
- this.uri = data.uri ?? null;
34
- this._rawLocalizations = data.localizations ?? {};
35
- this._cache = {};
36
-
37
- this.relatedConcepts = _mapInstances(data.relatedConcepts ?? data.related ?? data.related_concepts ?? [], RelatedConcept);
38
-
39
- // Unified hyperedge array. Every hyperedge (PartitiveHyperedge,
40
- // GenericHyperedge, future TemporalHyperedge / AssociativeHyperedge /
41
- // SequentialHyperedge) lives here. The single public API is
42
- // `.relations`; external systems that need to filter by type use
43
- // `concept.relations.filter(r => r instanceof X)` or dispatch via
44
- // HyperedgeRegistry (preferred — see Phases 6 and 11).
45
- //
46
- // Throws on ambiguous input (caller passed both `relations` and any
47
- // typed wire key) and on duplicate relations (same identity twice).
48
- // Silent drop / silent dedupe are footguns the audit (C4, C6)
49
- // flagged; explicit throws make corruption loud.
50
- this.relations = _resolveHyperedges(data);
51
-
52
- this.domains = _normalizeDomains(data.domains, data.groups);
53
- this.groups = Array.isArray(data.groups)
54
- ? data.groups.map(g => typeof g === 'string' ? g : (g?.id ?? g?.sectionId ?? null)).filter(Boolean)
55
- : [];
56
- this.sections = Array.isArray(data.sections)
57
- ? data.sections.map(s => typeof s === 'string' ? s : (s?.id ?? s?.sectionId ?? null)).filter(Boolean)
58
- : [];
59
- this.tags = Array.isArray(data.tags) ? [...data.tags] : [];
60
- this.dates = _mapInstances(data.dates ?? [], ConceptDate);
61
- this.sources = _mapInstances(data.sources ?? [], ConceptSource);
62
- this._rawFigures = data.figures ?? [];
63
- this._rawTables = data.tables ?? [];
64
- this._rawFormulas = data.formulas ?? [];
65
- this._figures = null;
66
- this._tables = null;
67
- this._formulas = null;
68
- this.status = data.status ?? null;
69
- this.schemaVersion = data.schemaVersion ?? data.schema_version ?? '3';
70
- this.raw = data.raw ?? null;
71
- }
72
-
73
- get termid() { return this.id; }
74
-
75
- // No typed projections. Use concept.relations directly and filter
76
- // by instanceof, or dispatch via HyperedgeRegistry. The old
77
- // .partitiveRelations / .genericRelations / .partitiveHyperedges
78
- // getters were removed per TODO Phase 4 — they were footguns
79
- // (audit C3: .partitiveHyperedges returned v2 instances but callers
80
- // expected v1 shape; multiple validators silently no-op'd).
81
- //
82
- // Snippet migration guide:
83
- // concept.partitiveRelations → concept.relations.filter(r => r instanceof PartitiveHyperedge)
84
- // concept.genericRelations → concept.relations.filter(r => r instanceof GenericHyperedge)
85
- // concept.partitiveHyperedges → concept.relations.filter(r => r instanceof PartitiveHyperedge)
86
-
87
- get languages() {
88
- return Object.keys(this._rawLocalizations);
89
- }
90
-
91
- get languageCodes() {
92
- return this.languages;
93
- }
94
-
95
- localization(lang) {
96
- if (!(lang in this._rawLocalizations)) return undefined;
97
- if (!this._cache[lang]) {
98
- const raw = this._rawLocalizations[lang];
99
- this._cache[lang] = new LocalizedConcept({ ...raw, language_code: lang });
100
- }
101
- return this._cache[lang];
102
- }
103
-
104
- diff(other, language = 'eng') {
105
- return diffConcepts(this, other, language);
106
- }
107
-
108
- primaryDesignation(lang) {
109
- return this.localization(lang)?.primaryDesignation ?? null;
110
- }
111
-
112
- definition(lang) {
113
- return this.localization(lang)?.primaryDefinition ?? null;
114
- }
115
-
116
- setLocalization(lang, lc) {
117
- if (lc instanceof LocalizedConcept) {
118
- this._cache[lang] = lc;
119
- const json = lc.toJSON();
120
- delete json.language_code;
121
- this._rawLocalizations[lang] = json;
122
- } else {
123
- this._rawLocalizations[lang] = lc;
124
- delete this._cache[lang];
125
- }
126
- return this;
127
- }
128
-
129
- hasLocalization(lang) {
130
- return lang in this._rawLocalizations;
131
- }
132
-
133
- /**
134
- * Find a source by its local id within this concept.
135
- *
136
- * The lookup walks concept-level, localization-level, and
137
- * designation-level sources in that order, returning the first
138
- * source whose `id` matches. Sources without an `id` are
139
- * skipped. The id is local to the concept; uniqueness is
140
- * enforced by the validator (see CiteRefIntegrityRule).
141
- *
142
- * @param {string} id
143
- * @returns {ConceptSource | null}
144
- */
145
- findSourceById(id) {
146
- if (typeof id !== 'string' || id.length === 0) return null;
147
-
148
- // 1. Concept-level sources.
149
- for (const source of this.sources) {
150
- if (source.id === id) return source;
151
- }
152
-
153
- // 2. Localization-level sources.
154
- for (const lang of this.languages) {
155
- const lc = this.localization(lang);
156
- if (!lc) continue;
157
- for (const source of lc.sources) {
158
- if (source.id === id) return source;
159
- }
160
-
161
- // 3. Designation-level sources.
162
- for (const designation of lc.terms) {
163
- for (const source of designation.sources) {
164
- if (source.id === id) return source;
165
- }
166
- }
167
- }
168
-
169
- return null;
170
- }
171
-
172
- get figures() {
173
- return this._lazy('_figures', '_rawFigures',
174
- r => FigureReference.fromJSON(r));
175
- }
176
-
177
- get tables() {
178
- return this._lazy('_tables', '_rawTables',
179
- r => TableReference.fromJSON(r));
180
- }
181
-
182
- get formulas() {
183
- return this._lazy('_formulas', '_rawFormulas',
184
- r => FormulaReference.fromJSON(r));
185
- }
186
-
187
- /**
188
- * Yield every content-text fragment in this concept across all
189
- * localizations, recursing through nested examples. Each yielded
190
- * `{ text, source }` carries a dotted path rooted at
191
- * `localizations.<lang>.<slot>[i]...content`.
192
- *
193
- * Designations are not included; they live on `LocalizedConcept.terms`
194
- * and have a different shape.
195
- */
196
- *walkTexts() {
197
- for (const lang of this.languages) {
198
- const lc = this.localization(lang);
199
- if (!lc) continue;
200
- yield* lc.walkTexts(`localizations.${lang}`);
201
- }
202
- }
203
-
204
- toJSON() {
205
- const obj = { id: this.id };
206
- if (this.term != null) obj.term = this.term;
207
- if (this.uri != null) obj.uri = this.uri;
208
-
209
- if (Object.keys(this._rawLocalizations).length > 0) {
210
- obj.localizations = {};
211
- for (const lang of this.languages) {
212
- const lc = this.localization(lang);
213
- if (lc) {
214
- const json = lc.toJSON();
215
- delete json.language_code;
216
- obj.localizations[lang] = json;
217
- }
218
- }
219
- } else {
220
- obj.localizations = {};
221
- }
222
-
223
- if (this.relatedConcepts.length > 0) {
224
- obj.related = this.relatedConcepts.map(rc => rc.toJSON());
225
- }
226
- // Emit per-type wire keys from the unified relations array,
227
- // partitioned via HyperedgeRegistry. Adding a new hyperedge type
228
- // means its wire key appears here automatically — no edits to
229
- // Concept.toJSON.
230
- const grouped = groupHyperedgesByWireKey(this.relations);
231
- for (const [wireKey, rels] of Object.entries(grouped)) {
232
- if (rels.length > 0) obj[wireKey] = rels.map(r => r.toJSON());
233
- }
234
- if (this.domains.length > 0) {
235
- obj.domains = this.domains.map(d => d.toJSON());
236
- }
237
- if (this.tags.length > 0) {
238
- obj.tags = [...this.tags];
239
- }
240
- if (this.dates.length > 0) {
241
- obj.dates = this.dates.map(d => d.toJSON());
242
- }
243
- if (this.sources.length > 0) {
244
- obj.sources = this.sources.map(s => s.toJSON());
245
- }
246
- this._serialize(obj, 'figures', '_figures', '_rawFigures');
247
- this._serialize(obj, 'tables', '_tables', '_rawTables');
248
- this._serialize(obj, 'formulas', '_formulas', '_rawFormulas');
249
- if (this.status != null) obj.status = this.status;
250
- obj.schema_version = this.schemaVersion;
251
- return obj;
252
- }
253
-
254
- static fromJSON(data) {
255
- return new Concept(data);
256
- }
257
- }
258
-
259
- function _mapInstances(arr, Cls) {
260
- return arr.map(item => item instanceof Cls ? item : new Cls(item));
261
- }
262
-
263
- // Resolve the unified hyperedge array from any input shape.
264
- //
265
- // Three input categories are accepted; mixing them throws (audit C4 —
266
- // silent drop was a footgun):
267
- //
268
- // 1. data.relations — preferred unified shape. Entries
269
- // are typed instances or hashes
270
- // carrying a `type` discriminator
271
- // (looked up via HyperedgeRegistry).
272
- // 2. Per-type v2 wire keys — one of HyperedgeRegistry.allWireKeys()
273
- // (partitive_relations, generic_relations).
274
- // 3. Per-type v1 legacy keys — declared on each class via
275
- // `static v1WireKeys` (e.g.
276
- // partitive_hyperedges → migrated).
277
- //
278
- // Adding a new hyperedge type means: declare the leaf class with its
279
- // metadata block, register it. Concept, parser, serializer, diff,
280
- // patch, renderer, RDF emitter do not change. (Phase 11 OCP contract.)
281
- function _resolveHyperedges(data) {
282
- _assertNoAmbiguousInput(data);
283
-
284
- if (Array.isArray(data.relations)) {
285
- return _dedupeOrThrow(data.relations.map(r => _coerceHyperedge(r)).filter(r => r != null));
286
- }
287
-
288
- const out = [];
289
-
290
- for (const cls of HyperedgeRegistry.allClasses()) {
291
- const arr = data[cls.wireKey] ?? data[_camelCase(cls.wireKey)];
292
- if (!Array.isArray(arr)) continue;
293
- for (const r of arr) {
294
- const coerced = _coerceHyperedge(r, cls);
295
- if (coerced) out.push(coerced);
296
- }
297
- }
298
-
299
- for (const cls of HyperedgeRegistry.allClasses()) {
300
- for (const v1Key of cls.v1WireKeys ?? []) {
301
- const camelV1 = _camelCase(v1Key);
302
- const arr = data[v1Key] ?? data[camelV1];
303
- if (!Array.isArray(arr)) continue;
304
- for (const h of arr) {
305
- const migrated = _migrateV1Hash(cls, v1Key, h);
306
- if (migrated) out.push(new cls(migrated));
307
- }
308
- }
309
- }
310
-
311
- return _dedupeOrThrow(out);
312
- }
313
-
314
- // Audit C4: silent drop on mixed input is a footgun. Throw instead.
315
- // Allows `relations` alone, or any combination of typed keys alone —
316
- // but not both at once.
317
- function _assertNoAmbiguousInput(data) {
318
- if (!Array.isArray(data.relations)) return;
319
- const typedKeys = [];
320
- for (const cls of HyperedgeRegistry.allClasses()) {
321
- if (Array.isArray(data[cls.wireKey])) typedKeys.push(cls.wireKey);
322
- const camelWire = _camelCase(cls.wireKey);
323
- if (camelWire !== cls.wireKey && Array.isArray(data[camelWire])) {
324
- typedKeys.push(camelWire);
325
- }
326
- for (const v1 of cls.v1WireKeys ?? []) {
327
- if (Array.isArray(data[v1])) typedKeys.push(v1);
328
- const camelV1 = _camelCase(v1);
329
- if (camelV1 !== v1 && Array.isArray(data[camelV1])) typedKeys.push(camelV1);
330
- }
331
- }
332
- if (typedKeys.length > 0) {
333
- throw new Error(
334
- `Concept input is ambiguous: pass either 'relations' (unified) ` +
335
- `or typed wire keys (${typedKeys.join(', ')}), not both. ` +
336
- ` Mixing them silently drops the typed keys, which has caused ` +
337
- ` data loss in production (see audit C4).`,
338
- );
339
- }
340
- }
341
-
342
- // Audit C6: do NOT silently dedupe. Duplicate relations indicate a bug
343
- // in the caller; surface it. The identity function delegates to each
344
- // class's static identityOf (polymorphic — handles mixed types).
345
- function _dedupeOrThrow(relations) {
346
- const seen = new Map();
347
- for (const r of relations) {
348
- const id = _hyperedgeIdentity(r);
349
- if (seen.has(id)) {
350
- throw new Error(
351
- `Concept input has duplicate hyperedge (identity=${id}). ` +
352
- `Passing the same relation twice is a bug — the parser and ` +
353
- `serializer would silently emit it twice on disk. ` +
354
- `Audit C6 requires we surface this loudly instead.`,
355
- );
356
- }
357
- seen.set(id, r);
358
- }
359
- return relations;
360
- }
361
-
362
- function _hyperedgeIdentity(r) {
363
- if (r == null) return '';
364
- const Cls = r.constructor;
365
- if (typeof Cls?.identityOf === 'function') {
366
- return `${Cls.name}::${Cls.identityOf(r)}`;
367
- }
368
- return String(r);
369
- }
370
-
371
- function _coerceHyperedge(value, fallbackClass) {
372
- if (value == null) return null;
373
- if (value instanceof AbstractHyperedge) return value;
374
-
375
- const hash = typeof value.toJSON === 'function' ? value.toJSON() : value;
376
- const cls = _classForHash(hash) ?? fallbackClass;
377
- if (!cls) return null;
378
- return new cls(hash);
379
- }
380
-
381
- function _classForHash(hash) {
382
- if (!hash || typeof hash !== 'object') return null;
383
- const type = hash.type;
384
- if (typeof type !== 'string') return null;
385
- return HyperedgeRegistry.forTypeTag(type);
386
- }
387
-
388
- // v1 → v2 migration for hashes from legacy wire keys. Currently only
389
- // PartitiveHyperedge has v1 wire keys (partitive_hyperedges). Future
390
- // types extend this dispatch — but it lives here in the migration
391
- // helper, not in the model classes, since v1 is a legacy concern.
392
- function _migrateV1Hash(cls, v1Key, hash) {
393
- if (cls === PartitiveHyperedge && v1Key === 'partitive_hyperedges') {
394
- const normalized = hash;
395
- return migrateHyperedgeToRelation(normalized);
396
- }
397
- return null;
398
- }
399
-
400
- // snake_case → camelCase
401
- function _camelCase(s) {
402
- if (typeof s !== 'string' || !s.includes('_')) return s;
403
- return s.replace(/_([a-z])/g, (_, c) => c.toUpperCase());
404
- }
405
-
406
- const CONCEPT_WIRE_NAMES = Object.freeze({
407
- schemaVersion: 'schema_version',
408
- });
409
-
410
- function _normalizeDomains(domains, groups) {
411
- if (domains) {
412
- return domains.map(d => d instanceof ConceptReference ? d : new ConceptReference(d));
413
- }
414
- if (groups) {
415
- return groups.map(g => typeof g === 'string'
416
- ? ConceptReference.domain(g)
417
- : new ConceptReference(g));
418
- }
419
- return [];
420
- }
@@ -1,187 +0,0 @@
1
- import { GlossaristModel } from './base.js';
2
- import { Designation } from './designation.js';
3
- import { DetailedDefinition } from './detailed-definition.js';
4
- import { ConceptSource } from './concept-source.js';
5
- import { ConceptDate } from './concept-date.js';
6
- import { NonVerbRep } from './non-verb-rep.js';
7
- import { RelatedConcept } from './related-concept.js';
8
-
9
- function wrapAs(Cls) {
10
- return item => item instanceof Cls ? item : new Cls(item);
11
- }
12
-
13
- const LOC_WIRE_NAMES = Object.freeze({
14
- entryStatus: 'entry_status',
15
- reviewType: 'review_type',
16
- lineageSourceSimilarity: 'lineage_source_similarity',
17
- reviewDate: 'review_date',
18
- reviewDecisionDate: 'review_decision_date',
19
- reviewDecisionEvent: 'review_decision_event',
20
- reviewStatus: 'review_status',
21
- reviewDecision: 'review_decision',
22
- reviewDecisionNotes: 'review_decision_notes',
23
- });
24
-
25
- export class LocalizedConcept extends GlossaristModel {
26
- // Scalar metadata fields the diff layer tracks per localization.
27
- // Adding a new scalar field requires only appending its name here;
28
- // diff/patch/similarity pick it up automatically.
29
- // (Invariant N2 — TODO.hyperedges-v2/07.)
30
- static get DIFF_FIELDS() {
31
- return Object.freeze([
32
- 'entryStatus', 'classification', 'reviewType', 'domain', 'release',
33
- 'lineageSourceSimilarity', 'script', 'system',
34
- 'reviewDate', 'reviewDecisionDate', 'reviewDecisionEvent',
35
- 'reviewStatus', 'reviewDecision', 'reviewDecisionNotes',
36
- ]);
37
- }
38
-
39
- static wireNameFor(field) {
40
- return LOC_WIRE_NAMES[field] ?? field;
41
- }
42
-
43
- constructor(data = {}) {
44
- super();
45
- this.languageCode = data.language_code ?? data.languageCode ?? null;
46
- this.script = data.script ?? null;
47
- this.system = data.system ?? null;
48
- this.entryStatus = data.entry_status ?? data.entryStatus ?? null;
49
- this.classification = data.classification ?? null;
50
- this.reviewType = data.review_type ?? data.reviewType ?? null;
51
- this.domain = data.domain ?? null;
52
- this.release = data.release ?? null;
53
- this.lineageSourceSimilarity = data.lineage_source_similarity ?? data.lineageSourceSimilarity ?? null;
54
-
55
- this.reviewDate = data.review_date ?? data.reviewDate ?? null;
56
- this.reviewDecisionDate = data.review_decision_date ?? data.reviewDecisionDate ?? null;
57
- this.reviewDecisionEvent = data.review_decision_event ?? data.reviewDecisionEvent ?? null;
58
- this.reviewStatus = data.review_status ?? data.reviewStatus ?? null;
59
- this.reviewDecision = data.review_decision ?? data.reviewDecision ?? null;
60
- this.reviewDecisionNotes = data.review_decision_notes ?? data.reviewDecisionNotes ?? null;
61
-
62
- this._rawTerms = data.terms ?? [];
63
- this._rawDefinition = data.definition ?? [];
64
- this._rawSources = data.sources ?? [];
65
- this._rawNotes = data.notes ?? [];
66
- this._rawAnnotations = data.annotations ?? [];
67
- this._rawExamples = data.examples ?? [];
68
- this._rawDates = data.dates ?? [];
69
- this._rawNonVerbal = data.non_verbal_rep ?? data.non_verb ?? [];
70
- this._rawRelated = data.related ?? [];
71
-
72
- this._terms = null;
73
- this._definitions = null;
74
- this._sources = null;
75
- this._notes = null;
76
- this._annotations = null;
77
- this._examples = null;
78
- this._dates = null;
79
- this._nonVerbal = null;
80
- this._related = null;
81
- }
82
-
83
- get terms() {
84
- return this._lazy('_terms', '_rawTerms', t => Designation.fromData(t));
85
- }
86
-
87
- get definitions() {
88
- return this._lazy('_definitions', '_rawDefinition', wrapAs(DetailedDefinition));
89
- }
90
-
91
- get definition() {
92
- return this.definitions;
93
- }
94
-
95
- get sources() {
96
- return this._lazy('_sources', '_rawSources', wrapAs(ConceptSource));
97
- }
98
-
99
- get notes() {
100
- return this._lazy('_notes', '_rawNotes', wrapAs(DetailedDefinition));
101
- }
102
-
103
- get annotations() {
104
- return this._lazy('_annotations', '_rawAnnotations', wrapAs(DetailedDefinition));
105
- }
106
-
107
- get examples() {
108
- return this._lazy('_examples', '_rawExamples', wrapAs(DetailedDefinition));
109
- }
110
-
111
- get dates() {
112
- return this._lazy('_dates', '_rawDates', wrapAs(ConceptDate));
113
- }
114
-
115
- get nonVerbalRep() {
116
- return this._lazy('_nonVerbal', '_rawNonVerbal', wrapAs(NonVerbRep));
117
- }
118
-
119
- get related() {
120
- return this._lazy('_related', '_rawRelated', wrapAs(RelatedConcept));
121
- }
122
-
123
- get primaryDesignation() {
124
- return this.terms[0]?.designation ?? null;
125
- }
126
-
127
- get primaryDefinition() {
128
- return this.definitions[0]?.content ?? null;
129
- }
130
-
131
- *_textSlots() {
132
- yield ['definitions', this.definitions];
133
- yield ['notes', this.notes];
134
- yield ['examples', this.examples];
135
- yield ['annotations', this.annotations];
136
- }
137
-
138
- /**
139
- * Yield every content-text fragment in this localization, recursing
140
- * through nested examples. `basePath` prefixes every emitted
141
- * `source` path; pass `localizations.<lang>` to get paths consistent
142
- * with the rest of the codebase. Designations are not included.
143
- */
144
- *walkTexts(basePath) {
145
- for (const [name, arr] of this._textSlots()) {
146
- for (let i = 0; i < arr.length; i++) {
147
- yield* arr[i].walkTexts(`${basePath}.${name}[${i}]`);
148
- }
149
- }
150
- }
151
-
152
- toJSON() {
153
- const obj = {};
154
- if (this.languageCode) obj.language_code = this.languageCode;
155
- if (this.script) obj.script = this.script;
156
- if (this.system) obj.system = this.system;
157
- if (this.entryStatus) obj.entry_status = this.entryStatus;
158
- if (this.classification) obj.classification = this.classification;
159
- if (this.reviewType) obj.review_type = this.reviewType;
160
- if (this.domain) obj.domain = this.domain;
161
- if (this.release) obj.release = this.release;
162
- if (this.lineageSourceSimilarity != null) obj.lineage_source_similarity = this.lineageSourceSimilarity;
163
-
164
- if (this.reviewDate) obj.review_date = this.reviewDate;
165
- if (this.reviewDecisionDate) obj.review_decision_date = this.reviewDecisionDate;
166
- if (this.reviewDecisionEvent) obj.review_decision_event = this.reviewDecisionEvent;
167
- if (this.reviewStatus) obj.review_status = this.reviewStatus;
168
- if (this.reviewDecision) obj.review_decision = this.reviewDecision;
169
- if (this.reviewDecisionNotes) obj.review_decision_notes = this.reviewDecisionNotes;
170
-
171
- this._serialize(obj, 'terms', '_terms', '_rawTerms');
172
- this._serialize(obj, 'definition', '_definitions', '_rawDefinition');
173
- this._serialize(obj, 'notes', '_notes', '_rawNotes');
174
- this._serialize(obj, 'annotations', '_annotations', '_rawAnnotations');
175
- this._serialize(obj, 'examples', '_examples', '_rawExamples');
176
- this._serialize(obj, 'sources', '_sources', '_rawSources');
177
- this._serialize(obj, 'dates', '_dates', '_rawDates');
178
- this._serialize(obj, 'non_verbal_rep', '_nonVerbal', '_rawNonVerbal');
179
- this._serialize(obj, 'related', '_related', '_rawRelated');
180
-
181
- return obj;
182
- }
183
-
184
- static fromJSON(data) {
185
- return new LocalizedConcept(data);
186
- }
187
- }