@glossarist/concept-browser 0.7.101 → 0.7.103

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 (45) hide show
  1. package/cli/index.mjs +6 -28
  2. package/cli/index.ts +289 -0
  3. package/package.json +2 -2
  4. package/src/__tests__/__fixtures__/concepts.ts +3 -3
  5. package/src/__tests__/composables/use-concept-edges-v2.test.ts +1 -0
  6. package/src/__tests__/designation-relationship.test.ts +2 -2
  7. package/src/__tests__/perf/serialization-perf.test.ts +1 -0
  8. package/src/__tests__/use-rdf-document.test.ts +1 -0
  9. package/src/adapters/bibliography-adapter.ts +1 -1
  10. package/src/adapters/model-bridge.ts +15 -15
  11. package/src/adapters/non-verbal/figure-bridge.ts +2 -2
  12. package/src/adapters/non-verbal/formula-bridge.ts +2 -2
  13. package/src/adapters/non-verbal/table-bridge.ts +2 -2
  14. package/src/components/CitationDisplay.vue +1 -1
  15. package/src/components/ConceptDetail.vue +2 -2
  16. package/src/components/ConceptDiffModal.vue +2 -2
  17. package/src/components/ConceptDiffView.vue +1 -1
  18. package/src/components/DesignationList.vue +4 -4
  19. package/src/components/NavIcon.vue +1 -0
  20. package/src/components/figure/FigureDisplay.vue +5 -5
  21. package/src/components/formula/FormulaDisplay.vue +4 -4
  22. package/src/components/table/TableDisplay.vue +3 -3
  23. package/src/components/use-rdf-document.ts +16 -7
  24. package/src/composables/use-concept-content.ts +3 -3
  25. package/src/composables/use-concept-diff.ts +2 -2
  26. package/src/composables/use-concept-edges.ts +3 -3
  27. package/src/config/page-types.ts +1 -1
  28. package/src/i18n/locales/eng.yml +12 -0
  29. package/src/i18n/locales/fra.yml +12 -0
  30. package/src/layouts/Default.astro +32 -2
  31. package/src/utils/content-renderer.ts +1 -1
  32. package/src/utils/partitive-multiplicity.ts +3 -2
  33. package/src/views/ConceptView.vue +3 -3
  34. package/src/views/learn/LearnRelationshipsView.vue +63 -0
  35. package/tailwind.config.js +3 -3
  36. package/tsconfig.json +1 -1
  37. package/src/adapters/model-bridge/bridges.ts +0 -159
  38. package/src/adapters/model-bridge/concept.ts +0 -97
  39. package/src/adapters/model-bridge/entity-refs.ts +0 -49
  40. package/src/adapters/model-bridge/hyperedge.ts +0 -103
  41. package/src/adapters/model-bridge/index.ts +0 -46
  42. package/src/adapters/model-bridge/mappers.ts +0 -277
  43. package/src/adapters/model-bridge/partitive.ts +0 -79
  44. package/src/adapters/non-verbal/glossarist-augment.d.ts +0 -339
  45. /package/src/adapters/{model-bridge/jsonld-types.ts → jsonld-types.ts} +0 -0
package/cli/index.mjs CHANGED
@@ -1,24 +1,14 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  /**
4
- * Glossarist Concept Browser CLI.
4
+ * Glossarist Concept Browser CLI — runtime entrypoint.
5
5
  *
6
- * Commands:
7
- * fetch Fetch/update datasets (from GCR packages or source repos)
8
- * generate Convert harmonized YAML concepts to JSON-LD static files
9
- * edges Build cross-reference edges from generated concept data
10
- * build Full pipeline: fetch + generate + edges + vite build
11
- * site Same as build (alias)
12
- * normalize NFC-normalize YAML files in .datasets/ (use --check for CI gate)
13
- * doctor Diagnose the local environment (deps, datasets, shapes, context)
6
+ * This is the published bin entry. It stays as plain JS (no tsx dependency)
7
+ * so consumers who install with --omit=dev can use it.
14
8
  *
15
- * Options:
16
- * --site <id> Site config to use (looks for site-config.yml in CWD)
17
- *
18
- * Environment:
19
- * SITE_CONFIG Path to site config file (overrides --site)
20
- * SITE_ID Site config ID (overrides --site)
21
- * GITHUB_TOKEN GitHub token for private repos
9
+ * The TypeScript version lives at cli/index.ts for type-checking during
10
+ * development. The two files should be kept in sync manually until
11
+ * TODO.typescript/03 adds a compile step.
22
12
  */
