@glossarist/concept-browser 0.7.114 → 0.7.116

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 (103) hide show
  1. package/README.md +45 -2
  2. package/cli/index.mjs +10 -13
  3. package/cli/index.ts +9 -11
  4. package/data/concept-model/shapes/glossarist.concept.yaml.schema.json +255 -0
  5. package/package.json +18 -12
  6. package/scripts/__tests__/{bibliography.test.mjs → bibliography.test.ts} +1 -1
  7. package/scripts/__tests__/{cli-script-contract.test.mjs → cli-script-contract.test.ts} +3 -3
  8. package/scripts/__tests__/{concept-groups.test.mjs → concept-groups.test.ts} +1 -1
  9. package/scripts/__tests__/{doctor.test.mjs → doctor.test.ts} +1 -1
  10. package/scripts/__tests__/{fetch-datasets.test.mjs → fetch-datasets.test.ts} +1 -1
  11. package/scripts/__tests__/{first-non-empty.test.mjs → first-non-empty.test.ts} +1 -1
  12. package/scripts/__tests__/{process-about-pages.test.mjs → process-about-pages.test.ts} +1 -1
  13. package/scripts/bridge-to-astro.js +132 -0
  14. package/scripts/{bridge-to-astro.mjs → bridge-to-astro.ts} +2 -4
  15. package/scripts/build-edges.js +242 -336
  16. package/scripts/build-edges.ts +572 -0
  17. package/scripts/build-scripts.mjs +22 -0
  18. package/scripts/check-syntax.js +81 -0
  19. package/scripts/{check-syntax.mjs → check-syntax.ts} +20 -13
  20. package/scripts/doctor.js +278 -0
  21. package/scripts/extract-source-refs.js +16 -24
  22. package/scripts/extract-source-refs.ts +32 -0
  23. package/scripts/fetch-datasets.js +278 -0
  24. package/scripts/{fetch-datasets.mjs → fetch-datasets.ts} +2 -2
  25. package/scripts/generate-404.js +10 -11
  26. package/scripts/generate-404.ts +15 -0
  27. package/scripts/generate-data.js +2220 -0
  28. package/scripts/{generate-data.mjs → generate-data.ts} +73 -460
  29. package/scripts/generate-ontology-data.js +139 -0
  30. package/scripts/generate-ontology-schema.js +472 -0
  31. package/scripts/{generate-ontology-schema.mjs → generate-ontology-schema.ts} +2 -2
  32. package/scripts/lib/{build-cache.mjs → build-cache.ts} +3 -2
  33. package/scripts/lib/concept-formats.ts +206 -0
  34. package/scripts/lib/concept-uri.ts +14 -0
  35. package/scripts/lib/{dataset-turtle.mjs → dataset-turtle.ts} +4 -3
  36. package/scripts/lib/harmonize-concept.ts +86 -0
  37. package/scripts/lib/jsonld-emitter.ts +169 -0
  38. package/scripts/lib/{local-path-safety.mjs → local-path-safety.ts} +6 -2
  39. package/scripts/lib/yaml-types.ts +262 -0
  40. package/scripts/load-site-config.js +83 -0
  41. package/scripts/migrate-v1-to-v3.js +113 -0
  42. package/scripts/normalize-yaml.js +96 -0
  43. package/scripts/process-about-pages.js +241 -0
  44. package/scripts/{process-about-pages.mjs → process-about-pages.ts} +1 -1
  45. package/scripts/smoke.js +211 -0
  46. package/scripts/{smoke.mjs → smoke.ts} +4 -3
  47. package/scripts/sync-concept-model.js +63 -0
  48. package/scripts/validate-shacl.js +152 -0
  49. package/scripts/{validate-shacl.mjs → validate-shacl.ts} +5 -5
  50. package/scripts/validate-yaml.js +132 -0
  51. package/scripts/validate-yaml.ts +144 -0
  52. package/src/__tests__/extract-source-refs.test.ts +2 -3
  53. package/src/__tests__/perf/serialization-perf.test.ts +5 -5
  54. package/src/__tests__/published-package-contract.test.ts +84 -0
  55. package/src/__tests__/scripts/agents-turtle.test.ts +1 -1
  56. package/src/__tests__/scripts/bibliography-turtle.test.ts +1 -1
  57. package/src/__tests__/scripts/build-activity-turtle.test.ts +1 -1
  58. package/src/__tests__/scripts/build-cache.test.ts +1 -1
  59. package/src/__tests__/scripts/build-integration.test.ts +19 -19
  60. package/src/__tests__/scripts/dataset-turtle.test.ts +1 -1
  61. package/src/__tests__/scripts/migrate-v1-to-v3.test.ts +1 -1
  62. package/src/__tests__/scripts/normalize-yaml.test.ts +1 -1
  63. package/src/__tests__/scripts/turtle-escape.test.ts +1 -1
  64. package/src/__tests__/scripts/validate-yaml.test.ts +76 -0
  65. package/src/__tests__/scripts/version-turtle.test.ts +1 -1
  66. package/src/__tests__/scripts/vocab-turtle.test.ts +1 -1
  67. package/src/__tests__/scripts-syntax.test.ts +4 -4
  68. package/src/__tests__/use-rdf-document.test.ts +2 -3
  69. package/src/__tests__/utils/content-renderer-xss.test.ts +57 -0
  70. package/src/__tests__/validate-shacl.test.ts +3 -3
  71. package/src/adapters/wire/index.ts +69 -0
  72. package/src/data/ontology-schema.json +1336 -183
  73. package/src/utils/content-renderer.ts +7 -3
  74. package/src/utils/url-safety.ts +1 -5
  75. package/tsconfig.json +2 -2
  76. package/vite.config.ts +1 -1
  77. package/src/__tests__/composables/use-concept-edges-v2.test.ts +0 -167
  78. package/src/__tests__/composables/use-concept-edges-v2.test.ts.bak +0 -166
  79. package/src/types/agents-version-turtle.d.ts +0 -27
  80. package/src/types/bibliography-turtle.d.ts +0 -12
  81. package/src/types/build-activity-turtle.d.ts +0 -16
  82. package/src/types/build-cache.d.ts +0 -20
  83. package/src/types/dataset-turtle.d.ts +0 -32
  84. package/src/types/normalize-yaml.d.ts +0 -16
  85. package/src/types/turtle-escape.d.ts +0 -6
  86. package/src/types/vocab-turtle.d.ts +0 -13
  87. /package/scripts/{doctor.mjs → doctor.ts} +0 -0
  88. /package/scripts/{generate-ontology-data.mjs → generate-ontology-data.ts} +0 -0
  89. /package/scripts/lib/{agents-turtle.mjs → agents-turtle.ts} +0 -0
  90. /package/scripts/lib/{bibliography-turtle.mjs → bibliography-turtle.ts} +0 -0
  91. /package/scripts/lib/{bibliography.mjs → bibliography.ts} +0 -0
  92. /package/scripts/lib/build/{image-assets.mjs → image-assets.ts} +0 -0
  93. /package/scripts/lib/build/{non-verbal-consumer.mjs → non-verbal-consumer.ts} +0 -0
  94. /package/scripts/lib/{build-activity-turtle.mjs → build-activity-turtle.ts} +0 -0
  95. /package/scripts/lib/{concept-groups.mjs → concept-groups.ts} +0 -0
  96. /package/scripts/lib/{first-non-empty.mjs → first-non-empty.ts} +0 -0
  97. /package/scripts/lib/{turtle-escape.mjs → turtle-escape.ts} +0 -0
  98. /package/scripts/lib/{version-turtle.mjs → version-turtle.ts} +0 -0
  99. /package/scripts/lib/{vocab-turtle.mjs → vocab-turtle.ts} +0 -0
  100. /package/scripts/{load-site-config.mjs → load-site-config.ts} +0 -0
  101. /package/scripts/{migrate-v1-to-v3.mjs → migrate-v1-to-v3.ts} +0 -0
  102. /package/scripts/{normalize-yaml.mjs → normalize-yaml.ts} +0 -0
  103. /package/scripts/{sync-concept-model.mjs → sync-concept-model.ts} +0 -0
