eyeling 1.7.6 → 1.7.7

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 (2) hide show
  1. package/eyeling.js +82 -11
  2. package/package.json +1 -1
package/eyeling.js CHANGED
@@ -91,6 +91,61 @@ const __logContentCache = new Map(); // iri -> string | null (null means fetch/r
91
91
  const __logSemanticsCache = new Map(); // iri -> GraphTerm | null (null means parse failed)
92
92
  const __logConclusionCache = new WeakMap(); // GraphTerm -> GraphTerm (deductive closure)
93
93
 
94
+ // Environment detection (Node vs Browser/Worker).
95
+ // Eyeling is primarily synchronous, so we use sync XHR in browsers for log:content/log:semantics.
96
+ // Note: Browser fetches are subject to CORS; use CORS-enabled resources or a proxy.
97
+ const __IS_NODE =
98
+ typeof process !== 'undefined' &&
99
+ !!(process.versions && process.versions.node);
100
+
101
+ function __hasXmlHttpRequest() {
102
+ return typeof XMLHttpRequest !== 'undefined';
103
+ }
104
+
105
+ function __resolveBrowserUrl(ref) {
106
+ if (!ref) return ref;
107
+ // If already absolute, keep as-is.
108
+ if (/^[A-Za-z][A-Za-z0-9+.-]*:/.test(ref)) return ref;
109
+ const base =
110
+ (typeof document !== 'undefined' && document.baseURI) ||
111
+ (typeof location !== 'undefined' && location.href) ||
112
+ '';
113
+ try {
114
+ return new URL(ref, base).toString();
115
+ } catch {
116
+ return ref;
117
+ }
118
+ }
119
+
120
+ function __fetchHttpTextSyncBrowser(url) {
121
+ if (!__hasXmlHttpRequest()) return null;
122
+ try {
123
+ const xhr = new XMLHttpRequest();
124
+ xhr.open('GET', url, false); // synchronous
125
+ try {
126
+ xhr.setRequestHeader(
127
+ 'Accept',
128
+ 'text/n3, text/turtle, application/n-triples, application/n-quads, text/plain;q=0.1, */*;q=0.01'
129
+ );
130
+ } catch {
131
+ // Some environments restrict setting headers (ignore).
132
+ }
133
+ xhr.send(null);
134
+ const sc = xhr.status || 0;
135
+ if (sc < 200 || sc >= 300) return null;
136
+ return xhr.responseText;
137
+ } catch {
138
+ return null;
139
+ }
140
+ }
141
+
142
+ function __normalizeDerefIri(iriNoFrag) {
143
+ // In Node, treat non-http as local path; leave as-is.
144
+ if (__IS_NODE) return iriNoFrag;
145
+ // In browsers/workers, resolve relative references against the page URL.
146
+ return __resolveBrowserUrl(iriNoFrag);
147
+ }
148
+
94
149
  function __stripFragment(iri) {
95
150
  const i = iri.indexOf('#');
96
151
  return i >= 0 ? iri.slice(0, i) : iri;
@@ -115,6 +170,7 @@ function __fileIriToPath(fileIri) {
115
170
  }
116
171
 
117
172
  function __readFileText(pathOrFileIri) {
173
+ if (!__IS_NODE) return null;
118
174
  const fs = require('fs');
119
175
  let path = pathOrFileIri;
120
176
  if (__isFileIri(pathOrFileIri)) path = __fileIriToPath(pathOrFileIri);
@@ -126,6 +182,7 @@ function __readFileText(pathOrFileIri) {
126
182
  }
127
183
 
128
184
  function __fetchHttpTextViaSubprocess(url) {
185
+ if (!__IS_NODE) return null;
129
186
  const cp = require('child_process');
130
187
  // Use a subprocess so this code remains synchronous without rewriting the whole reasoner to async.
131
188
  const script = `
@@ -181,16 +238,27 @@ function __fetchHttpTextViaSubprocess(url) {
181
238
  }
182
239
 
183
240
  function __derefTextSync(iriNoFrag) {
184
- if (__logContentCache.has(iriNoFrag)) return __logContentCache.get(iriNoFrag);
241
+ const norm = __normalizeDerefIri(iriNoFrag);
242
+ const key = typeof norm === 'string' && norm ? norm : iriNoFrag;
243
+
244
+ if (__logContentCache.has(key)) return __logContentCache.get(key);
185
245
 
186
246
  let text = null;
187
- if (__isHttpIri(iriNoFrag)) {
188
- text = __fetchHttpTextViaSubprocess(iriNoFrag);
247
+
248
+ if (__IS_NODE) {
249
+ if (__isHttpIri(key)) {
250
+ text = __fetchHttpTextViaSubprocess(key);
251
+ } else {
252
+ // Treat any non-http(s) IRI as a local path (including file://), for basic usability.
253
+ text = __readFileText(key);
254
+ }
189
255
  } else {
190
- // Treat any non-http(s) IRI as a local path (including file://), for basic usability.
191
- text = __readFileText(iriNoFrag);
256
+ // Browser / Worker: we can only dereference over HTTP(S), and it must pass CORS.
257
+ const url = typeof norm === 'string' && norm ? norm : key;
258
+ if (__isHttpIri(url)) text = __fetchHttpTextSyncBrowser(url);
192
259
  }
193
- __logContentCache.set(iriNoFrag, text);
260
+
261
+ __logContentCache.set(key, text);
194
262
  return text;
195
263
  }
196
264
 
@@ -218,19 +286,22 @@ function __parseSemanticsToFormula(text, baseIri) {
218
286
  }
219
287
 
220
288
  function __derefSemanticsSync(iriNoFrag) {
221
- if (__logSemanticsCache.has(iriNoFrag)) return __logSemanticsCache.get(iriNoFrag);
289
+ const norm = __normalizeDerefIri(iriNoFrag);
290
+ const key = typeof norm === 'string' && norm ? norm : iriNoFrag;
291
+ if (__logSemanticsCache.has(key)) return __logSemanticsCache.get(key);
222
292
 
223
293
  const text = __derefTextSync(iriNoFrag);
224
294
  if (typeof text !== 'string') {
225
- __logSemanticsCache.set(iriNoFrag, null);
295
+ __logSemanticsCache.set(key, null);
226
296
  return null;
227
297
  }
228
298
  try {
229
- const formula = __parseSemanticsToFormula(text, iriNoFrag);
230
- __logSemanticsCache.set(iriNoFrag, formula);
299
+ const baseIri = (typeof key === 'string' && key) ? key : iriNoFrag;
300
+ const formula = __parseSemanticsToFormula(text, baseIri);
301
+ __logSemanticsCache.set(key, formula);
231
302
  return formula;
232
303
  } catch {
233
- __logSemanticsCache.set(iriNoFrag, null);
304
+ __logSemanticsCache.set(key, null);
234
305
  return null;
235
306
  }
236
307
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eyeling",
3
- "version": "1.7.6",
3
+ "version": "1.7.7",
4
4
  "description": "A minimal Notation3 (N3) reasoner in JavaScript.",
5
5
  "main": "./index.js",
6
6
  "keywords": [