23
13
 
24
14
  import { loadSiteConfig } from '../scripts/load-site-config.mjs';
@@ -79,7 +69,6 @@ Environment:
79
69
  process.exit(cmd ? 0 : 1);
80
70
  }
81
71
 
82
- // Pre-load site config so scripts can use it
83
72
  if (!process.env.SITE_CONFIG && !process.env.SITE_ID && named.site) {
84
73
  process.env.SITE_ID = named.site;
85
74
  }
@@ -94,7 +83,6 @@ Environment:
94
83
  await commands[step]();
95
84
  }
96
85
 
97
- // Generate favicons from site logo
98
86
  const fs = await import('fs');
99
87
  const publicDir = resolve(process.cwd(), 'public');
100
88
  fs.mkdirSync(publicDir, { recursive: true });
@@ -124,7 +112,6 @@ Environment:
124
112
  },
125
113
  });
126
114
 
127
- // Write all generated images and files to public/
128
115
  for (const img of response.images) {
129
116
  fs.writeFileSync(resolve(publicDir, img.name), img.contents);
130
117
  }
@@ -132,22 +119,18 @@ Environment:
132
119
  fs.writeFileSync(resolve(publicDir, file.name), file.contents);
133
120
  }
134
121
 
135
- // Keep the original SVG for dark mode support
136
122
  if (faviconSrc.endsWith('.svg')) {
137
123
  fs.copyFileSync(faviconSrc, resolve(publicDir, 'favicon.svg'));
138
124
  }
139
125
 
140
126
  faviconHtml = response.html.join('\n ');
141
- // Add SVG favicon
142
127
  if (faviconSrc.endsWith('.svg')) {
143
128
  faviconHtml += '\n <link rel="icon" type="image/svg+xml" href="/favicon.svg">';
144
129
  }
145
- // Write tags for Vite plugin to pick up
146
130
  fs.writeFileSync(resolve(publicDir, 'favicon-links.html'), faviconHtml);
147
131
  console.log(` Generated ${response.images.length} favicon files`);
148
132
  }
149
133
 
150
- // Pass favicon tags to Vite via env
151
134
  if (faviconHtml) {
152
135
  const basePath = process.env.BASE_PATH?.replace(/\/+$/, '') || '';
153
136
  if (basePath) {
@@ -156,7 +139,6 @@ Environment:
156
139
  process.env.FAVICON_HTML = faviconHtml;
157
140
  }
158
141
 
159
- // Pass branding info for HTML transformation
160
142
  process.env.SITE_TITLE = config?.title || 'Glossarist';
161
143
  if (branding.fonts?.header || branding.fonts?.body) {
162
144
  const fontFamilies = [];
@@ -173,14 +155,12 @@ Environment:
173
155
  }
174
156
  }
175
157
 
176
- // Bridge data to Astro content collections
177
158
  console.log(`\n=== BRIDGE DATA ===\n`);
178
159
  const bridge = resolve(pkgRoot, 'scripts', 'bridge-to-astro.mjs');
179
160
  if (existsSync(bridge)) {
180
161
  await import(`file://${bridge}`);
181
162
  }
182
163
 
183
- // Run Astro build (TODO.astro/22)
184
164
  console.log(`\n=== BUILD ASTRO ===\n`);
185
165
  const astroConfig = resolve(pkgRoot, 'astro.config.mjs');
186
166
  if (existsSync(astroConfig)) {
@@ -195,14 +175,12 @@ Environment:
195
175
  await viteBuild({ configFile: viteConfig, root: pkgRoot, mode: 'production' });
196
176
  }