@@ -26,6 +26,7 @@
26
26
  * The v-math directive upgrades the placeholders to Plurimath at runtime.
27
27
  */
28
28
  import { escapeHtml, escapeAttr } from './escape';
29
+ import { sanitizeUrl, isSafeUrl } from './url-safety';
29
30
  import { parseMention } from 'glossarist';
30
31
  import type { NonVerbalKind } from '../adapters/non-verbal/types';
31
32
  import { entityKindFromMentionKind } from '../adapters/non-verbal/kind';
@@ -135,7 +136,7 @@ function convertLists(text: string): string {
135
136
  items.push(m[1].trim());
136
137
  }
137
138
  if (!items.length) return _;
138
- const lis = items.map(item => `<li>${item}</li>`).join('');
139
+ const lis = items.map(item => `<li>${escapeHtml(item)}</li>`).join('');
139
140
  return `\n<ul class="concept-list">${lis}</ul>`;
140
141
  });
141
142
 
@@ -147,7 +148,7 @@ function convertLists(text: string): string {
147
148
  items.push(m[1].trim());
148
149
  }
149
150
  if (!items.length) return _;
150
- const lis = items.map(item => `<li>${item}</li>`).join('');
151
+ const lis = items.map(item => `<li>${escapeHtml(item)}</li>`).join('');
151
152
  return `\n<ol class="concept-list concept-list-ordered">${lis}</ol>`;
