@glossarist/concept-browser 0.7.100 → 0.7.102

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 (58) hide show
  1. package/cli/index.mjs +14 -251
  2. package/cli/index.ts +289 -0
  3. package/package.json +3 -2
  4. package/src/__tests__/__fixtures__/concepts.ts +3 -3
  5. package/src/__tests__/components/learn/learn-card-dimension-card.test.ts +112 -0
  6. package/src/__tests__/composables/use-concept-edges-v2.test.ts +1 -0
  7. package/src/__tests__/designation-relationship.test.ts +2 -2
  8. package/src/__tests__/perf/serialization-perf.test.ts +1 -0
  9. package/src/__tests__/use-rdf-document.test.ts +1 -0
  10. package/src/__tests__/views/learn/learn-designations-view.test.ts +84 -0
  11. package/src/__tests__/views/learn/learn-index-view.test.ts +45 -0
  12. package/src/__tests__/views/learn/learn-relationships-view.test.ts +83 -0
  13. package/src/__tests__/views/learn/learn-statuses-view.test.ts +60 -0
  14. package/src/adapters/bibliography-adapter.ts +1 -1
  15. package/src/adapters/model-bridge.ts +15 -15
  16. package/src/adapters/non-verbal/figure-bridge.ts +2 -2
  17. package/src/adapters/non-verbal/formula-bridge.ts +2 -2
  18. package/src/adapters/non-verbal/table-bridge.ts +2 -2
  19. package/src/components/CitationDisplay.vue +1 -1
  20. package/src/components/ConceptDetail.vue +2 -2
  21. package/src/components/ConceptDiffModal.vue +2 -2
  22. package/src/components/ConceptDiffView.vue +1 -1
  23. package/src/components/DesignationList.vue +4 -4
  24. package/src/components/NavIcon.vue +1 -0
  25. package/src/components/RelationSphere.vue +1 -1
  26. package/src/components/figure/FigureDisplay.vue +5 -5
  27. package/src/components/formula/FormulaDisplay.vue +4 -4
  28. package/src/components/learn/DimensionCard.vue +71 -0
  29. package/src/components/learn/LearnCard.vue +59 -0
  30. package/src/components/table/TableDisplay.vue +3 -3
  31. package/src/components/use-rdf-document.ts +16 -7
  32. package/src/composables/use-concept-content.ts +3 -3
  33. package/src/composables/use-concept-diff.ts +2 -2
  34. package/src/composables/use-concept-edges.ts +3 -3
  35. package/src/config/page-types.ts +1 -1
  36. package/src/i18n/locales/eng.yml +114 -9
  37. package/src/i18n/locales/fra.yml +114 -9
  38. package/src/layouts/Default.astro +32 -2
  39. package/src/router/index.ts +21 -2
  40. package/src/utils/content-renderer.ts +1 -1
  41. package/src/utils/partitive-multiplicity.ts +3 -2
  42. package/src/views/ConceptView.vue +3 -3
  43. package/src/views/learn/LearnDesignationsView.vue +190 -0
  44. package/src/views/learn/LearnIndexView.vue +75 -0
  45. package/src/views/learn/LearnRelationshipsView.vue +317 -0
  46. package/src/views/learn/LearnStatusesView.vue +114 -0
  47. package/tailwind.config.js +3 -3
  48. package/tsconfig.json +1 -1
  49. package/src/__tests__/views/relation-types-view.test.ts +0 -85
  50. package/src/adapters/model-bridge/bridges.ts +0 -159
  51. package/src/adapters/model-bridge/concept.ts +0 -97
  52. package/src/adapters/model-bridge/entity-refs.ts +0 -49
  53. package/src/adapters/model-bridge/hyperedge.ts +0 -103
  54. package/src/adapters/model-bridge/index.ts +0 -46
  55. package/src/adapters/model-bridge/mappers.ts +0 -277
  56. package/src/adapters/model-bridge/partitive.ts +0 -79
  57. package/src/adapters/non-verbal/glossarist-augment.d.ts +0 -339
  58. /package/src/adapters/{model-bridge/jsonld-types.ts → jsonld-types.ts} +0 -0
