@glossarist/concept-browser 0.7.114 → 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.
- package/README.md +45 -2
- package/cli/index.mjs +15 -15
- package/cli/index.ts +9 -11
- package/data/concept-model/shapes/glossarist.concept.yaml.schema.json +255 -0
- package/package.json +14 -12
- package/scripts/__tests__/{bibliography.test.mjs → bibliography.test.ts} +1 -1
- package/scripts/__tests__/{cli-script-contract.test.mjs → cli-script-contract.test.ts} +3 -3
- package/scripts/__tests__/{concept-groups.test.mjs → concept-groups.test.ts} +1 -1
- package/scripts/__tests__/{doctor.test.mjs → doctor.test.ts} +1 -1
- package/scripts/__tests__/{fetch-datasets.test.mjs → fetch-datasets.test.ts} +1 -1
- package/scripts/__tests__/{first-non-empty.test.mjs → first-non-empty.test.ts} +1 -1
- package/scripts/__tests__/{process-about-pages.test.mjs → process-about-pages.test.ts} +1 -1
- package/scripts/{bridge-to-astro.mjs → bridge-to-astro.ts} +2 -4
- package/scripts/{build-edges.js → build-edges.ts} +11 -13
- package/scripts/{check-syntax.mjs → check-syntax.ts} +20 -13
- package/scripts/{fetch-datasets.mjs → fetch-datasets.ts} +2 -2
- package/scripts/{generate-data.mjs → generate-data.ts} +73 -460
- package/scripts/{generate-ontology-schema.mjs → generate-ontology-schema.ts} +2 -2
- package/scripts/lib/{build-cache.mjs → build-cache.ts} +3 -2
- package/scripts/lib/concept-formats.ts +206 -0
- package/scripts/lib/concept-uri.ts +14 -0
- package/scripts/lib/{dataset-turtle.mjs → dataset-turtle.ts} +4 -3
- package/scripts/lib/harmonize-concept.ts +86 -0
- package/scripts/lib/jsonld-emitter.ts +169 -0
- package/scripts/lib/{local-path-safety.mjs → local-path-safety.ts} +6 -2
- package/scripts/lib/yaml-types.ts +262 -0
- package/scripts/{process-about-pages.mjs → process-about-pages.ts} +1 -1
- package/scripts/{smoke.mjs → smoke.ts} +4 -3
- package/scripts/{validate-shacl.mjs → validate-shacl.ts} +5 -5
- package/scripts/validate-yaml.ts +144 -0
- package/src/__tests__/extract-source-refs.test.ts +2 -3
- package/src/__tests__/perf/serialization-perf.test.ts +5 -5
- package/src/__tests__/scripts/agents-turtle.test.ts +1 -1
- package/src/__tests__/scripts/bibliography-turtle.test.ts +1 -1
- package/src/__tests__/scripts/build-activity-turtle.test.ts +1 -1
- package/src/__tests__/scripts/build-cache.test.ts +1 -1
- package/src/__tests__/scripts/build-integration.test.ts +19 -19
- package/src/__tests__/scripts/dataset-turtle.test.ts +1 -1
- package/src/__tests__/scripts/migrate-v1-to-v3.test.ts +1 -1
- package/src/__tests__/scripts/normalize-yaml.test.ts +1 -1
- package/src/__tests__/scripts/turtle-escape.test.ts +1 -1
- package/src/__tests__/scripts/validate-yaml.test.ts +76 -0
- package/src/__tests__/scripts/version-turtle.test.ts +1 -1
- package/src/__tests__/scripts/vocab-turtle.test.ts +1 -1
- package/src/__tests__/scripts-syntax.test.ts +2 -2
- package/src/__tests__/use-rdf-document.test.ts +2 -3
- package/src/__tests__/utils/content-renderer-xss.test.ts +57 -0
- package/src/__tests__/validate-shacl.test.ts +3 -3
- package/src/adapters/wire/index.ts +69 -0
- package/src/utils/content-renderer.ts +7 -3
- package/src/utils/url-safety.ts +1 -5
- package/tsconfig.json +2 -2
- package/vite.config.ts +1 -1
- package/src/__tests__/composables/use-concept-edges-v2.test.ts +0 -167
- package/src/__tests__/composables/use-concept-edges-v2.test.ts.bak +0 -166
- package/src/types/agents-version-turtle.d.ts +0 -27
- package/src/types/bibliography-turtle.d.ts +0 -12
- package/src/types/build-activity-turtle.d.ts +0 -16
- package/src/types/build-cache.d.ts +0 -20
- package/src/types/dataset-turtle.d.ts +0 -32
- package/src/types/normalize-yaml.d.ts +0 -16
- package/src/types/turtle-escape.d.ts +0 -6
- package/src/types/vocab-turtle.d.ts +0 -13
- /package/scripts/{doctor.mjs → doctor.ts} +0 -0
- /package/scripts/{extract-source-refs.js → extract-source-refs.ts} +0 -0
- /package/scripts/{generate-404.js → generate-404.ts} +0 -0
- /package/scripts/{generate-ontology-data.mjs → generate-ontology-data.ts} +0 -0
- /package/scripts/lib/{agents-turtle.mjs → agents-turtle.ts} +0 -0
- /package/scripts/lib/{bibliography-turtle.mjs → bibliography-turtle.ts} +0 -0
- /package/scripts/lib/{bibliography.mjs → bibliography.ts} +0 -0
- /package/scripts/lib/build/{image-assets.mjs → image-assets.ts} +0 -0
- /package/scripts/lib/build/{non-verbal-consumer.mjs → non-verbal-consumer.ts} +0 -0
- /package/scripts/lib/{build-activity-turtle.mjs → build-activity-turtle.ts} +0 -0
- /package/scripts/lib/{concept-groups.mjs → concept-groups.ts} +0 -0
- /package/scripts/lib/{first-non-empty.mjs → first-non-empty.ts} +0 -0
- /package/scripts/lib/{turtle-escape.mjs → turtle-escape.ts} +0 -0
- /package/scripts/lib/{version-turtle.mjs → version-turtle.ts} +0 -0
- /package/scripts/lib/{vocab-turtle.mjs → vocab-turtle.ts} +0 -0
- /package/scripts/{load-site-config.mjs → load-site-config.ts} +0 -0
- /package/scripts/{migrate-v1-to-v3.mjs → migrate-v1-to-v3.ts} +0 -0
- /package/scripts/{normalize-yaml.mjs → normalize-yaml.ts} +0 -0
- /package/scripts/{sync-concept-model.mjs → sync-concept-model.ts} +0 -0
package/README.md
CHANGED
|
@@ -39,14 +39,57 @@ Your project (CWD) Package (read-only)
|
|
|
39
39
|
│ └── my-vocab/ ─┤ ┌──────────────────────────┐
|
|
40
40
|
│ └── concepts/*.yaml ─┤ │ @glossarist/concept-browser │
|
|
41
41
|
├── public/ ─┤ ──> │ │
|
|
42
|
-
│ └── (logos, favicons) ─┤ │ generate-data.
|
|
43
|
-
├── .cb-content/ ← generated │ │ bridge-to-astro.
|
|
42
|
+
│ └── (logos, favicons) ─┤ │ generate-data.ts │
|
|
43
|
+
├── .cb-content/ ← generated │ │ bridge-to-astro.ts │
|
|
44
44
|
└── dist/ ← output │ │ astro build │
|
|
45
45
|
──┘ └──────────────────────────┘
|
|
46
46
|
```
|
|
47
47
|
|
|
48
48
|
The package reads your config and data, generates `.cb-content/` (content collections) and `dist/` (static site) in your project. The package itself (`node_modules/`) is never modified.
|
|
49
49
|
|
|
50
|
+
## Data / Deployment Boundary
|
|
51
|
+
|
|
52
|
+
There is a strict separation between **dataset authors** and **deployers**:
|
|
53
|
+
|
|
54
|
+
| Role | Owns | Does NOT touch |
|
|
55
|
+
|------|------|----------------|
|
|
56
|
+
| **Dataset author** | YAML concept files, bibliography, figures/tables | site-config.yml, deployment URLs, other datasets |
|
|
57
|
+
| **Deployer** | site-config.yml, URI patterns, branding, fonts | dataset content, inline references, concept IDs |
|
|
58
|
+
| **concept-browser** | Resolves every citation at runtime | — |
|
|
59
|
+
|
|
60
|
+
The same dataset YAML works in any deployment. A citation like `{{cite:iso704}}` resolves differently depending on which datasets are co-deployed — the deployer's `site-config.yml:uriPatterns` decides.
|
|
61
|
+
|
|
62
|
+
## Inline Content Syntax
|
|
63
|
+
|
|
64
|
+
All inline references in concept text use the unified `{{kind:target}}` notation:
|
|
65
|
+
|
|
66
|
+
| Kind | Syntax | Example | Description |
|
|
67
|
+
|------|--------|---------|-------------|
|
|
68
|
+
| cite | `{{cite:sourceId}}` | `{{cite:iso7301}}` | Cite a ConceptSource from this concept's `sources[]` |
|
|
69
|
+
| cite+label | `{{cite:id, label}}` | `{{cite:iso7301, rice}}` | Same, with explicit display label |
|
|
70
|
+
| urn | `{{urn:URN}}` | `{{urn:iso:std:iso:704}}` | Reference via URN routing |
|
|
71
|
+
| fig | `{{fig:id}}` | `{{fig:diagram_3}}` | Reference a figure entity |
|
|
72
|
+
| table | `{{table:id}}` | `{{table:unit_list}}` | Reference a table entity |
|
|
73
|
+
| formula | `{{formula:id}}` | `{{formula:ohm_law}}` | Reference a formula entity |
|
|
74
|
+
| bib | `{{bib:id}}` | `{{bib:ref_1}}` | Bibliography entry (no underlying concept) |
|
|
75
|
+
| link | `{{link:URL}}` | `{{link:https://example.com}}` | External URL |
|
|
76
|
+
| link+label | `{{link:URL, label}}` | `{{link:https://example.com, click here}}` | External URL with label |
|
|
77
|
+
| image | `{{image:src}}` | `{{image:diagram.png}}` | Inline image embed |
|
|
78
|
+
| image+alt | `{{image:src, alt}}` | `{{image:diagram.png, The diagram}}` | Image with alt text |
|
|
79
|
+
| designation | `{{designation}}` | `{{measurement unit}}` | Reference by designation text |
|
|
80
|
+
| numeric | `{{numeric_id}}` | `{{112-01-10}}` | Reference by numeric ID |
|
|
81
|
+
|
|
82
|
+
The deprecated `<<ref,title>>` AsciiDoc xref syntax still works but emits a console warning. Migrate to the unified syntax.
|
|
83
|
+
|
|
84
|
+
### Citation Resolution Cascade
|
|
85
|
+
|
|
86
|
+
When concept-browser encounters `{{cite:sourceId}}`, it walks this cascade:
|
|
87
|
+
|
|
88
|
+
1. **uriPatterns** — Is there a co-deployed dataset matching this URI? → internal link
|
|
89
|
+
2. **routing[]** — Is there a routing entry in site-config.yml? → external link
|
|
90
|
+
3. **citation.link** — Does the source have a canonical link? → flat bibliography record
|
|
91
|
+
4. **unresolved** — No match → plain text
|
|
92
|
+
|
|
50
93
|
## Configuration
|
|
51
94
|
|
|
52
95
|
Everything is in `site-config.yml`. See [`site-config.example.yml`](site-config.example.yml) for all options.
|
package/cli/index.mjs
CHANGED
|
@@ -3,15 +3,15 @@
|
|
|
3
3
|
/**
|
|
4
4
|
* Glossarist Concept Browser CLI — runtime entrypoint.
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
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.
|
|
6
|
+
* Registers tsx as the ESM loader so dynamic imports of .ts scripts
|
|
7
|
+
* work at runtime. tsx is a regular dependency (not devDep), so it
|
|
8
|
+
* survives --omit=dev.
|
|
12
9
|
*/
|
|
13
10
|
|
|
14
|
-
import {
|
|
11
|
+
import { register } from 'node:module';
|
|
12
|
+
register('tsx/esm', import.meta.url);
|
|
13
|
+
|
|
14
|
+
import { loadSiteConfig } from '../scripts/load-site-config.ts';
|
|
15
15
|
import { existsSync } from 'fs';
|
|
16
16
|
import { resolve, dirname } from 'path';
|
|
17
17
|
import { fileURLToPath } from 'url';
|
|
@@ -20,10 +20,10 @@ const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
|
20
20
|
const pkgRoot = resolve(__dirname, '..');
|
|
21
21
|
|
|
22
22
|
const commands = {
|
|
23
|
-
fetch: async () => (await import('../scripts/fetch-datasets.
|
|
24
|
-
generate: async () => { await import('../scripts/generate-data.
|
|
25
|
-
edges: async () => (await import('../scripts/build-edges.
|
|
26
|
-
about: async () => (await import('../scripts/process-about-pages.
|
|
23
|
+
fetch: async () => (await import('../scripts/fetch-datasets.ts')).main(),
|
|
24
|
+
generate: async () => { await import('../scripts/generate-data.ts'); },
|
|
25
|
+
edges: async () => (await import('../scripts/build-edges.ts')).main(),
|
|
26
|
+
about: async () => (await import('../scripts/process-about-pages.ts')).main(),
|
|
27
27
|
};
|
|
28
28
|
|
|
29
29
|
function parseArgs(argv) {
|
|
@@ -294,7 +294,7 @@ Environment:
|
|
|
294
294
|
}
|
|
295
295
|
|
|
296
296
|
console.log(`\n=== BRIDGE DATA ===\n`);
|
|
297
|
-
const bridge = resolve(pkgRoot, 'scripts', 'bridge-to-astro.
|
|
297
|
+
const bridge = resolve(pkgRoot, 'scripts', 'bridge-to-astro.ts');
|
|
298
298
|
if (existsSync(bridge)) {
|
|
299
299
|
await import(`file://${bridge}`);
|
|
300
300
|
}
|
|
@@ -319,7 +319,7 @@ Environment:
|
|
|
319
319
|
await viteBuild({ configFile: viteConfig, root: pkgRoot, mode: 'production' });
|
|
320
320
|
}
|
|
321
321
|
|
|
322
|
-
const postbuild = resolve(pkgRoot, 'scripts', 'generate-404.
|
|
322
|
+
const postbuild = resolve(pkgRoot, 'scripts', 'generate-404.ts');
|
|
323
323
|
if (existsSync(postbuild)) {
|
|
324
324
|
await import(`file://${postbuild}`);
|
|
325
325
|
}
|
|
@@ -334,7 +334,7 @@ Environment:
|
|
|
334
334
|
}
|
|
335
335
|
|
|
336
336
|
if (cmd === 'normalize') {
|
|
337
|
-
const { normalizeYaml } = await import('../scripts/normalize-yaml.
|
|
337
|
+
const { normalizeYaml } = await import('../scripts/normalize-yaml.ts');
|
|
338
338
|
const check = process.argv.includes('--check');
|
|
339
339
|
const paths = process.argv.slice(2).filter(a => !a.startsWith('-') && a !== 'normalize');
|
|
340
340
|
const { checked, nonNfc, fixed } = normalizeYaml({ check, paths });
|
|
@@ -357,7 +357,7 @@ Environment:
|
|
|
357
357
|
}
|
|
358
358
|
|
|
359
359
|
if (cmd === 'doctor') {
|
|
360
|
-
const { main: doctorMain } = await import('../scripts/doctor.
|
|
360
|
+
const { main: doctorMain } = await import('../scripts/doctor.ts');
|
|
361
361
|
await doctorMain();
|
|
362
362
|
return;
|
|
363
363
|
}
|
package/cli/index.ts
CHANGED
|
@@ -21,10 +21,7 @@
|
|
|
21
21
|
* GITHUB_TOKEN GitHub token for private repos
|
|
22
22
|
*/
|
|
23
23
|
|
|
24
|
-
|
|
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';
|
|
24
|
+
import { loadSiteConfig } from '../scripts/load-site-config';
|
|
28
25
|
import { existsSync } from 'fs';
|
|
29
26
|
import { resolve, dirname } from 'path';
|
|
30
27
|
import { fileURLToPath } from 'url';
|
|
@@ -40,10 +37,11 @@ interface ParsedArgs {
|
|
|
40
37
|
type CommandRunner = () => Promise<void>;
|
|
41
38
|
|
|
42
39
|
const commands: Record<string, CommandRunner> = {
|
|
43
|
-
fetch:
|
|
44
|
-
generate:
|
|
45
|
-
edges:
|
|
46
|
-
about:
|
|
40
|
+
fetch: async () => (await import('../scripts/fetch-datasets')).main(),
|
|
41
|
+
generate: async () => { await import('../scripts/generate-data'); },
|
|
42
|
+
edges: async () => (await import('../scripts/build-edges')).main(),
|
|
43
|
+
about: async () => (await import('../scripts/process-about-pages')).main(),
|
|
44
|
+
'validate-yaml': async () => { await import('../scripts/validate-yaml'); },
|
|
47
45
|
};
|
|
48
46
|
|
|
49
47
|
function parseArgs(argv: readonly string[]): ParsedArgs {
|
|
@@ -308,7 +306,7 @@ async function runBuildPipeline(config: SiteConfig | null): Promise<void> {
|
|
|
308
306
|
}
|
|
309
307
|
|
|
310
308
|
console.log(`\n=== BRIDGE DATA ===\n`);
|
|
311
|
-
const bridge: string = resolve(pkgRoot, 'scripts', 'bridge-to-astro.
|
|
309
|
+
const bridge: string = resolve(pkgRoot, 'scripts', 'bridge-to-astro.ts');
|
|
312
310
|
if (existsSync(bridge)) {
|
|
313
311
|
await import(`file://${bridge}`);
|
|
314
312
|
}
|
|
@@ -341,7 +339,7 @@ async function runBuildPipeline(config: SiteConfig | null): Promise<void> {
|
|
|
341
339
|
}
|
|
342
340
|
|
|
343
341
|
async function runNormalize(): Promise<void> {
|
|
344
|
-
const mod = await import('../scripts/normalize-yaml
|
|
342
|
+
const mod = await import('../scripts/normalize-yaml');
|
|
345
343
|
const normalizeYaml = mod.normalizeYaml;
|
|
346
344
|
const check: boolean = process.argv.includes('--check');
|
|
347
345
|
const paths: string[] = process.argv.slice(2).filter(a => !a.startsWith('-') && a !== 'normalize');
|
|
@@ -414,7 +412,7 @@ Environment:
|
|
|
414
412
|
}
|
|
415
413
|
|
|
416
414
|
if (cmd === 'doctor') {
|
|
417
|
-
const mod = await import('../scripts/doctor
|
|
415
|
+
const mod = await import('../scripts/doctor');
|
|
418
416
|
await mod.main();
|
|
419
417
|
return;
|
|
420
418
|
}
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://glossarist.org/concept-model/shapes/glossarist.concept.yaml.schema.json",
|
|
4
|
+
"title": "Glossarist Concept YAML",
|
|
5
|
+
"description": "Schema for dataset author YAML files. Derived from scripts/lib/yaml-types.ts — the TypeScript interfaces are the source of truth.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"required": ["termid"],
|
|
8
|
+
"properties": {
|
|
9
|
+
"termid": {
|
|
10
|
+
"type": "string",
|
|
11
|
+
"description": "Unique concept identifier within the dataset"
|
|
12
|
+
},
|
|
13
|
+
"id": { "type": "string" },
|
|
14
|
+
"uri": { "type": "string", "format": "uri" },
|
|
15
|
+
"status": {
|
|
16
|
+
"type": "string",
|
|
17
|
+
"enum": ["valid", "not_valid", "withdrawn", "draft", "superseded"]
|
|
18
|
+
},
|
|
19
|
+
"_related": {
|
|
20
|
+
"type": "array",
|
|
21
|
+
"items": { "$ref": "#/$defs/relation" }
|
|
22
|
+
},
|
|
23
|
+
"_partitiveRelations": {
|
|
24
|
+
"type": "array",
|
|
25
|
+
"items": { "$ref": "#/$defs/partitiveRelation" }
|
|
26
|
+
},
|
|
27
|
+
"_genericRelations": {
|
|
28
|
+
"type": "array",
|
|
29
|
+
"items": { "$ref": "#/$defs/genericRelation" }
|
|
30
|
+
},
|
|
31
|
+
"_domains": {
|
|
32
|
+
"type": "array",
|
|
33
|
+
"items": { "type": "string" }
|
|
34
|
+
},
|
|
35
|
+
"_dates": {
|
|
36
|
+
"type": "array",
|
|
37
|
+
"items": { "$ref": "#/$defs/date" }
|
|
38
|
+
},
|
|
39
|
+
"_sources": {
|
|
40
|
+
"type": "array",
|
|
41
|
+
"items": { "$ref": "#/$defs/source" }
|
|
42
|
+
},
|
|
43
|
+
"_status": { "type": "string" },
|
|
44
|
+
"_schemaVersion": { "type": "string" },
|
|
45
|
+
"_dateAccepted": { "type": "string", "format": "date" }
|
|
46
|
+
},
|
|
47
|
+
"patternProperties": {
|
|
48
|
+
"^[a-z]{3}$": { "$ref": "#/$defs/localization" }
|
|
49
|
+
},
|
|
50
|
+
"additionalProperties": true,
|
|
51
|
+
|
|
52
|
+
"$defs": {
|
|
53
|
+
"localization": {
|
|
54
|
+
"type": "object",
|
|
55
|
+
"properties": {
|
|
56
|
+
"language_code": { "type": "string", "pattern": "^[a-z]{3}$" },
|
|
57
|
+
"terms": { "type": "array", "items": { "$ref": "#/$defs/term" } },
|
|
58
|
+
"definition": { "type": "array", "items": { "$ref": "#/$defs/content" } },
|
|
59
|
+
"notes": { "type": "array", "items": { "$ref": "#/$defs/content" } },
|
|
60
|
+
"examples": { "type": "array", "items": { "$ref": "#/$defs/content" } },
|
|
61
|
+
"annotations": { "type": "array", "items": { "$ref": "#/$defs/content" } },
|
|
62
|
+
"sources": { "type": "array", "items": { "$ref": "#/$defs/source" } },
|
|
63
|
+
"dates": { "type": "array", "items": { "$ref": "#/$defs/date" } },
|
|
64
|
+
"domain": { "type": "array", "items": { "type": "string" } },
|
|
65
|
+
"entry_status": {
|
|
66
|
+
"type": "string",
|
|
67
|
+
"enum": ["valid", "not_valid", "withdrawn", "draft", "superseded"]
|
|
68
|
+
},
|
|
69
|
+
"classification": { "type": "string" },
|
|
70
|
+
"release": { "type": "string" }
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
|
|
74
|
+
"term": {
|
|
75
|
+
"type": "object",
|
|
76
|
+
"required": ["designation"],
|
|
77
|
+
"properties": {
|
|
78
|
+
"type": {
|
|
79
|
+
"type": "string",
|
|
80
|
+
"enum": ["expression", "abbreviation", "symbol", "letter_symbol", "graphical symbol"]
|
|
81
|
+
},
|
|
82
|
+
"designation": { "type": "string" },
|
|
83
|
+
"normative_status": {
|
|
84
|
+
"type": "string",
|
|
85
|
+
"enum": ["preferred", "admitted", "deprecated", ""]
|
|
86
|
+
},
|
|
87
|
+
"origin": { "$ref": "#/$defs/origin" },
|
|
88
|
+
"grammar_info": {
|
|
89
|
+
"type": "array",
|
|
90
|
+
"items": { "$ref": "#/$defs/grammarInfo" }
|
|
91
|
+
},
|
|
92
|
+
"international": { "type": "boolean" },
|
|
93
|
+
"absent": { "type": "boolean" },
|
|
94
|
+
"geographical_area": { "type": "string" },
|
|
95
|
+
"term_type": { "type": "string" },
|
|
96
|
+
"prefix": { "type": "string" },
|
|
97
|
+
"usage_info": { "type": "string" },
|
|
98
|
+
"field_of_application": { "type": "string" }
|
|
99
|
+
}
|
|
100
|
+
},
|
|
101
|
+
|
|
102
|
+
"content": {
|
|
103
|
+
"type": "object",
|
|
104
|
+
"properties": {
|
|
105
|
+
"content": { "type": "string" }
|
|
106
|
+
}
|
|
107
|
+
},
|
|
108
|
+
|
|
109
|
+
"source": {
|
|
110
|
+
"type": "object",
|
|
111
|
+
"properties": {
|
|
112
|
+
"id": { "type": "string" },
|
|
113
|
+
"type": {
|
|
114
|
+
"type": "string",
|
|
115
|
+
"enum": ["authoritative", "lineage", "adapted"]
|
|
116
|
+
},
|
|
117
|
+
"status": {
|
|
118
|
+
"type": "string",
|
|
119
|
+
"enum": ["identical", "modified", "restyled", "context-added", "extracted", "expanded-coverage"]
|
|
120
|
+
},
|
|
121
|
+
"origin": { "$ref": "#/$defs/origin" }
|
|
122
|
+
}
|
|
123
|
+
},
|
|
124
|
+
|
|
125
|
+
"origin": {
|
|
126
|
+
"type": "object",
|
|
127
|
+
"properties": {
|
|
128
|
+
"ref": {
|
|
129
|
+
"type": "object",
|
|
130
|
+
"properties": {
|
|
131
|
+
"source": { "type": "string" },
|
|
132
|
+
"id": { "type": "string" },
|
|
133
|
+
"version": { "type": "string" },
|
|
134
|
+
"text": { "type": "string" }
|
|
135
|
+
}
|
|
136
|
+
},
|
|
137
|
+
"locality": {
|
|
138
|
+
"type": "object",
|
|
139
|
+
"properties": {
|
|
140
|
+
"type": { "type": "string" },
|
|
141
|
+
"reference_from": { "type": "string" },
|
|
142
|
+
"reference_to": { "type": "string" }
|
|
143
|
+
}
|
|
144
|
+
},
|
|
145
|
+
"link": { "type": "string", "format": "uri" }
|
|
146
|
+
}
|
|
147
|
+
},
|
|
148
|
+
|
|
149
|
+
"grammarInfo": {
|
|
150
|
+
"type": "object",
|
|
151
|
+
"properties": {
|
|
152
|
+
"gender": { "type": "string" },
|
|
153
|
+
"number": { "type": "string" },
|
|
154
|
+
"partOfSpeech": { "type": "string" },
|
|
155
|
+
"noun": { "type": "boolean" },
|
|
156
|
+
"verb": { "type": "boolean" },
|
|
157
|
+
"adj": { "type": "boolean" },
|
|
158
|
+
"adverb": { "type": "boolean" },
|
|
159
|
+
"preposition": { "type": "boolean" },
|
|
160
|
+
"participle": { "type": "boolean" }
|
|
161
|
+
}
|
|
162
|
+
},
|
|
163
|
+
|
|
164
|
+
"date": {
|
|
165
|
+
"type": "object",
|
|
166
|
+
"properties": {
|
|
167
|
+
"type": { "type": "string" },
|
|
168
|
+
"date": { "type": "string", "format": "date" }
|
|
169
|
+
}
|
|
170
|
+
},
|
|
171
|
+
|
|
172
|
+
"relation": {
|
|
173
|
+
"type": "object",
|
|
174
|
+
"properties": {
|
|
175
|
+
"type": { "type": "string" },
|
|
176
|
+
"target": {
|
|
177
|
+
"oneOf": [
|
|
178
|
+
{ "type": "string" },
|
|
179
|
+
{
|
|
180
|
+
"type": "object",
|
|
181
|
+
"properties": {
|
|
182
|
+
"ref": { "type": "string" },
|
|
183
|
+
"uri": { "type": "string", "format": "uri" }
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
]
|
|
187
|
+
},
|
|
188
|
+
"content": { "type": "string" },
|
|
189
|
+
"label": { "type": "string" }
|
|
190
|
+
}
|
|
191
|
+
},
|
|
192
|
+
|
|
193
|
+
"partitiveRelation": {
|
|
194
|
+
"type": "object",
|
|
195
|
+
"properties": {
|
|
196
|
+
"type": { "type": "string" },
|
|
197
|
+
"target": { "type": "string" },
|
|
198
|
+
"members": {
|
|
199
|
+
"type": "array",
|
|
200
|
+
"items": { "$ref": "#/$defs/partitiveMember" }
|
|
201
|
+
},
|
|
202
|
+
"criterion": { "type": "string" },
|
|
203
|
+
"multiplicity": {
|
|
204
|
+
"type": "string",
|
|
205
|
+
"enum": ["compulsory", "optional", "compulsory_multiple", "optional_multiple", "compulsory_at_least_one"]
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
},
|
|
209
|
+
|
|
210
|
+
"partitiveMember": {
|
|
211
|
+
"type": "object",
|
|
212
|
+
"properties": {
|
|
213
|
+
"ref": { "type": "string" },
|
|
214
|
+
"uri": { "type": "string", "format": "uri" },
|
|
215
|
+
"presence": {
|
|
216
|
+
"type": "string",
|
|
217
|
+
"enum": ["required", "optional"]
|
|
218
|
+
},
|
|
219
|
+
"count": {
|
|
220
|
+
"type": "string",
|
|
221
|
+
"enum": ["exactly_one", "at_least_one", "multiple"]
|
|
222
|
+
},
|
|
223
|
+
"delimiting": { "type": "boolean" },
|
|
224
|
+
"multiplicity": {
|
|
225
|
+
"type": "string",
|
|
226
|
+
"enum": ["compulsory", "optional", "compulsory_multiple", "optional_multiple", "compulsory_at_least_one"]
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
},
|
|
230
|
+
|
|
231
|
+
"genericRelation": {
|
|
232
|
+
"type": "object",
|
|
233
|
+
"properties": {
|
|
234
|
+
"type": { "type": "string" },
|
|
235
|
+
"target": { "type": "string" },
|
|
236
|
+
"members": {
|
|
237
|
+
"type": "array",
|
|
238
|
+
"items": {
|
|
239
|
+
"oneOf": [
|
|
240
|
+
{ "type": "string" },
|
|
241
|
+
{
|
|
242
|
+
"type": "object",
|
|
243
|
+
"properties": {
|
|
244
|
+
"ref": { "type": "string" },
|
|
245
|
+
"uri": { "type": "string", "format": "uri" }
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
]
|
|
249
|
+
}
|
|
250
|
+
},
|
|
251
|
+
"criterion": { "type": "string" }
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@glossarist/concept-browser",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.115",
|
|
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": {
|
|
@@ -9,22 +9,23 @@
|
|
|
9
9
|
"scripts": {
|
|
10
10
|
"dev": "vite",
|
|
11
11
|
"build": "vue-tsc --noEmit && vite build",
|
|
12
|
-
"postbuild": "
|
|
12
|
+
"postbuild": "tsx scripts/generate-404.ts",
|
|
13
13
|
"preview": "vite preview",
|
|
14
|
-
"check:scripts": "
|
|
15
|
-
"
|
|
16
|
-
"
|
|
17
|
-
"
|
|
18
|
-
"
|
|
19
|
-
"
|
|
20
|
-
"
|
|
21
|
-
"
|
|
14
|
+
"check:scripts": "tsx scripts/check-syntax.ts",
|
|
15
|
+
"validate:yaml": "tsx scripts/validate-yaml.ts",
|
|
16
|
+
"validate:shacl": "tsx scripts/validate-shacl.ts",
|
|
17
|
+
"pretest": "tsx scripts/check-syntax.ts",
|
|
18
|
+
"prebuild": "tsx scripts/check-syntax.ts",
|
|
19
|
+
"fetch-datasets": "tsx scripts/fetch-datasets.ts",
|
|
20
|
+
"generate-data": "tsx scripts/generate-data.ts",
|
|
21
|
+
"generate-ontology": "tsx scripts/generate-ontology-data.ts && tsx scripts/generate-ontology-schema.ts",
|
|
22
|
+
"sync:model": "tsx scripts/sync-concept-model.ts",
|
|
23
|
+
"build:full": "npm run generate-ontology && npm run fetch-datasets && npm run generate-data && tsx scripts/build-edges.ts && npm run build",
|
|
22
24
|
"build:site": "concept-browser --site build",
|
|
23
|
-
"smoke": "
|
|
25
|
+
"smoke": "tsx scripts/smoke.ts",
|
|
24
26
|
"test": "vitest run",
|
|
25
27
|
"test:watch": "vitest",
|
|
26
28
|
"mutation:test": "stryker run stryker.config.mjs",
|
|
27
|
-
"validate:shacl": "node scripts/validate-shacl.mjs",
|
|
28
29
|
"dev:astro": "astro dev",
|
|
29
30
|
"build:astro": "astro build",
|
|
30
31
|
"preview:astro": "astro preview"
|
|
@@ -44,6 +45,7 @@
|
|
|
44
45
|
"pinia": "^2.3.1",
|
|
45
46
|
"sharp": "^0.34.5",
|
|
46
47
|
"tailwindcss": "^4.3.2",
|
|
48
|
+
"tsx": "^4.23.4",
|
|
47
49
|
"vite": "^8.1.4",
|
|
48
50
|
"vue": "^3.5.13",
|
|
49
51
|
"vue-router": "^4.5.1"
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, it, expect } from 'vitest';
|
|
2
|
-
import { normalizeBibliography } from '../lib/bibliography
|
|
2
|
+
import { normalizeBibliography } from '../lib/bibliography';
|
|
3
3
|
|
|
4
4
|
describe('normalizeBibliography', () => {
|
|
5
5
|
it('wraps a bare array into { bibliography: [...] }', () => {
|
|
@@ -13,9 +13,9 @@ import { resolve, dirname } from 'node:path';
|
|
|
13
13
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
14
14
|
|
|
15
15
|
const SCRIPT_PATHS = [
|
|
16
|
-
'../../scripts/fetch-datasets.
|
|
17
|
-
'../../scripts/build-edges.
|
|
18
|
-
'../../scripts/process-about-pages.
|
|
16
|
+
'../../scripts/fetch-datasets.ts',
|
|
17
|
+
'../../scripts/build-edges.ts',
|
|
18
|
+
'../../scripts/process-about-pages.ts',
|
|
19
19
|
];
|
|
20
20
|
|
|
21
21
|
describe('CLI script contract: every script exports main()', () => {
|
|
@@ -3,7 +3,7 @@ import { writeFileSync, mkdirSync, rmSync, existsSync } from 'node:fs';
|
|
|
3
3
|
import { resolve, join } from 'node:path';
|
|
4
4
|
import { mkdtempSync } from 'node:fs';
|
|
5
5
|
import { tmpdir } from 'node:os';
|
|
6
|
-
import { runDoctor, formatResults, CHECKS, NODE_MIN_MAJOR } from '../doctor
|
|
6
|
+
import { runDoctor, formatResults, CHECKS, NODE_MIN_MAJOR } from '../doctor';
|
|
7
7
|
|
|
8
8
|
function makeTmpProject() {
|
|
9
9
|
const dir = mkdtempSync(join(tmpdir(), 'doctor-'));
|
|
@@ -2,7 +2,7 @@ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
|
2
2
|
import fs from 'fs';
|
|
3
3
|
import path from 'path';
|
|
4
4
|
import os from 'os';
|
|
5
|
-
import { assertLocalPathSafe } from '../lib/local-path-safety
|
|
5
|
+
import { assertLocalPathSafe } from '../lib/local-path-safety';
|
|
6
6
|
|
|
7
7
|
function makeTmpTree() {
|
|
8
8
|
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'cb-fetch-'));
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, it, expect } from 'vitest';
|
|
2
|
-
import { firstNonEmpty } from '../lib/first-non-empty
|
|
2
|
+
import { firstNonEmpty } from '../lib/first-non-empty';
|
|
3
3
|
|
|
4
4
|
describe('firstNonEmpty — register-wins resolution', () => {
|
|
5
5
|
it('returns the first value when both are present (register wins)', () => {
|
|
@@ -4,7 +4,7 @@ import { join, dirname } from 'node:path';
|
|
|
4
4
|
import { mkdtempSync } from 'node:fs';
|
|
5
5
|
import { tmpdir } from 'node:os';
|
|
6
6
|
|
|
7
|
-
const scriptPath = join(dirname(new URL(import.meta.url).pathname), '..', 'process-about-pages
|
|
7
|
+
const scriptPath = join(dirname(new URL(import.meta.url).pathname), '..', 'process-about-pages');
|
|
8
8
|
|
|
9
9
|
// Process-about-pages discovers per-dataset about content from either
|
|
10
10
|
// .datasets/<id>/about/ (fetched) OR site-config localPath overrides.
|
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
return `${uriBase}/${registerId}/concept/${conceptId}`;
|
|
3
|
-
}
|
|
1
|
+
import { buildConceptUri } from './lib/concept-uri';
|
|
4
2
|
|
|
5
3
|
|
|
6
4
|
/**
|
|
@@ -79,7 +77,7 @@ for (const reg of registry) {
|
|
|
79
77
|
if (index && index.concepts) {
|
|
80
78
|
for (const concept of index.concepts) {
|
|
81
79
|
if (!concept) continue;
|
|
82
|
-
const conceptEntry = {
|
|
80
|
+
const conceptEntry: Record<string, any> = {
|
|
83
81
|
registerId: id,
|
|
84
82
|
conceptId: concept.id,
|
|
85
83
|
uri: buildConceptUri(entry.uriBase || '', id, concept.id),
|
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
return `${uriBase}/${registerId}/concept/${conceptId}`;
|
|
3
|
-
}
|
|
1
|
+
import { buildConceptUri } from './lib/concept-uri';
|
|
4
2
|
|
|
5
3
|
const VALID_COMPLETENESS = new Set(['complete', 'partial']);
|
|
6
4
|
|
|
@@ -97,14 +95,14 @@ function slugify(text) {
|
|
|
97
95
|
|
|
98
96
|
// --- Extractors (open/closed: add new extractors to EXTRACTORS array) ---
|
|
99
97
|
|
|
100
|
-
function extractReferences(concept, registerId) {
|
|
101
|
-
const edges = [];
|
|
98
|
+
function extractReferences(concept: Record<string, any>, registerId: string) {
|
|
99
|
+
const edges: Record<string, any>[] = [];
|
|
102
100
|
const sourceUri = concept['@id'];
|
|
103
|
-
for (const [lang, lc] of Object.entries(concept['gl:localizedConcept'] || {})) {
|
|
101
|
+
for (const [lang, lc] of Object.entries(concept['gl:localizedConcept'] || {}) as [string, any][]) {
|
|
104
102
|
if (lc['gl:references']) {
|
|
105
|
-
for (const ref of lc['gl:references']) {
|
|
103
|
+
for (const ref of lc['gl:references'] as Record<string, any>[]) {
|
|
106
104
|
if (ref['@id'] && ref['@id'] !== sourceUri) {
|
|
107
|
-
const edge = {
|
|
105
|
+
const edge: Record<string, any> = {
|
|
108
106
|
source: sourceUri,
|
|
109
107
|
target: ref['@id'],
|
|
110
108
|
type: ref['@id'].startsWith('cite:') ? 'citation' : 'references',
|
|
@@ -368,11 +366,11 @@ function buildEdgesForDataset(datasetDir, registerId, uriBase, urnMap, manifest)
|
|
|
368
366
|
const uriBase = manifest.uriBase;
|
|
369
367
|
if (!uriBase) throw new Error('build-edges: manifest.uriBase is required');
|
|
370
368
|
|
|
371
|
-
function buildSectionNode(section, idx) {
|
|
369
|
+
function buildSectionNode(section: Record<string, any>, idx: number): Record<string, any> {
|
|
372
370
|
const sectionId = `section-${section.id}`;
|
|
373
371
|
const domainUri = `${uriBase}/${registerId}/domain/${sectionId}`;
|
|
374
372
|
const domainLabel = section.names?.eng || section.id;
|
|
375
|
-
const node = {
|
|
373
|
+
const node: Record<string, any> = {
|
|
376
374
|
uri: domainUri,
|
|
377
375
|
id: sectionId,
|
|
378
376
|
names: section.names || {},
|
|
@@ -525,7 +523,7 @@ if (auditUnmatched.size > 0) {
|
|
|
525
523
|
|
|
526
524
|
// Build cross-reference index: for each dataset, which other datasets'
|
|
527
525
|
// edges.json contains edges targeting that dataset's URIs.
|
|
528
|
-
const datasetUriPrefixes = new Map();
|
|
526
|
+
const datasetUriPrefixes = new Map<string, string>();
|
|
529
527
|
for (const [ds, manifest] of manifestCache) {
|
|
530
528
|
const uriBase = manifest.uriBase;
|
|
531
529
|
if (!uriBase) throw new Error('build-edges: manifest.uriBase is required');
|
|
@@ -538,7 +536,7 @@ for (const ds of datasets) {
|
|
|
538
536
|
}
|
|
539
537
|
|
|
540
538
|
for (const [sourceDs, edges] of allDatasetEdges) {
|
|
541
|
-
const targets = new Set();
|
|
539
|
+
const targets = new Set<string>();
|
|
542
540
|
for (const edge of edges) {
|
|
543
541
|
for (const [targetDs, prefix] of datasetUriPrefixes) {
|
|
544
542
|
if (targetDs !== sourceDs && edge.target.startsWith(prefix)) {
|
|
@@ -561,7 +559,7 @@ for (const [sourceDs, edges] of allDatasetEdges) {
|
|
|
561
559
|
|
|
562
560
|
const crossRefPath = join(DATA_DIR, 'cross-ref-index.json');
|
|
563
561
|
writeFileSync(crossRefPath, JSON.stringify(crossRefIndex));
|
|
564
|
-
const refCount = Object.values(crossRefIndex).reduce((sum, arr) => sum + arr.length, 0);
|
|
562
|
+
const refCount = Object.values(crossRefIndex).reduce((sum: number, arr: string[]) => sum + arr.length, 0);
|
|
565
563
|
console.log(`Written cross-ref-index.json (${refCount} cross-references across ${datasets.length} datasets)`);
|
|
566
564
|
|
|
567
565
|
console.log('Done.');
|