@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
@@ -0,0 +1,262 @@
1
+ /**
2
+ * YAML author-format TypeScript interfaces.
3
+ *
4
+ * These types describe the YAML wire format that dataset authors write
5
+ * and that `scripts/generate-data.ts` reads. They are the author-facing
6
+ * contract — the complement to `src/adapters/jsonld-types.ts` (the
7
+ * browser-facing JSON-LD wire format).
8
+ *
9
+ * MECE boundary:
10
+ * - yaml-types.ts → INPUT to the build pipeline (what authors write)
11
+ * - jsonld-types.ts → OUTPUT of the build pipeline (what the browser reads)
12
+ * - model-bridge.ts → converts JSON-LD → glossarist-js Concept instances
13
+ *
14
+ * Model-driven: field names match the YAML keys authors actually write
15
+ * (snake_case for author-facing, camelCase for internal normalized form).
16
+ */
17
+
18
+ // ── Content blocks ─────────────────────────────────────────────────────────
19
+
20
+ export interface YamlContent {
21
+ content?: string;
22
+ }
23
+
24
+ // ── Terms / designations ──────────────────────────────────────────────────
25
+
26
+ export interface YamlTerm {
27
+ type?: string;
28
+ designation?: string;
29
+ normative_status?: string;
30
+ origin?: YamlOrigin;
31
+ grammar_info?: YamlGrammarInfo[];
32
+ english?: string[];
33
+ }
34
+
35
+ export interface YamlOrigin {
36
+ source?: string;
37
+ ref?: string;
38
+ version?: string;
39
+ }
40
+
41
+ export interface YamlGrammarInfo {
42
+ gender?: string;
43
+ number?: string;
44
+ partOfSpeech?: string;
45
+ noun?: boolean;
46
+ verb?: boolean;
47
+ adj?: boolean;
48
+ adverb?: boolean;
49
+ preposition?: boolean;
50
+ participle?: boolean;
51
+ }
52
+
53
+ // ── Sources ───────────────────────────────────────────────────────────────
54
+
55
+ export interface YamlSource {
56
+ origin?: YamlOrigin;
57
+ ref?: string;
58
+ link?: string;
59
+ relationship_type?: string;
60
+ status?: string;
61
+ title?: string;
62
+ type?: string;
63
+ }
64
+
65
+ // ── Dates ─────────────────────────────────────────────────────────────────
66
+
67
+ export interface YamlDate {
68
+ type?: string;
69
+ date?: string;
70
+ }
71
+
72
+ // ── Relations ─────────────────────────────────────────────────────────────
73
+
74
+ export interface YamlRelation {
75
+ type?: string;
76
+ target?: string | { ref?: string; uri?: string };
77
+ content?: string;
78
+ label?: string;
79
+ }
80
+
81
+ export interface YamlPartitiveMember {
82
+ ref?: string;
83
+ uri?: string;
84
+ presence?: string;
85
+ count?: string;
86
+ delimiting?: boolean;
87
+ multiplicity?: string;
88
+ certainty?: string;
89
+ designation?: string;
90
+ content?: string;
91
+ }
92
+
93
+ export interface YamlPartitiveRelation {
94
+ type?: string;
95
+ target?: string;
96
+ members?: YamlPartitiveMember[];
97
+ criterion?: string;
98
+ multiplicity?: string;
99
+ certainty?: string;
100
+ }
101
+
102
+ export interface YamlGenericRelation {
103
+ type?: string;
104
+ target?: string;
105
+ members?: (string | { ref?: string; uri?: string })[];
106
+ criterion?: string;
107
+ }
108
+
109
+ // ── Localization (per-language concept data) ──────────────────────────────
110
+
111
+ export interface YamlLocalization {
112
+ language_code?: string;
113
+ terms?: YamlTerm[];
114
+ definition?: YamlContent[];
115
+ notes?: YamlContent[];
116
+ examples?: YamlContent[];
117
+ annotations?: YamlContent[];
118
+ sources?: YamlSource[];
119
+ dates?: YamlDate[];
120
+ domain?: string[];
121
+ references?: YamlSource[];
122
+ entry_status?: string;
123
+ classification?: string;
124
+ review_type?: string;
125
+ review_date?: string;
126
+ review_decision_date?: string;
127
+ review_decision_event?: string;
128
+ review_status?: string;
129
+ review_decision?: string;
130
+ review_decision_notes?: string;
131
+ lineage_source_similarity?: number;
132
+ release?: string;
133
+ script?: string;
134
+ system?: string;
135
+ [key: string]: unknown;
136
+ }
137
+
138
+ // ── Harmonized concept (output of loadConceptFile) ────────────────────────
139
+ //
140
+ // After loadConceptFile(), the concept is normalized to this shape
141
+ // regardless of whether the source was simple or managed format.
142
+ // The `_`-prefixed fields are managed-concept metadata hoisted to top level.
143
+
144
+ export interface HarmonizedConcept {
145
+ termid: string;
146
+ id?: string;
147
+ uri?: string;
148
+ status?: string;
149
+ _related?: YamlRelation[];
150
+ _partitiveRelations?: YamlPartitiveRelation[];
151
+ _genericRelations?: YamlGenericRelation[];
152
+ _domains?: string[];
153
+ _dates?: YamlDate[];
154
+ _sources?: YamlSource[];
155
+ _status?: string;
156
+ _schemaVersion?: string;
157
+ _dateAccepted?: string;
158
+ [lang: string]: unknown;
159
+ }
160
+
161
+ // ── Managed concept YAML doc (multi-doc format) ───────────────────────────
162
+
163
+ export interface YamlManagedConceptDoc {
164
+ data?: {
165
+ identifier?: string | number;
166
+ language_code?: string;
167
+ domains?: string[];
168
+ [key: string]: unknown;
169
+ };
170
+ related?: YamlRelation[];
171
+ partitive_relations?: YamlPartitiveRelation[];
172
+ generic_relations?: YamlGenericRelation[];
173
+ dates?: YamlDate[];
174
+ sources?: YamlSource[];
175
+ status?: string;
176
+ schema_version?: string;
177
+ date_accepted?: string;
178
+ terms?: YamlTerm[];
179
+ definition?: YamlContent[];
180
+ notes?: YamlContent[];
181
+ examples?: YamlContent[];
182
+ [key: string]: unknown;
183
+ }
184
+
185
+ // ── Dataset manifest ──────────────────────────────────────────────────────
186
+
187
+ export interface YamlManifestSection {
188
+ id?: string;
189
+ slug?: string;
190
+ title?: string;
191
+ name?: string;
192
+ members?: string[];
193
+ children?: YamlManifestSection[];
194
+ ordering?: string;
195
+ }
196
+
197
+ export interface YamlManifest {
198
+ id?: string;
199
+ title?: string;
200
+ description?: string;
201
+ lastUpdated?: string;
202
+ sourceRepoUrl?: string;
203
+ publisher?: string;
204
+ contactPoint?: string;
205
+ sections?: YamlManifestSection[];
206
+ languageOrder?: string[];
207
+ ref?: Record<string, string>;
208
+ refAliases?: string[];
209
+ editionStatus?: string;
210
+ ordering?: string;
211
+ [key: string]: unknown;
212
+ }
213
+
214
+ // ── Bibliography ──────────────────────────────────────────────────────────
215
+
216
+ export interface YamlBibliographyEntry {
217
+ reference?: string;
218
+ title?: string;
219
+ link?: string;
220
+ author?: string;
221
+ date?: string;
222
+ publisher?: string;
223
+ type?: string;
224
+ [key: string]: unknown;
225
+ }
226
+
227
+ // ── News/content frontmatter ──────────────────────────────────────────────
228
+
229
+ export interface YamlNewsFrontmatter {
230
+ title?: string;
231
+ date?: string;
232
+ categories?: string;
233
+ [key: string]: unknown;
234
+ }
235
+
236
+ // ── Content page config ───────────────────────────────────────────────────
237
+
238
+ export interface YamlContentPage {
239
+ route?: string;
240
+ source?: string;
241
+ title?: string;
242
+ translations?: Record<string, { source?: string; title?: string }>;
243
+ [key: string]: unknown;
244
+ }
245
+
246
+ // ── SHACL constraint (ontology schema) ────────────────────────────────────
247
+
248
+ export interface ShaclConstraint {
249
+ path?: string | null;
250
+ datatype?: string | null;
251
+ class?: string | null;
252
+ valuesFrom?: string | null;
253
+ nodeKind?: string | null;
254
+ minCount?: number | null;
255
+ maxCount?: number | null;
256
+ in?: string[];
257
+ [key: string]: unknown;
258
+ }
259
+
260
+ // ── Prefix map (Turtle emission) ──────────────────────────────────────────
261
+
262
+ export type PrefixMap = Record<string, string>;
@@ -19,7 +19,7 @@ import { join, basename, extname, resolve } from 'node:path';
19
19
  import { cwd } from 'node:process';
