@glossarist/concept-browser 0.7.113 → 0.7.115

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 (86) hide show
  1. package/README.md +87 -2
  2. package/cli/index.mjs +15 -15
  3. package/cli/index.ts +9 -11
  4. package/data/concept-model/shapes/glossarist.concept.yaml.schema.json +255 -0
  5. package/package.json +14 -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.mjs → bridge-to-astro.ts} +2 -4
  14. package/scripts/{build-edges.js → build-edges.ts} +11 -13
  15. package/scripts/{check-syntax.mjs → check-syntax.ts} +20 -13
  16. package/scripts/{fetch-datasets.mjs → fetch-datasets.ts} +2 -2
  17. package/scripts/{generate-data.mjs → generate-data.ts} +73 -460
  18. package/scripts/{generate-ontology-schema.mjs → generate-ontology-schema.ts} +2 -2
  19. package/scripts/lib/{build-cache.mjs → build-cache.ts} +3 -2
  20. package/scripts/lib/concept-formats.ts +206 -0
  21. package/scripts/lib/concept-uri.ts +14 -0
  22. package/scripts/lib/{dataset-turtle.mjs → dataset-turtle.ts} +4 -3
  23. package/scripts/lib/harmonize-concept.ts +86 -0
  24. package/scripts/lib/jsonld-emitter.ts +169 -0
  25. package/scripts/lib/{local-path-safety.mjs → local-path-safety.ts} +6 -2
  26. package/scripts/lib/yaml-types.ts +262 -0
  27. package/scripts/{process-about-pages.mjs → process-about-pages.ts} +1 -1
  28. package/scripts/{smoke.mjs → smoke.ts} +4 -3
  29. package/scripts/{validate-shacl.mjs → validate-shacl.ts} +5 -5
  30. package/scripts/validate-yaml.ts +144 -0
  31. package/src/__tests__/extract-source-refs.test.ts +2 -3
  32. package/src/__tests__/perf/serialization-perf.test.ts +5 -5
  33. package/src/__tests__/scripts/agents-turtle.test.ts +1 -1
  34. package/src/__tests__/scripts/bibliography-turtle.test.ts +1 -1
  35. package/src/__tests__/scripts/build-activity-turtle.test.ts +1 -1
  36. package/src/__tests__/scripts/build-cache.test.ts +1 -1
  37. package/src/__tests__/scripts/build-integration.test.ts +19 -19
  38. package/src/__tests__/scripts/dataset-turtle.test.ts +1 -1
  39. package/src/__tests__/scripts/migrate-v1-to-v3.test.ts +1 -1
  40. package/src/__tests__/scripts/normalize-yaml.test.ts +1 -1
  41. package/src/__tests__/scripts/turtle-escape.test.ts +1 -1
  42. package/src/__tests__/scripts/validate-yaml.test.ts +76 -0
  43. package/src/__tests__/scripts/version-turtle.test.ts +1 -1
  44. package/src/__tests__/scripts/vocab-turtle.test.ts +1 -1
  45. package/src/__tests__/scripts-syntax.test.ts +2 -2
  46. package/src/__tests__/use-rdf-document.test.ts +2 -3
  47. package/src/__tests__/utils/content-renderer-xss.test.ts +57 -0
  48. package/src/__tests__/utils/inline-content-kinds.test.ts +139 -0
  49. package/src/__tests__/validate-shacl.test.ts +3 -3
  50. package/src/adapters/wire/index.ts +69 -0
  51. package/src/pages/[...path].astro +1 -0
  52. package/src/router/index.ts +5 -0
  53. package/src/utils/content-renderer.ts +106 -23
  54. package/src/utils/url-safety.ts +1 -5
  55. package/src/views/learn/LearnInlineContentView.vue +128 -0
  56. package/tsconfig.json +2 -2
  57. package/vite.config.ts +1 -1
  58. package/src/__tests__/composables/use-concept-edges-v2.test.ts +0 -167
  59. package/src/__tests__/composables/use-concept-edges-v2.test.ts.bak +0 -166
  60. package/src/types/agents-version-turtle.d.ts +0 -27
  61. package/src/types/bibliography-turtle.d.ts +0 -12
  62. package/src/types/build-activity-turtle.d.ts +0 -16
  63. package/src/types/build-cache.d.ts +0 -20
  64. package/src/types/dataset-turtle.d.ts +0 -32
  65. package/src/types/normalize-yaml.d.ts +0 -16
  66. package/src/types/turtle-escape.d.ts +0 -6
  67. package/src/types/vocab-turtle.d.ts +0 -13
  68. /package/scripts/{doctor.mjs → doctor.ts} +0 -0
  69. /package/scripts/{extract-source-refs.js → extract-source-refs.ts} +0 -0
  70. /package/scripts/{generate-404.js → generate-404.ts} +0 -0
  71. /package/scripts/{generate-ontology-data.mjs → generate-ontology-data.ts} +0 -0
  72. /package/scripts/lib/{agents-turtle.mjs → agents-turtle.ts} +0 -0
  73. /package/scripts/lib/{bibliography-turtle.mjs → bibliography-turtle.ts} +0 -0
  74. /package/scripts/lib/{bibliography.mjs → bibliography.ts} +0 -0
  75. /package/scripts/lib/build/{image-assets.mjs → image-assets.ts} +0 -0
  76. /package/scripts/lib/build/{non-verbal-consumer.mjs → non-verbal-consumer.ts} +0 -0
  77. /package/scripts/lib/{build-activity-turtle.mjs → build-activity-turtle.ts} +0 -0
  78. /package/scripts/lib/{concept-groups.mjs → concept-groups.ts} +0 -0
  79. /package/scripts/lib/{first-non-empty.mjs → first-non-empty.ts} +0 -0
  80. /package/scripts/lib/{turtle-escape.mjs → turtle-escape.ts} +0 -0
  81. /package/scripts/lib/{version-turtle.mjs → version-turtle.ts} +0 -0
  82. /package/scripts/lib/{vocab-turtle.mjs → vocab-turtle.ts} +0 -0
  83. /package/scripts/{load-site-config.mjs → load-site-config.ts} +0 -0
  84. /package/scripts/{migrate-v1-to-v3.mjs → migrate-v1-to-v3.ts} +0 -0
  85. /package/scripts/{normalize-yaml.mjs → normalize-yaml.ts} +0 -0
  86. /package/scripts/{sync-concept-model.mjs → sync-concept-model.ts} +0 -0
