@lde/sparql-qlever 0.18.1 → 0.18.3

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.
@@ -1,5 +1,5 @@
1
1
  import { Downloader } from '@lde/distribution-downloader';
2
- import { Importer, QleverIndexOptions } from './importer.js';
2
+ import { Importer, QleverImporterOptions, QleverIndexOptions } from './importer.js';
3
3
  import { QleverServerOptions, Server } from './server.js';
4
4
  export type QleverOptions = {
5
5
  /** Directory where downloaded data files are stored. */
@@ -12,6 +12,8 @@ export type QleverOptions = {
12
12
  cacheIndex?: boolean;
13
13
  /** Options for `qlever-index` (index building). */
14
14
  indexOptions?: QleverIndexOptions;
15
+ /** See {@link QleverImporterOptions.replaceJsonLdContext}. */
16
+ replaceJsonLdContext?: QleverImporterOptions['replaceJsonLdContext'];
15
17
  /** Options for `qlever-server` (query processing). */
16
18
  serverOptions?: QleverServerOptions;
17
19
  } & ({
@@ -1 +1 @@
1
- {"version":3,"file":"createQlever.d.ts","sourceRoot":"","sources":["../src/createQlever.ts"],"names":[],"mappings":"AAIA,OAAO,EACL,UAAU,EAEX,MAAM,8BAA8B,CAAC;AACtC,OAAO,EAAE,QAAQ,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAC7D,OAAO,EAAE,mBAAmB,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAE1D,MAAM,MAAM,aAAa,GAAG;IAC1B,wDAAwD;IACxD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,oBAAoB;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,6FAA6F;IAC7F,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,mDAAmD;IACnD,YAAY,CAAC,EAAE,kBAAkB,CAAC;IAClC,sDAAsD;IACtD,aAAa,CAAC,EAAE,mBAAmB,CAAC;CACrC,GAAG,CACA;IACE,IAAI,EAAE,QAAQ,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,OAAO,CAAC,EAAE,KAAK,CAAC;CACjB,GACD;IACE,IAAI,EAAE,QAAQ,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd;;;;;;;;OAQG;IACH,OAAO,EAAE,MAAM,CAAC;IAChB,yEAAyE;IACzE,aAAa,EAAE,MAAM,CAAC;CACvB,GACD;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,GAClB;IACE;;;;;OAKG;IACH,IAAI,EAAE,QAAQ,CAAC;IACf,sDAAsD;IACtD,OAAO,EAAE,MAAM,CAAC;CACjB,CACJ,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,aAAa;;;EAsClD"}
1
+ {"version":3,"file":"createQlever.d.ts","sourceRoot":"","sources":["../src/createQlever.ts"],"names":[],"mappings":"AAIA,OAAO,EACL,UAAU,EAEX,MAAM,8BAA8B,CAAC;AACtC,OAAO,EACL,QAAQ,EACR,qBAAqB,EACrB,kBAAkB,EACnB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,mBAAmB,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAE1D,MAAM,MAAM,aAAa,GAAG;IAC1B,wDAAwD;IACxD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,oBAAoB;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,6FAA6F;IAC7F,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,mDAAmD;IACnD,YAAY,CAAC,EAAE,kBAAkB,CAAC;IAClC,8DAA8D;IAC9D,oBAAoB,CAAC,EAAE,qBAAqB,CAAC,sBAAsB,CAAC,CAAC;IACrE,sDAAsD;IACtD,aAAa,CAAC,EAAE,mBAAmB,CAAC;CACrC,GAAG,CACA;IACE,IAAI,EAAE,QAAQ,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,OAAO,CAAC,EAAE,KAAK,CAAC;CACjB,GACD;IACE,IAAI,EAAE,QAAQ,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd;;;;;;;;OAQG;IACH,OAAO,EAAE,MAAM,CAAC;IAChB,yEAAyE;IACzE,aAAa,EAAE,MAAM,CAAC;CACvB,GACD;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,GAClB;IACE;;;;;OAKG;IACH,IAAI,EAAE,QAAQ,CAAC;IACf,sDAAsD;IACtD,OAAO,EAAE,MAAM,CAAC;CACjB,CACJ,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,aAAa;;;EAuClD"}
@@ -2,7 +2,7 @@ import { DockerTaskRunner } from '@lde/task-runner-docker';
2
2
  import { HttpTaskRunner } from '@lde/task-runner-http';
3
3
  import { NativeTaskRunner } from '@lde/task-runner-native';
4
4
  import { LastModifiedDownloader, } from '@lde/distribution-downloader';
5
- import { Importer } from './importer.js';
5
+ import { Importer, } from './importer.js';
6
6
  import { Server } from './server.js';
7
7
  /**
8
8
  * Create a paired QLever {@link Importer} and {@link Server} that share a
@@ -35,6 +35,7 @@ export function createQlever(options) {
35
35
  downloader: options.downloader ?? new LastModifiedDownloader(options.dataDir),
36
36
  cacheIndex: options.cacheIndex,
37
37
  qleverOptions: options.indexOptions,
38
+ replaceJsonLdContext: options.replaceJsonLdContext,
38
39
  }),
39
40
  server: new Server({
40
41
  taskRunner,
@@ -16,6 +16,15 @@ export interface QleverImporterOptions extends ImporterOptions {
16
16
  /** @default 'data' */
17
17
  indexName?: string;
18
18
  qleverOptions?: QleverIndexOptions;
19
+ /**
20
+ * Replace remote JSON-LD contexts when converting a JSON-LD distribution.
21
+ * Called with the URL of each remote `@context`; return the parsed document
22
+ * to use instead (shaped like what the URL serves, so
23
+ * `{ '@context': { … } }`), or `undefined` to fetch it as usual. A matching
24
+ * function rather than a fixed list, because one context is often served
25
+ * from several URLs.
26
+ */
27
+ replaceJsonLdContext?: (url: string) => object | undefined;
19
28
  }
20
29
  export declare class Importer implements ImporterInterface {
21
30
  private readonly options;
@@ -1 +1 @@
1
- {"version":3,"file":"importer.d.ts","sourceRoot":"","sources":["../src/importer.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,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;IA0EtB,OAAO,CAAC,gBAAgB;IAQxB;gEAC4D;IAC5D,OAAO,CAAC,WAAW;IAKnB,OAAO,CAAC,aAAa;IAIrB;;OAEG;YACW,eAAe;IA0B7B,0EAA0E;YAC5D,YAAY;YAYZ,cAAc;YAKd,KAAK;CA8CpB"}
1
+ {"version":3,"file":"importer.d.ts","sourceRoot":"","sources":["../src/importer.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,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;IACnC;;;;;;;OAOG;IACH,oBAAoB,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC;CAC5D;AAcD,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;IA8EtB,OAAO,CAAC,gBAAgB;IAQxB;gEAC4D;IAC5D,OAAO,CAAC,WAAW;IAKnB,OAAO,CAAC,aAAa;IAIrB;;OAEG;YACW,eAAe;IA0B7B,0EAA0E;YAC5D,YAAY;YAYZ,cAAc;YAKd,KAAK;CA8CpB"}
package/dist/importer.js CHANGED
@@ -63,7 +63,7 @@ export class Importer {
63
63
  let format;
64
64
  const warnings = [];
65
65
  if (needsPreprocessing(distribution)) {
66
- const result = await preprocess(localFile, distribution);
66
+ const result = await preprocess(localFile, distribution, this.options.replaceJsonLdContext);
67
67
  indexFile = result.path;
68
68
  format = result.format;
69
69
  warnings.push(...result.warnings);
@@ -5,6 +5,11 @@ export interface PreprocessResult {
5
5
  format: 'nq';
6
6
  warnings: string[];
7
7
  }
8
+ /**
9
+ * Called with the URL of each remote JSON-LD `@context`; returns the document
10
+ * to use instead, or `undefined` to fetch it.
11
+ */
12
+ type ReplaceJsonLdContext = (url: string) => object | undefined;
8
13
  /**
9
14
  * Whether a distribution needs Node-side preprocessing before `qlever-index`
10
15
  * can read it.
@@ -28,7 +33,12 @@ export declare function needsPreprocessing(distribution: Distribution): boolean;
28
33
  * (detected from the file’s magic bytes, not from declared metadata) and zip
29
34
  * containers (folds every parseable entry into the output stream in order).
30
35
  *
31
- * Cached: if the output is newer than the input, it is reused as-is.
36
+ * `replaceJsonLdContext` answers remote JSON-LD contexts locally; see
37
+ * {@link ReplaceJsonLdContext}.
38
+ *
39
+ * Cached: if the output is newer than the input, it is reused as-is – also
40
+ * when it was converted without the current `replaceJsonLdContext`.
32
41
  */
33
- export declare function preprocess(localFile: string, distribution: Distribution): Promise<PreprocessResult>;
42
+ export declare function preprocess(localFile: string, distribution: Distribution, replaceJsonLdContext?: ReplaceJsonLdContext): Promise<PreprocessResult>;
43
+ export {};
34
44
  //# sourceMappingURL=preprocess.d.ts.map
@@ -1 +1 @@
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"}
1
+ {"version":3,"file":"preprocess.d.ts","sourceRoot":"","sources":["../src/preprocess.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AA+B5C,MAAM,WAAW,gBAAgB;IAC/B,iEAAiE;IACjE,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,IAAI,CAAC;IACb,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED;;;GAGG;AACH,KAAK,oBAAoB,GAAG,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC;AAEhE;;;;;;;;;;;;GAYG;AACH,wBAAgB,kBAAkB,CAAC,YAAY,EAAE,YAAY,GAAG,OAAO,CAKtE;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,UAAU,CAC9B,SAAS,EAAE,MAAM,EACjB,YAAY,EAAE,YAAY,EAC1B,oBAAoB,CAAC,EAAE,oBAAoB,GAC1C,OAAO,CAAC,gBAAgB,CAAC,CAqC3B"}
@@ -1,4 +1,5 @@
1
1
  import { rdfParser } from 'rdf-parse';
2
+ import { FetchDocumentLoader, } from 'jsonld-context-parser';
2
3
  import { rdfSerializer } from 'rdf-serialize';
3
4
  import { createGunzip } from 'node:zlib';
4
5
  import { createReadStream, createWriteStream } from 'node:fs';
@@ -46,9 +47,13 @@ export function needsPreprocessing(distribution) {
46
47
  * (detected from the file’s magic bytes, not from declared metadata) and zip
47
48
  * containers (folds every parseable entry into the output stream in order).
48
49
  *
49
- * Cached: if the output is newer than the input, it is reused as-is.
50
+ * `replaceJsonLdContext` answers remote JSON-LD contexts locally; see
51
+ * {@link ReplaceJsonLdContext}.
52
+ *
53
+ * Cached: if the output is newer than the input, it is reused as-is – also
54
+ * when it was converted without the current `replaceJsonLdContext`.
50
55
  */
51
- export async function preprocess(localFile, distribution) {
56
+ export async function preprocess(localFile, distribution, replaceJsonLdContext) {
52
57
  const contentType = distribution.mimeType;
53
58
  const label = contentType === undefined ? undefined : preprocessFormats.get(contentType);
54
59
  if (contentType === undefined || label === undefined) {
@@ -61,10 +66,10 @@ export async function preprocess(localFile, distribution) {
61
66
  await rm(outputFile, { force: true });
62
67
  const warnings = [];
63
68
  if (distribution.compressMimeType === ZIP_MIME) {
64
- await streamRdfZip(localFile, outputFile, contentType, label, warnings);
69
+ await streamRdfZip(localFile, outputFile, contentType, label, warnings, replaceJsonLdContext);
65
70
  }
66
71
  else {
67
- await streamRdfFile(localFile, outputFile, contentType);
72
+ await streamRdfFile(localFile, outputFile, contentType, replaceJsonLdContext);
68
73
  }
69
74
  return { path: outputFile, format: 'nq', warnings };
70
75
  }
@@ -85,14 +90,34 @@ async function outputIsUpToDate(inputFile, outputFile) {
85
90
  * open writable, without closing it. Back-pressure is handled by Node's
86
91
  * built-in `.pipe()`; the caller manages `output`'s lifecycle.
87
92
  */
88
- async function pipeRdfToWritable(input, output, contentType) {
89
- const quads = rdfParser.parse(input, { contentType });
93
+ async function pipeRdfToWritable(input, output, contentType, replaceJsonLdContext) {
94
+ const quads = rdfParser.parse(input, {
95
+ contentType,
96
+ ...jsonLdParseContext(replaceJsonLdContext),
97
+ });
90
98
  const bytes = rdfSerializer.serialize(quads, {
91
99
  contentType: 'application/n-quads',
92
100
  });
93
101
  bytes.pipe(output, { end: false });
94
102
  await finished(bytes);
95
103
  }
104
+ /**
105
+ * `rdf-parse` context that answers replaced JSON-LD contexts locally and
106
+ * fetches the rest. rdf-parse only routes context requests through a document
107
+ * loader set under this Comunica context key. Empty without a replacement, so
108
+ * the parser keeps its own loader.
109
+ */
110
+ function jsonLdParseContext(replaceJsonLdContext) {
111
+ if (replaceJsonLdContext === undefined)
112
+ return {};
113
+ const fetchLoader = new FetchDocumentLoader(fetch);
114
+ return {
115
+ '@comunica/actor-rdf-parse-jsonld:documentLoader': {
116
+ load: async (url) => replaceJsonLdContext(url) ??
117
+ fetchLoader.load(url),
118
+ },
119
+ };
120
+ }
96
121
  async function closeWritable(output) {
97
122
  await new Promise((resolve, reject) => {
98
123
  output.once('close', resolve);
@@ -100,14 +125,14 @@ async function closeWritable(output) {
100
125
  output.end();
101
126
  });
102
127
  }
103
- async function streamRdfFile(localFile, outputFile, contentType) {
128
+ async function streamRdfFile(localFile, outputFile, contentType, replaceJsonLdContext) {
104
129
  const source = createReadStream(localFile);
105
130
  const input = (await isGzipFile(localFile))
106
131
  ? source.pipe(createGunzip())
107
132
  : source;
108
133
  const output = createWriteStream(outputFile);
109
134
  try {
110
- await pipeRdfToWritable(input, output, contentType);
135
+ await pipeRdfToWritable(input, output, contentType, replaceJsonLdContext);
111
136
  }
112
137
  finally {
113
138
  await closeWritable(output);
@@ -138,7 +163,7 @@ const openZip = promisify(yauzl.open);
138
163
  * declared `contentType` drives the parser; an entry that fails to parse (a
139
164
  * sidecar, OS metadata) is skipped with a warning. Throws if nothing parses.
140
165
  */
141
- async function streamRdfZip(zipFile, outputFile, contentType, label, warnings) {
166
+ async function streamRdfZip(zipFile, outputFile, contentType, label, warnings, replaceJsonLdContext) {
142
167
  const zip = await openZip(zipFile, { lazyEntries: true });
143
168
  const output = createWriteStream(outputFile);
144
169
  let entriesProcessed = 0;
@@ -155,7 +180,7 @@ async function streamRdfZip(zipFile, outputFile, contentType, label, warnings) {
155
180
  try {
156
181
  const stream = await openZipEntry(zip, entry);
157
182
  try {
158
- await pipeRdfToWritable(stream, output, contentType);
183
+ await pipeRdfToWritable(stream, output, contentType, replaceJsonLdContext);
159
184
  entriesProcessed++;
160
185
  }
161
186
  finally {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lde/sparql-qlever",
3
- "version": "0.18.1",
3
+ "version": "0.18.3",
4
4
  "repository": {
5
5
  "url": "git+https://github.com/ldelements/lde.git",
6
6
  "directory": "packages/sparql-qlever"
@@ -32,7 +32,8 @@
32
32
  "@lde/task-runner-docker": "^0.4.0",
33
33
  "@lde/task-runner-http": "^0.1.0",
34
34
  "@lde/task-runner-native": "^0.3.0",
35
- "@lde/wait-for-sparql": "^0.3.0",
35
+ "@lde/wait-for-sparql": "^0.3.1",
36
+ "jsonld-context-parser": "^3.1.0",
36
37
  "rdf-parse": "^5.0.0",
37
38
  "rdf-serialize": "^5.1.0",
38
39
  "tslib": "^2.3.0",