@lde/distribution-probe 0.3.0 → 0.3.1
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/probe.d.ts +13 -3
- package/dist/probe.d.ts.map +1 -1
- package/dist/probe.js +50 -27
- package/package.json +2 -1
package/dist/probe.d.ts
CHANGED
|
@@ -34,7 +34,7 @@ export interface ProbeOptions {
|
|
|
34
34
|
* carries at least one triple. When `false` (the default) a data dump is only
|
|
35
35
|
* checked for reachability (a `HEAD`, with a body-less `GET` fallback if `HEAD`
|
|
36
36
|
* is unsupported) and its body is never read. When `true`, every declared-RDF
|
|
37
|
-
* dump
|
|
37
|
+
* dump – regardless of size – is fetched and validated; non-RDF and
|
|
38
38
|
* undeclared-type distributions are still reachability-only. Validation is
|
|
39
39
|
* opt-in because reading a body forces a slow, generate-on-the-fly endpoint to
|
|
40
40
|
* start producing its export, which a `HEAD` does not.
|
|
@@ -47,10 +47,20 @@ export interface ProbeOptions {
|
|
|
47
47
|
* the distribution is reported reachable but unvalidated (no `failureReason`),
|
|
48
48
|
* never failed. This bounds the extra latency content validation adds on slow,
|
|
49
49
|
* generate-on-the-fly endpoints. Clamped to {@link timeoutMs} (a longer budget
|
|
50
|
-
* is meaningless
|
|
50
|
+
* is meaningless – the request times out first). Defaults to
|
|
51
51
|
* `min(timeoutMs, 2000)`.
|
|
52
52
|
*/
|
|
53
53
|
rdfValidationBudgetMs?: number;
|
|
54
|
+
/**
|
|
55
|
+
* Replace remote JSON-LD contexts when {@link validateRdfContent} parses a
|
|
56
|
+
* JSON-LD body. Called with the URL of each remote `@context`; return the
|
|
57
|
+
* parsed document to use instead (shaped like what the URL serves, so
|
|
58
|
+
* `{ '@context': { … } }`), or `undefined` to fetch it as usual. A matching
|
|
59
|
+
* function rather than a fixed list, because one context is often served
|
|
60
|
+
* from several URLs. A context answered here never makes the result
|
|
61
|
+
* inconclusive for a slow or unreachable context host.
|
|
62
|
+
*/
|
|
63
|
+
replaceJsonLdContext?: (url: string) => object | undefined;
|
|
54
64
|
}
|
|
55
65
|
/**
|
|
56
66
|
* Options for {@link probeMany}: the per-probe {@link ProbeOptions} plus the
|
|
@@ -146,7 +156,7 @@ export declare function probe(distribution: Distribution, options?: ProbeOptions
|
|
|
146
156
|
*
|
|
147
157
|
* The per-host cap keeps the batch a polite client. Distributions sharing a host
|
|
148
158
|
* (by {@link Distribution.accessUrl}) contend for the same budget, so no single
|
|
149
|
-
* server is hit by the full global pool at once
|
|
159
|
+
* server is hit by the full global pool at once – the burst that trips a rate
|
|
150
160
|
* limiter (HTTP 429). When the next queued probe’s host is saturated it is
|
|
151
161
|
* skipped in favour of a later probe on a different host, so one busy host never
|
|
152
162
|
* idles the global pool (no head-of-line blocking).
|
package/dist/probe.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"probe.d.ts","sourceRoot":"","sources":["../src/probe.ts"],"names":[],"mappings":"AAKA,OAAO,EAAyB,YAAY,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"probe.d.ts","sourceRoot":"","sources":["../src/probe.ts"],"names":[],"mappings":"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"}
|
package/dist/probe.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { hostKeyOf, mapHostLimited, positiveIntOrDefault, } from '@lde/host-limiter';
|
|
2
2
|
import { compressionMediaTypes } from '@lde/dataset';
|
|
3
3
|
import { rdfParser } from 'rdf-parse';
|
|
4
|
+
import { FetchDocumentLoader, } from 'jsonld-context-parser';
|
|
4
5
|
import { Readable } from 'node:stream';
|
|
5
6
|
import { createGunzip } from 'node:zlib';
|
|
6
7
|
const DEFAULT_SPARQL_QUERY = 'SELECT * { ?s ?p ?o } LIMIT 1';
|
|
@@ -22,7 +23,7 @@ const VALIDATION_TIMED_OUT = Symbol('rdf-validation-timed-out');
|
|
|
22
23
|
* releases the connection. Reachability needs only that the endpoint answered
|
|
23
24
|
* with a success status and produced bytes; a large dump must never be
|
|
24
25
|
* downloaded in full within the probe's timeout budget. 256 KiB comfortably
|
|
25
|
-
* surfaces the first RDF triple
|
|
26
|
+
* surfaces the first RDF triple – the signal {@link validateBody} needs – while
|
|
26
27
|
* bounding the read regardless of the dump's true size, chunked transfer, or
|
|
27
28
|
* compression. Applied to both the raw read and, for a gzip body, the inflated
|
|
28
29
|
* output.
|
|
@@ -180,7 +181,7 @@ export async function probe(distribution, options) {
|
|
|
180
181
|
*
|
|
181
182
|
* The per-host cap keeps the batch a polite client. Distributions sharing a host
|
|
182
183
|
* (by {@link Distribution.accessUrl}) contend for the same budget, so no single
|
|
183
|
-
* server is hit by the full global pool at once
|
|
184
|
+
* server is hit by the full global pool at once – the burst that trips a rate
|
|
184
185
|
* limiter (HTTP 429). When the next queued probe’s host is saturated it is
|
|
185
186
|
* skipped in favour of a later probe on a different host, so one busy host never
|
|
186
187
|
* idles the global pool (no head-of-line blocking).
|
|
@@ -252,6 +253,7 @@ function resolveOptions(options) {
|
|
|
252
253
|
validateRdfContent: options?.validateRdfContent ?? false,
|
|
253
254
|
rdfValidationBudgetMs: options?.rdfValidationBudgetMs ??
|
|
254
255
|
Math.min(options?.timeoutMs ?? DEFAULT_TIMEOUT_MS, DEFAULT_RDF_VALIDATION_BUDGET_MS),
|
|
256
|
+
replaceJsonLdContext: options?.replaceJsonLdContext,
|
|
255
257
|
};
|
|
256
258
|
}
|
|
257
259
|
/**
|
|
@@ -396,8 +398,8 @@ async function probeDataDump(url, distribution, options, authHeaders, start) {
|
|
|
396
398
|
// Express a preference for the declared media type, but accept anything as a
|
|
397
399
|
// fallback. Servers that implement RFC 9110 §12.5.1 content negotiation will
|
|
398
400
|
// pick the declared type (preserving our ability to detect real Content-Type
|
|
399
|
-
// mismatches). Servers that reject any non-*/* Accept with 406
|
|
400
|
-
// Dataverse's /api/access/datafile/ endpoint (IQSS/dataverse#12410)
|
|
401
|
+
// mismatches). Servers that reject any non-*/* Accept with 406 – notably
|
|
402
|
+
// Dataverse's /api/access/datafile/ endpoint (IQSS/dataverse#12410) – fall
|
|
401
403
|
// back to */* and return the file unchanged.
|
|
402
404
|
const headers = new Headers({
|
|
403
405
|
Accept: distribution.mimeType
|
|
@@ -418,7 +420,7 @@ async function probeDataDump(url, distribution, options, authHeaders, start) {
|
|
|
418
420
|
});
|
|
419
421
|
// Validate body content only when asked to and the distribution declares an
|
|
420
422
|
// RDF media type; otherwise the probe is reachability-only and never reads a
|
|
421
|
-
// body
|
|
423
|
+
// body – which keeps it from forcing a slow, generate-on-the-fly endpoint to
|
|
422
424
|
// start producing its export.
|
|
423
425
|
if (options.validateRdfContent &&
|
|
424
426
|
isDeclaredRdf(distribution) &&
|
|
@@ -454,8 +456,8 @@ function finalizeDataDump(url, distribution, response, start, failureReason) {
|
|
|
454
456
|
/**
|
|
455
457
|
* GET the dump and validate that its body carries a triple, but only for as long
|
|
456
458
|
* as the validation budget allows. Reachability is already settled by the prior
|
|
457
|
-
* HEAD, so any shortfall
|
|
458
|
-
* a GET that cannot start
|
|
459
|
+
* HEAD, so any shortfall – a budget that elapses before a triple, a read error,
|
|
460
|
+
* a GET that cannot start – yields a `null` failureReason (reachable,
|
|
459
461
|
* unvalidated), never a failure. Returns the response to draw metadata from
|
|
460
462
|
* (the GET, or the HEAD when the GET could not start) alongside that reason.
|
|
461
463
|
*/
|
|
@@ -489,7 +491,7 @@ async function validateDumpBody(url, headers, options, headResponse) {
|
|
|
489
491
|
const { text, truncated, corrupt } = await decodeProbeBody(bounded);
|
|
490
492
|
return corrupt
|
|
491
493
|
? 'Distribution is not valid gzip'
|
|
492
|
-
: await validateBody(text, getResponse.headers.get('Content-Type'), url, budgetController.signal, truncated);
|
|
494
|
+
: await validateBody(text, getResponse.headers.get('Content-Type'), url, budgetController.signal, truncated, options.replaceJsonLdContext);
|
|
493
495
|
})().catch(() => null);
|
|
494
496
|
let budgetTimer;
|
|
495
497
|
const budgetExpiry = new Promise((resolve) => {
|
|
@@ -513,7 +515,7 @@ async function validateDumpBody(url, headers, options, headResponse) {
|
|
|
513
515
|
* Read at most `maxBytes` from a response body, then cancel the stream to free
|
|
514
516
|
* the underlying connection. Returns the bytes read and whether the body was
|
|
515
517
|
* longer than the cap (`truncated`), so the caller can tell a complete, small
|
|
516
|
-
* body
|
|
518
|
+
* body – whose emptiness or parse errors are meaningful – from a deliberately
|
|
517
519
|
* cut-off prefix of a large one, where only the presence of content is
|
|
518
520
|
* conclusive. This is what keeps the probe from downloading a multi-hundred-MB
|
|
519
521
|
* streamed dump in full just to confirm it is reachable.
|
|
@@ -527,7 +529,7 @@ async function readBoundedBody(response, maxBytes) {
|
|
|
527
529
|
let total = 0;
|
|
528
530
|
let truncated = false;
|
|
529
531
|
// Breaking out of `for await` cancels the stream, which stops any further
|
|
530
|
-
// download and releases the underlying connection
|
|
532
|
+
// download and releases the underlying connection – so a large dump is never
|
|
531
533
|
// pulled in full once we have the prefix we need.
|
|
532
534
|
for await (const chunk of stream) {
|
|
533
535
|
chunks.push(chunk);
|
|
@@ -541,12 +543,12 @@ async function readBoundedBody(response, maxBytes) {
|
|
|
541
543
|
}
|
|
542
544
|
/**
|
|
543
545
|
* Decode a bounded body to text for RDF validation, inflating it first when it
|
|
544
|
-
* is a gzip stream that `fetch` did not transparently decompress
|
|
546
|
+
* is a gzip stream that `fetch` did not transparently decompress – e.g. a `.gz`
|
|
545
547
|
* data dump served as-is, or one labelled with a non-standard Content-Encoding
|
|
546
548
|
* (`application/gzip`) that undici does not recognise as a content coding.
|
|
547
549
|
* Detection is by the gzip magic on the delivered bytes, so a body that `fetch`
|
|
548
550
|
* already inflated (a standard `Content-Encoding: gzip`) is passed through
|
|
549
|
-
* untouched. A truncated gzip tail is expected
|
|
551
|
+
* untouched. A truncated gzip tail is expected – we only read a prefix – and
|
|
550
552
|
* inflates cleanly up to the cut, so it is never mistaken for corruption.
|
|
551
553
|
*/
|
|
552
554
|
async function decodeProbeBody(bounded) {
|
|
@@ -594,7 +596,7 @@ function gunzipPrefix(bytes, maxBytes, inputComplete) {
|
|
|
594
596
|
let total = 0;
|
|
595
597
|
// `resolve` and `destroy` are both idempotent, so the first outcome wins and
|
|
596
598
|
// any later event (e.g. a premature-close error emitted by `destroy`) is a
|
|
597
|
-
// harmless no-op
|
|
599
|
+
// harmless no-op – no `settled` guard needed.
|
|
598
600
|
function finish(outcome) {
|
|
599
601
|
gunzip.destroy();
|
|
600
602
|
resolve({ bytes: Buffer.concat(chunks), ...outcome });
|
|
@@ -612,8 +614,8 @@ function gunzipPrefix(bytes, maxBytes, inputComplete) {
|
|
|
612
614
|
});
|
|
613
615
|
}
|
|
614
616
|
// The RDF serializations whose bodies we parse to confirm they carry triples. A
|
|
615
|
-
// non-empty body in one of these formats that yields zero triples
|
|
616
|
-
// graph such as a JSON-LD `{}`, an `<rdf:RDF/>`, or prefix-only Turtle
|
|
617
|
+
// non-empty body in one of these formats that yields zero triples – an empty
|
|
618
|
+
// graph such as a JSON-LD `{}`, an `<rdf:RDF/>`, or prefix-only Turtle – is a
|
|
617
619
|
// faulty distribution, not a usable one, so it must be caught here. Other
|
|
618
620
|
// content types (CSV, HTML, …) are left untouched: the probe is not the place
|
|
619
621
|
// to assert what a non-RDF body should contain.
|
|
@@ -630,20 +632,20 @@ const rdfContentTypes = [
|
|
|
630
632
|
// The line/statement-oriented formats (N-Triples, N-Quads, Turtle, TriG, N3) and
|
|
631
633
|
// SAX-based RDF/XML all yield their first triple from the opening chunk, but
|
|
632
634
|
// JSON-LD is a single JSON value whose parser emits nothing until the whole
|
|
633
|
-
// document closes
|
|
635
|
+
// document closes – a truncated JSON-LD body parses to an ‘unclosed document’
|
|
634
636
|
// error, never a triple. So a truncated body in one of these can only be
|
|
635
637
|
// validated if it happened to fit the read cap in full; beyond that it is
|
|
636
638
|
// inconclusive, and we must not download it in full to find out.
|
|
637
639
|
const nonStreamableRdfContentTypes = ['application/ld+json'];
|
|
638
|
-
async function validateBody(body, contentType, baseIRI, budgetSignal, truncated) {
|
|
640
|
+
async function validateBody(body, contentType, baseIRI, budgetSignal, truncated, replaceJsonLdContext) {
|
|
639
641
|
if (body.length === 0) {
|
|
640
642
|
// A complete, empty body is a faulty distribution; an empty *prefix* (a
|
|
641
643
|
// truncated read that yielded no bytes, e.g. a corrupt gzip header) is
|
|
642
|
-
// inconclusive
|
|
644
|
+
// inconclusive – the endpoint answered, we just could not validate content.
|
|
643
645
|
return truncated ? null : 'Distribution is empty';
|
|
644
646
|
}
|
|
645
647
|
// Media types are case-insensitive (RFC 9110 §8.3.1), so normalise before
|
|
646
|
-
// matching the lower-case allow-list
|
|
648
|
+
// matching the lower-case allow-list – a server sending `Application/LD+JSON`
|
|
647
649
|
// must still have its body validated.
|
|
648
650
|
const serialization = contentType?.split(';')[0].trim().toLowerCase();
|
|
649
651
|
if (!serialization || !rdfContentTypes.includes(serialization)) {
|
|
@@ -651,19 +653,19 @@ async function validateBody(body, contentType, baseIRI, budgetSignal, truncated)
|
|
|
651
653
|
}
|
|
652
654
|
if (truncated && nonStreamableRdfContentTypes.includes(serialization)) {
|
|
653
655
|
// A bounded prefix of a non-streamable serialization (JSON-LD) can never
|
|
654
|
-
// yield a triple, so skip the doomed parse and report it inconclusive
|
|
656
|
+
// yield a triple, so skip the doomed parse and report it inconclusive – only
|
|
655
657
|
// a complete document, small enough to fit the read cap, can be validated.
|
|
656
658
|
return null;
|
|
657
659
|
}
|
|
658
|
-
const outcome = await classifyRdfBody(body, serialization, baseIRI, budgetSignal, truncated);
|
|
660
|
+
const outcome = await classifyRdfBody(body, serialization, baseIRI, budgetSignal, truncated, replaceJsonLdContext);
|
|
659
661
|
switch (outcome.type) {
|
|
660
662
|
case 'empty':
|
|
661
663
|
return 'Distribution contains no RDF triples';
|
|
662
664
|
case 'parseError':
|
|
663
665
|
return outcome.message;
|
|
664
666
|
// 'hasTriples' proves content. 'inconclusive' means the parse timed out or a
|
|
665
|
-
// remote JSON-LD @context could not be loaded
|
|
666
|
-
// evidence the distribution is faulty
|
|
667
|
+
// remote JSON-LD @context could not be loaded – a third-party hiccup, not
|
|
668
|
+
// evidence the distribution is faulty – so neither is reported as a failure.
|
|
667
669
|
default:
|
|
668
670
|
return null;
|
|
669
671
|
}
|
|
@@ -680,18 +682,21 @@ async function validateBody(body, contentType, baseIRI, budgetSignal, truncated)
|
|
|
680
682
|
* host’s failure. Sharing the caller’s single budget (rather than starting a
|
|
681
683
|
* second, identical timer) keeps that timeout deterministic: it fires once, when
|
|
682
684
|
* the budget elapses, instead of leaving an orphan timer to settle on its own
|
|
683
|
-
* schedule. `baseIRI` resolves any relative IRIs in the document.
|
|
685
|
+
* schedule. `baseIRI` resolves any relative IRIs in the document. A context
|
|
686
|
+
* that `replaceJsonLdContext` answers is never fetched, so its host cannot
|
|
687
|
+
* stall the parse.
|
|
684
688
|
*
|
|
685
689
|
* When `truncated` is true the body is only a bounded prefix of a larger one, so
|
|
686
690
|
* only finding a triple ('hasTriples') is conclusive: a parse error at the cut
|
|
687
691
|
* or a clean end with no triple yet means we did not read far enough, not that
|
|
688
692
|
* the distribution is empty or malformed, and is reported as 'inconclusive'.
|
|
689
693
|
*/
|
|
690
|
-
function classifyRdfBody(body, contentType, baseIRI, budgetSignal, truncated) {
|
|
694
|
+
function classifyRdfBody(body, contentType, baseIRI, budgetSignal, truncated, replaceJsonLdContext) {
|
|
691
695
|
return new Promise((resolve) => {
|
|
692
696
|
const quads = rdfParser.parse(Readable.from([body]), {
|
|
693
697
|
contentType,
|
|
694
698
|
baseIRI,
|
|
699
|
+
...jsonLdParseContext(replaceJsonLdContext, budgetSignal),
|
|
695
700
|
});
|
|
696
701
|
const onBudgetElapsed = () => settle({ type: 'inconclusive' });
|
|
697
702
|
let settled = false;
|
|
@@ -724,6 +729,24 @@ function classifyRdfBody(body, contentType, baseIRI, budgetSignal, truncated) {
|
|
|
724
729
|
}
|
|
725
730
|
});
|
|
726
731
|
}
|
|
732
|
+
/**
|
|
733
|
+
* `rdf-parse` context that answers replaced JSON-LD contexts locally and
|
|
734
|
+
* fetches the rest. rdf-parse only routes context requests through a document
|
|
735
|
+
* loader set under this Comunica context key. Empty without a replacement, so
|
|
736
|
+
* the parser keeps its own loader. Context fetches stop when `budgetSignal`
|
|
737
|
+
* aborts, so none outlive the probe.
|
|
738
|
+
*/
|
|
739
|
+
function jsonLdParseContext(replaceJsonLdContext, budgetSignal) {
|
|
740
|
+
if (replaceJsonLdContext === undefined)
|
|
741
|
+
return {};
|
|
742
|
+
const fetchLoader = new FetchDocumentLoader((url, init) => fetch(url, { ...init, signal: budgetSignal }));
|
|
743
|
+
return {
|
|
744
|
+
'@comunica/actor-rdf-parse-jsonld:documentLoader': {
|
|
745
|
+
load: async (url) => replaceJsonLdContext(url) ??
|
|
746
|
+
fetchLoader.load(url),
|
|
747
|
+
},
|
|
748
|
+
};
|
|
749
|
+
}
|
|
727
750
|
/**
|
|
728
751
|
* Whether a parse error is the RDF parser failing to load a remote JSON-LD
|
|
729
752
|
* `@context` (an unreachable or broken third-party context host) rather than a
|
|
@@ -739,8 +762,8 @@ function isRemoteContextError(error) {
|
|
|
739
762
|
* The declared compressed form (e.g. `application/n-quads+gzip`) is the expected
|
|
740
763
|
* answer for a `+gzip`/`+zip` distribution, since the body is a gzip/zip archive
|
|
741
764
|
* served as-is. The bare media type (`application/n-quads`) is also accepted as a
|
|
742
|
-
* lenient fallback
|
|
743
|
-
* wrapper is absent
|
|
765
|
+
* lenient fallback – the RDF serialization is the same and only the compression
|
|
766
|
+
* wrapper is absent – so a server that serves the uncompressed representation is
|
|
744
767
|
* not flagged. A different compression suffix or a different base serialization is
|
|
745
768
|
* still a genuine mismatch. The registry strips the suffix into a separate compress
|
|
746
769
|
* format on ingest, so comparing against {@link Distribution.mimeType} alone would
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lde/distribution-probe",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"repository": {
|
|
5
5
|
"url": "git+https://github.com/ldelements/lde.git",
|
|
6
6
|
"directory": "packages/distribution-probe"
|
|
@@ -26,6 +26,7 @@
|
|
|
26
26
|
"dependencies": {
|
|
27
27
|
"@lde/dataset": "^0.8.0",
|
|
28
28
|
"@lde/host-limiter": "^0.1.0",
|
|
29
|
+
"jsonld-context-parser": "^2.4.0",
|
|
29
30
|
"rdf-parse": "^5.0.0",
|
|
30
31
|
"tslib": "^2.3.0"
|
|
31
32
|
}
|