@@ -1,6 +1,6 @@
1
1
  import { describe, it, expect } from 'vitest';
2
2
  import { Parser, Store } from 'n3';
3
- import { buildDatasetTurtle } from '../../../scripts/lib/dataset-turtle.mjs';
3
+ import { buildDatasetTurtle } from '../../../scripts/lib/dataset-turtle';
4
4
 
5
5
  const BASE = 'https://glossarist.org/dataset';
6
6
 
@@ -5,7 +5,7 @@ import {
5
5
  migrateRelation,
6
6
  migrateConceptData,
7
7
  isAlreadyMigrated,
8
- } from '../../../scripts/migrate-v1-to-v3.mjs';
8
+ } from '../../../scripts/migrate-v1-to-v3';
9
9
 
10
10
  describe('migrate-v1-to-v3 pure conversion', () => {
11
11
  describe('isV1PartitiveRelation', () => {
@@ -2,7 +2,7 @@ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
2
2
  import { mkdtempSync, writeFileSync, readFileSync, rmSync, mkdirSync } from 'node:fs';
3
3
  import { join } from 'node:path';
4
4
  import { tmpdir } from 'node:os';
5
- import { normalizeYaml } from '../../../scripts/normalize-yaml.mjs';
5
+ import { normalizeYaml } from '../../../scripts/normalize-yaml';
6
6
 
7
7
  const NFC_STRING = 'café';
8
8
  const NON_NFC_STRING = 'café';
@@ -1,5 +1,5 @@
1
1
  import { describe, it, expect } from 'vitest';
2
- import { ttlLit, ttlPrefixed, ttlIri, assertValidIri } from '../../../scripts/lib/turtle-escape.mjs';
2
+ import { ttlLit, ttlPrefixed, ttlIri, assertValidIri } from '../../../scripts/lib/turtle-escape';
3
3
 
4
4
  describe('D1/D2 — Turtle escaping hardening', () => {
5
5
  describe('ttlLit', () => {
@@ -0,0 +1,76 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { readFileSync } from 'node:fs';
3
+ import { resolve, dirname } from 'node:path';
4
+ import { fileURLToPath } from 'node:url';
5
+ import { validateYaml } from '../../../scripts/validate-yaml';
6
+
7
+ const __dirname = dirname(fileURLToPath(import.meta.url));
8
+ const SCHEMA_PATH = resolve(__dirname, '..', '..', '..', 'data', 'concept-model', 'shapes', 'glossarist.concept.yaml.schema.json');
9
+ const schema = JSON.parse(readFileSync(SCHEMA_PATH, 'utf8'));
10
+
11
+ describe('validate-yaml', () => {
12
+ it('accepts a valid concept with termid and localization', () => {
13
+ const yaml = `
14
+ termid: '1'
15
+ eng:
16
+ terms:
17
+ - designation: measurement unit
18
+ normative_status: preferred
19
+ definition:
20
+ - content: A concept used as a basis for comparison.
21
+ `;
22
+ const result = validateYaml(yaml, schema);
23
+ if (!result.valid) console.log('Errors:', result.errors);
24
+ expect(result.valid).toBe(true);
25
+ });
26
+
27
+ it('rejects a concept without termid', () => {
28
+ const yaml = `
29
+ eng:
30
+ terms:
31
+ - designation: missing id
32
+ `;
33
+ const result = validateYaml(yaml, schema);
34
+ expect(result.valid).toBe(false);
35
+ expect(result.errors.some(e => e.message.includes('termid'))).toBe(true);
36
+ });
37
+
38
+ it('accepts managed concept format with underscore fields', () => {
39
+ const yaml = `
40
+ termid: '5'
41
+ _domains:
42
+ - physics
43
+ _status: valid
44
+ `;
45
+ const result = validateYaml(yaml, schema);
46
+ if (!result.valid) console.log('Errors:', result.errors);
47
+ expect(result.valid).toBe(true);
48
+ });
49
+
50
+ it('accepts partitive relations with MECE axes', () => {
51
+ const yaml = `
52
+ termid: '3'
53
+ _partitiveRelations:
54
+ - type: hasPart
55
+ members:
56
+ - ref: part-a
57
+ presence: required
58
+ count: exactly_one
59
+ `;
60
+ const result = validateYaml(yaml, schema);
61
+ if (!result.valid) console.log('Errors:', result.errors);
62
+ expect(result.valid).toBe(true);
63
+ });
64
+
65
+ it('rejects non-string termid', () => {
66
+ const yaml = `
67
+ termid: 12345
68
+ eng:
69
+ terms:
70
+ - designation: test
71
+ `;
72
+ const result = validateYaml(yaml, schema);
73
+ expect(result.valid).toBe(false);
74
+ });
75
+ });
76
+
@@ -1,6 +1,6 @@
1
1
  import { describe, it, expect } from 'vitest';
2
2
  import { Parser, Store } from 'n3';
3
- import { buildVersionTurtle, buildVersionHistoryTurtle } from '../../../scripts/lib/version-turtle.mjs';
3
+ import { buildVersionTurtle, buildVersionHistoryTurtle } from '../../../scripts/lib/version-turtle';
4
4
 
5
5
  function parse(turtle: string): Store {
6
6
  const parser = new Parser({ format: 'Turtle' });
@@ -1,6 +1,6 @@
1
1
  import { describe, it, expect } from 'vitest';
2
2
  import { Parser, Store } from 'n3';
3
- import { buildVocabularyTurtle, listVocabSchemes } from '../../../scripts/lib/vocab-turtle.mjs';
3
+ import { buildVocabularyTurtle, listVocabSchemes } from '../../../scripts/lib/vocab-turtle';
4
4
 
5
5
  function parse(turtle: string): Store {
6
6
  const parser = new Parser({ format: 'Turtle' });
@@ -9,10 +9,10 @@ const repoRoot = path.resolve(
9
9
  );
10
10
 
11
11
  describe('scripts syntax gate', () => {
12
- it('`npm run check:scripts` exits 0 (every .mjs/.js/.cjs in scripts/ and cli/ parses)', () => {
12
+ it('`npm run check:scripts` exits 0 (every .ts/.mjs/.js/.cjs in scripts/ and cli/ parses)', () => {
13
13
  const result = spawnSync(
14
14
  process.execPath,
15
- [path.join(repoRoot, 'scripts', 'check-syntax.mjs')],
15
+ ['--import', 'tsx', path.join(repoRoot, 'scripts', 'check-syntax.ts')],
16
16
  { encoding: 'utf8' },
17
17
  );
18
18
 
@@ -1,10 +1,9 @@
1
- // @ts-nocheck — TODO.typescript/12: remove after glossarist TS migration
2
1
  import { describe, it, expect } from 'vitest';
3
2
  import { ref } from 'vue';
4
3
  import { Concept } from 'glossarist';
5
4
  import { useRdfDocument } from '../components/use-rdf-document';
6
5
 
7
- function makeConcept(): Concept {
6
+ function makeConcept(): any {
8
7
  return Concept.fromJSON({
9
8
  id: '3.1.1',
10
9
  uri: 'https://glossarist.org/test/concept/3.1.1',
@@ -24,7 +23,7 @@ function makeConcept(): Concept {
24
23
  });
25
24
  }
26
25
 
27
- function makeConceptWithNonVerbal(): Concept {
26
+ function makeConceptWithNonVerbal(): any {
28
27
  return Concept.fromJSON({
29
28
  id: '3.1.2',
30
29
  uri: 'https://glossarist.org/test/concept/3.1.2',
@@ -0,0 +1,57 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { renderContent } from '../../utils/content-renderer';
3
+
4
+ describe('content-renderer XSS protection', () => {
5
+ it('blocks javascript: URLs in {{link:}} (no anchor tag)', () => {
6
+ const html = renderContent('{{link:javascript:alert(1)}}', {});
7
+ expect(html).not.toContain('<a');
8
+ expect(html).not.toContain('href=');
9
+ });
10
+
11
+ it('blocks javascript: URLs in {{link:}} with label (renders as text)', () => {
12
+ const html = renderContent('{{link:javascript:alert(1), click me}}', {});
13
+ expect(html).not.toContain('<a');
14
+ expect(html).not.toContain('href=');
15
+ expect(html).toContain('click me');
16
+ });
17
+
18
+ it('blocks data: URLs in {{link:}} (no anchor tag)', () => {
19
+ const html = renderContent('{{link:data:text/html,<script>alert(1)</script>}}', {});
20
+ expect(html).not.toContain('<a');
21
+ expect(html).not.toContain('href=');
22
+ expect(html).not.toContain('<script>');
23
+ });
24
+
25
+ it('allows https: URLs in {{link:}}', () => {
26
+ const html = renderContent('{{link:https://example.com}}', {});
27
+ expect(html).toContain('href="https://example.com"');
28
+ });
29
+
30
+ it('blocks javascript: URLs in {{image:}} (no img tag)', () => {
31
+ const html = renderContent('{{image:javascript:alert(1)}}', {});
32
+ expect(html).not.toContain('<img');
33
+ });
34
+
35
+ it('blocks data: URLs in {{image:}} (no img tag with executable src)', () => {
36
+ const html = renderContent('{{image:data:text/html,<script>}}', {});
37
+ expect(html).not.toContain('<img');
38
+ expect(html).not.toContain('data:text/html');
39
+ });
40
+
41
+ it('allows relative paths in {{image:}}', () => {
42
+ const html = renderContent('{{image:diagram.png}}', {});
43
+ expect(html).toContain('src="diagram.png"');
44
+ });
45
+
46
+ it('escapes HTML in unordered list items', () => {
47
+ const html = renderContent('* <script>alert(1)</script>\n* safe item', {});
48
+ expect(html).not.toContain('<script>');
49
+ expect(html).toContain('&lt;script&gt;');
50
+ });
51
+
52
+ it('escapes HTML in ordered list items', () => {
53
+ const html = renderContent('1. <img onerror=alert(1)>\n2. safe', {});
54
+ expect(html).not.toContain('<img onerror');
55
+ expect(html).toContain('&lt;img');
56
+ });
57
+ });
@@ -0,0 +1,139 @@
1
+ import { describe, it, expect, vi, afterEach } from 'vitest';
2
+ import { renderContent, cleanContent, type RenderOptions } from '../../utils/content-renderer';
3
+
4
+ /**
5
+ * Unified mention syntax contract — {{kind:target[, label]}}.
6
+ *
7
+ * Pins the dispatch for every kind and the deprecation behavior for
8
+ * the legacy <<ref,title>> AsciiDoc xref syntax.
9
+ */
10
+
11
+ describe('content-renderer — unified mention kinds', () => {
12
+
13
+ describe('{{link:URL}} — external link', () => {
14
+ it('renders bare URL as external anchor', () => {
15
+ const html = renderContent('{{link:https://example.com}}');
16
+ expect(html).toContain('<a href="https://example.com"');
17
+ expect(html).toContain('target="_blank"');
18
+ expect(html).toContain('rel="noopener"');
19
+ expect(html).toContain('https://example.com');
20
+ });
21
+
22
+ it('renders URL with label', () => {
23
+ const html = renderContent('{{link:https://example.com, click here}}');
24
+ expect(html).toContain('click here');
25
+ expect(html).toContain('href="https://example.com"');
26
+ });
27
+
28
+ it('delegates to linkResolver when provided', () => {
29
+ const opts: RenderOptions = { linkResolver: (url, label) => `<a href="${url}" class="custom">${label}</a>` };
30
+ const html = renderContent('{{link:https://example.com, test}}', opts);
31
+ expect(html).toContain('class="custom"');
32
+ expect(html).toContain('test');
33
+ });
34
+ });
35
+
36
+ describe('{{image:src}} — inline image embed', () => {
37
+ it('renders bare src as img tag', () => {
38
+ const html = renderContent('{{image:photo.png}}');
39
+ expect(html).toContain('<img');
40
+ expect(html).toContain('src="photo.png"');
41
+ });
42
+
43
+ it('renders src with alt text', () => {
44
+ const html = renderContent('{{image:photo.png, A photo of the setup}}');
45
+ expect(html).toContain('src="photo.png"');
46
+ expect(html).toContain('alt="A photo of the setup"');
47
+ });
48
+
49
+ it('delegates to imageResolver when provided', () => {
50
+ const opts: RenderOptions = { imageResolver: (src, alt) => `<img src="/base/${src}" alt="${alt}" class="custom-img" />` };
51
+ const html = renderContent('{{image:photo.png, test}}', opts);
52
+ expect(html).toContain('src="/base/photo.png"');
53
+ expect(html).toContain('class="custom-img"');
54
+ });
55
+ });
56
+
57
+ describe('{{bib:id}} — bibliography entry', () => {
58
+ it('renders id as bib-ref span when no resolver', () => {
59
+ const html = renderContent('{{bib:ref_1}}');
60
+ expect(html).toContain('bib-ref');
61
+ expect(html).toContain('ref_1');
62
+ });
63
+
64
+ it('renders label when provided', () => {
65
+ const html = renderContent('{{bib:ref_1, ISO 704}}');
66
+ expect(html).toContain('ISO 704');
67
+ });
68
+
69
+ it('delegates to bibResolver when provided', () => {
70
+ const opts: RenderOptions = { bibResolver: (id, title) => `<a href="/bib/${id}" class="bib-link">${title}</a>` };
71
+ const html = renderContent('{{bib:ref_1, ISO 704}}', opts);
72
+ expect(html).toContain('href="/bib/ref_1"');
73
+ expect(html).toContain('ISO 704');
74
+ });
75
+ });
76
+
77
+ describe('cleanContent — new kinds stripped correctly', () => {
78
+ it('strips {{link:URL}} to the URL', () => {
79
+ expect(cleanContent('{{link:https://example.com}}')).toBe('https://example.com');
80
+ });
81
+
82
+ it('strips {{link:URL, label}} to the label', () => {
83
+ expect(cleanContent('{{link:https://example.com, click here}}')).toBe('click here');
84
+ });
85
+
86
+ it('strips {{image:src}} to empty string', () => {
87
+ expect(cleanContent('{{image:photo.png}}')).toBe('');
88
+ });
89
+
90
+ it('strips {{image:src, alt}} to the alt text', () => {
91
+ expect(cleanContent('{{image:photo.png, A photo}}')).toBe('A photo');
92
+ });
93
+
94
+ it('strips {{bib:id}} to the id', () => {
95
+ expect(cleanContent('{{bib:ref_1}}')).toBe('ref_1');
96
+ });
97
+
98
+ it('strips {{bib:id, label}} to the label', () => {
99
+ expect(cleanContent('{{bib:ref_1, ISO 704}}')).toBe('ISO 704');
100
+ });
101
+ });
102
+ });
103
+
104
+ describe('content-renderer — legacy <<ref,title>> deprecation', () => {
105
+ afterEach(() => {
106
+ vi.restoreAllMocks();
107
+ });
108
+
109
+ it('emits a deprecation warning via console.warn', () => {
110
+ const spy = vi.spyOn(console, 'warn').mockImplementation(() => {});
111
+ renderContent('See <<845-01-01, 845-01-01>> for details.');
112
+ expect(spy).toHaveBeenCalledTimes(1);
113
+ expect(spy.mock.calls[0][0]).toContain('deprecated');
114
+ expect(spy.mock.calls[0][0]).toContain('<<845-01-01,845-01-01>>');
115
+ });
116
+
117
+ it('renders the title as plain text in a legacy-xref span', () => {
118
+ vi.spyOn(console, 'warn').mockImplementation(() => {});
119
+ const html = renderContent('See <<845-01-01, 845-01-01>> for details.');
120
+ expect(html).toContain('legacy-xref');
121
+ expect(html).toContain('845-01-01');
122
+ // Must NOT contain bib-ref or bib-link (the old wrong behavior)
123
+ expect(html).not.toContain('bib-ref');
124
+ expect(html).not.toContain('bib-link');
125
+ });
126
+
127
+ it('does NOT call bibResolver (cascade is not bypassed)', () => {
128
+ vi.spyOn(console, 'warn').mockImplementation(() => {});
129
+ const bibResolver = vi.fn(() => '<a class="bib-link">mock</a>');
130
+ const opts: RenderOptions = { bibResolver };
131
+ const html = renderContent('<<845-01-01, 845-01-01>>', opts);
132
+ expect(bibResolver).not.toHaveBeenCalled();
133
+ expect(html).not.toContain('bib-link');
134
+ });
135
+
136
+ it('cleanContent extracts the title from <<ref,title>>', () => {
137
+ expect(cleanContent('See <<845-01-01, 845-01-01>>')).toContain('845-01-01');
138
+ });
139
+ });
@@ -4,7 +4,7 @@ import { join, dirname } from 'node:path';
4
4
  import { fileURLToPath } from 'node:url';
5
5
 
6
6
  const __dirname = dirname(fileURLToPath(import.meta.url));
7
- const SCRIPT = join(__dirname, '..', '..', 'scripts', 'validate-shacl.mjs');
7
+ const SCRIPT = join(__dirname, '..', '..', 'scripts', 'validate-shacl.ts');
8
8
  const FIXTURES = join(__dirname, '..', '__fixtures__', 'shacl');
9
9
  const SHAPES = join(__dirname, '..', '__fixtures__', 'concept-shape.ttl');
10
10
 
@@ -31,13 +31,13 @@ function execCaught(cmd: string, args: string[], opts: Parameters<typeof execFil
31
31
  }
32
32
 
33
33
  function runValidate(dataDir: string, { env, shapes }: RunOptions = {}): RunResult {
34
- return execCaught('node', [SCRIPT, '--shapes', shapes ?? SHAPES, dataDir], {
34
+ return execCaught('node', ['--import', 'tsx', SCRIPT, '--shapes', shapes ?? SHAPES, dataDir], {
35
35
  env: { ...process.env, ...env },
36
36
  });
37
37
  }
38
38
 
39
39
  function runValidateRaw(args: string[], { env }: RunOptions = {}): RunResult {
40
- return execCaught('node', [SCRIPT, ...args], {
40
+ return execCaught('node', ['--import', 'tsx', SCRIPT, ...args], {
41
41
  env: { ...process.env, ...env },
42
42
  stdio: ['ignore', 'pipe', 'pipe'],
43
43
  });
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Wire-format types barrel — single import path for all wire types.
3
+ *
4
+ * Three MECE layers:
5
+ * - YAML author format (INPUT to build pipeline)
6
+ * - JSON-LD browser format (OUTPUT of build pipeline)
7
+ * - Manifest / site-config metadata
8
+ *
9
+ * Usage:
10
+ * import type { HarmonizedConcept, JsonLdConcept, Manifest } from '@/adapters/wire'
11
+ *
12
+ * Or layer-specific:
13
+ * import type { HarmonizedConcept } from '@/adapters/wire/yaml'
14
+ * import type { JsonLdConcept } from '@/adapters/wire/jsonld'
15
+ */
16
+
17
+ // YAML author format — what dataset authors write
18
+ export type {
19
+ YamlContent,
20
+ YamlTerm,
21
+ YamlOrigin,
22
+ YamlGrammarInfo,
23
+ YamlSource,
24
+ YamlDate,
25
+ YamlRelation,
26
+ YamlPartitiveMember,
27
+ YamlPartitiveRelation,
28
+ YamlGenericRelation,
29
+ YamlLocalization,
30
+ HarmonizedConcept,
31
+ YamlManagedConceptDoc,
32
+ YamlManifestSection,
33
+ YamlManifest,
34
+ YamlBibliographyEntry,
35
+ YamlNewsFrontmatter,
36
+ YamlContentPage,
37
+ ShaclConstraint,
38
+ PrefixMap,
39
+ } from '../../../scripts/lib/yaml-types';
40
+
41
+ // JSON-LD browser format — what the SPA reads
42
+ export type {
43
+ JsonLdContent,
44
+ JsonLdDate,
45
+ JsonLdPronunciation,
46
+ JsonLdGrammarInfo,
47
+ JsonLdRef,
48
+ JsonLdLocality,
49
+ JsonLdOrigin,
50
+ JsonLdSource,
51
+ JsonLdRelated,
52
+ JsonLdDesignation,
53
+ JsonLdLocalizedConcept,
54
+ JsonLdPartitiveMember,
55
+ JsonLdPartitiveRelation,
56
+ JsonLdConcept,
57
+ } from '../jsonld-types';
58
+
59
+ // Manifest + summary types
60
+ export type {
61
+ Manifest,
62
+ ManifestSection,
63
+ ConceptSummary,
64
+ CitationClassification,
65
+ CiteResolution,
66
+ } from '../types';
67
+
68
+ // Wire-key constants (runtime value, not just type)
69
+ export { GL } from '../wire-keys';
@@ -17,6 +17,7 @@ export async function getStaticPaths() {
17
17
  { params: { path: 'learn/relationships' } },
18
18
  { params: { path: 'learn/designations' } },
19
19
  { params: { path: 'learn/statuses' } },
20
+ { params: { path: 'learn/inline-content' } },
20
21
  ];
21
22
  for (const ds of datasets) {
22
23
  paths.push({ params: { path: `dataset/${ds.data.id}/about` } });
@@ -118,6 +118,11 @@ export const routes: RouteRecordRaw[] = [
118
118
  name: 'learn-statuses',
119
119
  component: () => import('../views/learn/LearnStatusesView.vue'),
120
120
  },
121
+ {
122
+ path: '/learn/inline-content',
123
+ name: 'learn-inline-content',
124
+ component: () => import('../views/learn/LearnInlineContentView.vue'),
125
+ },
121
126
  {
122
127
  path: '/about',
123
128
  name: 'about-global',