@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.
Files changed (193) hide show
  1. package/CHANGELOG.md +503 -1
  2. package/NOTICE +2 -21
  3. package/README.md +117 -993
  4. package/api.d.ts +53 -0
  5. package/api.js +5 -0
  6. package/api.mjs +56 -0
  7. package/bin/engine.mjs +38 -19
  8. package/bin/factoidal.mjs +512 -66
  9. package/bin/pack.mjs +37 -0
  10. package/bin/serve-worker.mjs +32 -0
  11. package/bin/serve.mjs +423 -0
  12. package/bin/store-worker-host.mjs +1 -1
  13. package/bin/store.mjs +1 -1
  14. package/browser.d.ts +110 -507
  15. package/browser.js +151 -2257
  16. package/docs/fuseki-replacement.md +108 -0
  17. package/fn.d.ts +24 -95
  18. package/fn.js +104 -150
  19. package/index.d.ts +598 -259
  20. package/index.js +4 -234
  21. package/index.mjs +176 -84
  22. package/l4-assets/l4factoidal.js +47 -7
  23. package/l4-assets/l4factoidal.mjs +1 -1
  24. package/l4-assets/l4factoidal.wasm +0 -0
  25. package/l4-assets/version.json +11 -7
  26. package/lib/api.mjs +2300 -0
  27. package/lib/browser-engine.mjs +69 -0
  28. package/lib/dataset.mjs +377 -0
  29. package/lib/delta-log-hosts.mjs +191 -0
  30. package/lib/lean-entry.mjs +204 -0
  31. package/lib/pure.mjs +98 -0
  32. package/lib/store.mjs +115 -0
  33. package/lib/version.mjs +5 -0
  34. package/lws/README.md +97 -0
  35. package/lws/server.mjs +331 -0
  36. package/package.json +59 -61
  37. package/rdfjs.js +5 -536
  38. package/rdfjs.mjs +594 -0
  39. package/sample-store.d.ts +8 -16
  40. package/sample-store.mjs +39 -29
  41. package/solid/client/README.md +100 -0
  42. package/solid/client/index.mjs +277 -0
  43. package/solid/server/README.md +96 -0
  44. package/solid/server/index.mjs +317 -0
  45. package/store-host/deno.mjs +8 -0
  46. package/store-host/index.mjs +12 -0
  47. package/store-host/node.mjs +8 -0
  48. package/xmpp/README.md +107 -0
  49. package/xmpp/index.mjs +229 -0
  50. package/browser-wasm.js +0 -872
  51. package/factoidal-npm-entry.js +0 -15477
  52. package/factoidal-npm-entry.wasm.assets/code-5a7fc68f2ab1323718b8.wasm +0 -0
  53. package/factoidal-npm-entry.wasm.js +0 -455
  54. package/factoidal.js +0 -13118
  55. package/factoidal.wasm.assets/code-bbe6099bfb5b10c4c3ab.wasm +0 -0
  56. package/factoidal.wasm.js +0 -457
  57. package/hacl-init.js +0 -92
  58. package/hacl-wasm/FStar.wasm +0 -0
  59. package/hacl-wasm/Hacl_Bignum.wasm +0 -0
  60. package/hacl-wasm/Hacl_Bignum25519_51.wasm +0 -0
  61. package/hacl-wasm/Hacl_Bignum_Base.wasm +0 -0
  62. package/hacl-wasm/Hacl_Curve25519_51.wasm +0 -0
  63. package/hacl-wasm/Hacl_Ed25519.wasm +0 -0
  64. package/hacl-wasm/Hacl_Ed25519_PrecompTable.wasm +0 -0
  65. package/hacl-wasm/Hacl_Hash_Base.wasm +0 -0
  66. package/hacl-wasm/Hacl_Hash_SHA2.wasm +0 -0
  67. package/hacl-wasm/Hacl_IntTypes_Intrinsics.wasm +0 -0
  68. package/hacl-wasm/LowStar_Endianness.wasm +0 -0
  69. package/hacl-wasm/WasmSupport.wasm +0 -0
  70. package/hacl-wasm/api.js +0 -775
  71. package/hacl-wasm/api.json +0 -3787
  72. package/hacl-wasm/layouts.json +0 -1
  73. package/hacl-wasm/loader.js +0 -568
  74. package/hacl-wasm/shell.js +0 -12
  75. package/l4-core.d.ts +0 -159
  76. package/l4-core.js +0 -188
  77. package/l4.d.ts +0 -50
  78. package/l4.js +0 -104
  79. package/lib/api.js +0 -2300
  80. package/lib/engine-js.js +0 -165
  81. package/lib/engine-wasm.js +0 -300
  82. package/sample-store/CURRENT +0 -1
  83. package/sample-store/gen-1/manifest.sbm2 +0 -0
  84. package/sample-store/gen-1/manifest.tsv +0 -14
  85. package/sample-store/gen-1/predicate-0.ibk3 +0 -0
  86. package/sample-store/gen-1/predicate-0.ibk3.merkle +0 -1
  87. package/sample-store/gen-1/predicate-0.ibk3.oli2 +0 -0
  88. package/sample-store/gen-1/predicate-0.ibk3.oli2.merkle +0 -1
  89. package/sample-store/gen-1/predicate-0.ibk3.sri2 +0 -0
  90. package/sample-store/gen-1/predicate-0.ibk3.sri2.merkle +0 -1
  91. package/sample-store/gen-1/predicate-0.ibk3.tli1 +0 -0
  92. package/sample-store/gen-1/predicate-0.ibk3.tli1.merkle +0 -0
  93. package/sample-store/gen-1/predicate-1.ibk3 +0 -0
  94. package/sample-store/gen-1/predicate-1.ibk3.merkle +0 -0
  95. package/sample-store/gen-1/predicate-1.ibk3.oli2 +0 -0
  96. package/sample-store/gen-1/predicate-1.ibk3.oli2.merkle +0 -0
  97. package/sample-store/gen-1/predicate-1.ibk3.sri2 +0 -0
  98. package/sample-store/gen-1/predicate-1.ibk3.sri2.merkle +0 -1
  99. package/sample-store/gen-1/predicate-1.ibk3.tli1 +0 -0
  100. package/sample-store/gen-1/predicate-1.ibk3.tli1.merkle +0 -1
  101. package/sample-store/gen-1/predicate-10.ibk3 +0 -0
  102. package/sample-store/gen-1/predicate-10.ibk3.merkle +0 -1
  103. package/sample-store/gen-1/predicate-10.ibk3.oli2 +0 -0
  104. package/sample-store/gen-1/predicate-10.ibk3.oli2.merkle +0 -0
  105. package/sample-store/gen-1/predicate-10.ibk3.sri2 +0 -0
  106. package/sample-store/gen-1/predicate-10.ibk3.sri2.merkle +0 -1
  107. package/sample-store/gen-1/predicate-10.ibk3.tli1 +0 -0
  108. package/sample-store/gen-1/predicate-10.ibk3.tli1.merkle +0 -1
  109. package/sample-store/gen-1/predicate-11.ibk3 +0 -0
  110. package/sample-store/gen-1/predicate-11.ibk3.merkle +0 -0
  111. package/sample-store/gen-1/predicate-11.ibk3.oli2 +0 -0
  112. package/sample-store/gen-1/predicate-11.ibk3.oli2.merkle +0 -1
  113. package/sample-store/gen-1/predicate-11.ibk3.sri2 +0 -0
  114. package/sample-store/gen-1/predicate-11.ibk3.sri2.merkle +0 -1
  115. package/sample-store/gen-1/predicate-11.ibk3.tli1 +0 -0
  116. package/sample-store/gen-1/predicate-11.ibk3.tli1.merkle +0 -1
  117. package/sample-store/gen-1/predicate-12.ibk3 +0 -0
  118. package/sample-store/gen-1/predicate-12.ibk3.merkle +0 -1
  119. package/sample-store/gen-1/predicate-12.ibk3.oli2 +0 -0
  120. package/sample-store/gen-1/predicate-12.ibk3.oli2.merkle +0 -1
  121. package/sample-store/gen-1/predicate-12.ibk3.sri2 +0 -0
  122. package/sample-store/gen-1/predicate-12.ibk3.sri2.merkle +0 -1
  123. package/sample-store/gen-1/predicate-12.ibk3.tli1 +0 -0
  124. package/sample-store/gen-1/predicate-12.ibk3.tli1.merkle +0 -1
  125. package/sample-store/gen-1/predicate-2.ibk3 +0 -0
  126. package/sample-store/gen-1/predicate-2.ibk3.merkle +0 -1
  127. package/sample-store/gen-1/predicate-2.ibk3.oli2 +0 -0
  128. package/sample-store/gen-1/predicate-2.ibk3.oli2.merkle +0 -1
  129. package/sample-store/gen-1/predicate-2.ibk3.sri2 +0 -0
  130. package/sample-store/gen-1/predicate-2.ibk3.sri2.merkle +0 -0
  131. package/sample-store/gen-1/predicate-2.ibk3.tli1 +0 -0
  132. package/sample-store/gen-1/predicate-2.ibk3.tli1.merkle +0 -0
  133. package/sample-store/gen-1/predicate-3.ibk3 +0 -0
  134. package/sample-store/gen-1/predicate-3.ibk3.merkle +0 -1
  135. package/sample-store/gen-1/predicate-3.ibk3.oli2 +0 -0
  136. package/sample-store/gen-1/predicate-3.ibk3.oli2.merkle +0 -1
  137. package/sample-store/gen-1/predicate-3.ibk3.sri2 +0 -0
  138. package/sample-store/gen-1/predicate-3.ibk3.sri2.merkle +0 -1
  139. package/sample-store/gen-1/predicate-3.ibk3.tli1 +0 -0
  140. package/sample-store/gen-1/predicate-3.ibk3.tli1.merkle +0 -1
  141. package/sample-store/gen-1/predicate-4.ibk3 +0 -0
  142. package/sample-store/gen-1/predicate-4.ibk3.merkle +0 -0
  143. package/sample-store/gen-1/predicate-4.ibk3.oli2 +0 -0
  144. package/sample-store/gen-1/predicate-4.ibk3.oli2.merkle +0 -1
  145. package/sample-store/gen-1/predicate-4.ibk3.sri2 +0 -0
  146. package/sample-store/gen-1/predicate-4.ibk3.sri2.merkle +0 -1
  147. package/sample-store/gen-1/predicate-4.ibk3.tli1 +0 -0
  148. package/sample-store/gen-1/predicate-4.ibk3.tli1.merkle +0 -2
  149. package/sample-store/gen-1/predicate-5.ibk3 +0 -0
  150. package/sample-store/gen-1/predicate-5.ibk3.merkle +0 -1
  151. package/sample-store/gen-1/predicate-5.ibk3.oli2 +0 -0
  152. package/sample-store/gen-1/predicate-5.ibk3.oli2.merkle +0 -1
  153. package/sample-store/gen-1/predicate-5.ibk3.sri2 +0 -0
  154. package/sample-store/gen-1/predicate-5.ibk3.sri2.merkle +0 -1
  155. package/sample-store/gen-1/predicate-5.ibk3.tli1 +0 -0
  156. package/sample-store/gen-1/predicate-5.ibk3.tli1.merkle +0 -1
  157. package/sample-store/gen-1/predicate-6.ibk3 +0 -0
  158. package/sample-store/gen-1/predicate-6.ibk3.merkle +0 -1
  159. package/sample-store/gen-1/predicate-6.ibk3.oli2 +0 -0
  160. package/sample-store/gen-1/predicate-6.ibk3.oli2.merkle +0 -1
  161. package/sample-store/gen-1/predicate-6.ibk3.sri2 +0 -0
  162. package/sample-store/gen-1/predicate-6.ibk3.sri2.merkle +0 -1
  163. package/sample-store/gen-1/predicate-6.ibk3.tli1 +0 -0
  164. package/sample-store/gen-1/predicate-6.ibk3.tli1.merkle +0 -0
  165. package/sample-store/gen-1/predicate-7.ibk3 +0 -0
  166. package/sample-store/gen-1/predicate-7.ibk3.merkle +0 -0
  167. package/sample-store/gen-1/predicate-7.ibk3.oli2 +0 -0
  168. package/sample-store/gen-1/predicate-7.ibk3.oli2.merkle +0 -2
  169. package/sample-store/gen-1/predicate-7.ibk3.sri2 +0 -0
  170. package/sample-store/gen-1/predicate-7.ibk3.sri2.merkle +0 -1
  171. package/sample-store/gen-1/predicate-7.ibk3.tli1 +0 -0
  172. package/sample-store/gen-1/predicate-7.ibk3.tli1.merkle +0 -1
  173. package/sample-store/gen-1/predicate-8.ibk3 +0 -0
  174. package/sample-store/gen-1/predicate-8.ibk3.merkle +0 -1
  175. package/sample-store/gen-1/predicate-8.ibk3.oli2 +0 -0
  176. package/sample-store/gen-1/predicate-8.ibk3.oli2.merkle +0 -1
  177. package/sample-store/gen-1/predicate-8.ibk3.sri2 +0 -0
  178. package/sample-store/gen-1/predicate-8.ibk3.sri2.merkle +0 -1
  179. package/sample-store/gen-1/predicate-8.ibk3.tli1 +0 -0
  180. package/sample-store/gen-1/predicate-8.ibk3.tli1.merkle +0 -1
  181. package/sample-store/gen-1/predicate-9.ibk3 +0 -0
  182. package/sample-store/gen-1/predicate-9.ibk3.merkle +0 -1
  183. package/sample-store/gen-1/predicate-9.ibk3.oli2 +0 -0
  184. package/sample-store/gen-1/predicate-9.ibk3.oli2.merkle +0 -1
  185. package/sample-store/gen-1/predicate-9.ibk3.sri2 +0 -0
  186. package/sample-store/gen-1/predicate-9.ibk3.sri2.merkle +0 -3
  187. package/sample-store/gen-1/predicate-9.ibk3.tli1 +0 -0
  188. package/sample-store/gen-1/predicate-9.ibk3.tli1.merkle +0 -2
  189. package/select.d.ts +0 -116
  190. package/select.js +0 -492
  191. package/version.json +0 -102
  192. package/wasm.d.ts +0 -130
  193. 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.7.1",
