@factoidal/core 0.13.0 → 0.15.0
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/CHANGELOG.md +23 -0
- package/README.md +58 -8
- package/bin/factoidal.mjs +6 -6
- package/browser.d.ts +10 -0
- package/browser.js +17 -0
- package/index.d.ts +279 -11
- package/index.mjs +26 -0
- package/l4-assets/l4factoidal.js +1 -1
- package/l4-assets/l4factoidal.mjs +1 -1
- package/l4-assets/l4factoidal.wasm +0 -0
- package/l4-assets/version.json +6 -6
- package/lib/api.mjs +55 -3
- package/lib/dataset.mjs +8 -0
- package/lib/evidence.mjs +93 -0
- package/lib/nodes.mjs +274 -0
- package/lib/sbm11-lws.mjs +200 -0
- package/lib/sbm11-node.mjs +61 -0
- package/lib/sbm11.mjs +155 -0
- package/lib/sync-http-node.mjs +71 -0
- package/lib/version.mjs +1 -1
- package/package.json +1 -1
- package/sample-store.mjs +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,28 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.15.0
|
|
4
|
+
|
|
5
|
+
- `openSbm11(source)` and `registerSbm11Source(source, reader)` are back, with the wasm ops `sbm11Open`, `sbm11Query`, `sbm11Close`: SPARQL over an SBM11 generation (the Shardborough store) in Node and in the browser, reading each file at an offset through a host reader (https://github.com/danbri/factoidal/issues/1009, https://github.com/danbri/factoidal-core/pull/93). Fix: in 0.14.0 `sbm11Open` trapped (wasm `unreachable`) because the C host-read functions had a signature that differs from the generated one; the wasm build now fails on any such mismatch. Not yet: the Merkle check of each read, a generation with a delta log (refused at open), packing SBM11 from npm.
|
|
6
|
+
- `lwsSbm11Reader(url, { auth, headers, transport })`: a reader for `openSbm11` of an SBM11 generation held as Linked Web Storage resources, by HTTP `Range` GETs when the server answers `206` with a matching `Content-Range`, else by whole GETs kept in memory; a changed `ETag` refuses the read, and `store.evidence` lists each resource read with its `ETag` (https://github.com/danbri/factoidal/issues/1010, https://github.com/danbri/factoidal-core/pull/90). In Lean every reader is now a storage `Medium`. `browser.d.ts` now types `openSbm11` and `registerSbm11Source`.
|
|
7
|
+
- The native `l4factoidal query` command keeps a named graph with no triple (RDF 1.1 Concepts §4); before, the dataset went through an N-Quads round trip, which cannot state an empty graph (https://github.com/danbri/factoidal/issues/876 part 1, https://github.com/danbri/factoidal-core/pull/94).
|
|
8
|
+
- SPARQL: a property-path alternative of IRIs (`?s p|q ?o`) is planned as the UNION of its triple patterns, so the store planners use their indexes for it; a theorem proves the two forms give the same solutions in the same order and multiplicity, and a test checks that both give the same plan and rows (https://github.com/danbri/factoidal/issues/1008, https://github.com/danbri/factoidal-core/pull/96).
|
|
9
|
+
- `pipe()` and `run()` take the type check and the union of the named assumptions from the engine's op `pipelineCheck`, computed from the Lean stage registry; one theorem (`Pipeline.guarantee_sound`) gives the guarantee of every well-typed pipeline (https://github.com/danbri/factoidal/issues/1002 step 1, https://github.com/danbri/factoidal-core/pull/82). `pipe()` adds `wellTyped` and `check`; `run()` adds `wellTyped` and refuses a pipeline that is not well typed unless `{unchecked: true}`, then reports `proved: false`. `stages()` adds `params`. With a wasm build that has no op `pipelineCheck`, `wellTyped` is null and the union is computed from the nodes as before.
|
|
10
|
+
|
|
11
|
+
## 0.14.0
|
|
12
|
+
|
|
13
|
+
**Checked pipelines.** Each component (reader, store, query evaluator, writer, reasoner, validator, proof checker) has one interface type whose contract against the W3C specifications is part of the type. A component cannot be added without a contract; a contract that is not proved yet is a named assumption, listed and counted in CI. A theorem states that the SPARQL answer agrees with the SPARQL 1.1 semantics for data from any source (a parsed document, a Dataset after edits, an update, a reasoner's output, a union of stores), given the listed assumptions. Status and gaps: https://github.com/danbri/factoidal/issues/975.
|
|
14
|
+
|
|
15
|
+
- `explain(sources, description, { profile: "rdfs" })`: whether the sources RDFS-entail a description graph (its blank nodes are variables), with a checked derivation (`bundle`) when it does and the reason (`rejection`) when not (https://github.com/danbri/factoidal/issues/1002).
|
|
16
|
+
- `run(pipeline, input, { unchecked: true })`: runs nodes whose stage is not proved; each evidence record has `checked`, and the result has `proved` and `uncheckedSteps`. Without the option a stage with no contract does not run.
|
|
17
|
+
- `Dataset.warnings`: a language tag that is not well-formed BCP 47 is kept and reported with its line, column and offset; the writers write it unchanged (https://github.com/danbri/factoidal/issues/1004).
|
|
18
|
+
- SPARQL `SELECT *` on the store path returns the variables in query order, the same header as the in-memory path (https://github.com/danbri/factoidal/issues/1014).
|
|
19
|
+
- SBM11 in npm (`openSbm11`) is **not** in this release. Its wasm op `sbm11Open` traps (`unreachable`) in the release build, so the ops are not dispatched and the functions are not exported; the native `l4sbm11` commands are unchanged (https://github.com/danbri/factoidal/issues/1009).
|
|
20
|
+
- `factoidal pack`, `factoidal sample-store` and `sampleStorePath()` write the IBK5 layout (wire version 10, manifest layout `quad-ibk5-ptd2-lgi4-gbi1-merkle-v0`) when no `--layout` is given; the default was IBK3. Owner, 2026-10-25: "yes IBK5 not IBK3 as default sounds sensible, assuming no major downside" (https://github.com/danbri/factoidal/issues/1009). `factoidal serve` over such a store takes changes to every graph; over an IBK3 store a change to a named graph answered 501. `--layout ibk3` still writes IBK3. `sampleStoreFacts.layout` is now `'ibk5'`.
|
|
21
|
+
- `stages()`, `node()`, `pipe()`, `run()`: the registered Lean stages as nodes (https://github.com/danbri/factoidal/issues/1002, step 2). `stages()` is the engine's op `stageList`, read from the Lean stage registry: name, interface type, wire types, status, contract and named assumptions of each stage. A value on a wire is `{graph, evidence}`; each node adds one evidence record `{stage, status, contract, assumptions, artefact}`, and `run()` reports the union of the named assumptions of the nodes. A stage with status `missing` does not run. Needs a wasm build with the op `stageList`.
|
|
22
|
+
- `factoidal pack` writes a set of rows in every layout (https://github.com/danbri/factoidal/issues/1006). Correction to 0.13.0: its entry says that the stores keep one copy of a repeated triple, but in 0.13.0 that was true for the IBK4 and IBK5 layouts only, and only on read. The default layout (IBK3) kept a repeated triple, and kept `"a"@en` and `"a"@EN` as two rows, so `COUNT(*)` counted both; the IBK4 and IBK5 packers also wrote the repeated rows. Now each packer keeps the distinct triples it has written and writes a triple only once (per graph on the quad layouts). A source with no repeated triple gives the same generation as before. The packer's memory now grows with the number of distinct triples of the source. Proved: `Dataflow.packedRows_keyNodup`; tests: `test/pack-is-a-set-1006.test.mjs`.
|
|
23
|
+
- The N-Triples, N-Quads, Turtle and TriG writers give a fresh valid label to a blank-node label outside BLANK_NODE_LABEL (the same label for each occurrence, never a label of another blank node), and refuse a language tag outside LANGTAG with an error that names the tag (https://github.com/danbri/factoidal/issues/986).
|
|
24
|
+
- A language tag is kept in lower case (RDF 1.2 Concepts §3.4: `"x"@en` and `"x"@EN` are one RDF term). Every reader, `STRLANG`, `STRLANGDIR` and the term constructors write the tag in lower case, so `LANG()` returns it in lower case, and a SPARQL answer no longer depends on the order of the triple patterns when the data spells one tag in two ways (https://github.com/danbri/factoidal/issues/1001).
|
|
25
|
+
|
|
3
26
|
## 0.13.0
|
|
4
27
|
|
|
5
28
|
**A parsed document is a set of quads** (RDF 1.1 Concepts §3 and §4;
|
package/README.md
CHANGED
|
@@ -52,6 +52,54 @@ RDF 1.2 input uses `turtle12`, `ntriples12`, `nquads12` or `trig12`.
|
|
|
52
52
|
"Terms given as strings".
|
|
53
53
|
- RDF/JS DataFactory and DatasetCore (`/rdfjs`), a functional API (`/fn`)
|
|
54
54
|
|
|
55
|
+
## Checked pipelines
|
|
56
|
+
|
|
57
|
+
Each component kind has one interface type whose contract against the W3C
|
|
58
|
+
specifications is part of the type: readers, stores, the SPARQL evaluator,
|
|
59
|
+
writers, reasoners, validators and the proof checker. A contract that is not
|
|
60
|
+
proved yet is a named assumption; CI fails when the list of assumptions grows
|
|
61
|
+
without a stated reason. A theorem states that the SPARQL answer agrees with
|
|
62
|
+
the SPARQL 1.1 semantics for data from any source (a parsed document, an
|
|
63
|
+
edited Dataset, an update, a reasoner's output, a union of stores), given the
|
|
64
|
+
listed assumptions. No new command-line verbs. Status:
|
|
65
|
+
https://github.com/danbri/factoidal/issues/975.
|
|
66
|
+
|
|
67
|
+
The same components are nodes you can compose. A value on a wire is
|
|
68
|
+
`{graph, evidence}`; each node adds one evidence record; the result lists the
|
|
69
|
+
union of the named assumptions of the nodes, so a pipeline says exactly what
|
|
70
|
+
its answer depends on.
|
|
71
|
+
`pipe()` and `run()` get the type check and that union from the engine
|
|
72
|
+
(op `pipelineCheck`); the theorem `Pipeline.guarantee_sound` gives the
|
|
73
|
+
guarantee of every well-typed pipeline, and `run()` refuses one that is not
|
|
74
|
+
well typed unless `{ unchecked: true }`.
|
|
75
|
+
|
|
76
|
+
```js
|
|
77
|
+
import { stages, node, pipe, run } from "@factoidal/core";
|
|
78
|
+
|
|
79
|
+
const sources = `@prefix ex: <http://example.org/> .
|
|
80
|
+
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
|
|
81
|
+
ex:alice ex:worksFor ex:acme .
|
|
82
|
+
ex:worksFor rdfs:domain ex:Employee .
|
|
83
|
+
ex:Employee rdfs:subClassOf ex:Person .`;
|
|
84
|
+
const description = `@prefix ex: <http://example.org/> .
|
|
85
|
+
_:x a ex:Person . _:x ex:worksFor ex:acme .`;
|
|
86
|
+
|
|
87
|
+
console.log((await stages()).length); // registered components, each with status and contract
|
|
88
|
+
const p = pipe(
|
|
89
|
+
await node("turtleDocumentReader"),
|
|
90
|
+
await node("rdfsReasoner"),
|
|
91
|
+
await node("rdfsExplainGraphStage", { description }),
|
|
92
|
+
);
|
|
93
|
+
const r = await run(p, sources);
|
|
94
|
+
console.log(r.evidence.map((e) => e.stage)); // one record per node
|
|
95
|
+
console.log(r.evidence[2].artefact.entailed); // true, with a checked derivation
|
|
96
|
+
console.log(r.assumptions, r.proved); // what the answer depends on
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
`run(p, input, { unchecked: true })` also runs components that are not proved,
|
|
100
|
+
and marks those steps in the evidence. Details: API.md, "Stages as nodes" and
|
|
101
|
+
"Explaining an entailment" (`explain()` is the same check as one call).
|
|
102
|
+
|
|
55
103
|
Formats (generated from `formats()`):
|
|
56
104
|
|
|
57
105
|
<!-- formats-table:start (generated by test/formats-table.test.mjs) -->
|
|
@@ -135,7 +183,7 @@ Changes: [CHANGELOG.md](CHANGELOG.md).
|
|
|
135
183
|
```console
|
|
136
184
|
$ npx factoidal serve --file data.ttl /ds # SPARQL endpoint at /ds/sparql
|
|
137
185
|
$ npx factoidal serve --tdb2 DIR /ds # serve a Jena TDB2 database
|
|
138
|
-
$ npx factoidal pack data.ttl store/gen-1 && npx factoidal activate store gen-1
|
|
186
|
+
$ npx factoidal pack data.ttl store/gen-1 && npx factoidal activate store gen-1 # IBK5 quad blocks
|
|
139
187
|
$ npx factoidal query store 'SELECT * WHERE { ?s ?p ?o } LIMIT 5'
|
|
140
188
|
$ npx factoidal query "$(npx factoidal sample-store)" 'SELECT (COUNT(*) AS ?n) WHERE { ?s ?p ?o }'
|
|
141
189
|
$ npx factoidal lws-serve DIR --issuer IRI # Linked Web Storage
|
|
@@ -150,13 +198,15 @@ $ npx factoidal solid-serve DIR # Solid Protocol
|
|
|
150
198
|
Official test suites, pass / fail / total, 2026-10-09 [B] unless marked.
|
|
151
199
|
|
|
152
200
|
The OWL DL rows below are historical measurements of the legacy harness;
|
|
153
|
-
they do not measure the current `owlIsConsistent` API. That API
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
201
|
+
they do not measure the current `owlIsConsistent` API. That API checks only
|
|
202
|
+
an admitted fragment of the default graph (atomic classes,
|
|
203
|
+
`owl:someValuesFrom`, `owl:TransitiveProperty`, and annotations, axiom
|
|
204
|
+
annotations included). It returns `true` for a checked finite model, `false`
|
|
205
|
+
for a checked contradiction and `null` for unsupported input, exhausted
|
|
206
|
+
`fuel` or an incomplete check. Other restrictions, negative property
|
|
207
|
+
assertions, imports and equivalentClass are unsupported. `owlEntails`
|
|
208
|
+
answers `false` only with a checked countermodel; its positive verdicts use a
|
|
209
|
+
separate legacy path that does not have this source-to-verdict proof. See the
|
|
160
210
|
[OWL API contract](https://github.com/danbri/factoidal-core/blob/main/npm/factoidal/API.md)
|
|
161
211
|
and the [assurance record](https://github.com/danbri/factoidal-core/blob/main/docs/20261022-owl-pr-reconciliation.md).
|
|
162
212
|
|
package/bin/factoidal.mjs
CHANGED
|
@@ -224,9 +224,9 @@ INPUT is an RDF file. OUTPUT is the generation directory to create; it is
|
|
|
224
224
|
normally STORE/gen-N. Packing does not activate: run activate next.
|
|
225
225
|
|
|
226
226
|
options:
|
|
227
|
-
--layout LAYOUT
|
|
228
|
-
|
|
229
|
-
|
|
227
|
+
--layout LAYOUT ibk5 (quads, wire version 10: RDF 1.2 terms,
|
|
228
|
+
out-of-line literals, zone maps; default), ibk4
|
|
229
|
+
(quads, wire version 9) or ibk3 (triples)
|
|
230
230
|
--syntax SYNTAX turtle, trig or nquads; default from the file extension
|
|
231
231
|
--chunk-bytes N Merkle chunk size; default is the engine's
|
|
232
232
|
--batch-bytes N ibk4 only: source bytes between publication batches;
|
|
@@ -319,7 +319,7 @@ activated generation's delta log as one batch before the response is sent,
|
|
|
319
319
|
and is visible to the next request. An IBK3 (triples) generation accepts
|
|
320
320
|
changes to the default graph only, because its delta log is replayed and
|
|
321
321
|
compacted for that graph only; a change to a named graph answers 501. A
|
|
322
|
-
quad generation (ibk4, ibk5) accepts every graph. Fold the log into a new
|
|
322
|
+
quad generation (ibk4, ibk5, the default) accepts every graph. Fold the log into a new
|
|
323
323
|
generation with l4block-shard-compact, activate it, and restart the server.
|
|
324
324
|
|
|
325
325
|
The server makes no protocol decision: the request framing, routing,
|
|
@@ -1266,7 +1266,7 @@ function currentDirectory () {
|
|
|
1266
1266
|
}
|
|
1267
1267
|
|
|
1268
1268
|
function packLayout (options) {
|
|
1269
|
-
if (typeof options.layout !== 'string') return '
|
|
1269
|
+
if (typeof options.layout !== 'string') return 'ibk5'
|
|
1270
1270
|
const layout = options.layout.toLowerCase()
|
|
1271
1271
|
if (PACK_LAYOUTS.indexOf(layout) >= 0) return layout
|
|
1272
1272
|
throw new UsageError(`--layout ${options.layout} is not one of ${PACK_LAYOUTS.join(', ')}`)
|
|
@@ -1528,7 +1528,7 @@ async function commandServe (positional, options) {
|
|
|
1528
1528
|
const { tmpdir } = await import('node:os')
|
|
1529
1529
|
const { join } = await import('node:path')
|
|
1530
1530
|
const root = mkdtempSync(join(tmpdir(), 'factoidal-serve-'))
|
|
1531
|
-
const packOptions = { quiet: true, layout:
|
|
1531
|
+
const packOptions = { quiet: true, layout: packLayout(options) }
|
|
1532
1532
|
let rc = await commandPack([options.file, join(root, 'gen-1')], packOptions)
|
|
1533
1533
|
if (rc !== 0) return rc
|
|
1534
1534
|
rc = await commandActivate([root, 'gen-1'], { quiet: true })
|
package/browser.d.ts
CHANGED
|
@@ -11,6 +11,8 @@ export type {
|
|
|
11
11
|
DataFormat, EntailRegime, Bindings, DataInput, QueryInput, ParseOptions,
|
|
12
12
|
QueryOptions, SerializeOptions, StoreReader, OpenStoreOptions,
|
|
13
13
|
DeltaLogHandle, DeltaLogInspection,
|
|
14
|
+
Sbm11Reader, Sbm11Answer, Sbm11Store, Sbm11ReadEvidence, Sbm11Transport,
|
|
15
|
+
ExplainResult, StageInfo, EvidenceRecord, Wire, FactoidalNode, Pipeline, RunResult, RunOptions,
|
|
14
16
|
} from './index';
|
|
15
17
|
|
|
16
18
|
export {
|
|
@@ -38,6 +40,9 @@ export {
|
|
|
38
40
|
openStore,
|
|
39
41
|
queryStore,
|
|
40
42
|
listStores,
|
|
43
|
+
openSbm11,
|
|
44
|
+
registerSbm11Source,
|
|
45
|
+
lwsSbm11Reader,
|
|
41
46
|
deltaLogOpen,
|
|
42
47
|
deltaLogAppend,
|
|
43
48
|
deltaLogReadAllHex,
|
|
@@ -50,6 +55,11 @@ export {
|
|
|
50
55
|
owlClosure,
|
|
51
56
|
coreRdfsClosure,
|
|
52
57
|
coreRdfsCheck,
|
|
58
|
+
explain,
|
|
59
|
+
stages,
|
|
60
|
+
node,
|
|
61
|
+
pipe,
|
|
62
|
+
run,
|
|
53
63
|
rdfsPlusClosure,
|
|
54
64
|
rhoDfClosure,
|
|
55
65
|
rhoDfFragmentCheck,
|
package/browser.js
CHANGED
|
@@ -26,6 +26,8 @@ import { buildApi } from './lib/api.mjs';
|
|
|
26
26
|
import { entryFromEngine } from './lib/lean-entry.mjs';
|
|
27
27
|
import { browserEngine } from './lib/browser-engine.mjs';
|
|
28
28
|
import { indexedDbDeltaLogStore } from './lib/delta-log-hosts.mjs';
|
|
29
|
+
import { sbm11Api, registerSbm11Source } from './lib/sbm11.mjs';
|
|
30
|
+
import { lwsSbm11Reader } from './lib/sbm11-lws.mjs';
|
|
29
31
|
|
|
30
32
|
const l4Engine = browserEngine('l4factoidal', new URL('./l4-assets/', import.meta.url).href);
|
|
31
33
|
|
|
@@ -59,6 +61,16 @@ const api = buildApi({
|
|
|
59
61
|
deltaLogStore: indexedDbDeltaLogStore(),
|
|
60
62
|
});
|
|
61
63
|
|
|
64
|
+
// SBM11, the Shardborough store, through host positioned reads
|
|
65
|
+
// (lib/sbm11.mjs; https://github.com/danbri/factoidal/issues/1009). In a
|
|
66
|
+
// browser every source needs a registered synchronous reader, for example
|
|
67
|
+
// over OPFS FileSystemSyncAccessHandle in a dedicated worker.
|
|
68
|
+
export const openSbm11 = sbm11Api(loadEngine).openSbm11;
|
|
69
|
+
export { registerSbm11Source };
|
|
70
|
+
// A reader of an SBM11 generation held as LWS resources (issue 1010 KR3). In
|
|
71
|
+
// a browser worker its default transport is a synchronous XMLHttpRequest.
|
|
72
|
+
export { lwsSbm11Reader };
|
|
73
|
+
|
|
62
74
|
// RDF and SPARQL
|
|
63
75
|
export const parse = api.parse;
|
|
64
76
|
export const query = api.query;
|
|
@@ -102,6 +114,11 @@ export const shexValidate = api.shexValidate;
|
|
|
102
114
|
export const owlClosure = api.owlClosure;
|
|
103
115
|
export const coreRdfsClosure = api.coreRdfsClosure;
|
|
104
116
|
export const coreRdfsCheck = api.coreRdfsCheck;
|
|
117
|
+
export const explain = api.explain;
|
|
118
|
+
export const stages = api.stages;
|
|
119
|
+
export const node = api.node;
|
|
120
|
+
export const pipe = api.pipe;
|
|
121
|
+
export const run = api.run;
|
|
105
122
|
export const rdfsPlusClosure = api.rdfsPlusClosure;
|
|
106
123
|
export const rhoDfClosure = api.rhoDfClosure;
|
|
107
124
|
export const rhoDfFragmentCheck = api.rhoDfFragmentCheck;
|
package/index.d.ts
CHANGED
|
@@ -184,7 +184,8 @@ export interface ParseDiagnostic {
|
|
|
184
184
|
* 'unterminated-string', 'bad-literal', 'bad-blank-node',
|
|
185
185
|
* 'bad-prefixed-name', 'bad-directive', 'bad-graph-name', 'rdf12-syntax',
|
|
186
186
|
* 'unclosed-group', 'nesting-limit', 'unexpected-token', 'internal' or
|
|
187
|
-
* 'too-many-errors'.
|
|
187
|
+
* 'too-many-errors'. A warning (`Dataset.warnings`) has the class
|
|
188
|
+
* 'ill-formed-language-tag'.
|
|
188
189
|
*/
|
|
189
190
|
class: string;
|
|
190
191
|
}
|
|
@@ -250,6 +251,13 @@ export class Dataset implements Iterable<Quad> {
|
|
|
250
251
|
readonly prefixes: Record<string, string>;
|
|
251
252
|
/** Diagnostics of a `{lenient:true}` parse; else empty. */
|
|
252
253
|
readonly diagnostics: ParseDiagnostic[];
|
|
254
|
+
/**
|
|
255
|
+
* Warnings of the parse of text: one per language tag that is not
|
|
256
|
+
* well-formed under BCP 47 section 2.2.9 (class 'ill-formed-language-tag',
|
|
257
|
+
* the position of the tag's first character). The reader keeps the triple
|
|
258
|
+
* and the writers write the tag unchanged. Empty otherwise.
|
|
259
|
+
*/
|
|
260
|
+
readonly warnings: ParseDiagnostic[];
|
|
253
261
|
/** The parse format tag, when the data was read from text. */
|
|
254
262
|
readonly format: string | undefined;
|
|
255
263
|
/** True when `format` was inferred because the caller gave none. */
|
|
@@ -627,6 +635,190 @@ export function coreRdfsCheck(
|
|
|
627
635
|
options?: { format?: DataFormat; baseIRI?: string }
|
|
628
636
|
): Promise<{ ok: boolean; fragment: boolean }>;
|
|
629
637
|
|
|
638
|
+
/** The evidence bundle and verdict of explain(). */
|
|
639
|
+
export interface ExplainResult {
|
|
640
|
+
profile: 'rdfs';
|
|
641
|
+
/** true: the checker accepted `bundle`; the sources RDFS-entail the description. */
|
|
642
|
+
entailed: boolean;
|
|
643
|
+
bundle: null | {
|
|
644
|
+
sources: string;
|
|
645
|
+
description: string;
|
|
646
|
+
mapping: Array<{ bnode: string; term: string }>;
|
|
647
|
+
steps: Array<{ rule: string; conclusion: string; premises: number[] }>;
|
|
648
|
+
support: number[];
|
|
649
|
+
};
|
|
650
|
+
/** Named assumptions that `entailed: false` relies on. */
|
|
651
|
+
assumptions: string[];
|
|
652
|
+
rejection:
|
|
653
|
+
| null
|
|
654
|
+
| { kind: 'noMatch'; index: number; triple: string }
|
|
655
|
+
| { kind: 'noJointMapping' }
|
|
656
|
+
| { kind: 'checkerRefused'; reason: string };
|
|
657
|
+
entailedMeans: string;
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
/**
|
|
661
|
+
* Is `description` (blank nodes shared across its triples) RDFS-entailed by
|
|
662
|
+
* `sources`? Full engine only; the description is default-graph only. Text
|
|
663
|
+
* inputs are Turtle unless `format` says otherwise.
|
|
664
|
+
* https://github.com/danbri/factoidal/issues/1002
|
|
665
|
+
*/
|
|
666
|
+
export function explain(
|
|
667
|
+
sources: DataInput,
|
|
668
|
+
description: DataInput,
|
|
669
|
+
options?: {
|
|
670
|
+
profile?: 'rdfs';
|
|
671
|
+
format?: DataFormat;
|
|
672
|
+
baseIRI?: string;
|
|
673
|
+
sourcesFormat?: DataFormat;
|
|
674
|
+
descriptionFormat?: DataFormat;
|
|
675
|
+
}
|
|
676
|
+
): Promise<ExplainResult>;
|
|
677
|
+
|
|
678
|
+
/**
|
|
679
|
+
* One registered stage, as the engine's op stageList reads it from the Lean
|
|
680
|
+
* registry (L4Factoidal.Dataflow.registry).
|
|
681
|
+
* https://github.com/danbri/factoidal/issues/1002
|
|
682
|
+
*/
|
|
683
|
+
export interface StageInfo {
|
|
684
|
+
/** The stage declaration relative to L4Factoidal.Dataflow (or L4Factoidal); the area when there is none. */
|
|
685
|
+
name: string;
|
|
686
|
+
/** The full Lean declaration name, or null (status tested or missing). */
|
|
687
|
+
declaration: string | null;
|
|
688
|
+
area: string;
|
|
689
|
+
dir: string;
|
|
690
|
+
/** The interface type of the declaration (Stage, RdfReader, SoundReasoner, ...), or null. */
|
|
691
|
+
kind: string | null;
|
|
692
|
+
wireIn: string;
|
|
693
|
+
wireOut: string;
|
|
694
|
+
status: 'proved' | 'tested' | 'assumed' | 'missing';
|
|
695
|
+
/** The contract declaration (full Lean name), or null. */
|
|
696
|
+
contract: string | null;
|
|
697
|
+
theorems: string[];
|
|
698
|
+
/** The named assumptions of the stage (full Lean names). */
|
|
699
|
+
assumptions: string[];
|
|
700
|
+
tests: string[];
|
|
701
|
+
note: string;
|
|
702
|
+
/** false exactly when status is 'missing': such a stage does not run. */
|
|
703
|
+
runnable: boolean;
|
|
704
|
+
/** true when this package has a function that runs the stage as a node. */
|
|
705
|
+
bound: boolean;
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
/** One evidence record: the node that ran and what it produced. */
|
|
709
|
+
export interface EvidenceRecord {
|
|
710
|
+
stage: string;
|
|
711
|
+
status: StageInfo['status'];
|
|
712
|
+
contract: string | null;
|
|
713
|
+
assumptions: string[];
|
|
714
|
+
/** true iff `status` is 'proved'. */
|
|
715
|
+
checked: boolean;
|
|
716
|
+
/** true on a step that ran under run()'s `unchecked` opt-in and is not checked. */
|
|
717
|
+
unchecked?: true;
|
|
718
|
+
/** With `unchecked`: the status and named assumptions, or 'no contract' for a missing stage. */
|
|
719
|
+
reason?: string;
|
|
720
|
+
/** The node's own output beside the graph: the explain result, the SHACL report, the written text, ... */
|
|
721
|
+
artefact: unknown;
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
/** The value on a wire. */
|
|
725
|
+
export interface Wire<G = unknown> {
|
|
726
|
+
graph: G;
|
|
727
|
+
evidence: EvidenceRecord[];
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
/** A node: a registered stage with its parameters. */
|
|
731
|
+
export interface FactoidalNode {
|
|
732
|
+
readonly stage: string;
|
|
733
|
+
readonly kind: string | null;
|
|
734
|
+
readonly status: StageInfo['status'];
|
|
735
|
+
readonly contract: string | null;
|
|
736
|
+
readonly assumptions: readonly string[];
|
|
737
|
+
readonly wireIn: string;
|
|
738
|
+
readonly wireOut: string;
|
|
739
|
+
readonly runnable: boolean;
|
|
740
|
+
readonly bound: boolean;
|
|
741
|
+
readonly params: Readonly<Record<string, unknown>>;
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
/** The engine's check of a list of stages (op pipelineCheck, Lean pipelineReport). */
|
|
745
|
+
export interface PipelineCheck {
|
|
746
|
+
/** Each stage's output wire is the next stage's input wire or converts to it (Lean wellTyped_iff). */
|
|
747
|
+
readonly wellTyped: boolean;
|
|
748
|
+
readonly wireIn: string | null;
|
|
749
|
+
readonly wireOut: string | null;
|
|
750
|
+
/** The first stage `at` whose output does not feed stage `at + 1`. */
|
|
751
|
+
readonly mismatch: { readonly at: number; readonly out: string; readonly in: string } | null;
|
|
752
|
+
/** Where an output reaches the next input by a named conversion (Dataflow/Wire.lean). */
|
|
753
|
+
readonly conversions: readonly { readonly at: number; readonly from: string; readonly to: string }[];
|
|
754
|
+
/** The union of the stages' named assumptions: the premises of Pipeline.guarantee_sound. */
|
|
755
|
+
readonly assumptions: readonly string[];
|
|
756
|
+
readonly contract: { readonly theorem: string; readonly stages: readonly (string | null)[] };
|
|
757
|
+
readonly stages: readonly {
|
|
758
|
+
readonly name: string; readonly status: StageInfo['status']; readonly checked: boolean;
|
|
759
|
+
readonly contract: string | null; readonly assumptions: readonly string[];
|
|
760
|
+
readonly wireIn: string; readonly wireOut: string;
|
|
761
|
+
}[];
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
/**
|
|
765
|
+
* A pipeline: nodes in order. `check` is the engine's op pipelineCheck;
|
|
766
|
+
* `assumptions` and `wellTyped` are its fields. With an engine that has no
|
|
767
|
+
* such op, `check` and `wellTyped` are null and `assumptions` is the union
|
|
768
|
+
* of the nodes' assumptions.
|
|
769
|
+
*/
|
|
770
|
+
export interface Pipeline {
|
|
771
|
+
readonly nodes: readonly FactoidalNode[];
|
|
772
|
+
readonly assumptions: readonly string[];
|
|
773
|
+
readonly wellTyped: boolean | null;
|
|
774
|
+
readonly check: PipelineCheck | null;
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
/** The result of run(): the last graph, one record per node, the assumption union. */
|
|
778
|
+
export interface RunResult<G = unknown> extends Wire<G> {
|
|
779
|
+
/** The assumptions of the records on the input wire, then the engine's union for the pipeline (op pipelineCheck). */
|
|
780
|
+
assumptions: string[];
|
|
781
|
+
/** true iff every evidence record is checked (status 'proved') and the pipeline is well typed. */
|
|
782
|
+
proved: boolean;
|
|
783
|
+
/** The engine's type check of the pipeline; null when the engine has no op pipelineCheck. */
|
|
784
|
+
wellTyped: boolean | null;
|
|
785
|
+
/** The stage names of the records that are not checked. */
|
|
786
|
+
uncheckedSteps: string[];
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
/** Options of run(). */
|
|
790
|
+
export interface RunOptions {
|
|
791
|
+
/**
|
|
792
|
+
* Let stages that are not 'proved' run, and 'missing' stages too when the
|
|
793
|
+
* package has a runner for them. Each such step's record says so, and the
|
|
794
|
+
* result's `proved` is false. A stage with no runner still rejects (op.unknown).
|
|
795
|
+
*/
|
|
796
|
+
unchecked?: boolean;
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
/** The engine's stage list (op stageList, full engine). */
|
|
800
|
+
export function stages(): Promise<StageInfo[]>;
|
|
801
|
+
|
|
802
|
+
/**
|
|
803
|
+
* A node for the registered stage `name`. `params` are passed to the
|
|
804
|
+
* package function that runs it: reader options; `{shapes}` for
|
|
805
|
+
* shaclValidator; `{query}` for shippedEvaluator; `{description}` for
|
|
806
|
+
* rdfsExplainGraphStage. An unknown name rejects with a FactoidalError.
|
|
807
|
+
*/
|
|
808
|
+
export function node(name: string, params?: Record<string, unknown>): Promise<FactoidalNode>;
|
|
809
|
+
|
|
810
|
+
/** Compose nodes and pipelines left to right. */
|
|
811
|
+
export function pipe(...nodes: Array<FactoidalNode | Pipeline>): Pipeline;
|
|
812
|
+
|
|
813
|
+
/**
|
|
814
|
+
* Run a node or pipeline on `input` (a Wire, or the graph: text for a
|
|
815
|
+
* reader, else a Dataset or data input). Rejects before any node runs when
|
|
816
|
+
* a node's stage has status 'missing' (unless `options.unchecked`) or has
|
|
817
|
+
* no package function, or when the engine's check finds the pipeline not
|
|
818
|
+
* well typed (unless `options.unchecked`; then `proved` is false).
|
|
819
|
+
*/
|
|
820
|
+
export function run(pipeline: FactoidalNode | Pipeline, input: unknown, options?: RunOptions): Promise<RunResult>;
|
|
821
|
+
|
|
630
822
|
/** Literature-name alias for coreRdfsClosure (ρdf). */
|
|
631
823
|
export function rhoDfClosure(
|
|
632
824
|
data: DataInput,
|
|
@@ -681,15 +873,17 @@ export function tableauDlInconsistent(
|
|
|
681
873
|
|
|
682
874
|
/**
|
|
683
875
|
* Checked consistency for the original default graph in a restricted
|
|
684
|
-
*
|
|
685
|
-
*
|
|
686
|
-
*
|
|
687
|
-
*
|
|
688
|
-
*
|
|
689
|
-
*
|
|
690
|
-
*
|
|
691
|
-
*
|
|
692
|
-
*
|
|
876
|
+
* OWL fragment (named subclass/class assertions, Thing/Nothing,
|
|
877
|
+
* owl:someValuesFrom restrictions as a superclass or a class assertion,
|
|
878
|
+
* owl:TransitiveProperty, sameAs/differentFrom, declarations, annotations
|
|
879
|
+
* and annotated axioms).
|
|
880
|
+
* true requires a checked finite model and has no `reason`; false requires
|
|
881
|
+
* an accepted refutation certificate; null means unsupported-syntax,
|
|
882
|
+
* budget-out or incomplete-search. Named graphs do not contribute axioms.
|
|
883
|
+
* RDF 1.2 terms are refused. Other restrictions, object/data property
|
|
884
|
+
* axioms, imports and equivalentClass are unsupported. `fuel` bounds the
|
|
885
|
+
* model search and the certificate checker (default 20000 rounds).
|
|
886
|
+
* This is not complete OWL DL support.
|
|
693
887
|
*/
|
|
694
888
|
export function owlIsConsistent(
|
|
695
889
|
data: DataInput,
|
|
@@ -703,7 +897,9 @@ export function owlIsConsistent(
|
|
|
703
897
|
* implementation returns `true` for those positive results and `null`
|
|
704
898
|
* when it cannot establish entailment. `reason` distinguishes budget-out,
|
|
705
899
|
* incomplete-search, unsupported-conclusion and unsupported-punning.
|
|
706
|
-
*
|
|
900
|
+
* `false` (via `refutation`, reason `countermodel: ...`) requires a checked
|
|
901
|
+
* finite model of the premise in which the conclusion fails; both graphs
|
|
902
|
+
* must be in the fragment that `owlIsConsistent` reads.
|
|
707
903
|
* The punning refusal is limited; full OWL DL recognition and universal
|
|
708
904
|
* Direct Semantics soundness are not established by this API.
|
|
709
905
|
*/
|
|
@@ -897,6 +1093,74 @@ export class Store {
|
|
|
897
1093
|
*/
|
|
898
1094
|
export function openStore(source: string | StoreReader, options?: OpenStoreOptions): Promise<Store>;
|
|
899
1095
|
|
|
1096
|
+
/** A synchronous reader of the files of one SBM11 generation directory (or
|
|
1097
|
+
* collection root): the engine names every file and checks every answer. */
|
|
1098
|
+
export interface Sbm11Reader {
|
|
1099
|
+
readAt(file: string, offset: number, length: number): Uint8Array | null;
|
|
1100
|
+
size(file: string): number | null;
|
|
1101
|
+
}
|
|
1102
|
+
|
|
1103
|
+
/** The answer of one SBM11 query. */
|
|
1104
|
+
export interface Sbm11Answer {
|
|
1105
|
+
/** `path=ids` or `path=reference reason="..." ...` */
|
|
1106
|
+
path: string;
|
|
1107
|
+
rows: number;
|
|
1108
|
+
/** `srj` (SELECT, ASK) or `ntriples` (CONSTRUCT, DESCRIBE) */
|
|
1109
|
+
format: 'srj' | 'ntriples';
|
|
1110
|
+
/** the bytes `l4sbm11 query` writes, as text */
|
|
1111
|
+
readonly result: string;
|
|
1112
|
+
/** the same answer as UTF-8 bytes */
|
|
1113
|
+
readonly bytes: Uint8Array;
|
|
1114
|
+
bytesRead: number;
|
|
1115
|
+
reads: number;
|
|
1116
|
+
/** the parsed SPARQL JSON for SELECT and ASK */
|
|
1117
|
+
readonly json: any;
|
|
1118
|
+
}
|
|
1119
|
+
|
|
1120
|
+
/** An open SBM11 generation. */
|
|
1121
|
+
export interface Sbm11Store {
|
|
1122
|
+
handle: string;
|
|
1123
|
+
/** the generation CURRENT names, or "" when the source is a generation */
|
|
1124
|
+
generation: string;
|
|
1125
|
+
terms: number;
|
|
1126
|
+
query(sparql: string, options?: { base?: string; sparql12?: boolean; reference?: boolean }): Sbm11Answer;
|
|
1127
|
+
/** the resources the reader read, when its reader records them (lwsSbm11Reader) */
|
|
1128
|
+
readonly evidence: Sbm11ReadEvidence[] | undefined;
|
|
1129
|
+
close(): void;
|
|
1130
|
+
}
|
|
1131
|
+
|
|
1132
|
+
/** Open an SBM11 generation (https://github.com/danbri/factoidal/issues/1009).
|
|
1133
|
+
* Node: `source` is a directory unless a reader is given or registered. */
|
|
1134
|
+
export function openSbm11(source: string, options?: { reader?: Sbm11Reader }): Promise<Sbm11Store>;
|
|
1135
|
+
|
|
1136
|
+
/** Serve `source` with `reader`; `null` removes it. */
|
|
1137
|
+
export function registerSbm11Source(source: string, reader: Sbm11Reader | null): void;
|
|
1138
|
+
|
|
1139
|
+
/** One LWS resource an SBM11 reader read (https://github.com/danbri/factoidal/issues/1010). */
|
|
1140
|
+
export interface Sbm11ReadEvidence {
|
|
1141
|
+
resource: string;
|
|
1142
|
+
etag: string;
|
|
1143
|
+
lastModified: string | null;
|
|
1144
|
+
/** HTTP requests made for this resource */
|
|
1145
|
+
requests: number;
|
|
1146
|
+
/** answers that were 206 Partial Content */
|
|
1147
|
+
partial: number;
|
|
1148
|
+
/** content bytes received */
|
|
1149
|
+
bytes: number;
|
|
1150
|
+
}
|
|
1151
|
+
|
|
1152
|
+
/** A synchronous HTTP GET: `(url, headers) -> answer`. */
|
|
1153
|
+
export type Sbm11Transport = (url: string, headers: Record<string, string>) =>
|
|
1154
|
+
{ status: number; header(name: string): string | null; bytes: Uint8Array };
|
|
1155
|
+
|
|
1156
|
+
/** A reader of the SBM11 files held as LWS resources under the container
|
|
1157
|
+
* `url` (one resource per file, CURRENT a resource too), by HTTP Range GETs
|
|
1158
|
+
* when the server answers 206, else by whole GETs kept in memory. Node: the
|
|
1159
|
+
* default transport is a fetch in a worker thread; close() stops it. */
|
|
1160
|
+
export function lwsSbm11Reader(url: string, opts?: {
|
|
1161
|
+
auth?: string; headers?: Record<string, string>; transport?: Sbm11Transport; timeoutMs?: number
|
|
1162
|
+
}): Sbm11Reader & { evidence(): Sbm11ReadEvidence[]; errors(): string[]; close(): void };
|
|
1163
|
+
|
|
900
1164
|
/** One query against a store: open with the query's plan, query, close. */
|
|
901
1165
|
export function queryStore(
|
|
902
1166
|
source: string | StoreReader,
|
|
@@ -1302,6 +1566,10 @@ export function capabilities(): Promise<{
|
|
|
1302
1566
|
shacl: boolean;
|
|
1303
1567
|
shex: boolean;
|
|
1304
1568
|
owlClosure: boolean;
|
|
1569
|
+
explain: boolean;
|
|
1570
|
+
stages: boolean;
|
|
1571
|
+
/** The op pipelineCheck: pipe() and run() use the engine's type check and assumption union. */
|
|
1572
|
+
pipelineCheck: boolean;
|
|
1305
1573
|
tableau: boolean;
|
|
1306
1574
|
rml: boolean;
|
|
1307
1575
|
csvw: boolean;
|
package/index.mjs
CHANGED
|
@@ -21,6 +21,10 @@ import { readFileSync } from 'node:fs';
|
|
|
21
21
|
import { join } from 'node:path';
|
|
22
22
|
import { buildApi } from './lib/api.mjs';
|
|
23
23
|
import { entryFromEngine } from './lib/lean-entry.mjs';
|
|
24
|
+
import { sbm11Api, registerSbm11Source, setDefaultSbm11Reader } from './lib/sbm11.mjs';
|
|
25
|
+
import { nodeDefaultReaderFactory } from './lib/sbm11-node.mjs';
|
|
26
|
+
import { lwsSbm11Reader as lwsSbm11ReaderWith } from './lib/sbm11-lws.mjs';
|
|
27
|
+
import { nodeSyncTransport } from './lib/sync-http-node.mjs';
|
|
24
28
|
|
|
25
29
|
/**
|
|
26
30
|
* The loaded Lean engine handle: `call(op, args)`, `callBlob`,
|
|
@@ -66,6 +70,23 @@ export const clearExtensionFunctions = api.clearExtensionFunctions;
|
|
|
66
70
|
export const registerServiceEndpoint = api.registerServiceEndpoint;
|
|
67
71
|
export const clearServiceEndpoints = api.clearServiceEndpoints;
|
|
68
72
|
|
|
73
|
+
// SBM11, the Shardborough store, through host positioned reads
|
|
74
|
+
// (lib/sbm11.mjs; https://github.com/danbri/factoidal/issues/1009). In Node a
|
|
75
|
+
// source with no registered reader is a directory.
|
|
76
|
+
setDefaultSbm11Reader(nodeDefaultReaderFactory());
|
|
77
|
+
export const openSbm11 = sbm11Api(loadEngine).openSbm11;
|
|
78
|
+
export { registerSbm11Source };
|
|
79
|
+
|
|
80
|
+
// A reader of an SBM11 generation held as LWS resources (lib/sbm11-lws.mjs;
|
|
81
|
+
// https://github.com/danbri/factoidal/issues/1010 KR3). In Node the default
|
|
82
|
+
// synchronous transport is a fetch in a worker thread (lib/sync-http-node.mjs),
|
|
83
|
+
// stopped by the reader's close().
|
|
84
|
+
export function lwsSbm11Reader(url, opts = {}) {
|
|
85
|
+
if (typeof opts.transport === 'function') return lwsSbm11ReaderWith(url, opts);
|
|
86
|
+
const transport = nodeSyncTransport({ timeoutMs: opts.timeoutMs });
|
|
87
|
+
return lwsSbm11ReaderWith(url, { ...opts, transport, onClose: () => transport.close() });
|
|
88
|
+
}
|
|
89
|
+
|
|
69
90
|
// The persisted store
|
|
70
91
|
export const openStore = api.openStore;
|
|
71
92
|
export const queryStore = api.queryStore;
|
|
@@ -86,6 +107,11 @@ export const shexValidate = api.shexValidate;
|
|
|
86
107
|
export const owlClosure = api.owlClosure;
|
|
87
108
|
export const coreRdfsClosure = api.coreRdfsClosure;
|
|
88
109
|
export const coreRdfsCheck = api.coreRdfsCheck;
|
|
110
|
+
export const explain = api.explain;
|
|
111
|
+
export const stages = api.stages;
|
|
112
|
+
export const node = api.node;
|
|
113
|
+
export const pipe = api.pipe;
|
|
114
|
+
export const run = api.run;
|
|
89
115
|
export const rdfsPlusClosure = api.rdfsPlusClosure;
|
|
90
116
|
export const rhoDfClosure = api.rhoDfClosure;
|
|
91
117
|
export const rhoDfFragmentCheck = api.rhoDfFragmentCheck;
|
package/l4-assets/l4factoidal.js
CHANGED