glossarist 0.4.27 → 0.4.28

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "glossarist",
3
- "version": "0.4.27",
3
+ "version": "0.4.28",
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",
@@ -1,4 +1,5 @@
1
1
  import { TextDiff } from './text-diff.js';
2
+ import { resolveMultiplicity } from '../models/multiplicity.js';
2
3
 
3
4
  const COLORS = {
4
5
  red: s => `\x1b[31m${s}\x1b[0m`,
@@ -198,16 +199,6 @@ function definitionLabel(d) {
198
199
  return d.content ?? '?';
199
200
  }
200
201
 
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
-
211
202
  function relationLabel(r) {
212
203
  if (!r) return '?';
213
204
  const c = r.comprehensive;
@@ -216,7 +207,7 @@ function relationLabel(r) {
216
207
  ? r.partitives.map(m => {
217
208
  const ref = m?.ref ?? m ?? {};
218
209
  let tail = '';
219
- const mult = _resolveMultiplicity(m);
210
+ const mult = resolveMultiplicity(m);
220
211
  if (mult && mult !== 'compulsory') tail = ` (${mult})`;
221
212
  if (m?.is_delimiting === true) tail += ' ⊘';
222
213
  return `${ref.id ?? ref.source ?? ref.text ?? '?'}${tail}`;
@@ -246,7 +237,7 @@ export function multiplicityStats(relations) {
246
237
  for (const rel of relations ?? []) {
247
238
  for (const m of rel?.partitives ?? []) {
248
239
  total += 1;
249
- const mult = _resolveMultiplicity(m);
240
+ const mult = resolveMultiplicity(m);
250
241
  byMultiplicity[mult] = (byMultiplicity[mult] ?? 0) + 1;
251
242
  const p = m?.presence ?? 'required';
252
243
  const c = m?.count ?? 'exactly_one';
@@ -63,6 +63,7 @@ export {
63
63
  isValidMultiplicity,
64
64
  multiplicityFromPair,
65
65
  pairFromMultiplicity,
66
+ resolveMultiplicity,
66
67
  } from './multiplicity.js';
67
68
 
68
69
  // v1 → v2 migration helper. Pure function; idempotent.
@@ -66,6 +66,32 @@ export function isValidMultiplicity(name) {
66
66
  return Object.prototype.hasOwnProperty.call(PAIR_BY_NAME, name);
67
67
  }
68
68
 
69
+ /**
70
+ * Resolve the ISO 704 multiplicity name from any object carrying
71
+ * `presence` + `count`, OR a legacy object that still has a literal
72
+ * `multiplicity` field (pre-v4 JSON).
73
+ *
74
+ * For v4 `PartitiveMember` instances, `presence` + `count` are always
75
+ * set (defaults applied at construction), so the legacy branch never
76
+ * fires on a real instance — it exists purely to round-trip raw JSON
77
+ * that bypassed the model constructor.
78
+ *
79
+ * Throws on the invalid `(optional, at_least_one)` combination — the
80
+ * model already rejects this at construction, so reaching the throw
81
+ * here means the caller bypassed the model. Let the error propagate
82
+ * rather than silently returning 'compulsory' (which would mask the
83
+ * data corruption).
84
+ */
85
+ export function resolveMultiplicity(member) {
86
+ if (member?.presence && member?.count) {
87
+ return multiplicityFromPair(member.presence, member.count);
88
+ }
89
+ if (typeof member?.multiplicity === 'string') {
90
+ return member.multiplicity;
91
+ }
92
+ return DEFAULT_MULTIPLICITY;
93
+ }
94
+
69
95
  function invalidCombinationError(presence, count) {
70
96
  return new Error(
71
97
  `Invalid multiplicity combination: presence=${presence}, count=${count}. ` +
@@ -21,7 +21,7 @@
21
21
 
22
22
  import { PRED } from './predicates.js';
23
23
  import { namedNode, literal, quad } from './terms.js';
24
- import { multiplicityFromPair } from '../models/multiplicity.js';
24
+ import { resolveMultiplicity } from '../models/multiplicity.js';
25
25
 
26
26
  // SKOS taxonomy namespaces (concept-model will ship these taxonomies).
27
27
  // Until then, the URIs are forward-compatible and resolve once the
@@ -46,16 +46,15 @@ export function* partitiveMemberToQuads(member, { memberUri }) {
46
46
  );
47
47
  }
48
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
- }
49
+ // v4 PartitiveMember carries presence + count natively; the legacy
50
+ // multiplicity name is derived via the SSOT resolveMultiplicity().
51
+ // For raw pre-v4 JSON that bypassed the model constructor and still
52
+ // carries a literal multiplicity field, the resolver falls back to
53
+ // that field. The invalid (optional, at_least_one) combination is
54
+ // rejected at model construction — if it reaches the throw here,
55
+ // the caller bypassed the model and we want the error to surface
56
+ // rather than silently producing wrong RDF.
57
+ const mult = resolveMultiplicity(member);
59
58
  if (mult) {
60
59
  yield quad(
61
60
  namedNode(memberUri),