eyeling 1.28.0 → 1.28.2
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 +6 -13
- package/dist/browser/eyeling.browser.js +252 -28
- package/examples/alma-rdf-messages.n3 +202 -0
- package/examples/output/alma-rdf-messages.n3 +0 -0
- package/eyeling.js +259 -29
- package/lib/cli.js +140 -27
- package/lib/engine.js +9 -1
- package/lib/lexer.js +103 -0
- package/notes/rdf-message-logs.md +228 -0
- package/package.json +1 -1
- package/test/api.test.js +16 -0
- package/test/stream_messages.test.js +102 -3
- package/tools/bundle.js +7 -1
package/eyeling.js
CHANGED
|
@@ -4623,10 +4623,13 @@ module.exports = {
|
|
|
4623
4623
|
'use strict';
|
|
4624
4624
|
|
|
4625
4625
|
const fs = require('node:fs');
|
|
4626
|
-
const os = require('node:os');
|
|
4627
4626
|
const path = require('node:path');
|
|
4628
|
-
const { pathToFileURL, fileURLToPath } = require('node:url');
|
|
4627
|
+
const { pathToFileURL, fileURLToPath, URL } = require('node:url');
|
|
4629
4628
|
const { TextDecoder } = require('node:util');
|
|
4629
|
+
const http = require('node:http');
|
|
4630
|
+
const https = require('node:https');
|
|
4631
|
+
const readline = require('node:readline');
|
|
4632
|
+
const zlib = require('node:zlib');
|
|
4630
4633
|
|
|
4631
4634
|
const engine = require('./engine');
|
|
4632
4635
|
const deref = require('./deref');
|
|
@@ -4760,7 +4763,8 @@ function __httpFetchScriptBody({ prefixOnly = false } = {}) {
|
|
|
4760
4763
|
if (finished) return;
|
|
4761
4764
|
chunks.push(chunk);
|
|
4762
4765
|
bytes += chunk.length;
|
|
4763
|
-
|
|
4766
|
+
const text = Buffer.concat(chunks, bytes).toString('utf8');
|
|
4767
|
+
if (bytes >= limit || /^\\s*(?:@version|VERSION)\\s+(["'])(?:1\\.1|1\\.2|1\\.2-basic)-messages\\1\\s*\\.?\\s*(?:#.*)?$/im.test(text)) finish();
|
|
4764
4768
|
});
|
|
4765
4769
|
body.on('end', finish);
|
|
4766
4770
|
body.on('error', (e) => { console.error(e && e.message ? e.message : String(e)); process.exit(5); });
|
|
@@ -4789,22 +4793,95 @@ function __readHttpPrefixSync(sourceLabel, byteLimit = 64 * 1024) {
|
|
|
4789
4793
|
return r.stdout;
|
|
4790
4794
|
}
|
|
4791
4795
|
|
|
4792
|
-
function
|
|
4793
|
-
const
|
|
4794
|
-
|
|
4795
|
-
|
|
4796
|
-
|
|
4797
|
-
|
|
4798
|
-
|
|
4799
|
-
|
|
4796
|
+
function __openHttpTextStream(sourceLabel, redirects = 0) {
|
|
4797
|
+
const maxRedirects = 10;
|
|
4798
|
+
return new Promise((resolve, reject) => {
|
|
4799
|
+
if (redirects > maxRedirects) {
|
|
4800
|
+
reject(new Error('Too many redirects'));
|
|
4801
|
+
return;
|
|
4802
|
+
}
|
|
4803
|
+
|
|
4804
|
+
let parsed;
|
|
4805
|
+
try {
|
|
4806
|
+
parsed = new URL(sourceLabel);
|
|
4807
|
+
} catch (e) {
|
|
4808
|
+
reject(e);
|
|
4809
|
+
return;
|
|
4810
|
+
}
|
|
4811
|
+
|
|
4812
|
+
const mod = parsed.protocol === 'https:' ? https : parsed.protocol === 'http:' ? http : null;
|
|
4813
|
+
if (!mod) {
|
|
4814
|
+
reject(new Error(`Unsupported protocol ${parsed.protocol}`));
|
|
4815
|
+
return;
|
|
4816
|
+
}
|
|
4817
|
+
|
|
4818
|
+
const req = mod.request(
|
|
4819
|
+
{
|
|
4820
|
+
protocol: parsed.protocol,
|
|
4821
|
+
hostname: parsed.hostname,
|
|
4822
|
+
port: parsed.port || undefined,
|
|
4823
|
+
path: parsed.pathname + parsed.search,
|
|
4824
|
+
headers: {
|
|
4825
|
+
accept: 'text/n3, text/turtle, application/trig, application/n-triples, application/n-quads, text/plain;q=0.8, */*;q=0.01',
|
|
4826
|
+
'accept-encoding': 'identity',
|
|
4827
|
+
'user-agent': 'eyeling-rdf-message-stream',
|
|
4828
|
+
},
|
|
4829
|
+
},
|
|
4830
|
+
(res) => {
|
|
4831
|
+
const sc = res.statusCode || 0;
|
|
4832
|
+
if (sc >= 300 && sc < 400 && res.headers && res.headers.location) {
|
|
4833
|
+
const next = new URL(res.headers.location, sourceLabel).toString();
|
|
4834
|
+
res.resume();
|
|
4835
|
+
resolve(__openHttpTextStream(next, redirects + 1));
|
|
4836
|
+
return;
|
|
4837
|
+
}
|
|
4838
|
+
if (sc < 200 || sc >= 300) {
|
|
4839
|
+
res.resume();
|
|
4840
|
+
reject(new Error(`HTTP status ${sc}`));
|
|
4841
|
+
return;
|
|
4842
|
+
}
|
|
4843
|
+
|
|
4844
|
+
const enc = String((res.headers && res.headers['content-encoding']) || '').toLowerCase();
|
|
4845
|
+
let body = res;
|
|
4846
|
+
if (enc.includes('gzip')) body = res.pipe(zlib.createGunzip());
|
|
4847
|
+
else if (enc.includes('deflate')) body = res.pipe(zlib.createInflate());
|
|
4848
|
+
else if (enc.includes('br')) body = res.pipe(zlib.createBrotliDecompress());
|
|
4849
|
+
resolve(body);
|
|
4850
|
+
},
|
|
4851
|
+
);
|
|
4852
|
+
req.on('error', reject);
|
|
4853
|
+
req.end();
|
|
4800
4854
|
});
|
|
4801
|
-
if (r.status !== 0) {
|
|
4802
|
-
try { fs.rmSync(dir, { recursive: true, force: true }); } catch {}
|
|
4803
|
-
throw new Error(`Failed to dereference ${sourceLabel}${r.stderr ? ': ' + String(r.stderr).trim() : ''}`);
|
|
4804
|
-
}
|
|
4805
|
-
return { file, cleanup: () => { try { fs.rmSync(dir, { recursive: true, force: true }); } catch {} } };
|
|
4806
4855
|
}
|
|
4807
4856
|
|
|
4857
|
+
async function forEachLineInHttpSource(sourceLabel, onLine) {
|
|
4858
|
+
const body = await __openHttpTextStream(sourceLabel);
|
|
4859
|
+
await new Promise((resolve, reject) => {
|
|
4860
|
+
let settled = false;
|
|
4861
|
+
function done(err) {
|
|
4862
|
+
if (settled) return;
|
|
4863
|
+
settled = true;
|
|
4864
|
+
if (err) reject(err);
|
|
4865
|
+
else resolve();
|
|
4866
|
+
}
|
|
4867
|
+
|
|
4868
|
+
const rl = readline.createInterface({ input: body, crlfDelay: Infinity });
|
|
4869
|
+
rl.on('line', (line) => {
|
|
4870
|
+
try {
|
|
4871
|
+
onLine(line + '\n');
|
|
4872
|
+
} catch (e) {
|
|
4873
|
+
try { rl.close(); } catch {}
|
|
4874
|
+
if (body && typeof body.destroy === 'function') {
|
|
4875
|
+
try { body.destroy(); } catch {}
|
|
4876
|
+
}
|
|
4877
|
+
done(e);
|
|
4878
|
+
}
|
|
4879
|
+
});
|
|
4880
|
+
rl.on('close', () => done());
|
|
4881
|
+
rl.on('error', done);
|
|
4882
|
+
body.on('error', done);
|
|
4883
|
+
});
|
|
4884
|
+
}
|
|
4808
4885
|
|
|
4809
4886
|
const RDF_MESSAGE_VERSION_RE = /^\s*(?:@version|VERSION)\s+(["'])(?:1\.1|1\.2|1\.2-basic)-messages\1\s*\.?\s*(?:#.*)?$/im;
|
|
4810
4887
|
const RDF_MESSAGE_VERSION_LINE_RE = /^\s*(?:@version|VERSION)\s+(["'])(?:1\.1|1\.2|1\.2-basic)-messages\1\s*\.?\s*(?:#.*)?$/i;
|
|
@@ -5022,15 +5099,50 @@ function __forEachRdfMessageChunkSync(sourceLabel, onMessage) {
|
|
|
5022
5099
|
return;
|
|
5023
5100
|
}
|
|
5024
5101
|
if (__isHttpSource(sourceLabel)) {
|
|
5025
|
-
|
|
5026
|
-
|
|
5027
|
-
|
|
5028
|
-
|
|
5029
|
-
|
|
5102
|
+
throw new Error('internal error: HTTP RDF Message Logs must be streamed asynchronously');
|
|
5103
|
+
}
|
|
5104
|
+
forEachRdfMessageChunkInText(__readInputSourceSync(sourceLabel), onMessage);
|
|
5105
|
+
}
|
|
5106
|
+
|
|
5107
|
+
|
|
5108
|
+
async function forEachRdfMessageChunkInHttpSource(sourceLabel, onMessage) {
|
|
5109
|
+
const directives = [];
|
|
5110
|
+
const seenDirectives = new Set();
|
|
5111
|
+
let chunk = '';
|
|
5112
|
+
let messageIndex = 1;
|
|
5113
|
+
let sawVersion = false;
|
|
5114
|
+
let sawDelimiter = false;
|
|
5115
|
+
|
|
5116
|
+
function emit() {
|
|
5117
|
+
onMessage({ messageIndex, chunk, directives: directives.slice() });
|
|
5118
|
+
messageIndex += 1;
|
|
5119
|
+
chunk = '';
|
|
5120
|
+
}
|
|
5121
|
+
|
|
5122
|
+
await forEachLineInHttpSource(sourceLabel, (line) => {
|
|
5123
|
+
if (RDF_MESSAGE_VERSION_LINE_RE.test(line)) {
|
|
5124
|
+
sawVersion = true;
|
|
5125
|
+
return;
|
|
5030
5126
|
}
|
|
5127
|
+
if (RDF_MESSAGE_DELIMITER_LINE_RE.test(line)) {
|
|
5128
|
+
emit();
|
|
5129
|
+
sawDelimiter = true;
|
|
5130
|
+
return;
|
|
5131
|
+
}
|
|
5132
|
+
addRdfDirective(directives, seenDirectives, line);
|
|
5133
|
+
chunk += line;
|
|
5134
|
+
});
|
|
5135
|
+
|
|
5136
|
+
if (!sawVersion) throw new Error('not an RDF Message Log: missing VERSION "*-messages" directive');
|
|
5137
|
+
if (sawDelimiter || hasRdfPayload(chunk)) emit();
|
|
5138
|
+
}
|
|
5139
|
+
|
|
5140
|
+
async function __forEachRdfMessageChunk(sourceLabel, onMessage) {
|
|
5141
|
+
if (__isHttpSource(sourceLabel)) {
|
|
5142
|
+
await forEachRdfMessageChunkInHttpSource(sourceLabel, onMessage);
|
|
5031
5143
|
return;
|
|
5032
5144
|
}
|
|
5033
|
-
|
|
5145
|
+
__forEachRdfMessageChunkSync(sourceLabel, onMessage);
|
|
5034
5146
|
}
|
|
5035
5147
|
|
|
5036
5148
|
function factsContainOutputStrings(triplesForOutput) {
|
|
@@ -5096,7 +5208,7 @@ function runParsedDocumentOnce(mergedDocument, { rdfMode = false, outputPrefixes
|
|
|
5096
5208
|
}
|
|
5097
5209
|
}
|
|
5098
5210
|
|
|
5099
|
-
function runStreamMessagesMode(sourceLabels, { rdfMode }) {
|
|
5211
|
+
async function runStreamMessagesMode(sourceLabels, { rdfMode }) {
|
|
5100
5212
|
const ordinarySourceLabels = [];
|
|
5101
5213
|
const messageSourceLabels = [];
|
|
5102
5214
|
|
|
@@ -5148,7 +5260,7 @@ function runStreamMessagesMode(sourceLabels, { rdfMode }) {
|
|
|
5148
5260
|
const fullIriPrefixes = new PrefixEnv({});
|
|
5149
5261
|
for (const messageSourceLabel of messageSourceLabels) {
|
|
5150
5262
|
try {
|
|
5151
|
-
|
|
5263
|
+
await __forEachRdfMessageChunk(messageSourceLabel, ({ messageIndex, chunk, directives }) => {
|
|
5152
5264
|
const messageText = buildSingleMessageReplayDocument({
|
|
5153
5265
|
sourceLabel: messageSourceLabel,
|
|
5154
5266
|
messageIndex,
|
|
@@ -5182,7 +5294,7 @@ function runStreamMessagesMode(sourceLabels, { rdfMode }) {
|
|
|
5182
5294
|
}
|
|
5183
5295
|
}
|
|
5184
5296
|
|
|
5185
|
-
function main() {
|
|
5297
|
+
async function main() {
|
|
5186
5298
|
// Drop "node" and script name; keep only user-provided args
|
|
5187
5299
|
// Expand combined short options: -pt == -p -t
|
|
5188
5300
|
const argvRaw = process.argv.slice(2);
|
|
@@ -5321,7 +5433,7 @@ function main() {
|
|
|
5321
5433
|
}
|
|
5322
5434
|
|
|
5323
5435
|
if (streamMessagesMode) {
|
|
5324
|
-
runStreamMessagesMode(sourceLabels, { rdfMode });
|
|
5436
|
+
await runStreamMessagesMode(sourceLabels, { rdfMode });
|
|
5325
5437
|
return;
|
|
5326
5438
|
}
|
|
5327
5439
|
|
|
@@ -5405,7 +5517,8 @@ function main() {
|
|
|
5405
5517
|
return false;
|
|
5406
5518
|
}
|
|
5407
5519
|
|
|
5408
|
-
|
|
5520
|
+
|
|
5521
|
+
function factsContainOutputStrings(triplesForOutput) {
|
|
5409
5522
|
return (
|
|
5410
5523
|
Array.isArray(triplesForOutput) &&
|
|
5411
5524
|
triplesForOutput.some(
|
|
@@ -9843,7 +9956,15 @@ function reasonRdfJs(input, opts = {}) {
|
|
|
9843
9956
|
// Minimal export surface for Node + browser/worker
|
|
9844
9957
|
function main() {
|
|
9845
9958
|
// Lazily require to avoid hard cycles in the module graph.
|
|
9846
|
-
|
|
9959
|
+
const result = require('./cli').main();
|
|
9960
|
+
if (result && typeof result.then === 'function') {
|
|
9961
|
+
result.catch((e) => {
|
|
9962
|
+
const msg = e && e.stack ? e.stack : e && e.message ? e.message : String(e);
|
|
9963
|
+
console.error(msg);
|
|
9964
|
+
process.exit(1);
|
|
9965
|
+
});
|
|
9966
|
+
}
|
|
9967
|
+
return result;
|
|
9847
9968
|
}
|
|
9848
9969
|
|
|
9849
9970
|
// ---------------------------------------------------------------------------
|
|
@@ -11019,11 +11140,114 @@ function normalizeRdfCompatibility(inputText) {
|
|
|
11019
11140
|
return text.slice(at, j);
|
|
11020
11141
|
}
|
|
11021
11142
|
|
|
11143
|
+
function readBalancedTermAt(text, at) {
|
|
11144
|
+
const open = text[at];
|
|
11145
|
+
const matching = { '{': '}', '[': ']', '(': ')' };
|
|
11146
|
+
if (!matching[open]) return null;
|
|
11147
|
+
|
|
11148
|
+
const stack = [matching[open]];
|
|
11149
|
+
let j = at + 1;
|
|
11150
|
+
while (j < text.length) {
|
|
11151
|
+
const ch = text[j];
|
|
11152
|
+
if (ch === '"' || ch === "'") {
|
|
11153
|
+
const str = readStringAt(text, j);
|
|
11154
|
+
j = str.end;
|
|
11155
|
+
continue;
|
|
11156
|
+
}
|
|
11157
|
+
if (ch === '<' && !text.startsWith('<<', j)) {
|
|
11158
|
+
const iri = readIriAt(text, j);
|
|
11159
|
+
j = iri.end;
|
|
11160
|
+
continue;
|
|
11161
|
+
}
|
|
11162
|
+
if (ch === '#') {
|
|
11163
|
+
while (j < text.length && text[j] !== '\n' && text[j] !== '\r') j += 1;
|
|
11164
|
+
continue;
|
|
11165
|
+
}
|
|
11166
|
+
if (matching[ch]) {
|
|
11167
|
+
stack.push(matching[ch]);
|
|
11168
|
+
j += 1;
|
|
11169
|
+
continue;
|
|
11170
|
+
}
|
|
11171
|
+
if (ch === stack[stack.length - 1]) {
|
|
11172
|
+
stack.pop();
|
|
11173
|
+
j += 1;
|
|
11174
|
+
if (stack.length === 0) return { text: text.slice(at, j), end: j };
|
|
11175
|
+
continue;
|
|
11176
|
+
}
|
|
11177
|
+
j += 1;
|
|
11178
|
+
}
|
|
11179
|
+
|
|
11180
|
+
throw new N3SyntaxError(`Unterminated term inside RDF 1.2 triple term, expected ${stack[stack.length - 1]}`);
|
|
11181
|
+
}
|
|
11182
|
+
|
|
11183
|
+
function readRdfTripleTermComponent(text, at) {
|
|
11184
|
+
const j = skipWsAndComments(text, at);
|
|
11185
|
+
if (j >= text.length) return null;
|
|
11186
|
+
const ch = text[j];
|
|
11187
|
+
|
|
11188
|
+
if (ch === '<') return readIriAt(text, j);
|
|
11189
|
+
|
|
11190
|
+
if (ch === '"' || ch === "'") {
|
|
11191
|
+
const str = readStringAt(text, j);
|
|
11192
|
+
let end = str.end;
|
|
11193
|
+
let termText = str.text;
|
|
11194
|
+
if (text.startsWith('^^', end)) {
|
|
11195
|
+
const datatype = readRdfTripleTermComponent(text, end + 2);
|
|
11196
|
+
if (datatype) {
|
|
11197
|
+
termText += '^^' + datatype.text;
|
|
11198
|
+
end = datatype.end;
|
|
11199
|
+
}
|
|
11200
|
+
} else if (text[end] === '@') {
|
|
11201
|
+
let k = end + 1;
|
|
11202
|
+
if (/[A-Za-z]/.test(text[k] || '')) {
|
|
11203
|
+
while (k < text.length && /[A-Za-z0-9-]/.test(text[k])) k += 1;
|
|
11204
|
+
termText += text.slice(end, k);
|
|
11205
|
+
end = k;
|
|
11206
|
+
}
|
|
11207
|
+
}
|
|
11208
|
+
return { text: termText, end };
|
|
11209
|
+
}
|
|
11210
|
+
|
|
11211
|
+
if (ch === '{' || ch === '[' || ch === '(') return readBalancedTermAt(text, j);
|
|
11212
|
+
|
|
11213
|
+
let k = j;
|
|
11214
|
+
while (k < text.length && !/\s/.test(text[k]) && !'{}[](),;'.includes(text[k])) k += 1;
|
|
11215
|
+
if (k === j) return null;
|
|
11216
|
+
const value = text.slice(j, k);
|
|
11217
|
+
if (!value || value.startsWith('@')) return null;
|
|
11218
|
+
return { text: value, end: k };
|
|
11219
|
+
}
|
|
11220
|
+
|
|
11221
|
+
function validateSingleRdfTripleTerm(rawTriple) {
|
|
11222
|
+
const triple = rawTriple.trim();
|
|
11223
|
+
if (!triple) throw new N3SyntaxError('RDF 1.2 triple term must contain exactly one subject, predicate, and object');
|
|
11224
|
+
|
|
11225
|
+
let pos = 0;
|
|
11226
|
+
for (const label of ['subject', 'predicate', 'object']) {
|
|
11227
|
+
const term = readRdfTripleTermComponent(triple, pos);
|
|
11228
|
+
if (!term) throw new N3SyntaxError(`RDF 1.2 triple term is missing a ${label}`);
|
|
11229
|
+
pos = term.end;
|
|
11230
|
+
}
|
|
11231
|
+
|
|
11232
|
+
const rest = skipWsAndComments(triple, pos);
|
|
11233
|
+
if (rest >= triple.length) return;
|
|
11234
|
+
|
|
11235
|
+
const found = triple[rest];
|
|
11236
|
+
if (found === ',') {
|
|
11237
|
+
throw new N3SyntaxError("RDF 1.2 triple terms must contain exactly one object; object lists using ',' are not valid inside <<( ... )>>");
|
|
11238
|
+
}
|
|
11239
|
+
if (found === ';') {
|
|
11240
|
+
throw new N3SyntaxError("RDF 1.2 triple terms must contain exactly one predicate-object pair; ';' is not valid inside <<( ... )>>");
|
|
11241
|
+
}
|
|
11242
|
+
throw new N3SyntaxError(`RDF 1.2 triple term must contain exactly one subject, predicate, and object; unexpected ${JSON.stringify(triple.slice(rest, rest + 20))}`);
|
|
11243
|
+
}
|
|
11244
|
+
|
|
11022
11245
|
function graphTermFromTripleBody(rawBody, parenthesized) {
|
|
11023
11246
|
let body = rawBody.trim();
|
|
11024
11247
|
if (parenthesized && body.startsWith('(') && body.endsWith(')')) body = body.slice(1, -1).trim();
|
|
11025
11248
|
const split = splitTopLevelReifier(body);
|
|
11026
11249
|
const triple = split.triple;
|
|
11250
|
+
validateSingleRdfTripleTerm(triple);
|
|
11027
11251
|
const graph = '{ ' + triple + ' }';
|
|
11028
11252
|
if (split.reifier) {
|
|
11029
11253
|
const reifier = firstTerm(split.reifier);
|
|
@@ -16061,7 +16285,13 @@ module.exports = {
|
|
|
16061
16285
|
|
|
16062
16286
|
try {
|
|
16063
16287
|
if (__outerModule && __outerRequire && __outerRequire.main === __outerModule && typeof __entry.main === "function") {
|
|
16064
|
-
__entry.main();
|
|
16288
|
+
const __mainResult = __entry.main();
|
|
16289
|
+
if (__mainResult && typeof __mainResult.then === "function") {
|
|
16290
|
+
__mainResult.catch((e) => {
|
|
16291
|
+
try { if (typeof console !== "undefined" && console.error) console.error(e && e.stack ? e.stack : e && e.message ? e.message : String(e)); } catch (ignoredError) {}
|
|
16292
|
+
try { if (typeof process !== "undefined" && process.exit) process.exit(1); } catch (ignoredError) {}
|
|
16293
|
+
});
|
|
16294
|
+
}
|
|
16065
16295
|
}
|
|
16066
16296
|
} catch (ignoredError) {}
|
|
16067
16297
|
})();
|
package/lib/cli.js
CHANGED
|
@@ -8,10 +8,13 @@
|
|
|
8
8
|
'use strict';
|
|
9
9
|
|
|
10
10
|
const fs = require('node:fs');
|
|
11
|
-
const os = require('node:os');
|
|
12
11
|
const path = require('node:path');
|
|
13
|
-
const { pathToFileURL, fileURLToPath } = require('node:url');
|
|
12
|
+
const { pathToFileURL, fileURLToPath, URL } = require('node:url');
|
|
14
13
|
const { TextDecoder } = require('node:util');
|
|
14
|
+
const http = require('node:http');
|
|
15
|
+
const https = require('node:https');
|
|
16
|
+
const readline = require('node:readline');
|
|
17
|
+
const zlib = require('node:zlib');
|
|
15
18
|
|
|
16
19
|
const engine = require('./engine');
|
|
17
20
|
const deref = require('./deref');
|
|
@@ -145,7 +148,8 @@ function __httpFetchScriptBody({ prefixOnly = false } = {}) {
|
|
|
145
148
|
if (finished) return;
|
|
146
149
|
chunks.push(chunk);
|
|
147
150
|
bytes += chunk.length;
|
|
148
|
-
|
|
151
|
+
const text = Buffer.concat(chunks, bytes).toString('utf8');
|
|
152
|
+
if (bytes >= limit || /^\\s*(?:@version|VERSION)\\s+(["'])(?:1\\.1|1\\.2|1\\.2-basic)-messages\\1\\s*\\.?\\s*(?:#.*)?$/im.test(text)) finish();
|
|
149
153
|
});
|
|
150
154
|
body.on('end', finish);
|
|
151
155
|
body.on('error', (e) => { console.error(e && e.message ? e.message : String(e)); process.exit(5); });
|
|
@@ -174,22 +178,95 @@ function __readHttpPrefixSync(sourceLabel, byteLimit = 64 * 1024) {
|
|
|
174
178
|
return r.stdout;
|
|
175
179
|
}
|
|
176
180
|
|
|
177
|
-
function
|
|
178
|
-
const
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
181
|
+
function __openHttpTextStream(sourceLabel, redirects = 0) {
|
|
182
|
+
const maxRedirects = 10;
|
|
183
|
+
return new Promise((resolve, reject) => {
|
|
184
|
+
if (redirects > maxRedirects) {
|
|
185
|
+
reject(new Error('Too many redirects'));
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
let parsed;
|
|
190
|
+
try {
|
|
191
|
+
parsed = new URL(sourceLabel);
|
|
192
|
+
} catch (e) {
|
|
193
|
+
reject(e);
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const mod = parsed.protocol === 'https:' ? https : parsed.protocol === 'http:' ? http : null;
|
|
198
|
+
if (!mod) {
|
|
199
|
+
reject(new Error(`Unsupported protocol ${parsed.protocol}`));
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
const req = mod.request(
|
|
204
|
+
{
|
|
205
|
+
protocol: parsed.protocol,
|
|
206
|
+
hostname: parsed.hostname,
|
|
207
|
+
port: parsed.port || undefined,
|
|
208
|
+
path: parsed.pathname + parsed.search,
|
|
209
|
+
headers: {
|
|
210
|
+
accept: 'text/n3, text/turtle, application/trig, application/n-triples, application/n-quads, text/plain;q=0.8, */*;q=0.01',
|
|
211
|
+
'accept-encoding': 'identity',
|
|
212
|
+
'user-agent': 'eyeling-rdf-message-stream',
|
|
213
|
+
},
|
|
214
|
+
},
|
|
215
|
+
(res) => {
|
|
216
|
+
const sc = res.statusCode || 0;
|
|
217
|
+
if (sc >= 300 && sc < 400 && res.headers && res.headers.location) {
|
|
218
|
+
const next = new URL(res.headers.location, sourceLabel).toString();
|
|
219
|
+
res.resume();
|
|
220
|
+
resolve(__openHttpTextStream(next, redirects + 1));
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
if (sc < 200 || sc >= 300) {
|
|
224
|
+
res.resume();
|
|
225
|
+
reject(new Error(`HTTP status ${sc}`));
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
const enc = String((res.headers && res.headers['content-encoding']) || '').toLowerCase();
|
|
230
|
+
let body = res;
|
|
231
|
+
if (enc.includes('gzip')) body = res.pipe(zlib.createGunzip());
|
|
232
|
+
else if (enc.includes('deflate')) body = res.pipe(zlib.createInflate());
|
|
233
|
+
else if (enc.includes('br')) body = res.pipe(zlib.createBrotliDecompress());
|
|
234
|
+
resolve(body);
|
|
235
|
+
},
|
|
236
|
+
);
|
|
237
|
+
req.on('error', reject);
|
|
238
|
+
req.end();
|
|
185
239
|
});
|
|
186
|
-
if (r.status !== 0) {
|
|
187
|
-
try { fs.rmSync(dir, { recursive: true, force: true }); } catch {}
|
|
188
|
-
throw new Error(`Failed to dereference ${sourceLabel}${r.stderr ? ': ' + String(r.stderr).trim() : ''}`);
|
|
189
|
-
}
|
|
190
|
-
return { file, cleanup: () => { try { fs.rmSync(dir, { recursive: true, force: true }); } catch {} } };
|
|
191
240
|
}
|
|
192
241
|
|
|
242
|
+
async function forEachLineInHttpSource(sourceLabel, onLine) {
|
|
243
|
+
const body = await __openHttpTextStream(sourceLabel);
|
|
244
|
+
await new Promise((resolve, reject) => {
|
|
245
|
+
let settled = false;
|
|
246
|
+
function done(err) {
|
|
247
|
+
if (settled) return;
|
|
248
|
+
settled = true;
|
|
249
|
+
if (err) reject(err);
|
|
250
|
+
else resolve();
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
const rl = readline.createInterface({ input: body, crlfDelay: Infinity });
|
|
254
|
+
rl.on('line', (line) => {
|
|
255
|
+
try {
|
|
256
|
+
onLine(line + '\n');
|
|
257
|
+
} catch (e) {
|
|
258
|
+
try { rl.close(); } catch {}
|
|
259
|
+
if (body && typeof body.destroy === 'function') {
|
|
260
|
+
try { body.destroy(); } catch {}
|
|
261
|
+
}
|
|
262
|
+
done(e);
|
|
263
|
+
}
|
|
264
|
+
});
|
|
265
|
+
rl.on('close', () => done());
|
|
266
|
+
rl.on('error', done);
|
|
267
|
+
body.on('error', done);
|
|
268
|
+
});
|
|
269
|
+
}
|
|
193
270
|
|
|
194
271
|
const RDF_MESSAGE_VERSION_RE = /^\s*(?:@version|VERSION)\s+(["'])(?:1\.1|1\.2|1\.2-basic)-messages\1\s*\.?\s*(?:#.*)?$/im;
|
|
195
272
|
const RDF_MESSAGE_VERSION_LINE_RE = /^\s*(?:@version|VERSION)\s+(["'])(?:1\.1|1\.2|1\.2-basic)-messages\1\s*\.?\s*(?:#.*)?$/i;
|
|
@@ -407,15 +484,50 @@ function __forEachRdfMessageChunkSync(sourceLabel, onMessage) {
|
|
|
407
484
|
return;
|
|
408
485
|
}
|
|
409
486
|
if (__isHttpSource(sourceLabel)) {
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
487
|
+
throw new Error('internal error: HTTP RDF Message Logs must be streamed asynchronously');
|
|
488
|
+
}
|
|
489
|
+
forEachRdfMessageChunkInText(__readInputSourceSync(sourceLabel), onMessage);
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
|
|
493
|
+
async function forEachRdfMessageChunkInHttpSource(sourceLabel, onMessage) {
|
|
494
|
+
const directives = [];
|
|
495
|
+
const seenDirectives = new Set();
|
|
496
|
+
let chunk = '';
|
|
497
|
+
let messageIndex = 1;
|
|
498
|
+
let sawVersion = false;
|
|
499
|
+
let sawDelimiter = false;
|
|
500
|
+
|
|
501
|
+
function emit() {
|
|
502
|
+
onMessage({ messageIndex, chunk, directives: directives.slice() });
|
|
503
|
+
messageIndex += 1;
|
|
504
|
+
chunk = '';
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
await forEachLineInHttpSource(sourceLabel, (line) => {
|
|
508
|
+
if (RDF_MESSAGE_VERSION_LINE_RE.test(line)) {
|
|
509
|
+
sawVersion = true;
|
|
510
|
+
return;
|
|
511
|
+
}
|
|
512
|
+
if (RDF_MESSAGE_DELIMITER_LINE_RE.test(line)) {
|
|
513
|
+
emit();
|
|
514
|
+
sawDelimiter = true;
|
|
515
|
+
return;
|
|
415
516
|
}
|
|
517
|
+
addRdfDirective(directives, seenDirectives, line);
|
|
518
|
+
chunk += line;
|
|
519
|
+
});
|
|
520
|
+
|
|
521
|
+
if (!sawVersion) throw new Error('not an RDF Message Log: missing VERSION "*-messages" directive');
|
|
522
|
+
if (sawDelimiter || hasRdfPayload(chunk)) emit();
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
async function __forEachRdfMessageChunk(sourceLabel, onMessage) {
|
|
526
|
+
if (__isHttpSource(sourceLabel)) {
|
|
527
|
+
await forEachRdfMessageChunkInHttpSource(sourceLabel, onMessage);
|
|
416
528
|
return;
|
|
417
529
|
}
|
|
418
|
-
|
|
530
|
+
__forEachRdfMessageChunkSync(sourceLabel, onMessage);
|
|
419
531
|
}
|
|
420
532
|
|
|
421
533
|
function factsContainOutputStrings(triplesForOutput) {
|
|
@@ -481,7 +593,7 @@ function runParsedDocumentOnce(mergedDocument, { rdfMode = false, outputPrefixes
|
|
|
481
593
|
}
|
|
482
594
|
}
|
|
483
595
|
|
|
484
|
-
function runStreamMessagesMode(sourceLabels, { rdfMode }) {
|
|
596
|
+
async function runStreamMessagesMode(sourceLabels, { rdfMode }) {
|
|
485
597
|
const ordinarySourceLabels = [];
|
|
486
598
|
const messageSourceLabels = [];
|
|
487
599
|
|
|
@@ -533,7 +645,7 @@ function runStreamMessagesMode(sourceLabels, { rdfMode }) {
|
|
|
533
645
|
const fullIriPrefixes = new PrefixEnv({});
|
|
534
646
|
for (const messageSourceLabel of messageSourceLabels) {
|
|
535
647
|
try {
|
|
536
|
-
|
|
648
|
+
await __forEachRdfMessageChunk(messageSourceLabel, ({ messageIndex, chunk, directives }) => {
|
|
537
649
|
const messageText = buildSingleMessageReplayDocument({
|
|
538
650
|
sourceLabel: messageSourceLabel,
|
|
539
651
|
messageIndex,
|
|
@@ -567,7 +679,7 @@ function runStreamMessagesMode(sourceLabels, { rdfMode }) {
|
|
|
567
679
|
}
|
|
568
680
|
}
|
|
569
681
|
|
|
570
|
-
function main() {
|
|
682
|
+
async function main() {
|
|
571
683
|
// Drop "node" and script name; keep only user-provided args
|
|
572
684
|
// Expand combined short options: -pt == -p -t
|
|
573
685
|
const argvRaw = process.argv.slice(2);
|
|
@@ -706,7 +818,7 @@ function main() {
|
|
|
706
818
|
}
|
|
707
819
|
|
|
708
820
|
if (streamMessagesMode) {
|
|
709
|
-
runStreamMessagesMode(sourceLabels, { rdfMode });
|
|
821
|
+
await runStreamMessagesMode(sourceLabels, { rdfMode });
|
|
710
822
|
return;
|
|
711
823
|
}
|
|
712
824
|
|
|
@@ -790,7 +902,8 @@ function main() {
|
|
|
790
902
|
return false;
|
|
791
903
|
}
|
|
792
904
|
|
|
793
|
-
|
|
905
|
+
|
|
906
|
+
function factsContainOutputStrings(triplesForOutput) {
|
|
794
907
|
return (
|
|
795
908
|
Array.isArray(triplesForOutput) &&
|
|
796
909
|
triplesForOutput.some(
|
package/lib/engine.js
CHANGED
|
@@ -3832,7 +3832,15 @@ function reasonRdfJs(input, opts = {}) {
|
|
|
3832
3832
|
// Minimal export surface for Node + browser/worker
|
|
3833
3833
|
function main() {
|
|
3834
3834
|
// Lazily require to avoid hard cycles in the module graph.
|
|
3835
|
-
|
|
3835
|
+
const result = require('./cli').main();
|
|
3836
|
+
if (result && typeof result.then === 'function') {
|
|
3837
|
+
result.catch((e) => {
|
|
3838
|
+
const msg = e && e.stack ? e.stack : e && e.message ? e.message : String(e);
|
|
3839
|
+
console.error(msg);
|
|
3840
|
+
process.exit(1);
|
|
3841
|
+
});
|
|
3842
|
+
}
|
|
3843
|
+
return result;
|
|
3836
3844
|
}
|
|
3837
3845
|
|
|
3838
3846
|
// ---------------------------------------------------------------------------
|