152
153
  });
153
154
 
@@ -225,7 +226,9 @@ function resolveMentions(text: string, opts: RenderOptions): string {
225
226
  const url = (commaIdx > 0 ? rest.slice(0, commaIdx).trim() : rest.trim());
226
227
  const label = commaIdx > 0 ? rest.slice(commaIdx + 1).trim() : url;
227
228
  if (opts.linkResolver) return opts.linkResolver(url, label);
228
- return `<a href="${escapeAttr(url)}" target="_blank" rel="noopener" class="ext-link">${escapeHtml(label)}</a>`;
229
+ const safeUrl = sanitizeUrl(url);
230
+ if (!safeUrl) return escapeHtml(label);
231
+ return `<a href="${escapeAttr(safeUrl)}" target="_blank" rel="noopener" class="ext-link">${escapeHtml(label)}</a>`;
229
232
  }
230
233
 
231
234
  const imageMatch = body.match(/^image:(.+)$/i);
@@ -235,6 +238,7 @@ function resolveMentions(text: string, opts: RenderOptions): string {
235
238
  const src = (commaIdx > 0 ? rest.slice(0, commaIdx).trim() : rest.trim());
236
239
  const alt = commaIdx > 0 ? rest.slice(commaIdx + 1).trim() : '';
237
240
  if (opts.imageResolver) return opts.imageResolver(src, alt);
241
+ if (!isSafeUrl(src)) return escapeHtml(alt || src);
238
242
  return `<img src="${escapeAttr(src)}" alt="${escapeAttr(alt)}" class="inline-image" />`;
239
243
  }
240
244
 
@@ -18,11 +18,7 @@ export function isSafeUrl(url: string): boolean {
18
18
  }
19
19
  }
20
20
 
21
- if (trimmed.startsWith('#') || trimmed.startsWith('/') || trimmed.startsWith('./') || trimmed.startsWith('../')) {
22
- return true;
23
- }
24
-
25
- return false;
21
+ return true;
26
22
  }
27
23
 
28
24
  export function sanitizeUrl(url: string): string {
package/tsconfig.json CHANGED
@@ -20,6 +20,6 @@
20
20
  },
21
21
  "baseUrl": "."
22
22
  },
23
- "include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue", "cli/**/*.ts", "env.d.ts", ".astro/types.d.ts"],
24
- "exclude": ["src/content.config.ts", "src/pages/**", "src/layouts/**", "src/astro-islands/**", "src/lib/site-config.ts"]
23
+ "include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue", "env.d.ts", ".astro/types.d.ts"],
24
+ "exclude": ["src/content.config.ts", "src/pages/**", "src/layouts/**", "src/astro-islands/**", "src/lib/site-config.ts", "src/__tests__/**", "scripts/**", "cli/**"]
25
25
  }
