glossarist 0.4.25 → 0.4.27
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/diff/diff-renderer.js +25 -4
- package/src/migration/partitive-relation-migrator.js +3 -2
- package/src/models/index.js +14 -0
- package/src/models/multiplicity.js +67 -17
- package/src/models/partitive-count.js +20 -0
- package/src/models/partitive-member.js +51 -72
- package/src/models/partitive-presence.js +18 -0
- package/src/rdf/gloss-partitive-member.js +46 -12
- package/src/rdf/index.d.ts +2 -0
- package/src/rdf/index.js +1 -1
- package/src/rdf/predicates.d.ts +4 -0
- package/src/rdf/predicates.js +2 -0
package/package.json
CHANGED
|
@@ -198,6 +198,16 @@ function definitionLabel(d) {
|
|
|
198
198
|
return d.content ?? '?';
|
|
199
199
|
}
|
|
200
200
|
|
|
201
|
+
import { multiplicityFromPair } from '../models/multiplicity.js';
|
|
202
|
+
|
|
203
|
+
function _resolveMultiplicity(m) {
|
|
204
|
+
if (m?.presence && m?.count) {
|
|
205
|
+
try { return multiplicityFromPair(m.presence, m.count); }
|
|
206
|
+
catch { return 'compulsory'; }
|
|
207
|
+
}
|
|
208
|
+
return m?.multiplicity ?? 'compulsory';
|
|
209
|
+
}
|
|
210
|
+
|
|
201
211
|
function relationLabel(r) {
|
|
202
212
|
if (!r) return '?';
|
|
203
213
|
const c = r.comprehensive;
|
|
@@ -206,7 +216,7 @@ function relationLabel(r) {
|
|
|
206
216
|
? r.partitives.map(m => {
|
|
207
217
|
const ref = m?.ref ?? m ?? {};
|
|
208
218
|
let tail = '';
|
|
209
|
-
const mult = m
|
|
219
|
+
const mult = _resolveMultiplicity(m);
|
|
210
220
|
if (mult && mult !== 'compulsory') tail = ` (${mult})`;
|
|
211
221
|
if (m?.is_delimiting === true) tail += ' ⊘';
|
|
212
222
|
return `${ref.id ?? ref.source ?? ref.text ?? '?'}${tail}`;
|
|
@@ -222,19 +232,30 @@ function relationLabel(r) {
|
|
|
222
232
|
// Aggregate multiplicity + delimiting stats across a concept's
|
|
223
233
|
// PartitiveRelations. Used by dataset-level summaries, not by the
|
|
224
234
|
// per-relation diff label.
|
|
235
|
+
//
|
|
236
|
+
// v4: presence and count are independent dimensions. Stats include
|
|
237
|
+
// byPresence and byCount breakdowns so tools can query each axis
|
|
238
|
+
// independently ("show me all optional parts" or "show me all
|
|
239
|
+
// multiple-count parts").
|
|
225
240
|
export function multiplicityStats(relations) {
|
|
226
241
|
const byMultiplicity = {};
|
|
242
|
+
const byPresence = {};
|
|
243
|
+
const byCount = {};
|
|
227
244
|
let delimitingCount = 0;
|
|
228
245
|
let total = 0;
|
|
229
246
|
for (const rel of relations ?? []) {
|
|
230
247
|
for (const m of rel?.partitives ?? []) {
|
|
231
248
|
total += 1;
|
|
232
|
-
const
|
|
233
|
-
byMultiplicity[
|
|
249
|
+
const mult = _resolveMultiplicity(m);
|
|
250
|
+
byMultiplicity[mult] = (byMultiplicity[mult] ?? 0) + 1;
|
|
251
|
+
const p = m?.presence ?? 'required';
|
|
252
|
+
const c = m?.count ?? 'exactly_one';
|
|
253
|
+
byPresence[p] = (byPresence[p] ?? 0) + 1;
|
|
254
|
+
byCount[c] = (byCount[c] ?? 0) + 1;
|
|
234
255
|
if (m?.is_delimiting === true) delimitingCount += 1;
|
|
235
256
|
}
|
|
236
257
|
}
|
|
237
|
-
return { total, byMultiplicity, delimiting: delimitingCount };
|
|
258
|
+
return { total, byMultiplicity, byPresence, byCount, delimiting: delimitingCount };
|
|
238
259
|
}
|
|
239
260
|
|
|
240
261
|
function itemLabel(item) {
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
// ISO 704:2022 correction):
|
|
9
9
|
//
|
|
10
10
|
// comprehensive → comprehensive (unchanged shape)
|
|
11
|
-
// parts: [ref, ...] → partitives: [{ ref,
|
|
11
|
+
// parts: [ref, ...] → partitives: [{ ref, presence: 'required', count: 'exactly_one' }, ...]
|
|
12
12
|
// enumeration: open → completeness: partial
|
|
13
13
|
// enumeration: closed→ completeness: complete
|
|
14
14
|
// markers: [...] → (dropped — v2 uses per-member multiplicity
|
|
@@ -54,7 +54,8 @@ export function migrateHyperedgeToRelation(v1Hash) {
|
|
|
54
54
|
const parts = Array.isArray(v1Hash.parts) ? v1Hash.parts : [];
|
|
55
55
|
out.partitives = parts.map(ref => ({
|
|
56
56
|
ref: { ...ref },
|
|
57
|
-
|
|
57
|
+
presence: 'required',
|
|
58
|
+
count: 'exactly_one',
|
|
58
59
|
}));
|
|
59
60
|
|
|
60
61
|
out.completeness = _migrateEnumeration(v1Hash.enumeration);
|
package/src/models/index.js
CHANGED
|
@@ -38,6 +38,18 @@ export {
|
|
|
38
38
|
// backward-compat input. New code should use v2 exclusively.
|
|
39
39
|
export { PartitiveRelation } from './partitive-relation.js';
|
|
40
40
|
export { PartitiveMember } from './partitive-member.js';
|
|
41
|
+
export {
|
|
42
|
+
PARTITIVE_PRESENCE,
|
|
43
|
+
PARTITIVE_PRESENCE_VALUES,
|
|
44
|
+
DEFAULT_PRESENCE,
|
|
45
|
+
isValidPresence,
|
|
46
|
+
} from './partitive-presence.js';
|
|
47
|
+
export {
|
|
48
|
+
PARTITIVE_COUNT,
|
|
49
|
+
PARTITIVE_COUNT_VALUES,
|
|
50
|
+
DEFAULT_COUNT,
|
|
51
|
+
isValidCount,
|
|
52
|
+
} from './partitive-count.js';
|
|
41
53
|
export {
|
|
42
54
|
COMPLETENESS,
|
|
43
55
|
COMPLETENESS_VALUES,
|
|
@@ -49,6 +61,8 @@ export {
|
|
|
49
61
|
MULTIPLICITY_VALUES,
|
|
50
62
|
DEFAULT_MULTIPLICITY,
|
|
51
63
|
isValidMultiplicity,
|
|
64
|
+
multiplicityFromPair,
|
|
65
|
+
pairFromMultiplicity,
|
|
52
66
|
} from './multiplicity.js';
|
|
53
67
|
|
|
54
68
|
// v1 → v2 migration helper. Pure function; idempotent.
|
|
@@ -1,24 +1,74 @@
|
|
|
1
|
-
// Multiplicity — ISO 704:2022 partitive
|
|
1
|
+
// Multiplicity — ISO 704:2022 partitive multiplicity (derived view).
|
|
2
2
|
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
// compulsory_multiple — 2 solid lines (multiple must exist)
|
|
7
|
-
// optional_multiple — 2 dashed lines (multiple may exist)
|
|
8
|
-
// compulsory_at_least_one — 1 solid + 1 dashed line (at least one must exist)
|
|
3
|
+
// ISO 704:2022 multiplicity is the cross product of two orthogonal
|
|
4
|
+
// dimensions (PartitivePresence × PartitiveCount). The 5 valid
|
|
5
|
+
// combinations map to the 5 ISO 704 names:
|
|
9
6
|
//
|
|
10
|
-
//
|
|
7
|
+
// required × exactly_one → compulsory
|
|
8
|
+
// optional × exactly_one → optional
|
|
9
|
+
// required × multiple → compulsory_multiple
|
|
10
|
+
// optional × multiple → optional_multiple
|
|
11
|
+
// required × at_least_one → compulsory_at_least_one
|
|
12
|
+
//
|
|
13
|
+
// (optional × at_least_one is REDUNDANT with optional × multiple —
|
|
14
|
+
// "may exist, ≥1 if it does" = "may exist in any number" — and is
|
|
15
|
+
// explicitly rejected at construction with a clear error message.)
|
|
16
|
+
//
|
|
17
|
+
// This module is the SSOT for the (presence, count) → ISO name
|
|
18
|
+
// mapping. Both MULTIPLICITY constants and MULTIPLICITY_VALUES are
|
|
19
|
+
// derived from this single table; no duplicate string literals.
|
|
11
20
|
|
|
12
|
-
import {
|
|
21
|
+
import { PARTITIVE_PRESENCE } from './partitive-presence.js';
|
|
22
|
+
import { PARTITIVE_COUNT } from './partitive-count.js';
|
|
13
23
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
OPTIONAL:
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
24
|
+
const NAME_BY_PAIR = Object.freeze({
|
|
25
|
+
[`${PARTITIVE_PRESENCE.REQUIRED}:${PARTITIVE_COUNT.EXACTLY_ONE}`]: 'compulsory',
|
|
26
|
+
[`${PARTITIVE_PRESENCE.OPTIONAL}:${PARTITIVE_COUNT.EXACTLY_ONE}`]: 'optional',
|
|
27
|
+
[`${PARTITIVE_PRESENCE.REQUIRED}:${PARTITIVE_COUNT.MULTIPLE}`]: 'compulsory_multiple',
|
|
28
|
+
[`${PARTITIVE_PRESENCE.OPTIONAL}:${PARTITIVE_COUNT.MULTIPLE}`]: 'optional_multiple',
|
|
29
|
+
[`${PARTITIVE_PRESENCE.REQUIRED}:${PARTITIVE_COUNT.AT_LEAST_ONE}`]: 'compulsory_at_least_one',
|
|
20
30
|
});
|
|
21
31
|
|
|
22
|
-
|
|
32
|
+
const PAIR_BY_NAME = Object.freeze(
|
|
33
|
+
Object.fromEntries(
|
|
34
|
+
Object.entries(NAME_BY_PAIR).map(([pair, name]) => [name, pair]),
|
|
35
|
+
),
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
export const MULTIPLICITY = Object.freeze(
|
|
39
|
+
Object.fromEntries(
|
|
40
|
+
Object.values(NAME_BY_PAIR).map(name => [name.toUpperCase(), name]),
|
|
41
|
+
),
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
export const MULTIPLICITY_VALUES = Object.freeze(Object.values(NAME_BY_PAIR));
|
|
45
|
+
|
|
23
46
|
export const DEFAULT_MULTIPLICITY = MULTIPLICITY.COMPULSORY;
|
|
24
|
-
|
|
47
|
+
|
|
48
|
+
export function multiplicityFromPair(presence, count) {
|
|
49
|
+
const name = NAME_BY_PAIR[`${presence}:${count}`];
|
|
50
|
+
if (name == null) {
|
|
51
|
+
throw invalidCombinationError(presence, count);
|
|
52
|
+
}
|
|
53
|
+
return name;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function pairFromMultiplicity(name) {
|
|
57
|
+
const pair = PAIR_BY_NAME[name];
|
|
58
|
+
if (pair == null) {
|
|
59
|
+
throw new Error(`Unknown multiplicity: ${JSON.stringify(name)}`);
|
|
60
|
+
}
|
|
61
|
+
const [presence, count] = pair.split(':');
|
|
62
|
+
return { presence, count };
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function isValidMultiplicity(name) {
|
|
66
|
+
return Object.prototype.hasOwnProperty.call(PAIR_BY_NAME, name);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function invalidCombinationError(presence, count) {
|
|
70
|
+
return new Error(
|
|
71
|
+
`Invalid multiplicity combination: presence=${presence}, count=${count}. ` +
|
|
72
|
+
`(optional + at_least_one collapses to optional + multiple — use count=multiple.)`,
|
|
73
|
+
);
|
|
74
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// PartitiveCount — ISO 704:2022 partitive member count.
|
|
2
|
+
//
|
|
3
|
+
// count (line count):
|
|
4
|
+
// exactly_one — 1 line (exactly one instance)
|
|
5
|
+
// at_least_one — 1 solid + 1 dashed (at least one instance)
|
|
6
|
+
// multiple — 2 lines (multiple instances)
|
|
7
|
+
//
|
|
8
|
+
// Default: exactly_one.
|
|
9
|
+
|
|
10
|
+
import { makeEnum } from './enum.js';
|
|
11
|
+
|
|
12
|
+
export const PARTITIVE_COUNT = makeEnum('PARTITIVE_COUNT', {
|
|
13
|
+
EXACTLY_ONE: 'exactly_one',
|
|
14
|
+
AT_LEAST_ONE: 'at_least_one',
|
|
15
|
+
MULTIPLE: 'multiple',
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
export const PARTITIVE_COUNT_VALUES = PARTITIVE_COUNT.VALUES;
|
|
19
|
+
export const DEFAULT_COUNT = PARTITIVE_COUNT.EXACTLY_ONE;
|
|
20
|
+
export const isValidCount = PARTITIVE_COUNT.isValid;
|
|
@@ -1,33 +1,35 @@
|
|
|
1
1
|
// PartitiveMember — one member of a PartitiveRelation.
|
|
2
2
|
//
|
|
3
3
|
// Carries a ConceptRef (the subordinate concept partitive) plus
|
|
4
|
-
// ISO 704:2022
|
|
4
|
+
// ISO 704:2022 presence + count + is_delimiting.
|
|
5
5
|
//
|
|
6
|
-
//
|
|
6
|
+
// Matches concept-model/models/concepts/PartitiveMember.lutaml exactly:
|
|
7
|
+
// +ref — ConceptRef [1]
|
|
8
|
+
// +presence — PartitivePresence [0..1] (default: required)
|
|
9
|
+
// +count — PartitiveCount [0..1] (default: exactly_one)
|
|
10
|
+
// +is_delimiting — Boolean [0..1] (default: false)
|
|
7
11
|
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
// compulsory_multiple — 2 solid lines (multiple must exist)
|
|
12
|
-
// optional_multiple — 2 dashed lines (multiple may exist)
|
|
13
|
-
// compulsory_at_least_one — 1 solid + 1 dashed (≥1 must exist)
|
|
12
|
+
// The combination (optional, at_least_one) is invalid: if a part is
|
|
13
|
+
// optional, the at-least-one constraint is vacuous and collapses to
|
|
14
|
+
// (optional, multiple). Rejected at construction.
|
|
14
15
|
//
|
|
15
|
-
//
|
|
16
|
-
//
|
|
17
|
-
//
|
|
18
|
-
//
|
|
19
|
-
// Whether a part is delimiting depends on the concept system, the
|
|
20
|
-
// coordinate concepts, the inheritance principle, and the criterion
|
|
21
|
-
// of subdivision used (ISO 704:2022 §5.5.4.2.1).
|
|
16
|
+
// ISO 704 "name" strings (compulsory, optional, etc.) are NOT part of
|
|
17
|
+
// the model — they're a display concern handled by multiplicityFromPair
|
|
18
|
+
// in the renderer/viewer layer. The model is purely presence + count.
|
|
22
19
|
|
|
23
20
|
import { GlossaristModel } from './base.js';
|
|
24
21
|
import { ConceptRef } from './concept-ref.js';
|
|
25
22
|
import {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
} from './
|
|
23
|
+
PARTITIVE_PRESENCE,
|
|
24
|
+
DEFAULT_PRESENCE,
|
|
25
|
+
PARTITIVE_PRESENCE_VALUES,
|
|
26
|
+
isValidPresence,
|
|
27
|
+
} from './partitive-presence.js';
|
|
28
|
+
import {
|
|
29
|
+
DEFAULT_COUNT,
|
|
30
|
+
PARTITIVE_COUNT_VALUES,
|
|
31
|
+
isValidCount,
|
|
32
|
+
} from './partitive-count.js';
|
|
31
33
|
|
|
32
34
|
export class PartitiveMember extends GlossaristModel {
|
|
33
35
|
constructor(data = {}) {
|
|
@@ -35,38 +37,17 @@ export class PartitiveMember extends GlossaristModel {
|
|
|
35
37
|
this.ref = data.ref instanceof ConceptRef
|
|
36
38
|
? data.ref
|
|
37
39
|
: new ConceptRef(data.ref ?? {});
|
|
38
|
-
this.
|
|
39
|
-
this.
|
|
40
|
+
this.presence = _resolvePresence(data.presence);
|
|
41
|
+
this.count = _resolveCount(data.count);
|
|
42
|
+
this.is_delimiting = data.is_delimiting === true;
|
|
40
43
|
|
|
41
44
|
_assertNonEmptyRef(this.ref);
|
|
45
|
+
_assertValidCombination(this.presence, this.count);
|
|
42
46
|
}
|
|
43
47
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
get isCompulsory() {
|
|
48
|
-
return this.multiplicity === MULTIPLICITY.COMPULSORY;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
get isOptional() {
|
|
52
|
-
return this.multiplicity === MULTIPLICITY.OPTIONAL;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
get isCompulsoryMultiple() {
|
|
56
|
-
return this.multiplicity === MULTIPLICITY.COMPULSORY_MULTIPLE;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
get isOptionalMultiple() {
|
|
60
|
-
return this.multiplicity === MULTIPLICITY.OPTIONAL_MULTIPLE;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
get isCompulsoryAtLeastOne() {
|
|
64
|
-
return this.multiplicity === MULTIPLICITY.COMPULSORY_AT_LEAST_ONE;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
get isDelimiting() {
|
|
68
|
-
return this.is_delimiting === true;
|
|
69
|
-
}
|
|
48
|
+
get isRequired() { return this.presence === PARTITIVE_PRESENCE.REQUIRED; }
|
|
49
|
+
get isOptional() { return this.presence === PARTITIVE_PRESENCE.OPTIONAL; }
|
|
50
|
+
get isDelimiting() { return this.is_delimiting === true; }
|
|
70
51
|
|
|
71
52
|
identity() {
|
|
72
53
|
return PartitiveMember.identityOf(this);
|
|
@@ -74,12 +55,9 @@ export class PartitiveMember extends GlossaristModel {
|
|
|
74
55
|
|
|
75
56
|
toJSON() {
|
|
76
57
|
const obj = { ref: this.ref.toJSON() };
|
|
77
|
-
if (this.
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
if (this.is_delimiting === true) {
|
|
81
|
-
obj.is_delimiting = true;
|
|
82
|
-
}
|
|
58
|
+
if (this.presence !== DEFAULT_PRESENCE) obj.presence = this.presence;
|
|
59
|
+
if (this.count !== DEFAULT_COUNT) obj.count = this.count;
|
|
60
|
+
if (this.is_delimiting === true) obj.is_delimiting = true;
|
|
83
61
|
return obj;
|
|
84
62
|
}
|
|
85
63
|
|
|
@@ -94,33 +72,34 @@ export class PartitiveMember extends GlossaristModel {
|
|
|
94
72
|
}
|
|
95
73
|
}
|
|
96
74
|
|
|
97
|
-
function
|
|
98
|
-
if (value == null) return
|
|
99
|
-
if (!
|
|
100
|
-
throw new Error(
|
|
101
|
-
`invalid multiplicity ${JSON.stringify(value)}; ` +
|
|
102
|
-
`must be one of ${MULTIPLICITY_VALUES.join(', ')}`,
|
|
103
|
-
);
|
|
75
|
+
function _resolvePresence(value) {
|
|
76
|
+
if (value == null) return DEFAULT_PRESENCE;
|
|
77
|
+
if (!isValidPresence(value)) {
|
|
78
|
+
throw new Error(`invalid presence ${JSON.stringify(value)}; must be one of ${PARTITIVE_PRESENCE_VALUES.join(', ')}`);
|
|
104
79
|
}
|
|
105
80
|
return value;
|
|
106
81
|
}
|
|
107
82
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
`PartitiveMember.${fieldName} must be a boolean; got ${typeof value} ` +
|
|
115
|
-
`${JSON.stringify(value)}`,
|
|
116
|
-
);
|
|
83
|
+
function _resolveCount(value) {
|
|
84
|
+
if (value == null) return DEFAULT_COUNT;
|
|
85
|
+
if (!isValidCount(value)) {
|
|
86
|
+
throw new Error(`invalid count ${JSON.stringify(value)}; must be one of ${PARTITIVE_COUNT_VALUES.join(', ')}`);
|
|
87
|
+
}
|
|
88
|
+
return value;
|
|
117
89
|
}
|
|
118
90
|
|
|
119
91
|
function _assertNonEmptyRef(ref) {
|
|
120
92
|
if (!ref.source && !ref.id && !ref.text) {
|
|
93
|
+
throw new Error('PartitiveMember#ref must be a non-empty ConceptRef (source, id, or text required)');
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function _assertValidCombination(presence, count) {
|
|
98
|
+
if (presence === 'optional' && count === 'at_least_one') {
|
|
121
99
|
throw new Error(
|
|
122
|
-
'PartitiveMember
|
|
123
|
-
'(
|
|
100
|
+
'PartitiveMember presence=optional + count=at_least_one is invalid — ' +
|
|
101
|
+
'it collapses to optional + multiple (zero or more). ' +
|
|
102
|
+
'Use presence: optional, count: multiple instead.',
|
|
124
103
|
);
|
|
125
104
|
}
|
|
126
105
|
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// PartitivePresence — ISO 704:2022 partitive member presence.
|
|
2
|
+
//
|
|
3
|
+
// presence (line style):
|
|
4
|
+
// required — solid line (must exist in every instance)
|
|
5
|
+
// optional — dashed line (may exist; exists in some instances)
|
|
6
|
+
//
|
|
7
|
+
// Default: required.
|
|
8
|
+
|
|
9
|
+
import { makeEnum } from './enum.js';
|
|
10
|
+
|
|
11
|
+
export const PARTITIVE_PRESENCE = makeEnum('PARTITIVE_PRESENCE', {
|
|
12
|
+
REQUIRED: 'required',
|
|
13
|
+
OPTIONAL: 'optional',
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
export const PARTITIVE_PRESENCE_VALUES = PARTITIVE_PRESENCE.VALUES;
|
|
17
|
+
export const DEFAULT_PRESENCE = PARTITIVE_PRESENCE.REQUIRED;
|
|
18
|
+
export const isValidPresence = PARTITIVE_PRESENCE.isValid;
|
|
@@ -1,32 +1,66 @@
|
|
|
1
1
|
// PartitiveMember → RDF quads.
|
|
2
2
|
//
|
|
3
|
-
// Emits per-member predicates (
|
|
4
|
-
// member's concept URI. The member URI
|
|
5
|
-
// (typically `gloss-partitive-relation.js`).
|
|
3
|
+
// Emits per-member predicates (presence, count, is_delimiting,
|
|
4
|
+
// derived multiplicity) onto the member's concept URI. The member URI
|
|
5
|
+
// is constructed by the caller (typically `gloss-partitive-relation.js`).
|
|
6
6
|
//
|
|
7
7
|
// Extracted from `gloss-partitive-relation.js` per
|
|
8
8
|
// TODO.partitive-relation-v3/04 so future emitters (a standalone
|
|
9
9
|
// PartitiveMember listing, a stats aggregator) can reuse this
|
|
10
10
|
// emission without modifying the relation emitter (OCP).
|
|
11
|
+
//
|
|
12
|
+
// v4 decomposition: ISO 704:2022 multiplicity is the cross product of
|
|
13
|
+
// two orthogonal dimensions (presence × count). Per the JSON-LD
|
|
14
|
+
// context (`"@type": "@id"`), all three are emitted as URI references
|
|
15
|
+
// into the corresponding SKOS taxonomy namespaces — none as literals.
|
|
16
|
+
//
|
|
17
|
+
// <member-uri> gloss:presence <.../presence/required|optional>
|
|
18
|
+
// <member-uri> gloss:count <.../count/exactly_one|at_least_one|multiple>
|
|
19
|
+
// <member-uri> gloss:multiplicity <.../multiplicity/{ISO name}> (derived, v3 compat)
|
|
20
|
+
// <member-uri> gloss:isDelimiting "true" (xsd:boolean)
|
|
11
21
|
|
|
12
22
|
import { PRED } from './predicates.js';
|
|
13
23
|
import { namedNode, literal, quad } from './terms.js';
|
|
24
|
+
import { multiplicityFromPair } from '../models/multiplicity.js';
|
|
14
25
|
|
|
15
|
-
// SKOS taxonomy
|
|
16
|
-
//
|
|
17
|
-
//
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
// MUST be URI references, not string literals. Emitted shape:
|
|
21
|
-
// <member-uri> gloss:multiplicity <.../multiplicity/compulsory>
|
|
26
|
+
// SKOS taxonomy namespaces (concept-model will ship these taxonomies).
|
|
27
|
+
// Until then, the URIs are forward-compatible and resolve once the
|
|
28
|
+
// taxonomies land.
|
|
29
|
+
export const PRESENCE_NS = 'https://www.glossarist.org/ontologies/presence/';
|
|
30
|
+
export const COUNT_NS = 'https://www.glossarist.org/ontologies/count/';
|
|
22
31
|
export const MULTIPLICITY_NS = 'https://www.glossarist.org/ontologies/multiplicity/';
|
|
23
32
|
|
|
24
33
|
export function* partitiveMemberToQuads(member, { memberUri }) {
|
|
25
|
-
if (member?.
|
|
34
|
+
if (member?.presence) {
|
|
35
|
+
yield quad(
|
|
36
|
+
namedNode(memberUri),
|
|
37
|
+
namedNode(PRED.gloss.presence),
|
|
38
|
+
namedNode(PRESENCE_NS + member.presence),
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
if (member?.count) {
|
|
42
|
+
yield quad(
|
|
43
|
+
namedNode(memberUri),
|
|
44
|
+
namedNode(PRED.gloss.count),
|
|
45
|
+
namedNode(COUNT_NS + member.count),
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
// Derived multiplicity URI for v3 RDF consumer backward compat.
|
|
49
|
+
// v4 PartitiveMember no longer exposes .multiplicity — compute from
|
|
50
|
+
// presence + count via multiplicityFromPair. Accept raw JSON that
|
|
51
|
+
// still carries a literal multiplicity field for pre-v4 data.
|
|
52
|
+
let mult = null;
|
|
53
|
+
if (member?.presence && member?.count) {
|
|
54
|
+
try { mult = multiplicityFromPair(member.presence, member.count); }
|
|
55
|
+
catch { /* invalid combination — skip derived emission */ }
|
|
56
|
+
} else if (typeof member?.multiplicity === 'string') {
|
|
57
|
+
mult = member.multiplicity;
|
|
58
|
+
}
|
|
59
|
+
if (mult) {
|
|
26
60
|
yield quad(
|
|
27
61
|
namedNode(memberUri),
|
|
28
62
|
namedNode(PRED.gloss.multiplicity),
|
|
29
|
-
namedNode(MULTIPLICITY_NS +
|
|
63
|
+
namedNode(MULTIPLICITY_NS + mult),
|
|
30
64
|
);
|
|
31
65
|
}
|
|
32
66
|
if (member?.is_delimiting === true) {
|
package/src/rdf/index.d.ts
CHANGED
|
@@ -121,6 +121,8 @@ export interface PartitiveMemberEmitOptions {
|
|
|
121
121
|
}
|
|
122
122
|
|
|
123
123
|
export declare const MULTIPLICITY_NS: string;
|
|
124
|
+
export declare const PRESENCE_NS: string;
|
|
125
|
+
export declare const COUNT_NS: string;
|
|
124
126
|
|
|
125
127
|
export declare function partitiveMemberToQuads(
|
|
126
128
|
member: PartitiveMember,
|
package/src/rdf/index.js
CHANGED
|
@@ -27,7 +27,7 @@ export {
|
|
|
27
27
|
export { detailedDefinitionToQuads } from './gloss-detailed-definition.js';
|
|
28
28
|
export { conceptSourceToQuads } from './gloss-source.js';
|
|
29
29
|
export { hyperedgeToQuads, hyperedgeSubjectUri } from './gloss-hyperedge.js';
|
|
30
|
-
export { partitiveMemberToQuads, MULTIPLICITY_NS } from './gloss-partitive-member.js';
|
|
30
|
+
export { partitiveMemberToQuads, MULTIPLICITY_NS, PRESENCE_NS, COUNT_NS } from './gloss-partitive-member.js';
|
|
31
31
|
export { partitiveRelationToQuads, partitiveRelationSubjectUri } from './gloss-partitive-relation.js';
|
|
32
32
|
export {
|
|
33
33
|
nonVerbalRepToQuads,
|
package/src/rdf/predicates.d.ts
CHANGED
|
@@ -110,6 +110,8 @@ export declare const PRED = {
|
|
|
110
110
|
"hasPartitiveRelation": "https://www.glossarist.org/ontologies/hasPartitiveRelation",
|
|
111
111
|
"hasPartitive": "https://www.glossarist.org/ontologies/hasPartitive",
|
|
112
112
|
"completeness": "https://www.glossarist.org/ontologies/completeness",
|
|
113
|
+
"presence": "https://www.glossarist.org/ontologies/presence",
|
|
114
|
+
"count": "https://www.glossarist.org/ontologies/count",
|
|
113
115
|
"multiplicity": "https://www.glossarist.org/ontologies/multiplicity",
|
|
114
116
|
"isDelimiting": "https://www.glossarist.org/ontologies/isDelimiting",
|
|
115
117
|
"criterion": "https://www.glossarist.org/ontologies/criterion",
|
|
@@ -392,6 +394,8 @@ export type Predicate =
|
|
|
392
394
|
| "https://www.glossarist.org/ontologies/hasPartitiveRelation"
|
|
393
395
|
| "https://www.glossarist.org/ontologies/hasPartitive"
|
|
394
396
|
| "https://www.glossarist.org/ontologies/completeness"
|
|
397
|
+
| "https://www.glossarist.org/ontologies/presence"
|
|
398
|
+
| "https://www.glossarist.org/ontologies/count"
|
|
395
399
|
| "https://www.glossarist.org/ontologies/multiplicity"
|
|
396
400
|
| "https://www.glossarist.org/ontologies/isDelimiting"
|
|
397
401
|
| "https://www.glossarist.org/ontologies/criterion"
|
package/src/rdf/predicates.js
CHANGED
|
@@ -110,6 +110,8 @@ export const PRED = {
|
|
|
110
110
|
"hasPartitiveRelation": "https://www.glossarist.org/ontologies/hasPartitiveRelation",
|
|
111
111
|
"hasPartitive": "https://www.glossarist.org/ontologies/hasPartitive",
|
|
112
112
|
"completeness": "https://www.glossarist.org/ontologies/completeness",
|
|
113
|
+
"presence": "https://www.glossarist.org/ontologies/presence",
|
|
114
|
+
"count": "https://www.glossarist.org/ontologies/count",
|
|
113
115
|
"multiplicity": "https://www.glossarist.org/ontologies/multiplicity",
|
|
114
116
|
"isDelimiting": "https://www.glossarist.org/ontologies/isDelimiting",
|
|
115
117
|
"criterion": "https://www.glossarist.org/ontologies/criterion",
|