20
20
  import { fileURLToPath } from 'node:url';
21
21
 
22
- import { loadSiteConfig } from './load-site-config.mjs';
22
+ import { loadSiteConfig } from './load-site-config';
23
23
 
24
24
  export function loadSiteConfigForAbout() {
25
25
  try {
@@ -36,7 +36,7 @@ const DIST = pathResolve(__dirname, '..', 'dist');
36
36
  const MIME = {
37
37
  '.html': 'text/html; charset=utf-8',
38
38
  '.js': 'application/javascript; charset=utf-8',
39
- '.mjs': 'application/javascript; charset=utf-8',
39
+ '': 'application/javascript; charset=utf-8',
40
40
  '.css': 'text/css; charset=utf-8',
41
41
  '.json': 'application/json; charset=utf-8',
42
42
  '.svg': 'image/svg+xml',
@@ -53,7 +53,7 @@ const MIME = {
53
53
  '.webmanifest': 'application/manifest+json; charset=utf-8',
54
54
  };
55
55
 
56
- function serveDist(root) {
56
+ function serveDist(root: string): Promise<{ server: ReturnType<typeof createServer>; origin: string }> {
57
57
  return new Promise((resolve) => {
58
58
  const server = createServer((req, res) => {
59
59
  try {
@@ -91,7 +91,8 @@ function serveDist(root) {
91
91
  });
92
92
  // Use port 0 to let the OS pick a free port; retrieve it after listen.
93
93
  server.listen(0, '127.0.0.1', () => {
94
- const { port } = server.address();
94
+ const addr = server.address();
95
+ const port = typeof addr === 'object' && addr ? addr.port : 0;
95
96
  resolve({ server, origin: `http://127.0.0.1:${port}` });
96
97
  });
97
98
  });
@@ -90,7 +90,7 @@ function parseTurtle(text, baseIri) {
90
90
  });
91
91
  }
92
92
 
93
- function* walkTtl(dir) {
93
+ function* walkTtl(dir: string): Generator<string> {
94
94
  for (const entry of readdirSync(dir)) {
95
95
  const full = join(dir, entry);
96
96
  if (statSync(full).isDirectory()) yield* walkTtl(full);
@@ -98,12 +98,12 @@ function* walkTtl(dir) {
98
98
  }
99
99
  }
100
100
 
101
- function formatViolation(v) {
101
+ function formatViolation(v: any): string {
102
102
  const focus = v.focusNode?.value ?? '(unknown)';
103
103
  const shape = v.shape?.value ?? '(unknown)';
104
104
  const path = v.path?.value ?? '';
105
105
  const message = (v.message && v.message.length > 0)
106
- ? v.message.map(m => m.value).join('; ')
106
+ ? v.message.map((m: any) => m.value).join('; ')
107
107
  : '(no message)';
108
108
  const pathPart = path ? `\n path: ${path}` : '';
109
109
  return ` shape: ${shape}${pathPart}\n node: ${focus}\n message: ${message}`;
@@ -121,7 +121,7 @@ async function main() {
121
121
  try {
122
122
  const shapesText = readFileSync(shapesPath, 'utf8');
123
123
  shapesDataset = await parseTurtle(shapesText, `file://${shapesPath}`);
124
- } catch (e) {
124
+ } catch (e: any) {
125
125
  console.error(`Failed to load SHACL shapes from ${shapesPath}: ${e.message}`);
126
126
  process.exit(2);
127
127
  }
@@ -150,7 +150,7 @@ async function main() {
150
150
  try {
151
151
  const text = readFileSync(path, 'utf8');
152
152
  graph = await parseTurtle(text, `file://${path}`);
153
- } catch (e) {
153
+ } catch (e: any) {
154
154
  violations.push({ path, parseError: e.message });
155
155
  continue;
156
156
  }
@@ -0,0 +1,144 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * validate-yaml — validate dataset author YAML files against the
4
+ * Glossarist concept JSON Schema.
5
+ *
6
+ * Usage:
7
+ * npx tsx scripts/validate-yaml.ts <path-to-yaml-or-dir>
8
+ * npx tsx scripts/validate-yaml.ts .datasets/my-vocab/concepts/
9
+ */
10
+
11
+ import { readFileSync, readdirSync, statSync, existsSync } from 'node:fs';
12
+ import { join, extname, resolve } from 'node:path';
13
+ import { cwd } from 'node:process';
14
+ import yaml from 'js-yaml';
15
+
16
+ const SCHEMA_PATH = join(cwd(), 'data', 'concept-model', 'shapes', 'glossarist.concept.yaml.schema.json');
17
+
18
+ export interface ValidationError { path: string; message: string; }
19
+ export interface ValidationResult { valid: boolean; errors: ValidationError[]; }
20
+
21
+ export function loadSchema(): any {
22
+ if (!existsSync(SCHEMA_PATH)) {
23
+ throw new Error(`Schema not found: ${SCHEMA_PATH}`);
24
+ }
25
+ return JSON.parse(readFileSync(SCHEMA_PATH, 'utf8'));
26
+ }
27
+
28
+ export function validateValue(value: any, schema: any, path: string, rootSchema: any, errors: ValidationError[]): void {
29
+ if (schema.$ref) {
30
+ const ref = schema.$ref.replace(/^#\/\$defs\//, '');
31
+ validateValue(value, rootSchema.$defs?.[ref] ?? {}, path, rootSchema, errors);
32
+ return;
33
+ }
34
+ if (schema.type === 'array') {
35
+ if (!Array.isArray(value)) {
36
+ errors.push({ path, message: `Expected array, got ${typeof value}` });
37
+ return;
38
+ }
39
+ for (let i = 0; i < value.length; i++) {
40
+ validateValue(value[i], schema.items ?? {}, `${path}[${i}]`, rootSchema, errors);
41
+ }
42
+ return;
43
+ }
44
+ if (schema.type && typeof value !== schema.type) {
45
+ errors.push({ path, message: `Expected ${schema.type}, got ${typeof value}` });
46
+ return;
47
+ }
48
+ if (schema.enum && !schema.enum.includes(value)) {
49
+ errors.push({ path, message: `"${value}" is not one of [${schema.enum.join(', ')}]` });
50
+ return;
51
+ }
52
+ if (schema.type === 'object' && schema.properties) {
53
+ for (const [key, subSchema] of Object.entries(schema.properties) as [string, any][]) {
54
+ if (value[key] !== undefined) {
55
+ validateValue(value[key], subSchema, `${path}.${key}`, rootSchema, errors);
56
+ }
57
+ }
58
+ if (schema.required) {
59
+ for (const req of schema.required) {
60
+ if (!(req in value)) {
61
+ errors.push({ path: `${path}.${req}`, message: `Missing required field: ${req}` });
62
+ }
63
+ }
64
+ }
65
+ if (schema.patternProperties) {
66
+ for (const [pattern, subSchema] of Object.entries(schema.patternProperties) as [string, any][]) {
67
+ const re = new RegExp(pattern);
68
+ for (const key of Object.keys(value)) {
69
+ if (re.test(key) && !(key in (schema.properties ?? {}))) {
70
+ validateValue(value[key], subSchema, `${path}.${key}`, rootSchema, errors);
71
+ }
72
+ }
73
+ }
74
+ }
75
+ }
76
+ if (schema.type === 'array' && Array.isArray(value)) {
77
+ for (let i = 0; i < value.length; i++) {
78
+ validateValue(value[i], schema.items ?? {}, `${path}[${i}]`, rootSchema, errors);
79
+ }
80
+ }
81
+ }
82
+
83
+ export function validateYaml(yamlContent: string, schema: any): ValidationResult {
84
+ const data = yaml.load(yamlContent);
85
+ const errors: ValidationError[] = [];
86
+ if (Array.isArray(data)) {
87
+ for (let i = 0; i < data.length; i++) {
88
+ validateValue(data[i], schema, `doc[${i}]`, schema, errors);
89
+ }
90
+ } else if (data && typeof data === 'object') {
91
+ validateValue(data, schema, 'root', schema, errors);
92
+ } else {
93
+ errors.push({ path: 'root', message: 'YAML must be an object or array of objects' });
94
+ }
95
+ return { valid: errors.length === 0, errors };
96
+ }
97
+
98
+ function walkYaml(dir: string): string[] {
99
+ const out: string[] = [];
100
+ for (const entry of readdirSync(dir)) {
101
+ const full = join(dir, entry);
102
+ const st = statSync(full);
103
+ if (st.isDirectory()) out.push(...walkYaml(full));
104
+ else if (['.yaml', '.yml'].includes(extname(entry))) out.push(full);
105
+ }
106
+ return out;
107
+ }
108
+
109
+ function main(): void {
110
+ const target = process.argv[2];
111
+ if (!target) {
112
+ console.error('Usage: validate-yaml <path-to-yaml-or-dir>');
113
+ process.exit(1);
114
+ }
115
+ const schema = loadSchema();
116
+ const resolvedTarget = resolve(target);
117
+ if (!existsSync(resolvedTarget)) {
118
+ console.error(`Path not found: ${resolvedTarget}`);
119
+ process.exit(1);
120
+ }
121
+ const files = statSync(resolvedTarget).isDirectory() ? walkYaml(resolvedTarget) : [resolvedTarget];
122
+ let totalValid = 0;
123
+ let totalErrors = 0;
124
+ for (const file of files) {
125
+ const content = readFileSync(file, 'utf8');
126
+ const result = validateYaml(content, schema);
127
+ if (result.valid) {
128
+ totalValid++;
129
+ } else {
130
+ totalErrors += result.errors.length;
131
+ console.error(`FAIL: ${file}`);
132
+ for (const err of result.errors) console.error(` ${err.path}: ${err.message}`);
133
+ }
134
+ }
135
+ console.log(`\n${totalValid}/${files.length} files valid, ${totalErrors} errors`);
136
+ process.exit(totalErrors > 0 ? 1 : 0);
137
+ }
138
+
139
+ const isDirect = process.argv[1] && resolve(process.argv[1]) === fileURLToPathPath();
140
+ function fileURLToPathPath(): string {
141
+ return resolve(new URL(import.meta.url).pathname);
142
+ }
143
+
144
+ if (isDirect) main();
@@ -1,8 +1,7 @@
1
- // @ts-nocheck — ESM .js import without type declarations
2
1
  import { describe, it, expect } from 'vitest';
3
- import { extractSourceRefs } from '../../scripts/extract-source-refs.js';
2
+ import { extractSourceRefs } from '../../scripts/extract-source-refs';
4
3
 
5
- function makeConcept(sources: any[], localizedSources?: any) {
4
+ function makeConcept(sources: any[] = [], localizedSources?: any) {
6
5
  return {
7
6
  '@id': 'https://glossarist.org/ds1/concept/1.1',
8
7
  'gl:source': sources || [],
@@ -1,5 +1,5 @@
1
- // @ts-nocheck — TODO.typescript/12: remove after glossarist TS migration
2
1
  import { describe, it, expect } from 'vitest';
2
+ import type { Quad } from 'n3';
3
3
  import { Concept } from 'glossarist';
4
4
  import { conceptToQuads, collectQuads, writeTurtleSync, PREFIXES } from 'glossarist/rdf';
5
5
  import type { ConceptFixture } from '../__fixtures__/concepts';
@@ -41,7 +41,7 @@ describe('Layer 7 — serialization performance regression', () => {
41
41
 
42
42
  const start = performance.now();
43
43
  for (const { concept } of concepts) {
44
- const quads = collectQuads(conceptToQuads(concept, { registerId: 'perf', uriBase: BASE }));
44
+ const quads = collectQuads(conceptToQuads(concept, { registerId: 'perf', uriBase: BASE }) as Generator<Quad>);
45
45
  writeTurtleSync(quads, { prefixes: PREFIXES });
46
46
  }
47
47
  const elapsed = performance.now() - start;
@@ -55,7 +55,7 @@ describe('Layer 7 — serialization performance regression', () => {
55
55
  const concepts = makeConcepts(100);
56
56
  const start = performance.now();
57
57
  for (const { concept } of concepts) {
58
- const quads = collectQuads(conceptToQuads(concept, { registerId: 'perf', uriBase: BASE }));
58
+ const quads = collectQuads(conceptToQuads(concept, { registerId: 'perf', uriBase: BASE }) as Generator<Quad>);
59
59
  writeTurtleSync(quads, { prefixes: PREFIXES });
60
60
  }
61
61
  const elapsed = performance.now() - start;
@@ -69,7 +69,7 @@ describe('Layer 7 — scale stress (P4: 10 000 concepts)', () => {
69
69
  const concepts = makeConcepts(SCALE_CONCEPT_COUNT);
70
70
  const start = performance.now();
71
71
  for (const { concept } of concepts) {
72
- const quads = collectQuads(conceptToQuads(concept, { registerId: 'perf', uriBase: BASE }));
72
+ const quads = collectQuads(conceptToQuads(concept, { registerId: 'perf', uriBase: BASE }) as Generator<Quad>);
73
73
  writeTurtleSync(quads, { prefixes: PREFIXES });
74
74
  }
75
75
  const elapsed = performance.now() - start;
@@ -98,7 +98,7 @@ describe('Layer 7 — scale stress (P4: 10 000 concepts)', () => {
98
98
  function timeTurtle(concepts: { concept: Concept; uri: string }[]): number {
99
99
  const start = performance.now();
100
100
  for (const { concept } of concepts) {
101
- const quads = collectQuads(conceptToQuads(concept, { registerId: 'perf', uriBase: BASE }));
101
+ const quads = collectQuads(conceptToQuads(concept, { registerId: 'perf', uriBase: BASE }) as Generator<Quad>);
102
102
  writeTurtleSync(quads, { prefixes: PREFIXES });
103
103
  }
104
104
  return performance.now() - start;
@@ -1,6 +1,6 @@
1
1
  import { describe, it, expect } from 'vitest';
2
2
  import { Parser, Store } from 'n3';
3
- import { buildAgentsTurtle as _buildAgentsTurtle } from '../../../scripts/lib/agents-turtle.mjs';
3
+ import { buildAgentsTurtle as _buildAgentsTurtle } from '../../../scripts/lib/agents-turtle';
4
4
 
5
5
  const buildAgentsTurtle = _buildAgentsTurtle as unknown as (...args: any[]) => Promise<string>;
6
6
 
@@ -1,6 +1,6 @@
1
1
  import { describe, it, expect } from 'vitest';
2
2
  import { Parser, Store } from 'n3';
3
- import { buildBibliographyTurtle } from '../../../scripts/lib/bibliography-turtle.mjs';
3
+ import { buildBibliographyTurtle } from '../../../scripts/lib/bibliography-turtle';
4
4
 
5
5
  const FOAF = 'http://xmlns.com/foaf/0.1/';
6
6
  const DCTERMS = 'http://purl.org/dc/terms/';
@@ -1,6 +1,6 @@
1
1
  import { describe, it, expect } from 'vitest';
2
2
  import { Parser, Store } from 'n3';
3
- import { buildActivityTurtle } from '../../../scripts/lib/build-activity-turtle.mjs';
3
+ import { buildActivityTurtle } from '../../../scripts/lib/build-activity-turtle';
4
4
 
5
5
  function makeInput(overrides: any = {}): any {
6
6
  return {
@@ -2,7 +2,7 @@ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
2
2
  import { rmSync, mkdirSync } from 'node:fs';
3
3
  import { tmpdir } from 'node:os';
4
4
  import { join } from 'node:path';
5
- import { BuildCache } from '../../../scripts/lib/build-cache.mjs';
5
+ import { BuildCache } from '../../../scripts/lib/build-cache';
6
6
 
7
7
  const SUITE_DIR = join(tmpdir(), `build-cache-${process.pid}-${Date.now()}`);
8
8
 
@@ -10,12 +10,12 @@ function parseTurtle(text: string): Store {
10
10
  }
11
11
 
12
12
  function runInline(code: string): string {
13
- return execFileSync('node', ['--input-type=module', '-e', code], { encoding: 'utf8' }).toString();
13
+ return execFileSync('node', ['--import', 'tsx', '--input-type=module', '-e', code], { encoding: 'utf8' }).toString();
14
14
  }
15
15
 
16
16
  describe('WS F Layer 8 — build pipeline integration', () => {
17
- it('vocab-turtle.mjs produces parseable Turtle with 7 schemes', () => {
18
- const out = runInline(`import { buildVocabularyTurtle } from './scripts/lib/vocab-turtle.mjs'; console.log(await buildVocabularyTurtle());`);
17
+ it('vocab-turtle.ts produces parseable Turtle with 7 schemes', () => {
18
+ const out = runInline(`import { buildVocabularyTurtle } from './scripts/lib/vocab-turtle'; console.log(await buildVocabularyTurtle());`);
19
19
  const store = parseTurtle(out);
20
20
  const schemes = [...store].filter(q =>
21
21
  q.predicate.value === 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type' &&
@@ -24,9 +24,9 @@ describe('WS F Layer 8 — build pipeline integration', () => {
24
24
  expect(schemes.length).toBe(7);
25
25
  });
26
26
 
27
- it('dataset-turtle.mjs produces parseable dcat:Dataset', () => {
27
+ it('dataset-turtle.ts produces parseable dcat:Dataset', () => {
28
28
  const out = runInline(`
29
- import { buildDatasetTurtle } from './scripts/lib/dataset-turtle.mjs';
29
+ import { buildDatasetTurtle } from './scripts/lib/dataset-turtle';
30
30
  console.log(await buildDatasetTurtle({
31
31
  datasetIri: 'https://glossarist.org/test/',
32
32
  registerId: 'test', title: 'Test', modified: '2026-06-28',
@@ -42,9 +42,9 @@ describe('WS F Layer 8 — build pipeline integration', () => {
42
42
  expect(types).toContain('http://www.w3.org/2004/02/skos/core#ConceptScheme');
43
43
  });
44
44
 
45
- it('build-activity-turtle.mjs produces parseable prov:Activity', () => {
45
+ it('build-activity-turtle.ts produces parseable prov:Activity', () => {
46
46
  const out = runInline(`
47
- import { buildActivityTurtle } from './scripts/lib/build-activity-turtle.mjs';
47
+ import { buildActivityTurtle } from './scripts/lib/build-activity-turtle';
48
48
  console.log(await buildActivityTurtle({
49
49
  baseUri: 'https://glossarist.org',
50
50
  runId: 'test', startedAt: '2026-01-01T00:00:00Z', endedAt: '2026-01-01T00:05:00Z',
@@ -59,9 +59,9 @@ describe('WS F Layer 8 — build pipeline integration', () => {
59
59
  expect(activities.length).toBe(1);
60
60
  });
61
61
 
62
- it('agents-turtle.mjs produces parseable foaf:Person', () => {
62
+ it('agents-turtle.ts produces parseable foaf:Person', () => {
63
63
  const out = runInline(`
64
- import { buildAgentsTurtle } from './scripts/lib/agents-turtle.mjs';
64
+ import { buildAgentsTurtle } from './scripts/lib/agents-turtle';
65
65
  console.log(await buildAgentsTurtle([{ name: 'Ada Lovelace', role: 'Editor', organization: 'Royal Society' }], 'https://glossarist.org/agent', 'https://glossarist.org/org'));
66
66
  `);
67
67
  const store = parseTurtle(out);
@@ -72,9 +72,9 @@ describe('WS F Layer 8 — build pipeline integration', () => {
72
72
  expect(persons.length).toBe(1);
73
73
  });
74
74
 
75
- it('bibliography-turtle.mjs produces parseable dcterms:BibliographicResource', () => {
75
+ it('bibliography-turtle.ts produces parseable dcterms:BibliographicResource', () => {
76
76
  const out = runInline(`
77
- import { buildBibliographyTurtle } from './scripts/lib/bibliography-turtle.mjs';
77
+ import { buildBibliographyTurtle } from './scripts/lib/bibliography-turtle';
78
78
  console.log(await buildBibliographyTurtle('test', { iso704: { reference: 'ISO 704' } }, 'https://glossarist.org'));
79
79
  `);
80
80
  const store = parseTurtle(out);
@@ -85,9 +85,9 @@ describe('WS F Layer 8 — build pipeline integration', () => {
85
85
  expect(bibs.length).toBe(1);
86
86
  });
87
87
 
88
- it('version-turtle.mjs produces parseable prov:Entity chain', () => {
88
+ it('version-turtle.ts produces parseable prov:Entity chain', () => {
89
89
  const out = runInline(`
90
- import { buildVersionHistoryTurtle } from './scripts/lib/version-turtle.mjs';
90
+ import { buildVersionHistoryTurtle } from './scripts/lib/version-turtle';
91
91
  console.log(await buildVersionHistoryTurtle({
92
92
  registerId: 'test', datasetIri: 'https://glossarist.org/test/',
93
93
  versions: [
@@ -113,12 +113,12 @@ describe('WS F Layer 8 — build pipeline integration', () => {
113
113
 
114
114
  it('all six emitters produce parseable output in a single subprocess', () => {
115
115
  const out = runInline(`
116
- import { buildVocabularyTurtle } from './scripts/lib/vocab-turtle.mjs';
117
- import { buildDatasetTurtle } from './scripts/lib/dataset-turtle.mjs';
118
- import { buildActivityTurtle } from './scripts/lib/build-activity-turtle.mjs';
119
- import { buildAgentsTurtle } from './scripts/lib/agents-turtle.mjs';
120
- import { buildBibliographyTurtle } from './scripts/lib/bibliography-turtle.mjs';
121
- import { buildVersionHistoryTurtle } from './scripts/lib/version-turtle.mjs';
116
+ import { buildVocabularyTurtle } from './scripts/lib/vocab-turtle';
117
+ import { buildDatasetTurtle } from './scripts/lib/dataset-turtle';
118
+ import { buildActivityTurtle } from './scripts/lib/build-activity-turtle';
119
+ import { buildAgentsTurtle } from './scripts/lib/agents-turtle';
120
+ import { buildBibliographyTurtle } from './scripts/lib/bibliography-turtle';
121
+ import { buildVersionHistoryTurtle } from './scripts/lib/version-turtle';
122
122
  const parts = [
123
123
  await buildVocabularyTurtle(),
124
124
  await buildDatasetTurtle({ datasetIri: 'https://glossarist.org/test/', registerId: 'test', title: 't', modified: '2026-01-01', languages: ['eng'], distributions: [], topConceptUris: [], sections: [] }),