@factoidal/core 0.15.0 → 0.17.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/lib/store.mjs CHANGED
@@ -99,16 +99,18 @@ export function manifestKeys(inspectEnvelope) {
99
99
  return keys;
100
100
  }
101
101
 
102
- const HEX = (() => {
103
- const t = new Array(256);
104
- for (let b = 0; b < 256; b += 1) t[b] = b.toString(16).padStart(2, '0');
105
- return t;
106
- })();
102
+ const HEX_DIGIT = Uint8Array.from('0123456789abcdef', (c) => c.charCodeAt(0));
103
+ const HEX_DECODER = new TextDecoder();
107
104
 
108
105
  /** Lowercase hexadecimal: the transport the manifest argument of the store
109
106
  * ops takes. Artifact bytes never take this path. */
110
107
  export function hexOfBytes(bytes) {
111
- const parts = new Array(bytes.length);
112
- for (let i = 0; i < bytes.length; i += 1) parts[i] = HEX[bytes[i]];
113
- return parts.join('');
108
+ // One byte buffer of ASCII digits, decoded once (issue 1039; see
109
+ // bin/engine.mjs hexOfBytes).
110
+ const out = new Uint8Array(bytes.length * 2);
111
+ for (let i = 0, at = 0; i < bytes.length; i += 1, at += 2) {
112
+ out[at] = HEX_DIGIT[bytes[i] >> 4];
113
+ out[at + 1] = HEX_DIGIT[bytes[i] & 15];
114
+ }
115
+ return HEX_DECODER.decode(out);
114
116
  }
package/lib/version.mjs CHANGED
@@ -2,4 +2,4 @@
2
2
  // needs import attributes, which not every runtime this package targets
3
3
  // reads). test/dts-drift.test.js checks that it equals package.json's
4
4
  // `version`.
5
- export const version = '0.15.0';
5
+ export const version = '0.17.0';
package/lws/server.mjs CHANGED
@@ -584,11 +584,33 @@ export async function createLwsServer (options = {}) {
584
584
  let settings = { ...options }
585
585
  let opening = null
586
586
  let opened = null
587
+ let authz = null
587
588
 
588
- /** The storage handle, opened once. */
589
+ /** The storage handle, opened once. With `authz` (an object of
590
+ * `openAuthorizationServer` options), an authorization server that shares
591
+ * the storage's issuer is opened first, on `mountedOn: baseIri`; the
592
+ * storage then trusts that issuer with the JWK Set the engine published
593
+ * (L4Factoidal/Authz/Mount.lean). */
589
594
  function storage () {
590
595
  if (opening === null) {
591
- opening = openLwsStorage(settings).then((value) => { opened = value; return value })
596
+ opening = (async () => {
597
+ let storageSettings = settings
598
+ if (settings.authz !== undefined && settings.authz !== null) {
599
+ if (typeof settings.baseIri !== 'string') throw new LwsHostError('authz needs baseIri')
600
+ const { openAuthorizationServer } = await import('../authz/server.mjs')
601
+ authz = await openAuthorizationServer({
602
+ ...settings.authz,
603
+ ...(settings.engine !== undefined ? { engine: settings.engine } : {}),
604
+ ...(settings.auth && typeof settings.auth.audience === 'string' ? { audience: settings.auth.audience } : {}),
605
+ mountedOn: settings.baseIri
606
+ })
607
+ const issuers = [{ issuer: authz.issuer, jwks: authz.jwks }]
608
+ const prior = settings.auth && Array.isArray(settings.auth.issuers) ? settings.auth.issuers : []
609
+ storageSettings = { ...settings, auth: { ...(settings.auth || {}), issuers: issuers.concat(prior) } }
610
+ }
611
+ opened = await openLwsStorage(storageSettings)
612
+ return opened
613
+ })()
592
614
  }
593
615
  return opening
594
616
  }
@@ -610,7 +632,13 @@ export async function createLwsServer (options = {}) {
610
632
  }
611
633
  let answer
612
634
  try {
613
- answer = await (await storage()).step(record)
635
+ const store = await storage()
636
+ // One port for both servers: the engine names the authorization
637
+ // server's paths (L4Factoidal.Authz.owns); every other request is the
638
+ // storage's. The token request body is form text.
639
+ answer = authz !== null && authz.owns(record.target)
640
+ ? await authz.step({ ...record, body: new TextDecoder('utf-8').decode(record.body) })
641
+ : await store.step(record)
614
642
  } catch (error) {
615
643
  // A failure of this host or of the engine, not a protocol answer.
616
644
  // The engine's own words are kept; nothing is invented.
@@ -627,6 +655,8 @@ export async function createLwsServer (options = {}) {
627
655
  return {
628
656
  server,
629
657
  storage,
658
+ /** The authorization server sharing the storage's issuer, or null. */
659
+ authz () { return authz },
630
660
  /** Set the storage root's absolute IRI, before the first request. */
631
661
  setBaseIri (value) {
632
662
  if (opening !== null) {
@@ -646,6 +676,7 @@ export async function createLwsServer (options = {}) {
646
676
  return new Promise((resolve) => {
647
677
  server.close(() => {
648
678
  if (opened !== null) opened.close()
679
+ if (authz !== null) authz.close()
649
680
  resolve()
650
681
  })
651
682
  })
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@factoidal/core",
3
- "version": "0.15.0",
3
+ "version": "0.17.0",
4
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",
@@ -142,6 +142,7 @@
142
142
  "scripts": {
143
143
  "build": "bash ../../formal/lean4/Wasm/build-wasm.sh",
144
144
  "test": "node --test 'test/*.test.js' 'test/*.test.mjs'",
145
+ "test:jena": "node --test 'test/jena/*.test.mjs'",
145
146
  "test:speed": "node ../../tests/perf/npm_speed_gate.mjs",
146
147
  "test:costs": "node ../../tests/perf/npm_costs.mjs",
147
148
  "prepublishOnly": "npm test"