package/vite.config.ts CHANGED
@@ -183,7 +183,7 @@ export default defineConfig({
183
183
  globals: true,
184
184
  include: [
185
185
  'src/__tests__/**/*.test.ts',
186
- 'scripts/__tests__/**/*.test.mjs',
186
+ 'scripts/__tests__/**/*.test.ts',
187
187
  ],
188
188
  },
189
189
  })
@@ -1,167 +0,0 @@
1
- // @ts-nocheck — TODO.typescript/12: remove after glossarist TS migration
2
- import { describe, it, expect } from 'vitest';
3
- // glossarist 0.4.33 renamed PartitiveRelation → PartitiveHyperedge.
4
- // Import from glossarist/models (extended by our local augmentation).
5
- import type {
6
- PartitiveHyperedge,
7
- PartitiveMember,
8
- ConceptRef,
9
- } from 'glossarist/models';
10
- import {
11
- PartitiveHyperedge as PartitiveHyperedgeModel,
12
- PartitiveMember as PartitiveMemberModel,
13
- ConceptRef as ConceptRefModel,
14
- } from 'glossarist/models';
15
-
16
- /**
17
- * Verifies glossarist-js 0.4.26's MECE-native PartitiveRelation model
18
- * (ISO 704:2022 presence × count + is_delimiting).
19
- *
20
- * The concept-browser consumes these fields directly (no migration
21
- * bridge). These specs pin the runtime contract so a regression in
22
- * upstream is caught here.
23
- */
24
-
25
- function makeRef(source: string, id: string): ConceptRef {
26
- return ConceptRefModel.fromJSON({ source, id }) as ConceptRef;
27
- }
28
-
29
- function makePartitive(
30
- source: string,
31
- id: string,
32
- fields: { presence?: 'required' | 'optional'; count?: 'exactly_one' | 'at_least_one' | 'multiple'; isDelimiting?: boolean } = {},
33
- ): PartitiveMember {
34
- const data: {
35
- ref: ConceptRef;
36
- presence?: 'required' | 'optional';
37
- count?: 'exactly_one' | 'at_least_one' | 'multiple';
38
- is_delimiting?: boolean;
39
- } = { ref: makeRef(source, id) };
40
- if (fields.presence) data.presence = fields.presence;
41
- if (fields.count) data.count = fields.count;
42
- if (fields.isDelimiting) data.is_delimiting = fields.isDelimiting;
43
- return new (PartitiveMemberModel as unknown as new (data: unknown) => PartitiveMember)(data) as PartitiveMember;
44
- }
45
-
46
- function makeRelation(opts: {
47
- comprehensive: ConceptRef;
48
- partitives: PartitiveMember[];
49
- completeness?: 'complete' | 'partial';
50
- criterion?: Record<string, string>;
51
- }): PartitiveHyperedge {
52
- return new (PartitiveHyperedgeModel as unknown as new (data: unknown) => PartitiveHyperedge)(opts) as PartitiveHyperedge;
53
- }
54
-
55
- describe('useConceptEdges — MECE PartitiveRelation (glossarist 0.4.26)', () => {
56
- describe('PartitiveMember (ISO 704:2022 presence × count + delimiting)', () => {
57
- it('presence defaults to required, count defaults to exactly_one', () => {
58
- const m = makePartitive('test', '1.1');
59
- expect(m.presence).toBe('required');
60
- expect(m.presence === "required").toBe(true);
61
- expect(m.presence === "optional").toBe(false);
62
- });
63
-
64
- it('count defaults to exactly_one', () => {
65
- const m = makePartitive('test', '1.1');
66
- expect(m.count).toBe('exactly_one');
67
- });
68
-
69
- it('presence=optional is preserved', () => {
70
- const m = makePartitive('test', '1.1', { presence: 'optional' });
71
- expect(m.presence).toBe('optional');
72
- expect(m.presence === "optional").toBe(true);
73
- expect(m.presence === "required").toBe(false);
74
- });
75
-
76
- it('count=multiple is preserved', () => {
77
- const m = makePartitive('test', '1.1', { count: 'multiple' });
78
- expect(m.count).toBe('multiple');
79
- });
80
-
81
- it('count=at_least_one is preserved', () => {
82
- const m = makePartitive('test', '1.1', { count: 'at_least_one' });
83
- expect(m.count).toBe('at_least_one');
84
- });
85
-
86
- it('is_delimiting defaults to false', () => {
87
- const m = makePartitive('test', '1.1');
88
- expect((m as any).is_delimiting).toBe(false);
89
- expect((m as any).is_delimiting).toBe(false);
90
- });
91
-
92
- it('is_delimiting is preserved when set to true', () => {
93
- const m = makePartitive('test', '1.1', { isDelimiting: true });
94
- expect((m as any).is_delimiting).toBe(true);
95
- expect((m as any).is_delimiting).toBe(true);
96
- });
97
-
98
- it('rejects the vacuous (presence=optional, count=at_least_one)', () => {
99
- expect(() =>
100
- makePartitive('test', '1.1', { presence: 'optional', count: 'at_least_one' }),
101
- ).toThrow(/collapses to optional \+ multiple/);
102
- });
103
- });
104
-
105
- describe('PartitiveRelation', () => {
106
- it('accepts 2 members with default axes', () => {
107
- const r = makeRelation({
108
- comprehensive: makeRef('test', '1.3'),
109
- partitives: [makePartitive('test', '1.4'), makePartitive('test', '1.5')],
110
- });
111
- expect((r as any).members).toHaveLength(2);
112
- expect((r as any).completeness).toBe('complete');
113
- expect((r as any).isComplete).toBe(true);
114
- expect((r as any).isPartial).toBe(false);
115
- });
116
-
117
- it('completeness=partial is preserved', () => {
118
- const r = makeRelation({
119
- comprehensive: makeRef('test', '1.3'),
120
- partitives: [makePartitive('test', '1.4'), makePartitive('test', '1.5')],
121
- completeness: 'partial',
122
- });
123
- expect((r as any).completeness).toBe('partial');
124
- expect((r as any).isPartial).toBe(true);
125
- });
126
-
127
- it('isCoordinate true when ≥2 partitives', () => {
128
- const r = makeRelation({
129
- comprehensive: makeRef('test', '1.3'),
130
- partitives: [makePartitive('test', '1.4'), makePartitive('test', '1.5')],
131
- });
132
- expect((r as any).isCoordinate).toBe(true);
133
- });
134
-
135
- it('rejects < 2 partitives (ISO 704 requirement)', () => {
136
- expect(() => makeRelation({
137
- comprehensive: makeRef('test', '1.3'),
138
- partitives: [makePartitive('test', '1.4')],
139
- })).toThrow(/≥2 partitives/);
140
- });
141
-
142
- it('round-trips through toJSON with MECE fields', () => {
143
- const r = makeRelation({
144
- comprehensive: makeRef('test', '1.3'),
145
- partitives: [
146
- makePartitive('test', '1.4', { presence: 'required', count: 'multiple' }),
147
- makePartitive('test', '1.5', { presence: 'optional', isDelimiting: true }),
148
- ],
149
- completeness: 'partial',
150
- criterion: { eng: 'physical structure' },
151
- });
152
- const json = r.toJSON() as {
153
- comprehensive: unknown;
154
- partitives: Array<{ presence?: string; count?: string; is_delimiting?: boolean; ref: unknown }>;
155
- completeness: string;
156
- criterion?: Record<string, string>;
157
- };
158
- expect(json.comprehensive).toBeDefined();
159
- expect(json.partitives).toHaveLength(2);
160
- expect(json.partitives[0].count).toBe('multiple');
161
- expect(json.partitives[1].presence).toBe('optional');
162
- expect(json.partitives[1].is_delimiting).toBe(true);
163
- expect(json.completeness).toBe('partial');
164
- expect(json.criterion).toEqual({ eng: 'physical structure' });
165
- });
166
- });
167
- });
@@ -1,166 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
- // glossarist 0.4.33 renamed PartitiveRelation → PartitiveHyperedge.
3
- // Import from glossarist/models (extended by our local augmentation).
4
- import type {
5
- PartitiveHyperedge,
6
- PartitiveMember,
7
- ConceptRef,
8
- } from 'glossarist/models';
9
- import {
10
- PartitiveHyperedge as PartitiveHyperedgeModel,
11
- PartitiveMember as PartitiveMemberModel,
12
- ConceptRef as ConceptRefModel,
13
- } from 'glossarist/models';
14
-
15
- /**
16
- * Verifies glossarist-js 0.4.26's MECE-native PartitiveRelation model
17
- * (ISO 704:2022 presence × count + is_delimiting).
18
- *
19
- * The concept-browser consumes these fields directly (no migration
20
- * bridge). These specs pin the runtime contract so a regression in
21
- * upstream is caught here.
22
- */
23
-
24
- function makeRef(source: string, id: string): ConceptRef {
25
- return ConceptRefModel.fromJSON({ source, id }) as ConceptRef;
26
- }
27
-
28
- function makePartitive(
29
- source: string,
30
- id: string,
31
- fields: { presence?: 'required' | 'optional'; count?: 'exactly_one' | 'at_least_one' | 'multiple'; isDelimiting?: boolean } = {},
32
- ): PartitiveMember {
33
- const data: {
34
- ref: ConceptRef;
35
- presence?: 'required' | 'optional';
36
- count?: 'exactly_one' | 'at_least_one' | 'multiple';
37
- is_delimiting?: boolean;
38
- } = { ref: makeRef(source, id) };
39
- if (fields.presence) data.presence = fields.presence;
40
- if (fields.count) data.count = fields.count;
41
- if (fields.isDelimiting) data.is_delimiting = fields.isDelimiting;
42
- return PartitiveMemberModel.fromJSON(data as unknown as Record<string, unknown>) as PartitiveMember;
43
- }
44
-
45
- function makeRelation(opts: {
46
- comprehensive: ConceptRef;
47
- partitives: PartitiveMember[];
48
- completeness?: 'complete' | 'partial';
49
- criterion?: Record<string, string>;
50
- }): PartitiveHyperedge {
51
- return PartitiveHyperedgeModel.fromJSON(opts as unknown as Record<string, unknown>) as PartitiveHyperedge;
52
- }
53
-
54
- describe('useConceptEdges — MECE PartitiveRelation (glossarist 0.4.26)', () => {
55
- describe('PartitiveMember (ISO 704:2022 presence × count + delimiting)', () => {
56
- it('presence defaults to required, count defaults to exactly_one', () => {
57
- const m = makePartitive('test', '1.1');
58
- expect(m.presence).toBe('required');
59
- expect(m.presence === "required").toBe(true);
60
- expect(m.presence === "optional").toBe(false);
61
- });
62
-
63
- it('count defaults to exactly_one', () => {
64
- const m = makePartitive('test', '1.1');
65
- expect(m.count).toBe('exactly_one');
66
- });
67
-
68
- it('presence=optional is preserved', () => {
69
- const m = makePartitive('test', '1.1', { presence: 'optional' });
70
- expect(m.presence).toBe('optional');
71
- expect(m.presence === "optional").toBe(true);
72
- expect(m.presence === "required").toBe(false);
73
- });
74
-
75
- it('count=multiple is preserved', () => {
76
- const m = makePartitive('test', '1.1', { count: 'multiple' });
77
- expect(m.count).toBe('multiple');
78
- });
79
-
80
- it('count=at_least_one is preserved', () => {
81
- const m = makePartitive('test', '1.1', { count: 'at_least_one' });
82
- expect(m.count).toBe('at_least_one');
83
- });
84
-
85
- it('is_delimiting defaults to false', () => {
86
- const m = makePartitive('test', '1.1');
87
- expect((m as any).is_delimiting).toBe(false);
88
- expect((m as any).is_delimiting).toBe(false);
89
- });
90
-
91
- it('is_delimiting is preserved when set to true', () => {
92
- const m = makePartitive('test', '1.1', { isDelimiting: true });
93
- expect((m as any).is_delimiting).toBe(true);
94
- expect((m as any).is_delimiting).toBe(true);
95
- });
96
-
97
- it('rejects the vacuous (presence=optional, count=at_least_one)', () => {
98
- expect(() =>
99
- makePartitive('test', '1.1', { presence: 'optional', count: 'at_least_one' }),
100
- ).toThrow(/collapses to optional \+ multiple/);
101
- });
102
- });
103
-
104
- describe('PartitiveRelation', () => {
105
- it('accepts 2 members with default axes', () => {
106
- const r = makeRelation({
107
- comprehensive: makeRef('test', '1.3'),
108
- partitives: [makePartitive('test', '1.4'), makePartitive('test', '1.5')],
109
- });
110
- expect((r as any).members).toHaveLength(2);
111
- expect((r as any).completeness).toBe('complete');
112
- expect((r as any).isComplete).toBe(true);
113
- expect((r as any).isPartial).toBe(false);
114
- });
115
-
116
- it('completeness=partial is preserved', () => {
117
- const r = makeRelation({
118
- comprehensive: makeRef('test', '1.3'),
119
- partitives: [makePartitive('test', '1.4'), makePartitive('test', '1.5')],
120
- completeness: 'partial',
121
- });
122
- expect((r as any).completeness).toBe('partial');
123
- expect((r as any).isPartial).toBe(true);
124
- });
125
-
126
- it('isCoordinate true when ≥2 partitives', () => {
127
- const r = makeRelation({
128
- comprehensive: makeRef('test', '1.3'),
129
- partitives: [makePartitive('test', '1.4'), makePartitive('test', '1.5')],
130
- });
131
- expect((r as any).isCoordinate).toBe(true);
132
- });
133
-
134
- it('rejects < 2 partitives (ISO 704 requirement)', () => {
135
- expect(() => makeRelation({
136
- comprehensive: makeRef('test', '1.3'),
137
- partitives: [makePartitive('test', '1.4')],
138
- })).toThrow(/≥2 partitives/);
139
- });
140
-
141
- it('round-trips through toJSON with MECE fields', () => {
142
- const r = makeRelation({
143
- comprehensive: makeRef('test', '1.3'),
144
- partitives: [
145
- makePartitive('test', '1.4', { presence: 'required', count: 'multiple' }),
146
- makePartitive('test', '1.5', { presence: 'optional', isDelimiting: true }),
147
- ],
148
- completeness: 'partial',
149
- criterion: { eng: 'physical structure' },
150
- });
151
- const json = r.toJSON() as {
152
- comprehensive: unknown;
153
- partitives: Array<{ presence?: string; count?: string; is_delimiting?: boolean; ref: unknown }>;
154
- completeness: string;
155
- criterion?: Record<string, string>;
156
- };
157
- expect(json.comprehensive).toBeDefined();
158
- expect(json.partitives).toHaveLength(2);
159
- expect(json.partitives[0].count).toBe('multiple');
160
- expect(json.partitives[1].presence).toBe('optional');
161
- expect(json.partitives[1].is_delimiting).toBe(true);
162
- expect(json.completeness).toBe('partial');
163
- expect(json.criterion).toEqual({ eng: 'physical structure' });
164
- });
165
- });
166
- });
@@ -1,27 +0,0 @@
1
- declare module '*/scripts/lib/agents-turtle.mjs' {
2
- export function buildAgentsTurtle(
3
- contributors: readonly { name: string; role?: string; organization?: string; url?: string; email?: string }[],
4
- agentBase?: string,
5
- ): string;
6
- }
7
-
8
- declare module '*/scripts/lib/version-turtle.mjs' {
9
- export interface VersionInput {
10
- readonly registerId: string;
11
- readonly version: string;
12
- readonly versionIri: string;
13
- readonly datasetIri: string;
14
- readonly generatedAt: string;
15
- readonly previousVersionIri?: string;
16
- readonly changeSummary?: string;
17
- readonly associatedAgentIri?: string;
18
- }
19
- export interface VersionHistoryInput {
20
- readonly registerId: string;
21
- readonly datasetIri: string;
22
- readonly versions: readonly { version: string; generatedAt: string; changeSummary?: string }[];
23
- readonly associatedAgentIri?: string;
24
- }
25
- export function buildVersionTurtle(input: VersionInput): string;
26
- export function buildVersionHistoryTurtle(input: VersionHistoryInput): string;
27
- }
@@ -1,12 +0,0 @@
1
- declare module '*/scripts/lib/bibliography-turtle.mjs' {
2
- export interface BibliographyEntryJson {
3
- readonly reference?: string;
4
- readonly title?: string;
5
- readonly link?: string;
6
- }
7
- export function buildBibliographyTurtle(
8
- register: string,
9
- bibliographyJson: Record<string, BibliographyEntryJson>,
10
- baseUri?: string,
11
- ): string;
12
- }
@@ -1,16 +0,0 @@
1
- declare module '*/scripts/lib/build-activity-turtle.mjs' {
2
- export interface BuildActivityTurtleInput {
3
- readonly runId: string;
4
- readonly startedAt: string;
5
- readonly endedAt: string;
6
- readonly gitSha?: string | null;
7
- readonly gitBranch?: string | null;
8
- readonly toolId: string;
9
- readonly toolVersion: string;
10
- readonly datasetRegisters: readonly string[];
11
- readonly conceptCount: number;
12
- readonly associatedAgentIri?: string | null;
13
- }
14
-
15
- export function buildActivityTurtle(input: BuildActivityTurtleInput): string;
16
- }
@@ -1,20 +0,0 @@
1
- declare module '*/scripts/lib/build-cache.mjs' {
2
- export interface BuildCacheEntry<T = unknown> {
3
- hash: string;
4
- value: T;
5
- storedAt: string;
6
- }
7
-
8
- export class BuildCache {
9
- constructor(cacheDir: string);
10
- get<T = unknown>(key: string): Promise<BuildCacheEntry<T> | null>;
11
- set<T = unknown>(key: string, value: T, hash: string): Promise<void>;
12
- readThrough<T>(
13
- key: string,
14
- hash: string,
15
- producer: () => Promise<T>,
16
- ): Promise<{ value: T; hit: boolean }>;
17
- pathFor(key: string): string;
18
- static hash(input: string): string;
19
- }
20
- }
@@ -1,32 +0,0 @@
1
- declare module '*/scripts/lib/dataset-turtle.mjs' {
2
- export interface DatasetDistribution {
3
- readonly id: string;
4
- readonly title: string;
5
- readonly mediaType: string;
6
- readonly downloadUrl: string;
7
- readonly byteSize?: number;
8
- }
9
-
10
- export interface DatasetSection {
11
- readonly collectionIri: string;
12
- readonly title: string;
13
- readonly memberUris: readonly string[];
14
- }
15
-
16
- export interface DatasetTurtleInput {
17
- readonly datasetIri: string;
18
- readonly registerId: string;
19
- readonly title: string;
20
- readonly description?: string;
21
- readonly modified: string;
22
- readonly languages: readonly string[];
23
- readonly distributions: readonly DatasetDistribution[];
24
- readonly topConceptUris: readonly string[];
25
- readonly sections: readonly DatasetSection[];
26
- readonly sourceRepoUrl?: string;
27
- readonly publisherIri?: string;
28
- readonly contactIri?: string;
29
- }
30
-
31
- export function buildDatasetTurtle(input: DatasetTurtleInput): string;
32
- }
@@ -1,16 +0,0 @@
1
- declare module '*/scripts/normalize-yaml.mjs' {
2
- export interface NormalizeResult {
3
- checked: number;
4
- nonNfc: number;
5
- fixed: string[];
6
- check: boolean;
7
- }
8
-
9
- export interface NormalizeOptions {
10
- root?: string;
11
- check?: boolean;
12
- paths?: string[];
13
- }
14
-
15
- export function normalizeYaml(options?: NormalizeOptions): NormalizeResult;
16
- }
@@ -1,6 +0,0 @@
1
- declare module '*/scripts/lib/turtle-escape.mjs' {
2
- export function ttlLit(s: unknown): string;
3
- export function ttlPrefixed(qname: string): string;
4
- export function ttlIri(iri: string): string;
5
- export function assertValidIri(iri: unknown, context?: string): string;
6
- }
@@ -1,13 +0,0 @@
1
- declare module '*/scripts/lib/vocab-turtle.mjs' {
2
- export interface VocabTerm {
3
- readonly iri: string;
4
- readonly label: string;
5
- }
6
- export interface VocabScheme {
7
- readonly schemeIri: string;
8
- readonly label: string;
9
- readonly terms: readonly VocabTerm[];
10
- }
11
- export function buildVocabularyTurtle(): string;
12
- export function listVocabSchemes(): readonly VocabScheme[];
13
- }
File without changes