eyeleng 1.0.6 → 1.0.8
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 +275 -18
- package/dist/browser/eyeleng.browser.js +123 -13
- package/eyeleng.js +123 -13
- package/package.json +7 -4
- package/playground.html +16 -4
- package/reports/w3c-rdf-earl.ttl +11707 -0
- package/src/parser.js +76 -2
- package/src/rdfManifest.js +13 -0
- package/src/shacl12RulesManifest.js +386 -0
- package/src/tokenizer.js +47 -11
- package/test/api.test.js +32 -0
- package/test/browser-bundle.test.js +8 -0
- package/test/shacl12-rules.test.js +29 -215
- package/test/w3c-rdf.test.js +2 -2
- package/tools/bundle.js +0 -17
- package/tools/w3c-rdf.js +20 -1
- package/tools/w3c-shacl12-rules.js +55 -0
- package/HANDBOOK.md +0 -1070
|
@@ -44,4 +44,12 @@ test('playground inline scripts are syntactically valid', () => {
|
|
|
44
44
|
assert.ok(checked > 0, 'expected at least one inline playground script');
|
|
45
45
|
});
|
|
46
46
|
|
|
47
|
+
|
|
48
|
+
test('playground loads version from package.json at runtime', () => {
|
|
49
|
+
const html = fs.readFileSync(path.join(root, 'playground.html'), 'utf8');
|
|
50
|
+
assert.equal(/window\.__EYELENG_VERSION__/.test(html), false, 'playground.html must not hard-code the package version');
|
|
51
|
+
assert.match(html, /fetch\(new URL\(['"]package\.json['"],\s*window\.location\.href\)/);
|
|
52
|
+
assert.match(html, /id=["']version-label["'][^>]*>v…<\/span>/);
|
|
53
|
+
});
|
|
54
|
+
|
|
47
55
|
main();
|
|
@@ -1,24 +1,18 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
|
-
const assert = require('node:assert/strict');
|
|
5
4
|
const fs = require('node:fs');
|
|
6
5
|
const path = require('node:path');
|
|
7
|
-
const { colors: C, info,
|
|
6
|
+
const { colors: C, info, summaryLine } = require('./harness.js');
|
|
7
|
+
const {
|
|
8
|
+
defaultShacl12RulesManifestUrl,
|
|
9
|
+
isLikelyNetworkError,
|
|
10
|
+
isW3cRequired,
|
|
11
|
+
runShacl12RulesManifest,
|
|
12
|
+
formatShacl12RulesProgressLine,
|
|
13
|
+
} = require('../src/shacl12RulesManifest.js');
|
|
8
14
|
|
|
9
|
-
const
|
|
10
|
-
const { tripleKey } = require('../src/term.js');
|
|
11
|
-
|
|
12
|
-
const DEFAULT_MANIFEST = 'https://w3c.github.io/data-shapes/shacl12-test-suite/tests/rules/manifest-rules.ttl';
|
|
13
|
-
const rootManifestUrl = process.env.EYELENG_SHACL12_RULES_MANIFEST || DEFAULT_MANIFEST;
|
|
14
|
-
const fetchTimeoutMs = Number(process.env.EYELENG_SHACL12_FETCH_TIMEOUT_MS || 30000);
|
|
15
|
-
const textCache = new Map();
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
function isLikelyNetworkError(err) {
|
|
19
|
-
const msg = String(err && (err.stack || err.message || err));
|
|
20
|
-
return /fetch failed|ENOTFOUND|EAI_AGAIN|ECONNREFUSED|ECONNRESET|network|timed out|GET .* failed/i.test(msg);
|
|
21
|
-
}
|
|
15
|
+
const rootManifestUrl = process.env.EYELENG_SHACL12_RULES_MANIFEST || defaultShacl12RulesManifestUrl;
|
|
22
16
|
|
|
23
17
|
function appendSummary(summary) {
|
|
24
18
|
const file = process.env.EYELENG_TEST_SUMMARY_FILE;
|
|
@@ -27,226 +21,46 @@ function appendSummary(summary) {
|
|
|
27
21
|
fs.appendFileSync(file, `${JSON.stringify(summary)}\n`, 'utf8');
|
|
28
22
|
}
|
|
29
23
|
|
|
30
|
-
function stripHash(url) {
|
|
31
|
-
const parsed = new URL(url);
|
|
32
|
-
parsed.hash = '';
|
|
33
|
-
return parsed.href;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
function resolveHref(baseUrl, href) {
|
|
37
|
-
return new URL(href, baseUrl).href;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
async function fetchText(url) {
|
|
41
|
-
const normalized = stripHash(url);
|
|
42
|
-
if (textCache.has(normalized)) return textCache.get(normalized);
|
|
43
|
-
|
|
44
|
-
const controller = new AbortController();
|
|
45
|
-
const timer = setTimeout(() => controller.abort(), fetchTimeoutMs);
|
|
46
|
-
try {
|
|
47
|
-
const response = await fetch(normalized, { signal: controller.signal });
|
|
48
|
-
if (!response.ok) throw new Error(`GET ${normalized} failed: ${response.status} ${response.statusText}`);
|
|
49
|
-
const text = await response.text();
|
|
50
|
-
textCache.set(normalized, text.replace(/\r\n/g, '\n').replace(/\r/g, '\n'));
|
|
51
|
-
return textCache.get(normalized);
|
|
52
|
-
} catch (err) {
|
|
53
|
-
if (err && err.name === 'AbortError') throw new Error(`GET ${normalized} timed out after ${fetchTimeoutMs} ms`);
|
|
54
|
-
throw err;
|
|
55
|
-
} finally {
|
|
56
|
-
clearTimeout(timer);
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
function parseIncludedManifests(rootUrl, text) {
|
|
61
|
-
const includeMatch = /mf:include\s*\(([\s\S]*?)\)\s*\./m.exec(text);
|
|
62
|
-
if (!includeMatch) throw new Error(`No mf:include list found in ${rootUrl}`);
|
|
63
|
-
return [...includeMatch[1].matchAll(/<([^>]+)>/g)].map((match) => resolveHref(rootUrl, match[1]));
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
function sectionName(manifestUrl) {
|
|
67
|
-
const pieces = new URL(manifestUrl).pathname.split('/').filter(Boolean);
|
|
68
|
-
if (pieces.length < 2) return manifestUrl;
|
|
69
|
-
return pieces[pieces.length - 2];
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
function manifestStatements(text) {
|
|
73
|
-
const statements = [];
|
|
74
|
-
const re = /([^\s;]+)\s+rdf:type\s+srt:([A-Za-z0-9]+)\s*;([\s\S]*?)\n\s*\./g;
|
|
75
|
-
let match;
|
|
76
|
-
while ((match = re.exec(text)) !== null) {
|
|
77
|
-
statements.push({ id: match[1], type: match[2], body: match[3] });
|
|
78
|
-
}
|
|
79
|
-
return statements;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
function parseManifestTests(manifestUrl, text) {
|
|
83
|
-
const section = sectionName(manifestUrl);
|
|
84
|
-
const tests = [];
|
|
85
|
-
|
|
86
|
-
for (const statement of manifestStatements(text)) {
|
|
87
|
-
const name = /mf:name\s+"((?:[^"\\]|\\.)*)"/m.exec(statement.body)?.[1] || statement.id;
|
|
88
|
-
|
|
89
|
-
if (statement.type === 'RulesEvalTest') {
|
|
90
|
-
const ruleset = /srt:ruleset\s+<([^>]+)>/m.exec(statement.body)?.[1];
|
|
91
|
-
const data = /srt:data\s+<([^>]+)>/m.exec(statement.body)?.[1];
|
|
92
|
-
const result = /mf:result\s+<([^>]+)>/m.exec(statement.body)?.[1];
|
|
93
|
-
if (!ruleset || !data || !result) throw new Error(`Incomplete eval test ${statement.id} in ${manifestUrl}`);
|
|
94
|
-
tests.push({
|
|
95
|
-
section,
|
|
96
|
-
id: statement.id,
|
|
97
|
-
type: statement.type,
|
|
98
|
-
name,
|
|
99
|
-
manifestUrl,
|
|
100
|
-
rulesetUrl: resolveHref(manifestUrl, ruleset),
|
|
101
|
-
dataUrl: resolveHref(manifestUrl, data),
|
|
102
|
-
resultUrl: resolveHref(manifestUrl, result),
|
|
103
|
-
});
|
|
104
|
-
continue;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
const action = /mf:action\s+<([^>]+)>/m.exec(statement.body)?.[1];
|
|
108
|
-
if (!action) throw new Error(`No mf:action found for ${statement.id} in ${manifestUrl}`);
|
|
109
|
-
tests.push({
|
|
110
|
-
section,
|
|
111
|
-
id: statement.id,
|
|
112
|
-
type: statement.type,
|
|
113
|
-
name,
|
|
114
|
-
manifestUrl,
|
|
115
|
-
actionUrl: resolveHref(manifestUrl, action),
|
|
116
|
-
});
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
return tests;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
async function loadTests() {
|
|
123
|
-
const rootText = await fetchText(rootManifestUrl);
|
|
124
|
-
const manifestUrls = parseIncludedManifests(rootManifestUrl, rootText);
|
|
125
|
-
const suites = await Promise.all(manifestUrls.map(async (manifestUrl) => ({
|
|
126
|
-
manifestUrl,
|
|
127
|
-
text: await fetchText(manifestUrl),
|
|
128
|
-
})));
|
|
129
|
-
return suites.flatMap(({ manifestUrl, text }) => parseManifestTests(manifestUrl, text));
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
function shouldPass(type) {
|
|
133
|
-
return /Positive/.test(type) || type === 'RulesEvalTest';
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
async function runSyntaxOrWellformedTest(test) {
|
|
137
|
-
const source = await fetchText(test.actionUrl);
|
|
138
|
-
const options = { filename: test.actionUrl, baseIRI: test.actionUrl, shacl12Conformance: true };
|
|
139
|
-
|
|
140
|
-
if (test.type.includes('Syntax')) {
|
|
141
|
-
if (shouldPass(test.type)) eyeleng.parse(source, options);
|
|
142
|
-
else assert.throws(() => eyeleng.parse(source, options), Error);
|
|
143
|
-
return;
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
if (shouldPass(test.type)) eyeleng.compile(source, options);
|
|
147
|
-
else assert.throws(() => eyeleng.compile(source, options), Error);
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
async function parseTurtleTriples(url) {
|
|
151
|
-
const source = await fetchText(url);
|
|
152
|
-
return eyeleng.parseRdfDocument(source, { filename: url, baseIRI: url }).triples;
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
function sortedTripleKeys(triples) {
|
|
156
|
-
return triples.map(tripleKey).sort();
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
function setDiff(actual, expected) {
|
|
160
|
-
const actualSet = new Set(actual);
|
|
161
|
-
return expected.filter((item) => !actualSet.has(item));
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
async function runEvalTest(test) {
|
|
165
|
-
const [rulesSource, dataTriples, expectedTriples] = await Promise.all([
|
|
166
|
-
fetchText(test.rulesetUrl),
|
|
167
|
-
parseTurtleTriples(test.dataUrl),
|
|
168
|
-
parseTurtleTriples(test.resultUrl),
|
|
169
|
-
]);
|
|
170
|
-
|
|
171
|
-
const compiled = eyeleng.compile(rulesSource, { filename: test.rulesetUrl, baseIRI: test.rulesetUrl, shacl12Conformance: true });
|
|
172
|
-
const program = { ...compiled.program, data: [...compiled.program.data, ...dataTriples] };
|
|
173
|
-
const result = eyeleng.evaluate(program, { analysis: compiled.analysis, shacl12Conformance: true });
|
|
174
|
-
|
|
175
|
-
const externalInput = new Set(dataTriples.map(tripleKey));
|
|
176
|
-
const actualTriples = result.closure.filter((triple) => !externalInput.has(tripleKey(triple)));
|
|
177
|
-
const actual = sortedTripleKeys(actualTriples);
|
|
178
|
-
const expected = sortedTripleKeys(expectedTriples);
|
|
179
|
-
|
|
180
|
-
try {
|
|
181
|
-
assert.deepEqual(actual, expected);
|
|
182
|
-
} catch (err) {
|
|
183
|
-
err.message += `\nMissing expected:\n${setDiff(actual, expected).join('\n')}\nUnexpected actual:\n${setDiff(expected, actual).join('\n')}`;
|
|
184
|
-
throw err;
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
async function runOne(test) {
|
|
189
|
-
if (test.type === 'RulesEvalTest') return runEvalTest(test);
|
|
190
|
-
return runSyntaxOrWellformedTest(test);
|
|
191
|
-
}
|
|
192
|
-
|
|
193
24
|
async function main() {
|
|
194
25
|
const suiteStart = Date.now();
|
|
195
26
|
info('W3C SHACL 1.2 Rules');
|
|
196
27
|
console.log(`${C.dim}manifest: ${rootManifestUrl}${C.n}`);
|
|
197
28
|
|
|
198
|
-
let
|
|
29
|
+
let result;
|
|
199
30
|
try {
|
|
200
|
-
|
|
31
|
+
result = await runShacl12RulesManifest(rootManifestUrl, {
|
|
32
|
+
onProgress(item, index) {
|
|
33
|
+
console.log(formatShacl12RulesProgressLine(item, index, { colors: C }));
|
|
34
|
+
},
|
|
35
|
+
});
|
|
201
36
|
} catch (err) {
|
|
202
|
-
if (isLikelyNetworkError(err) &&
|
|
203
|
-
console.log(`${C.dim}W3C SHACL 1.2 Rules manifests not reachable
|
|
37
|
+
if (isLikelyNetworkError(err) && !isW3cRequired()) {
|
|
38
|
+
console.log(`${C.dim}W3C SHACL 1.2 Rules manifests not reachable; EYELENG_W3C_REQUIRED=0 permits this skip.${C.n}`);
|
|
204
39
|
appendSummary({ section: 'W3C SHACL 1.2 Rules', passed: 1, failed: 0, total: 1, ms: Date.now() - suiteStart });
|
|
205
40
|
return;
|
|
206
41
|
}
|
|
207
|
-
failLine('---', 'load W3C SHACL 1.2 Rules manifests', Date.now() - suiteStart);
|
|
208
42
|
console.error(err.stack || err.message || String(err));
|
|
209
43
|
appendSummary({ section: 'W3C SHACL 1.2 Rules', passed: 0, failed: 1, total: 1, ms: Date.now() - suiteStart });
|
|
210
44
|
process.exitCode = 1;
|
|
211
45
|
return;
|
|
212
46
|
}
|
|
213
47
|
|
|
214
|
-
const idxWidth = Math.max(3, String(Math.max(1, tests.length)).length);
|
|
215
|
-
let passed = 0;
|
|
216
|
-
let failed = 0;
|
|
217
|
-
const bySection = new Map();
|
|
218
|
-
|
|
219
|
-
for (let i = 0; i < tests.length; i++) {
|
|
220
|
-
const test = tests[i];
|
|
221
|
-
const idx = String(i + 1).padStart(idxWidth, '0');
|
|
222
|
-
const start = Date.now();
|
|
223
|
-
try {
|
|
224
|
-
await runOne(test);
|
|
225
|
-
okLine(idx, `${test.section}/${test.name}`, Date.now() - start);
|
|
226
|
-
passed++;
|
|
227
|
-
const section = bySection.get(test.section) || { passed: 0, failed: 0 };
|
|
228
|
-
section.passed++;
|
|
229
|
-
bySection.set(test.section, section);
|
|
230
|
-
} catch (err) {
|
|
231
|
-
failLine(idx, `${test.section}/${test.name}`, Date.now() - start);
|
|
232
|
-
console.error(err.stack || err.message || String(err));
|
|
233
|
-
failed++;
|
|
234
|
-
const section = bySection.get(test.section) || { passed: 0, failed: 0 };
|
|
235
|
-
section.failed++;
|
|
236
|
-
bySection.set(test.section, section);
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
48
|
|
|
240
|
-
const
|
|
241
|
-
|
|
242
|
-
const total = counts.passed + counts.failed;
|
|
243
|
-
summaryLine(counts.failed === 0 ? 'ok' : 'fail', counts.passed, total, null, { label: section });
|
|
49
|
+
for (const section of result.bySection) {
|
|
50
|
+
summaryLine(section.failed === 0 ? 'ok' : 'fail', section.passed, section.total, null, { label: section.section });
|
|
244
51
|
}
|
|
245
|
-
summaryLine(
|
|
52
|
+
summaryLine(result.counts.fail === 0 ? 'ok' : 'fail', result.counts.pass, result.counts.total, result.durationMs);
|
|
246
53
|
console.log('');
|
|
247
54
|
|
|
248
|
-
appendSummary({
|
|
249
|
-
|
|
55
|
+
appendSummary({
|
|
56
|
+
section: 'W3C SHACL 1.2 Rules',
|
|
57
|
+
passed: result.counts.pass,
|
|
58
|
+
failed: result.counts.fail,
|
|
59
|
+
skipped: result.counts.skip,
|
|
60
|
+
total: result.counts.total,
|
|
61
|
+
ms: result.durationMs,
|
|
62
|
+
});
|
|
63
|
+
if (result.counts.fail > 0) process.exitCode = 1;
|
|
250
64
|
}
|
|
251
65
|
|
|
252
66
|
main().catch((err) => {
|
package/test/w3c-rdf.test.js
CHANGED
|
@@ -177,8 +177,8 @@ test('official W3C RDF manifests run with streaming progress when reachable', as
|
|
|
177
177
|
},
|
|
178
178
|
});
|
|
179
179
|
} catch (err) {
|
|
180
|
-
if (isLikelyNetworkError(err) && process.env.EYELENG_W3C_REQUIRED
|
|
181
|
-
console.log(`${C.dim}W3C RDF manifests not reachable
|
|
180
|
+
if (isLikelyNetworkError(err) && process.env.EYELENG_W3C_REQUIRED === '0') {
|
|
181
|
+
console.log(`${C.dim}W3C RDF manifests not reachable; EYELENG_W3C_REQUIRED=0 permits this skip.${C.n}`);
|
|
182
182
|
return;
|
|
183
183
|
}
|
|
184
184
|
throw err;
|
package/tools/bundle.js
CHANGED
|
@@ -57,22 +57,6 @@ function js(value) {
|
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
|
|
60
|
-
function packageVersion() {
|
|
61
|
-
const packageJson = JSON.parse(fs.readFileSync(path.join(root, 'package.json'), 'utf8'));
|
|
62
|
-
if (!packageJson.version) throw new Error('package.json is missing a version');
|
|
63
|
-
return packageJson.version;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
function syncPlaygroundVersion() {
|
|
67
|
-
const version = packageVersion();
|
|
68
|
-
const outPath = path.join(root, playgroundOutput);
|
|
69
|
-
const html = fs.readFileSync(outPath, 'utf8');
|
|
70
|
-
const pattern = /(window\.__EYELENG_VERSION__\s*=\s*)["'][^"']*["']\s*;/;
|
|
71
|
-
if (!pattern.test(html)) throw new Error('Could not find window.__EYELENG_VERSION__ in playground.html');
|
|
72
|
-
const next = html.replace(pattern, `$1${js(version)};`);
|
|
73
|
-
fs.writeFileSync(outPath, next, 'utf8');
|
|
74
|
-
console.log(`wrote ${playgroundOutput} version ${version}`);
|
|
75
|
-
}
|
|
76
60
|
|
|
77
61
|
function ensureParentDir(filename) {
|
|
78
62
|
fs.mkdirSync(path.dirname(filename), { recursive: true });
|
|
@@ -172,4 +156,3 @@ function indent(source, spaces) {
|
|
|
172
156
|
|
|
173
157
|
buildCli();
|
|
174
158
|
buildBrowser();
|
|
175
|
-
syncPlaygroundVersion();
|
package/tools/w3c-rdf.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
|
+
const path = require('node:path');
|
|
4
5
|
const { colors: C } = require('../test/harness.js');
|
|
5
6
|
const {
|
|
6
7
|
defaultW3cRdfManifestUrls,
|
|
@@ -8,13 +9,27 @@ const {
|
|
|
8
9
|
formatW3cRdfProgressLine,
|
|
9
10
|
formatW3cRdfManifestsResult,
|
|
10
11
|
rdfManifestsToEarl,
|
|
12
|
+
writeRdfEarlReport,
|
|
13
|
+
defaultRdfReportPath,
|
|
11
14
|
} = require('../src/rdfManifest.js');
|
|
12
15
|
|
|
16
|
+
function argValue(argv, name) {
|
|
17
|
+
const index = argv.indexOf(name);
|
|
18
|
+
if (index < 0) return null;
|
|
19
|
+
return argv[index + 1] || null;
|
|
20
|
+
}
|
|
21
|
+
|
|
13
22
|
async function main(argv = process.argv.slice(2)) {
|
|
14
23
|
const json = argv.includes('--json');
|
|
15
24
|
const earl = argv.includes('--earl');
|
|
25
|
+
const noReport = argv.includes('--no-report');
|
|
16
26
|
const quiet = json || earl || argv.includes('--quiet');
|
|
17
|
-
const
|
|
27
|
+
const output = argValue(argv, '--output') || defaultRdfReportPath();
|
|
28
|
+
const manifests = argv.filter((arg, index) => {
|
|
29
|
+
if (arg.startsWith('--')) return false;
|
|
30
|
+
if (argv[index - 1] === '--output') return false;
|
|
31
|
+
return true;
|
|
32
|
+
});
|
|
18
33
|
const resources = manifests.length ? manifests : defaultW3cRdfManifestUrls;
|
|
19
34
|
const result = await runW3cRdfManifests(resources, {
|
|
20
35
|
onManifestStart(resource, index, total) {
|
|
@@ -24,6 +39,10 @@ async function main(argv = process.argv.slice(2)) {
|
|
|
24
39
|
if (!quiet) console.log(formatW3cRdfProgressLine(item, index, { colors: C }));
|
|
25
40
|
},
|
|
26
41
|
});
|
|
42
|
+
if (!noReport) {
|
|
43
|
+
const reportPath = writeRdfEarlReport(result, output, { assertedBy: '<https://github.com/eyereasoner/eyeleng>' });
|
|
44
|
+
if (!quiet) console.log(`${C.dim}EARL report: ${path.relative(path.join(__dirname, '..'), reportPath)}${C.n}`);
|
|
45
|
+
}
|
|
27
46
|
if (json) process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
|
|
28
47
|
else if (earl) process.stdout.write(`${rdfManifestsToEarl(result, { assertedBy: '<https://github.com/eyereasoner/eyeleng>' })}\n`);
|
|
29
48
|
else process.stdout.write(`${formatW3cRdfManifestsResult(result, { colors: C })}\n`);
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const path = require('node:path');
|
|
5
|
+
const { colors: C } = require('../test/harness.js');
|
|
6
|
+
const {
|
|
7
|
+
defaultShacl12RulesManifestUrl,
|
|
8
|
+
runShacl12RulesManifest,
|
|
9
|
+
formatShacl12RulesProgressLine,
|
|
10
|
+
formatShacl12RulesManifestResult,
|
|
11
|
+
shacl12RulesManifestToEarl,
|
|
12
|
+
writeShacl12RulesEarlReport,
|
|
13
|
+
defaultReportPath,
|
|
14
|
+
} = require('../src/shacl12RulesManifest.js');
|
|
15
|
+
|
|
16
|
+
function argValue(argv, name) {
|
|
17
|
+
const index = argv.indexOf(name);
|
|
18
|
+
if (index < 0) return null;
|
|
19
|
+
return argv[index + 1] || null;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
async function main(argv = process.argv.slice(2)) {
|
|
23
|
+
const json = argv.includes('--json');
|
|
24
|
+
const earl = argv.includes('--earl');
|
|
25
|
+
const noReport = argv.includes('--no-report');
|
|
26
|
+
const quiet = json || earl || argv.includes('--quiet');
|
|
27
|
+
const output = argValue(argv, '--output') || defaultReportPath();
|
|
28
|
+
const manifests = argv.filter((arg, index) => {
|
|
29
|
+
if (arg.startsWith('--')) return false;
|
|
30
|
+
if (argv[index - 1] === '--output') return false;
|
|
31
|
+
return true;
|
|
32
|
+
});
|
|
33
|
+
const manifest = manifests[0] || process.env.EYELENG_SHACL12_RULES_MANIFEST || defaultShacl12RulesManifestUrl;
|
|
34
|
+
|
|
35
|
+
const result = await runShacl12RulesManifest(manifest, {
|
|
36
|
+
onProgress(item, index) {
|
|
37
|
+
if (!quiet) console.log(formatShacl12RulesProgressLine(item, index, { colors: C }));
|
|
38
|
+
},
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
if (!noReport) {
|
|
42
|
+
const reportPath = writeShacl12RulesEarlReport(result, output);
|
|
43
|
+
if (!quiet) console.log(`${C.dim}EARL report: ${path.relative(path.join(__dirname, '..'), reportPath)}${C.n}`);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (json) process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
|
|
47
|
+
else if (earl) process.stdout.write(`${shacl12RulesManifestToEarl(result)}\n`);
|
|
48
|
+
else process.stdout.write(`${formatShacl12RulesManifestResult(result, { colors: C })}\n`);
|
|
49
|
+
return result.counts.fail === 0 ? 0 : 1;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
main().then((code) => { process.exitCode = code; }, (err) => {
|
|
53
|
+
console.error(err.stack || err.message || String(err));
|
|
54
|
+
process.exitCode = 1;
|
|
55
|
+
});
|