@glossarist/concept-browser 0.7.115 → 0.7.116
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/cli/index.mjs +12 -15
- package/package.json +5 -1
- package/scripts/bridge-to-astro.js +132 -0
- package/scripts/build-edges.js +480 -0
- package/scripts/build-scripts.mjs +22 -0
- package/scripts/check-syntax.js +81 -0
- package/scripts/doctor.js +278 -0
- package/scripts/extract-source-refs.js +24 -0
- package/scripts/fetch-datasets.js +278 -0
- package/scripts/generate-404.js +14 -0
- package/scripts/generate-data.js +2220 -0
- package/scripts/generate-ontology-data.js +139 -0
- package/scripts/generate-ontology-schema.js +472 -0
- package/scripts/load-site-config.js +83 -0
- package/scripts/migrate-v1-to-v3.js +113 -0
- package/scripts/normalize-yaml.js +96 -0
- package/scripts/process-about-pages.js +241 -0
- package/scripts/smoke.js +211 -0
- package/scripts/sync-concept-model.js +63 -0
- package/scripts/validate-shacl.js +152 -0
- package/scripts/validate-yaml.js +132 -0
- package/src/__tests__/published-package-contract.test.ts +84 -0
- package/src/__tests__/scripts-syntax.test.ts +2 -2
- package/src/data/ontology-schema.json +1336 -183
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// scripts/sync-concept-model.ts
|
|
4
|
+
import { mkdirSync, writeFileSync } from "node:fs";
|
|
5
|
+
import { dirname, resolve } from "node:path";
|
|
6
|
+
import { fileURLToPath } from "node:url";
|
|
7
|
+
import { execSync } from "node:child_process";
|
|
8
|
+
var ROOT = resolve(dirname(fileURLToPath(import.meta.url)), "..");
|
|
9
|
+
var OUT_DIR = resolve(ROOT, "data", "concept-model");
|
|
10
|
+
var REPO = "glossarist/concept-model";
|
|
11
|
+
function parseArgs(argv) {
|
|
12
|
+
const out = { ref: null };
|
|
13
|
+
for (let i = 2; i < argv.length; i++) {
|
|
14
|
+
const a = argv[i];
|
|
15
|
+
if (a === "--ref") out.ref = argv[++i];
|
|
16
|
+
else if (!a.startsWith("--")) out.ref = a;
|
|
17
|
+
}
|
|
18
|
+
return out;
|
|
19
|
+
}
|
|
20
|
+
function latestTag() {
|
|
21
|
+
return execSync(`gh api repos/${REPO}/releases/latest --jq .tag_name`, { encoding: "utf8" }).trim();
|
|
22
|
+
}
|
|
23
|
+
function fetchFile(ref2, path) {
|
|
24
|
+
const url = `https://raw.githubusercontent.com/${REPO}/${ref2}/${path}`;
|
|
25
|
+
return execSync(`curl -fsSL ${url}`, { encoding: "utf8" });
|
|
26
|
+
}
|
|
27
|
+
var args = parseArgs(process.argv);
|
|
28
|
+
var ref = args.ref || latestTag();
|
|
29
|
+
if (!ref) {
|
|
30
|
+
console.error("Could not determine concept-model ref to sync.");
|
|
31
|
+
process.exit(1);
|
|
32
|
+
}
|
|
33
|
+
console.log(`Syncing from ${REPO}@${ref}`);
|
|
34
|
+
mkdirSync(resolve(OUT_DIR, "shapes"), { recursive: true });
|
|
35
|
+
function fetchAny(ref2, candidates) {
|
|
36
|
+
for (const p of candidates) {
|
|
37
|
+
try {
|
|
38
|
+
return fetchFile(ref2, p);
|
|
39
|
+
} catch (e) {
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
throw new Error(`Could not fetch any of: ${candidates.join(", ")}`);
|
|
43
|
+
}
|
|
44
|
+
var targets = {
|
|
45
|
+
"glossarist.context.jsonld": ["ontologies/glossarist.context.jsonld", "glossarist.context.jsonld"],
|
|
46
|
+
"glossarist.ttl": ["ontologies/glossarist.ttl", "glossarist.ttl"],
|
|
47
|
+
"prefixes.ttl": ["ontologies/prefixes.ttl"],
|
|
48
|
+
"shapes/glossarist.shacl.ttl": ["ontologies/shapes/glossarist.shacl.ttl", "shapes/glossarist.shacl.ttl"]
|
|
49
|
+
};
|
|
50
|
+
var updated = 0;
|
|
51
|
+
for (const [outPath, candidates] of Object.entries(targets)) {
|
|
52
|
+
const content = fetchAny(ref, candidates);
|
|
53
|
+
writeFileSync(resolve(OUT_DIR, outPath), content);
|
|
54
|
+
console.log(` \u2713 ${outPath} (${content.length} bytes)`);
|
|
55
|
+
updated++;
|
|
56
|
+
}
|
|
57
|
+
writeFileSync(
|
|
58
|
+
resolve(OUT_DIR, "SOURCE.json"),
|
|
59
|
+
JSON.stringify({ repo: REPO, ref, syncedAt: (/* @__PURE__ */ new Date()).toISOString() }, null, 2) + "\n"
|
|
60
|
+
);
|
|
61
|
+
console.log(`
|
|
62
|
+
Synced ${updated} file(s) from ${REPO}@${ref}.`);
|
|
63
|
+
console.log("Review the diff, then commit.");
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// scripts/validate-shacl.ts
|
|
4
|
+
import { readFileSync, readdirSync, statSync } from "node:fs";
|
|
5
|
+
import { join, extname, dirname, resolve } from "node:path";
|
|
6
|
+
import { fileURLToPath } from "node:url";
|
|
7
|
+
import { Parser as N3Parser } from "n3";
|
|
8
|
+
import rdfDataset from "@rdfjs/dataset";
|
|
9
|
+
import { validateShacl } from "glossarist/rdf/shacl";
|
|
10
|
+
var __dirname = dirname(fileURLToPath(import.meta.url));
|
|
11
|
+
var VENDORED_SHAPES = resolve(__dirname, "..", "data", "concept-model", "shapes", "glossarist.shacl.ttl");
|
|
12
|
+
var createDataset = rdfDataset.dataset.bind(rdfDataset);
|
|
13
|
+
function parseArgs(argv) {
|
|
14
|
+
const out = { dataRoot: "public/data", shapesPath: null };
|
|
15
|
+
for (let i = 2; i < argv.length; i++) {
|
|
16
|
+
const arg = argv[i];
|
|
17
|
+
if (arg === "--shapes") {
|
|
18
|
+
out.shapesPath = argv[++i];
|
|
19
|
+
if (!out.shapesPath) throw new Error("--shapes requires a path argument");
|
|
20
|
+
} else if (arg === "--help" || arg === "-h") {
|
|
21
|
+
console.log(USAGE);
|
|
22
|
+
process.exit(0);
|
|
23
|
+
} else {
|
|
24
|
+
out.dataRoot = arg;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
if (process.env.SHAPES_PATH && !out.shapesPath) {
|
|
28
|
+
out.shapesPath = process.env.SHAPES_PATH;
|
|
29
|
+
}
|
|
30
|
+
return out;
|
|
31
|
+
}
|
|
32
|
+
var USAGE = `Usage: validate-shacl.mjs [options] [data-root]
|
|
33
|
+
|
|
34
|
+
Options:
|
|
35
|
+
--shapes <path> Path to a SHACL shapes .ttl file (overrides the default
|
|
36
|
+
vendored shapes under data/concept-model/shapes/).
|
|
37
|
+
--help, -h Show this help.
|
|
38
|
+
|
|
39
|
+
Environment:
|
|
40
|
+
SHAPES_PATH Same as --shapes. Lower priority than the CLI flag.
|
|
41
|
+
|
|
42
|
+
Default data-root is public/data.
|
|
43
|
+
`;
|
|
44
|
+
function resolveShapesPath(cliPath) {
|
|
45
|
+
if (cliPath) return cliPath;
|
|
46
|
+
if (!statSync(VENDORED_SHAPES, { throwIfNoEntry: false })) {
|
|
47
|
+
throw new Error(
|
|
48
|
+
`Vendored SHACL shapes not found at ${VENDORED_SHAPES}.
|
|
49
|
+
Run \`npm run sync:model\` to fetch them from glossarist/concept-model, or pass --shapes <path>, or set SHAPES_PATH=<path>.`
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
return VENDORED_SHAPES;
|
|
53
|
+
}
|
|
54
|
+
function parseTurtle(text, baseIri) {
|
|
55
|
+
const parser = new N3Parser({ baseIRI: baseIri });
|
|
56
|
+
const out = createDataset();
|
|
57
|
+
return new Promise((resolve2, reject) => {
|
|
58
|
+
parser.parse(text, (err, quad) => {
|
|
59
|
+
if (err) reject(err);
|
|
60
|
+
else if (quad) out.add(quad);
|
|
61
|
+
else resolve2(out);
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
function* walkTtl(dir) {
|
|
66
|
+
for (const entry of readdirSync(dir)) {
|
|
67
|
+
const full = join(dir, entry);
|
|
68
|
+
if (statSync(full).isDirectory()) yield* walkTtl(full);
|
|
69
|
+
else if (extname(full) === ".ttl") yield full;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
function formatViolation(v) {
|
|
73
|
+
const focus = v.focusNode?.value ?? "(unknown)";
|
|
74
|
+
const shape = v.shape?.value ?? "(unknown)";
|
|
75
|
+
const path = v.path?.value ?? "";
|
|
76
|
+
const message = v.message && v.message.length > 0 ? v.message.map((m) => m.value).join("; ") : "(no message)";
|
|
77
|
+
const pathPart = path ? `
|
|
78
|
+
path: ${path}` : "";
|
|
79
|
+
return ` shape: ${shape}${pathPart}
|
|
80
|
+
node: ${focus}
|
|
81
|
+
message: ${message}`;
|
|
82
|
+
}
|
|
83
|
+
async function main() {
|
|
84
|
+
const args = parseArgs(process.argv);
|
|
85
|
+
const shapesPath = resolveShapesPath(args.shapesPath);
|
|
86
|
+
let shapesDataset;
|
|
87
|
+
try {
|
|
88
|
+
const shapesText = readFileSync(shapesPath, "utf8");
|
|
89
|
+
shapesDataset = await parseTurtle(shapesText, `file://${shapesPath}`);
|
|
90
|
+
} catch (e) {
|
|
91
|
+
console.error(`Failed to load SHACL shapes from ${shapesPath}: ${e.message}`);
|
|
92
|
+
process.exit(2);
|
|
93
|
+
}
|
|
94
|
+
let statDir;
|
|
95
|
+
try {
|
|
96
|
+
statDir = statSync(args.dataRoot);
|
|
97
|
+
} catch (e) {
|
|
98
|
+
console.error(`Data root not found: ${args.dataRoot}`);
|
|
99
|
+
process.exit(2);
|
|
100
|
+
}
|
|
101
|
+
if (!statDir.isDirectory()) {
|
|
102
|
+
console.error(`Data root is not a directory: ${args.dataRoot}`);
|
|
103
|
+
process.exit(2);
|
|
104
|
+
}
|
|
105
|
+
const files = [...walkTtl(args.dataRoot)];
|
|
106
|
+
if (files.length === 0) {
|
|
107
|
+
console.log(`No .ttl files under ${args.dataRoot} \u2014 SHACL gate skipped.`);
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
const violations = [];
|
|
111
|
+
for (const path of files) {
|
|
112
|
+
let graph;
|
|
113
|
+
try {
|
|
114
|
+
const text = readFileSync(path, "utf8");
|
|
115
|
+
graph = await parseTurtle(text, `file://${path}`);
|
|
116
|
+
} catch (e) {
|
|
117
|
+
violations.push({ path, parseError: e.message });
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
const report = await validateShacl(graph, { shapes: shapesDataset });
|
|
121
|
+
if (!report.conforms) {
|
|
122
|
+
for (const v of report.results) {
|
|
123
|
+
violations.push({ path, result: v });
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
if (violations.length === 0) {
|
|
128
|
+
console.log(`SHACL validation passed \u2014 ${files.length} file(s) conform.`);
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
console.error(`SHACL validation FAILED \u2014 ${violations.length} violation(s) in ${files.length} file(s):
|
|
132
|
+
`);
|
|
133
|
+
let currentPath = null;
|
|
134
|
+
for (const v of violations) {
|
|
135
|
+
if (v.path !== currentPath) {
|
|
136
|
+
currentPath = v.path;
|
|
137
|
+
console.error(`
|
|
138
|
+
${v.path}`);
|
|
139
|
+
}
|
|
140
|
+
if (v.parseError) {
|
|
141
|
+
console.error(` PARSE ERROR: ${v.parseError}`);
|
|
142
|
+
} else {
|
|
143
|
+
console.error(formatViolation(v.result));
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
console.error("");
|
|
147
|
+
process.exit(1);
|
|
148
|
+
}
|
|
149
|
+
main().catch((e) => {
|
|
150
|
+
console.error(`validate-shacl: unexpected error: ${e.stack ?? e}`);
|
|
151
|
+
process.exit(2);
|
|
152
|
+
});
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// scripts/validate-yaml.ts
|
|
4
|
+
import { readFileSync, readdirSync, statSync, existsSync } from "node:fs";
|
|
5
|
+
import { join, extname, resolve } from "node:path";
|
|
6
|
+
import { cwd } from "node:process";
|
|
7
|
+
import yaml from "js-yaml";
|
|
8
|
+
var SCHEMA_PATH = join(cwd(), "data", "concept-model", "shapes", "glossarist.concept.yaml.schema.json");
|
|
9
|
+
function loadSchema() {
|
|
10
|
+
if (!existsSync(SCHEMA_PATH)) {
|
|
11
|
+
throw new Error(`Schema not found: ${SCHEMA_PATH}`);
|
|
12
|
+
}
|
|
13
|
+
return JSON.parse(readFileSync(SCHEMA_PATH, "utf8"));
|
|
14
|
+
}
|
|
15
|
+
function validateValue(value, schema, path, rootSchema, errors) {
|
|
16
|
+
if (schema.$ref) {
|
|
17
|
+
const ref = schema.$ref.replace(/^#\/\$defs\//, "");
|
|
18
|
+
validateValue(value, rootSchema.$defs?.[ref] ?? {}, path, rootSchema, errors);
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
if (schema.type === "array") {
|
|
22
|
+
if (!Array.isArray(value)) {
|
|
23
|
+
errors.push({ path, message: `Expected array, got ${typeof value}` });
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
for (let i = 0; i < value.length; i++) {
|
|
27
|
+
validateValue(value[i], schema.items ?? {}, `${path}[${i}]`, rootSchema, errors);
|
|
28
|
+
}
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
if (schema.type && typeof value !== schema.type) {
|
|
32
|
+
errors.push({ path, message: `Expected ${schema.type}, got ${typeof value}` });
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
if (schema.enum && !schema.enum.includes(value)) {
|
|
36
|
+
errors.push({ path, message: `"${value}" is not one of [${schema.enum.join(", ")}]` });
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
if (schema.type === "object" && schema.properties) {
|
|
40
|
+
for (const [key, subSchema] of Object.entries(schema.properties)) {
|
|
41
|
+
if (value[key] !== void 0) {
|
|
42
|
+
validateValue(value[key], subSchema, `${path}.${key}`, rootSchema, errors);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
if (schema.required) {
|
|
46
|
+
for (const req of schema.required) {
|
|
47
|
+
if (!(req in value)) {
|
|
48
|
+
errors.push({ path: `${path}.${req}`, message: `Missing required field: ${req}` });
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
if (schema.patternProperties) {
|
|
53
|
+
for (const [pattern, subSchema] of Object.entries(schema.patternProperties)) {
|
|
54
|
+
const re = new RegExp(pattern);
|
|
55
|
+
for (const key of Object.keys(value)) {
|
|
56
|
+
if (re.test(key) && !(key in (schema.properties ?? {}))) {
|
|
57
|
+
validateValue(value[key], subSchema, `${path}.${key}`, rootSchema, errors);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
if (schema.type === "array" && Array.isArray(value)) {
|
|
64
|
+
for (let i = 0; i < value.length; i++) {
|
|
65
|
+
validateValue(value[i], schema.items ?? {}, `${path}[${i}]`, rootSchema, errors);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
function validateYaml(yamlContent, schema) {
|
|
70
|
+
const data = yaml.load(yamlContent);
|
|
71
|
+
const errors = [];
|
|
72
|
+
if (Array.isArray(data)) {
|
|
73
|
+
for (let i = 0; i < data.length; i++) {
|
|
74
|
+
validateValue(data[i], schema, `doc[${i}]`, schema, errors);
|
|
75
|
+
}
|
|
76
|
+
} else if (data && typeof data === "object") {
|
|
77
|
+
validateValue(data, schema, "root", schema, errors);
|
|
78
|
+
} else {
|
|
79
|
+
errors.push({ path: "root", message: "YAML must be an object or array of objects" });
|
|
80
|
+
}
|
|
81
|
+
return { valid: errors.length === 0, errors };
|
|
82
|
+
}
|
|
83
|
+
function walkYaml(dir) {
|
|
84
|
+
const out = [];
|
|
85
|
+
for (const entry of readdirSync(dir)) {
|
|
86
|
+
const full = join(dir, entry);
|
|
87
|
+
const st = statSync(full);
|
|
88
|
+
if (st.isDirectory()) out.push(...walkYaml(full));
|
|
89
|
+
else if ([".yaml", ".yml"].includes(extname(entry))) out.push(full);
|
|
90
|
+
}
|
|
91
|
+
return out;
|
|
92
|
+
}
|
|
93
|
+
function main() {
|
|
94
|
+
const target = process.argv[2];
|
|
95
|
+
if (!target) {
|
|
96
|
+
console.error("Usage: validate-yaml <path-to-yaml-or-dir>");
|
|
97
|
+
process.exit(1);
|
|
98
|
+
}
|
|
99
|
+
const schema = loadSchema();
|
|
100
|
+
const resolvedTarget = resolve(target);
|
|
101
|
+
if (!existsSync(resolvedTarget)) {
|
|
102
|
+
console.error(`Path not found: ${resolvedTarget}`);
|
|
103
|
+
process.exit(1);
|
|
104
|
+
}
|
|
105
|
+
const files = statSync(resolvedTarget).isDirectory() ? walkYaml(resolvedTarget) : [resolvedTarget];
|
|
106
|
+
let totalValid = 0;
|
|
107
|
+
let totalErrors = 0;
|
|
108
|
+
for (const file of files) {
|
|
109
|
+
const content = readFileSync(file, "utf8");
|
|
110
|
+
const result = validateYaml(content, schema);
|
|
111
|
+
if (result.valid) {
|
|
112
|
+
totalValid++;
|
|
113
|
+
} else {
|
|
114
|
+
totalErrors += result.errors.length;
|
|
115
|
+
console.error(`FAIL: ${file}`);
|
|
116
|
+
for (const err of result.errors) console.error(` ${err.path}: ${err.message}`);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
console.log(`
|
|
120
|
+
${totalValid}/${files.length} files valid, ${totalErrors} errors`);
|
|
121
|
+
process.exit(totalErrors > 0 ? 1 : 0);
|
|
122
|
+
}
|
|
123
|
+
var isDirect = process.argv[1] && resolve(process.argv[1]) === fileURLToPathPath();
|
|
124
|
+
function fileURLToPathPath() {
|
|
125
|
+
return resolve(new URL(import.meta.url).pathname);
|
|
126
|
+
}
|
|
127
|
+
if (isDirect) main();
|
|
128
|
+
export {
|
|
129
|
+
loadSchema,
|
|
130
|
+
validateValue,
|
|
131
|
+
validateYaml
|
|
132
|
+
};
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { spawnSync } from 'node:child_process';
|
|
3
|
+
import { existsSync } from 'node:fs';
|
|
4
|
+
import { resolve, join, dirname } from 'node:path';
|
|
5
|
+
import { fileURLToPath } from 'node:url';
|
|
6
|
+
|
|
7
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
8
|
+
const ROOT = resolve(__dirname, '..', '..');
|
|
9
|
+
const SCRIPTS_DIR = join(ROOT, 'scripts');
|
|
10
|
+
|
|
11
|
+
const LIBRARY_SCRIPTS = [
|
|
12
|
+
'load-site-config',
|
|
13
|
+
'validate-yaml',
|
|
14
|
+
'normalize-yaml',
|
|
15
|
+
'extract-source-refs',
|
|
16
|
+
'migrate-v1-to-v3',
|
|
17
|
+
];
|
|
18
|
+
|
|
19
|
+
const PIPELINE_SCRIPTS = [
|
|
20
|
+
'fetch-datasets',
|
|
21
|
+
'generate-data',
|
|
22
|
+
'build-edges',
|
|
23
|
+
'process-about-pages',
|
|
24
|
+
'smoke',
|
|
25
|
+
'generate-ontology-data',
|
|
26
|
+
'generate-ontology-schema',
|
|
27
|
+
'doctor',
|
|
28
|
+
'validate-shacl',
|
|
29
|
+
'bridge-to-astro',
|
|
30
|
+
'sync-concept-model',
|
|
31
|
+
'generate-404',
|
|
32
|
+
];
|
|
33
|
+
|
|
34
|
+
function runPlain(args: string[], opts?: Parameters<typeof spawnSync>[2]): { status: number | null; stdout: string; stderr: string } {
|
|
35
|
+
const result = spawnSync('node', args, { encoding: 'utf8', cwd: ROOT, ...opts });
|
|
36
|
+
return {
|
|
37
|
+
status: result.status,
|
|
38
|
+
stdout: typeof result.stdout === 'string' ? result.stdout : result.stdout?.toString('utf8') ?? '',
|
|
39
|
+
stderr: typeof result.stderr === 'string' ? result.stderr : result.stderr?.toString('utf8') ?? '',
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
describe('published-package contract — compiled .js scripts load under plain node (no tsx)', () => {
|
|
44
|
+
it('npm run build:scripts produces .js for every script', () => {
|
|
45
|
+
const build = spawnSync('node', [join(ROOT, 'scripts', 'build-scripts.mjs')], {
|
|
46
|
+
encoding: 'utf8',
|
|
47
|
+
cwd: ROOT,
|
|
48
|
+
});
|
|
49
|
+
expect(build.status).toBe(0);
|
|
50
|
+
|
|
51
|
+
for (const name of [...LIBRARY_SCRIPTS, ...PIPELINE_SCRIPTS]) {
|
|
52
|
+
const jsPath = join(SCRIPTS_DIR, `${name}.js`);
|
|
53
|
+
expect(existsSync(jsPath)).toBe(true);
|
|
54
|
+
}
|
|
55
|
+
}, 30000);
|
|
56
|
+
|
|
57
|
+
it('library scripts export functions under plain node (ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING regression)', () => {
|
|
58
|
+
for (const name of LIBRARY_SCRIPTS) {
|
|
59
|
+
const jsPath = `./scripts/${name}.js`;
|
|
60
|
+
const result = runPlain(['--input-type=module', '-e', `import(${JSON.stringify(jsPath)}).then(m => console.log('OK', Object.keys(m).length)).catch(e => { console.error('FAIL', e.message); process.exit(1); })`]);
|
|
61
|
+
expect(result.stderr).not.toContain('ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING');
|
|
62
|
+
expect(result.status).toBe(0);
|
|
63
|
+
expect(result.stdout).toContain('OK');
|
|
64
|
+
}
|
|
65
|
+
}, 60000);
|
|
66
|
+
|
|
67
|
+
it('pipeline scripts do not throw ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING (may fail on missing config, that is OK)', () => {
|
|
68
|
+
for (const name of PIPELINE_SCRIPTS) {
|
|
69
|
+
const result = runPlain(['--input-type=module', '-e', `import(${JSON.stringify(`./scripts/${name}.js`)}).then(() => console.log('OK')).catch(e => { console.error(e.message); })`]);
|
|
70
|
+
expect(result.stderr).not.toContain('ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING');
|
|
71
|
+
}
|
|
72
|
+
}, 60000);
|
|
73
|
+
|
|
74
|
+
it('validate-yaml.js exports validateYaml function', () => {
|
|
75
|
+
const result = runPlain(['--input-type=module', '-e', `
|
|
76
|
+
import { validateYaml } from './scripts/validate-yaml.js';
|
|
77
|
+
const schema = { type: 'object', required: ['termid'], properties: { termid: { type: 'string' } } };
|
|
78
|
+
const r = validateYaml('termid: test', schema);
|
|
79
|
+
console.log('valid=' + r.valid);
|
|
80
|
+
`]);
|
|
81
|
+
expect(result.status).toBe(0);
|
|
82
|
+
expect(result.stdout).toContain('valid=true');
|
|
83
|
+
});
|
|
84
|
+
});
|
|
@@ -13,7 +13,7 @@ describe('scripts syntax gate', () => {
|
|
|
13
13
|
const result = spawnSync(
|
|
14
14
|
process.execPath,
|
|
15
15
|
['--import', 'tsx', path.join(repoRoot, 'scripts', 'check-syntax.ts')],
|
|
16
|
-
{ encoding: 'utf8' },
|
|
16
|
+
{ encoding: 'utf8', timeout: 60000 },
|
|
17
17
|
);
|
|
18
18
|
|
|
19
19
|
if (result.status !== 0) {
|
|
@@ -24,5 +24,5 @@ describe('scripts syntax gate', () => {
|
|
|
24
24
|
|
|
25
25
|
expect(result.status).toBe(0);
|
|
26
26
|
expect(result.stdout).toMatch(/syntax OK:/);
|
|
27
|
-
});
|
|
27
|
+
}, 120000);
|
|
28
28
|
});
|