@factoidal/core 0.7.1 → 0.10.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 +503 -1
- package/NOTICE +2 -21
- package/README.md +117 -993
- package/api.d.ts +53 -0
- package/api.js +5 -0
- package/api.mjs +56 -0
- package/bin/engine.mjs +38 -19
- package/bin/factoidal.mjs +512 -66
- package/bin/pack.mjs +37 -0
- package/bin/serve-worker.mjs +32 -0
- package/bin/serve.mjs +423 -0
- package/bin/store-worker-host.mjs +1 -1
- package/bin/store.mjs +1 -1
- package/browser.d.ts +110 -507
- package/browser.js +151 -2257
- package/docs/fuseki-replacement.md +108 -0
- package/fn.d.ts +24 -95
- package/fn.js +104 -150
- package/index.d.ts +598 -259
- package/index.js +4 -234
- package/index.mjs +176 -84
- package/l4-assets/l4factoidal.js +47 -7
- package/l4-assets/l4factoidal.mjs +1 -1
- package/l4-assets/l4factoidal.wasm +0 -0
- package/l4-assets/version.json +11 -7
- package/lib/api.mjs +2300 -0
- package/lib/browser-engine.mjs +69 -0
- package/lib/dataset.mjs +377 -0
- package/lib/delta-log-hosts.mjs +191 -0
- package/lib/lean-entry.mjs +204 -0
- package/lib/pure.mjs +98 -0
- package/lib/store.mjs +115 -0
- package/lib/version.mjs +5 -0
- package/lws/README.md +97 -0
- package/lws/server.mjs +331 -0
- package/package.json +59 -61
- package/rdfjs.js +5 -536
- package/rdfjs.mjs +594 -0
- package/sample-store.d.ts +8 -16
- package/sample-store.mjs +39 -29
- package/solid/client/README.md +100 -0
- package/solid/client/index.mjs +277 -0
- package/solid/server/README.md +96 -0
- package/solid/server/index.mjs +317 -0
- package/store-host/deno.mjs +8 -0
- package/store-host/index.mjs +12 -0
- package/store-host/node.mjs +8 -0
- package/xmpp/README.md +107 -0
- package/xmpp/index.mjs +229 -0
- package/browser-wasm.js +0 -872
- package/factoidal-npm-entry.js +0 -15477
- package/factoidal-npm-entry.wasm.assets/code-5a7fc68f2ab1323718b8.wasm +0 -0
- package/factoidal-npm-entry.wasm.js +0 -455
- package/factoidal.js +0 -13118
- package/factoidal.wasm.assets/code-bbe6099bfb5b10c4c3ab.wasm +0 -0
- package/factoidal.wasm.js +0 -457
- package/hacl-init.js +0 -92
- package/hacl-wasm/FStar.wasm +0 -0
- package/hacl-wasm/Hacl_Bignum.wasm +0 -0
- package/hacl-wasm/Hacl_Bignum25519_51.wasm +0 -0
- package/hacl-wasm/Hacl_Bignum_Base.wasm +0 -0
- package/hacl-wasm/Hacl_Curve25519_51.wasm +0 -0
- package/hacl-wasm/Hacl_Ed25519.wasm +0 -0
- package/hacl-wasm/Hacl_Ed25519_PrecompTable.wasm +0 -0
- package/hacl-wasm/Hacl_Hash_Base.wasm +0 -0
- package/hacl-wasm/Hacl_Hash_SHA2.wasm +0 -0
- package/hacl-wasm/Hacl_IntTypes_Intrinsics.wasm +0 -0
- package/hacl-wasm/LowStar_Endianness.wasm +0 -0
- package/hacl-wasm/WasmSupport.wasm +0 -0
- package/hacl-wasm/api.js +0 -775
- package/hacl-wasm/api.json +0 -3787
- package/hacl-wasm/layouts.json +0 -1
- package/hacl-wasm/loader.js +0 -568
- package/hacl-wasm/shell.js +0 -12
- package/l4-core.d.ts +0 -159
- package/l4-core.js +0 -188
- package/l4.d.ts +0 -50
- package/l4.js +0 -104
- package/lib/api.js +0 -2300
- package/lib/engine-js.js +0 -165
- package/lib/engine-wasm.js +0 -300
- package/sample-store/CURRENT +0 -1
- package/sample-store/gen-1/manifest.sbm2 +0 -0
- package/sample-store/gen-1/manifest.tsv +0 -14
- package/sample-store/gen-1/predicate-0.ibk3 +0 -0
- package/sample-store/gen-1/predicate-0.ibk3.merkle +0 -1
- package/sample-store/gen-1/predicate-0.ibk3.oli2 +0 -0
- package/sample-store/gen-1/predicate-0.ibk3.oli2.merkle +0 -1
- package/sample-store/gen-1/predicate-0.ibk3.sri2 +0 -0
- package/sample-store/gen-1/predicate-0.ibk3.sri2.merkle +0 -1
- package/sample-store/gen-1/predicate-0.ibk3.tli1 +0 -0
- package/sample-store/gen-1/predicate-0.ibk3.tli1.merkle +0 -0
- package/sample-store/gen-1/predicate-1.ibk3 +0 -0
- package/sample-store/gen-1/predicate-1.ibk3.merkle +0 -0
- package/sample-store/gen-1/predicate-1.ibk3.oli2 +0 -0
- package/sample-store/gen-1/predicate-1.ibk3.oli2.merkle +0 -0
- package/sample-store/gen-1/predicate-1.ibk3.sri2 +0 -0
- package/sample-store/gen-1/predicate-1.ibk3.sri2.merkle +0 -1
- package/sample-store/gen-1/predicate-1.ibk3.tli1 +0 -0
- package/sample-store/gen-1/predicate-1.ibk3.tli1.merkle +0 -1
- package/sample-store/gen-1/predicate-10.ibk3 +0 -0
- package/sample-store/gen-1/predicate-10.ibk3.merkle +0 -1
- package/sample-store/gen-1/predicate-10.ibk3.oli2 +0 -0
- package/sample-store/gen-1/predicate-10.ibk3.oli2.merkle +0 -0
- package/sample-store/gen-1/predicate-10.ibk3.sri2 +0 -0
- package/sample-store/gen-1/predicate-10.ibk3.sri2.merkle +0 -1
- package/sample-store/gen-1/predicate-10.ibk3.tli1 +0 -0
- package/sample-store/gen-1/predicate-10.ibk3.tli1.merkle +0 -1
- package/sample-store/gen-1/predicate-11.ibk3 +0 -0
- package/sample-store/gen-1/predicate-11.ibk3.merkle +0 -0
- package/sample-store/gen-1/predicate-11.ibk3.oli2 +0 -0
- package/sample-store/gen-1/predicate-11.ibk3.oli2.merkle +0 -1
- package/sample-store/gen-1/predicate-11.ibk3.sri2 +0 -0
- package/sample-store/gen-1/predicate-11.ibk3.sri2.merkle +0 -1
- package/sample-store/gen-1/predicate-11.ibk3.tli1 +0 -0
- package/sample-store/gen-1/predicate-11.ibk3.tli1.merkle +0 -1
- package/sample-store/gen-1/predicate-12.ibk3 +0 -0
- package/sample-store/gen-1/predicate-12.ibk3.merkle +0 -1
- package/sample-store/gen-1/predicate-12.ibk3.oli2 +0 -0
- package/sample-store/gen-1/predicate-12.ibk3.oli2.merkle +0 -1
- package/sample-store/gen-1/predicate-12.ibk3.sri2 +0 -0
- package/sample-store/gen-1/predicate-12.ibk3.sri2.merkle +0 -1
- package/sample-store/gen-1/predicate-12.ibk3.tli1 +0 -0
- package/sample-store/gen-1/predicate-12.ibk3.tli1.merkle +0 -1
- package/sample-store/gen-1/predicate-2.ibk3 +0 -0
- package/sample-store/gen-1/predicate-2.ibk3.merkle +0 -1
- package/sample-store/gen-1/predicate-2.ibk3.oli2 +0 -0
- package/sample-store/gen-1/predicate-2.ibk3.oli2.merkle +0 -1
- package/sample-store/gen-1/predicate-2.ibk3.sri2 +0 -0
- package/sample-store/gen-1/predicate-2.ibk3.sri2.merkle +0 -0
- package/sample-store/gen-1/predicate-2.ibk3.tli1 +0 -0
- package/sample-store/gen-1/predicate-2.ibk3.tli1.merkle +0 -0
- package/sample-store/gen-1/predicate-3.ibk3 +0 -0
- package/sample-store/gen-1/predicate-3.ibk3.merkle +0 -1
- package/sample-store/gen-1/predicate-3.ibk3.oli2 +0 -0
- package/sample-store/gen-1/predicate-3.ibk3.oli2.merkle +0 -1
- package/sample-store/gen-1/predicate-3.ibk3.sri2 +0 -0
- package/sample-store/gen-1/predicate-3.ibk3.sri2.merkle +0 -1
- package/sample-store/gen-1/predicate-3.ibk3.tli1 +0 -0
- package/sample-store/gen-1/predicate-3.ibk3.tli1.merkle +0 -1
- package/sample-store/gen-1/predicate-4.ibk3 +0 -0
- package/sample-store/gen-1/predicate-4.ibk3.merkle +0 -0
- package/sample-store/gen-1/predicate-4.ibk3.oli2 +0 -0
- package/sample-store/gen-1/predicate-4.ibk3.oli2.merkle +0 -1
- package/sample-store/gen-1/predicate-4.ibk3.sri2 +0 -0
- package/sample-store/gen-1/predicate-4.ibk3.sri2.merkle +0 -1
- package/sample-store/gen-1/predicate-4.ibk3.tli1 +0 -0
- package/sample-store/gen-1/predicate-4.ibk3.tli1.merkle +0 -2
- package/sample-store/gen-1/predicate-5.ibk3 +0 -0
- package/sample-store/gen-1/predicate-5.ibk3.merkle +0 -1
- package/sample-store/gen-1/predicate-5.ibk3.oli2 +0 -0
- package/sample-store/gen-1/predicate-5.ibk3.oli2.merkle +0 -1
- package/sample-store/gen-1/predicate-5.ibk3.sri2 +0 -0
- package/sample-store/gen-1/predicate-5.ibk3.sri2.merkle +0 -1
- package/sample-store/gen-1/predicate-5.ibk3.tli1 +0 -0
- package/sample-store/gen-1/predicate-5.ibk3.tli1.merkle +0 -1
- package/sample-store/gen-1/predicate-6.ibk3 +0 -0
- package/sample-store/gen-1/predicate-6.ibk3.merkle +0 -1
- package/sample-store/gen-1/predicate-6.ibk3.oli2 +0 -0
- package/sample-store/gen-1/predicate-6.ibk3.oli2.merkle +0 -1
- package/sample-store/gen-1/predicate-6.ibk3.sri2 +0 -0
- package/sample-store/gen-1/predicate-6.ibk3.sri2.merkle +0 -1
- package/sample-store/gen-1/predicate-6.ibk3.tli1 +0 -0
- package/sample-store/gen-1/predicate-6.ibk3.tli1.merkle +0 -0
- package/sample-store/gen-1/predicate-7.ibk3 +0 -0
- package/sample-store/gen-1/predicate-7.ibk3.merkle +0 -0
- package/sample-store/gen-1/predicate-7.ibk3.oli2 +0 -0
- package/sample-store/gen-1/predicate-7.ibk3.oli2.merkle +0 -2
- package/sample-store/gen-1/predicate-7.ibk3.sri2 +0 -0
- package/sample-store/gen-1/predicate-7.ibk3.sri2.merkle +0 -1
- package/sample-store/gen-1/predicate-7.ibk3.tli1 +0 -0
- package/sample-store/gen-1/predicate-7.ibk3.tli1.merkle +0 -1
- package/sample-store/gen-1/predicate-8.ibk3 +0 -0
- package/sample-store/gen-1/predicate-8.ibk3.merkle +0 -1
- package/sample-store/gen-1/predicate-8.ibk3.oli2 +0 -0
- package/sample-store/gen-1/predicate-8.ibk3.oli2.merkle +0 -1
- package/sample-store/gen-1/predicate-8.ibk3.sri2 +0 -0
- package/sample-store/gen-1/predicate-8.ibk3.sri2.merkle +0 -1
- package/sample-store/gen-1/predicate-8.ibk3.tli1 +0 -0
- package/sample-store/gen-1/predicate-8.ibk3.tli1.merkle +0 -1
- package/sample-store/gen-1/predicate-9.ibk3 +0 -0
- package/sample-store/gen-1/predicate-9.ibk3.merkle +0 -1
- package/sample-store/gen-1/predicate-9.ibk3.oli2 +0 -0
- package/sample-store/gen-1/predicate-9.ibk3.oli2.merkle +0 -1
- package/sample-store/gen-1/predicate-9.ibk3.sri2 +0 -0
- package/sample-store/gen-1/predicate-9.ibk3.sri2.merkle +0 -3
- package/sample-store/gen-1/predicate-9.ibk3.tli1 +0 -0
- package/sample-store/gen-1/predicate-9.ibk3.tli1.merkle +0 -2
- package/select.d.ts +0 -116
- package/select.js +0 -492
- package/version.json +0 -102
- package/wasm.d.ts +0 -130
- package/wasm.js +0 -158
package/bin/factoidal.mjs
CHANGED
|
@@ -24,10 +24,10 @@ import {
|
|
|
24
24
|
runtime
|
|
25
25
|
} from '../store-host/index.mjs'
|
|
26
26
|
import { fileUrlToPath, joinPath } from '../store-host/paths.mjs'
|
|
27
|
-
import { loadEngine } from './engine.mjs'
|
|
28
|
-
import { sampleStoreFacts, sampleStorePath } from '../sample-store.mjs'
|
|
27
|
+
import { hexOfBytes, loadEngine } from './engine.mjs'
|
|
29
28
|
import { PackError, packSupported, verifyGeneration } from './pack.mjs'
|
|
30
29
|
import { denoReexec, isStackOverflow, runPack } from './pack-host.mjs'
|
|
30
|
+
import { SAMPLE_TURTLE, sampleStoreFacts } from '../sample-store.mjs'
|
|
31
31
|
import {
|
|
32
32
|
planQueryOnWorker, queryStoreOnWorker, workerRouteAvailable
|
|
33
33
|
} from './store-worker-host.mjs'
|
|
@@ -91,7 +91,8 @@ function exit (code) {
|
|
|
91
91
|
else process.exitCode = code
|
|
92
92
|
}
|
|
93
93
|
|
|
94
|
-
|
|
94
|
+
let outSilenced = false
|
|
95
|
+
function out (line) { if (!outSilenced) console.log(line) }
|
|
95
96
|
function err (line) { console.error(line) }
|
|
96
97
|
|
|
97
98
|
// ---------------------------------------------------------------- usage
|
|
@@ -102,13 +103,18 @@ usage: factoidal <command> [options]
|
|
|
102
103
|
|
|
103
104
|
commands:
|
|
104
105
|
version print the package and engine versions
|
|
105
|
-
sample-store
|
|
106
|
+
sample-store [DIR] pack a small built-in store and print its path
|
|
106
107
|
inspect STORE report what the activated manifest commits
|
|
107
108
|
query STORE [QUERY] evaluate a SPARQL query against a store
|
|
108
109
|
pack INPUT OUTPUT build one immutable generation from an RDF file
|
|
109
110
|
activate STORE GENERATION make one generation the activated generation
|
|
110
111
|
update STORE [UPDATE] apply a SPARQL Update through the delta log
|
|
111
112
|
compact STORE GENERATION fold the delta log into a new generation
|
|
113
|
+
serve STORE serve SPARQL 1.1 Protocol, Graph Store and
|
|
114
|
+
Service Description over HTTP
|
|
115
|
+
lws-serve DIR serve Linked Web Storage 1.0 over HTTP
|
|
116
|
+
solid-serve DIR serve the Solid Protocol over HTTP
|
|
117
|
+
solid-client OP URL run one Solid client operation
|
|
112
118
|
|
|
113
119
|
global options:
|
|
114
120
|
-h, --help print this text, or a command's own help
|
|
@@ -118,33 +124,30 @@ global options:
|
|
|
118
124
|
exit codes:
|
|
119
125
|
0 success 1 failure 2 usage error 3 not yet wired (${ISSUE})
|
|
120
126
|
|
|
121
|
-
STORE is a collection root: the directory that holds CURRENT.
|
|
122
|
-
|
|
127
|
+
STORE is a collection root: the directory that holds CURRENT. Make one from
|
|
128
|
+
an RDF file with pack and activate, or try a small built-in one:
|
|
123
129
|
|
|
124
130
|
factoidal query "$(factoidal sample-store)" 'SELECT * WHERE { ?s ?p ?o } LIMIT 5'`
|
|
125
131
|
|
|
126
132
|
const COMMAND_USAGE = {
|
|
127
|
-
|
|
133
|
+
'sample-store': `factoidal sample-store - pack a small built-in store, print its path
|
|
128
134
|
|
|
129
|
-
usage: factoidal
|
|
135
|
+
usage: factoidal sample-store [DIR] [--json]
|
|
130
136
|
|
|
131
|
-
|
|
132
|
-
|
|
137
|
+
Packs the few triples held in sample-store.mjs (no data file ships) into a
|
|
138
|
+
store under DIR, or under the OS temporary directory, activates it, and
|
|
139
|
+
prints the collection root. A DIR that already holds CURRENT is reused.
|
|
133
140
|
|
|
134
|
-
|
|
141
|
+
factoidal query "$(factoidal sample-store)" \\
|
|
142
|
+
'SELECT ?c ?l WHERE { ?c <http://www.w3.org/2004/02/skos/core#prefLabel> ?l }'`,
|
|
135
143
|
|
|
136
|
-
|
|
144
|
+
version: `factoidal version - print the package and engine versions
|
|
137
145
|
|
|
138
|
-
|
|
139
|
-
carries, so a fresh install can query something at once:
|
|
146
|
+
usage: factoidal version [--json]
|
|
140
147
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
'SELECT (COUNT(*) AS ?n) WHERE { ?s ?p ?o }'
|
|
148
|
+
Prints the npm package version, the Lean engine's WebAssembly digest as
|
|
149
|
+
recorded by its build, and which host-I/O implementation is loaded.`,
|
|
144
150
|
|
|
145
|
-
The store holds five IPTC NewsCodes vocabularies (CC BY 4.0; see NOTICE)
|
|
146
|
-
packed into IBK3 predicate blocks. --json adds what was recorded when it
|
|
147
|
-
was packed.`,
|
|
148
151
|
|
|
149
152
|
inspect: `factoidal inspect - report what a store's manifest commits
|
|
150
153
|
|
|
@@ -200,14 +203,16 @@ every later query from what it retained.
|
|
|
200
203
|
|
|
201
204
|
formats:
|
|
202
205
|
table a human display of the engine's SPARQL Query Results JSON;
|
|
203
|
-
ASK prints true or false, CONSTRUCT
|
|
206
|
+
ASK prints true or false, CONSTRUCT and DESCRIBE print their
|
|
207
|
+
N-Triples
|
|
204
208
|
json SELECT prints the engine's SPARQL 1.1 Query Results JSON
|
|
205
|
-
document; ASK and
|
|
206
|
-
because the operation answers those
|
|
207
|
-
serialized graph rather than with a results document
|
|
208
|
-
nquads CONSTRUCT only: the graph the engine serialized
|
|
209
|
-
turtle CONSTRUCT only: that graph through the engine's
|
|
210
|
-
writer, which flattens named graphs into the default
|
|
209
|
+
document; ASK, CONSTRUCT and DESCRIBE print the operation's
|
|
210
|
+
envelope, because the operation answers those with a boolean
|
|
211
|
+
or a serialized graph rather than with a results document
|
|
212
|
+
nquads CONSTRUCT and DESCRIBE only: the graph the engine serialized
|
|
213
|
+
turtle CONSTRUCT and DESCRIBE only: that graph through the engine's
|
|
214
|
+
own Turtle writer, which flattens named graphs into the default
|
|
215
|
+
graph
|
|
211
216
|
|
|
212
217
|
not available, and why:
|
|
213
218
|
--base IRI the store query operation takes no base argument;
|
|
@@ -215,8 +220,6 @@ not available, and why:
|
|
|
215
220
|
xml, csv, tsv the engine has no operation that writes the SPARQL
|
|
216
221
|
Results XML, CSV or TSV documents, and writing one
|
|
217
222
|
here would be a second serializer (iron rule 7)
|
|
218
|
-
DESCRIBE the engine answers "DESCRIBE is not supported by the
|
|
219
|
-
npm entry yet"
|
|
220
223
|
|
|
221
224
|
A store carrying a non-empty delta log is not served by this path: the
|
|
222
225
|
operation reads the manifest's committed artifacts only. Use the native
|
|
@@ -279,7 +282,137 @@ compacted epoch. It does not activate unless --activate is given.
|
|
|
279
282
|
|
|
280
283
|
options:
|
|
281
284
|
--activate activate the new generation when it verifies
|
|
282
|
-
--json emit one JSON object
|
|
285
|
+
--json emit one JSON object`,
|
|
286
|
+
|
|
287
|
+
serve: `factoidal serve - a SPARQL 1.1 endpoint over an activated store
|
|
288
|
+
|
|
289
|
+
usage: factoidal serve STORE [--port N] [--host ADDRESS] [options]
|
|
290
|
+
factoidal serve --file DATA /DATASET [--port N] [options]
|
|
291
|
+
factoidal serve --tdb2 DIR /DATASET [--port N] [options]
|
|
292
|
+
|
|
293
|
+
The second form matches fuseki-server --file DATA /DATASET: DATA is packed
|
|
294
|
+
into a temporary collection (layout ibk5 unless --layout is given), served
|
|
295
|
+
at /DATASET/sparql (also /DATASET/query and /DATASET/update) and
|
|
296
|
+
/DATASET/data (also /DATASET/get, read only). Updates are lost on exit.
|
|
297
|
+
|
|
298
|
+
The third form matches fuseki-server --loc DIR /DATASET for an Apache Jena
|
|
299
|
+
TDB2 database: DIR is read in place, read only. SPARQL query and Graph Store
|
|
300
|
+
GET work; an update or a Graph Store write answers 501 with a message. The
|
|
301
|
+
current Data-NNNN generation is served; a database whose journal.jrnl is not
|
|
302
|
+
empty (Jena has not replayed it) is refused. The Lean decoder reads the
|
|
303
|
+
indexes and node table (formal/lean4/L4Factoidal/Tdb2/Read.lean); every
|
|
304
|
+
quad is held in memory while the server runs.
|
|
305
|
+
|
|
306
|
+
Serves the activated generation of STORE, with its delta log, over HTTP:
|
|
307
|
+
|
|
308
|
+
GET/POST /sparql SPARQL 1.1 Protocol query and update
|
|
309
|
+
(GET /sparql with no query string: the SPARQL 1.1
|
|
310
|
+
Service Description of the endpoint)
|
|
311
|
+
/data, /data/... SPARQL 1.1 Graph Store HTTP Protocol (?default,
|
|
312
|
+
?graph=IRI, or a path under /data naming the graph)
|
|
313
|
+
|
|
314
|
+
options:
|
|
315
|
+
--port N the port to bind; 0 takes an ephemeral one (default 3030)
|
|
316
|
+
--host ADDRESS the address to bind (default 127.0.0.1)
|
|
317
|
+
--query-path PATH the query and update endpoint (default /sparql)
|
|
318
|
+
--gsp-path PATH the Graph Store endpoint (default /data)
|
|
319
|
+
--file DATA pack and serve an RDF file (see the second form)
|
|
320
|
+
--tdb2 DIR serve a Jena TDB2 database read only (third form)
|
|
321
|
+
--layout LAYOUT with --file: ibk3, ibk4 or ibk5 (default ibk5)
|
|
322
|
+
--no-worker serve on this thread instead of a worker thread with a
|
|
323
|
+
raised call stack (large collections need the worker)
|
|
324
|
+
--quiet print the origin only
|
|
325
|
+
|
|
326
|
+
An update, or a Graph Store PUT, POST or DELETE, is appended to the
|
|
327
|
+
activated generation's delta log as one batch before the response is sent,
|
|
328
|
+
and is visible to the next request. An IBK3 (triples) generation accepts
|
|
329
|
+
changes to the default graph only, because its delta log is replayed and
|
|
330
|
+
compacted for that graph only; a change to a named graph answers 501. A
|
|
331
|
+
quad generation (ibk4, ibk5) accepts every graph. Fold the log into a new
|
|
332
|
+
generation with l4block-shard-compact, activate it, and restart the server.
|
|
333
|
+
|
|
334
|
+
The server makes no protocol decision: the request framing, routing,
|
|
335
|
+
status codes, content negotiation, evaluation and the delta-log batch are
|
|
336
|
+
decided by formal/lean4/L4Factoidal/HTTP/Wire.lean and
|
|
337
|
+
formal/lean4/L4Factoidal/SPARQL/StoreEndpoint.lean through the
|
|
338
|
+
storeServe operations. A WebAssembly module built before those operations
|
|
339
|
+
landed makes this command exit 3 and say so.
|
|
340
|
+
|
|
341
|
+
Specifications: https://www.w3.org/TR/sparql11-protocol/
|
|
342
|
+
https://www.w3.org/TR/sparql11-http-rdf-update/
|
|
343
|
+
https://www.w3.org/TR/sparql11-service-description/`,
|
|
344
|
+
|
|
345
|
+
'lws-serve': `factoidal lws-serve - serve Linked Web Storage 1.0 over HTTP
|
|
346
|
+
|
|
347
|
+
usage: factoidal lws-serve DIR [--port N] [--host ADDRESS]
|
|
348
|
+
|
|
349
|
+
Starts a Node HTTP server that answers every request through the engine's
|
|
350
|
+
lwsStep operation.
|
|
351
|
+
|
|
352
|
+
DIR names the storage directory. The first slice keeps the resource tree
|
|
353
|
+
in the engine handle, so DIR is not read or written yet; it is the place
|
|
354
|
+
a later snapshot goes (design record, section 2).
|
|
355
|
+
|
|
356
|
+
--port N the port to bind; 0 takes an ephemeral one (default 3000)
|
|
357
|
+
--host ADDRESS the address to bind (default 127.0.0.1)
|
|
358
|
+
--base IRI the absolute IRI the storage root maps to
|
|
359
|
+
(default: the bound origin)
|
|
360
|
+
|
|
361
|
+
The server makes no protocol decision: status codes, Link relations,
|
|
362
|
+
Last-Modified and the PATCH blank-node refusal are all answered by
|
|
363
|
+
formal/lean4/L4Factoidal/LWS/. A WebAssembly module built before those
|
|
364
|
+
operations landed makes this command exit 3 and say so.
|
|
365
|
+
|
|
366
|
+
Specification: https://w3c.github.io/lws-protocol/lws10-core/`,
|
|
367
|
+
|
|
368
|
+
'solid-serve': `factoidal solid-serve - serve the Solid Protocol over HTTP
|
|
369
|
+
|
|
370
|
+
usage: factoidal solid-serve DIR [--port N] [--host ADDRESS]
|
|
371
|
+
|
|
372
|
+
Starts a Node HTTP server that answers every request through the engine's
|
|
373
|
+
solidStep operation.
|
|
374
|
+
|
|
375
|
+
DIR names the storage directory. The first slice keeps the resource tree
|
|
376
|
+
in the engine handle, so DIR is not read or written yet; it is the place
|
|
377
|
+
a later snapshot goes (design record, section 2).
|
|
378
|
+
|
|
379
|
+
--port N the port to bind; 0 takes an ephemeral one (default 3000)
|
|
380
|
+
--host ADDRESS the address to bind (default 127.0.0.1)
|
|
381
|
+
--base IRI the absolute IRI the storage root maps to
|
|
382
|
+
(default: the bound origin)
|
|
383
|
+
--owner WEBID the WebID the storage advertises as its owner
|
|
384
|
+
|
|
385
|
+
The server makes no protocol decision. Storage discovery, containment
|
|
386
|
+
triples, slash semantics, the Allow and Accept-* headers, the PUT/POST/
|
|
387
|
+
PATCH/DELETE rules, the acl and describedby lifecycle, N3 Patch, Web
|
|
388
|
+
Access Control, CORS and the LDN inbox are answered by
|
|
389
|
+
formal/lean4/L4Factoidal/Solid/Server/. A WebAssembly module built before
|
|
390
|
+
those operations landed makes this command exit 3 and say so.
|
|
391
|
+
|
|
392
|
+
Specification: https://solidproject.org/TR/protocol`,
|
|
393
|
+
|
|
394
|
+
'solid-client': `factoidal solid-client - run one Solid client operation
|
|
395
|
+
|
|
396
|
+
usage: factoidal solid-client <get|put|post|delete|discover> URL [options]
|
|
397
|
+
|
|
398
|
+
Every request is built by the engine's solidClientRequest operation and
|
|
399
|
+
every response is read by solidClientResponse. This command supplies a
|
|
400
|
+
socket and prints what the engine answered.
|
|
401
|
+
|
|
402
|
+
The five verbs are aliases for the operation kinds of the wasm ABI; the
|
|
403
|
+
kind names are accepted directly as well.
|
|
404
|
+
|
|
405
|
+
get read put replace post create
|
|
406
|
+
delete delete discover discoverStorage
|
|
407
|
+
(also: patch, readProfile)
|
|
408
|
+
|
|
409
|
+
--body TEXT the request body for put, post and patch
|
|
410
|
+
--file PATH read the request body from a file
|
|
411
|
+
--content-type M the body's media type
|
|
412
|
+
--interpret KIND one of storage, containment, auxiliaries, profile,
|
|
413
|
+
wacAllow, used when the engine names none
|
|
414
|
+
|
|
415
|
+
Specification: https://solidproject.org/TR/protocol`
|
|
283
416
|
}
|
|
284
417
|
|
|
285
418
|
// ------------------------------------------------------------- arguments
|
|
@@ -334,7 +467,11 @@ const VALUE_OPTIONS = {
|
|
|
334
467
|
pack: new Set(['layout', 'syntax', 'chunk-bytes', 'base', 'batch-bytes']),
|
|
335
468
|
activate: new Set([]),
|
|
336
469
|
update: new Set(['update', 'file']),
|
|
337
|
-
compact: new Set([])
|
|
470
|
+
compact: new Set([]),
|
|
471
|
+
serve: new Set(['port', 'host', 'query-path', 'gsp-path', 'file', 'layout', 'tdb2']),
|
|
472
|
+
'lws-serve': new Set(['port', 'host', 'base']),
|
|
473
|
+
'solid-serve': new Set(['port', 'host', 'base', 'owner']),
|
|
474
|
+
'solid-client': new Set(['body', 'file', 'content-type', 'interpret'])
|
|
338
475
|
}
|
|
339
476
|
|
|
340
477
|
// ------------------------------------------------------------- commands
|
|
@@ -348,55 +485,115 @@ function readJsonFile (path) {
|
|
|
348
485
|
return JSON.parse(new TextDecoder('utf-8', { fatal: true }).decode(bytes))
|
|
349
486
|
}
|
|
350
487
|
|
|
351
|
-
function
|
|
488
|
+
async function sha256Hex (bytes) {
|
|
489
|
+
const digest = await globalThis.crypto.subtle.digest('SHA-256', bytes)
|
|
490
|
+
return hexOfBytes(new Uint8Array(digest))
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
// `factoidal version`: what l4-assets/version.json says (the release stamp,
|
|
494
|
+
// written by tools/fetch-builds.sh --set web from the factoidal-builds
|
|
495
|
+
// release that built the wasm), what the installed wasm files hash to, and
|
|
496
|
+
// the loaded engine's own version(). A stamp whose sha256 differs from the
|
|
497
|
+
// installed wasm is an error (exit 1): the stamp then describes another
|
|
498
|
+
// build. A source checkout carries the unstamped placeholder.
|
|
499
|
+
async function commandVersion (options) {
|
|
352
500
|
const root = packageDirectory()
|
|
353
501
|
const own = readJsonFile(joinPath(root, 'package.json'))
|
|
354
|
-
let
|
|
502
|
+
let stamp = null
|
|
355
503
|
try {
|
|
356
|
-
|
|
504
|
+
stamp = readJsonFile(joinPath(root, 'l4-assets/version.json'))
|
|
357
505
|
} catch (_error) {
|
|
358
|
-
|
|
506
|
+
stamp = null
|
|
507
|
+
}
|
|
508
|
+
const stamped = stamp !== null && stamp.stamped === true
|
|
509
|
+
const installed = {}
|
|
510
|
+
for (const [key, file] of [['wasm', 'l4factoidal.wasm']]) {
|
|
511
|
+
try {
|
|
512
|
+
const bytes = readWhole(joinPath(root, `l4-assets/${file}`))
|
|
513
|
+
installed[key] = { sha256: await sha256Hex(bytes), bytes: bytes.length }
|
|
514
|
+
} catch (_error) {
|
|
515
|
+
installed[key] = null
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
let engineVersion = null
|
|
519
|
+
let engineError = null
|
|
520
|
+
if (installed.wasm !== null) {
|
|
521
|
+
try {
|
|
522
|
+
engineVersion = (await loadEngine()).version()
|
|
523
|
+
} catch (error) {
|
|
524
|
+
engineError = String(error && error.message ? error.message : error)
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
const mismatches = []
|
|
528
|
+
if (stamped) {
|
|
529
|
+
for (const [key, shaKey, bytesKey] of [['wasm', 'wasmSha256', 'wasmBytes']]) {
|
|
530
|
+
if (installed[key] === null) continue
|
|
531
|
+
if (stamp[shaKey] !== installed[key].sha256 || stamp[bytesKey] !== installed[key].bytes) {
|
|
532
|
+
mismatches.push(`${key}: version.json says sha256 ${stamp[shaKey]} (${stamp[bytesKey]} bytes), ` +
|
|
533
|
+
`installed file is ${installed[key].sha256} (${installed[key].bytes} bytes)`)
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
if (engineVersion !== null && typeof stamp.engineVersion === 'string' &&
|
|
537
|
+
stamp.engineVersion !== engineVersion) {
|
|
538
|
+
mismatches.push(`engine: version.json says ${stamp.engineVersion}, the loaded wasm says ${engineVersion}`)
|
|
539
|
+
}
|
|
359
540
|
}
|
|
360
541
|
const report = {
|
|
361
542
|
package: own.name,
|
|
362
543
|
version: own.version,
|
|
363
544
|
host: runtime,
|
|
364
|
-
|
|
545
|
+
stamp: stamp === null
|
|
365
546
|
? null
|
|
366
|
-
:
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
547
|
+
: stamped
|
|
548
|
+
? {
|
|
549
|
+
stamped: true,
|
|
550
|
+
engineVersion: stamp.engineVersion,
|
|
551
|
+
gitSha: stamp.gitSha,
|
|
552
|
+
builtAt: stamp.builtAt,
|
|
553
|
+
release: stamp.release,
|
|
554
|
+
leanToolchain: stamp.leanToolchain,
|
|
555
|
+
wasmSha256: stamp.wasmSha256,
|
|
556
|
+
wasmBytes: stamp.wasmBytes
|
|
557
|
+
}
|
|
558
|
+
: { stamped: false, status: stamp.status || 'stamped at release' },
|
|
559
|
+
installed,
|
|
560
|
+
engineVersion,
|
|
561
|
+
engineError,
|
|
562
|
+
mismatches
|
|
374
563
|
}
|
|
375
564
|
if (options.json) {
|
|
376
565
|
out(JSON.stringify(report, null, 2))
|
|
377
|
-
return EXIT_OK
|
|
378
|
-
}
|
|
379
|
-
out(`${report.package} ${report.version}`)
|
|
380
|
-
out(`host-io ${report.host}`)
|
|
381
|
-
if (report.engine !== null) {
|
|
382
|
-
out(`engine ${report.engine.engine} ${report.engine.version} (${report.engine.leanToolchain})`)
|
|
383
|
-
out(`wasm sha256 ${report.engine.wasmSha256} (${report.engine.wasmBytes} bytes)`)
|
|
384
566
|
} else {
|
|
385
|
-
out(
|
|
567
|
+
out(`${report.package} ${report.version}`)
|
|
568
|
+
out(`host-io ${report.host}`)
|
|
569
|
+
if (stamp === null) {
|
|
570
|
+
out('stamp: no l4-assets/version.json in this install')
|
|
571
|
+
} else if (!stamped) {
|
|
572
|
+
out('stamp: not stamped (l4-assets/version.json is the source placeholder; ' +
|
|
573
|
+
'it is stamped at release by tools/fetch-builds.sh --set web)')
|
|
574
|
+
} else {
|
|
575
|
+
out(`stamp: built from ${stamp.gitSha} at ${stamp.builtAt}` +
|
|
576
|
+
(stamp.release ? ` (${stamp.release})` : ''))
|
|
577
|
+
out(`stamp: engine ${stamp.engineVersion} (${stamp.leanToolchain})`)
|
|
578
|
+
out(`stamp: wasm sha256 ${stamp.wasmSha256} (${stamp.wasmBytes} bytes)`)
|
|
579
|
+
}
|
|
580
|
+
for (const [key, label] of [['wasm', 'wasm']]) {
|
|
581
|
+
out(installed[key] === null
|
|
582
|
+
? `installed ${label}: absent`
|
|
583
|
+
: `installed ${label} sha256 ${installed[key].sha256} (${installed[key].bytes} bytes)`)
|
|
584
|
+
}
|
|
585
|
+
out(engineVersion !== null
|
|
586
|
+
? `engine version() ${engineVersion}`
|
|
587
|
+
: `engine version() unavailable${engineError ? ': ' + engineError : ''}`)
|
|
386
588
|
}
|
|
387
|
-
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
function commandSampleStore (options) {
|
|
391
|
-
const path = sampleStorePath()
|
|
392
|
-
if (options.json === true) {
|
|
393
|
-
out(JSON.stringify({ path, ...sampleStoreFacts }, null, 2))
|
|
394
|
-
return EXIT_OK
|
|
589
|
+
if (mismatches.length > 0) {
|
|
590
|
+
for (const m of mismatches) err(`factoidal version: STAMP MISMATCH ${m}`)
|
|
591
|
+
return EXIT_FAILURE
|
|
395
592
|
}
|
|
396
|
-
out(path)
|
|
397
593
|
return EXIT_OK
|
|
398
594
|
}
|
|
399
595
|
|
|
596
|
+
|
|
400
597
|
// ------------------------------------------------------------ rendering
|
|
401
598
|
|
|
402
599
|
/**
|
|
@@ -674,7 +871,7 @@ function renderQueryResult (engine, result, format, limit, quiet) {
|
|
|
674
871
|
return EXIT_OK
|
|
675
872
|
}
|
|
676
873
|
if (format !== 'table') {
|
|
677
|
-
err(`factoidal query: --format ${format} needs a CONSTRUCT query; this one is a SELECT`)
|
|
874
|
+
err(`factoidal query: --format ${format} needs a CONSTRUCT or DESCRIBE query; this one is a SELECT`)
|
|
678
875
|
return EXIT_FAILURE
|
|
679
876
|
}
|
|
680
877
|
const shown = limit === null ? bindings : bindings.slice(0, limit)
|
|
@@ -694,13 +891,13 @@ function renderQueryResult (engine, result, format, limit, quiet) {
|
|
|
694
891
|
return EXIT_OK
|
|
695
892
|
}
|
|
696
893
|
if (format !== 'table') {
|
|
697
|
-
err(`factoidal query: --format ${format} needs a CONSTRUCT query; this one is an ASK`)
|
|
894
|
+
err(`factoidal query: --format ${format} needs a CONSTRUCT or DESCRIBE query; this one is an ASK`)
|
|
698
895
|
return EXIT_FAILURE
|
|
699
896
|
}
|
|
700
897
|
out(result.boolean ? 'true' : 'false')
|
|
701
898
|
return EXIT_OK
|
|
702
899
|
}
|
|
703
|
-
if (result.kind === 'construct') {
|
|
900
|
+
if (result.kind === 'construct' || result.kind === 'describe') {
|
|
704
901
|
if (format === 'json') {
|
|
705
902
|
out(JSON.stringify(result, null, 2))
|
|
706
903
|
return EXIT_OK
|
|
@@ -835,6 +1032,34 @@ async function commandPack (positional, options) {
|
|
|
835
1032
|
return EXIT_OK
|
|
836
1033
|
}
|
|
837
1034
|
|
|
1035
|
+
async function commandSampleStore (positional, options) {
|
|
1036
|
+
if (positional.length > 1) throw new UsageError('sample-store takes at most one DIR')
|
|
1037
|
+
const { mkdtempSync, writeFileSync, existsSync } = await import('node:fs')
|
|
1038
|
+
const { tmpdir } = await import('node:os')
|
|
1039
|
+
const root = positional.length === 1
|
|
1040
|
+
? positional[0]
|
|
1041
|
+
: mkdtempSync(joinPath(tmpdir(), 'factoidal-sample-store-'))
|
|
1042
|
+
if (!existsSync(joinPath(root, 'CURRENT'))) {
|
|
1043
|
+
makeDirectory(joinPath(root, 'gen-1'))
|
|
1044
|
+
const source = joinPath(root, 'sample.ttl')
|
|
1045
|
+
writeFileSync(source, SAMPLE_TURTLE)
|
|
1046
|
+
outSilenced = true
|
|
1047
|
+
try {
|
|
1048
|
+
let code = await commandPack([source, joinPath(root, 'gen-1')], { quiet: true })
|
|
1049
|
+
if (code === EXIT_OK) code = await commandActivate([root, 'gen-1'], { quiet: true })
|
|
1050
|
+
if (code !== EXIT_OK) return code
|
|
1051
|
+
} finally {
|
|
1052
|
+
outSilenced = false
|
|
1053
|
+
}
|
|
1054
|
+
}
|
|
1055
|
+
if (options.json === true) {
|
|
1056
|
+
out(JSON.stringify({ path: root, ...sampleStoreFacts }, null, 2))
|
|
1057
|
+
} else {
|
|
1058
|
+
out(root)
|
|
1059
|
+
}
|
|
1060
|
+
return EXIT_OK
|
|
1061
|
+
}
|
|
1062
|
+
|
|
838
1063
|
async function commandActivate (positional, options) {
|
|
839
1064
|
if (positional.length !== 2) throw new UsageError('activate needs STORE and GENERATION')
|
|
840
1065
|
const [root, generation] = positional
|
|
@@ -906,15 +1131,236 @@ function commandCompact (positional, _options) {
|
|
|
906
1131
|
return notWired('compact', 'Compaction is stage 4 of the milestone.')
|
|
907
1132
|
}
|
|
908
1133
|
|
|
1134
|
+
// ------------------------------------------------- the protocol servers
|
|
1135
|
+
// These commands hand a socket to a host module and print where it is
|
|
1136
|
+
// listening. They decide nothing about the protocol they serve; see
|
|
1137
|
+
// npm/factoidal/lws/README.md and npm/factoidal/solid/*/README.md.
|
|
1138
|
+
// https://github.com/danbri/factoidal/issues/659
|
|
1139
|
+
|
|
1140
|
+
function servePort (options, fallback) {
|
|
1141
|
+
if (options.port === undefined) return fallback
|
|
1142
|
+
const port = Number(options.port)
|
|
1143
|
+
if (!Number.isInteger(port) || port < 0 || port > 65535) {
|
|
1144
|
+
throw new UsageError(`--port must be an integer from 0 to 65535, not "${options.port}"`)
|
|
1145
|
+
}
|
|
1146
|
+
return port
|
|
1147
|
+
}
|
|
1148
|
+
|
|
1149
|
+
function serveHost (options) {
|
|
1150
|
+
if (options.host === undefined) return '127.0.0.1'
|
|
1151
|
+
if (typeof options.host !== 'string' || options.host.length === 0) {
|
|
1152
|
+
throw new UsageError('--host needs an address')
|
|
1153
|
+
}
|
|
1154
|
+
return options.host
|
|
1155
|
+
}
|
|
1156
|
+
|
|
1157
|
+
/** Keep the process alive until the runtime is interrupted. */
|
|
1158
|
+
function untilInterrupted (running, quiet) {
|
|
1159
|
+
return new Promise((resolve) => {
|
|
1160
|
+
const stop = async () => {
|
|
1161
|
+
if (!quiet) err('')
|
|
1162
|
+
await running.close()
|
|
1163
|
+
resolve(EXIT_OK)
|
|
1164
|
+
}
|
|
1165
|
+
if (isDeno) {
|
|
1166
|
+
globalThis.Deno.addSignalListener('SIGINT', stop)
|
|
1167
|
+
} else {
|
|
1168
|
+
process.on('SIGINT', stop)
|
|
1169
|
+
process.on('SIGTERM', stop)
|
|
1170
|
+
}
|
|
1171
|
+
})
|
|
1172
|
+
}
|
|
1173
|
+
|
|
1174
|
+
async function commandServe (positional, options) {
|
|
1175
|
+
const quiet = options.quiet === true
|
|
1176
|
+
// The Apache Jena Fuseki form: `serve --file DATA /ds`. The file is
|
|
1177
|
+
// packed into a new collection in a temporary directory (ibk5 unless
|
|
1178
|
+
// --layout says otherwise, so every graph accepts writes), activated, and
|
|
1179
|
+
// served at /ds/sparql and /ds/data; /ds/query, /ds/update and /ds/get
|
|
1180
|
+
// are answered too (StoreEndpoint.datasetPrefix). Updates go to that
|
|
1181
|
+
// temporary collection and are lost when the process ends, as with
|
|
1182
|
+
// fuseki-server --file. docs/fuseki-replacement.md.
|
|
1183
|
+
if (typeof options.file === 'string') {
|
|
1184
|
+
if (positional.length !== 1 || !positional[0].startsWith('/')) {
|
|
1185
|
+
throw new UsageError('serve --file DATA needs one dataset name, such as /ds')
|
|
1186
|
+
}
|
|
1187
|
+
const dsName = positional[0].replace(/\/+$/, '')
|
|
1188
|
+
const { mkdtempSync } = await import('node:fs')
|
|
1189
|
+
const { tmpdir } = await import('node:os')
|
|
1190
|
+
const { join } = await import('node:path')
|
|
1191
|
+
const root = mkdtempSync(join(tmpdir(), 'factoidal-serve-'))
|
|
1192
|
+
const packOptions = { quiet: true, layout: typeof options.layout === 'string' ? options.layout : 'ibk5' }
|
|
1193
|
+
let rc = await commandPack([options.file, join(root, 'gen-1')], packOptions)
|
|
1194
|
+
if (rc !== 0) return rc
|
|
1195
|
+
rc = await commandActivate([root, 'gen-1'], { quiet: true })
|
|
1196
|
+
if (rc !== 0) return rc
|
|
1197
|
+
if (!quiet) err(`serve: packed ${options.file} into ${root} (temporary; updates are lost on exit)`)
|
|
1198
|
+
positional = [root]
|
|
1199
|
+
options = { ...options, 'query-path': `${dsName}/sparql`, 'gsp-path': `${dsName}/data` }
|
|
1200
|
+
}
|
|
1201
|
+
// The Apache Jena Fuseki form: `serve --tdb2 DIR /ds`, a TDB2 database
|
|
1202
|
+
// read in place and read only (https://github.com/danbri/factoidal/issues/834).
|
|
1203
|
+
const tdb2 = typeof options.tdb2 === 'string'
|
|
1204
|
+
if (tdb2) {
|
|
1205
|
+
if (positional.length !== 1 || !positional[0].startsWith('/')) {
|
|
1206
|
+
throw new UsageError('serve --tdb2 DIR needs one dataset name, such as /ds')
|
|
1207
|
+
}
|
|
1208
|
+
const dsName = positional[0].replace(/\/+$/, '')
|
|
1209
|
+
positional = [options.tdb2]
|
|
1210
|
+
options = { ...options, 'query-path': `${dsName}/sparql`, 'gsp-path': `${dsName}/data` }
|
|
1211
|
+
}
|
|
1212
|
+
if (positional.length !== 1) throw new UsageError('serve needs one STORE')
|
|
1213
|
+
const { listen, listenOnWorker, ServeHostError } = await import('./serve.mjs')
|
|
1214
|
+
const serveOptions = {
|
|
1215
|
+
port: servePort(options, 3030),
|
|
1216
|
+
host: serveHost(options),
|
|
1217
|
+
...(tdb2 ? { tdb2: true } : {}),
|
|
1218
|
+
...(typeof options['query-path'] === 'string' ? { queryPath: options['query-path'] } : {}),
|
|
1219
|
+
...(typeof options['gsp-path'] === 'string' ? { gspPath: options['gsp-path'] } : {})
|
|
1220
|
+
}
|
|
1221
|
+
const onWorker = options['no-worker'] !== true && workerRouteAvailable()
|
|
1222
|
+
let running
|
|
1223
|
+
try {
|
|
1224
|
+
running = onWorker
|
|
1225
|
+
? await listenOnWorker(positional[0], serveOptions)
|
|
1226
|
+
: await listen(positional[0], serveOptions)
|
|
1227
|
+
} catch (error) {
|
|
1228
|
+
if (error instanceof ServeHostError && error.unknownOp) {
|
|
1229
|
+
return notWired('serve', error.message)
|
|
1230
|
+
}
|
|
1231
|
+
if (error instanceof Error && typeof error.message === 'string') {
|
|
1232
|
+
err(`factoidal serve: ${error.message.replace(/^l4factoidal:\s*/, '')}`)
|
|
1233
|
+
return EXIT_FAILURE
|
|
1234
|
+
}
|
|
1235
|
+
throw error
|
|
1236
|
+
}
|
|
1237
|
+
const info = onWorker ? running.info : running.served.info
|
|
1238
|
+
if (!quiet) {
|
|
1239
|
+
err(`serve: listening on ${running.origin} for ${positional[0]} ` +
|
|
1240
|
+
`(layout ${info.layout}, ${plural(info.rows, 'row')}, ` +
|
|
1241
|
+
`delta batches replayed: ${info.deltaBatches}, write scope ${info.writeScope})`)
|
|
1242
|
+
err(`serve: query ${running.origin}${info.queryPath}, graph store ${running.origin}${info.gspPath}`)
|
|
1243
|
+
}
|
|
1244
|
+
out(running.origin)
|
|
1245
|
+
return untilInterrupted(running, quiet)
|
|
1246
|
+
}
|
|
1247
|
+
|
|
1248
|
+
async function commandLwsServe (positional, options) {
|
|
1249
|
+
if (positional.length !== 1) throw new UsageError('lws-serve needs one DIR')
|
|
1250
|
+
const quiet = options.quiet === true
|
|
1251
|
+
const { listen, LwsHostError } = await import('../lws/server.mjs')
|
|
1252
|
+
let running
|
|
1253
|
+
try {
|
|
1254
|
+
running = await listen({
|
|
1255
|
+
port: servePort(options, 3000),
|
|
1256
|
+
host: serveHost(options),
|
|
1257
|
+
...(typeof options.base === 'string' ? { baseIri: options.base } : {}),
|
|
1258
|
+
realClock: true
|
|
1259
|
+
})
|
|
1260
|
+
} catch (error) {
|
|
1261
|
+
if (error instanceof LwsHostError && error.unknownOp) {
|
|
1262
|
+
return notWired('lws-serve', error.message)
|
|
1263
|
+
}
|
|
1264
|
+
throw error
|
|
1265
|
+
}
|
|
1266
|
+
if (!quiet) err(`lws-serve: listening on ${running.origin} for ${positional[0]}`)
|
|
1267
|
+
out(running.origin)
|
|
1268
|
+
return untilInterrupted(running, quiet)
|
|
1269
|
+
}
|
|
1270
|
+
|
|
1271
|
+
async function commandSolidServe (positional, options) {
|
|
1272
|
+
if (positional.length !== 1) throw new UsageError('solid-serve needs one DIR')
|
|
1273
|
+
const quiet = options.quiet === true
|
|
1274
|
+
const { listen, SolidServerHostError } = await import('../solid/server/index.mjs')
|
|
1275
|
+
let running
|
|
1276
|
+
try {
|
|
1277
|
+
running = await listen({
|
|
1278
|
+
port: servePort(options, 3000),
|
|
1279
|
+
host: serveHost(options),
|
|
1280
|
+
...(typeof options.base === 'string' ? { baseIri: options.base } : {}),
|
|
1281
|
+
...(typeof options.owner === 'string' ? { owner: options.owner } : {}),
|
|
1282
|
+
realClock: true
|
|
1283
|
+
})
|
|
1284
|
+
} catch (error) {
|
|
1285
|
+
if (error instanceof SolidServerHostError && error.unknownOp) {
|
|
1286
|
+
return notWired('solid-serve', error.message)
|
|
1287
|
+
}
|
|
1288
|
+
throw error
|
|
1289
|
+
}
|
|
1290
|
+
if (!quiet) err(`solid-serve: listening on ${running.origin} for ${positional[0]}`)
|
|
1291
|
+
out(running.origin)
|
|
1292
|
+
return untilInterrupted(running, quiet)
|
|
1293
|
+
}
|
|
1294
|
+
|
|
1295
|
+
// The command's verbs, as aliases for the wasm ABI's request kinds. A
|
|
1296
|
+
// kind name is accepted directly too, so this table adds a spelling and
|
|
1297
|
+
// hides nothing.
|
|
1298
|
+
const SOLID_CLIENT_ALIASES = {
|
|
1299
|
+
get: 'read',
|
|
1300
|
+
put: 'replace',
|
|
1301
|
+
post: 'create',
|
|
1302
|
+
delete: 'delete',
|
|
1303
|
+
discover: 'discoverStorage'
|
|
1304
|
+
}
|
|
1305
|
+
|
|
1306
|
+
async function commandSolidClient (positional, options) {
|
|
1307
|
+
if (positional.length !== 2) {
|
|
1308
|
+
throw new UsageError('solid-client needs an operation and a URL')
|
|
1309
|
+
}
|
|
1310
|
+
const [verb, target] = positional
|
|
1311
|
+
const { createSolidClient, SolidClientHostError, SOLID_REQUEST_KINDS } =
|
|
1312
|
+
await import('../solid/client/index.mjs')
|
|
1313
|
+
const kind = Object.prototype.hasOwnProperty.call(SOLID_CLIENT_ALIASES, verb)
|
|
1314
|
+
? SOLID_CLIENT_ALIASES[verb]
|
|
1315
|
+
: verb
|
|
1316
|
+
if (!SOLID_REQUEST_KINDS.includes(kind)) {
|
|
1317
|
+
throw new UsageError(
|
|
1318
|
+
`unknown operation "${verb}"; one of ` +
|
|
1319
|
+
`${Object.keys(SOLID_CLIENT_ALIASES).join(', ')} or ` +
|
|
1320
|
+
`${SOLID_REQUEST_KINDS.join(', ')}`)
|
|
1321
|
+
}
|
|
1322
|
+
const args = { target }
|
|
1323
|
+
if (typeof options.file === 'string') {
|
|
1324
|
+
args.body = new TextDecoder('utf-8', { fatal: true }).decode(readWhole(options.file))
|
|
1325
|
+
} else if (typeof options.body === 'string') {
|
|
1326
|
+
args.body = options.body
|
|
1327
|
+
}
|
|
1328
|
+
if (typeof options['content-type'] === 'string') {
|
|
1329
|
+
args.contentType = options['content-type']
|
|
1330
|
+
}
|
|
1331
|
+
if (['create', 'replace', 'patch'].includes(kind) && args.body === undefined) {
|
|
1332
|
+
throw new UsageError(`${verb} needs --body or --file`)
|
|
1333
|
+
}
|
|
1334
|
+
const extra = typeof options.interpret === 'string'
|
|
1335
|
+
? { interpret: options.interpret }
|
|
1336
|
+
: {}
|
|
1337
|
+
let answer
|
|
1338
|
+
try {
|
|
1339
|
+
const client = await createSolidClient()
|
|
1340
|
+
answer = await client.run(kind, args, extra)
|
|
1341
|
+
} catch (error) {
|
|
1342
|
+
if (error instanceof SolidClientHostError && error.unknownOp) {
|
|
1343
|
+
return notWired('solid-client', error.message)
|
|
1344
|
+
}
|
|
1345
|
+
throw error
|
|
1346
|
+
}
|
|
1347
|
+
out(JSON.stringify(answer, null, 2))
|
|
1348
|
+
return EXIT_OK
|
|
1349
|
+
}
|
|
1350
|
+
|
|
909
1351
|
const COMMANDS = {
|
|
910
1352
|
version: (positional, options) => commandVersion(options),
|
|
911
|
-
'sample-store': (positional, options) => commandSampleStore(options),
|
|
1353
|
+
'sample-store': (positional, options) => commandSampleStore(positional, options),
|
|
912
1354
|
inspect: commandInspect,
|
|
913
1355
|
query: commandQuery,
|
|
914
1356
|
pack: commandPack,
|
|
915
1357
|
activate: commandActivate,
|
|
916
1358
|
update: commandUpdate,
|
|
917
|
-
compact: commandCompact
|
|
1359
|
+
compact: commandCompact,
|
|
1360
|
+
serve: commandServe,
|
|
1361
|
+
'lws-serve': commandLwsServe,
|
|
1362
|
+
'solid-serve': commandSolidServe,
|
|
1363
|
+
'solid-client': commandSolidClient
|
|
918
1364
|
}
|
|
919
1365
|
|
|
920
1366
|
// ----------------------------------------------------------------- main
|