197
177
  } else {
198
- // Fall back to Vite SPA if Astro config doesn't exist
199
178
  console.log(`\n=== BUILD SPA ===\n`);
200
179
  const viteConfig = resolve(pkgRoot, 'vite.config.ts');
201
180
  const { build: viteBuild } = await import('vite');
202
181
  await viteBuild({ configFile: viteConfig, root: pkgRoot, mode: 'production' });
203
182
  }
204
183
 
205
- // Run postbuild (404 page) via dynamic import
206
184
  const postbuild = resolve(pkgRoot, 'scripts', 'generate-404.js');
207
185
  if (existsSync(postbuild)) {
208
186
  await import(`file://${postbuild}`);
package/cli/index.ts ADDED
@@ -0,0 +1,289 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Glossarist Concept Browser CLI.
5
+ *
6
+ * Commands:
7
+ * fetch Fetch/update datasets (from GCR packages or source repos)
8
+ * generate Convert harmonized YAML concepts to JSON-LD static files
9
+ * edges Build cross-reference edges from generated concept data
10
+ * build Full pipeline: fetch + generate + edges + vite build
11
+ * site Same as build (alias)
12
+ * normalize NFC-normalize YAML files in .datasets/ (use --check for CI gate)
13
+ * doctor Diagnose the local environment (deps, datasets, shapes, context)
14
+ *
15
+ * Options:
16
+ * --site <id> Site config to use (looks for site-config.yml in CWD)
17
+ *
18
+ * Environment:
19
+ * SITE_CONFIG Path to site config file (overrides --site)
20
+ * SITE_ID Site config ID (overrides --site)
21
+ * GITHUB_TOKEN GitHub token for private repos
22
+ */
23
+
24
+ // Scripts are still .mjs (TODO.typescript/02). These imports will be fully
25
+ // typed once the scripts convert to .ts.
26
+ // @ts-expect-error — .mjs module without declaration
27
+ import { loadSiteConfig } from '../scripts/load-site-config.mjs';
28
+ import { existsSync } from 'fs';
29
+ import { resolve, dirname } from 'path';
30
+ import { fileURLToPath } from 'url';
31
+
32
+ const __dirname: string = dirname(fileURLToPath(import.meta.url));
33
+ const pkgRoot: string = resolve(__dirname, '..');
34
+
35
+ interface ParsedArgs {
36
+ positional: string[];
37
+ named: Record<string, string>;
38
+ }
39
+
40
+ type CommandRunner = () => Promise<void>;
41
+
42
+ const commands: Record<string, CommandRunner> = {
43
+ fetch: async () => (await import('../scripts/fetch-datasets.mjs' as string)).main(),
44
+ generate: async () => { await import('../scripts/generate-data.mjs' as string); },
45
+ edges: async () => (await import('../scripts/build-edges.js' as string)).main(),
46
+ about: async () => (await import('../scripts/process-about-pages.mjs' as string)).main(),
47
+ };
48
+
49
+ function parseArgs(argv: readonly string[]): ParsedArgs {
50
+ const positional: string[] = [];
51
+ const named: Record<string, string> = {};
52
+ for (let i = 0; i < argv.length; i++) {
53
+ if (argv[i].startsWith('--') && i + 1 < argv.length) {
54
+ named[argv[i].slice(2)] = argv[i + 1];
55
+ i++;
56
+ } else if (!argv[i].startsWith('-')) {
57
+ positional.push(argv[i]);
58
+ }
59
+ }
60
+ return { positional, named };
61
+ }
62
+
63
+ interface BrandingFonts {
64
+ source?: string;
65
+ family?: string;
66
+ weights?: number[];
67
+ }
68
+
69
+ interface BrandingConfig {
70
+ favicon?: string;
71
+ logo?: { localPath?: string };
72
+ primaryColor?: string;
73
+ fonts?: { header?: BrandingFonts; body?: BrandingFonts };
74
+ }
75
+
76
+ interface SiteConfig {
77
+ basePath?: string;
78
+ title?: string;
79
+ branding?: BrandingConfig;
80
+ }
81
+
82
+ interface FaviconResponse {
83
+ images: Array<{ name: string; contents: string | Buffer }>;
84
+ files: Array<{ name: string; contents: string | Buffer }>;
85
+ html: string[];
86
+ }
87
+
88
+ async function runBuildPipeline(config: SiteConfig | null): Promise<void> {
89
+ if (!process.env.BASE_PATH && config?.basePath) {
90
+ process.env.BASE_PATH = config.basePath;
91
+ }
92
+ for (const step of ['fetch', 'generate', 'edges', 'about'] as const) {
93
+ console.log(`\n=== ${step.toUpperCase()} ===\n`);
94
+ await commands[step]();
95
+ }
96
+
97
+ const fs = await import('fs');
98
+ const publicDir: string = resolve(process.cwd(), 'public');
99
+ fs.mkdirSync(publicDir, { recursive: true });
100
+
101
+ const branding: BrandingConfig = config?.branding ?? {};
102
+ const faviconSrc: string =
103
+ (branding.favicon && resolve(process.cwd(), branding.favicon)) ||
104
+ (branding.logo?.localPath && resolve(process.cwd(), branding.logo.localPath)) ||
105
+ resolve(pkgRoot, 'public', 'favicon.svg');
106
+
107
+ let faviconHtml = '';
108
+ if (fs.existsSync(faviconSrc)) {
109
+ console.log(`\n=== FAVICONS ===\n`);
110
+ const favicons = (await import('favicons')).default;
111
+ const source: Buffer = fs.readFileSync(faviconSrc);
112
+ const response: FaviconResponse = await favicons(source, {
113
+ appName: config?.title || 'Glossarist',
114
+ background: branding.primaryColor || '#2563eb',
115
+ theme_color: branding.primaryColor || '#2563eb',
116
+ icons: {
117
+ android: false,
118
+ appleIcon: true,
119
+ appleStartup: false,
120
+ favicons: true,
121
+ windows: false,
122
+ yandex: false,
123
+ },
124
+ });
125
+
126
+ for (const img of response.images) {
127
+ fs.writeFileSync(resolve(publicDir, img.name), img.contents);
128
+ }
129
+ for (const file of response.files) {
130
+ fs.writeFileSync(resolve(publicDir, file.name), file.contents);
131
+ }
132
+
133
+ if (faviconSrc.endsWith('.svg')) {
134
+ fs.copyFileSync(faviconSrc, resolve(publicDir, 'favicon.svg'));
135
+ }
136
+
137
+ faviconHtml = response.html.join('\n ');
138
+ if (faviconSrc.endsWith('.svg')) {
139
+ faviconHtml += '\n <link rel="icon" type="image/svg+xml" href="/favicon.svg">';
140
+ }
141
+ fs.writeFileSync(resolve(publicDir, 'favicon-links.html'), faviconHtml);
142
+ console.log(` Generated ${response.images.length} favicon files`);
143
+ }
144
+
145
+ if (faviconHtml) {
146
+ const basePath: string = process.env.BASE_PATH?.replace(/\/+$/, '') || '';
147
+ if (basePath) {
148
+ faviconHtml = faviconHtml.replace(/(href|content)="\/([^"]+)"/g, `$1="${basePath}/$2"`);
149
+ }
150
+ process.env.FAVICON_HTML = faviconHtml;
151
+ }
152
+
153
+ process.env.SITE_TITLE = config?.title || 'Glossarist';
154
+ if (branding.fonts?.header || branding.fonts?.body) {
155
+ const fontFamilies: string[] = [];
156
+ if (branding.fonts.header?.source === 'google') {
157
+ const w: string = (branding.fonts.header.weights || [400, 700]).join(';');
158
+ fontFamilies.push(`family=${branding.fonts.header.family!.replace(/ /g, '+')}:wght@${w}`);
159
+ }
160
+ if (branding.fonts.body?.source === 'google') {
161
+ const w: string = (branding.fonts.body.weights || [400, 700]).join(';');
162
+ fontFamilies.push(`family=${branding.fonts.body.family!.replace(/ /g, '+')}:wght@${w}`);
163
+ }
164
+ if (fontFamilies.length) {
165
+ process.env.SITE_FONTS_URL = `https://fonts.googleapis.com/css2?${fontFamilies.join('&')}&display=swap`;
166
+ }
167
+ }
168
+
169
+ console.log(`\n=== BRIDGE DATA ===\n`);
170
+ const bridge: string = resolve(pkgRoot, 'scripts', 'bridge-to-astro.mjs');
171
+ if (existsSync(bridge)) {
172
+ await import(`file://${bridge}`);
173
+ }
174
+
175
+ console.log(`\n=== BUILD ASTRO ===\n`);
176
+ const astroConfig: string = resolve(pkgRoot, 'astro.config.mjs');
177
+ if (existsSync(astroConfig)) {
178
+ try {
179
+ const { build: astroBuild } = await import('astro');
180
+ await astroBuild({ root: pkgRoot, logLevel: 'info' });
181
+ } catch (e: unknown) {
182
+ const msg = e instanceof Error ? e.message : String(e);
183
+ console.warn(` Astro build failed (${msg}), falling back to Vite SPA`);
184
+ console.log(`\n=== BUILD SPA ===\n`);
185
+ const viteConfig: string = resolve(pkgRoot, 'vite.config.ts');
186
+ const { build: viteBuild } = await import('vite');
187
+ await viteBuild({ configFile: viteConfig, root: pkgRoot, mode: 'production' });
188
+ }
189
+ } else {
190
+ console.log(`\n=== BUILD SPA ===\n`);
191
+ const viteConfig: string = resolve(pkgRoot, 'vite.config.ts');
192
+ const { build: viteBuild } = await import('vite');
193
+ await viteBuild({ configFile: viteConfig, root: pkgRoot, mode: 'production' });
194
+ }
195
+
196
+ const postbuild: string = resolve(pkgRoot, 'scripts', 'generate-404.js');
197
+ if (existsSync(postbuild)) {
198
+ await import(`file://${postbuild}`);
199
+ }
200
+ }
201
+
202
+ async function runNormalize(): Promise<void> {
203
+ const mod = await import('../scripts/normalize-yaml.mjs' as string);
204
+ const normalizeYaml = mod.normalizeYaml;
205
+ const check: boolean = process.argv.includes('--check');
206
+ const paths: string[] = process.argv.slice(2).filter(a => !a.startsWith('-') && a !== 'normalize');
207
+ const { checked, nonNfc, fixed }: { checked: number; nonNfc: number; fixed: string[] } = normalizeYaml({ check, paths });
208
+ if (check) {
209
+ if (nonNfc === 0) {
210
+ console.log(`NFC OK: ${checked} file(s) checked, all normalized`);
211
+ return;
212
+ }
213
+ console.error(`NFC check failed: ${nonNfc} of ${checked} file(s) are not NFC-normalized\n`);
214
+ for (const f of fixed) console.error(` ${f}`);
215
+ process.exit(1);
216
+ }
217
+ if (nonNfc === 0) {
218
+ console.log(`NFC OK: ${checked} file(s) checked, all already normalized`);
219
+ } else {
220
+ console.log(`Normalized ${nonNfc} of ${checked} file(s)`);
221
+ for (const f of fixed) console.log(` ${f}`);
222
+ }
223
+ }
224
+
225
+ async function main(): Promise<void> {
226
+ const args: readonly string[] = process.argv.slice(2);
227
+ const { positional, named }: ParsedArgs = parseArgs(args);
228
+ const cmd: string | undefined = positional[0];
229
+
230
+ if (!cmd || cmd === 'help' || cmd === '--help') {
231
+ console.log(`concept-browser <command> [options]
232
+
233
+ Commands:
234
+ fetch Fetch/update datasets from GCR packages or source repos
235
+ generate Convert YAML concepts to JSON-LD static data
236
+ edges Build cross-reference edges from generated concepts
237
+ about Compile per-dataset and per-group about pages
238
+ build Full pipeline (fetch + generate + edges + about + vite build)
239
+ site Same as build
240
+ normalize NFC-normalize YAML files in .datasets/
241
+ doctor Diagnose the local environment (deps, datasets, shapes, context)
242
+
243
+ Options:
244
+ --site <id> Site config ID (looks for site-config.yml in CWD)
245
+ --check (normalize only) Non-mutating; exit 1 if any file is not NFC
246
+
247
+ Environment:
248
+ SITE_CONFIG Site config file path (highest priority)
249
+ SITE_ID Site config ID (same as --site)
250
+ GITHUB_TOKEN GitHub token for private repos`);
251
+ process.exit(cmd ? 0 : 1);
252
+ }
253
+
254
+ if (!process.env.SITE_CONFIG && !process.env.SITE_ID && named.site) {
255
+ process.env.SITE_ID = named.site;
256
+ }
257
+ const { config }: { config: SiteConfig | null } = loadSiteConfig(named.site ? [named.site] : []);
258
+
259
+ if (cmd === 'build' || cmd === 'site') {
260
+ await runBuildPipeline(config);
261
+ return;
262
+ }
263
+
264
+ const runner: CommandRunner | undefined = commands[cmd];
265
+ if (runner) {
266
+ await runner();
267
+ return;
268
+ }
269
+
270
+ if (cmd === 'normalize') {
271
+ await runNormalize();
272
+ return;
273
+ }
274
+
275
+ if (cmd === 'doctor') {
276
+ const mod = await import('../scripts/doctor.mjs' as string);
277
+ await mod.main();
278
+ return;
279
+ }
280
+
281
+ console.error(`Unknown command: ${cmd}`);
282
+ console.error('Run `concept-browser help` for usage.');
283
+ process.exit(1);
284
+ }
285
+
286
+ main().catch((e: unknown) => {
287
+ console.error(e);
288
+ process.exit(1);
289
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@glossarist/concept-browser",
3
- "version": "0.7.101",
3
+ "version": "0.7.103",
4
4
  "description": "Vue SPA for browsing Glossarist terminology datasets with cross-reference resolution, graph visualization, and multi-language support",
5
5
  "type": "module",
6
6
  "bin": {
@@ -37,7 +37,7 @@
37
37
  "astro": "^7.0.7",
38
38
  "d3": "^7.9.0",
39
39
  "favicons": "^7.2.0",
40
- "glossarist": "^0.4.34",
40
+ "glossarist": "^0.4.51",
41
41
  "js-yaml": "^4.1.0",
42
42
  "jszip": "^3.10.1",
43
43
  "pinia": "^2.3.1",
@@ -87,8 +87,8 @@ function withSources(): Concept {
87
87
  type: 'authoritative',
88
88
  modification: 'revised 2024',
89
89
  origin: {
90
- ref: { source: 'ISO 704', id: '3.1', version: '2020' },
91
- locality: { type: 'clause', referenceFrom: '3.1', referenceTo: '3.5' },
90
+ ref: { source: 'ISO 704', id: '3.1', version: '2020' } as any,
91
+ locality: { type: 'clause', referenceFrom: '3.1', referenceTo: '3.5' } as any,
92
92
  link: 'https://example.org/iso-704',
93
93
  original: 'Original wording here.',
94
94
  },
@@ -98,7 +98,7 @@ function withSources(): Concept {
98
98
  type: 'lineage',
99
99
  origin: {
100
100
  ref: { source: 'IEC 60050', id: '102-01', version: '2008' },
101
- locality: { type: 'clause', referenceFrom: '102-01-01' },
101
+ locality: { type: 'clause', referenceFrom: '102-01-01' } as any,
102
102
  },
103
103
  },
104
104
  {
@@ -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';
@@ -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 {