glossarist 0.4.35 → 0.4.37
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 +15 -6
- package/src/models/abstract-hyperedge.ts +164 -0
- package/src/models/base.ts +93 -0
- package/src/models/{completeness.js → completeness.ts} +3 -6
- package/src/models/concept-date.ts +48 -0
- package/src/models/concept-ref.ts +64 -0
- package/src/models/concept-source.ts +96 -0
- package/src/models/concept-system-type.ts +39 -0
- package/src/models/concept-system.ts +152 -0
- package/src/models/concept-type.ts +33 -0
- package/src/models/{dataset-color.js → dataset-color.ts} +26 -17
- package/src/models/{definition-type.js → definition-type.ts} +8 -1
- package/src/models/enum.ts +60 -0
- package/src/models/equivalence-degree.ts +34 -0
- package/src/models/generic-hyperedge.ts +72 -0
- package/src/models/generic-member.ts +80 -0
- package/src/models/{hyperedge-index.js → hyperedge-index.ts} +32 -18
- package/src/models/{hyperedge-member.js → hyperedge-member.ts} +38 -26
- package/src/models/{hyperedge-registry.js → hyperedge-registry.ts} +59 -29
- package/src/models/{multiplicity.js → multiplicity.ts} +58 -29
- package/src/models/{partitive-count.js → partitive-count.ts} +3 -1
- package/src/models/partitive-enumeration.ts +27 -0
- package/src/models/partitive-hyperedge.ts +90 -0
- package/src/models/{partitive-member.js → partitive-member.ts} +18 -17
- package/src/models/{partitive-presence.js → partitive-presence.ts} +3 -1
- package/src/models/{plurality-marker.js → plurality-marker.ts} +14 -7
- package/src/models/sequential-hyperedge.ts +70 -0
- package/src/models/{sequential-member.js → sequential-member.ts} +6 -3
- package/src/models/abstract-hyperedge.js +0 -119
- package/src/models/base.js +0 -32
- package/src/models/concept-date.js +0 -36
- package/src/models/concept-ref.js +0 -36
- package/src/models/concept-source.js +0 -52
- package/src/models/concept-system-type.js +0 -26
- package/src/models/concept-system.js +0 -117
- package/src/models/concept-type.js +0 -25
- package/src/models/enum.js +0 -42
- package/src/models/equivalence-degree.js +0 -25
- package/src/models/generic-hyperedge.js +0 -55
- package/src/models/generic-member.js +0 -82
- package/src/models/partitive-enumeration.js +0 -23
- package/src/models/partitive-hyperedge.js +0 -91
- package/src/models/sequential-hyperedge.js +0 -56
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
// SequentialHyperedge — an ISO 12620 A.6.3 / ISO 704:2022 §5.5.5
|
|
2
|
+
// sequential relation connecting a comprehensive concept to two or
|
|
3
|
+
// more subordinate concepts in an ORDERED sequence (temporal,
|
|
4
|
+
// spatial, causal, developmental).
|
|
5
|
+
//
|
|
6
|
+
// Mirror of PartitiveHyperedge and GenericHyperedge. Member array
|
|
7
|
+
// order IS significant — reversing the array reverses the sequence.
|
|
8
|
+
// Use cases: manufacturing process steps, biological life cycles,
|
|
9
|
+
// software release lifecycles, supply chain stages, geological eras.
|
|
10
|
+
|
|
11
|
+
import { AbstractHyperedge } from './abstract-hyperedge.js';
|
|
12
|
+
import type { AbstractHyperedgeJson } from './abstract-hyperedge.js';
|
|
13
|
+
import { SequentialMember } from './sequential-member.js';
|
|
14
|
+
import type { SequentialMemberJson } from './sequential-member.js';
|
|
15
|
+
import { HyperedgeRegistry } from './hyperedge-registry.js';
|
|
16
|
+
|
|
17
|
+
export interface SequentialHyperedgeJson extends AbstractHyperedgeJson {
|
|
18
|
+
members: ReadonlyArray<SequentialMemberJson | SequentialMember>;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export class SequentialHyperedge extends AbstractHyperedge {
|
|
22
|
+
// Per-class metadata block — the OCP contract.
|
|
23
|
+
static wireKey = 'sequential_relations' as const;
|
|
24
|
+
static typeTag = 'sequential_relation' as const;
|
|
25
|
+
static rdfType = 'gloss:SequentialRelation' as const;
|
|
26
|
+
static memberClass = SequentialMember;
|
|
27
|
+
static v1WireKeys: ReadonlyArray<string> = [];
|
|
28
|
+
static kindLabel = 'SEQ';
|
|
29
|
+
|
|
30
|
+
declare readonly members: ReadonlyArray<SequentialMember>;
|
|
31
|
+
|
|
32
|
+
constructor(data: Partial<SequentialHyperedgeJson> = {}) {
|
|
33
|
+
super({
|
|
34
|
+
comprehensive: data.comprehensive ?? {},
|
|
35
|
+
members: data.members ?? [],
|
|
36
|
+
completeness: data.completeness ?? 'complete',
|
|
37
|
+
criterion: data.criterion,
|
|
38
|
+
sources: data.sources,
|
|
39
|
+
notes: data.notes,
|
|
40
|
+
status: data.status,
|
|
41
|
+
});
|
|
42
|
+
this.members = (Array.isArray(data.members) ? data.members : []).map((m) =>
|
|
43
|
+
m instanceof SequentialMember ? m : new SequentialMember(m as SequentialMemberJson),
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
static identityOf(
|
|
48
|
+
value: {
|
|
49
|
+
comprehensive?: { source?: string | null; id?: string | null } | null;
|
|
50
|
+
members?: ReadonlyArray<{ ref?: { source?: string | null; id?: string | null } | null } | null>;
|
|
51
|
+
} | null | undefined,
|
|
52
|
+
): string {
|
|
53
|
+
const v = value ?? {};
|
|
54
|
+
const c = v.comprehensive ?? {};
|
|
55
|
+
// For sequential, member order IS significant — do NOT sort.
|
|
56
|
+
const memberKeys = (v.members ?? []).map((m) => {
|
|
57
|
+
const r = m?.ref ?? (m as { source?: string; id?: string } | null) ?? {};
|
|
58
|
+
return `${r.source ?? ''}:${r.id ?? ''}`;
|
|
59
|
+
});
|
|
60
|
+
return `${c.source ?? ''}:${c.id ?? ''}|${memberKeys.join('|')}`;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
static override fromJSON(data: SequentialHyperedgeJson): SequentialHyperedge {
|
|
64
|
+
return new SequentialHyperedge(data);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export default SequentialHyperedge;
|
|
69
|
+
|
|
70
|
+
HyperedgeRegistry.register(SequentialHyperedge);
|
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
// SequentialMember — one member of a SequentialHyperedge. Inherits
|
|
2
2
|
// the MECE shape from HyperedgeMember. Distinct leaf class for type
|
|
3
3
|
// safety. Member array order IS significant for sequential hyperedges.
|
|
4
|
-
//
|
|
5
|
-
// See docs/design/abstract-nary-relation.md (concept-model repo).
|
|
6
4
|
|
|
7
5
|
import { HyperedgeMember } from './hyperedge-member.js';
|
|
6
|
+
import type { HyperedgeMemberJson } from './hyperedge-member.js';
|
|
7
|
+
|
|
8
|
+
export type SequentialMemberJson = HyperedgeMemberJson;
|
|
8
9
|
|
|
9
10
|
export class SequentialMember extends HyperedgeMember {
|
|
10
|
-
|
|
11
|
+
static override fromJSON(data: SequentialMemberJson): SequentialMember {
|
|
12
|
+
return new SequentialMember(data);
|
|
13
|
+
}
|
|
11
14
|
}
|
|
12
15
|
|
|
13
16
|
export default SequentialMember;
|
|
@@ -1,119 +0,0 @@
|
|
|
1
|
-
// AbstractHyperedge — shared base for directed 1→N hyperedges
|
|
2
|
-
// relations (PartitiveHyperedge, GenericHyperedge, future
|
|
3
|
-
// SequentialRelation, AssociativeRelation).
|
|
4
|
-
//
|
|
5
|
-
// Carries the common shape:
|
|
6
|
-
// - comprehensive: ConceptRef
|
|
7
|
-
// - members: 2+ MECE dimensioned concept refs (≥2 per ISO 704)
|
|
8
|
-
// - completeness: complete | partial
|
|
9
|
-
// - criterion: localized subdivision criterion (ISO 12620)
|
|
10
|
-
// - sources / notes / status: administrative metadata
|
|
11
|
-
//
|
|
12
|
-
// Concrete leaves add type-specific fields if needed (currently none).
|
|
13
|
-
// See docs/design/abstract-hyperedge.md (concept-model repo).
|
|
14
|
-
|
|
15
|
-
import { GlossaristModel } from './base.js';
|
|
16
|
-
import { ConceptRef } from './concept-ref.js';
|
|
17
|
-
import {
|
|
18
|
-
COMPLETENESS,
|
|
19
|
-
DEFAULT_COMPLETENESS,
|
|
20
|
-
isValidCompleteness,
|
|
21
|
-
} from './completeness.js';
|
|
22
|
-
|
|
23
|
-
export class AbstractHyperedge extends GlossaristModel {
|
|
24
|
-
constructor(data = {}) {
|
|
25
|
-
super();
|
|
26
|
-
this.comprehensive = _ensureComprehensive(data.comprehensive);
|
|
27
|
-
this.members = _ensureMembers(data.members, this.constructor.name);
|
|
28
|
-
this.completeness = _resolveCompleteness(data.completeness);
|
|
29
|
-
this.criterion = _normalizeCriterion(data.criterion);
|
|
30
|
-
this.sources = Array.isArray(data.sources) ? [...data.sources] : [];
|
|
31
|
-
this.notes = data.notes ?? null;
|
|
32
|
-
this.status = data.status ?? null;
|
|
33
|
-
|
|
34
|
-
_assertNoSelfLoop(this.comprehensive, this.members);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
get isComplete() { return this.completeness === COMPLETENESS.COMPLETE; }
|
|
38
|
-
get isPartial() { return this.completeness === COMPLETENESS.PARTIAL; }
|
|
39
|
-
get isCoordinate() { return this.members.length >= 2; }
|
|
40
|
-
|
|
41
|
-
hasCriterion() { return this.criterion != null; }
|
|
42
|
-
|
|
43
|
-
identity() {
|
|
44
|
-
return this.constructor.identityOf(this);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
toJSON() {
|
|
48
|
-
const obj = {
|
|
49
|
-
comprehensive: this.comprehensive.toJSON(),
|
|
50
|
-
members: this.members.map(m => m.toJSON()),
|
|
51
|
-
completeness: this.completeness,
|
|
52
|
-
};
|
|
53
|
-
if (this.criterion != null) obj.criterion = { ...this.criterion };
|
|
54
|
-
if (this.sources.length > 0) obj.sources = [...this.sources];
|
|
55
|
-
if (this.notes != null) obj.notes = { ...this.notes };
|
|
56
|
-
if (this.status != null) obj.status = this.status;
|
|
57
|
-
return obj;
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
function _ensureComprehensive(value) {
|
|
62
|
-
const ref = value instanceof ConceptRef ? value : new ConceptRef(value ?? {});
|
|
63
|
-
if (!ref.source && !ref.id && !ref.text) {
|
|
64
|
-
throw new Error(
|
|
65
|
-
'AbstractHyperedge.comprehensive must be a non-empty ConceptReference ' +
|
|
66
|
-
'(source, id, or text required)',
|
|
67
|
-
);
|
|
68
|
-
}
|
|
69
|
-
return ref;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
function _ensureMembers(values, typeName) {
|
|
73
|
-
if (!Array.isArray(values)) {
|
|
74
|
-
throw new Error(
|
|
75
|
-
`${typeName}.members must be an array (got ${typeof values})`,
|
|
76
|
-
);
|
|
77
|
-
}
|
|
78
|
-
if (values.length < 2) {
|
|
79
|
-
throw new Error(
|
|
80
|
-
`${typeName} requires ≥2 partitives (ISO 704); got ${values.length}`,
|
|
81
|
-
);
|
|
82
|
-
}
|
|
83
|
-
return values;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
function _resolveCompleteness(value) {
|
|
87
|
-
const c = value ?? DEFAULT_COMPLETENESS;
|
|
88
|
-
if (!isValidCompleteness(c)) {
|
|
89
|
-
throw new Error(
|
|
90
|
-
`${c === value ? 'completeness' : 'completeness'} has invalid value ${JSON.stringify(value)}`,
|
|
91
|
-
);
|
|
92
|
-
}
|
|
93
|
-
return c;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
function _normalizeCriterion(value) {
|
|
97
|
-
if (value == null) return null;
|
|
98
|
-
if (typeof value === 'string') {
|
|
99
|
-
return value.length > 0 ? { default: value } : null;
|
|
100
|
-
}
|
|
101
|
-
if (typeof value === 'object') {
|
|
102
|
-
const entries = Object.entries(value).filter(([, v]) => typeof v === 'string');
|
|
103
|
-
return entries.length > 0 ? Object.fromEntries(entries) : null;
|
|
104
|
-
}
|
|
105
|
-
return null;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
function _assertNoSelfLoop(comprehensive, members) {
|
|
109
|
-
const cKey = `${comprehensive.source ?? ''}:${comprehensive.id ?? ''}`;
|
|
110
|
-
for (const m of members) {
|
|
111
|
-
const ref = m.ref;
|
|
112
|
-
const pKey = `${ref?.source ?? ''}:${ref?.id ?? ''}`;
|
|
113
|
-
if (pKey === cKey && (ref?.source || ref?.id)) {
|
|
114
|
-
throw new Error(
|
|
115
|
-
'Relation.members cannot include the comprehensive (no self-loops)',
|
|
116
|
-
);
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
}
|
package/src/models/base.js
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
export class GlossaristModel {
|
|
2
|
-
toJSON() {
|
|
3
|
-
throw new Error(`${this.constructor.name} must implement toJSON()`);
|
|
4
|
-
}
|
|
5
|
-
|
|
6
|
-
static fromJSON() {
|
|
7
|
-
throw new Error(`${this.name} must implement fromJSON()`);
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
equals(other) {
|
|
11
|
-
if (!(other instanceof this.constructor)) return false;
|
|
12
|
-
return JSON.stringify(this.toJSON()) === JSON.stringify(other.toJSON());
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
clone() {
|
|
16
|
-
return this.constructor.fromJSON(JSON.parse(JSON.stringify(this.toJSON())));
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
_lazy(cacheKey, rawKey, wrapFn) {
|
|
20
|
-
if (this[cacheKey] === null) {
|
|
21
|
-
this[cacheKey] = this[rawKey].map(wrapFn);
|
|
22
|
-
}
|
|
23
|
-
return this[cacheKey];
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
_serialize(obj, jsonKey, cacheKey, rawKey) {
|
|
27
|
-
const items = this[cacheKey] ?? (this[rawKey].length > 0 ? this[rawKey] : []);
|
|
28
|
-
if (items.length > 0) {
|
|
29
|
-
obj[jsonKey] = items.map(i => (i instanceof GlossaristModel) ? i.toJSON() : i);
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
}
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
import { GlossaristModel } from './base.js';
|
|
2
|
-
|
|
3
|
-
export const DATE_TYPES = Object.freeze([
|
|
4
|
-
'accepted', 'amended', 'retired',
|
|
5
|
-
]);
|
|
6
|
-
|
|
7
|
-
export class ConceptDate extends GlossaristModel {
|
|
8
|
-
constructor(data = {}) {
|
|
9
|
-
super();
|
|
10
|
-
this.date = data.date ?? null;
|
|
11
|
-
this.type = data.type ?? null;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
get parsedDate() {
|
|
15
|
-
return this.date ? new Date(this.date) : null;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
static identityOf(value) {
|
|
19
|
-
return value?.type ?? '';
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
identity() {
|
|
23
|
-
return ConceptDate.identityOf(this);
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
toJSON() {
|
|
27
|
-
const obj = {};
|
|
28
|
-
if (this.date != null) obj.date = this.date;
|
|
29
|
-
if (this.type != null) obj.type = this.type;
|
|
30
|
-
return obj;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
static fromJSON(data) {
|
|
34
|
-
return new ConceptDate(data);
|
|
35
|
-
}
|
|
36
|
-
}
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
import { GlossaristModel } from './base.js';
|
|
2
|
-
|
|
3
|
-
export class ConceptRef extends GlossaristModel {
|
|
4
|
-
constructor(data = {}) {
|
|
5
|
-
super();
|
|
6
|
-
this.source = data.source ?? null;
|
|
7
|
-
this.id = data.id ?? null;
|
|
8
|
-
this.text = data.text ?? null;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
toString() {
|
|
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;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
toJSON() {
|
|
22
|
-
const obj = {};
|
|
23
|
-
if (this.source != null) obj.source = this.source;
|
|
24
|
-
if (this.id != null) obj.id = this.id;
|
|
25
|
-
if (this.text != null) obj.text = this.text;
|
|
26
|
-
return obj;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
identity() {
|
|
30
|
-
return `${this.source ?? ''}|${this.id ?? ''}`;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
static fromJSON(data) {
|
|
34
|
-
return new ConceptRef(data);
|
|
35
|
-
}
|
|
36
|
-
}
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
import { GlossaristModel } from './base.js';
|
|
2
|
-
import { Citation } from './citation.js';
|
|
3
|
-
|
|
4
|
-
export class ConceptSource extends GlossaristModel {
|
|
5
|
-
constructor(data = {}) {
|
|
6
|
-
super();
|
|
7
|
-
this.id = data.id ?? null;
|
|
8
|
-
this.status = data.status ?? null;
|
|
9
|
-
this.type = data.type ?? null;
|
|
10
|
-
this.origin = data.origin
|
|
11
|
-
? (data.origin instanceof Citation ? data.origin : new Citation(data.origin))
|
|
12
|
-
: null;
|
|
13
|
-
this.modification = data.modification ?? null;
|
|
14
|
-
this.sourcedFrom = (data.sourcedFrom ?? data.sourced_from ?? []).map(
|
|
15
|
-
c => c instanceof Citation ? c : new Citation(c)
|
|
16
|
-
);
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
// Backward-compat alias for callers using the snake_case JS field name.
|
|
20
|
-
// Prefer `.sourcedFrom` going forward; the wire name remains
|
|
21
|
-
// `sourced_from` (see toJSON).
|
|
22
|
-
get sourced_from() {
|
|
23
|
-
return this.sourcedFrom;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
toJSON() {
|
|
27
|
-
const obj = {};
|
|
28
|
-
if (this.id != null) obj.id = this.id;
|
|
29
|
-
if (this.status != null) obj.status = this.status;
|
|
30
|
-
if (this.type != null) obj.type = this.type;
|
|
31
|
-
if (this.origin != null) obj.origin = this.origin.toJSON();
|
|
32
|
-
if (this.modification != null) obj.modification = this.modification;
|
|
33
|
-
if (this.sourcedFrom.length > 0) {
|
|
34
|
-
obj.sourced_from = this.sourcedFrom.map(c => c.toJSON());
|
|
35
|
-
}
|
|
36
|
-
return obj;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
static identityOf(value) {
|
|
40
|
-
const v = value ?? {};
|
|
41
|
-
const ref = v?.origin?.ref ?? (v?.origin instanceof Citation ? v.origin.ref : null);
|
|
42
|
-
return `${v.type ?? ''}|${ref?.source ?? ''}|${ref?.id ?? ''}`;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
identity() {
|
|
46
|
-
return ConceptSource.identityOf(this);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
static fromJSON(data) {
|
|
50
|
-
return new ConceptSource(data);
|
|
51
|
-
}
|
|
52
|
-
}
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
// ConceptSystemType — ISO 12620 A.7.1 concept system type enum.
|
|
2
|
-
//
|
|
3
|
-
// Permissible instances per ISO 12620:
|
|
4
|
-
// generic — species are part of the extension of the genus
|
|
5
|
-
// partitive — based on whole-part relations
|
|
6
|
-
// sequential — based on spatial and temporal relations
|
|
7
|
-
// associative — based on thematic or pragmatic relations
|
|
8
|
-
// mixed — two or more of the above (the realistic case)
|
|
9
|
-
|
|
10
|
-
export const CONCEPT_SYSTEM_TYPE = Object.freeze({
|
|
11
|
-
GENERIC: 'generic',
|
|
12
|
-
PARTITIVE: 'partitive',
|
|
13
|
-
SEQUENTIAL: 'sequential',
|
|
14
|
-
ASSOCIATIVE: 'associative',
|
|
15
|
-
MIXED: 'mixed',
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
export const CONCEPT_SYSTEM_TYPE_VALUES = Object.freeze(
|
|
19
|
-
Object.values(CONCEPT_SYSTEM_TYPE),
|
|
20
|
-
);
|
|
21
|
-
|
|
22
|
-
export function isValidConceptSystemType(value) {
|
|
23
|
-
return CONCEPT_SYSTEM_TYPE_VALUES.includes(value);
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export default CONCEPT_SYSTEM_TYPE;
|
|
@@ -1,117 +0,0 @@
|
|
|
1
|
-
// ConceptSystem — first-class concept system entity per ISO 12620 A.7.
|
|
2
|
-
//
|
|
3
|
-
// Promotes the previously-implicit concept system (domains + tags +
|
|
4
|
-
// relation graph) to an explicit first-class entity. Per-file at
|
|
5
|
-
// `concept-systems/<id>.yaml`.
|
|
6
|
-
//
|
|
7
|
-
// See docs/design/concept-systems.md (concept-model repo).
|
|
8
|
-
|
|
9
|
-
import { GlossaristModel } from './base.js';
|
|
10
|
-
import { ConceptRef } from './concept-ref.js';
|
|
11
|
-
import {
|
|
12
|
-
CONCEPT_SYSTEM_TYPE,
|
|
13
|
-
CONCEPT_SYSTEM_TYPE_VALUES,
|
|
14
|
-
isValidConceptSystemType,
|
|
15
|
-
} from './concept-system-type.js';
|
|
16
|
-
|
|
17
|
-
export class ConceptSystem extends GlossaristModel {
|
|
18
|
-
constructor(data = {}) {
|
|
19
|
-
super();
|
|
20
|
-
this.id = _ensureNonEmptyString(data.id, 'id');
|
|
21
|
-
this.name = _ensureName(data.name);
|
|
22
|
-
this.type = _resolveType(data.type);
|
|
23
|
-
this.domain = data.domain ?? null;
|
|
24
|
-
this.purpose = data.purpose ?? null;
|
|
25
|
-
this.members = _ensureMembers(data.members);
|
|
26
|
-
this.hyperedges = Array.isArray(data.hyperedges) ? [...data.hyperedges] : [];
|
|
27
|
-
this.rootConcepts = Array.isArray(data.rootConcepts)
|
|
28
|
-
? data.rootConcepts.map(c => c instanceof ConceptRef ? c : new ConceptRef(c))
|
|
29
|
-
: [];
|
|
30
|
-
this.sources = Array.isArray(data.sources) ? [...data.sources] : [];
|
|
31
|
-
this.status = data.status ?? null;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
get isGeneric() { return this.type === CONCEPT_SYSTEM_TYPE.GENERIC; }
|
|
35
|
-
get isPartitive() { return this.type === CONCEPT_SYSTEM_TYPE.PARTITIVE; }
|
|
36
|
-
get isSequential() { return this.type === CONCEPT_SYSTEM_TYPE.SEQUENTIAL; }
|
|
37
|
-
get isAssociative() { return this.type === CONCEPT_SYSTEM_TYPE.ASSOCIATIVE; }
|
|
38
|
-
get isMixed() { return this.type === CONCEPT_SYSTEM_TYPE.MIXED; }
|
|
39
|
-
|
|
40
|
-
hasHyperedge(edgeId) {
|
|
41
|
-
return this.hyperedges.includes(edgeId);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
hasMember(refOrUri) {
|
|
45
|
-
const uri = typeof refOrUri === 'string' ? refOrUri : _refUri(refOrUri);
|
|
46
|
-
return this.members.some(m => _refUri(m) === uri);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
identity() {
|
|
50
|
-
return this.id;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
toJSON() {
|
|
54
|
-
const obj = {
|
|
55
|
-
id: this.id,
|
|
56
|
-
name: { ...this.name },
|
|
57
|
-
type: this.type,
|
|
58
|
-
members: this.members.map(m => m.toJSON()),
|
|
59
|
-
};
|
|
60
|
-
if (this.domain != null) obj.domain = this.domain;
|
|
61
|
-
if (this.purpose != null) obj.purpose = { ...this.purpose };
|
|
62
|
-
if (this.hyperedges.length > 0) obj.hyperedges = [...this.hyperedges];
|
|
63
|
-
if (this.rootConcepts.length > 0) obj.rootConcepts = this.rootConcepts.map(c => c.toJSON());
|
|
64
|
-
if (this.sources.length > 0) obj.sources = [...this.sources];
|
|
65
|
-
if (this.status != null) obj.status = this.status;
|
|
66
|
-
return obj;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
static fromJSON(data) {
|
|
70
|
-
return new ConceptSystem(data);
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
function _ensureNonEmptyString(value, fieldName) {
|
|
75
|
-
if (typeof value !== 'string' || value.length === 0) {
|
|
76
|
-
throw new Error(`ConceptSystem#${fieldName} must be a non-empty string`);
|
|
77
|
-
}
|
|
78
|
-
return value;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
function _ensureName(value) {
|
|
82
|
-
if (value == null) return {};
|
|
83
|
-
if (typeof value !== 'object' || Array.isArray(value)) {
|
|
84
|
-
throw new Error('ConceptSystem#name must be a localized object (e.g. { eng: "..." })');
|
|
85
|
-
}
|
|
86
|
-
return { ...value };
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
function _resolveType(value) {
|
|
90
|
-
if (value == null) {
|
|
91
|
-
throw new Error(
|
|
92
|
-
'ConceptSystem#type is required; one of ' +
|
|
93
|
-
CONCEPT_SYSTEM_TYPE_VALUES.join(', '),
|
|
94
|
-
);
|
|
95
|
-
}
|
|
96
|
-
if (!isValidConceptSystemType(value)) {
|
|
97
|
-
throw new Error(
|
|
98
|
-
`ConceptSystem#type ${JSON.stringify(value)} invalid; ` +
|
|
99
|
-
`must be one of: ${CONCEPT_SYSTEM_TYPE_VALUES.join(', ')}`,
|
|
100
|
-
);
|
|
101
|
-
}
|
|
102
|
-
return value;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
function _ensureMembers(values) {
|
|
106
|
-
if (!Array.isArray(values) || values.length === 0) {
|
|
107
|
-
throw new Error('ConceptSystem#members must be a non-empty array');
|
|
108
|
-
}
|
|
109
|
-
return values.map(v => v instanceof ConceptRef ? v : new ConceptRef(v));
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
function _refUri(ref) {
|
|
113
|
-
const r = ref?.ref ?? ref ?? {};
|
|
114
|
-
return `${r.source ?? ''}:${r.id ?? ''}`;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
export default ConceptSystem;
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
// ConceptType — ISO 704:2022 §5.2-5.3 general vs individual concept.
|
|
2
|
-
//
|
|
3
|
-
// general — concept corresponds to a category of objects
|
|
4
|
-
// (designated by terms or symbols)
|
|
5
|
-
// individual — concept corresponds to a unique object
|
|
6
|
-
// (designated by proper names / appellations)
|
|
7
|
-
//
|
|
8
|
-
// Individual concepts occupy the bottom rung of a concept ladder
|
|
9
|
-
// and cannot be subdivided into more specific concepts, but CAN be
|
|
10
|
-
// subdivided into parts via a partitive relation.
|
|
11
|
-
|
|
12
|
-
export const CONCEPT_TYPE = Object.freeze({
|
|
13
|
-
GENERAL: 'general',
|
|
14
|
-
INDIVIDUAL: 'individual',
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
export const CONCEPT_TYPE_VALUES = Object.freeze(Object.values(CONCEPT_TYPE));
|
|
18
|
-
|
|
19
|
-
export const DEFAULT_CONCEPT_TYPE = CONCEPT_TYPE.GENERAL;
|
|
20
|
-
|
|
21
|
-
export function isValidConceptType(value) {
|
|
22
|
-
return CONCEPT_TYPE_VALUES.includes(value);
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export default CONCEPT_TYPE;
|
package/src/models/enum.js
DELETED
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
// Enum factory — produces a frozen KEY/VALUES bundle, a frozen values
|
|
2
|
-
// array, and an isValid* predicate from a single `makeEnum` call.
|
|
3
|
-
//
|
|
4
|
-
// Adding a new enum means one line:
|
|
5
|
-
//
|
|
6
|
-
// export const COLOR = makeEnum('COLOR', { RED: 'red', BLUE: 'blue' });
|
|
7
|
-
//
|
|
8
|
-
// The factory returns:
|
|
9
|
-
// {
|
|
10
|
-
// CLOSED: 'closed', // exposed by destructuring
|
|
11
|
-
// OPEN: 'open',
|
|
12
|
-
// VALUES: ['closed', 'open'], // frozen array
|
|
13
|
-
// isValid: (v) => boolean, // predicate
|
|
14
|
-
// }
|
|
15
|
-
//
|
|
16
|
-
// We attach the bundle via Object.assign on a frozen object so the
|
|
17
|
-
// bundle is itself frozen and exposes both the per-value constants
|
|
18
|
-
// (CLOSED, OPEN, ...) and the VALUES list + isValid predicate.
|
|
19
|
-
|
|
20
|
-
export function makeEnum(name, values) {
|
|
21
|
-
if (typeof name !== 'string') {
|
|
22
|
-
throw new Error(`makeEnum: name must be a string, got ${typeof name}`);
|
|
23
|
-
}
|
|
24
|
-
if (typeof values !== 'object' || values === null) {
|
|
25
|
-
throw new Error(`makeEnum: values must be an object, got ${typeof values}`);
|
|
26
|
-
}
|
|
27
|
-
for (const [k, v] of Object.entries(values)) {
|
|
28
|
-
if (typeof v !== 'string') {
|
|
29
|
-
throw new Error(`makeEnum(${name}): value for ${k} must be a string, got ${typeof v}`);
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
const frozen = Object.freeze({ ...values });
|
|
34
|
-
const valuesArr = Object.freeze(Object.values(frozen));
|
|
35
|
-
const isValid = (v) => valuesArr.includes(v);
|
|
36
|
-
|
|
37
|
-
return Object.assign(Object.create(null), frozen, {
|
|
38
|
-
name,
|
|
39
|
-
VALUES: valuesArr,
|
|
40
|
-
isValid,
|
|
41
|
-
});
|
|
42
|
-
}
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
// EquivalenceDegree — ISO 704:2022 §7.7.3 cross-language equivalence.
|
|
2
|
-
//
|
|
3
|
-
// full — equivalent intension across languages (default)
|
|
4
|
-
// partial — overlapping but not identical intension
|
|
5
|
-
// none — no corresponding concept in this language
|
|
6
|
-
// directional — monodirectional equivalence (see equivalence_note)
|
|
7
|
-
|
|
8
|
-
export const EQUIVALENCE_DEGREE = Object.freeze({
|
|
9
|
-
FULL: 'full',
|
|
10
|
-
PARTIAL: 'partial',
|
|
11
|
-
NONE: 'none',
|
|
12
|
-
DIRECTIONAL: 'directional',
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
export const EQUIVALENCE_DEGREE_VALUES = Object.freeze(
|
|
16
|
-
Object.values(EQUIVALENCE_DEGREE),
|
|
17
|
-
);
|
|
18
|
-
|
|
19
|
-
export const DEFAULT_EQUIVALENCE_DEGREE = EQUIVALENCE_DEGREE.FULL;
|
|
20
|
-
|
|
21
|
-
export function isValidEquivalenceDegree(value) {
|
|
22
|
-
return EQUIVALENCE_DEGREE_VALUES.includes(value);
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export default EQUIVALENCE_DEGREE;
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
// GenericHyperedge — an ISO 704 / ISO 1087-1 / ISO 12620 generic
|
|
2
|
-
// relation connecting a comprehensive concept (the genus) to two
|
|
3
|
-
// or more specific concepts (the species) which together constitute
|
|
4
|
-
// a decomposition by some criterion of subdivision.
|
|
5
|
-
//
|
|
6
|
-
// Mirror of PartitiveHyperedge. Multiple GenericRelations on the same
|
|
7
|
-
// comprehensive distinguished by `criterion` is the OIML pattern
|
|
8
|
-
// (5.1 measurement standard has 6 criterion groups).
|
|
9
|
-
//
|
|
10
|
-
// See docs/design/generic-relation.md (concept-model repo).
|
|
11
|
-
|
|
12
|
-
import { AbstractHyperedge } from './abstract-hyperedge.js';
|
|
13
|
-
import { GenericMember } from './generic-member.js';
|
|
14
|
-
import { HyperedgeRegistry } from './hyperedge-registry.js';
|
|
15
|
-
|
|
16
|
-
export class GenericHyperedge extends AbstractHyperedge {
|
|
17
|
-
// Per-class metadata block — the OCP contract. See PartitiveHyperedge
|
|
18
|
-
// for field documentation.
|
|
19
|
-
static wireKey = 'generic_relations';
|
|
20
|
-
static typeTag = 'generic_relation';
|
|
21
|
-
static rdfType = 'gloss:GenericRelation';
|
|
22
|
-
static memberClass = GenericMember;
|
|
23
|
-
static v1WireKeys = [];
|
|
24
|
-
static kindLabel = 'GEN';
|
|
25
|
-
|
|
26
|
-
constructor(data = {}) {
|
|
27
|
-
super(data);
|
|
28
|
-
this.members = data?.members == null
|
|
29
|
-
? this.members
|
|
30
|
-
: (Array.isArray(data.members)
|
|
31
|
-
? data.members.map(m =>
|
|
32
|
-
m instanceof GenericMember ? m : new GenericMember(m))
|
|
33
|
-
: this.members);
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
static identityOf(value) {
|
|
37
|
-
const v = value ?? {};
|
|
38
|
-
const c = v.comprehensive ?? {};
|
|
39
|
-
const memberKeys = Array.isArray(v.members)
|
|
40
|
-
? v.members.map(m => {
|
|
41
|
-
const r = m?.ref ?? m ?? {};
|
|
42
|
-
return `${r.source ?? ''}:${r.id ?? ''}`;
|
|
43
|
-
}).sort()
|
|
44
|
-
: [];
|
|
45
|
-
return `${c.source ?? ''}:${c.id ?? ''}|${memberKeys.join('|')}`;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
static fromJSON(data) {
|
|
49
|
-
return new GenericHyperedge(data);
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
export default GenericHyperedge;
|
|
54
|
-
|
|
55
|
-
HyperedgeRegistry.register(GenericHyperedge);
|