@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/lws/server.mjs
ADDED
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
// A Node HTTP host for the Linked Web Storage Protocol 1.0 core.
|
|
2
|
+
// https://github.com/danbri/factoidal/issues/659
|
|
3
|
+
// Design record: docs/designissues/2026-09-06-lws-and-solid-protocols.md
|
|
4
|
+
//
|
|
5
|
+
// WHAT THIS FILE IS ALLOWED TO DO
|
|
6
|
+
// Accept a socket, turn one HTTP request into the JSON request record,
|
|
7
|
+
// call the engine's `lwsStep` on a handle, and write the JSON response
|
|
8
|
+
// record back onto the socket. That is the whole contract.
|
|
9
|
+
//
|
|
10
|
+
// It makes NO protocol decision. It does not choose a status code, does
|
|
11
|
+
// not build a `Link` header, does not read or write a resource, does not
|
|
12
|
+
// decide whether a PATCH is refused, and does not know what a container
|
|
13
|
+
// is. Every one of those lives in `formal/lean4/L4Factoidal/LWS/`
|
|
14
|
+
// (iron rule 7 of CLAUDE.md). A reviewer who finds a status code, a
|
|
15
|
+
// media type decision or an RDF term in this file has found a rule
|
|
16
|
+
// violation. The one status code written here is 500, and only for a
|
|
17
|
+
// failure of this host itself.
|
|
18
|
+
//
|
|
19
|
+
// THE WASM CONTRACT
|
|
20
|
+
// Fixed in docs/lws-solid-conformance.md, section "wasm ABI".
|
|
21
|
+
// lwsOpen [configJson] -> { ok, handle, root }
|
|
22
|
+
// lwsStep [handle, requestJson] -> { ok, response }
|
|
23
|
+
// lwsClose [handle] -> { ok }
|
|
24
|
+
//
|
|
25
|
+
// request { method, target, headers: [[name, value]], body }
|
|
26
|
+
// response { status, headers: [[name, value]], body }
|
|
27
|
+
//
|
|
28
|
+
// The config members are `baseIri`, `now`, `owner` and `agent`, all
|
|
29
|
+
// optional. `target` is a path within the storage, so this host passes
|
|
30
|
+
// Node's `request.url` through unchanged and never composes an absolute
|
|
31
|
+
// IRI: `baseIri` tells the engine what the root maps to.
|
|
32
|
+
//
|
|
33
|
+
// A request may carry `now` and `agent` beside `method` to override the
|
|
34
|
+
// handle's clock and agent for one step. That is how a host with a real
|
|
35
|
+
// clock and a real token verifier feeds both in; this host sets `now`
|
|
36
|
+
// from its own clock only when the caller asked for it, so a test can
|
|
37
|
+
// keep the engine's deterministic clock.
|
|
38
|
+
//
|
|
39
|
+
// `headers` is a list of pairs rather than an object because HTTP allows
|
|
40
|
+
// a field name to repeat and the order of repeated fields is
|
|
41
|
+
// significant; an object would lose both.
|
|
42
|
+
//
|
|
43
|
+
// The reader of the response envelope accepts the response record either
|
|
44
|
+
// under `response` or spread onto the envelope itself, and the handle
|
|
45
|
+
// either as `handle` or as `id`. This is deliberate tolerance while the
|
|
46
|
+
// Lean side of the contract lands, not two supported shapes: when
|
|
47
|
+
// `docs/lws-solid-conformance.md` fixes one, the other stops being
|
|
48
|
+
// produced and this code keeps working unchanged.
|
|
49
|
+
|
|
50
|
+
import { loadEngine } from '../bin/engine.mjs'
|
|
51
|
+
|
|
52
|
+
/** The ops this host needs from the engine. */
|
|
53
|
+
export const LWS_OPS = ['lwsOpen', 'lwsStep', 'lwsClose']
|
|
54
|
+
|
|
55
|
+
/** An error raised by the LWS host, as opposed to one the engine
|
|
56
|
+
* reported. `unknownOp` is set when the loaded WebAssembly module does
|
|
57
|
+
* not carry the operation at all, which is what a test skips on. */
|
|
58
|
+
export class LwsHostError extends Error {
|
|
59
|
+
constructor (message, detail = {}) {
|
|
60
|
+
super(message)
|
|
61
|
+
this.name = 'LwsHostError'
|
|
62
|
+
this.unknownOp = detail.unknownOp === true
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function isUnknownOp (error) {
|
|
67
|
+
const text = error && error.message ? String(error.message) : String(error)
|
|
68
|
+
return text.indexOf('unknown op') >= 0
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Whether the loaded engine carries the LWS operations.
|
|
73
|
+
*
|
|
74
|
+
* One call is made and its refusal read. A module built before the LWS
|
|
75
|
+
* ops landed answers "unknown op"; anything else -- including an engine
|
|
76
|
+
* complaint about the configuration -- means the op is present.
|
|
77
|
+
*
|
|
78
|
+
* @param {object} engine the handle from `loadEngine()`
|
|
79
|
+
* @returns {Promise<{available: boolean, reason: string}>}
|
|
80
|
+
*/
|
|
81
|
+
export async function lwsOpsAvailable (engine) {
|
|
82
|
+
try {
|
|
83
|
+
const opened = engine.call('lwsOpen', [JSON.stringify({ probe: true })])
|
|
84
|
+
const handle = handleOf(opened)
|
|
85
|
+
if (handle !== null) {
|
|
86
|
+
try { engine.call('lwsClose', [handle]) } catch (_error) { /* probe only */ }
|
|
87
|
+
}
|
|
88
|
+
return { available: true, reason: 'lwsOpen answered' }
|
|
89
|
+
} catch (error) {
|
|
90
|
+
if (isUnknownOp(error)) {
|
|
91
|
+
return {
|
|
92
|
+
available: false,
|
|
93
|
+
reason: 'the loaded l4factoidal WebAssembly module does not carry ' +
|
|
94
|
+
'the lws ops (rebuild formal/lean4/Wasm)'
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return { available: true, reason: 'lwsOpen answered with an engine error' }
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function handleOf (envelope) {
|
|
102
|
+
if (envelope === null || typeof envelope !== 'object') return null
|
|
103
|
+
for (const name of ['handle', 'id']) {
|
|
104
|
+
const value = envelope[name]
|
|
105
|
+
if (typeof value === 'string' && value.length > 0) return value
|
|
106
|
+
if (typeof value === 'number') return String(value)
|
|
107
|
+
}
|
|
108
|
+
return null
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function responseOf (envelope) {
|
|
112
|
+
if (envelope === null || typeof envelope !== 'object') {
|
|
113
|
+
throw new LwsHostError('lwsStep answered with no envelope')
|
|
114
|
+
}
|
|
115
|
+
const record = (envelope.response !== null && typeof envelope.response === 'object')
|
|
116
|
+
? envelope.response
|
|
117
|
+
: envelope
|
|
118
|
+
if (typeof record.status !== 'number') {
|
|
119
|
+
throw new LwsHostError('lwsStep answered with no status in its response record')
|
|
120
|
+
}
|
|
121
|
+
const headers = Array.isArray(record.headers) ? record.headers : []
|
|
122
|
+
const body = typeof record.body === 'string' ? record.body : ''
|
|
123
|
+
return { status: record.status, headers, body }
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Open one LWS storage on the engine and return the handle plus the two
|
|
128
|
+
* calls a host makes against it.
|
|
129
|
+
*
|
|
130
|
+
* The config members are the ABI's own: `baseIri`, `now`, `owner`,
|
|
131
|
+
* `agent`. Nothing else is sent.
|
|
132
|
+
*
|
|
133
|
+
* @param {object} options
|
|
134
|
+
* @param {string} [options.baseIri] the absolute IRI the storage root maps to
|
|
135
|
+
* @param {number} [options.now] the engine clock, seconds since the epoch
|
|
136
|
+
* @param {string} [options.owner] the WebID the storage advertises
|
|
137
|
+
* @param {string} [options.agent] the WebID of the requesting agent
|
|
138
|
+
* @param {boolean} [options.realClock] stamp each request with this host's clock
|
|
139
|
+
* @param {object} [options.engine] an engine handle to reuse
|
|
140
|
+
* @returns {Promise<{step: Function, close: Function, handle: string, engine: object}>}
|
|
141
|
+
*/
|
|
142
|
+
export async function openLwsStorage (options = {}) {
|
|
143
|
+
const engine = options.engine !== undefined ? options.engine : await loadEngine()
|
|
144
|
+
const config = {}
|
|
145
|
+
if (typeof options.baseIri === 'string') config.baseIri = options.baseIri
|
|
146
|
+
if (typeof options.now === 'number') config.now = options.now
|
|
147
|
+
if (typeof options.owner === 'string') config.owner = options.owner
|
|
148
|
+
if (typeof options.agent === 'string') config.agent = options.agent
|
|
149
|
+
let opened
|
|
150
|
+
try {
|
|
151
|
+
opened = engine.call('lwsOpen', [JSON.stringify(config)])
|
|
152
|
+
} catch (error) {
|
|
153
|
+
if (isUnknownOp(error)) {
|
|
154
|
+
throw new LwsHostError(
|
|
155
|
+
'the loaded l4factoidal WebAssembly module does not carry lwsOpen',
|
|
156
|
+
{ unknownOp: true })
|
|
157
|
+
}
|
|
158
|
+
throw error
|
|
159
|
+
}
|
|
160
|
+
const handle = handleOf(opened)
|
|
161
|
+
if (handle === null) {
|
|
162
|
+
throw new LwsHostError('lwsOpen answered with no handle')
|
|
163
|
+
}
|
|
164
|
+
return {
|
|
165
|
+
engine,
|
|
166
|
+
handle,
|
|
167
|
+
/**
|
|
168
|
+
* One protocol step.
|
|
169
|
+
* @param {{method: string, target: string,
|
|
170
|
+
* headers: Array<[string, string]>, body: string}} request
|
|
171
|
+
* @returns {{status: number, headers: Array<[string, string]>, body: string}}
|
|
172
|
+
*/
|
|
173
|
+
step (request) {
|
|
174
|
+
const record = options.realClock === true
|
|
175
|
+
? { ...request, now: Math.floor(Date.now() / 1000) }
|
|
176
|
+
: request
|
|
177
|
+
return responseOf(engine.call('lwsStep', [handle, JSON.stringify(record)]))
|
|
178
|
+
},
|
|
179
|
+
close () {
|
|
180
|
+
try { engine.call('lwsClose', [handle]) } catch (_error) { /* already gone */ }
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/** Read a Node request body as one UTF-8 string. */
|
|
186
|
+
function readBody (request) {
|
|
187
|
+
return new Promise((resolve, reject) => {
|
|
188
|
+
const chunks = []
|
|
189
|
+
request.on('data', (chunk) => chunks.push(chunk))
|
|
190
|
+
request.on('error', reject)
|
|
191
|
+
request.on('end', () => resolve(Buffer.concat(chunks).toString('utf8')))
|
|
192
|
+
})
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/** Node's flat `rawHeaders` array as the pairs the contract wants. */
|
|
196
|
+
function headerPairs (raw) {
|
|
197
|
+
const pairs = []
|
|
198
|
+
for (let index = 0; index + 1 < raw.length; index += 2) {
|
|
199
|
+
pairs.push([raw[index], raw[index + 1]])
|
|
200
|
+
}
|
|
201
|
+
return pairs
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* A Node `http.Server` that answers every request through `lwsStep`.
|
|
206
|
+
*
|
|
207
|
+
* The server is returned unlistened: the caller chooses the port, which
|
|
208
|
+
* is what lets a test take an ephemeral one. The storage handle is
|
|
209
|
+
* opened on demand rather than here, because with an ephemeral port the
|
|
210
|
+
* origin `baseIri` maps the storage root to is not known until the
|
|
211
|
+
* socket is bound.
|
|
212
|
+
*
|
|
213
|
+
* @param {object} options as `openLwsStorage`, plus `onError`
|
|
214
|
+
* @returns {Promise<{server: import('node:http').Server,
|
|
215
|
+
* storage: Function, close: Function,
|
|
216
|
+
* setBaseIri: Function}>}
|
|
217
|
+
*/
|
|
218
|
+
export async function createLwsServer (options = {}) {
|
|
219
|
+
const http = await import('node:http')
|
|
220
|
+
const onError = typeof options.onError === 'function'
|
|
221
|
+
? options.onError
|
|
222
|
+
: (error) => { console.error(`lws-serve: ${error.message}`) }
|
|
223
|
+
|
|
224
|
+
let settings = { ...options }
|
|
225
|
+
let opening = null
|
|
226
|
+
let opened = null
|
|
227
|
+
|
|
228
|
+
/** The storage handle, opened once. */
|
|
229
|
+
function storage () {
|
|
230
|
+
if (opening === null) {
|
|
231
|
+
opening = openLwsStorage(settings).then((value) => { opened = value; return value })
|
|
232
|
+
}
|
|
233
|
+
return opening
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
const server = http.createServer(async (request, response) => {
|
|
237
|
+
let record
|
|
238
|
+
try {
|
|
239
|
+
record = {
|
|
240
|
+
method: request.method,
|
|
241
|
+
target: request.url,
|
|
242
|
+
headers: headerPairs(request.rawHeaders),
|
|
243
|
+
body: await readBody(request)
|
|
244
|
+
}
|
|
245
|
+
} catch (error) {
|
|
246
|
+
onError(error)
|
|
247
|
+
response.writeHead(500, { 'content-type': 'text/plain' })
|
|
248
|
+
response.end('lws-serve: could not read the request body\n')
|
|
249
|
+
return
|
|
250
|
+
}
|
|
251
|
+
let answer
|
|
252
|
+
try {
|
|
253
|
+
answer = (await storage()).step(record)
|
|
254
|
+
} catch (error) {
|
|
255
|
+
// A failure of this host or of the engine, not a protocol answer.
|
|
256
|
+
// The engine's own words are kept; nothing is invented.
|
|
257
|
+
onError(error)
|
|
258
|
+
response.writeHead(500, { 'content-type': 'text/plain' })
|
|
259
|
+
response.end(`lws-serve: ${error.message}\n`)
|
|
260
|
+
return
|
|
261
|
+
}
|
|
262
|
+
writeHeaders(response, answer.headers)
|
|
263
|
+
response.statusCode = answer.status
|
|
264
|
+
response.end(answer.body)
|
|
265
|
+
})
|
|
266
|
+
|
|
267
|
+
return {
|
|
268
|
+
server,
|
|
269
|
+
storage,
|
|
270
|
+
/** Set the storage root's absolute IRI, before the first request. */
|
|
271
|
+
setBaseIri (value) {
|
|
272
|
+
if (opening !== null) {
|
|
273
|
+
throw new LwsHostError('the storage is already open; set baseIri first')
|
|
274
|
+
}
|
|
275
|
+
settings = { ...settings, baseIri: value }
|
|
276
|
+
},
|
|
277
|
+
close () {
|
|
278
|
+
return new Promise((resolve) => {
|
|
279
|
+
server.close(() => {
|
|
280
|
+
if (opened !== null) opened.close()
|
|
281
|
+
resolve()
|
|
282
|
+
})
|
|
283
|
+
})
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/** Write one response record's header pairs, keeping repeats. */
|
|
289
|
+
function writeHeaders (response, pairs) {
|
|
290
|
+
for (const pair of pairs) {
|
|
291
|
+
if (!Array.isArray(pair) || pair.length < 2) continue
|
|
292
|
+
const existing = response.getHeader(pair[0])
|
|
293
|
+
if (existing === undefined) {
|
|
294
|
+
response.setHeader(pair[0], String(pair[1]))
|
|
295
|
+
} else if (Array.isArray(existing)) {
|
|
296
|
+
response.setHeader(pair[0], existing.concat([String(pair[1])]))
|
|
297
|
+
} else {
|
|
298
|
+
response.setHeader(pair[0], [String(existing), String(pair[1])])
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* Start a server and resolve once it is listening and the storage is
|
|
305
|
+
* open. The storage is opened here rather than on the first request so
|
|
306
|
+
* that a module without the LWS ops is reported by the command that
|
|
307
|
+
* started the server, not by a later request.
|
|
308
|
+
*
|
|
309
|
+
* @param {object} options as `createLwsServer`, plus `port` and `host`
|
|
310
|
+
* @returns {Promise<{server: object, storage: Function, port: number,
|
|
311
|
+
* origin: string, close: Function}>}
|
|
312
|
+
*/
|
|
313
|
+
export async function listen (options = {}) {
|
|
314
|
+
const port = typeof options.port === 'number' ? options.port : 0
|
|
315
|
+
const host = typeof options.host === 'string' ? options.host : '127.0.0.1'
|
|
316
|
+
const created = await createLwsServer(options)
|
|
317
|
+
await new Promise((resolve, reject) => {
|
|
318
|
+
created.server.once('error', reject)
|
|
319
|
+
created.server.listen(port, host, resolve)
|
|
320
|
+
})
|
|
321
|
+
const bound = created.server.address()
|
|
322
|
+
const origin = `http://${host}:${bound.port}`
|
|
323
|
+
if (typeof options.baseIri !== 'string') created.setBaseIri(`${origin}/`)
|
|
324
|
+
try {
|
|
325
|
+
await created.storage()
|
|
326
|
+
} catch (error) {
|
|
327
|
+
await new Promise((resolve) => created.server.close(resolve))
|
|
328
|
+
throw error
|
|
329
|
+
}
|
|
330
|
+
return { ...created, port: bound.port, origin }
|
|
331
|
+
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@factoidal/core",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.10.0",
|
|
4
|
+
"description": "RDF parsing and writing (Turtle, TriG, N-Triples, N-Quads, RDF/XML, JSON-LD), SPARQL 1.1 and 1.2, RDFS and OWL reasoning, SHACL and ShEx validation, CSVW and RML mapping, RDFC-1.0, and SPARQL, LWS and Solid servers, for Node, Deno and browsers.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"sparql",
|
|
7
7
|
"rdf",
|
|
@@ -10,9 +10,15 @@
|
|
|
10
10
|
"nquads",
|
|
11
11
|
"trig",
|
|
12
12
|
"rdf-xml",
|
|
13
|
-
"
|
|
14
|
-
"
|
|
15
|
-
"
|
|
13
|
+
"semantic-web",
|
|
14
|
+
"webassembly",
|
|
15
|
+
"shacl",
|
|
16
|
+
"json-ld",
|
|
17
|
+
"owl",
|
|
18
|
+
"shex",
|
|
19
|
+
"csvw",
|
|
20
|
+
"linked-data",
|
|
21
|
+
"solid"
|
|
16
22
|
],
|
|
17
23
|
"homepage": "https://github.com/danbri/factoidal",
|
|
18
24
|
"bugs": {
|
|
@@ -22,8 +28,7 @@
|
|
|
22
28
|
"author": "Dan Brickley <danbri@danbri.org>",
|
|
23
29
|
"repository": {
|
|
24
30
|
"type": "git",
|
|
25
|
-
"url": "git+https://github.com/danbri/factoidal.git"
|
|
26
|
-
"directory": "npm/factoidal"
|
|
31
|
+
"url": "git+https://github.com/danbri/factoidal-builds.git"
|
|
27
32
|
},
|
|
28
33
|
"main": "./index.js",
|
|
29
34
|
"module": "./index.mjs",
|
|
@@ -40,11 +45,17 @@
|
|
|
40
45
|
"require": "./index.js",
|
|
41
46
|
"default": "./index.js"
|
|
42
47
|
},
|
|
43
|
-
"./
|
|
44
|
-
"types": "./
|
|
45
|
-
"
|
|
48
|
+
"./api": {
|
|
49
|
+
"types": "./api.d.ts",
|
|
50
|
+
"import": "./api.mjs",
|
|
51
|
+
"require": "./api.js",
|
|
52
|
+
"default": "./api.js"
|
|
53
|
+
},
|
|
54
|
+
"./rdfjs": {
|
|
55
|
+
"import": "./rdfjs.mjs",
|
|
56
|
+
"require": "./rdfjs.js",
|
|
57
|
+
"default": "./rdfjs.js"
|
|
46
58
|
},
|
|
47
|
-
"./rdfjs": "./rdfjs.js",
|
|
48
59
|
"./fn": {
|
|
49
60
|
"types": "./fn.d.ts",
|
|
50
61
|
"default": "./fn.js"
|
|
@@ -53,95 +64,82 @@
|
|
|
53
64
|
"types": "./browser.d.ts",
|
|
54
65
|
"default": "./browser.js"
|
|
55
66
|
},
|
|
56
|
-
"./browser-wasm": {
|
|
57
|
-
"browser": "./browser-wasm.js",
|
|
58
|
-
"default": "./browser-wasm.js"
|
|
59
|
-
},
|
|
60
|
-
"./factoidal.js": "./factoidal.js",
|
|
61
|
-
"./factoidal.wasm.js": "./factoidal.wasm.js",
|
|
62
|
-
"./factoidal.wasm.assets/*": "./factoidal.wasm.assets/*",
|
|
63
67
|
"./package.json": "./package.json",
|
|
64
|
-
"./l4": {
|
|
65
|
-
"types": "./l4.d.ts",
|
|
66
|
-
"default": "./l4.js"
|
|
67
|
-
},
|
|
68
|
-
"./l4-core": {
|
|
69
|
-
"types": "./l4-core.d.ts",
|
|
70
|
-
"default": "./l4-core.js"
|
|
71
|
-
},
|
|
72
68
|
"./l4-assets/*": "./l4-assets/*",
|
|
73
69
|
"./store-host": "./store-host/index.mjs",
|
|
74
70
|
"./store-host/paths": "./store-host/paths.mjs",
|
|
75
|
-
"./
|
|
76
|
-
"types": "./select.d.ts",
|
|
77
|
-
"default": "./select.js"
|
|
78
|
-
},
|
|
79
|
-
"./sample-store": {
|
|
80
|
-
"types": "./sample-store.d.ts",
|
|
81
|
-
"default": "./sample-store.mjs"
|
|
82
|
-
},
|
|
71
|
+
"./xmpp": "./xmpp/index.mjs",
|
|
83
72
|
"./store": {
|
|
84
73
|
"default": "./bin/store.mjs"
|
|
85
74
|
},
|
|
86
75
|
"./store-worker": {
|
|
87
76
|
"default": "./bin/store-worker-host.mjs"
|
|
88
77
|
},
|
|
78
|
+
"./serve": {
|
|
79
|
+
"default": "./bin/serve.mjs"
|
|
80
|
+
},
|
|
89
81
|
"./pack": {
|
|
90
82
|
"default": "./bin/pack.mjs"
|
|
91
83
|
},
|
|
92
84
|
"./engine": {
|
|
93
85
|
"default": "./bin/engine.mjs"
|
|
86
|
+
},
|
|
87
|
+
"./lws": {
|
|
88
|
+
"default": "./lws/server.mjs"
|
|
89
|
+
},
|
|
90
|
+
"./solid/server": {
|
|
91
|
+
"default": "./solid/server/index.mjs"
|
|
92
|
+
},
|
|
93
|
+
"./solid/client": {
|
|
94
|
+
"default": "./solid/client/index.mjs"
|
|
95
|
+
},
|
|
96
|
+
"./sample-store": {
|
|
97
|
+
"types": "./sample-store.d.ts",
|
|
98
|
+
"default": "./sample-store.mjs"
|
|
94
99
|
}
|
|
95
100
|
},
|
|
96
101
|
"files": [
|
|
97
102
|
"index.js",
|
|
98
103
|
"index.mjs",
|
|
99
104
|
"index.d.ts",
|
|
100
|
-
"
|
|
101
|
-
"
|
|
105
|
+
"api.js",
|
|
106
|
+
"api.mjs",
|
|
107
|
+
"api.d.ts",
|
|
102
108
|
"rdfjs.js",
|
|
109
|
+
"rdfjs.mjs",
|
|
103
110
|
"fn.js",
|
|
104
111
|
"fn.d.ts",
|
|
105
|
-
"
|
|
106
|
-
"
|
|
107
|
-
"l4-core.js",
|
|
108
|
-
"l4-core.d.ts",
|
|
112
|
+
"browser.js",
|
|
113
|
+
"browser.d.ts",
|
|
109
114
|
"l4-assets/",
|
|
110
|
-
"
|
|
111
|
-
"select.d.ts",
|
|
115
|
+
"!l4-assets/l4factoidal-core.*",
|
|
112
116
|
"lib/",
|
|
113
117
|
"bin/",
|
|
118
|
+
"lws/",
|
|
119
|
+
"solid/",
|
|
114
120
|
"store-host/",
|
|
115
|
-
"
|
|
116
|
-
"
|
|
117
|
-
"
|
|
118
|
-
"
|
|
119
|
-
"factoidal.wasm.js",
|
|
120
|
-
"factoidal.wasm.assets/",
|
|
121
|
-
"factoidal-npm-entry.js",
|
|
122
|
-
"factoidal-npm-entry.wasm.js",
|
|
123
|
-
"factoidal-npm-entry.wasm.assets/",
|
|
124
|
-
"hacl-init.js",
|
|
125
|
-
"hacl-wasm/",
|
|
126
|
-
"version.json",
|
|
121
|
+
"xmpp/",
|
|
122
|
+
"sample-store.mjs",
|
|
123
|
+
"sample-store.d.ts",
|
|
124
|
+
"docs/",
|
|
127
125
|
"README.md",
|
|
128
126
|
"CHANGELOG.md",
|
|
129
127
|
"LICENSE",
|
|
130
|
-
"sample-store.mjs",
|
|
131
|
-
"sample-store.d.ts",
|
|
132
|
-
"sample-store/",
|
|
133
128
|
"NOTICE"
|
|
134
129
|
],
|
|
135
130
|
"engines": {
|
|
136
|
-
"node": ">=
|
|
131
|
+
"node": ">=22.12"
|
|
137
132
|
},
|
|
138
133
|
"sideEffects": false,
|
|
139
134
|
"scripts": {
|
|
140
|
-
"build": "
|
|
141
|
-
"test": "node --test 'test/*.test.js'",
|
|
142
|
-
"test:
|
|
135
|
+
"build": "bash ../../formal/lean4/Wasm/build-wasm.sh",
|
|
136
|
+
"test": "node --test 'test/*.test.js' 'test/*.test.mjs'",
|
|
137
|
+
"test:speed": "node ../../tests/perf/npm_speed_gate.mjs",
|
|
143
138
|
"prepublishOnly": "npm test"
|
|
144
139
|
},
|
|
140
|
+
"devDependencies": {
|
|
141
|
+
"esbuild": "^0.28.2"
|
|
142
|
+
},
|
|
145
143
|
"publishConfig": {
|
|
146
144
|
"access": "public"
|
|
147
145
|
}
|