eyeling 1.7.17 → 1.7.18

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/LICENSE.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # MIT License
2
2
 
3
- ### Copyright 2021-2026 Jos De Roo, KNoWS office of IDLab, Ghent University - imec
3
+ ### Copyright 2025-2026 Jos De Roo, KNoWS office of IDLab, Ghent University - imec
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/README.md CHANGED
@@ -17,13 +17,14 @@ A [Notation3 (N3)](https://notation3.org/) reasoner in **JavaScript**.
17
17
  Try it here:
18
18
 
19
19
  - [Eyeling playground](https://eyereasoner.github.io/eyeling/demo)
20
- - [Eyeling streaming playground](https://eyereasoner.github.io/eyeling/stream)
21
-
22
- The playground runs `eyeling` client-side. You can:
20
+ - Edit an N3 program directly.
21
+ - Load an N3 program from a URL (in the "Load N3 from URL" box or as ?url=...).
22
+ - Share a link with the program encoded in the URL fragment (`#...`).
23
23
 
24
- - edit an N3 program directly
25
- - load an N3 program from a URL (in the "Load N3 from URL" box or as ?url=...)
26
- - share a link with the program encoded in the URL fragment (`#...`)
24
+ - [Eyeling streaming playground](https://eyereasoner.github.io/eyeling/stream)
25
+ - Browse a Wikidata entity, load its facts, and see Eyeling’s **deductive closure appear incrementally** as triples are derived.
26
+ - Edit **N3 rules live** and re-run to watch how different inference rules change what gets derived.
27
+ - Demo **CORS-safe dynamic fetching**: derived “fetch requests” can trigger extra facts (e.g., Wikiquote extracts) that are injected and re-reasoned.
27
28
 
28
29
  ## Quick start
29
30
 
@@ -120,6 +121,7 @@ Options:
120
121
  -s, --super-restricted Disable all builtins except => and <=.
121
122
  -a, --ast Print parsed AST as JSON and exit.
122
123
  --strings Print log:outputString strings (ordered by key) instead of N3 output.
124
+ --enforce-https Rewrite http:// IRIs to https:// for log dereferencing builtins.
123
125
  ```
124
126
 
125
127
  By default, `eyeling`:
@@ -165,6 +167,7 @@ The CLI prints only newly derived forward facts.
165
167
  - backward rules are indexed by head predicate
166
168
  - the backward prover is **iterative** (explicit stack), so deep chains won’t blow the JS call stack
167
169
  - for very deep backward chains, substitutions may be compactified (semantics-preserving) to avoid quadratic “copy a growing substitution object” behavior
170
+ - if the head is **structurally ground** and has no head blanks, one body proof suffices—and if the head triples are already known, we can skip the body proof.
168
171
 
169
172
  ## Blank nodes and quantification
170
173
 
@@ -202,7 +205,7 @@ Rules whose conclusion is `false` are treated as hard failures:
202
205
 
203
206
  As soon as the premise is provable, `eyeling` exits with status code `2`.
204
207
 
205
- ## Syntax + built-ins
208
+ ## Syntax
206
209
 
207
210
  `eyeling`’s parser targets (nearly) the full *Notation3 Language* grammar from the [W3C N3 Community Group spec](https://w3c.github.io/N3/spec/).
208
211
 
@@ -223,14 +226,9 @@ Commonly used N3/Turtle features:
223
226
  - Resource paths (`!` and `^`)
224
227
  - `#` line comments
225
228
 
226
- `eyeling` implements a pragmatic subset of common N3 builtin families and evaluates them during backward goal proving:
229
+ ## Builtins
227
230
 
228
- - **crypto**: `crypto:md5` `crypto:sha` `crypto:sha256` `crypto:sha512`
229
- - **list**: `list:append` `list:first` `list:firstRest` `list:in` `list:iterate` `list:last` `list:length` `list:map` `list:member` `list:memberAt` `list:notMember` `list:remove` `list:rest` `list:reverse` `list:sort`
230
- - **log**: `log:collectAllIn` `log:content` `log:dtlit` `log:equalTo` `log:forAllIn` `log:impliedBy` `log:implies` `log:includes` `log:langlit` `log:notEqualTo` `log:notIncludes` `log:outputString` `log:parsedAsN3` `log:rawType` `log:semantics` `log:semanticsOrError` `log:skolem` `log:trace` `log:uri`
231
- - **math**: `math:absoluteValue` `math:acos` `math:asin` `math:atan` `math:cos` `math:cosh` `math:degrees` `math:difference` `math:equalTo` `math:exponentiation` `math:greaterThan` `math:integerQuotient` `math:lessThan` `math:negation` `math:notEqualTo` `math:notGreaterThan` `math:notLessThan` `math:product` `math:quotient` `math:remainder` `math:rounded` `math:sin` `math:sinh` `math:sum` `math:tan` `math:tanh`
232
- - **string**: `string:concatenation` `string:contains` `string:containsIgnoringCase` `string:endsWith` `string:equalIgnoringCase` `string:format` `string:greaterThan` `string:jsonPointer` `string:lessThan` `string:matches` `string:notEqualIgnoringCase` `string:notGreaterThan` `string:notLessThan` `string:notMatches` `string:replace` `string:scrape` `string:startsWith`
233
- - **time**: `time:day` `time:hour` `time:localTime` `time:minute` `time:month` `time:second` `time:timeZone` `time:year`
231
+ `eyeling` implements the builtins described in [eyeling-builtins](https://github.com/eyereasoner/eyeling/blob/main/eyeling-builtins.ttl).
234
232
 
235
233
  ## License
236
234
 
package/eyeling.js CHANGED
@@ -104,6 +104,17 @@ const __logSemanticsCache = new Map(); // iri -> GraphTerm | null (null means pa
104
104
  const __logSemanticsOrErrorCache = new Map(); // iri -> Term (GraphTerm | Literal) for log:semanticsOrError
105
105
  const __logConclusionCache = new WeakMap(); // GraphTerm -> GraphTerm (deductive closure)
106
106
 
107
+ // When enabled, force http:// IRIs to be dereferenced as https://
108
+ // (CLI: --enforce-https, API: reasonStream({ enforceHttps: true })).
109
+ let enforceHttpsEnabled = false;
110
+
111
+ function __maybeEnforceHttps(iri) {
112
+ if (!enforceHttpsEnabled) return iri;
113
+ return typeof iri === 'string' && iri.startsWith('http://')
114
+ ? 'https://' + iri.slice('http://'.length)
115
+ : iri;
116
+ }
117
+
107
118
  // Environment detection (Node vs Browser/Worker).
108
119
  // Eyeling is primarily synchronous, so we use sync XHR in browsers for log:content/log:semantics.
109
120
  // Note: Browser fetches are subject to CORS; use CORS-enabled resources or a proxy.
@@ -150,9 +161,9 @@ function __fetchHttpTextSyncBrowser(url) {
150
161
 
151
162
  function __normalizeDerefIri(iriNoFrag) {
152
163
  // In Node, treat non-http as local path; leave as-is.
153
- if (__IS_NODE) return iriNoFrag;
164
+ if (__IS_NODE) return __maybeEnforceHttps(iriNoFrag);
154
165
  // In browsers/workers, resolve relative references against the page URL.
155
- return __resolveBrowserUrl(iriNoFrag);
166
+ return __maybeEnforceHttps(__resolveBrowserUrl(iriNoFrag));
156
167
  }
157
168
 
158
169
  function __stripFragment(iri) {
@@ -195,9 +206,17 @@ function __fetchHttpTextViaSubprocess(url) {
195
206
  const cp = require('child_process');
196
207
  // Use a subprocess so this code remains synchronous without rewriting the whole reasoner to async.
197
208
  const script = `
209
+ const enforceHttps = ${enforceHttpsEnabled ? 'true' : 'false'};
198
210
  const url = process.argv[1];
199
211
  const maxRedirects = 10;
212
+ function norm(u) {
213
+ if (enforceHttps && typeof u === 'string' && u.startsWith('http://')) {
214
+ return 'https://' + u.slice('http://'.length);
215
+ }
216
+ return u;
217
+ }
200
218
  function get(u, n) {
219
+ u = norm(u);
201
220
  if (n > maxRedirects) { console.error('Too many redirects'); process.exit(3); }
202
221
  let mod;
203
222
  if (u.startsWith('https://')) mod = require('https');
@@ -219,7 +238,8 @@ function __fetchHttpTextViaSubprocess(url) {
219
238
  const req = mod.request(opts, (res) => {
220
239
  const sc = res.statusCode || 0;
221
240
  if (sc >= 300 && sc < 400 && res.headers && res.headers.location) {
222
- const next = new URL(res.headers.location, u).toString();
241
+ let next = new URL(res.headers.location, u).toString();
242
+ next = norm(next);
223
243
  res.resume();
224
244
  return get(next, n + 1);
225
245
  }
@@ -6991,8 +7011,16 @@ function __collectOutputStringsFromFacts(facts, prefixes) {
6991
7011
  }
6992
7012
 
6993
7013
  function reasonStream(n3Text, opts = {}) {
6994
- const { baseIri = null, proof = false, onDerived = null, includeInputFactsInClosure = true } = opts;
6995
-
7014
+ const {
7015
+ baseIri = null,
7016
+ proof = false,
7017
+ onDerived = null,
7018
+ includeInputFactsInClosure = true,
7019
+ enforceHttps = false,
7020
+ } = opts;
7021
+
7022
+ const __oldEnforceHttps = enforceHttpsEnabled;
7023
+ enforceHttpsEnabled = !!enforceHttps;
6996
7024
  proofCommentsEnabled = !!proof;
6997
7025
 
6998
7026
  const toks = lex(n3Text);
@@ -7020,12 +7048,14 @@ function reasonStream(n3Text, opts = {}) {
7020
7048
 
7021
7049
  const closureTriples = includeInputFactsInClosure ? facts : derived.map((d) => d.fact);
7022
7050
 
7023
- return {
7051
+ const __out = {
7024
7052
  prefixes,
7025
7053
  facts, // saturated closure (Triple[])
7026
7054
  derived, // DerivedFact[]
7027
7055
  closureN3: closureTriples.map((t) => tripleToN3(t, prefixes)).join('\n'),
7028
7056
  };
7057
+ enforceHttpsEnabled = __oldEnforceHttps;
7058
+ return __out;
7029
7059
  }
7030
7060
 
7031
7061
  // Minimal export surface for Node + browser/worker
@@ -7056,7 +7086,8 @@ function main() {
7056
7086
  ` -n, --no-proof-comments Disable proof explanations (default).\n` +
7057
7087
  ` -s, --super-restricted Disable all builtins except => and <=.\n` +
7058
7088
  ` -a, --ast Print parsed AST as JSON and exit.\n` +
7059
- ` --strings Print log:outputString strings (ordered by key) instead of N3 output.\n`;
7089
+ ` --strings Print log:outputString strings (ordered by key) instead of N3 output.\n` +
7090
+ ` --enforce-https Rewrite http:// IRIs to https:// for log dereferencing builtins.\n`;
7060
7091
  (toStderr ? console.error : console.log)(msg);
7061
7092
  }
7062
7093
 
@@ -7079,6 +7110,11 @@ function main() {
7079
7110
 
7080
7111
  const outputStringsMode = argv.includes('--strings');
7081
7112
 
7113
+ // --enforce-https: rewrite http:// -> https:// for log dereferencing builtins
7114
+ if (argv.includes('--enforce-https')) {
7115
+ enforceHttpsEnabled = true;
7116
+ }
7117
+
7082
7118
  // --proof-comments / -p: enable proof explanations
7083
7119
  if (argv.includes('--proof-comments') || argv.includes('-p')) {
7084
7120
  proofCommentsEnabled = true;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eyeling",
3
- "version": "1.7.17",
3
+ "version": "1.7.18",
4
4
  "description": "A minimal Notation3 (N3) reasoner in JavaScript.",
5
5
  "main": "./index.js",
6
6
  "keywords": [