@@ -0,0 +1,112 @@
1
+ import { describe, it, expect, beforeEach } from 'vitest';
2
+ import { mount } from '@vue/test-utils';
3
+ import { createPinia, setActivePinia } from 'pinia';
4
+ import LearnCard from '../../../components/learn/LearnCard.vue';
5
+ import DimensionCard from '../../../components/learn/DimensionCard.vue';
6
+
7
+ describe('LearnCard', () => {
8
+ beforeEach(() => setActivePinia(createPinia()));
9
+
10
+ it('renders the title and ISO reference', () => {
11
+ const w = mount(LearnCard, {
12
+ props: { title: 'partitive relation', isoReference: 'ISO 704:2022 §5.5.4.3' },
13
+ });
14
+ expect(w.find('h3').text()).toBe('partitive relation');
15
+ expect(w.find('.font-mono').text()).toBe('ISO 704:2022 §5.5.4.3');
16
+ });
17
+
18
+ it('renders the body slot', () => {
19
+ const w = mount(LearnCard, {
20
+ props: { title: 'x' },
21
+ slots: { default: '<p>Body content</p>' },
22
+ });
23
+ expect(w.find('.learn-card-body').html()).toContain('Body content');
24
+ });
25
+
26
+ it('renders the example slot in its own section', () => {
27
+ const w = mount(LearnCard, {
28
+ props: { title: 'x' },
29
+ slots: { example: 'VIM 1.9 measurement unit' },
30
+ });
31
+ expect(w.find('.learn-card-example').text()).toContain('VIM 1.9');
32
+ });
33
+
34
+ it('omits the "learn more" footer when learnMoreUrl is absent', () => {
35
+ const w = mount(LearnCard, { props: { title: 'x' } });
36
+ expect(w.find('.learn-card-footer').exists()).toBe(false);
37
+ });
38
+
39
+ it('renders the learn-more footer when learnMoreUrl is present', () => {
40
+ const w = mount(LearnCard, {
41
+ props: { title: 'x', learnMoreUrl: 'https://www.glossarist.org/model/x' },
42
+ });
43
+ const link = w.find('.learn-card-footer a');
44
+ expect(link.attributes('href')).toBe('https://www.glossarist.org/model/x');
45
+ expect(link.attributes('target')).toBe('_blank');
46
+ expect(link.attributes('rel')).toBe('noopener');
47
+ });
48
+
49
+ it('binds the id prop for deep-link anchors', () => {
50
+ const w = mount(LearnCard, {
51
+ props: { id: '__partitive__', title: 'x' },
52
+ });
53
+ expect(w.find('article').attributes('id')).toBe('__partitive__');
54
+ });
55
+ });
56
+
57
+ describe('DimensionCard', () => {
58
+ beforeEach(() => setActivePinia(createPinia()));
59
+
60
+ it('renders one row per value', () => {
61
+ const w = mount(DimensionCard, {
62
+ props: {
63
+ name: 'Presence',
64
+ summary: 'Compulsory or optional',
65
+ values: [
66
+ { value: 'required', label: 'required', meaning: 'must be present' },
67
+ { value: 'optional', label: 'optional', meaning: 'may be absent' },
68
+ ],
69
+ },
70
+ });
71
+ expect(w.findAll('.dimension-value-row').length).toBe(2);
72
+ });
73
+
74
+ it('renders value labels and meanings', () => {
75
+ const w = mount(DimensionCard, {
76
+ props: {
77
+ name: 'x',
78
+ summary: 's',
79
+ values: [{ value: 'required', label: 'required', meaning: 'must be present' }],
80
+ },
81
+ });
82
+ const row = w.find('.dimension-value-row');
83
+ expect(row.text()).toContain('required');
84
+ expect(row.text()).toContain('must be present');
85
+ });
86
+
87
+ it('exposes a visual scoped slot per value', () => {
88
+ const w = mount(DimensionCard, {
89
+ props: {
90
+ name: 'x',
91
+ summary: 's',
92
+ values: [{ value: 'required', label: 'required', meaning: 'm' }],
93
+ },
94
+ slots: {
95
+ visual: '<svg data-test="custom-visual" />',
96
+ },
97
+ });
98
+ expect(w.find('svg[data-test="custom-visual"]').exists()).toBe(true);
99
+ });
100
+
101
+ it('binds the id prop for deep-link anchors', () => {
102
+ const w = mount(DimensionCard, {
103
+ props: {
104
+ id: 'partitive-presence',
105
+ name: 'x',
106
+ summary: 's',
107
+ values: [{ value: 'required', label: 'required', meaning: 'm' }],
108
+ },
109
+ });
110
+ expect(w.find('article').attributes('id')).toBe('partitive-presence');
111
+ });
112
+ });
@@ -1,3 +1,4 @@
1
+ // @ts-nocheck — TODO.typescript/12: remove after glossarist TS migration
1
2
  import { describe, it, expect } from 'vitest';
2
3
  // glossarist 0.4.33 renamed PartitiveRelation → PartitiveHyperedge.
3
4
  // Import from glossarist/models (extended by our local augmentation).
@@ -197,8 +197,8 @@ describe('designation target matching robustness', () => {
197
197
  // Terms are in model order, not raw order
198
198
  const pdf = lc.terms.find(t => t.designation === 'PDF')!;
199
199
  const iso = lc.terms.find(t => t.designation === 'ISO')!;
200
- expect(getDesignationTarget(pdf.related[0])).toBe('Portable Document Format');
201
- expect(getDesignationTarget(iso.related[0])).toBe('International Organization for Standardization');
200
+ expect(getDesignationTarget(pdf.related[0] as any)).toBe('Portable Document Format');
201
+ expect(getDesignationTarget(iso.related[0] as any)).toBe('International Organization for Standardization');
202
202
  });
