@lde/sparql-qlever 0.15.4 → 0.15.6
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/dist/importer.d.ts.map +1 -1
- package/dist/importer.js +1 -14
- package/dist/preprocess.d.ts +4 -4
- package/dist/preprocess.d.ts.map +1 -1
- package/dist/preprocess.js +29 -13
- package/package.json +5 -5
package/dist/importer.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"importer.d.ts","sourceRoot":"","sources":["../src/importer.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,QAAQ,IAAI,iBAAiB,EAC7B,eAAe,EACf,YAAY,EACZ,gBAAgB,EAChB,YAAY,EACb,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAyB,YAAY,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"importer.d.ts","sourceRoot":"","sources":["../src/importer.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,QAAQ,IAAI,iBAAiB,EAC7B,eAAe,EACf,YAAY,EACZ,gBAAgB,EAChB,YAAY,EACb,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAyB,YAAY,EAAE,MAAM,cAAc,CAAC;AAOnE,MAAM,WAAW,kBAAkB;IACjC,oBAAoB;IACpB,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,yBAAyB;IACzB,uBAAuB,CAAC,EAAE,MAAM,CAAC;IACjC,uEAAuE;IACvE,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,oBAAoB;IACpB,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,yJAAyJ;IACzJ,+BAA+B,CAAC,EAAE,OAAO,CAAC;CAC3C;AAED,MAAM,WAAW,qBAAsB,SAAQ,eAAe;IAC5D,sBAAsB;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,kBAAkB,CAAC;CACpC;AAWD,qBAAa,QAAS,YAAW,iBAAiB;IAChD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAkB;gBAE9B,OAAO,EAAE,qBAAqB;IAa7B,MAAM,CACjB,aAAa,EAAE,YAAY,EAAE,GAC5B,OAAO,CAAC,YAAY,GAAG,gBAAgB,GAAG,YAAY,CAAC;YAqC5C,QAAQ;IA6DtB,OAAO,CAAC,gBAAgB;IAQxB,OAAO,CAAC,aAAa;IAIrB;;OAEG;YACW,eAAe;IA0B7B,yDAAyD;YAC3C,eAAe;YAaf,cAAc;YAKd,KAAK;CA8CpB"}
|
package/dist/importer.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { ImportFailed, ImportSuccessful, NotSupported, } from '@lde/sparql-importer';
|
|
2
2
|
import { compressionMediaTypes } from '@lde/dataset';
|
|
3
3
|
import { LastModifiedDownloader } from '@lde/distribution-downloader';
|
|
4
|
+
import { shellQuote } from '@lde/task-runner';
|
|
4
5
|
import { basename, dirname, join } from 'path';
|
|
5
6
|
import { readFile, stat, writeFile } from 'node:fs/promises';
|
|
6
7
|
import { needsPreprocessing, preprocess } from './preprocess.js';
|
|
@@ -176,20 +177,6 @@ export class Importer {
|
|
|
176
177
|
return await this.options.taskRunner.wait(indexTask);
|
|
177
178
|
}
|
|
178
179
|
}
|
|
179
|
-
/**
|
|
180
|
-
* POSIX-quote a value for safe interpolation into a shell command: wrap it in
|
|
181
|
-
* single quotes and escape any embedded single quote as `'\''`.
|
|
182
|
-
*
|
|
183
|
-
* Without this, a data filename containing an apostrophe – e.g. a dataset
|
|
184
|
-
* titled `'s-Hertogenbosch`, whose distribution URL maps to a local file like
|
|
185
|
-
* `…Erfgoed+'s-Hertogenbosch.nt` – would terminate the surrounding quotes, so
|
|
186
|
-
* `cat`/`gunzip` would read a non-existent path and feed `qlever-index` empty
|
|
187
|
-
* input. The index then "succeeds" with 0 triples, the import is treated as
|
|
188
|
-
* failed, and every distribution (and the JSON-LD fallback) fails the same way.
|
|
189
|
-
*/
|
|
190
|
-
function shellQuote(value) {
|
|
191
|
-
return `'${value.replace(/'/g, "'\\''")}'`;
|
|
192
|
-
}
|
|
193
180
|
/**
|
|
194
181
|
* Native QLever index formats – `qlever-index -F <flag>` consumes these
|
|
195
182
|
* directly. JSON-LD is not here: it is preprocessed to N-Quads first (see
|
package/dist/preprocess.d.ts
CHANGED
|
@@ -12,8 +12,8 @@ export interface PreprocessResult {
|
|
|
12
12
|
* JSON-LD, RDF/XML and TriG distributions return `true`: `qlever-index` cannot
|
|
13
13
|
* parse any of them, so we stream them through `rdf-parse` into N-Quads first.
|
|
14
14
|
*
|
|
15
|
-
* Native RDF formats (`nt`, `nq`, `ttl`)
|
|
16
|
-
* `application/gzip` or `application/zip`
|
|
15
|
+
* Native RDF formats (`nt`, `nq`, `ttl`) – including when wrapped in
|
|
16
|
+
* `application/gzip` or `application/zip` – go straight through the shell
|
|
17
17
|
* pipeline in `index()`, which uses `gunzip -c` or `unzip -p` as appropriate.
|
|
18
18
|
* Standalone `mediaType=application/zip` is rejected upstream: the inner
|
|
19
19
|
* format must be declared.
|
|
@@ -25,8 +25,8 @@ export declare function needsPreprocessing(distribution: Distribution): boolean;
|
|
|
25
25
|
*
|
|
26
26
|
* Streams the source through `rdf-parse` → `rdf-serialize` so memory use
|
|
27
27
|
* stays bounded regardless of input size. Handles gzip transparently
|
|
28
|
-
* (
|
|
29
|
-
* every parseable entry into the output stream in order).
|
|
28
|
+
* (detected from the file’s magic bytes, not from declared metadata) and zip
|
|
29
|
+
* containers (folds every parseable entry into the output stream in order).
|
|
30
30
|
*
|
|
31
31
|
* Cached: if the output is newer than the input, it is reused as-is.
|
|
32
32
|
*/
|
package/dist/preprocess.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"preprocess.d.ts","sourceRoot":"","sources":["../src/preprocess.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"preprocess.d.ts","sourceRoot":"","sources":["../src/preprocess.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AA2B5C,MAAM,WAAW,gBAAgB;IAC/B,iEAAiE;IACjE,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,IAAI,CAAC;IACb,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,kBAAkB,CAAC,YAAY,EAAE,YAAY,GAAG,OAAO,CAKtE;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,UAAU,CAC9B,SAAS,EAAE,MAAM,EACjB,YAAY,EAAE,YAAY,GACzB,OAAO,CAAC,gBAAgB,CAAC,CAyB3B"}
|
package/dist/preprocess.js
CHANGED
|
@@ -2,7 +2,7 @@ import { rdfParser } from 'rdf-parse';
|
|
|
2
2
|
import { rdfSerializer } from 'rdf-serialize';
|
|
3
3
|
import { createGunzip } from 'node:zlib';
|
|
4
4
|
import { createReadStream, createWriteStream } from 'node:fs';
|
|
5
|
-
import { rm, stat } from 'node:fs/promises';
|
|
5
|
+
import { open, rm, stat } from 'node:fs/promises';
|
|
6
6
|
import { finished } from 'node:stream/promises';
|
|
7
7
|
import { promisify } from 'node:util';
|
|
8
8
|
import yauzl from 'yauzl';
|
|
@@ -10,8 +10,6 @@ const JSONLD_MIME = 'application/ld+json';
|
|
|
10
10
|
const RDFXML_MIME = 'application/rdf+xml';
|
|
11
11
|
const TRIG_MIME = 'application/trig';
|
|
12
12
|
const ZIP_MIME = 'application/zip';
|
|
13
|
-
const GZIP_MIME = 'application/gzip';
|
|
14
|
-
const GZIP_MIME_LEGACY = 'application/x-gzip';
|
|
15
13
|
/**
|
|
16
14
|
* RDF media types `qlever-index` cannot read natively, keyed by `rdf-parse`
|
|
17
15
|
* `contentType` with a label for warnings. TriG belongs here too: its
|
|
@@ -29,8 +27,8 @@ const preprocessFormats = new Map([
|
|
|
29
27
|
* JSON-LD, RDF/XML and TriG distributions return `true`: `qlever-index` cannot
|
|
30
28
|
* parse any of them, so we stream them through `rdf-parse` into N-Quads first.
|
|
31
29
|
*
|
|
32
|
-
* Native RDF formats (`nt`, `nq`, `ttl`)
|
|
33
|
-
* `application/gzip` or `application/zip`
|
|
30
|
+
* Native RDF formats (`nt`, `nq`, `ttl`) – including when wrapped in
|
|
31
|
+
* `application/gzip` or `application/zip` – go straight through the shell
|
|
34
32
|
* pipeline in `index()`, which uses `gunzip -c` or `unzip -p` as appropriate.
|
|
35
33
|
* Standalone `mediaType=application/zip` is rejected upstream: the inner
|
|
36
34
|
* format must be declared.
|
|
@@ -45,8 +43,8 @@ export function needsPreprocessing(distribution) {
|
|
|
45
43
|
*
|
|
46
44
|
* Streams the source through `rdf-parse` → `rdf-serialize` so memory use
|
|
47
45
|
* stays bounded regardless of input size. Handles gzip transparently
|
|
48
|
-
* (
|
|
49
|
-
* every parseable entry into the output stream in order).
|
|
46
|
+
* (detected from the file’s magic bytes, not from declared metadata) and zip
|
|
47
|
+
* containers (folds every parseable entry into the output stream in order).
|
|
50
48
|
*
|
|
51
49
|
* Cached: if the output is newer than the input, it is reused as-is.
|
|
52
50
|
*/
|
|
@@ -66,7 +64,7 @@ export async function preprocess(localFile, distribution) {
|
|
|
66
64
|
await streamRdfZip(localFile, outputFile, contentType, label, warnings);
|
|
67
65
|
}
|
|
68
66
|
else {
|
|
69
|
-
await streamRdfFile(localFile, outputFile, contentType
|
|
67
|
+
await streamRdfFile(localFile, outputFile, contentType);
|
|
70
68
|
}
|
|
71
69
|
return { path: outputFile, format: 'nq', warnings };
|
|
72
70
|
}
|
|
@@ -102,12 +100,11 @@ async function closeWritable(output) {
|
|
|
102
100
|
output.end();
|
|
103
101
|
});
|
|
104
102
|
}
|
|
105
|
-
async function streamRdfFile(localFile, outputFile, contentType
|
|
106
|
-
const isGzipped = distribution.compressMimeType === GZIP_MIME ||
|
|
107
|
-
distribution.compressMimeType === GZIP_MIME_LEGACY ||
|
|
108
|
-
localFile.toLowerCase().endsWith('.gz');
|
|
103
|
+
async function streamRdfFile(localFile, outputFile, contentType) {
|
|
109
104
|
const source = createReadStream(localFile);
|
|
110
|
-
const input =
|
|
105
|
+
const input = (await isGzipFile(localFile))
|
|
106
|
+
? source.pipe(createGunzip())
|
|
107
|
+
: source;
|
|
111
108
|
const output = createWriteStream(outputFile);
|
|
112
109
|
try {
|
|
113
110
|
await pipeRdfToWritable(input, output, contentType);
|
|
@@ -116,6 +113,25 @@ async function streamRdfFile(localFile, outputFile, contentType, distribution) {
|
|
|
116
113
|
await closeWritable(output);
|
|
117
114
|
}
|
|
118
115
|
}
|
|
116
|
+
/**
|
|
117
|
+
* Whether the file begins with the gzip magic number (RFC 1952 §2.3.1).
|
|
118
|
+
*
|
|
119
|
+
* Sniffing the bytes is the only reliable signal for a downloaded dump.
|
|
120
|
+
* `dcat:compressFormat` is frequently absent, and the local filename is derived
|
|
121
|
+
* from the access URL, so a dump behind a query string (`…/download.trig.gz?graph=…`)
|
|
122
|
+
* carries no `.gz` suffix. Conversely, a `.gz` name says nothing once the server
|
|
123
|
+
* declared a real `Content-Encoding: gzip` and `fetch` already inflated the body.
|
|
124
|
+
*/
|
|
125
|
+
async function isGzipFile(path) {
|
|
126
|
+
const file = await open(path);
|
|
127
|
+
try {
|
|
128
|
+
const { buffer, bytesRead } = await file.read(Buffer.alloc(2), 0, 2, 0);
|
|
129
|
+
return bytesRead === 2 && buffer[0] === 0x1f && buffer[1] === 0x8b;
|
|
130
|
+
}
|
|
131
|
+
finally {
|
|
132
|
+
await file.close();
|
|
133
|
+
}
|
|
134
|
+
}
|
|
119
135
|
const openZip = promisify(yauzl.open);
|
|
120
136
|
/**
|
|
121
137
|
* Fold every parseable entry of a zip into the N-Quads output, in order. The
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lde/sparql-qlever",
|
|
3
|
-
"version": "0.15.
|
|
3
|
+
"version": "0.15.6",
|
|
4
4
|
"repository": {
|
|
5
5
|
"url": "git+https://github.com/ldelements/lde.git",
|
|
6
6
|
"directory": "packages/sparql-qlever"
|
|
@@ -26,11 +26,11 @@
|
|
|
26
26
|
"dependencies": {
|
|
27
27
|
"@lde/dataset": "^0.7.9",
|
|
28
28
|
"@lde/distribution-downloader": "^0.6.8",
|
|
29
|
-
"@lde/sparql-importer": "^0.6.
|
|
29
|
+
"@lde/sparql-importer": "^0.6.10",
|
|
30
30
|
"@lde/sparql-server": "^0.4.14",
|
|
31
|
-
"@lde/task-runner": "^0.2.
|
|
32
|
-
"@lde/task-runner-docker": "^0.2.
|
|
33
|
-
"@lde/task-runner-native": "^0.2.
|
|
31
|
+
"@lde/task-runner": "^0.2.14",
|
|
32
|
+
"@lde/task-runner-docker": "^0.2.17",
|
|
33
|
+
"@lde/task-runner-native": "^0.2.17",
|
|
34
34
|
"@lde/wait-for-sparql": "^0.2.17",
|
|
35
35
|
"rdf-parse": "^5.0.0",
|
|
36
36
|
"rdf-serialize": "^5.1.0",
|