4
- "description": "Factoidal: a linked information system with graph data and the Web at its heart - RDF parsing, SPARQL 1.1 query, canonicalization and entailment for Node and the browser (JS + Wasm), grounded in F* and Lean 4. Parser and algebra spec verified in F*; on-disk backend has unverified OCaml-side optimization layers being migrated back to F*.",
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
- "fstar",
14
- "formal-verification",
15
- "semantic-web"
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
- "./wasm": {
44
- "types": "./wasm.d.ts",
45
- "default": "./wasm.js"
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
- "./select": {
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
- "wasm.js",
101
- "wasm.d.ts",
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
- "l4.js",
106
- "l4.d.ts",
107
- "l4-core.js",
108
- "l4-core.d.ts",
112
+ "browser.js",
113
+ "browser.d.ts",
109
114
  "l4-assets/",
110
- "select.js",
111
- "select.d.ts",
115
+ "!l4-assets/l4factoidal-core.*",
112
116
  "lib/",
113
117
  "bin/",
118
+ "lws/",
119
+ "solid/",
114
120
  "store-host/",
115
- "browser.js",
116
- "browser.d.ts",
117
- "browser-wasm.js",
118
- "factoidal.js",
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": ">=20"
131
+ "node": ">=22.12"
137
132
  },
138
133
  "sideEffects": false,
139
134
  "scripts": {
140
- "build": "cd ../../formal/fstar && ./build-ocaml.sh npm",
141
- "test": "node --test 'test/*.test.js'",
142
- "test:smoke": "node test/smoke.js && node test/smoke-wasm.mjs",
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
  }