203
203
  });
204
204
 
@@ -1,3 +1,4 @@
1
+ // @ts-nocheck — TODO.typescript/12: remove after glossarist TS migration
1
2
  import { describe, it, expect } from 'vitest';
2
3
  import { Concept } from 'glossarist';
3
4
  import { conceptToQuads, collectQuads, writeTurtleSync, PREFIXES } from 'glossarist/rdf';
@@ -1,3 +1,4 @@
1
+ // @ts-nocheck — TODO.typescript/12: remove after glossarist TS migration
1
2
  import { describe, it, expect } from 'vitest';
2
3
  import { ref } from 'vue';
3
4
  import { Concept } from 'glossarist';
@@ -0,0 +1,84 @@
1
+ import { describe, it, expect, beforeEach } from 'vitest';
2
+ import { mount } from '@vue/test-utils';
3
+ import { createPinia, setActivePinia } from 'pinia';
4
+ import { createRouter, createMemoryHistory } from 'vue-router';
5
+ import LearnDesignationsView from '../../../views/learn/LearnDesignationsView.vue';
6
+
7
+ async function makeRouter() {
8
+ const router = createRouter({
9
+ history: createMemoryHistory(),
10
+ routes: [
11
+ { path: '/', name: 'home', component: { template: '<div/>' } },
12
+ { path: '/learn/designations', name: 'learn-designations', component: LearnDesignationsView },
13
+ ],
14
+ });
15
+ await router.push('/learn/designations');
16
+ await router.isReady();
17
+ return router;
18
+ }
19
+
20
+ describe('LearnDesignationsView', () => {
21
+ beforeEach(() => setActivePinia(createPinia()));
22
+
23
+ it('renders the page header', async () => {
24
+ const router = await makeRouter();
25
+ const w = mount(LearnDesignationsView, { global: { plugins: [router] } });
26
+ expect(w.find('h1').text()).toContain('Designations');
27
+ });
28
+
29
+ it('renders three top-level sections (normative, kinds, types)', async () => {
30
+ const router = await makeRouter();
31
+ const w = mount(LearnDesignationsView, { global: { plugins: [router] } });
32
+ const sections = w.findAll('section[aria-label]');
33
+ expect(sections.length).toBe(3);
34
+ });
35
+
36
+ it('normative section has all three ratings (preferred/admitted/deprecated)', async () => {
37
+ const router = await makeRouter();
38
+ const w = mount(LearnDesignationsView, { global: { plugins: [router] } });
39
+ const normative = w.find('section[aria-label="Normative status"]');
40
+ const cardTitles = normative.findAll('h3').map(h => h.text());
41
+ expect(cardTitles).toEqual(expect.arrayContaining([
42
+ 'preferred term', 'admitted term', 'deprecated term',
43
+ ]));
44
+ });
45
+
46
+ it('each normative card notes "there can be more than one"', async () => {
47
+ /* The user's specific concern: explain that there can be multiple. */
48
+ const router = await makeRouter();
49
+ const w = mount(LearnDesignationsView, { global: { plugins: [router] } });
50
+ const normative = w.find('section[aria-label="Normative status"]');
51
+ const notes = normative.findAll('.italic').map(i => i.text().toLowerCase());
52
+ expect(notes.filter(t => t.includes('more than one')).length).toBe(3);
53
+ });
54
+
55
+ it('kinds section renders all four kinds (full form, abbreviated, borrowed, variant)', async () => {
56
+ const router = await makeRouter();
57
+ const w = mount(LearnDesignationsView, { global: { plugins: [router] } });
58
+ const kinds = w.find('section[aria-label="Kinds and forms of term"]');
59
+ const titles = kinds.findAll('h3').map(h => h.text());
60
+ expect(titles).toEqual(expect.arrayContaining([
61
+ 'full form', 'abbreviated form', 'borrowed term', 'variant',
62
+ ]));
63
+ });
64
+
65
+ it('types section renders term, acronym, initialism, symbol', async () => {
66
+ const router = await makeRouter();
67
+ const w = mount(LearnDesignationsView, { global: { plugins: [router] } });
68
+ const types = w.find('section[aria-label="Designation types"]');
69
+ const titles = types.findAll('h3').map(h => h.text());
70
+ expect(titles).toEqual(expect.arrayContaining([
71
+ 'term (expression)', 'acronym', 'initialism', 'symbol',
72
+ ]));
73
+ });
74
+
75
+ it('every example slot mentions a concrete ISO example', async () => {
76
+ const router = await makeRouter();
77
+ const w = mount(LearnDesignationsView, { global: { plugins: [router] } });
78
+ const examples = w.findAll('.learn-card-example').map(e => e.text());
79
+ expect(examples.some(t => t.includes('compact disc'))).toBe(true);
80
+ expect(examples.some(t => t.includes('WWW'))).toBe(true);
81
+ expect(examples.some(t => t.includes('laser'))).toBe(true);
82
+ expect(examples.some(t => t.includes('OIML'))).toBe(true);
83
+ });
84
+ });
@@ -0,0 +1,45 @@
1
+ import { describe, it, expect, beforeEach } from 'vitest';
2
+ import { mount } from '@vue/test-utils';
3
+ import { createPinia, setActivePinia } from 'pinia';
4
+ import { createRouter, createMemoryHistory } from 'vue-router';
5
+ import LearnIndexView from '../../../views/learn/LearnIndexView.vue';
6
+
7
+ async function makeRouter() {
8
+ const router = createRouter({
9
+ history: createMemoryHistory(),
10
+ routes: [
11
+ { path: '/', name: 'home', component: { template: '<div/>' } },
12
+ { path: '/learn', name: 'learn', component: LearnIndexView },
13
+ { path: '/learn/relationships', name: 'learn-relationships', component: { template: '<div/>' } },
14
+ { path: '/learn/designations', name: 'learn-designations', component: { template: '<div/>' } },
15
+ { path: '/learn/statuses', name: 'learn-statuses', component: { template: '<div/>' } },
16
+ ],
17
+ });
18
+ await router.push('/learn');
19
+ await router.isReady();
20
+ return router;
21
+ }
22
+
23
+ describe('LearnIndexView', () => {
24
+ beforeEach(() => setActivePinia(createPinia()));
25
+
26
+ it('renders the page header with translated title', async () => {
27
+ const router = await makeRouter();
28
+ const w = mount(LearnIndexView, { global: { plugins: [router] } });
29
+ expect(w.find('h1').text()).toContain('Learn');
30
+ });
31
+
32
+ it('renders exactly 3 topic cards for Phase 1', async () => {
33
+ const router = await makeRouter();
34
+ const w = mount(LearnIndexView, { global: { plugins: [router] } });
35
+ /* Each RouterLink wraps a LearnCard. Phase 1 = 3 topics. */
36
+ expect(w.findAllComponents({ name: 'LearnCard' }).length).toBe(3);
37
+ });
38
+
39
+ it('each topic card links to one of the Phase 1 routes', async () => {
40
+ const router = await makeRouter();
41
+ const w = mount(LearnIndexView, { global: { plugins: [router] } });
42
+ const links = w.findAll('a').map(a => a.attributes('href'));
43
+ expect(links).toEqual(expect.arrayContaining(['/learn/relationships', '/learn/designations', '/learn/statuses']));
44
+ });
45
+ });
@@ -0,0 +1,83 @@
1
+ import { describe, it, expect, beforeEach } from 'vitest';
2
+ import { mount } from '@vue/test-utils';
3
+ import { createPinia, setActivePinia } from 'pinia';
4
+ import { createRouter, createMemoryHistory } from 'vue-router';
5
+ import LearnRelationshipsView from '../../../views/learn/LearnRelationshipsView.vue';
6
+
7
+ async function makeRouter() {
8
+ const router = createRouter({
9
+ history: createMemoryHistory(),
10
+ routes: [
11
+ { path: '/', name: 'home', component: { template: '<div/>' } },
12
+ { path: '/learn/relationships', name: 'learn-relationships', component: LearnRelationshipsView },
13
+ ],
14
+ });
15
+ await router.push('/learn/relationships');
16
+ await router.isReady();
17
+ return router;
18
+ }
19
+
20
+ describe('LearnRelationshipsView', () => {
21
+ beforeEach(() => setActivePinia(createPinia()));
22
+
23
+ it('renders the page header with translated title', async () => {
24
+ const router = await makeRouter();
25
+ const w = mount(LearnRelationshipsView, { global: { plugins: [router] } });
26
+ expect(w.find('h1').text()).toContain('Relationships');
27
+ });
28
+
29
+ it('renders three top-level sections (bilateral, partitive, general)', async () => {
30
+ const router = await makeRouter();
31
+ const w = mount(LearnRelationshipsView, { global: { plugins: [router] } });
32
+ const sections = w.findAll('section[aria-label]');
33
+ expect(sections.length).toBe(3);
34
+ });
35
+
36
+ it('renders the hyperedge cards with the expected anchors', async () => {
37
+ const router = await makeRouter();
38
+ const w = mount(LearnRelationshipsView, { global: { plugins: [router] } });
39
+ const ids = w.findAll('.learn-card[id]').map(c => c.attributes('id'));
40
+ expect(ids).toContain('__partitive__');
41
+ expect(ids).toContain('__generic__');
42
+ });
43
+
44
+ it('renders three DimensionCards for the partitive dimensions', async () => {
45
+ const router = await makeRouter();
46
+ const w = mount(LearnRelationshipsView, { global: { plugins: [router] } });
47
+ const dimIds = w.findAll('.dimension-card[id]').map(c => c.attributes('id'));
48
+ expect(dimIds).toEqual(expect.arrayContaining(['partitive-presence', 'partitive-count', 'partitive-delimiting']));
49
+ });
50
+
51
+ it('renders the bilateral section grouped by category', async () => {
52
+ const router = await makeRouter();
53
+ const w = mount(LearnRelationshipsView, { global: { plugins: [router] } });
54
+ const bilateralArticles = w.find('section[aria-label="Bilateral relations (one-to-one)"]').findAll('article');
55
+ expect(bilateralArticles.length).toBeGreaterThanOrEqual(5);
56
+ });
57
+
58
+ it('every bilateral type row has an id anchor matching the type key', async () => {
59
+ const router = await makeRouter();
60
+ const w = mount(LearnRelationshipsView, { global: { plugins: [router] } });
61
+ const rows = w.findAll('.bilateral-row');
62
+ expect(rows.length).toBeGreaterThan(10);
63
+ for (const row of rows) {
64
+ const id = row.attributes('id');
65
+ expect(id).toBeTruthy();
66
+ expect(id).toMatch(/^[a-z_]+$/);
67
+ }
68
+ });
69
+
70
+ it('renders a live diagram for each hyperedge card', async () => {
71
+ const router = await makeRouter();
72
+ const w = mount(LearnRelationshipsView, { global: { plugins: [router] } });
73
+ expect(w.findAll('svg.hyperedge-diagram').length).toBe(2);
74
+ });
75
+
76
+ it('renders ISO references on each hyperedge card', async () => {
77
+ const router = await makeRouter();
78
+ const w = mount(LearnRelationshipsView, { global: { plugins: [router] } });
79
+ const isoRefs = w.findAll('.learn-card span.font-mono').map(s => s.text());
80
+ expect(isoRefs.some(t => t.includes('ISO 704:2022 §5.5.4.3'))).toBe(true);
81
+ expect(isoRefs.some(t => t.includes('ISO 704:2022 §5.5.4.2'))).toBe(true);
82
+ });
83
+ });
@@ -0,0 +1,60 @@
1
+ import { describe, it, expect, beforeEach } from 'vitest';
2
+ import { mount } from '@vue/test-utils';
3
+ import { createPinia, setActivePinia } from 'pinia';
4
+ import { createRouter, createMemoryHistory } from 'vue-router';
5
+ import LearnStatusesView from '../../../views/learn/LearnStatusesView.vue';
6
+
7
+ async function makeRouter() {
8
+ const router = createRouter({
9
+ history: createMemoryHistory(),
10
+ routes: [
11
+ { path: '/', name: 'home', component: { template: '<div/>' } },
12
+ { path: '/learn/statuses', name: 'learn-statuses', component: LearnStatusesView },
13
+ { path: '/learn/designations', name: 'learn-designations', component: { template: '<div/>' } },
14
+ ],
15
+ });
16
+ await router.push('/learn/statuses');
17
+ await router.isReady();
18
+ return router;
19
+ }
20
+
21
+ describe('LearnStatusesView', () => {
22
+ beforeEach(() => setActivePinia(createPinia()));
23
+
24
+ it('renders the page header', async () => {
25
+ const router = await makeRouter();
26
+ const w = mount(LearnStatusesView, { global: { plugins: [router] } });
27
+ expect(w.find('h1').text()).toContain('Statuses');
28
+ });
29
+
30
+ it('renders three top-level sections (entry, normative, source)', async () => {
31
+ const router = await makeRouter();
32
+ const w = mount(LearnStatusesView, { global: { plugins: [router] } });
33
+ const sections = w.findAll('section[aria-label]');
34
+ expect(sections.length).toBe(3);
35
+ });
36
+
37
+ it('entry section renders all four entry statuses with anchors', async () => {
38
+ const router = await makeRouter();
39
+ const w = mount(LearnStatusesView, { global: { plugins: [router] } });
40
+ const entrySection = w.find('section[aria-label="Entry status"]');
41
+ const ids = entrySection.findAll('.learn-card[id]').map(c => c.attributes('id'));
42
+ expect(ids).toEqual(expect.arrayContaining(['entry-valid', 'entry-withdrawn', 'entry-draft', 'entry-superseded']));
43
+ });
44
+
45
+ it('normative section cross-links to /learn/designations', async () => {
46
+ const router = await makeRouter();
47
+ const w = mount(LearnStatusesView, { global: { plugins: [router] } });
48
+ const normative = w.find('section[aria-label="Normative status"]');
49
+ const link = normative.find('a');
50
+ expect(link.attributes('href')).toBe('/learn/designations');
51
+ });
52
+
53
+ it('source section renders the four source statuses with anchors', async () => {
54
+ const router = await makeRouter();
55
+ const w = mount(LearnStatusesView, { global: { plugins: [router] } });
56
+ const sourceSection = w.find('section[aria-label="Source status"]');
57
+ const ids = sourceSection.findAll('.learn-card[id]').map(c => c.attributes('id'));
58
+ expect(ids).toEqual(expect.arrayContaining(['source-identical', 'source-modified', 'source-restyled', 'source-lineage']));
59
+ });
60
+ });
@@ -38,7 +38,7 @@ export class BibliographyAdapter {
38
38
  return this.data?.find(id) ?? null;
39
39
  }
