eyeling 1.27.8 → 1.28.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.
@@ -0,0 +1,378 @@
1
+ # RDF/JS integration in Eyeling
2
+
3
+ ## Short version
4
+
5
+ Eyeling still reasons over its normal N3 data model. RDF/JS support is an adapter layer:
6
+
7
+ ```text
8
+ RDF/JS quads, RDF/JS Quad terms, N3 text, or Eyeling rule objects
9
+
10
+ lib/rdfjs.js input normalization
11
+
12
+ Eyeling N3 triples/rules, including GraphTerm and log:nameOf encodings
13
+
14
+ normal parser + reasoner
15
+
16
+ N3 output and, when requested, RDF/JS quads
17
+ ```
18
+
19
+ The important RDF 1.2 mappings are:
20
+
21
+ ```text
22
+ RDF/JS Quad term in subject/object position
23
+ ↔ singleton N3 GraphTerm
24
+ ↔ RDF 1.2 triple term
25
+
26
+ RDF/JS named graph quad
27
+ ↔ graph log:nameOf { ... }
28
+ ↔ TriG-style named graph output/input
29
+ ```
30
+
31
+ So RDF/JS is now aligned with the same internal representation used by Eyeling's RDF 1.2/TriG compatibility mode.
32
+
33
+ ## Public API pieces
34
+
35
+ ```js
36
+ const {
37
+ reason,
38
+ reasonStream,
39
+ reasonRdfJs,
40
+ rdfjs,
41
+ } = require('eyeling');
42
+ ```
43
+
44
+ - `rdfjs` is Eyeling's lightweight RDF/JS-style `DataFactory`.
45
+ - `reasonStream(input, { rdfjs: true })` returns the normal structured result plus RDF/JS quad arrays.
46
+ - `reasonRdfJs(input, opts)` returns an async iterable of derived RDF/JS quads.
47
+ - `dataFactory` can be supplied in options to use another RDF/JS factory.
48
+
49
+ ## Lightweight DataFactory
50
+
51
+ `lib/rdfjs.js` defines small RDF/JS-compatible term classes:
52
+
53
+ - `NamedNode`
54
+ - `BlankNode`
55
+ - `Literal`
56
+ - `Variable`
57
+ - `DefaultGraph`
58
+ - `Quad`
59
+
60
+ Example:
61
+
62
+ ```js
63
+ const { rdfjs } = require('eyeling');
64
+
65
+ const ex = 'http://example.org/';
66
+ const s = rdfjs.namedNode(ex + 's');
67
+ const p = rdfjs.namedNode(ex + 'p');
68
+ const o = rdfjs.literal('hello');
69
+ const q = rdfjs.quad(s, p, o, rdfjs.defaultGraph());
70
+ ```
71
+
72
+ Each term has `termType`, `value`, and `equals(other)`. Literals also carry `language` and `datatype`. `defaultGraph()` returns a singleton default graph term.
73
+
74
+ ## RDF/JS input forms
75
+
76
+ Eyeling accepts RDF/JS quads through any of these object keys:
77
+
78
+ ```js
79
+ { quads: iterableOfQuads }
80
+ { facts: iterableOfQuads }
81
+ { dataset: iterableOfQuads }
82
+ ```
83
+
84
+ The iterable may be synchronous for `reasonStream()` and may be synchronous or asynchronous for `reasonRdfJs()`.
85
+
86
+ Accepted RDF/JS term types are:
87
+
88
+ - `NamedNode`
89
+ - `BlankNode`
90
+ - `Literal`
91
+ - `Variable`
92
+ - `Quad` in subject or object position
93
+ - `DefaultGraph` only as a quad graph
94
+
95
+ `Quad` terms are rejected in predicate position, because RDF triple terms are not valid RDF predicates. A `Quad` term used as a quoted triple term must itself have the default graph.
96
+
97
+ ## Default graph input quads
98
+
99
+ A normal RDF/JS quad:
100
+
101
+ ```js
102
+ rdfjs.quad(
103
+ rdfjs.namedNode('http://example.org/s'),
104
+ rdfjs.namedNode('http://example.org/p'),
105
+ rdfjs.literal('hello'),
106
+ )
107
+ ```
108
+
109
+ becomes the N3 fact:
110
+
111
+ ```n3
112
+ <http://example.org/s> <http://example.org/p> "hello" .
113
+ ```
114
+
115
+ and internally:
116
+
117
+ ```js
118
+ Triple(
119
+ Iri('http://example.org/s'),
120
+ Iri('http://example.org/p'),
121
+ Literal('"hello"')
122
+ )
123
+ ```
124
+
125
+ Language and datatype literals are preserved:
126
+
127
+ ```n3
128
+ "hello"@en
129
+ "42"^^<http://www.w3.org/2001/XMLSchema#integer>
130
+ ```
131
+
132
+ ## Named graph input quads
133
+
134
+ Named graph RDF/JS quads are now accepted.
135
+
136
+ Input:
137
+
138
+ ```js
139
+ rdfjs.quad(
140
+ rdfjs.namedNode('http://example.org/s'),
141
+ rdfjs.namedNode('http://example.org/p'),
142
+ rdfjs.namedNode('http://example.org/o'),
143
+ rdfjs.namedNode('http://example.org/g'),
144
+ )
145
+ ```
146
+
147
+ is represented internally as the same shape used for TriG compatibility:
148
+
149
+ ```n3
150
+ <http://example.org/g> log:nameOf {
151
+ <http://example.org/s> <http://example.org/p> <http://example.org/o> .
152
+ } .
153
+ ```
154
+
155
+ Multiple input quads with the same graph are grouped into one `log:nameOf` graph term:
156
+
157
+ ```n3
158
+ :g log:nameOf {
159
+ :s1 :p :o1 .
160
+ :s2 :p :o2 .
161
+ } .
162
+ ```
163
+
164
+ On RDF/JS output, this `log:nameOf` representation expands back to RDF/JS quads with the corresponding `graph` term.
165
+
166
+ ## RDF/JS `Quad` terms as RDF 1.2 triple terms
167
+
168
+ RDF/JS `Quad` terms in subject or object position are now accepted as RDF 1.2 quoted triple terms.
169
+
170
+ Input:
171
+
172
+ ```js
173
+ const quoted = rdfjs.quad(
174
+ rdfjs.namedNode('http://example.org/s'),
175
+ rdfjs.namedNode('http://example.org/p'),
176
+ rdfjs.namedNode('http://example.org/o'),
177
+ );
178
+
179
+ rdfjs.quad(
180
+ rdfjs.namedNode('http://example.org/obs'),
181
+ rdfjs.namedNode('http://example.org/about'),
182
+ quoted,
183
+ );
184
+ ```
185
+
186
+ is normalized to a singleton N3 graph term:
187
+
188
+ ```n3
189
+ <http://example.org/obs> <http://example.org/about> {
190
+ <http://example.org/s> <http://example.org/p> <http://example.org/o> .
191
+ } .
192
+ ```
193
+
194
+ With RDF compatibility output enabled, the same structure can print as RDF 1.2 triple-term syntax:
195
+
196
+ ```turtle
197
+ <http://example.org/obs> <http://example.org/about> <<(
198
+ <http://example.org/s> <http://example.org/p> <http://example.org/o>
199
+ )>> .
200
+ ```
201
+
202
+ Internally, the object is:
203
+
204
+ ```js
205
+ GraphTerm([
206
+ Triple(Iri(s), Iri(p), Iri(o))
207
+ ])
208
+ ```
209
+
210
+ This is the same representation used by the lexer when `<<( s p o )>>` is parsed in RDF compatibility mode.
211
+
212
+ ## Mixing `{ quads, n3 }`
213
+
214
+ The object form implied by the README is now supported: RDF/JS quads and N3 text are merged before reasoning.
215
+
216
+ Example:
217
+
218
+ ```js
219
+ const { reasonStream, rdfjs } = require('eyeling');
220
+ const ex = 'http://example.org/';
221
+
222
+ const result = reasonStream(
223
+ {
224
+ n3: `
225
+ @prefix : <http://example.org/> .
226
+ { ?x :p ?y } => { ?x :q ?y } .
227
+ `,
228
+ quads: [
229
+ rdfjs.quad(
230
+ rdfjs.namedNode(ex + 'a'),
231
+ rdfjs.namedNode(ex + 'p'),
232
+ rdfjs.namedNode(ex + 'b'),
233
+ ),
234
+ ],
235
+ },
236
+ {
237
+ rdfjs: true,
238
+ includeInputFactsInClosure: false,
239
+ },
240
+ );
241
+
242
+ console.log(result.closureN3);
243
+ console.log(result.closureQuads);
244
+ ```
245
+
246
+ The RDF/JS fact supplies `:a :p :b`; the N3 rule derives `:a :q :b`.
247
+
248
+ The merge also works with Eyeling rule objects and RDF/JS facts. In that path, `normalizeParsedReasonerInputSync()` or `normalizeParsedReasonerInputAsync()` builds an Eyeling document and appends the RDF/JS quads as facts.
249
+
250
+ ## RDF/JS output from `reasonStream()`
251
+
252
+ When `rdfjs: true` is passed to `reasonStream()`, the result can include:
253
+
254
+ ```js
255
+ result.closureQuads
256
+ result.queryQuads
257
+ ```
258
+
259
+ The `onDerived` callback receives RDF/JS quads too:
260
+
261
+ ```js
262
+ reasonStream(input, {
263
+ rdfjs: true,
264
+ onDerived({ triple, quad, quads, df }) {
265
+ console.log(triple); // N3 or RDF-compatible text form
266
+ console.log(quad); // first RDF/JS quad when exactly/conveniently available
267
+ console.log(quads); // all RDF/JS quads emitted for this derived fact
268
+ },
269
+ });
270
+ ```
271
+
272
+ A single internal triple can produce more than one RDF/JS quad when it is a `log:nameOf` named-graph wrapper, so the plural `quads` payload is the complete form. `quad` is present when there is exactly one emitted quad.
273
+
274
+ ## Output conversion rules
275
+
276
+ The normal output path now uses `internalTripleToRdfJsQuads()`.
277
+
278
+ Ordinary terms map directly:
279
+
280
+ ```text
281
+ Iri → NamedNode
282
+ Blank → BlankNode
283
+ Literal → Literal
284
+ Var → Variable
285
+ ```
286
+
287
+ Singleton graph terms in subject or object position map to RDF/JS `Quad` terms:
288
+
289
+ ```n3
290
+ :x :holds { :s :p :o } .
291
+ ```
292
+
293
+ becomes an RDF/JS quad whose object is:
294
+
295
+ ```js
296
+ rdfjs.quad(
297
+ rdfjs.namedNode('http://example.org/s'),
298
+ rdfjs.namedNode('http://example.org/p'),
299
+ rdfjs.namedNode('http://example.org/o'),
300
+ rdfjs.defaultGraph(),
301
+ )
302
+ ```
303
+
304
+ Named graph wrappers map back to named-graph RDF/JS quads:
305
+
306
+ ```n3
307
+ :g log:nameOf {
308
+ :s :p :o .
309
+ } .
310
+ ```
311
+
312
+ becomes:
313
+
314
+ ```js
315
+ rdfjs.quad(s, p, o, g)
316
+ ```
317
+
318
+ ## Remaining N3-only cases
319
+
320
+ `skipUnsupportedRdfJs` is still useful, but the previous RDF 1.2 cases no longer require it.
321
+
322
+ Still unsupported as ordinary RDF/JS output:
323
+
324
+ - non-singleton `GraphTerm` in subject/object position;
325
+ - `GraphTerm` in predicate or graph position;
326
+ - `ListTerm` and `OpenListTerm`;
327
+ - other N3-only terms that do not have an RDF/JS representation.
328
+
329
+ By default, unsupported output raises a conversion error. With:
330
+
331
+ ```js
332
+ skipUnsupportedRdfJs: true
333
+ ```
334
+
335
+ Eyeling keeps the N3 result and omits the unsupported RDF/JS quads from `closureQuads`, `queryQuads`, and `onDerived` payloads.
336
+
337
+ ## `reasonRdfJs()`
338
+
339
+ `reasonRdfJs(input, opts)` returns an async iterable of derived RDF/JS quads:
340
+
341
+ ```js
342
+ const { reasonRdfJs, rdfjs } = require('eyeling');
343
+
344
+ for await (const quad of reasonRdfJs({
345
+ quads: [
346
+ rdfjs.quad(
347
+ rdfjs.namedNode('http://example.org/a'),
348
+ rdfjs.namedNode('http://example.org/p'),
349
+ rdfjs.namedNode('http://example.org/b'),
350
+ ),
351
+ ],
352
+ n3: `
353
+ @prefix : <http://example.org/> .
354
+ { ?x :p ?y } => { ?x :q ?y } .
355
+ `,
356
+ })) {
357
+ console.log(quad.subject.value, quad.predicate.value, quad.object.value);
358
+ }
359
+ ```
360
+
361
+ Internally, `reasonRdfJs()`:
362
+
363
+ 1. normalizes the input, collecting async RDF/JS input if necessary;
364
+ 2. runs `reasonStream()` on the normalized N3/Eyeling document;
365
+ 3. converts each derived fact with `internalTripleToRdfJsQuads()`;
366
+ 4. yields all resulting RDF/JS quads.
367
+
368
+ It is an async output interface, not a streaming RDF parser. Async input quads are collected before reasoning starts.
369
+
370
+ ## Practical summary
371
+
372
+ Use RDF/JS integration when Eyeling needs to sit inside a JavaScript RDF pipeline:
373
+
374
+ - feed default-graph or named-graph RDF/JS quads as facts;
375
+ - use RDF/JS `Quad` terms for RDF 1.2 triple terms in subject/object positions;
376
+ - mix RDF/JS facts with N3 text using `{ quads, n3 }`;
377
+ - request RDF/JS output with `rdfjs: true` or `reasonRdfJs()`;
378
+ - keep `skipUnsupportedRdfJs: true` only for genuinely N3-only terms such as lists or non-singleton formulas.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eyeling",
3
- "version": "1.27.8",
3
+ "version": "1.28.0",
4
4
  "description": "A minimal Notation3 (N3) reasoner in JavaScript.",
5
5
  "main": "./index.js",
6
6
  "keywords": [
@@ -30,7 +30,9 @@
30
30
  "lib",
31
31
  "test",
32
32
  "tools",
33
- "examples"
33
+ "examples",
34
+ "spec",
35
+ "notes"
34
36
  ],
35
37
  "engines": {
36
38
  "node": ">=18"