40
40
 
41
- all(): BibliographyEntry[] {
41
+ all(): readonly BibliographyEntry[] {
42
42
  return this.data?.entries ?? [];
43
43
  }
44
44
 
@@ -61,7 +61,7 @@ import type {
61
61
  JsonLdPartitiveMember,
62
62
  JsonLdPartitiveRelation,
63
63
  JsonLdConcept,
64
- } from './model-bridge/jsonld-types';
64
+ } from './jsonld-types';
65
65
 
66
66
  // ── Bridges for fields not yet in glossarist-js ────────────────────────────
67
67
  // Remove each bridge when glossarist-js publishes native support.
@@ -173,7 +173,7 @@ function attachBridges(concept: Concept, localizations: Record<string, unknown>)
173
173
  * instances, since RelatedConcept.fromJSON only reads type/content/ref.
174
174
  */
175
175
  function attachRelatedBridges(
176
- modelRelated: Array<{ type?: string | null; content?: Record<string, string> | string | null; ref?: any; related?: any[] }>,
176
+ modelRelated: ReadonlyArray<{ type?: string | null; content?: Record<string, string> | string | null; ref?: any; related?: ReadonlyArray<any> }>,
177
177
  rawRelated: unknown[],
178
178
  ): void {
179
179
  for (const rawRel of rawRelated) {
@@ -241,7 +241,7 @@ function mapDesignationFromJsonLd(d: JsonLdDesignation): Record<string, unknown>
241
241
 
242
242
  const pronunciation = d[GL.PRONUNCIATION];
243
243
  if (pronunciation?.length) {
244
- result.pronunciation = pronunciation.map(p => ({
244
+ result.pronunciation = pronunciation.map((p: any) => ({
245
245
  content: p[GL.CONTENT] ?? null,
246
246
  language: p[GL.LANGUAGE] ?? null,
247
247
  script: p[GL.SCRIPT] ?? null,
@@ -257,7 +257,7 @@ function mapDesignationFromJsonLd(d: JsonLdDesignation): Record<string, unknown>
257
257
 
258
258
  const related = d[GL.RELATED];
259
259
  if (related?.length) {
260
- result.related = related.map(r => {
260
+ result.related = related.map((r: any) => {
261
261
  const relType = r[GL.RELATIONSHIP_TYPE] ?? 'references';
262
262
  if (DESIGNATION_REL_TYPES.has(relType) && r[GL.TARGET]) {
263
263
  return { type: relType, target: r[GL.TARGET] };
@@ -272,7 +272,7 @@ function mapDesignationFromJsonLd(d: JsonLdDesignation): Record<string, unknown>
272
272
  }
273
273
  const grammarInfo = d[GL.GRAMMAR_INFO];
274
274
  if (grammarInfo?.length) {
275
- result.grammar_info = grammarInfo.map(gi => ({
275
+ result.grammar_info = grammarInfo.map((gi: any) => ({
276
276
  gender: gi[GL.GENDER] ?? null,
277
277
  number: gi[GL.NUMBER] ?? null,
278
278
  part_of_speech: gi[GL.PART_OF_SPEECH] ?? null,
@@ -354,7 +354,7 @@ function mapSourceFromJsonLd(s: JsonLdSource): Record<string, unknown> {
354
354
 
355
355
  const sf = s[GL.SOURCED_FROM] ?? s[GL.SOURCED_FROM_ALT];
356
356
  if (sf?.length) {
357
- result.sourced_from = sf.map(item => mapOriginFromJsonLd(item));
357
+ result.sourced_from = sf.map((item: any) => mapOriginFromJsonLd(item));
358
358
  }
359
359
 
360
360
  return result;
@@ -445,7 +445,7 @@ function mapLocalizedFromJsonLd(lc: JsonLdLocalizedConcept): Record<string, unkn
445
445
 
446
446
  const dates = lc[GL.DATES];
447
447
  if (dates?.length) {
448
- data.dates = dates.map(d => ({
448
+ data.dates = dates.map((d: any) => ({
449
449
  date: d[GL.DATE] ?? null,
450
450
  type: d[GL.DATE_TYPE] ?? null,
451
451
  }));
@@ -471,7 +471,7 @@ function mapPartitiveRelationFromJsonLd(r: JsonLdPartitiveRelation): Record<stri
471
471
  if (!comprehensive) return null;
472
472
 
473
473
  const partitives = (r[GL.HAS_PARTITIVE] ?? [])
474
- .map((m): Record<string, unknown> | null => {
474
+ .map((m: any): Record<string, unknown> | null => {
475
475
  const ref = m[GL.REF] ? mapRefFromJsonLd(m[GL.REF]) : null;
476
476
  if (!ref) return null;
477
477
  const out: Record<string, unknown> = { ref };
@@ -494,7 +494,7 @@ function mapPartitiveRelationFromJsonLd(r: JsonLdPartitiveRelation): Record<stri
494
494
  if (m[GL.IS_DELIMITING] === true) out.is_delimiting = true;
495
495
  return out;
496
496
  })
497
- .filter((m): m is Record<string, unknown> => m !== null);
497
+ .filter((m: any): m is Record<string, unknown> => m !== null);
498
498
 
499
499
  // ISO 704 requires ≥2 partitives. Skip malformed relations rather
500
500
  // than letting the constructor throw and break the whole concept.
@@ -529,7 +529,7 @@ function mapGenericRelationFromJsonLd(r: JsonLdPartitiveRelation): Record<string
529
529
  if (!comprehensive) return null;
530
530
 
531
531
  const members = (r[GL.HAS_GENERIC] ?? [])
532
- .map((m): Record<string, unknown> | null => {
532
+ .map((m: any): Record<string, unknown> | null => {
533
533
  const ref = m[GL.REF] ? mapRefFromJsonLd(m[GL.REF]) : null;
534
534
  if (!ref) return null;
535
535
  const out: Record<string, unknown> = { ref };
@@ -552,7 +552,7 @@ function mapGenericRelationFromJsonLd(r: JsonLdPartitiveRelation): Record<string
552
552
  }
553
553
  return out;
554
554
  })
555
- .filter((m): m is Record<string, unknown> => m !== null);
555
+ .filter((m: any): m is Record<string, unknown> => m !== null);
556
556
 
557
557
  if (members.length < 2) return null;
558
558
 
@@ -589,7 +589,7 @@ function splitMultiplicity(m: string): { presence: 'required' | 'optional'; coun
589
589
 
590
590
  function conceptFromJsonLd(doc: JsonLdConcept): Concept {
591
591
  const id = String(doc[GL.IDENTIFIER] ?? doc['@id']?.split('/').pop() ?? '');
592
- const localizations: Record<string, unknown> = {};
592
+ const localizations: Record<string, Record<string, unknown>> = {};
593
593
 
594
594
  const rawLc = doc[GL.LOCALIZED_CONCEPT] ?? {};
595
595
  for (const [lang, lc] of Object.entries(rawLc)) {
@@ -601,10 +601,10 @@ function conceptFromJsonLd(doc: JsonLdConcept): Concept {
601
601
  const related = (doc[GL.RELATED] ?? []).map(mapRelatedFromJsonLd);
602
602
  const partitiveRelations = (doc[GL.PARTITIVE_RELATIONS] ?? [])
603
603
  .map(mapPartitiveRelationFromJsonLd)
604
- .filter((r): r is Record<string, unknown> => r !== null);
604
+ .filter((r: any): r is Record<string, unknown> => r !== null);
605
605
  const genericRelations = (doc[GL.GENERIC_RELATIONS] ?? [])
606
606
  .map(mapGenericRelationFromJsonLd)
607
- .filter((r): r is Record<string, unknown> => r !== null);
607
+ .filter((r: any): r is Record<string, unknown> => r !== null);
608
608
  const tagsArr = doc[GL.TAGS];
609
609
  const tags = Array.isArray(tagsArr) ? [...tagsArr] : [];
610
610
 
@@ -612,7 +612,7 @@ function conceptFromJsonLd(doc: JsonLdConcept): Concept {
612
612
  id,
613
613
  term: doc[GL.TERM] ?? null,
614
614
  uri: doc['@id'] ?? null,
615
- localizations,
615
+ localizations: localizations as Record<string, any>,
616
616
  related,
617
617
  partitive_relations: partitiveRelations,
618
618
  generic_relations: genericRelations,
@@ -48,7 +48,7 @@ function imageFromJsonLd(raw: Record<string, unknown>): FigureImage | null {
48
48
  ...(typeof width === 'number' && { width }),
49
49
  ...(typeof height === 'number' && { height }),
50
50
  ...(typeof scale === 'number' && { scale }),
51
- });
51
+ } as any);
52
52
  }
53
53
 
54
54
  function imagesFromJsonLd(raw: unknown): FigureImage[] {
@@ -96,5 +96,5 @@ export function figureFromJsonLd(doc: Record<string, unknown>): Figure | null {
96
96
  ...(description && { description }),
97
97
  ...(subfigures && { subfigures }),
98
98
  ...(sources.length && { sources }),
99
- });
99
+ } as any);
100
100
  }
@@ -40,11 +40,11 @@ export function formulaFromJsonLd(doc: Record<string, unknown>): Formula | null
40
40
 
41
41
  return new Formula({
42
42
  id,
43
- expression,
43
+ expression: expression as any,
44
44
  notation,
45
45
  ...(identifier && { identifier }),
46
46
  ...(caption && { caption }),
47
47
  ...(description && { description }),
48
48
  ...(sources.length && { sources }),
49
- });
49
+ } as any);
50
50
  }
@@ -90,11 +90,11 @@ export function tableFromJsonLd(doc: Record<string, unknown>): Table | null {
90
90
 
91
91
  return new Table({
92
92
  id,
93
- content: content as Record<string, unknown>,
93
+ content: content as any,
94
94
  ...(identifier && { identifier }),
95
95
  ...(caption && { caption }),
96
96
  ...(description && { description }),
97
97
  ...(format && { format }),
98
98
  ...(sources.length && { sources }),
99
- });
99
+ } as any);
100
100
  }
@@ -107,7 +107,7 @@ async function loadPreview() {
107
107
  const def = lc?.definitions?.[0]?.content || concept.definition('eng');
108
108
  const term = lc?.terms?.[0]?.designation || concept.primaryDesignation('eng');
109
109
  preview.value = {
110
- designation: term || entry?.designations?.eng || conceptId,
110
+ designation: String(term || entry?.designations?.eng || conceptId),
111
111
  definition: typeof def === 'string' ? def.slice(0, 200) : '',
112
112
  };
113
113
  } catch {
@@ -200,7 +200,7 @@ function getTermForLang(lang: string): string {
200
200
 
201
201
  function getDesignationsForLang(lang: string): Designation[] {
202
202
  const lc = props.concept.localization(lang);
203
- return lc?.terms ?? [];
203
+ return [...(lc?.terms ?? [])];
204
204
  }
205
205
 
206
206
  function hasDefinition(lang: string): boolean {
@@ -444,7 +444,7 @@ const nonVerbalReps = computed(() => {
444
444
  </div>
445
445
 
446
446
  <!-- Non-verbal representations -->
447
- <NonVerbalRepDisplay v-if="lc.lc.nonVerbalRep?.length" :reps="lc.lc.nonVerbalRep" :locale="lc.lang" :register-id="registerId" :dataset-locales="languages" />
447
+ <NonVerbalRepDisplay v-if="lc.lc.nonVerbalRep?.length" :reps="[...lc.lc.nonVerbalRep]" :locale="lc.lang" :register-id="registerId" :dataset-locales="languages" />
448
448
 
449
449
  <!-- Sources -->
450
450
  <div v-if="lc.sources.length" class="space-y-2">
@@ -33,7 +33,7 @@ const { t } = useI18n();
33
33
  const targetConcept = ref<Concept | null>(null);
34
34
  const loadError = ref<string | null>(null);
35
35
 
36
- function conceptToData(c: Concept | null, registerId: string): ConceptLikeData | null {
36
+ function conceptToData(c: any, registerId: string): ConceptLikeData | null {
37
37
  if (!c) return null;
38
38
  const langs = c.languages ?? [];
39
39
  const localizations: Record<string, unknown> = {};
@@ -51,7 +51,7 @@ function conceptToData(c: Concept | null, registerId: string): ConceptLikeData |
51
51
  }
52
52
 
53
53
  const oldData = computed(() => conceptToData(targetConcept.value, props.targetRegisterId));
54
- const newData = computed(() => conceptToData(props.currentConcept, props.currentRegisterId));
54
+ const newData = computed(() => conceptToData(props.currentConcept as any, props.currentRegisterId));
55
55
 
56
56
  async function loadTarget() {
57
57
  if (!props.open || !props.targetRegisterId || !props.targetConceptId) return;
@@ -51,7 +51,7 @@ watchEffect(() => {
51
51
  }
52
52
  });
53
53
 
54
- const effectiveDiff = computed(() => props.diff ?? internal.diffResult.value);
54
+ const effectiveDiff = computed(() => (props.diff ?? internal.diffResult.value) as any);
55
55
  const sections = computed(() => deriveDiffSections(effectiveDiff.value, props.lang));
56
56
  const similarity = computed(() => computeSimilarity(effectiveDiff.value));
57
57
  const hasChanges = computed(() => effectiveDiff.value?.hasChanges === true);