rdflib 2.2.35 → 2.2.36
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/dist/rdflib.min.js +1 -1
- package/dist/rdflib.min.js.LICENSE.txt +0 -2
- package/dist/rdflib.min.js.map +1 -1
- package/esm/blank-node.js +57 -82
- package/esm/class-order.js +1 -1
- package/esm/collection.js +70 -103
- package/esm/default-graph.js +13 -30
- package/esm/empty.js +8 -23
- package/esm/factories/canonical-data-factory.js +33 -29
- package/esm/factories/extended-term-factory.js +18 -13
- package/esm/factories/factory-types.js +1 -1
- package/esm/factories/rdflib-data-factory.js +9 -11
- package/esm/fetcher.js +1366 -1651
- package/esm/formula.js +631 -736
- package/esm/index.js +31 -48
- package/esm/jsonldparser.js +19 -26
- package/esm/jsonparser.js +1 -1
- package/esm/lists.js +38 -86
- package/esm/literal.js +120 -154
- package/esm/log.js +7 -7
- package/esm/n3parser.js +1009 -1086
- package/esm/named-node.js +69 -96
- package/esm/namespace.js +2 -4
- package/esm/node-internal.js +73 -96
- package/esm/node.js +1 -1
- package/esm/parse.js +3 -3
- package/esm/patch-parser.js +1 -1
- package/esm/query.js +15 -16
- package/esm/rdfaparser.js +775 -841
- package/esm/rdfxmlparser.js +348 -364
- package/esm/serialize.js +2 -2
- package/esm/serializer.js +835 -877
- package/esm/statement.js +52 -71
- package/esm/store.js +853 -957
- package/esm/types.js +21 -21
- package/esm/update-manager.js +965 -1100
- package/esm/updates-via.js +104 -132
- package/esm/uri.js +3 -3
- package/esm/utils/default-graph-uri.js +2 -2
- package/esm/utils/terms.js +4 -5
- package/esm/utils-js.js +5 -5
- package/esm/utils.js +6 -6
- package/esm/variable.js +32 -55
- package/esm/xsd.js +2 -2
- package/lib/blank-node.js +57 -80
- package/lib/class-order.js +1 -1
- package/lib/collection.js +70 -101
- package/lib/default-graph.js +14 -29
- package/lib/empty.js +9 -22
- package/lib/factories/canonical-data-factory.js +35 -31
- package/lib/factories/extended-term-factory.js +18 -13
- package/lib/factories/factory-types.js +1 -1
- package/lib/factories/rdflib-data-factory.js +9 -11
- package/lib/fetcher.js +1375 -1654
- package/lib/formula.js +632 -735
- package/lib/index.js +80 -84
- package/lib/jsonldparser.js +21 -32
- package/lib/jsonparser.js +1 -1
- package/lib/lists.js +47 -87
- package/lib/literal.js +121 -153
- package/lib/log.js +7 -7
- package/lib/n3parser.js +1012 -1090
- package/lib/named-node.js +70 -95
- package/lib/namespace.js +2 -4
- package/lib/node-internal.js +73 -94
- package/lib/node.js +1 -1
- package/lib/parse.js +5 -6
- package/lib/patch-parser.js +1 -1
- package/lib/query.js +20 -18
- package/lib/rdfaparser.js +778 -843
- package/lib/rdfxmlparser.js +351 -365
- package/lib/serialize.js +2 -2
- package/lib/serializer.js +840 -880
- package/lib/statement.js +55 -73
- package/lib/store.js +860 -958
- package/lib/types.js +21 -21
- package/lib/update-manager.js +970 -1102
- package/lib/updates-via.js +107 -132
- package/lib/uri.js +3 -3
- package/lib/utils/default-graph-uri.js +2 -2
- package/lib/utils/terms.js +4 -6
- package/lib/utils-js.js +8 -9
- package/lib/utils.js +6 -6
- package/lib/variable.js +35 -57
- package/lib/xsd.js +2 -2
- package/package.json +2 -2
- package/src/n3parser.js +1 -1
- package/src/serializer.js +1 -1
- package/src/update-manager.ts +2 -1
- package/.babelrc +0 -20
package/lib/fetcher.js
CHANGED
|
@@ -1,18 +1,10 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
3
|
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
4
|
-
var _typeof = require("@babel/runtime/helpers/typeof");
|
|
5
4
|
Object.defineProperty(exports, "__esModule", {
|
|
6
5
|
value: true
|
|
7
6
|
});
|
|
8
7
|
exports.default = void 0;
|
|
9
|
-
var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"));
|
|
10
|
-
var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator"));
|
|
11
|
-
var _possibleConstructorReturn2 = _interopRequireDefault(require("@babel/runtime/helpers/possibleConstructorReturn"));
|
|
12
|
-
var _getPrototypeOf2 = _interopRequireDefault(require("@babel/runtime/helpers/getPrototypeOf"));
|
|
13
|
-
var _inherits2 = _interopRequireDefault(require("@babel/runtime/helpers/inherits"));
|
|
14
|
-
var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
|
|
15
|
-
var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
|
|
16
8
|
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
|
|
17
9
|
var _store = _interopRequireDefault(require("./store"));
|
|
18
10
|
var _log = _interopRequireDefault(require("./log"));
|
|
@@ -30,13 +22,10 @@ var _crossFetch = _interopRequireWildcard(require("cross-fetch"));
|
|
|
30
22
|
var _types = require("./types");
|
|
31
23
|
var _termValue = require("./utils/termValue");
|
|
32
24
|
var _jsonldparser = _interopRequireDefault(require("./jsonldparser"));
|
|
33
|
-
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function
|
|
34
|
-
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" !=
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; }
|
|
38
|
-
function _callSuper(t, o, e) { return o = (0, _getPrototypeOf2.default)(o), (0, _possibleConstructorReturn2.default)(t, _isNativeReflectConstruct() ? Reflect.construct(o, e || [], (0, _getPrototypeOf2.default)(t).constructor) : o.apply(t, e)); }
|
|
39
|
-
function _isNativeReflectConstruct() { try { var t = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); } catch (t) {} return (_isNativeReflectConstruct = function _isNativeReflectConstruct() { return !!t; })(); } /* global $SolidTestEnvironment */ /**
|
|
25
|
+
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
|
26
|
+
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
|
|
27
|
+
/* global $SolidTestEnvironment */
|
|
28
|
+
/**
|
|
40
29
|
*
|
|
41
30
|
* Project: rdflib.js
|
|
42
31
|
*
|
|
@@ -54,13 +43,16 @@ function _isNativeReflectConstruct() { try { var t = !Boolean.prototype.valueOf.
|
|
|
54
43
|
* TO do:
|
|
55
44
|
* - Implement a runtime registry for parsers and serializers
|
|
56
45
|
* -
|
|
57
|
-
*/
|
|
46
|
+
*/
|
|
47
|
+
|
|
48
|
+
/**
|
|
58
49
|
* Things to test: callbacks on request, refresh, retract
|
|
59
50
|
* loading from HTTP, HTTPS, FTP, FILE, others?
|
|
60
51
|
* To do:
|
|
61
52
|
* Firing up a mail client for mid: (message:) URLs
|
|
62
53
|
*/
|
|
63
|
-
|
|
54
|
+
|
|
55
|
+
const Parsable = {
|
|
64
56
|
'text/n3': true,
|
|
65
57
|
'text/turtle': true,
|
|
66
58
|
'application/rdf+xml': true,
|
|
@@ -70,7 +62,7 @@ var Parsable = {
|
|
|
70
62
|
};
|
|
71
63
|
|
|
72
64
|
// This is a minimal set to allow the use of damaged servers if necessary
|
|
73
|
-
|
|
65
|
+
const CONTENT_TYPE_BY_EXT = {
|
|
74
66
|
'rdf': _types.RDFXMLContentType,
|
|
75
67
|
'owl': _types.RDFXMLContentType,
|
|
76
68
|
'n3': 'text/n3',
|
|
@@ -86,7 +78,7 @@ var CONTENT_TYPE_BY_EXT = {
|
|
|
86
78
|
// make its own list and not rely on the prefixes used here,
|
|
87
79
|
// and not be tempted to add to them, and them clash with those of another
|
|
88
80
|
// application.
|
|
89
|
-
|
|
81
|
+
const getNS = factory => {
|
|
90
82
|
return {
|
|
91
83
|
link: (0, _namespace.default)('http://www.w3.org/2007/ont/link#', factory),
|
|
92
84
|
http: (0, _namespace.default)('http://www.w3.org/2007/ont/http#', factory),
|
|
@@ -98,7 +90,7 @@ var getNS = function getNS(factory) {
|
|
|
98
90
|
ldp: (0, _namespace.default)('http://www.w3.org/ns/ldp#', factory)
|
|
99
91
|
};
|
|
100
92
|
};
|
|
101
|
-
|
|
93
|
+
const ns = getNS();
|
|
102
94
|
|
|
103
95
|
/** An extended interface of Response, since RDFlib.js adds some properties. */
|
|
104
96
|
|
|
@@ -107,435 +99,352 @@ var ns = getNS();
|
|
|
107
99
|
/** All valid inputs for initFetchOptions */
|
|
108
100
|
|
|
109
101
|
/** Initiated by initFetchOptions, which runs on load */
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
102
|
+
|
|
103
|
+
class Handler {
|
|
104
|
+
constructor(response, dom) {
|
|
105
|
+
// TODO: Document, type
|
|
106
|
+
(0, _defineProperty2.default)(this, "response", void 0);
|
|
107
|
+
// TODO: Document, type
|
|
108
|
+
(0, _defineProperty2.default)(this, "dom", void 0);
|
|
109
|
+
this.response = response;
|
|
110
|
+
// The type assertion operator here might need to be removed.
|
|
111
|
+
this.dom = dom;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
120
114
|
(0, _defineProperty2.default)(Handler, "pattern", void 0);
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
115
|
+
class RDFXMLHandler extends Handler {
|
|
116
|
+
static toString() {
|
|
117
|
+
return 'RDFXMLHandler';
|
|
118
|
+
}
|
|
119
|
+
static register(fetcher) {
|
|
120
|
+
fetcher.mediatypes[_types.RDFXMLContentType] = {
|
|
121
|
+
'q': 0.9
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
parse(fetcher, /** An XML String */
|
|
125
|
+
responseText, /** Requires .original */
|
|
126
|
+
options) {
|
|
127
|
+
let kb = fetcher.store;
|
|
128
|
+
if (!this.dom) {
|
|
129
|
+
this.dom = Util.parseXML(responseText);
|
|
130
|
+
}
|
|
131
|
+
let root = this.dom.documentElement;
|
|
132
|
+
if (root.nodeName === 'parsererror') {
|
|
133
|
+
// Mozilla only See issue/issue110
|
|
134
|
+
// have to fail the request
|
|
135
|
+
return fetcher.failFetch(options, 'Badly formed XML in ' + options.resource.value, 'parse_error');
|
|
136
|
+
}
|
|
137
|
+
let parser = new _rdfxmlparser.default(kb);
|
|
138
|
+
try {
|
|
139
|
+
parser.parse(this.dom, options.original.value, options.original);
|
|
140
|
+
} catch (err) {
|
|
141
|
+
return fetcher.failFetch(options, 'Syntax error parsing RDF/XML! ' + err, 'parse_error');
|
|
142
|
+
}
|
|
143
|
+
if (!options.noMeta) {
|
|
144
|
+
kb.add(options.original, ns.rdf('type'), ns.link('RDFDocument'), fetcher.appNode);
|
|
145
|
+
}
|
|
146
|
+
return fetcher.doneFetch(options, this.response);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
RDFXMLHandler.pattern = new RegExp('application/rdf\\+xml');
|
|
150
|
+
class XHTMLHandler extends Handler {
|
|
151
|
+
static toString() {
|
|
152
|
+
return 'XHTMLHandler';
|
|
153
|
+
}
|
|
154
|
+
static register(fetcher) {
|
|
155
|
+
fetcher.mediatypes[_types.XHTMLContentType] = {};
|
|
156
|
+
}
|
|
157
|
+
parse(fetcher, responseText, options) {
|
|
158
|
+
let relation, reverse;
|
|
159
|
+
if (!this.dom) {
|
|
160
|
+
this.dom = Util.parseXML(responseText);
|
|
157
161
|
}
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
162
|
+
let kb = fetcher.store;
|
|
163
|
+
|
|
164
|
+
// dc:title
|
|
165
|
+
let title = this.dom.getElementsByTagName('title');
|
|
166
|
+
if (title.length > 0) {
|
|
167
|
+
kb.add(options.resource, ns.dc('title'), kb.rdfFactory.literal(title[0].textContent), options.resource);
|
|
168
|
+
// log.info("Inferring title of " + xhr.resource)
|
|
164
169
|
}
|
|
165
|
-
}]);
|
|
166
|
-
}(Handler);
|
|
167
|
-
RDFXMLHandler.pattern = new RegExp('application/rdf\\+xml');
|
|
168
|
-
var XHTMLHandler = /*#__PURE__*/function (_Handler3) {
|
|
169
|
-
function XHTMLHandler() {
|
|
170
|
-
(0, _classCallCheck2.default)(this, XHTMLHandler);
|
|
171
|
-
return _callSuper(this, XHTMLHandler, arguments);
|
|
172
|
-
}
|
|
173
|
-
(0, _inherits2.default)(XHTMLHandler, _Handler3);
|
|
174
|
-
return (0, _createClass2.default)(XHTMLHandler, [{
|
|
175
|
-
key: "parse",
|
|
176
|
-
value: function parse(fetcher, responseText, options) {
|
|
177
|
-
var relation, reverse;
|
|
178
|
-
if (!this.dom) {
|
|
179
|
-
this.dom = Util.parseXML(responseText);
|
|
180
|
-
}
|
|
181
|
-
var kb = fetcher.store;
|
|
182
170
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
171
|
+
// link rel
|
|
172
|
+
let links = this.dom.getElementsByTagName('link');
|
|
173
|
+
for (let x = links.length - 1; x >= 0; x--) {
|
|
174
|
+
// @@ rev
|
|
175
|
+
relation = links[x].getAttribute('rel');
|
|
176
|
+
reverse = false;
|
|
177
|
+
if (!relation) {
|
|
178
|
+
relation = links[x].getAttribute('rev');
|
|
179
|
+
reverse = true;
|
|
188
180
|
}
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
var links = this.dom.getElementsByTagName('link');
|
|
192
|
-
for (var x = links.length - 1; x >= 0; x--) {
|
|
193
|
-
// @@ rev
|
|
194
|
-
relation = links[x].getAttribute('rel');
|
|
195
|
-
reverse = false;
|
|
196
|
-
if (!relation) {
|
|
197
|
-
relation = links[x].getAttribute('rev');
|
|
198
|
-
reverse = true;
|
|
199
|
-
}
|
|
200
|
-
if (relation) {
|
|
201
|
-
fetcher.linkData(options.original, relation, links[x].getAttribute('href'), options.resource, reverse);
|
|
202
|
-
}
|
|
181
|
+
if (relation) {
|
|
182
|
+
fetcher.linkData(options.original, relation, links[x].getAttribute('href'), options.resource, reverse);
|
|
203
183
|
}
|
|
184
|
+
}
|
|
204
185
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
if (!options.noMeta) {
|
|
217
|
-
kb.add(options.resource, ns.rdf('type'), ns.link('WebPage'), fetcher.appNode);
|
|
218
|
-
}
|
|
219
|
-
if (!options.noRDFa && _rdfaparser.parseRDFaDOM) {
|
|
220
|
-
// enable by default
|
|
221
|
-
try {
|
|
222
|
-
(0, _rdfaparser.parseRDFaDOM)(this.dom, kb, options.original.value);
|
|
223
|
-
} catch (err) {
|
|
224
|
-
// @ts-ignore
|
|
225
|
-
var msg = 'Error trying to parse ' + options.resource + ' as RDFa:\n' + err + ':\n' + err.stack;
|
|
226
|
-
return fetcher.failFetch(options, msg, 'parse_error');
|
|
227
|
-
}
|
|
186
|
+
// Data Islands
|
|
187
|
+
let scripts = this.dom.getElementsByTagName('script');
|
|
188
|
+
for (let i = 0; i < scripts.length; i++) {
|
|
189
|
+
let contentType = scripts[i].getAttribute('type');
|
|
190
|
+
if (Parsable[contentType]) {
|
|
191
|
+
// @ts-ignore incompatibility between Store.add and Formula.add
|
|
192
|
+
(0, _parse.default)(scripts[i].textContent, kb, options.original.value, contentType);
|
|
193
|
+
// @ts-ignore incompatibility between Store.add and Formula.add
|
|
194
|
+
(0, _parse.default)(scripts[i].textContent, kb, options.original.value, contentType);
|
|
228
195
|
}
|
|
229
|
-
return fetcher.doneFetch(options, this.response);
|
|
230
196
|
}
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
value: function toString() {
|
|
234
|
-
return 'XHTMLHandler';
|
|
197
|
+
if (!options.noMeta) {
|
|
198
|
+
kb.add(options.resource, ns.rdf('type'), ns.link('WebPage'), fetcher.appNode);
|
|
235
199
|
}
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
200
|
+
if (!options.noRDFa && _rdfaparser.parseRDFaDOM) {
|
|
201
|
+
// enable by default
|
|
202
|
+
try {
|
|
203
|
+
(0, _rdfaparser.parseRDFaDOM)(this.dom, kb, options.original.value);
|
|
204
|
+
} catch (err) {
|
|
205
|
+
// @ts-ignore
|
|
206
|
+
let msg = 'Error trying to parse ' + options.resource + ' as RDFa:\n' + err + ':\n' + err.stack;
|
|
207
|
+
return fetcher.failFetch(options, msg, 'parse_error');
|
|
208
|
+
}
|
|
240
209
|
}
|
|
241
|
-
|
|
242
|
-
}
|
|
210
|
+
return fetcher.doneFetch(options, this.response);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
243
213
|
XHTMLHandler.pattern = new RegExp('application/xhtml');
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
if (dom.doctype) {
|
|
277
|
-
// log.info("We found a DOCTYPE in " + xhr.resource)
|
|
278
|
-
if (dom.doctype.name === 'html' && dom.doctype.publicId.match(/^-\/\/W3C\/\/DTD XHTML/) && dom.doctype.systemId.match(/http:\/\/www.w3.org\/TR\/xhtml/)) {
|
|
279
|
-
fetcher.addStatus(options.req, 'Has XHTML DOCTYPE. Switching to XHTML Handler.\n');
|
|
280
|
-
var xhtmlHandler = new XHTMLHandler(this.response, dom);
|
|
281
|
-
return xhtmlHandler.parse(fetcher, responseText, options);
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
// Or what about an XHTML namespace?
|
|
286
|
-
var html = dom.getElementsByTagName('html')[0];
|
|
287
|
-
if (html) {
|
|
288
|
-
var xmlns = html.getAttribute('xmlns');
|
|
289
|
-
if (xmlns && xmlns.match(/^http:\/\/www.w3.org\/1999\/xhtml/)) {
|
|
290
|
-
fetcher.addStatus(options.req, 'Has a default namespace for ' + 'XHTML. Switching to XHTMLHandler.\n');
|
|
291
|
-
var _xhtmlHandler = new XHTMLHandler(this.response, dom);
|
|
292
|
-
return _xhtmlHandler.parse(fetcher, responseText, options);
|
|
214
|
+
class XMLHandler extends Handler {
|
|
215
|
+
static toString() {
|
|
216
|
+
return 'XMLHandler';
|
|
217
|
+
}
|
|
218
|
+
static register(fetcher) {
|
|
219
|
+
fetcher.mediatypes['text/xml'] = {
|
|
220
|
+
'q': 0.5
|
|
221
|
+
};
|
|
222
|
+
fetcher.mediatypes['application/xml'] = {
|
|
223
|
+
'q': 0.5
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
static isElement(node) {
|
|
227
|
+
return node.nodeType === Node.ELEMENT_NODE;
|
|
228
|
+
}
|
|
229
|
+
parse(fetcher, responseText, options) {
|
|
230
|
+
let dom = Util.parseXML(responseText);
|
|
231
|
+
|
|
232
|
+
// XML Semantics defined by root element namespace
|
|
233
|
+
// figure out the root element
|
|
234
|
+
for (let c = 0; c < dom.childNodes.length; c++) {
|
|
235
|
+
const node = dom.childNodes[c];
|
|
236
|
+
// is this node an element?
|
|
237
|
+
if (XMLHandler.isElement(node)) {
|
|
238
|
+
// We've found the first element, it's the root
|
|
239
|
+
let ns = node.namespaceURI;
|
|
240
|
+
|
|
241
|
+
// Is it RDF/XML?
|
|
242
|
+
if (ns && ns === ns['rdf']) {
|
|
243
|
+
fetcher.addStatus(options.req, 'Has XML root element in the RDF namespace, so assume RDF/XML.');
|
|
244
|
+
let rdfHandler = new RDFXMLHandler(this.response, dom);
|
|
245
|
+
return rdfHandler.parse(fetcher, responseText, options);
|
|
293
246
|
}
|
|
247
|
+
break;
|
|
294
248
|
}
|
|
295
|
-
|
|
296
|
-
// At this point we should check the namespace document (cache it!) and
|
|
297
|
-
// look for a GRDDL transform
|
|
298
|
-
// @@ Get namespace document <n>, parse it, look for <n> grddl:namespaceTransform ?y
|
|
299
|
-
// Apply ?y to dom
|
|
300
|
-
// We give up. What dialect is this?
|
|
301
|
-
return fetcher.failFetch(options, 'Unsupported dialect of XML: not RDF or XHTML namespace, etc.\n' + responseText.slice(0, 80), 901);
|
|
302
|
-
}
|
|
303
|
-
}], [{
|
|
304
|
-
key: "toString",
|
|
305
|
-
value: function toString() {
|
|
306
|
-
return 'XMLHandler';
|
|
307
|
-
}
|
|
308
|
-
}, {
|
|
309
|
-
key: "register",
|
|
310
|
-
value: function register(fetcher) {
|
|
311
|
-
fetcher.mediatypes['text/xml'] = {
|
|
312
|
-
'q': 0.5
|
|
313
|
-
};
|
|
314
|
-
fetcher.mediatypes['application/xml'] = {
|
|
315
|
-
'q': 0.5
|
|
316
|
-
};
|
|
317
249
|
}
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
var HTMLHandler = /*#__PURE__*/function (_Handler5) {
|
|
327
|
-
function HTMLHandler() {
|
|
328
|
-
(0, _classCallCheck2.default)(this, HTMLHandler);
|
|
329
|
-
return _callSuper(this, HTMLHandler, arguments);
|
|
330
|
-
}
|
|
331
|
-
(0, _inherits2.default)(HTMLHandler, _Handler5);
|
|
332
|
-
return (0, _createClass2.default)(HTMLHandler, [{
|
|
333
|
-
key: "parse",
|
|
334
|
-
value: function parse(fetcher, responseText, options) {
|
|
335
|
-
var kb = fetcher.store;
|
|
336
|
-
|
|
337
|
-
// We only handle XHTML so we have to figure out if this is XML
|
|
338
|
-
// log.info("Sniffing HTML " + xhr.resource + " for XHTML.")
|
|
339
|
-
if (isXML(responseText)) {
|
|
340
|
-
fetcher.addStatus(options.req, "Has an XML declaration. We'll assume " + "it's XHTML as the content-type was text/html.\n");
|
|
341
|
-
var xhtmlHandler = new XHTMLHandler(this.response);
|
|
250
|
+
|
|
251
|
+
// Or it could be XHTML?
|
|
252
|
+
// Maybe it has an XHTML DOCTYPE?
|
|
253
|
+
if (dom.doctype) {
|
|
254
|
+
// log.info("We found a DOCTYPE in " + xhr.resource)
|
|
255
|
+
if (dom.doctype.name === 'html' && dom.doctype.publicId.match(/^-\/\/W3C\/\/DTD XHTML/) && dom.doctype.systemId.match(/http:\/\/www.w3.org\/TR\/xhtml/)) {
|
|
256
|
+
fetcher.addStatus(options.req, 'Has XHTML DOCTYPE. Switching to XHTML Handler.\n');
|
|
257
|
+
let xhtmlHandler = new XHTMLHandler(this.response, dom);
|
|
342
258
|
return xhtmlHandler.parse(fetcher, responseText, options);
|
|
343
259
|
}
|
|
260
|
+
}
|
|
344
261
|
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
262
|
+
// Or what about an XHTML namespace?
|
|
263
|
+
let html = dom.getElementsByTagName('html')[0];
|
|
264
|
+
if (html) {
|
|
265
|
+
let xmlns = html.getAttribute('xmlns');
|
|
266
|
+
if (xmlns && xmlns.match(/^http:\/\/www.w3.org\/1999\/xhtml/)) {
|
|
267
|
+
fetcher.addStatus(options.req, 'Has a default namespace for ' + 'XHTML. Switching to XHTMLHandler.\n');
|
|
268
|
+
let xhtmlHandler = new XHTMLHandler(this.response, dom);
|
|
269
|
+
return xhtmlHandler.parse(fetcher, responseText, options);
|
|
350
270
|
}
|
|
271
|
+
}
|
|
351
272
|
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
273
|
+
// At this point we should check the namespace document (cache it!) and
|
|
274
|
+
// look for a GRDDL transform
|
|
275
|
+
// @@ Get namespace document <n>, parse it, look for <n> grddl:namespaceTransform ?y
|
|
276
|
+
// Apply ?y to dom
|
|
277
|
+
// We give up. What dialect is this?
|
|
278
|
+
return fetcher.failFetch(options, 'Unsupported dialect of XML: not RDF or XHTML namespace, etc.\n' + responseText.slice(0, 80), 901);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
XMLHandler.pattern = new RegExp('(text|application)/(.*)xml');
|
|
282
|
+
class HTMLHandler extends Handler {
|
|
283
|
+
static toString() {
|
|
284
|
+
return 'HTMLHandler';
|
|
285
|
+
}
|
|
286
|
+
static register(fetcher) {
|
|
287
|
+
fetcher.mediatypes['text/html'] = {
|
|
288
|
+
'q': 0.9
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
parse(fetcher, responseText, options) {
|
|
292
|
+
let kb = fetcher.store;
|
|
358
293
|
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
kb.add(options.resource, ns.rdf('type'), ns.link('WebPage'), fetcher.appNode);
|
|
366
|
-
fetcher.addStatus(options.req, 'non-XML HTML document, not parsed for data.');
|
|
367
|
-
return fetcher.doneFetch(options, this.response);
|
|
368
|
-
}
|
|
369
|
-
}], [{
|
|
370
|
-
key: "toString",
|
|
371
|
-
value: function toString() {
|
|
372
|
-
return 'HTMLHandler';
|
|
373
|
-
}
|
|
374
|
-
}, {
|
|
375
|
-
key: "register",
|
|
376
|
-
value: function register(fetcher) {
|
|
377
|
-
fetcher.mediatypes['text/html'] = {
|
|
378
|
-
'q': 0.9
|
|
379
|
-
};
|
|
380
|
-
}
|
|
381
|
-
}]);
|
|
382
|
-
}(Handler);
|
|
383
|
-
HTMLHandler.pattern = new RegExp('text/html');
|
|
384
|
-
var JsonLdHandler = /*#__PURE__*/function (_Handler6) {
|
|
385
|
-
function JsonLdHandler() {
|
|
386
|
-
(0, _classCallCheck2.default)(this, JsonLdHandler);
|
|
387
|
-
return _callSuper(this, JsonLdHandler, arguments);
|
|
388
|
-
}
|
|
389
|
-
(0, _inherits2.default)(JsonLdHandler, _Handler6);
|
|
390
|
-
return (0, _createClass2.default)(JsonLdHandler, [{
|
|
391
|
-
key: "parse",
|
|
392
|
-
value: function parse(fetcher, responseText, options, response) {
|
|
393
|
-
var kb = fetcher.store;
|
|
394
|
-
return new Promise(function (resolve, reject) {
|
|
395
|
-
try {
|
|
396
|
-
(0, _jsonldparser.default)(responseText, kb, options.original.value, function () {
|
|
397
|
-
resolve(fetcher.doneFetch(options, response));
|
|
398
|
-
});
|
|
399
|
-
} catch (err) {
|
|
400
|
-
var msg = 'Error trying to parse ' + options.resource + ' as JSON-LD:\n' + err; // not err.stack -- irrelevant
|
|
401
|
-
resolve(fetcher.failFetch(options, msg, 'parse_error', response));
|
|
402
|
-
}
|
|
403
|
-
});
|
|
294
|
+
// We only handle XHTML so we have to figure out if this is XML
|
|
295
|
+
// log.info("Sniffing HTML " + xhr.resource + " for XHTML.")
|
|
296
|
+
if (isXML(responseText)) {
|
|
297
|
+
fetcher.addStatus(options.req, "Has an XML declaration. We'll assume " + "it's XHTML as the content-type was text/html.\n");
|
|
298
|
+
let xhtmlHandler = new XHTMLHandler(this.response);
|
|
299
|
+
return xhtmlHandler.parse(fetcher, responseText, options);
|
|
404
300
|
}
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
301
|
+
|
|
302
|
+
// DOCTYPE html
|
|
303
|
+
if (isXHTML(responseText)) {
|
|
304
|
+
fetcher.addStatus(options.req, 'Has XHTML DOCTYPE. Switching to XHTMLHandler.\n');
|
|
305
|
+
let xhtmlHandler = new XHTMLHandler(this.response);
|
|
306
|
+
return xhtmlHandler.parse(fetcher, responseText, options);
|
|
409
307
|
}
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
fetcher.
|
|
414
|
-
|
|
415
|
-
|
|
308
|
+
|
|
309
|
+
// xmlns
|
|
310
|
+
if (isXMLNS(responseText)) {
|
|
311
|
+
fetcher.addStatus(options.req, 'Has default namespace for XHTML, so switching to XHTMLHandler.\n');
|
|
312
|
+
let xhtmlHandler = new XHTMLHandler(this.response);
|
|
313
|
+
return xhtmlHandler.parse(fetcher, responseText, options);
|
|
416
314
|
}
|
|
417
|
-
}]);
|
|
418
|
-
}(Handler);
|
|
419
|
-
JsonLdHandler.pattern = /application\/ld\+json/;
|
|
420
|
-
var TextHandler = /*#__PURE__*/function (_Handler7) {
|
|
421
|
-
function TextHandler() {
|
|
422
|
-
(0, _classCallCheck2.default)(this, TextHandler);
|
|
423
|
-
return _callSuper(this, TextHandler, arguments);
|
|
424
|
-
}
|
|
425
|
-
(0, _inherits2.default)(TextHandler, _Handler7);
|
|
426
|
-
return (0, _createClass2.default)(TextHandler, [{
|
|
427
|
-
key: "parse",
|
|
428
|
-
value: function parse(fetcher, responseText, options) {
|
|
429
|
-
// We only speak dialects of XML right now. Is this XML?
|
|
430
|
-
|
|
431
|
-
// Look for an XML declaration
|
|
432
|
-
if (isXML(responseText)) {
|
|
433
|
-
fetcher.addStatus(options.req, 'Warning: ' + options.resource + " has an XML declaration. We'll assume " + "it's XML but its content-type wasn't XML.\n");
|
|
434
|
-
var xmlHandler = new XMLHandler(this.response);
|
|
435
|
-
return xmlHandler.parse(fetcher, responseText, options);
|
|
436
|
-
}
|
|
437
315
|
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
316
|
+
// dc:title
|
|
317
|
+
// no need to escape '/' here
|
|
318
|
+
let titleMatch = new RegExp('<title>([\\s\\S]+?)</title>', 'im').exec(responseText);
|
|
319
|
+
if (titleMatch) {
|
|
320
|
+
kb.add(options.resource, ns.dc('title'), kb.rdfFactory.literal(titleMatch[1]), options.resource); // think about xml:lang later
|
|
321
|
+
}
|
|
322
|
+
kb.add(options.resource, ns.rdf('type'), ns.link('WebPage'), fetcher.appNode);
|
|
323
|
+
fetcher.addStatus(options.req, 'non-XML HTML document, not parsed for data.');
|
|
324
|
+
return fetcher.doneFetch(options, this.response);
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
HTMLHandler.pattern = new RegExp('text/html');
|
|
328
|
+
class JsonLdHandler extends Handler {
|
|
329
|
+
static toString() {
|
|
330
|
+
return 'JsonLdHandler';
|
|
331
|
+
}
|
|
332
|
+
static register(fetcher) {
|
|
333
|
+
fetcher.mediatypes['application/ld+json'] = {
|
|
334
|
+
'q': 0.9
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
parse(fetcher, responseText, options, response) {
|
|
338
|
+
const kb = fetcher.store;
|
|
339
|
+
return new Promise((resolve, reject) => {
|
|
340
|
+
try {
|
|
341
|
+
(0, _jsonldparser.default)(responseText, kb, options.original.value, () => {
|
|
342
|
+
resolve(fetcher.doneFetch(options, response));
|
|
343
|
+
});
|
|
344
|
+
} catch (err) {
|
|
345
|
+
const msg = 'Error trying to parse ' + options.resource + ' as JSON-LD:\n' + err; // not err.stack -- irrelevant
|
|
346
|
+
resolve(fetcher.failFetch(options, msg, 'parse_error', response));
|
|
443
347
|
}
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
JsonLdHandler.pattern = /application\/ld\+json/;
|
|
352
|
+
class TextHandler extends Handler {
|
|
353
|
+
static toString() {
|
|
354
|
+
return 'TextHandler';
|
|
355
|
+
}
|
|
356
|
+
static register(fetcher) {
|
|
357
|
+
fetcher.mediatypes['text/plain'] = {
|
|
358
|
+
'q': 0.5
|
|
359
|
+
};
|
|
360
|
+
}
|
|
361
|
+
parse(fetcher, responseText, options) {
|
|
362
|
+
// We only speak dialects of XML right now. Is this XML?
|
|
444
363
|
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
364
|
+
// Look for an XML declaration
|
|
365
|
+
if (isXML(responseText)) {
|
|
366
|
+
fetcher.addStatus(options.req, 'Warning: ' + options.resource + " has an XML declaration. We'll assume " + "it's XML but its content-type wasn't XML.\n");
|
|
367
|
+
let xmlHandler = new XMLHandler(this.response);
|
|
368
|
+
return xmlHandler.parse(fetcher, responseText, options);
|
|
448
369
|
}
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
key: "register",
|
|
456
|
-
value: function register(fetcher) {
|
|
457
|
-
fetcher.mediatypes['text/plain'] = {
|
|
458
|
-
'q': 0.5
|
|
459
|
-
};
|
|
370
|
+
|
|
371
|
+
// Look for an XML declaration
|
|
372
|
+
if (responseText.slice(0, 500).match(/xmlns:/)) {
|
|
373
|
+
fetcher.addStatus(options.req, "May have an XML namespace. We'll assume " + "it's XML but its content-type wasn't XML.\n");
|
|
374
|
+
let xmlHandler = new XMLHandler(this.response);
|
|
375
|
+
return xmlHandler.parse(fetcher, responseText, options);
|
|
460
376
|
}
|
|
461
|
-
|
|
462
|
-
|
|
377
|
+
|
|
378
|
+
// We give up finding semantics - this is not an error, just no data
|
|
379
|
+
fetcher.addStatus(options.req, 'Plain text document, no known RDF semantics.');
|
|
380
|
+
return fetcher.doneFetch(options, this.response);
|
|
381
|
+
}
|
|
382
|
+
}
|
|
463
383
|
TextHandler.pattern = new RegExp('text/plain');
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
return '
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
}; // as per 2008 spec
|
|
498
|
-
/*
|
|
499
|
-
fetcher.mediatypes['application/x-turtle'] = {
|
|
500
|
-
'q': 1.0
|
|
501
|
-
} // pre 2008
|
|
502
|
-
*/
|
|
503
|
-
fetcher.mediatypes['text/turtle'] = {
|
|
504
|
-
'q': 1.0
|
|
505
|
-
}; // post 2008
|
|
506
|
-
}
|
|
507
|
-
}]);
|
|
508
|
-
}(Handler);
|
|
384
|
+
class N3Handler extends Handler {
|
|
385
|
+
static toString() {
|
|
386
|
+
return 'N3Handler';
|
|
387
|
+
}
|
|
388
|
+
static register(fetcher) {
|
|
389
|
+
fetcher.mediatypes['text/n3'] = {
|
|
390
|
+
'q': '1.0'
|
|
391
|
+
}; // as per 2008 spec
|
|
392
|
+
/*
|
|
393
|
+
fetcher.mediatypes['application/x-turtle'] = {
|
|
394
|
+
'q': 1.0
|
|
395
|
+
} // pre 2008
|
|
396
|
+
*/
|
|
397
|
+
fetcher.mediatypes['text/turtle'] = {
|
|
398
|
+
'q': 1.0
|
|
399
|
+
}; // post 2008
|
|
400
|
+
}
|
|
401
|
+
parse(fetcher, responseText, options, response) {
|
|
402
|
+
// Parse the text of this N3 file
|
|
403
|
+
let kb = fetcher.store;
|
|
404
|
+
let p = (0, _n3parser.default)(kb, kb, options.original.value, options.original.value, null, null, '', null);
|
|
405
|
+
// p.loadBuf(xhr.responseText)
|
|
406
|
+
try {
|
|
407
|
+
p.loadBuf(responseText);
|
|
408
|
+
} catch (err) {
|
|
409
|
+
let msg = 'Error trying to parse ' + options.resource + ' as Notation3:\n' + err; // not err.stack -- irrelevant
|
|
410
|
+
return fetcher.failFetch(options, msg, 'parse_error', response);
|
|
411
|
+
}
|
|
412
|
+
fetcher.addStatus(options.req, 'N3 parsed: ' + p.statementCount + ' triples in ' + p.lines + ' lines.');
|
|
413
|
+
fetcher.store.add(options.original, ns.rdf('type'), ns.link('RDFDocument'), fetcher.appNode);
|
|
414
|
+
return fetcher.doneFetch(options, this.response);
|
|
415
|
+
}
|
|
416
|
+
}
|
|
509
417
|
N3Handler.pattern = new RegExp('(application|text)/(x-)?(rdf\\+)?(n3|turtle)');
|
|
510
|
-
|
|
511
|
-
RDFXMLHandler
|
|
512
|
-
XHTMLHandler
|
|
513
|
-
XMLHandler
|
|
514
|
-
HTMLHandler
|
|
515
|
-
TextHandler
|
|
516
|
-
N3Handler
|
|
517
|
-
JsonLdHandler
|
|
418
|
+
const defaultHandlers = {
|
|
419
|
+
RDFXMLHandler,
|
|
420
|
+
XHTMLHandler,
|
|
421
|
+
XMLHandler,
|
|
422
|
+
HTMLHandler,
|
|
423
|
+
TextHandler,
|
|
424
|
+
N3Handler,
|
|
425
|
+
JsonLdHandler
|
|
518
426
|
};
|
|
519
427
|
function isXHTML(responseText) {
|
|
520
|
-
|
|
521
|
-
|
|
428
|
+
const docTypeStart = responseText.indexOf('<!DOCTYPE html');
|
|
429
|
+
const docTypeEnd = responseText.indexOf('>');
|
|
522
430
|
if (docTypeStart === -1 || docTypeEnd === -1 || docTypeStart > docTypeEnd) {
|
|
523
431
|
return false;
|
|
524
432
|
}
|
|
525
433
|
return responseText.substr(docTypeStart, docTypeEnd - docTypeStart).indexOf('XHTML') !== -1;
|
|
526
434
|
}
|
|
527
435
|
function isXML(responseText) {
|
|
528
|
-
|
|
436
|
+
const match = responseText.match(/\s*<\?xml\s+version\s*=[^<>]+\?>/);
|
|
529
437
|
return !!match;
|
|
530
438
|
}
|
|
531
439
|
function isXMLNS(responseText) {
|
|
532
|
-
|
|
440
|
+
const match = responseText.match(/[^(<html)]*<html\s+[^<]*xmlns=['"]http:\/\/www.w3.org\/1999\/xhtml["'][^<]*>/);
|
|
533
441
|
return !!match;
|
|
534
442
|
}
|
|
535
443
|
|
|
536
444
|
// Not sure about the shapes of this. Response? FetchError?
|
|
537
445
|
|
|
538
446
|
/** Differs from normal Fetch, has an extended Response type */
|
|
447
|
+
|
|
539
448
|
/** Fetcher
|
|
540
449
|
*
|
|
541
450
|
* The Fetcher object is a helper object for a quadstore
|
|
@@ -544,11 +453,9 @@ function isXMLNS(responseText) {
|
|
|
544
453
|
* figuring how to parse them. It will also refresh, remove, the data
|
|
545
454
|
* and put back the data to the web.
|
|
546
455
|
*/
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
|
551
|
-
(0, _classCallCheck2.default)(this, Fetcher);
|
|
456
|
+
class Fetcher {
|
|
457
|
+
constructor(store) {
|
|
458
|
+
let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
|
552
459
|
(0, _defineProperty2.default)(this, "store", void 0);
|
|
553
460
|
(0, _defineProperty2.default)(this, "timeout", void 0);
|
|
554
461
|
(0, _defineProperty2.default)(this, "_fetch", void 0);
|
|
@@ -617,280 +524,287 @@ var Fetcher = exports.default = /*#__PURE__*/function () {
|
|
|
617
524
|
// 'refresh', 'retract', 'done'])
|
|
618
525
|
// In switching to fetch(), 'recv', 'headers' and 'load' do not make sense
|
|
619
526
|
Util.callbackify(this, ['request', 'fail', 'refresh', 'retract', 'done']);
|
|
620
|
-
Object.keys(options.handlers || defaultHandlers).map(
|
|
621
|
-
return _this.addHandler(defaultHandlers[key]);
|
|
622
|
-
});
|
|
527
|
+
Object.keys(options.handlers || defaultHandlers).map(key => this.addHandler(defaultHandlers[key]));
|
|
623
528
|
}
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
*
|
|
646
|
-
* @param [options.contentType] {string} Provided content type (for writes)
|
|
647
|
-
*
|
|
648
|
-
* @param [options.forceContentType] {string} Override the incoming header to
|
|
649
|
-
* force the data to be treated as this content-type (for reads)
|
|
650
|
-
*
|
|
651
|
-
* @param [options.force] {boolean} Load the data even if loaded before.
|
|
652
|
-
* Also sets the `Cache-Control:` header to `no-cache`
|
|
653
|
-
*
|
|
654
|
-
* @param [options.baseURI=docuri] {Node|string} Original uri to preserve
|
|
655
|
-
* through proxying etc (`xhr.original`).
|
|
656
|
-
*
|
|
657
|
-
* @param [options.proxyUsed] {boolean} Whether this request is a retry via
|
|
658
|
-
* a proxy (generally done from an error handler)
|
|
659
|
-
*
|
|
660
|
-
* @param [options.withCredentials] {boolean} flag for XHR/CORS etc
|
|
661
|
-
*
|
|
662
|
-
* @param [options.clearPreviousData] {boolean} Before we parse new data,
|
|
663
|
-
* clear old, but only on status 200 responses
|
|
664
|
-
*
|
|
665
|
-
* @param [options.noMeta] {boolean} Prevents the addition of various metadata
|
|
666
|
-
* triples (about the fetch request) to the store
|
|
667
|
-
*
|
|
668
|
-
* @param [options.noRDFa] {boolean}
|
|
669
|
-
*
|
|
670
|
-
* @returns {Promise<Result>}
|
|
671
|
-
*/
|
|
672
|
-
function load(uri) {
|
|
673
|
-
var _this2 = this;
|
|
674
|
-
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
|
675
|
-
options = Object.assign({}, options); // Take a copy as we add stuff to the options!!
|
|
676
|
-
if (uri instanceof Array) {
|
|
677
|
-
return Promise.all(uri.map(function (x) {
|
|
678
|
-
return _this2.load(x, Object.assign({}, options));
|
|
679
|
-
}));
|
|
529
|
+
static crossSiteProxy(uri) {
|
|
530
|
+
if (Fetcher.crossSiteProxyTemplate) {
|
|
531
|
+
return Fetcher.crossSiteProxyTemplate.replace('{uri}', encodeURIComponent(uri));
|
|
532
|
+
} else {
|
|
533
|
+
return undefined;
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
static offlineOverride(uri) {
|
|
537
|
+
// Map the URI to a localhost proxy if we are running on localhost
|
|
538
|
+
// This is used for working offline, e.g. on planes.
|
|
539
|
+
// Is the script itself is running in localhost, then access all
|
|
540
|
+
// data in a localhost mirror.
|
|
541
|
+
// Do not remove without checking with TimBL
|
|
542
|
+
let requestedURI = uri;
|
|
543
|
+
var UI;
|
|
544
|
+
if (typeof window !== 'undefined' && window.panes && (UI = window.panes.UI) && UI.preferences && UI.preferences.get('offlineModeUsingLocalhost')) {
|
|
545
|
+
if (requestedURI.slice(0, 7) === 'http://' && requestedURI.slice(7, 17) !== 'localhost/') {
|
|
546
|
+
requestedURI = 'http://localhost/' + requestedURI.slice(7);
|
|
547
|
+
_log.default.warn('Localhost kludge for offline use: actually getting <' + requestedURI + '>');
|
|
548
|
+
} else {
|
|
549
|
+
// log.warn("Localhost kludge NOT USED <" + requestedURI + ">")
|
|
680
550
|
}
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
options.force = true;
|
|
702
|
-
options.clearPreviousData = true;
|
|
703
|
-
}
|
|
551
|
+
} else {
|
|
552
|
+
// log.warn("Localhost kludge OFF offline use: actually getting <" +
|
|
553
|
+
// requestedURI + ">")
|
|
554
|
+
}
|
|
555
|
+
return requestedURI;
|
|
556
|
+
}
|
|
557
|
+
static proxyIfNecessary(uri) {
|
|
558
|
+
var UI;
|
|
559
|
+
if (typeof window !== 'undefined' && window.panes && (UI = window.panes.UI) && UI.isExtension) {
|
|
560
|
+
return uri;
|
|
561
|
+
} // Extension does not need proxy
|
|
562
|
+
|
|
563
|
+
if (typeof $SolidTestEnvironment !== 'undefined' && $SolidTestEnvironment.localSiteMap) {
|
|
564
|
+
// nested dictionaries of URI parts from origin down
|
|
565
|
+
let hostpath = uri.split('/').slice(2); // the bit after the //
|
|
566
|
+
|
|
567
|
+
const lookup = (parts, index) => {
|
|
568
|
+
let z = index[parts.shift()];
|
|
569
|
+
if (!z) {
|
|
570
|
+
return null;
|
|
704
571
|
}
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
} finally {
|
|
708
|
-
_iterator.f();
|
|
709
|
-
}
|
|
710
|
-
var initialisedOptions = this.initFetchOptions(docuri, options);
|
|
711
|
-
return this.pendingFetchPromise(docuri, initialisedOptions.baseURI, initialisedOptions);
|
|
712
|
-
}
|
|
713
|
-
}, {
|
|
714
|
-
key: "pendingFetchPromise",
|
|
715
|
-
value: function () {
|
|
716
|
-
var _pendingFetchPromise = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee(uri, originalUri, options) {
|
|
717
|
-
var _this3 = this;
|
|
718
|
-
var pendingPromise;
|
|
719
|
-
return _regenerator.default.wrap(function _callee$(_context) {
|
|
720
|
-
while (1) switch (_context.prev = _context.next) {
|
|
721
|
-
case 0:
|
|
722
|
-
_context.t0 = !options.force;
|
|
723
|
-
if (!_context.t0) {
|
|
724
|
-
_context.next = 5;
|
|
725
|
-
break;
|
|
726
|
-
}
|
|
727
|
-
_context.next = 4;
|
|
728
|
-
return this.fetchQueue[originalUri];
|
|
729
|
-
case 4:
|
|
730
|
-
_context.t0 = _context.sent;
|
|
731
|
-
case 5:
|
|
732
|
-
if (!_context.t0) {
|
|
733
|
-
_context.next = 9;
|
|
734
|
-
break;
|
|
735
|
-
}
|
|
736
|
-
pendingPromise = this.fetchQueue[originalUri];
|
|
737
|
-
_context.next = 12;
|
|
738
|
-
break;
|
|
739
|
-
case 9:
|
|
740
|
-
pendingPromise = Promise.race([this.setRequestTimeout(uri, options), this.fetchUri(uri, options)]);
|
|
741
|
-
this.fetchQueue[originalUri] = pendingPromise;
|
|
742
|
-
|
|
743
|
-
// Clean up the queued promise after a time, if it's resolved
|
|
744
|
-
this.cleanupFetchRequest(originalUri, undefined, this.timeout);
|
|
745
|
-
case 12:
|
|
746
|
-
return _context.abrupt("return", pendingPromise.then(function (x) {
|
|
747
|
-
if (uri in _this3.timeouts) {
|
|
748
|
-
_this3.timeouts[uri].forEach(clearTimeout);
|
|
749
|
-
delete _this3.timeouts[uri];
|
|
750
|
-
}
|
|
751
|
-
return x;
|
|
752
|
-
}));
|
|
753
|
-
case 13:
|
|
754
|
-
case "end":
|
|
755
|
-
return _context.stop();
|
|
756
|
-
}
|
|
757
|
-
}, _callee, this);
|
|
758
|
-
}));
|
|
759
|
-
function pendingFetchPromise(_x, _x2, _x3) {
|
|
760
|
-
return _pendingFetchPromise.apply(this, arguments);
|
|
761
|
-
}
|
|
762
|
-
return pendingFetchPromise;
|
|
763
|
-
}()
|
|
764
|
-
/**
|
|
765
|
-
* @param _options - DEPRECATED
|
|
766
|
-
*/
|
|
767
|
-
}, {
|
|
768
|
-
key: "cleanupFetchRequest",
|
|
769
|
-
value: function cleanupFetchRequest(originalUri, _options, timeout) {
|
|
770
|
-
var _this4 = this;
|
|
771
|
-
if (_options !== undefined) {
|
|
772
|
-
console.warn("_options is deprecated");
|
|
773
|
-
}
|
|
774
|
-
this.timeouts[originalUri] = (this.timeouts[originalUri] || []).concat(setTimeout(function () {
|
|
775
|
-
if (!_this4.isPending(originalUri)) {
|
|
776
|
-
delete _this4.fetchQueue[originalUri];
|
|
572
|
+
if (typeof z === 'string') {
|
|
573
|
+
return z + parts.join('/');
|
|
777
574
|
}
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
options.force = true;
|
|
787
|
-
}
|
|
788
|
-
options.resource = kb.rdfFactory.namedNode(uri); // This might be proxified
|
|
789
|
-
options.baseURI = options.baseURI || uri; // Preserve though proxying etc
|
|
790
|
-
options.original = kb.rdfFactory.namedNode(options.baseURI);
|
|
791
|
-
options.req = kb.bnode();
|
|
792
|
-
options.headers = options.headers || new _crossFetch.Headers();
|
|
793
|
-
if (options.contentType) {
|
|
794
|
-
// @ts-ignore
|
|
795
|
-
options.headers['content-type'] = options.contentType;
|
|
796
|
-
}
|
|
797
|
-
if (options.force) {
|
|
798
|
-
options.cache = 'no-cache';
|
|
575
|
+
if (!parts) {
|
|
576
|
+
return null;
|
|
577
|
+
}
|
|
578
|
+
return lookup(parts, z);
|
|
579
|
+
};
|
|
580
|
+
const y = lookup(hostpath, $SolidTestEnvironment.localSiteMap);
|
|
581
|
+
if (y) {
|
|
582
|
+
return y;
|
|
799
583
|
}
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
// browser does 2014 on as https browser script not trusted
|
|
587
|
+
// If the web app origin is https: then the mixed content rules
|
|
588
|
+
// prevent it loading insecure http: stuff so we need proxy.
|
|
589
|
+
if (Fetcher.crossSiteProxyTemplate && typeof document !== 'undefined' && document.location && ('' + document.location).slice(0, 6) === 'https:' &&
|
|
590
|
+
// origin is secure
|
|
591
|
+
uri.slice(0, 5) === 'http:') {
|
|
592
|
+
// requested data is not
|
|
593
|
+
return Fetcher.crossSiteProxyTemplate.replace('{uri}', encodeURIComponent(uri));
|
|
594
|
+
}
|
|
595
|
+
return uri;
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
/**
|
|
599
|
+
* Tests whether the uri's protocol is supported by the Fetcher.
|
|
600
|
+
* @param uri
|
|
601
|
+
*/
|
|
602
|
+
static unsupportedProtocol(uri) {
|
|
603
|
+
let pcol = Uri.protocol(uri);
|
|
604
|
+
return pcol === 'tel' || pcol === 'mailto' || pcol === 'urn';
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
/** Decide on credentials using old XXHR api or new fetch() one
|
|
608
|
+
* @param requestedURI
|
|
609
|
+
* @param options
|
|
610
|
+
*/
|
|
611
|
+
static setCredentials(requestedURI) {
|
|
612
|
+
let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
|
613
|
+
// 2014 CORS problem:
|
|
614
|
+
// XMLHttpRequest cannot load http://www.w3.org/People/Berners-Lee/card.
|
|
615
|
+
// A wildcard '*' cannot be used in the 'Access-Control-Allow-Origin'
|
|
616
|
+
// header when the credentials flag is true.
|
|
617
|
+
// @ Many ontology files under http: and need CORS wildcard ->
|
|
618
|
+
// can't have credentials
|
|
619
|
+
if (options.credentials === undefined) {
|
|
620
|
+
// Caller using new fetch convention
|
|
621
|
+
if (options.withCredentials !== undefined) {
|
|
622
|
+
// XHR style is what Fetcher specified before
|
|
623
|
+
options.credentials = options.withCredentials ? 'include' : 'omit';
|
|
624
|
+
} else {
|
|
625
|
+
options.credentials = 'include'; // default is to be logged on
|
|
809
626
|
}
|
|
810
|
-
options.actualProxyURI = actualProxyURI;
|
|
811
|
-
return options;
|
|
812
627
|
}
|
|
628
|
+
}
|
|
813
629
|
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
630
|
+
/**
|
|
631
|
+
* Promise-based load function
|
|
632
|
+
*
|
|
633
|
+
* Loads a web resource or resources into the store.
|
|
634
|
+
*
|
|
635
|
+
* A resource may be given as NamedNode object, or as a plain URI.
|
|
636
|
+
* an array of resources will be given, in which they will be fetched in parallel.
|
|
637
|
+
* By default, the HTTP headers are recorded also, in the same store, in a separate graph.
|
|
638
|
+
* This allows code like editable() for example to test things about the resource.
|
|
639
|
+
*
|
|
640
|
+
* @param uri {Array<RDFlibNamedNode>|Array<string>|RDFlibNamedNode|string}
|
|
641
|
+
*
|
|
642
|
+
* @param [options={}] {Object}
|
|
643
|
+
*
|
|
644
|
+
* @param [options.fetch] {Function}
|
|
645
|
+
*
|
|
646
|
+
* @param [options.referringTerm] {RDFlibNamedNode} Referring term, the resource which
|
|
647
|
+
* referred to this (for tracking bad links)
|
|
648
|
+
*
|
|
649
|
+
* @param [options.contentType] {string} Provided content type (for writes)
|
|
650
|
+
*
|
|
651
|
+
* @param [options.forceContentType] {string} Override the incoming header to
|
|
652
|
+
* force the data to be treated as this content-type (for reads)
|
|
653
|
+
*
|
|
654
|
+
* @param [options.force] {boolean} Load the data even if loaded before.
|
|
655
|
+
* Also sets the `Cache-Control:` header to `no-cache`
|
|
656
|
+
*
|
|
657
|
+
* @param [options.baseURI=docuri] {Node|string} Original uri to preserve
|
|
658
|
+
* through proxying etc (`xhr.original`).
|
|
659
|
+
*
|
|
660
|
+
* @param [options.proxyUsed] {boolean} Whether this request is a retry via
|
|
661
|
+
* a proxy (generally done from an error handler)
|
|
662
|
+
*
|
|
663
|
+
* @param [options.withCredentials] {boolean} flag for XHR/CORS etc
|
|
664
|
+
*
|
|
665
|
+
* @param [options.clearPreviousData] {boolean} Before we parse new data,
|
|
666
|
+
* clear old, but only on status 200 responses
|
|
667
|
+
*
|
|
668
|
+
* @param [options.noMeta] {boolean} Prevents the addition of various metadata
|
|
669
|
+
* triples (about the fetch request) to the store
|
|
670
|
+
*
|
|
671
|
+
* @param [options.noRDFa] {boolean}
|
|
672
|
+
*
|
|
673
|
+
* @returns {Promise<Result>}
|
|
674
|
+
*/
|
|
675
|
+
load(uri) {
|
|
676
|
+
let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
|
677
|
+
options = Object.assign({}, options); // Take a copy as we add stuff to the options!!
|
|
678
|
+
if (uri instanceof Array) {
|
|
679
|
+
return Promise.all(uri.map(x => {
|
|
680
|
+
return this.load(x, Object.assign({}, options));
|
|
681
|
+
}));
|
|
682
|
+
}
|
|
683
|
+
const uriIn = uri;
|
|
684
|
+
let docuri = (0, _termValue.termValue)(uriIn);
|
|
685
|
+
docuri = docuri.split('#')[0];
|
|
686
|
+
options = this.initFetchOptions(docuri, options);
|
|
687
|
+
// if metadata flaged clear cache and removeDocument
|
|
688
|
+
const meta = this.appNode;
|
|
689
|
+
const kb = this.store;
|
|
690
|
+
const requests = kb.statementsMatching(undefined, this.ns.link('requestedURI'), kb.sym(docuri), meta).map(st => st.subject);
|
|
691
|
+
for (const request of requests) {
|
|
692
|
+
const response = kb.any(request, this.ns.link('response'), null, meta);
|
|
693
|
+
if (response != undefined) {
|
|
694
|
+
// ts
|
|
695
|
+
const quad = kb.statementsMatching(response, this.ns.link('outOfDate'), true, meta);
|
|
696
|
+
kb.remove(quad);
|
|
697
|
+
options.force = true;
|
|
698
|
+
options.clearPreviousData = true;
|
|
828
699
|
}
|
|
829
|
-
|
|
830
|
-
|
|
700
|
+
}
|
|
701
|
+
const initialisedOptions = this.initFetchOptions(docuri, options);
|
|
702
|
+
return this.pendingFetchPromise(docuri, initialisedOptions.baseURI, initialisedOptions);
|
|
703
|
+
}
|
|
704
|
+
async pendingFetchPromise(uri, originalUri, options) {
|
|
705
|
+
let pendingPromise;
|
|
706
|
+
|
|
707
|
+
// Check to see if some request is already dealing with this uri
|
|
708
|
+
if (!options.force && (await this.fetchQueue[originalUri])) {
|
|
709
|
+
pendingPromise = this.fetchQueue[originalUri];
|
|
710
|
+
} else {
|
|
711
|
+
pendingPromise = Promise.race([this.setRequestTimeout(uri, options), this.fetchUri(uri, options)]);
|
|
712
|
+
this.fetchQueue[originalUri] = pendingPromise;
|
|
713
|
+
|
|
714
|
+
// Clean up the queued promise after a time, if it's resolved
|
|
715
|
+
this.cleanupFetchRequest(originalUri, undefined, this.timeout);
|
|
716
|
+
}
|
|
717
|
+
return pendingPromise.then(x => {
|
|
718
|
+
if (uri in this.timeouts) {
|
|
719
|
+
this.timeouts[uri].forEach(clearTimeout);
|
|
720
|
+
delete this.timeouts[uri];
|
|
831
721
|
}
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
var _message = 'Previously failed: ' + this.requested[docuri];
|
|
847
|
-
// @ts-ignore This is not a valid response object
|
|
848
|
-
var dummyResponse = {
|
|
849
|
-
url: docuri,
|
|
850
|
-
// This does not comply to Fetch spec, it can be a string value in rdflib
|
|
851
|
-
status: this.requested[docuri],
|
|
852
|
-
statusText: _message,
|
|
853
|
-
responseText: _message,
|
|
854
|
-
headers: new _crossFetch.Headers(),
|
|
855
|
-
// Headers() ???
|
|
856
|
-
ok: false,
|
|
857
|
-
body: null,
|
|
858
|
-
bodyUsed: false,
|
|
859
|
-
size: 0,
|
|
860
|
-
timeout: 0
|
|
861
|
-
};
|
|
862
|
-
return this.failFetch(options, _message, this.requested[docuri], dummyResponse);
|
|
863
|
-
}
|
|
864
|
-
} else {
|
|
865
|
-
// options.force == true
|
|
866
|
-
delete this.nonexistent[docuri];
|
|
722
|
+
return x;
|
|
723
|
+
});
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
/**
|
|
727
|
+
* @param _options - DEPRECATED
|
|
728
|
+
*/
|
|
729
|
+
cleanupFetchRequest(originalUri, _options, timeout) {
|
|
730
|
+
if (_options !== undefined) {
|
|
731
|
+
console.warn("_options is deprecated");
|
|
732
|
+
}
|
|
733
|
+
this.timeouts[originalUri] = (this.timeouts[originalUri] || []).concat(setTimeout(() => {
|
|
734
|
+
if (!this.isPending(originalUri)) {
|
|
735
|
+
delete this.fetchQueue[originalUri];
|
|
867
736
|
}
|
|
868
|
-
|
|
869
|
-
|
|
737
|
+
}, timeout));
|
|
738
|
+
}
|
|
739
|
+
initFetchOptions(uri, options) {
|
|
740
|
+
let kb = this.store;
|
|
741
|
+
let isGet = !options.method || options.method.toUpperCase() === 'GET';
|
|
742
|
+
if (!isGet) {
|
|
743
|
+
options.force = true;
|
|
744
|
+
}
|
|
745
|
+
options.resource = kb.rdfFactory.namedNode(uri); // This might be proxified
|
|
746
|
+
options.baseURI = options.baseURI || uri; // Preserve though proxying etc
|
|
747
|
+
options.original = kb.rdfFactory.namedNode(options.baseURI);
|
|
748
|
+
options.req = kb.bnode();
|
|
749
|
+
options.headers = options.headers || new _crossFetch.Headers();
|
|
750
|
+
if (options.contentType) {
|
|
751
|
+
// @ts-ignore
|
|
752
|
+
options.headers['content-type'] = options.contentType;
|
|
753
|
+
}
|
|
754
|
+
if (options.force) {
|
|
755
|
+
options.cache = 'no-cache';
|
|
756
|
+
}
|
|
757
|
+
let acceptString = this.acceptString();
|
|
758
|
+
// @ts-ignore
|
|
759
|
+
options.headers['accept'] = acceptString;
|
|
760
|
+
let requestedURI = Fetcher.offlineOverride(uri);
|
|
761
|
+
options.requestedURI = requestedURI;
|
|
762
|
+
Fetcher.setCredentials(requestedURI, options);
|
|
763
|
+
let actualProxyURI = Fetcher.proxyIfNecessary(requestedURI);
|
|
764
|
+
if (requestedURI !== actualProxyURI) {
|
|
765
|
+
options.proxyUsed = true;
|
|
766
|
+
}
|
|
767
|
+
options.actualProxyURI = actualProxyURI;
|
|
768
|
+
return options;
|
|
769
|
+
}
|
|
870
770
|
|
|
871
|
-
|
|
872
|
-
|
|
771
|
+
/**
|
|
772
|
+
* (The promise chain ends in either a `failFetch()` or a `doneFetch()`)
|
|
773
|
+
*
|
|
774
|
+
* @param docuri {string}
|
|
775
|
+
* @param options {Object}
|
|
776
|
+
*
|
|
777
|
+
* @returns {Promise<Object>} fetch() result or an { error, status } object
|
|
778
|
+
*/
|
|
779
|
+
fetchUri(docuri, options) {
|
|
780
|
+
if (!docuri) {
|
|
781
|
+
return Promise.reject(new Error('Cannot fetch an empty uri'));
|
|
782
|
+
}
|
|
783
|
+
if (Fetcher.unsupportedProtocol(docuri)) {
|
|
784
|
+
return this.failFetch(options, 'fetcher: Unsupported protocol', 'unsupported_protocol');
|
|
785
|
+
}
|
|
786
|
+
let state = this.getState(docuri);
|
|
787
|
+
if (!options.force) {
|
|
788
|
+
if (state === 'fetched') {
|
|
789
|
+
// URI already fetched and added to store
|
|
790
|
+
return Promise.resolve(
|
|
791
|
+
// @ts-ignore This is not a valid response object
|
|
792
|
+
this.doneFetch(options, {
|
|
793
|
+
status: 200,
|
|
794
|
+
ok: true,
|
|
795
|
+
statusText: 'Already loaded into quadstore.'
|
|
796
|
+
}));
|
|
873
797
|
}
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
return _this5.handleResponse(response, docuri, options);
|
|
885
|
-
}, function (error) {
|
|
886
|
-
// @@ handleError?
|
|
887
|
-
// @ts-ignore Invalid response object
|
|
888
|
-
var dummyResponse = {
|
|
889
|
-
url: actualProxyURI,
|
|
890
|
-
status: 999,
|
|
891
|
-
// @@ what number/string should fetch failures report?
|
|
892
|
-
statusText: (error.name || 'network failure') + ': ' + (error.errno || error.code || error.type),
|
|
893
|
-
responseText: error.message,
|
|
798
|
+
if (state === 'failed' && this.requested[docuri] === 404) {
|
|
799
|
+
// Remember nonexistence
|
|
800
|
+
let message = 'Previously failed: ' + this.requested[docuri];
|
|
801
|
+
// @ts-ignore This is not a valid response object
|
|
802
|
+
let dummyResponse = {
|
|
803
|
+
url: docuri,
|
|
804
|
+
// This does not comply to Fetch spec, it can be a string value in rdflib
|
|
805
|
+
status: this.requested[docuri],
|
|
806
|
+
statusText: message,
|
|
807
|
+
responseText: message,
|
|
894
808
|
headers: new _crossFetch.Headers(),
|
|
895
809
|
// Headers() ???
|
|
896
810
|
ok: false,
|
|
@@ -899,1080 +813,887 @@ var Fetcher = exports.default = /*#__PURE__*/function () {
|
|
|
899
813
|
size: 0,
|
|
900
814
|
timeout: 0
|
|
901
815
|
};
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
816
|
+
return this.failFetch(options, message, this.requested[docuri], dummyResponse);
|
|
817
|
+
}
|
|
818
|
+
} else {
|
|
819
|
+
// options.force == true
|
|
820
|
+
delete this.nonexistent[docuri];
|
|
821
|
+
}
|
|
822
|
+
this.fireCallbacks('request', [docuri]);
|
|
823
|
+
this.requested[docuri] = true; // mark this uri as 'requested'
|
|
824
|
+
|
|
825
|
+
if (!options.noMeta) {
|
|
826
|
+
this.saveRequestMetadata(docuri, options);
|
|
827
|
+
}
|
|
828
|
+
let {
|
|
829
|
+
actualProxyURI
|
|
830
|
+
} = options;
|
|
831
|
+
|
|
832
|
+
// Map might get mistakenly added into headers
|
|
833
|
+
// error TS2339: Property 'map' does not exist on type 'Headers'.
|
|
834
|
+
/* let map
|
|
835
|
+
if (options.headers && map in options.headers) {
|
|
836
|
+
delete options.headers.map
|
|
837
|
+
} */
|
|
838
|
+
|
|
839
|
+
return this._fetch(actualProxyURI, options).then(response => this.handleResponse(response, docuri, options), error => {
|
|
840
|
+
// @@ handleError?
|
|
841
|
+
// @ts-ignore Invalid response object
|
|
842
|
+
let dummyResponse = {
|
|
843
|
+
url: actualProxyURI,
|
|
844
|
+
status: 999,
|
|
845
|
+
// @@ what number/string should fetch failures report?
|
|
846
|
+
statusText: (error.name || 'network failure') + ': ' + (error.errno || error.code || error.type),
|
|
847
|
+
responseText: error.message,
|
|
848
|
+
headers: new _crossFetch.Headers(),
|
|
849
|
+
// Headers() ???
|
|
850
|
+
ok: false,
|
|
851
|
+
body: null,
|
|
852
|
+
bodyUsed: false,
|
|
853
|
+
size: 0,
|
|
854
|
+
timeout: 0
|
|
855
|
+
};
|
|
856
|
+
// console.log('Fetcher: <' + actualProxyURI + '> Non-HTTP fetch exception: ' + error)
|
|
857
|
+
return this.handleError(dummyResponse, docuri, options); // possible credentials retry
|
|
858
|
+
// return this.failFetch(options, 'fetch failed: ' + error, 999, dummyResponse) // Fake status code: fetch exception
|
|
912
859
|
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
userCallback(false, oops, fetchResponse);
|
|
968
|
-
}
|
|
860
|
+
// handleError expects a response so we fake some important bits.
|
|
861
|
+
/*
|
|
862
|
+
this.handleError(, docuri, options)
|
|
863
|
+
*/
|
|
864
|
+
});
|
|
865
|
+
}
|
|
866
|
+
|
|
867
|
+
/**
|
|
868
|
+
* Asks for a doc to be loaded if necessary then calls back
|
|
869
|
+
*
|
|
870
|
+
* Calling methods:
|
|
871
|
+
* nowOrWhenFetched (uri, userCallback)
|
|
872
|
+
* nowOrWhenFetched (uri, options, userCallback)
|
|
873
|
+
* nowOrWhenFetched (uri, referringTerm, userCallback, options) <-- old
|
|
874
|
+
* nowOrWhenFetched (uri, referringTerm, userCallback) <-- old
|
|
875
|
+
*
|
|
876
|
+
* Options include:
|
|
877
|
+
* referringTerm The document in which this link was found.
|
|
878
|
+
* this is valuable when finding the source of bad URIs
|
|
879
|
+
* force boolean. Never mind whether you have tried before,
|
|
880
|
+
* load this from scratch.
|
|
881
|
+
* forceContentType Override the incoming header to force the data to be
|
|
882
|
+
* treated as this content-type.
|
|
883
|
+
*
|
|
884
|
+
* Callback function takes:
|
|
885
|
+
*
|
|
886
|
+
* ok True if the fetch worked, and got a 200 response.
|
|
887
|
+
* False if any error happened
|
|
888
|
+
*
|
|
889
|
+
* errmessage Text error message if not OK.
|
|
890
|
+
*
|
|
891
|
+
* response The fetch Response object (was: XHR) if there was was one
|
|
892
|
+
* includes response.status as the HTTP status if any.
|
|
893
|
+
*/
|
|
894
|
+
nowOrWhenFetched(uriIn, p2, userCallback) {
|
|
895
|
+
let options = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};
|
|
896
|
+
const uri = (0, _termValue.termValue)(uriIn);
|
|
897
|
+
if (typeof p2 === 'function') {
|
|
898
|
+
// nowOrWhenFetched (uri, userCallback)
|
|
899
|
+
userCallback = p2;
|
|
900
|
+
} else if (typeof p2 === 'undefined') {// original calling signature
|
|
901
|
+
// referringTerm = undefined
|
|
902
|
+
} else if ((0, _terms.isNamedNode)(p2)) {
|
|
903
|
+
// referringTerm = p2
|
|
904
|
+
options.referringTerm = p2;
|
|
905
|
+
} else {
|
|
906
|
+
// nowOrWhenFetched (uri, options, userCallback)
|
|
907
|
+
options = p2;
|
|
908
|
+
}
|
|
909
|
+
this.load(uri, options).then(fetchResponse => {
|
|
910
|
+
if (userCallback) {
|
|
911
|
+
if (fetchResponse) {
|
|
912
|
+
if (fetchResponse.ok) {
|
|
913
|
+
userCallback(true, 'OK', fetchResponse);
|
|
969
914
|
} else {
|
|
970
|
-
|
|
971
|
-
|
|
915
|
+
let oops = 'HTTP error: Status ' + fetchResponse.status + ' (' + fetchResponse.statusText + ')';
|
|
916
|
+
if (fetchResponse.responseText) {
|
|
917
|
+
oops += ' ' + fetchResponse.responseText; // not in 404, dns error, nock failure
|
|
918
|
+
}
|
|
919
|
+
userCallback(false, oops, fetchResponse);
|
|
972
920
|
}
|
|
973
|
-
}
|
|
974
|
-
}, function (err) {
|
|
975
|
-
var message = err.message || err.statusText;
|
|
976
|
-
message = 'Failed to load <' + uri + '> ' + message;
|
|
977
|
-
if (err.response && err.response.status) {
|
|
978
|
-
message += ' status: ' + err.response.status;
|
|
979
|
-
}
|
|
980
|
-
userCallback(false, message, err.response);
|
|
981
|
-
});
|
|
982
|
-
}
|
|
983
|
-
|
|
984
|
-
/**
|
|
985
|
-
* Records a status message (as a literal node) by appending it to the
|
|
986
|
-
* request's metadata status collection.
|
|
987
|
-
*
|
|
988
|
-
*/
|
|
989
|
-
}, {
|
|
990
|
-
key: "addStatus",
|
|
991
|
-
value: function addStatus(req, statusMessage) {
|
|
992
|
-
// <Debug about="parsePerformance">
|
|
993
|
-
var now = new Date();
|
|
994
|
-
statusMessage = '[' + now.getHours() + ':' + now.getMinutes() + ':' + now.getSeconds() + '.' + now.getMilliseconds() + '] ' + statusMessage;
|
|
995
|
-
// </Debug>
|
|
996
|
-
var kb = this.store;
|
|
997
|
-
var statusNode = kb.the(req, this.ns.link('status'));
|
|
998
|
-
if ((0, _terms.isCollection)(statusNode)) {
|
|
999
|
-
statusNode.append(kb.rdfFactory.literal(statusMessage));
|
|
1000
|
-
} else {
|
|
1001
|
-
_log.default.warn('web.js: No list to add to: ' + statusNode + ',' + statusMessage);
|
|
1002
|
-
}
|
|
1003
|
-
}
|
|
1004
|
-
|
|
1005
|
-
/**
|
|
1006
|
-
* Records errors in the system on failure:
|
|
1007
|
-
*
|
|
1008
|
-
* - Adds an entry to the request status collection
|
|
1009
|
-
* - Adds an error triple with the fail message to the metadata
|
|
1010
|
-
* - Fires the 'fail' callback
|
|
1011
|
-
* - Rejects with an error result object, which has a response object if any
|
|
1012
|
-
*/
|
|
1013
|
-
}, {
|
|
1014
|
-
key: "failFetch",
|
|
1015
|
-
value: function failFetch(options, errorMessage, statusCode, response) {
|
|
1016
|
-
this.addStatus(options.req, errorMessage);
|
|
1017
|
-
if (!options.noMeta) {
|
|
1018
|
-
this.store.add(options.original, this.ns.link('error'), this.store.rdfFactory.literal(errorMessage));
|
|
1019
|
-
}
|
|
1020
|
-
var meth = (options.method || 'GET').toUpperCase();
|
|
1021
|
-
var isGet = meth === 'GET' || meth === 'HEAD';
|
|
1022
|
-
if (isGet) {
|
|
1023
|
-
// only cache the status code on GET or HEAD
|
|
1024
|
-
if (!options.resource.equals(options.original)) {
|
|
1025
|
-
// console.log('@@ Recording failure ' + meth + ' original ' + options.original +option '( as ' + options.resource + ') : ' + statusCode)
|
|
1026
921
|
} else {
|
|
1027
|
-
|
|
922
|
+
let oops = '@@ nowOrWhenFetched: no response object!';
|
|
923
|
+
userCallback(false, oops);
|
|
1028
924
|
}
|
|
1029
|
-
this.requested[Uri.docpart(options.original.value)] = statusCode;
|
|
1030
|
-
this.fireCallbacks('fail', [options.original.value, errorMessage]);
|
|
1031
925
|
}
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
err.status
|
|
1036
|
-
|
|
1037
|
-
err.response = response;
|
|
1038
|
-
return Promise.reject(err);
|
|
1039
|
-
}
|
|
1040
|
-
|
|
1041
|
-
// in the why part of the quad distinguish between HTML and HTTP header
|
|
1042
|
-
// Reverse is set iif the link was rev= as opposed to rel=
|
|
1043
|
-
}, {
|
|
1044
|
-
key: "linkData",
|
|
1045
|
-
value: function linkData(originalUri, rel, uri, why, reverse) {
|
|
1046
|
-
if (!uri) return;
|
|
1047
|
-
var kb = this.store;
|
|
1048
|
-
var predicate;
|
|
1049
|
-
// See http://www.w3.org/TR/powder-dr/#httplink for describedby 2008-12-10
|
|
1050
|
-
var obj = kb.rdfFactory.namedNode(Uri.join(uri, originalUri.value));
|
|
1051
|
-
if (rel === 'alternate' || rel === 'seeAlso' || rel === 'meta' || rel === 'describedby') {
|
|
1052
|
-
if (obj.value === originalUri.value) {
|
|
1053
|
-
return;
|
|
1054
|
-
}
|
|
1055
|
-
predicate = this.ns.rdfs('seeAlso');
|
|
1056
|
-
} else if (rel === 'type') {
|
|
1057
|
-
predicate = kb.rdfFactory.namedNode('http://www.w3.org/1999/02/22-rdf-syntax-ns#type');
|
|
1058
|
-
} else {
|
|
1059
|
-
// See https://www.iana.org/assignments/link-relations/link-relations.xml
|
|
1060
|
-
// Alas not yet in RDF yet for each predicate
|
|
1061
|
-
// encode space in e.g. rel="shortcut icon"
|
|
1062
|
-
predicate = kb.rdfFactory.namedNode(Uri.join(encodeURIComponent(rel), 'http://www.iana.org/assignments/link-relations/'));
|
|
926
|
+
}, function (err) {
|
|
927
|
+
var message = err.message || err.statusText;
|
|
928
|
+
message = 'Failed to load <' + uri + '> ' + message;
|
|
929
|
+
if (err.response && err.response.status) {
|
|
930
|
+
message += ' status: ' + err.response.status;
|
|
1063
931
|
}
|
|
1064
|
-
|
|
1065
|
-
|
|
932
|
+
userCallback(false, message, err.response);
|
|
933
|
+
});
|
|
934
|
+
}
|
|
935
|
+
|
|
936
|
+
/**
|
|
937
|
+
* Records a status message (as a literal node) by appending it to the
|
|
938
|
+
* request's metadata status collection.
|
|
939
|
+
*
|
|
940
|
+
*/
|
|
941
|
+
addStatus(req, statusMessage) {
|
|
942
|
+
// <Debug about="parsePerformance">
|
|
943
|
+
let now = new Date();
|
|
944
|
+
statusMessage = '[' + now.getHours() + ':' + now.getMinutes() + ':' + now.getSeconds() + '.' + now.getMilliseconds() + '] ' + statusMessage;
|
|
945
|
+
// </Debug>
|
|
946
|
+
let kb = this.store;
|
|
947
|
+
const statusNode = kb.the(req, this.ns.link('status'));
|
|
948
|
+
if ((0, _terms.isCollection)(statusNode)) {
|
|
949
|
+
statusNode.append(kb.rdfFactory.literal(statusMessage));
|
|
950
|
+
} else {
|
|
951
|
+
_log.default.warn('web.js: No list to add to: ' + statusNode + ',' + statusMessage);
|
|
952
|
+
}
|
|
953
|
+
}
|
|
954
|
+
|
|
955
|
+
/**
|
|
956
|
+
* Records errors in the system on failure:
|
|
957
|
+
*
|
|
958
|
+
* - Adds an entry to the request status collection
|
|
959
|
+
* - Adds an error triple with the fail message to the metadata
|
|
960
|
+
* - Fires the 'fail' callback
|
|
961
|
+
* - Rejects with an error result object, which has a response object if any
|
|
962
|
+
*/
|
|
963
|
+
failFetch(options, errorMessage, statusCode, response) {
|
|
964
|
+
this.addStatus(options.req, errorMessage);
|
|
965
|
+
if (!options.noMeta) {
|
|
966
|
+
this.store.add(options.original, this.ns.link('error'), this.store.rdfFactory.literal(errorMessage));
|
|
967
|
+
}
|
|
968
|
+
let meth = (options.method || 'GET').toUpperCase();
|
|
969
|
+
let isGet = meth === 'GET' || meth === 'HEAD';
|
|
970
|
+
if (isGet) {
|
|
971
|
+
// only cache the status code on GET or HEAD
|
|
972
|
+
if (!options.resource.equals(options.original)) {
|
|
973
|
+
// console.log('@@ Recording failure ' + meth + ' original ' + options.original +option '( as ' + options.resource + ') : ' + statusCode)
|
|
1066
974
|
} else {
|
|
1067
|
-
|
|
975
|
+
// console.log('@@ Recording ' + meth + ' failure for ' + options.original + ': ' + statusCode)
|
|
1068
976
|
}
|
|
977
|
+
this.requested[Uri.docpart(options.original.value)] = statusCode;
|
|
978
|
+
this.fireCallbacks('fail', [options.original.value, errorMessage]);
|
|
1069
979
|
}
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
980
|
+
var err = new Error('Fetcher: ' + errorMessage);
|
|
981
|
+
|
|
982
|
+
// err.ok = false // Is taken as a response, will work too @@ phase out?
|
|
983
|
+
err.status = statusCode;
|
|
984
|
+
err.statusText = errorMessage;
|
|
985
|
+
err.response = response;
|
|
986
|
+
return Promise.reject(err);
|
|
987
|
+
}
|
|
988
|
+
|
|
989
|
+
// in the why part of the quad distinguish between HTML and HTTP header
|
|
990
|
+
// Reverse is set iif the link was rev= as opposed to rel=
|
|
991
|
+
linkData(originalUri, rel, uri, why, reverse) {
|
|
992
|
+
if (!uri) return;
|
|
993
|
+
let kb = this.store;
|
|
994
|
+
let predicate;
|
|
995
|
+
// See http://www.w3.org/TR/powder-dr/#httplink for describedby 2008-12-10
|
|
996
|
+
let obj = kb.rdfFactory.namedNode(Uri.join(uri, originalUri.value));
|
|
997
|
+
if (rel === 'alternate' || rel === 'seeAlso' || rel === 'meta' || rel === 'describedby') {
|
|
998
|
+
if (obj.value === originalUri.value) {
|
|
1074
999
|
return;
|
|
1075
1000
|
}
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
//
|
|
1081
|
-
//
|
|
1082
|
-
//
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1001
|
+
predicate = this.ns.rdfs('seeAlso');
|
|
1002
|
+
} else if (rel === 'type') {
|
|
1003
|
+
predicate = kb.rdfFactory.namedNode('http://www.w3.org/1999/02/22-rdf-syntax-ns#type');
|
|
1004
|
+
} else {
|
|
1005
|
+
// See https://www.iana.org/assignments/link-relations/link-relations.xml
|
|
1006
|
+
// Alas not yet in RDF yet for each predicate
|
|
1007
|
+
// encode space in e.g. rel="shortcut icon"
|
|
1008
|
+
predicate = kb.rdfFactory.namedNode(Uri.join(encodeURIComponent(rel), 'http://www.iana.org/assignments/link-relations/'));
|
|
1009
|
+
}
|
|
1010
|
+
if (reverse) {
|
|
1011
|
+
kb.add(obj, predicate, originalUri, why);
|
|
1012
|
+
} else {
|
|
1013
|
+
kb.add(originalUri, predicate, obj, why);
|
|
1014
|
+
}
|
|
1015
|
+
}
|
|
1016
|
+
parseLinkHeader(linkHeader, originalUri, reqNode) {
|
|
1017
|
+
if (!linkHeader) {
|
|
1018
|
+
return;
|
|
1019
|
+
}
|
|
1020
|
+
|
|
1021
|
+
// const linkexp = /<[^>]*>\s*(\s*;\s*[^()<>@,;:"/[\]?={} \t]+=(([^()<>@,;:"/[]?={} \t]+)|("[^"]*")))*(,|$)/g
|
|
1022
|
+
// const paramexp = /[^()<>@,;:"/[]?={} \t]+=(([^()<>@,;:"/[]?={} \t]+)|("[^"]*"))/g
|
|
1023
|
+
|
|
1024
|
+
// From https://www.dcode.fr/regular-expression-simplificator:
|
|
1025
|
+
// const linkexp = /<[^>]*>\s*(\s*;\s*[^()<>@,;:"/[\]?={} t]+=["]))*[,$]/g
|
|
1026
|
+
// const paramexp = /[^\\<>@,;:"\/\[\]?={} \t]+=["])/g
|
|
1027
|
+
// Original:
|
|
1028
|
+
const linkexp = /<[^>]*>\s*(\s*;\s*[^()<>@,;:"/[\]?={} \t]+=(([^\(\)<>@,;:"\/\[\]\?={} \t]+)|("[^"]*")))*(,|$)/g;
|
|
1029
|
+
const paramexp = /[^\(\)<>@,;:"\/\[\]\?={} \t]+=(([^\(\)<>@,;:"\/\[\]\?={} \t]+)|("[^"]*"))/g;
|
|
1030
|
+
const matches = linkHeader.match(linkexp);
|
|
1031
|
+
if (matches == null) return;
|
|
1032
|
+
for (let i = 0; i < matches.length; i++) {
|
|
1033
|
+
let split = matches[i].split('>');
|
|
1034
|
+
let href = split[0].substring(1);
|
|
1035
|
+
let ps = split[1];
|
|
1036
|
+
let s = ps.match(paramexp);
|
|
1037
|
+
if (s == null) return;
|
|
1038
|
+
for (let j = 0; j < s.length; j++) {
|
|
1039
|
+
let p = s[j];
|
|
1040
|
+
let paramsplit = p.split('=');
|
|
1041
|
+
// var name = paramsplit[0]
|
|
1042
|
+
let rel = paramsplit[1].replace(/["']/g, ''); // '"
|
|
1043
|
+
this.linkData(originalUri, rel, href, reqNode);
|
|
1101
1044
|
}
|
|
1102
1045
|
}
|
|
1103
|
-
}
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
response.req = options.req; // Set the request meta blank node
|
|
1046
|
+
}
|
|
1047
|
+
doneFetch(options, response) {
|
|
1048
|
+
this.addStatus(options.req, 'Done.');
|
|
1049
|
+
this.requested[options.original.value] = 'done';
|
|
1050
|
+
this.fireCallbacks('done', [options.original.value]);
|
|
1051
|
+
response.req = options.req; // Set the request meta blank node
|
|
1110
1052
|
|
|
1111
|
-
|
|
1112
|
-
|
|
1053
|
+
return response;
|
|
1054
|
+
}
|
|
1113
1055
|
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
if (this.lookedUp[
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
if (!this.lookedUp[was.value]) {
|
|
1129
|
-
this.lookUpThing(was, now);
|
|
1130
|
-
}
|
|
1056
|
+
/**
|
|
1057
|
+
* Note two nodes are now smushed
|
|
1058
|
+
* If only one was flagged as looked up, then the new node is looked up again,
|
|
1059
|
+
* which will make sure all the URIs are dereferenced
|
|
1060
|
+
*/
|
|
1061
|
+
nowKnownAs(was, now) {
|
|
1062
|
+
if (this.lookedUp[was.value]) {
|
|
1063
|
+
// Transfer userCallback
|
|
1064
|
+
if (!this.lookedUp[now.value]) {
|
|
1065
|
+
this.lookUpThing(now, was);
|
|
1066
|
+
}
|
|
1067
|
+
} else if (this.lookedUp[now.value]) {
|
|
1068
|
+
if (!this.lookedUp[was.value]) {
|
|
1069
|
+
this.lookUpThing(was, now);
|
|
1131
1070
|
}
|
|
1132
1071
|
}
|
|
1072
|
+
}
|
|
1133
1073
|
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
options.data = jsonString;
|
|
1153
|
-
_this6.webOperation('PUT', uri, options).then(function (res) {
|
|
1154
|
-
return resolve(res);
|
|
1155
|
-
}).catch(function (error) {
|
|
1156
|
-
return reject(error);
|
|
1157
|
-
});
|
|
1158
|
-
}
|
|
1159
|
-
});
|
|
1160
|
-
});
|
|
1161
|
-
}
|
|
1162
|
-
options.data = (0, _serialize.default)(doc, this.store, doc.value, options.contentType);
|
|
1163
|
-
return this.webOperation('PUT', uriSting, options);
|
|
1164
|
-
}
|
|
1165
|
-
}, {
|
|
1166
|
-
key: "webCopy",
|
|
1167
|
-
value: function webCopy(here, there, contentType) {
|
|
1168
|
-
var _this7 = this;
|
|
1169
|
-
return this.webOperation('GET', here).then(function (result) {
|
|
1170
|
-
return _this7.webOperation('PUT',
|
|
1171
|
-
// change to binary from text
|
|
1172
|
-
there, {
|
|
1173
|
-
data: result.responseText,
|
|
1174
|
-
contentType: contentType
|
|
1074
|
+
/**
|
|
1075
|
+
* Writes back to the web what we have in the store for this uri
|
|
1076
|
+
*/
|
|
1077
|
+
putBack(uri) {
|
|
1078
|
+
let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
|
1079
|
+
const uriSting = (0, _termValue.termValue)(uri);
|
|
1080
|
+
let doc = new _namedNode.default(uriSting).doc(); // strip off #
|
|
1081
|
+
options.contentType = options["content-type"] || options["Content-Type"] || options.contentType || _types.TurtleContentType;
|
|
1082
|
+
if (options.contentType === 'application/ld+json') {
|
|
1083
|
+
return new Promise((resolve, reject) => {
|
|
1084
|
+
(0, _serialize.default)(doc, this.store, doc.uri, options.contentType, (err, jsonString) => {
|
|
1085
|
+
if (err) {
|
|
1086
|
+
reject(err);
|
|
1087
|
+
} else {
|
|
1088
|
+
// @ts-ignore
|
|
1089
|
+
options.data = jsonString;
|
|
1090
|
+
this.webOperation('PUT', uri, options).then(res => resolve(res)).catch(error => reject(error));
|
|
1091
|
+
}
|
|
1175
1092
|
});
|
|
1176
1093
|
});
|
|
1177
1094
|
}
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1095
|
+
options.data = (0, _serialize.default)(doc, this.store, doc.value, options.contentType);
|
|
1096
|
+
return this.webOperation('PUT', uriSting, options);
|
|
1097
|
+
}
|
|
1098
|
+
webCopy(here, there, contentType) {
|
|
1099
|
+
return this.webOperation('GET', here).then(result => {
|
|
1100
|
+
return this.webOperation('PUT',
|
|
1101
|
+
// change to binary from text
|
|
1102
|
+
there, {
|
|
1103
|
+
data: result.responseText,
|
|
1104
|
+
contentType
|
|
1187
1105
|
});
|
|
1188
|
-
}
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
var _createIfNotExists = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee2(doc) {
|
|
1199
|
-
var contentType,
|
|
1200
|
-
data,
|
|
1201
|
-
fetcher,
|
|
1202
|
-
response,
|
|
1203
|
-
_args2 = arguments;
|
|
1204
|
-
return _regenerator.default.wrap(function _callee2$(_context2) {
|
|
1205
|
-
while (1) switch (_context2.prev = _context2.next) {
|
|
1206
|
-
case 0:
|
|
1207
|
-
contentType = _args2.length > 1 && _args2[1] !== undefined ? _args2[1] : _types.TurtleContentType;
|
|
1208
|
-
data = _args2.length > 2 && _args2[2] !== undefined ? _args2[2] : '';
|
|
1209
|
-
fetcher = this;
|
|
1210
|
-
_context2.prev = 3;
|
|
1211
|
-
_context2.next = 6;
|
|
1212
|
-
return fetcher.load(doc);
|
|
1213
|
-
case 6:
|
|
1214
|
-
response = _context2.sent;
|
|
1215
|
-
_context2.next = 26;
|
|
1216
|
-
break;
|
|
1217
|
-
case 9:
|
|
1218
|
-
_context2.prev = 9;
|
|
1219
|
-
_context2.t0 = _context2["catch"](3);
|
|
1220
|
-
if (!(_context2.t0.response.status === 404)) {
|
|
1221
|
-
_context2.next = 25;
|
|
1222
|
-
break;
|
|
1223
|
-
}
|
|
1224
|
-
_context2.prev = 12;
|
|
1225
|
-
_context2.next = 15;
|
|
1226
|
-
return fetcher.webOperation('PUT', doc.value, {
|
|
1227
|
-
data: data,
|
|
1228
|
-
contentType: contentType
|
|
1229
|
-
});
|
|
1230
|
-
case 15:
|
|
1231
|
-
response = _context2.sent;
|
|
1232
|
-
_context2.next = 21;
|
|
1233
|
-
break;
|
|
1234
|
-
case 18:
|
|
1235
|
-
_context2.prev = 18;
|
|
1236
|
-
_context2.t1 = _context2["catch"](12);
|
|
1237
|
-
throw _context2.t1;
|
|
1238
|
-
case 21:
|
|
1239
|
-
delete fetcher.requested[doc.value]; // delete cached 404 error
|
|
1240
|
-
// console.log('createIfNotExists doc created ok ' + doc)
|
|
1241
|
-
return _context2.abrupt("return", response);
|
|
1242
|
-
case 25:
|
|
1243
|
-
throw _context2.t0;
|
|
1244
|
-
case 26:
|
|
1245
|
-
return _context2.abrupt("return", response);
|
|
1246
|
-
case 27:
|
|
1247
|
-
case "end":
|
|
1248
|
-
return _context2.stop();
|
|
1249
|
-
}
|
|
1250
|
-
}, _callee2, this, [[3, 9], [12, 18]]);
|
|
1251
|
-
}));
|
|
1252
|
-
function createIfNotExists(_x4) {
|
|
1253
|
-
return _createIfNotExists.apply(this, arguments);
|
|
1254
|
-
}
|
|
1255
|
-
return createIfNotExists;
|
|
1256
|
-
}()
|
|
1257
|
-
/**
|
|
1258
|
-
* @param parentURI URI of parent container
|
|
1259
|
-
* @param folderName - Optional folder name (slug)
|
|
1260
|
-
* @param data - Optional folder metadata
|
|
1261
|
-
*/
|
|
1262
|
-
)
|
|
1263
|
-
}, {
|
|
1264
|
-
key: "createContainer",
|
|
1265
|
-
value: function createContainer(parentURI, folderName, data) {
|
|
1266
|
-
var headers = {
|
|
1267
|
-
// Force the right mime type for containers
|
|
1268
|
-
'content-type': _types.TurtleContentType,
|
|
1269
|
-
'link': this.ns.ldp('BasicContainer') + '; rel="type"'
|
|
1270
|
-
};
|
|
1271
|
-
if (folderName) {
|
|
1272
|
-
headers['slug'] = folderName;
|
|
1273
|
-
}
|
|
1106
|
+
});
|
|
1107
|
+
}
|
|
1108
|
+
delete(uri, options) {
|
|
1109
|
+
return this.webOperation('DELETE', uri, options).then(response => {
|
|
1110
|
+
this.requested[uri] = 404;
|
|
1111
|
+
this.nonexistent[uri] = true;
|
|
1112
|
+
this.unload(this.store.rdfFactory.namedNode(uri));
|
|
1113
|
+
return response;
|
|
1114
|
+
});
|
|
1115
|
+
}
|
|
1274
1116
|
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
var uri = (0, _termValue.termValue)(iri);
|
|
1288
|
-
var fetcher = this;
|
|
1117
|
+
/** Create an empty resource if it really does not exist
|
|
1118
|
+
* Be absolutely sure something does not exist before creating a new empty file
|
|
1119
|
+
* as otherwise existing could be deleted.
|
|
1120
|
+
* @param doc - The resource
|
|
1121
|
+
*/
|
|
1122
|
+
async createIfNotExists(doc) {
|
|
1123
|
+
let contentType = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : _types.TurtleContentType;
|
|
1124
|
+
let data = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : '';
|
|
1125
|
+
const fetcher = this;
|
|
1126
|
+
try {
|
|
1127
|
+
var response = await fetcher.load(doc);
|
|
1128
|
+
} catch (err) {
|
|
1289
1129
|
// @ts-ignore
|
|
1290
|
-
if (
|
|
1291
|
-
// console.log('
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
// console.log('
|
|
1130
|
+
if (err.response.status === 404) {
|
|
1131
|
+
// console.log('createIfNotExists: doc does NOT exist, will create... ' + doc)
|
|
1132
|
+
try {
|
|
1133
|
+
response = await fetcher.webOperation('PUT', doc.value, {
|
|
1134
|
+
data,
|
|
1135
|
+
contentType
|
|
1136
|
+
});
|
|
1137
|
+
} catch (err) {
|
|
1138
|
+
// console.log('createIfNotExists doc FAILED: ' + doc + ': ' + err)
|
|
1139
|
+
throw err;
|
|
1299
1140
|
}
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
console.error(msg);
|
|
1304
|
-
// alert(msg)
|
|
1141
|
+
delete fetcher.requested[doc.value]; // delete cached 404 error
|
|
1142
|
+
// console.log('createIfNotExists doc created ok ' + doc)
|
|
1143
|
+
return response;
|
|
1305
1144
|
} else {
|
|
1306
|
-
|
|
1307
|
-
|
|
1145
|
+
// console.log('createIfNotExists doc load error NOT 404: ' + doc + ': ' + err)
|
|
1146
|
+
throw err;
|
|
1308
1147
|
}
|
|
1309
1148
|
}
|
|
1149
|
+
// console.log('createIfNotExists: doc exists, all good: ' + doc)
|
|
1150
|
+
return response;
|
|
1151
|
+
}
|
|
1310
1152
|
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1153
|
+
/**
|
|
1154
|
+
* @param parentURI URI of parent container
|
|
1155
|
+
* @param folderName - Optional folder name (slug)
|
|
1156
|
+
* @param data - Optional folder metadata
|
|
1157
|
+
*/
|
|
1158
|
+
createContainer(parentURI, folderName, data) {
|
|
1159
|
+
let headers = {
|
|
1160
|
+
// Force the right mime type for containers
|
|
1161
|
+
'content-type': _types.TurtleContentType,
|
|
1162
|
+
'link': this.ns.ldp('BasicContainer') + '; rel="type"'
|
|
1163
|
+
};
|
|
1164
|
+
if (folderName) {
|
|
1165
|
+
headers['slug'] = folderName;
|
|
1166
|
+
}
|
|
1167
|
+
|
|
1168
|
+
// @ts-ignore These headers lack some of the required operators.
|
|
1169
|
+
let options = {
|
|
1170
|
+
headers
|
|
1171
|
+
};
|
|
1172
|
+
if (data) {
|
|
1173
|
+
options.body = data;
|
|
1174
|
+
}
|
|
1175
|
+
return this.webOperation('POST', parentURI, options);
|
|
1176
|
+
}
|
|
1177
|
+
invalidateCache(iri) {
|
|
1178
|
+
const uri = (0, _termValue.termValue)(iri);
|
|
1179
|
+
const fetcher = this;
|
|
1180
|
+
// @ts-ignore
|
|
1181
|
+
if (fetcher.fetchQueue && fetcher.fetchQueue[uri]) {
|
|
1182
|
+
// console.log('Internal error - fetchQueue exists ' + uri)
|
|
1183
|
+
var promise = fetcher.fetchQueue[uri];
|
|
1184
|
+
if (promise['PromiseStatus'] === 'resolved') {
|
|
1185
|
+
delete fetcher.fetchQueue[uri];
|
|
1186
|
+
} else {
|
|
1187
|
+
// pending
|
|
1188
|
+
delete fetcher.fetchQueue[uri];
|
|
1189
|
+
// console.log('*** Fetcher: pending fetchQueue deleted ' + uri)
|
|
1333
1190
|
}
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1191
|
+
}
|
|
1192
|
+
if (fetcher.requested[uri] && fetcher.requested[uri] !== 'done' && fetcher.requested[uri] !== 'failed' && fetcher.requested[uri] !== 404) {
|
|
1193
|
+
let msg = `Rdflib: fetcher: Destructive operation on <${fetcher.requested[uri]}> file being fetched! ` + uri;
|
|
1194
|
+
console.error(msg);
|
|
1195
|
+
// alert(msg)
|
|
1196
|
+
} else {
|
|
1197
|
+
delete fetcher.requested[uri]; // invalidate read cache -- @@ messes up logic if request in progress ??
|
|
1198
|
+
delete fetcher.nonexistent[uri];
|
|
1199
|
+
}
|
|
1200
|
+
}
|
|
1201
|
+
|
|
1202
|
+
/**
|
|
1203
|
+
* A generic web operation, at the fetch() level.
|
|
1204
|
+
* does not involve the quad store.
|
|
1205
|
+
*
|
|
1206
|
+
* Returns promise of Response
|
|
1207
|
+
* If data is returned, copies it to response.responseText before returning
|
|
1208
|
+
*/
|
|
1209
|
+
webOperation(method, uriIn) {
|
|
1210
|
+
let options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
|
|
1211
|
+
const uri = (0, _termValue.termValue)(uriIn);
|
|
1212
|
+
options.method = method;
|
|
1213
|
+
options.body = options.data || options.body;
|
|
1214
|
+
options.force = true;
|
|
1215
|
+
const fetcher = this;
|
|
1216
|
+
if (options.body && !options.contentType) {
|
|
1217
|
+
throw new Error('Web operation sending data must have a defined contentType.');
|
|
1218
|
+
}
|
|
1219
|
+
if (options.contentType) {
|
|
1220
|
+
options.headers = options.headers || {};
|
|
1221
|
+
options.headers['content-type'] = options.contentType;
|
|
1222
|
+
}
|
|
1223
|
+
Fetcher.setCredentials(uri, options);
|
|
1224
|
+
return new Promise(function (resolve, reject) {
|
|
1225
|
+
fetcher._fetch(uri, options).then(response => {
|
|
1226
|
+
if (response.ok) {
|
|
1227
|
+
if (method === 'PUT' || method === 'PATCH' || method === 'POST' || method === 'DELETE') {
|
|
1228
|
+
fetcher.invalidateCache(uri);
|
|
1229
|
+
} // response.body with Chrome can't be relied on
|
|
1230
|
+
if (response.text) {
|
|
1231
|
+
// Was: response.body https://github.com/linkeddata/rdflib.js/issues/506
|
|
1232
|
+
response.text().then(data => {
|
|
1233
|
+
response.responseText = data;
|
|
1348
1234
|
resolve(response);
|
|
1349
|
-
}
|
|
1235
|
+
});
|
|
1350
1236
|
} else {
|
|
1351
|
-
|
|
1352
|
-
if (response.statusText) msg += ' (' + response.statusText + ')';
|
|
1353
|
-
msg += ' on ' + method + ' of <' + uri + '>';
|
|
1354
|
-
if (response.responseText) msg += ': ' + response.responseText;
|
|
1355
|
-
var e2 = new Error(msg);
|
|
1356
|
-
e2.response = response;
|
|
1357
|
-
reject(e2);
|
|
1237
|
+
resolve(response);
|
|
1358
1238
|
}
|
|
1359
|
-
}
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1239
|
+
} else {
|
|
1240
|
+
let msg = 'Web error: ' + response.status;
|
|
1241
|
+
if (response.statusText) msg += ' (' + response.statusText + ')';
|
|
1242
|
+
msg += ' on ' + method + ' of <' + uri + '>';
|
|
1243
|
+
if (response.responseText) msg += ': ' + response.responseText;
|
|
1244
|
+
let e2 = new Error(msg);
|
|
1245
|
+
e2.response = response;
|
|
1246
|
+
reject(e2);
|
|
1247
|
+
}
|
|
1248
|
+
}, err => {
|
|
1249
|
+
let msg = 'Fetch error for ' + method + ' of <' + uri + '>:' + err;
|
|
1250
|
+
reject(new Error(msg));
|
|
1363
1251
|
});
|
|
1364
|
-
}
|
|
1252
|
+
});
|
|
1253
|
+
}
|
|
1365
1254
|
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
}); // Drop hash fragments
|
|
1383
|
-
|
|
1384
|
-
uris.forEach(function (u) {
|
|
1385
|
-
_this9.lookedUp[u] = true;
|
|
1386
|
-
});
|
|
1255
|
+
/**
|
|
1256
|
+
* Looks up something.
|
|
1257
|
+
* Looks up all the URIs a things has.
|
|
1258
|
+
*
|
|
1259
|
+
* @param term - canonical term for the thing whose URI is
|
|
1260
|
+
* to be dereferenced
|
|
1261
|
+
* @param rterm - the resource which referred to this
|
|
1262
|
+
* (for tracking bad links)
|
|
1263
|
+
*/
|
|
1264
|
+
lookUpThing(term, rterm) {
|
|
1265
|
+
let uris = this.store.uris(term); // Get all URIs
|
|
1266
|
+
uris = uris.map(u => Uri.docpart(u)); // Drop hash fragments
|
|
1267
|
+
|
|
1268
|
+
uris.forEach(u => {
|
|
1269
|
+
this.lookedUp[u] = true;
|
|
1270
|
+
});
|
|
1387
1271
|
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1272
|
+
// @ts-ignore Recursive type
|
|
1273
|
+
return this.load(uris, {
|
|
1274
|
+
referringTerm: rterm
|
|
1275
|
+
});
|
|
1276
|
+
}
|
|
1393
1277
|
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
if (
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
return v.value;
|
|
1418
|
-
});
|
|
1419
|
-
}
|
|
1420
|
-
return [];
|
|
1278
|
+
/**
|
|
1279
|
+
* Looks up response header.
|
|
1280
|
+
*
|
|
1281
|
+
* @returns {Array|undefined} a list of header values found in a stored HTTP
|
|
1282
|
+
* response, or [] if response was found but no header found,
|
|
1283
|
+
* or undefined if no response is available.
|
|
1284
|
+
* Looks for { [] link:requestedURI ?uri; link:response [ httph:header-name ?value ] }
|
|
1285
|
+
*/
|
|
1286
|
+
getHeader(doc, header) {
|
|
1287
|
+
const kb = this.store; // look for the URI (AS A STRING NOT A NODE) for a stored request
|
|
1288
|
+
const docuri = doc.value;
|
|
1289
|
+
const requests = kb.each(undefined, this.ns.link('requestedURI'), kb.rdfFactory.literal(docuri));
|
|
1290
|
+
for (let r = 0; r < requests.length; r++) {
|
|
1291
|
+
let request = requests[r];
|
|
1292
|
+
if (request !== undefined) {
|
|
1293
|
+
let response = kb.any(request, this.ns.link('response'));
|
|
1294
|
+
if (response !== undefined && kb.anyValue(response, this.ns.http('status')) && kb.anyValue(response, this.ns.http('status')).startsWith('2')) {
|
|
1295
|
+
// Only look at success returns - not 401 error messagess etc
|
|
1296
|
+
let results = kb.each(response, this.ns.httph(header.toLowerCase()));
|
|
1297
|
+
if (results.length) {
|
|
1298
|
+
return results.map(v => {
|
|
1299
|
+
return v.value;
|
|
1300
|
+
});
|
|
1421
1301
|
}
|
|
1302
|
+
return [];
|
|
1422
1303
|
}
|
|
1423
1304
|
}
|
|
1424
|
-
return undefined;
|
|
1425
1305
|
}
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1306
|
+
return undefined;
|
|
1307
|
+
}
|
|
1308
|
+
saveRequestMetadata(docuri, options) {
|
|
1309
|
+
let req = options.req;
|
|
1310
|
+
let kb = this.store;
|
|
1311
|
+
let rterm = options.referringTerm;
|
|
1312
|
+
this.addStatus(options.req, 'Accept: ' + options.headers['accept']);
|
|
1313
|
+
if ((0, _terms.isNamedNode)(rterm)) {
|
|
1314
|
+
kb.add(kb.rdfFactory.namedNode(docuri), this.ns.link('requestedBy'), rterm, this.appNode);
|
|
1315
|
+
}
|
|
1316
|
+
if (options.original && options.original.value !== docuri) {
|
|
1317
|
+
kb.add(req, this.ns.link('orginalURI'), kb.rdfFactory.literal(options.original.value), this.appNode);
|
|
1318
|
+
}
|
|
1319
|
+
const now = new Date();
|
|
1320
|
+
const timeNow = '[' + now.getHours() + ':' + now.getMinutes() + ':' + now.getSeconds() + '] ';
|
|
1321
|
+
kb.add(req, this.ns.rdfs('label'), kb.rdfFactory.literal(timeNow + ' Request for ' + docuri), this.appNode);
|
|
1322
|
+
// We store the docuri as a string, not as a node,
|
|
1323
|
+
// see https://github.com/linkeddata/rdflib.js/pull/427#pullrequestreview-447910061
|
|
1324
|
+
kb.add(req, this.ns.link('requestedURI'), kb.rdfFactory.literal(docuri), this.appNode);
|
|
1325
|
+
kb.add(req, this.ns.link('status'), kb.collection(), this.appNode);
|
|
1326
|
+
}
|
|
1327
|
+
saveResponseMetadata(response, options) {
|
|
1328
|
+
const kb = this.store;
|
|
1329
|
+
let responseNode = kb.bnode();
|
|
1330
|
+
kb.add(options.req, this.ns.link('response'), responseNode, this.appNode);
|
|
1331
|
+
kb.add(responseNode, this.ns.http('status'), kb.rdfFactory.literal(response.status), this.appNode);
|
|
1332
|
+
kb.add(responseNode, this.ns.http('statusText'), kb.rdfFactory.literal(response.statusText), this.appNode);
|
|
1333
|
+
|
|
1334
|
+
// Save the response headers
|
|
1335
|
+
response.headers.forEach((value, header) => {
|
|
1336
|
+
kb.add(responseNode, this.ns.httph(header), this.store.rdfFactory.literal(value), this.appNode);
|
|
1337
|
+
if (header === 'content-type') {
|
|
1338
|
+
kb.add(options.resource, this.ns.rdf('type'), kb.rdfFactory.namedNode(Util.mediaTypeClass(value).value), this.appNode // responseNode
|
|
1339
|
+
);
|
|
1438
1340
|
}
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
key: "saveResponseMetadata",
|
|
1449
|
-
value: function saveResponseMetadata(response, options) {
|
|
1450
|
-
var _this10 = this;
|
|
1451
|
-
var kb = this.store;
|
|
1452
|
-
var responseNode = kb.bnode();
|
|
1453
|
-
kb.add(options.req, this.ns.link('response'), responseNode, this.appNode);
|
|
1454
|
-
kb.add(responseNode, this.ns.http('status'), kb.rdfFactory.literal(response.status), this.appNode);
|
|
1455
|
-
kb.add(responseNode, this.ns.http('statusText'), kb.rdfFactory.literal(response.statusText), this.appNode);
|
|
1456
|
-
|
|
1457
|
-
// Save the response headers
|
|
1458
|
-
response.headers.forEach(function (value, header) {
|
|
1459
|
-
kb.add(responseNode, _this10.ns.httph(header), _this10.store.rdfFactory.literal(value), _this10.appNode);
|
|
1460
|
-
if (header === 'content-type') {
|
|
1461
|
-
kb.add(options.resource, _this10.ns.rdf('type'), kb.rdfFactory.namedNode(Util.mediaTypeClass(value).value), _this10.appNode // responseNode
|
|
1462
|
-
);
|
|
1463
|
-
}
|
|
1464
|
-
});
|
|
1465
|
-
return responseNode;
|
|
1466
|
-
}
|
|
1467
|
-
}, {
|
|
1468
|
-
key: "objectRefresh",
|
|
1469
|
-
value: function objectRefresh(term) {
|
|
1470
|
-
var uris = this.store.uris(term); // Get all URIs
|
|
1471
|
-
if (typeof uris !== 'undefined') {
|
|
1472
|
-
for (var i = 0; i < uris.length; i++) {
|
|
1473
|
-
this.refresh(this.store.rdfFactory.namedNode(Uri.docpart(uris[i])));
|
|
1474
|
-
// what about rterm?
|
|
1475
|
-
}
|
|
1341
|
+
});
|
|
1342
|
+
return responseNode;
|
|
1343
|
+
}
|
|
1344
|
+
objectRefresh(term) {
|
|
1345
|
+
let uris = this.store.uris(term); // Get all URIs
|
|
1346
|
+
if (typeof uris !== 'undefined') {
|
|
1347
|
+
for (let i = 0; i < uris.length; i++) {
|
|
1348
|
+
this.refresh(this.store.rdfFactory.namedNode(Uri.docpart(uris[i])));
|
|
1349
|
+
// what about rterm?
|
|
1476
1350
|
}
|
|
1477
1351
|
}
|
|
1352
|
+
}
|
|
1478
1353
|
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
if (!exp || new Date(exp[0]).getTime() <= new Date().getTime()) {
|
|
1505
|
-
this.refresh(term, userCallback);
|
|
1506
|
-
} else {
|
|
1507
|
-
userCallback(true, 'Not expired', {});
|
|
1508
|
-
}
|
|
1354
|
+
/* refresh Reload data from a given document
|
|
1355
|
+
**
|
|
1356
|
+
** @param term - An RDF Named Node for the eodcument in question
|
|
1357
|
+
** @param userCallback - A function userCallback(ok, message, response)
|
|
1358
|
+
*/
|
|
1359
|
+
refresh(term, userCallback) {
|
|
1360
|
+
// sources_refresh
|
|
1361
|
+
this.fireCallbacks('refresh', arguments);
|
|
1362
|
+
this.nowOrWhenFetched(term, {
|
|
1363
|
+
force: true,
|
|
1364
|
+
clearPreviousData: true
|
|
1365
|
+
}, userCallback);
|
|
1366
|
+
}
|
|
1367
|
+
|
|
1368
|
+
/* refreshIfExpired Conditional refresh if Expired
|
|
1369
|
+
**
|
|
1370
|
+
** @param term - An RDF Named Node for the eodcument in question
|
|
1371
|
+
** @param userCallback - A function userCallback(ok, message, response)
|
|
1372
|
+
*/
|
|
1373
|
+
refreshIfExpired(term, userCallback) {
|
|
1374
|
+
let exp = this.getHeader(term, 'Expires');
|
|
1375
|
+
if (!exp || new Date(exp[0]).getTime() <= new Date().getTime()) {
|
|
1376
|
+
this.refresh(term, userCallback);
|
|
1377
|
+
} else {
|
|
1378
|
+
userCallback(true, 'Not expired', {});
|
|
1509
1379
|
}
|
|
1510
|
-
}
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
delete this.requested[Uri.docpart(term.value)];
|
|
1517
|
-
}
|
|
1518
|
-
this.fireCallbacks('retract', arguments);
|
|
1519
|
-
}
|
|
1520
|
-
}, {
|
|
1521
|
-
key: "getState",
|
|
1522
|
-
value: function getState(docuri) {
|
|
1523
|
-
if (typeof this.requested[docuri] === 'undefined') {
|
|
1524
|
-
return 'unrequested';
|
|
1525
|
-
} else if (this.requested[docuri] === true) {
|
|
1526
|
-
return 'requested';
|
|
1527
|
-
} else if (this.requested[docuri] === 'done') {
|
|
1528
|
-
return 'fetched';
|
|
1529
|
-
} else if (this.requested[docuri] === 'redirected') {
|
|
1530
|
-
return this.getState(this.redirectedTo[docuri]);
|
|
1531
|
-
} else {
|
|
1532
|
-
// An non-200 HTTP error status
|
|
1533
|
-
return 'failed';
|
|
1534
|
-
}
|
|
1380
|
+
}
|
|
1381
|
+
retract(term) {
|
|
1382
|
+
// sources_retract
|
|
1383
|
+
this.store.removeMany(undefined, undefined, undefined, term);
|
|
1384
|
+
if (term.value) {
|
|
1385
|
+
delete this.requested[Uri.docpart(term.value)];
|
|
1535
1386
|
}
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
}
|
|
1551
|
-
}, {
|
|
1552
|
-
key: "addHandler",
|
|
1553
|
-
value: function addHandler(handler) {
|
|
1554
|
-
this.handlers.push(handler);
|
|
1555
|
-
handler.register(this);
|
|
1556
|
-
}
|
|
1557
|
-
}, {
|
|
1558
|
-
key: "retryNoCredentials",
|
|
1559
|
-
value: function retryNoCredentials(docuri, options) {
|
|
1560
|
-
// console.log('Fetcher: CORS: RETRYING with NO CREDENTIALS for ' + options.resource)
|
|
1561
|
-
|
|
1562
|
-
options.retriedWithNoCredentials = true; // protect against being called twice
|
|
1563
|
-
|
|
1564
|
-
delete this.requested[docuri]; // forget the original request happened
|
|
1565
|
-
delete this.fetchQueue[docuri];
|
|
1566
|
-
// Note: XHR property was withCredentials, but fetch property is just credentials
|
|
1567
|
-
var newOptions = Object.assign({}, options, {
|
|
1568
|
-
credentials: 'omit'
|
|
1569
|
-
});
|
|
1570
|
-
this.addStatus(options.req, 'Abort: Will retry with credentials SUPPRESSED to see if that helps');
|
|
1571
|
-
return this.load(docuri, newOptions);
|
|
1387
|
+
this.fireCallbacks('retract', arguments);
|
|
1388
|
+
}
|
|
1389
|
+
getState(docuri) {
|
|
1390
|
+
if (typeof this.requested[docuri] === 'undefined') {
|
|
1391
|
+
return 'unrequested';
|
|
1392
|
+
} else if (this.requested[docuri] === true) {
|
|
1393
|
+
return 'requested';
|
|
1394
|
+
} else if (this.requested[docuri] === 'done') {
|
|
1395
|
+
return 'fetched';
|
|
1396
|
+
} else if (this.requested[docuri] === 'redirected') {
|
|
1397
|
+
return this.getState(this.redirectedTo[docuri]);
|
|
1398
|
+
} else {
|
|
1399
|
+
// An non-200 HTTP error status
|
|
1400
|
+
return 'failed';
|
|
1572
1401
|
}
|
|
1402
|
+
}
|
|
1403
|
+
isPending(docuri) {
|
|
1404
|
+
// sources_pending
|
|
1405
|
+
// doing anyStatementMatching is wasting time
|
|
1406
|
+
// if it's not pending: false -> flailed
|
|
1407
|
+
// 'done' -> done 'redirected' -> redirected
|
|
1408
|
+
return this.requested[docuri] === true;
|
|
1409
|
+
}
|
|
1410
|
+
unload(term) {
|
|
1411
|
+
this.store.removeDocument(term);
|
|
1412
|
+
delete this.requested[term.value]; // So it can be load2ed again
|
|
1413
|
+
}
|
|
1414
|
+
addHandler(handler) {
|
|
1415
|
+
this.handlers.push(handler);
|
|
1416
|
+
handler.register(this);
|
|
1417
|
+
}
|
|
1418
|
+
retryNoCredentials(docuri, options) {
|
|
1419
|
+
// console.log('Fetcher: CORS: RETRYING with NO CREDENTIALS for ' + options.resource)
|
|
1573
1420
|
|
|
1574
|
-
|
|
1575
|
-
* Tests whether a request is being made to a cross-site URI (for purposes
|
|
1576
|
-
* of retrying with a proxy)
|
|
1577
|
-
*/
|
|
1578
|
-
}, {
|
|
1579
|
-
key: "isCrossSite",
|
|
1580
|
-
value: function isCrossSite(uri) {
|
|
1581
|
-
// Mashup situation, not node etc
|
|
1582
|
-
if (typeof document === 'undefined' || !document.location) {
|
|
1583
|
-
return false;
|
|
1584
|
-
}
|
|
1585
|
-
var hostpart = Uri.hostpart;
|
|
1586
|
-
var here = '' + document.location;
|
|
1587
|
-
return (hostpart(here) && hostpart(uri) && hostpart(here)) !== hostpart(uri);
|
|
1588
|
-
}
|
|
1421
|
+
options.retriedWithNoCredentials = true; // protect against being called twice
|
|
1589
1422
|
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
if (options.credentials && options.credentials === 'include' && !options.retriedWithNoCredentials) {
|
|
1600
|
-
return this.retryNoCredentials(docuri, options);
|
|
1601
|
-
}
|
|
1423
|
+
delete this.requested[docuri]; // forget the original request happened
|
|
1424
|
+
delete this.fetchQueue[docuri];
|
|
1425
|
+
// Note: XHR property was withCredentials, but fetch property is just credentials
|
|
1426
|
+
let newOptions = Object.assign({}, options, {
|
|
1427
|
+
credentials: 'omit'
|
|
1428
|
+
});
|
|
1429
|
+
this.addStatus(options.req, 'Abort: Will retry with credentials SUPPRESSED to see if that helps');
|
|
1430
|
+
return this.load(docuri, newOptions);
|
|
1431
|
+
}
|
|
1602
1432
|
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
message += " ".concat(response.responseText);
|
|
1617
|
-
}
|
|
1618
|
-
}
|
|
1433
|
+
/**
|
|
1434
|
+
* Tests whether a request is being made to a cross-site URI (for purposes
|
|
1435
|
+
* of retrying with a proxy)
|
|
1436
|
+
*/
|
|
1437
|
+
isCrossSite(uri) {
|
|
1438
|
+
// Mashup situation, not node etc
|
|
1439
|
+
if (typeof document === 'undefined' || !document.location) {
|
|
1440
|
+
return false;
|
|
1441
|
+
}
|
|
1442
|
+
const hostpart = Uri.hostpart;
|
|
1443
|
+
const here = '' + document.location;
|
|
1444
|
+
return (hostpart(here) && hostpart(uri) && hostpart(here)) !== hostpart(uri);
|
|
1445
|
+
}
|
|
1619
1446
|
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1447
|
+
/**
|
|
1448
|
+
* Called when there's a network error in fetch(), or a response
|
|
1449
|
+
* with status of 0.
|
|
1450
|
+
*/
|
|
1451
|
+
handleError(response, docuri, options) {
|
|
1452
|
+
if (this.isCrossSite(docuri)) {
|
|
1453
|
+
// Make sure we haven't retried already
|
|
1454
|
+
if (options.credentials && options.credentials === 'include' && !options.retriedWithNoCredentials) {
|
|
1455
|
+
return this.retryNoCredentials(docuri, options);
|
|
1456
|
+
}
|
|
1623
1457
|
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
var prev = req;
|
|
1630
|
-
if (locURI) {
|
|
1631
|
-
var reqURI = kb.any(prev, this.ns.link('requestedURI'));
|
|
1632
|
-
if (reqURI && reqURI.value !== locURI) {
|
|
1633
|
-
kb.add(kb.rdfFactory.namedNode(locURI), this.ns.rdf('type'), rdfType, this.appNode);
|
|
1634
|
-
}
|
|
1458
|
+
// Now attempt retry via proxy
|
|
1459
|
+
let proxyUri = Fetcher.crossSiteProxy(docuri);
|
|
1460
|
+
if (proxyUri && !options.proxyUsed) {
|
|
1461
|
+
// console.log('web: Direct failed so trying proxy ' + proxyUri)
|
|
1462
|
+
return this.redirectToProxy(proxyUri, options);
|
|
1635
1463
|
}
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
}
|
|
1645
|
-
var response = kb.any(prev, kb.rdfFactory.namedNode('http://www.w3.org/2007/ont/link#response'));
|
|
1646
|
-
if (!response) {
|
|
1647
|
-
break;
|
|
1648
|
-
}
|
|
1649
|
-
var redirection = kb.any(response, kb.rdfFactory.namedNode('http://www.w3.org/2007/ont/http#status'));
|
|
1650
|
-
if (!redirection) {
|
|
1651
|
-
break;
|
|
1652
|
-
}
|
|
1653
|
-
// @ts-ignore always true?
|
|
1654
|
-
if (redirection !== '301' && redirection !== '302') {
|
|
1655
|
-
break;
|
|
1656
|
-
}
|
|
1464
|
+
}
|
|
1465
|
+
var message;
|
|
1466
|
+
if (response instanceof Error) {
|
|
1467
|
+
message = 'Fetch error: ' + response.message;
|
|
1468
|
+
} else {
|
|
1469
|
+
message = response.statusText;
|
|
1470
|
+
if (response.responseText) {
|
|
1471
|
+
message += ` ${response.responseText}`;
|
|
1657
1472
|
}
|
|
1658
1473
|
}
|
|
1659
1474
|
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
var
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
var contentLocation = headers.get('content-location');
|
|
1673
|
-
|
|
1674
|
-
// this.fireCallbacks('recv', xhr.args)
|
|
1675
|
-
// this.fireCallbacks('headers', [{uri: docuri, headers: xhr.headers}])
|
|
1676
|
-
|
|
1677
|
-
// Check for masked errors (CORS, etc)
|
|
1678
|
-
if (response.status === 0) {
|
|
1679
|
-
// console.log('Masked error - status 0 for ' + docuri)
|
|
1680
|
-
return this.handleError(response, docuri, options);
|
|
1475
|
+
// This is either not a CORS error, or retries have been made
|
|
1476
|
+
return this.failFetch(options, message, response.status || 998, response);
|
|
1477
|
+
}
|
|
1478
|
+
|
|
1479
|
+
// deduce some things from the HTTP transaction
|
|
1480
|
+
addType(rdfType, req, kb, locURI) {
|
|
1481
|
+
// add type to all redirected resources too
|
|
1482
|
+
let prev = req;
|
|
1483
|
+
if (locURI) {
|
|
1484
|
+
var reqURI = kb.any(prev, this.ns.link('requestedURI'));
|
|
1485
|
+
if (reqURI && reqURI.value !== locURI) {
|
|
1486
|
+
kb.add(kb.rdfFactory.namedNode(locURI), this.ns.rdf('type'), rdfType, this.appNode);
|
|
1681
1487
|
}
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1488
|
+
}
|
|
1489
|
+
for (;;) {
|
|
1490
|
+
const doc = kb.any(prev, this.ns.link('requestedURI'));
|
|
1491
|
+
if (doc && doc.value) {
|
|
1492
|
+
kb.add(kb.rdfFactory.namedNode(doc.value), this.ns.rdf('type'), rdfType, this.appNode);
|
|
1493
|
+
} // convert Literal
|
|
1494
|
+
prev = kb.any(undefined, kb.rdfFactory.namedNode('http://www.w3.org/2007/ont/link#redirectedRequest'), prev);
|
|
1495
|
+
if (!prev) {
|
|
1496
|
+
break;
|
|
1691
1497
|
}
|
|
1692
|
-
var
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
absContentLocation = Uri.join(contentLocation, docuri);
|
|
1696
|
-
if (absContentLocation !== docuri) {
|
|
1697
|
-
diffLocation = absContentLocation;
|
|
1698
|
-
}
|
|
1498
|
+
var response = kb.any(prev, kb.rdfFactory.namedNode('http://www.w3.org/2007/ont/link#response'));
|
|
1499
|
+
if (!response) {
|
|
1500
|
+
break;
|
|
1699
1501
|
}
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
this.addType(this.ns.link('Document'), reqNode, kb, diffLocation);
|
|
1704
|
-
}
|
|
1705
|
-
|
|
1706
|
-
// Before we parse new data clear old but only on 200
|
|
1707
|
-
if (options.clearPreviousData) {
|
|
1708
|
-
// kb.removeDocument(options.resource)
|
|
1709
|
-
// only remove content, keep metatdata
|
|
1710
|
-
var sts = kb.statementsMatching(undefined, undefined, undefined, options.resource).slice(); // Take a copy as this is the actual index
|
|
1711
|
-
for (var i = 0; i < sts.length; i++) {
|
|
1712
|
-
kb.removeStatement(sts[i]);
|
|
1713
|
-
}
|
|
1714
|
-
}
|
|
1715
|
-
var isImage = contentType.includes('image/') || contentType.includes('application/pdf');
|
|
1716
|
-
if (contentType && isImage) {
|
|
1717
|
-
this.addType(kb.rdfFactory.namedNode('http://purl.org/dc/terms/Image'), reqNode, kb, docuri);
|
|
1718
|
-
if (diffLocation) {
|
|
1719
|
-
this.addType(kb.rdfFactory.namedNode('http://purl.org/dc/terms/Image'), reqNode, kb, diffLocation);
|
|
1720
|
-
}
|
|
1721
|
-
}
|
|
1502
|
+
var redirection = kb.any(response, kb.rdfFactory.namedNode('http://www.w3.org/2007/ont/http#status'));
|
|
1503
|
+
if (!redirection) {
|
|
1504
|
+
break;
|
|
1722
1505
|
}
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
if (!options.force && diffLocation && this.requested[absContentLocation] === 'done') {
|
|
1727
|
-
// we have already fetched this
|
|
1728
|
-
// should we smush too?
|
|
1729
|
-
// log.info("HTTP headers indicate we have already" + " retrieved " +
|
|
1730
|
-
// xhr.resource + " as " + absContentLocation + ". Aborting.")
|
|
1731
|
-
return this.doneFetch(options, response);
|
|
1732
|
-
}
|
|
1733
|
-
this.requested[absContentLocation] = true;
|
|
1734
|
-
}
|
|
1735
|
-
this.parseLinkHeader(headers.get('link'), options.original, reqNode);
|
|
1736
|
-
var handler = this.handlerForContentType(contentType, response);
|
|
1737
|
-
if (!handler) {
|
|
1738
|
-
// Not a problem, we just don't extract data
|
|
1739
|
-
this.addStatus(reqNode, 'Fetch over. No data handled.');
|
|
1740
|
-
return this.doneFetch(options, response);
|
|
1506
|
+
// @ts-ignore always true?
|
|
1507
|
+
if (redirection !== '301' && redirection !== '302') {
|
|
1508
|
+
break;
|
|
1741
1509
|
}
|
|
1742
|
-
return response.text()
|
|
1743
|
-
// @ts-ignore Types seem right
|
|
1744
|
-
.then(function (responseText) {
|
|
1745
|
-
response.responseText = responseText;
|
|
1746
|
-
return handler.parse(_this11, responseText, options, response);
|
|
1747
|
-
});
|
|
1748
1510
|
}
|
|
1749
|
-
}
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1511
|
+
}
|
|
1512
|
+
|
|
1513
|
+
/**
|
|
1514
|
+
* Handle fetch() response
|
|
1515
|
+
*/
|
|
1516
|
+
handleResponse(response, docuri, options) {
|
|
1517
|
+
const kb = this.store;
|
|
1518
|
+
const headers = response.headers;
|
|
1519
|
+
const reqNode = options.req;
|
|
1520
|
+
const responseNode = this.saveResponseMetadata(response, options);
|
|
1521
|
+
const contentType = this.normalizedContentType(options, headers) || '';
|
|
1522
|
+
let contentLocation = headers.get('content-location');
|
|
1523
|
+
|
|
1524
|
+
// this.fireCallbacks('recv', xhr.args)
|
|
1525
|
+
// this.fireCallbacks('headers', [{uri: docuri, headers: xhr.headers}])
|
|
1526
|
+
|
|
1527
|
+
// Check for masked errors (CORS, etc)
|
|
1528
|
+
if (response.status === 0) {
|
|
1529
|
+
// console.log('Masked error - status 0 for ' + docuri)
|
|
1530
|
+
return this.handleError(response, docuri, options);
|
|
1531
|
+
}
|
|
1532
|
+
if (response.status >= 400) {
|
|
1533
|
+
if (response.status === 404) {
|
|
1534
|
+
this.nonexistent[options.original.value] = true;
|
|
1535
|
+
this.nonexistent[docuri] = true;
|
|
1536
|
+
}
|
|
1537
|
+
return this.saveErrorResponse(response, responseNode).then(() => {
|
|
1538
|
+
let errorMessage = options.resource + ' ' + response.statusText;
|
|
1539
|
+
return this.failFetch(options, errorMessage, response.status, response);
|
|
1758
1540
|
});
|
|
1759
1541
|
}
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1542
|
+
var diffLocation = null;
|
|
1543
|
+
var absContentLocation = null;
|
|
1544
|
+
if (contentLocation) {
|
|
1545
|
+
absContentLocation = Uri.join(contentLocation, docuri);
|
|
1546
|
+
if (absContentLocation !== docuri) {
|
|
1547
|
+
diffLocation = absContentLocation;
|
|
1765
1548
|
}
|
|
1766
|
-
var Handler = this.handlers.find(function (handler) {
|
|
1767
|
-
return contentType.match(handler.pattern);
|
|
1768
|
-
});
|
|
1769
|
-
|
|
1770
|
-
// @ts-ignore in practice all Handlers have constructors.
|
|
1771
|
-
return Handler ? new Handler(response) : null;
|
|
1772
1549
|
}
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
}
|
|
1778
|
-
}, {
|
|
1779
|
-
key: "normalizedContentType",
|
|
1780
|
-
value: function normalizedContentType(options, headers) {
|
|
1781
|
-
if (options.forceContentType) {
|
|
1782
|
-
return options.forceContentType;
|
|
1550
|
+
if (response.status === 200) {
|
|
1551
|
+
this.addType(this.ns.link('Document'), reqNode, kb, docuri);
|
|
1552
|
+
if (diffLocation) {
|
|
1553
|
+
this.addType(this.ns.link('Document'), reqNode, kb, diffLocation);
|
|
1783
1554
|
}
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1555
|
+
|
|
1556
|
+
// Before we parse new data clear old but only on 200
|
|
1557
|
+
if (options.clearPreviousData) {
|
|
1558
|
+
// kb.removeDocument(options.resource)
|
|
1559
|
+
// only remove content, keep metatdata
|
|
1560
|
+
const sts = kb.statementsMatching(undefined, undefined, undefined, options.resource).slice(); // Take a copy as this is the actual index
|
|
1561
|
+
for (let i = 0; i < sts.length; i++) {
|
|
1562
|
+
kb.removeStatement(sts[i]);
|
|
1789
1563
|
}
|
|
1790
1564
|
}
|
|
1791
|
-
|
|
1792
|
-
if (
|
|
1793
|
-
|
|
1565
|
+
let isImage = contentType.includes('image/') || contentType.includes('application/pdf');
|
|
1566
|
+
if (contentType && isImage) {
|
|
1567
|
+
this.addType(kb.rdfFactory.namedNode('http://purl.org/dc/terms/Image'), reqNode, kb, docuri);
|
|
1568
|
+
if (diffLocation) {
|
|
1569
|
+
this.addType(kb.rdfFactory.namedNode('http://purl.org/dc/terms/Image'), reqNode, kb, diffLocation);
|
|
1570
|
+
}
|
|
1794
1571
|
}
|
|
1795
|
-
return contentType;
|
|
1796
1572
|
}
|
|
1797
1573
|
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
options.proxyUsed = true;
|
|
1807
|
-
var kb = this.store;
|
|
1808
|
-
var oldReq = options.req; // request metadata blank node
|
|
1809
|
-
|
|
1810
|
-
if (!options.noMeta) {
|
|
1811
|
-
kb.add(oldReq, this.ns.link('redirectedTo'), kb.rdfFactory.namedNode(newURI), oldReq);
|
|
1812
|
-
this.addStatus(oldReq, 'redirected to new request'); // why
|
|
1574
|
+
// If we have already got the thing at this location, abort
|
|
1575
|
+
if (contentLocation) {
|
|
1576
|
+
if (!options.force && diffLocation && this.requested[absContentLocation] === 'done') {
|
|
1577
|
+
// we have already fetched this
|
|
1578
|
+
// should we smush too?
|
|
1579
|
+
// log.info("HTTP headers indicate we have already" + " retrieved " +
|
|
1580
|
+
// xhr.resource + " as " + absContentLocation + ". Aborting.")
|
|
1581
|
+
return this.doneFetch(options, response);
|
|
1813
1582
|
}
|
|
1814
|
-
this.requested[
|
|
1815
|
-
this.redirectedTo[options.resource.value] = newURI;
|
|
1816
|
-
var newOptions = Object.assign({}, options);
|
|
1817
|
-
newOptions.baseURI = options.resource.value;
|
|
1818
|
-
return this.fetchUri(newURI, newOptions).then(function (response) {
|
|
1819
|
-
if (!newOptions.noMeta) {
|
|
1820
|
-
kb.add(oldReq, _this13.ns.link('redirectedRequest'), newOptions.req, _this13.appNode);
|
|
1821
|
-
}
|
|
1822
|
-
return response;
|
|
1823
|
-
});
|
|
1583
|
+
this.requested[absContentLocation] = true;
|
|
1824
1584
|
}
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
if (_this14.isPending(uri) && !options.retriedWithNoCredentials && !options.proxyUsed) {
|
|
1832
|
-
resolve(_this14.failFetch(options, "Request to ".concat(uri, " timed out"), 'timeout'));
|
|
1833
|
-
}
|
|
1834
|
-
}, _this14.timeout));
|
|
1835
|
-
});
|
|
1836
|
-
}
|
|
1837
|
-
}, {
|
|
1838
|
-
key: "addFetchCallback",
|
|
1839
|
-
value: function addFetchCallback(uri, callback) {
|
|
1840
|
-
if (!this.fetchCallbacks[uri]) {
|
|
1841
|
-
this.fetchCallbacks[uri] = [callback];
|
|
1842
|
-
} else {
|
|
1843
|
-
this.fetchCallbacks[uri].push(callback);
|
|
1844
|
-
}
|
|
1585
|
+
this.parseLinkHeader(headers.get('link'), options.original, reqNode);
|
|
1586
|
+
let handler = this.handlerForContentType(contentType, response);
|
|
1587
|
+
if (!handler) {
|
|
1588
|
+
// Not a problem, we just don't extract data
|
|
1589
|
+
this.addStatus(reqNode, 'Fetch over. No data handled.');
|
|
1590
|
+
return this.doneFetch(options, response);
|
|
1845
1591
|
}
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
}
|
|
1859
|
-
return acceptstring;
|
|
1860
|
-
}
|
|
1861
|
-
// var updatesVia = new $rdf.UpdatesVia(this) // Subscribe to headers
|
|
1862
|
-
// @@@@@@@@ This is turned off because it causes a websocket to be set up for ANY fetch
|
|
1863
|
-
// whether we want to track it ot not. including ontologies loaed though the XSSproxy
|
|
1864
|
-
}], [{
|
|
1865
|
-
key: "crossSiteProxy",
|
|
1866
|
-
value: function crossSiteProxy(uri) {
|
|
1867
|
-
if (Fetcher.crossSiteProxyTemplate) {
|
|
1868
|
-
return Fetcher.crossSiteProxyTemplate.replace('{uri}', encodeURIComponent(uri));
|
|
1869
|
-
} else {
|
|
1870
|
-
return undefined;
|
|
1592
|
+
return response.text()
|
|
1593
|
+
// @ts-ignore Types seem right
|
|
1594
|
+
.then(responseText => {
|
|
1595
|
+
response.responseText = responseText;
|
|
1596
|
+
return handler.parse(this, responseText, options, response);
|
|
1597
|
+
});
|
|
1598
|
+
}
|
|
1599
|
+
saveErrorResponse(response, responseNode) {
|
|
1600
|
+
let kb = this.store;
|
|
1601
|
+
return response.text().then(content => {
|
|
1602
|
+
if (content.length > 10) {
|
|
1603
|
+
kb.add(responseNode, this.ns.http('content'), kb.rdfFactory.literal(content), responseNode);
|
|
1871
1604
|
}
|
|
1605
|
+
});
|
|
1606
|
+
}
|
|
1607
|
+
handlerForContentType(contentType, response) {
|
|
1608
|
+
if (!contentType) {
|
|
1609
|
+
return null;
|
|
1872
1610
|
}
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
// Map the URI to a localhost proxy if we are running on localhost
|
|
1877
|
-
// This is used for working offline, e.g. on planes.
|
|
1878
|
-
// Is the script itself is running in localhost, then access all
|
|
1879
|
-
// data in a localhost mirror.
|
|
1880
|
-
// Do not remove without checking with TimBL
|
|
1881
|
-
var requestedURI = uri;
|
|
1882
|
-
var UI;
|
|
1883
|
-
if (typeof window !== 'undefined' && window.panes && (UI = window.panes.UI) && UI.preferences && UI.preferences.get('offlineModeUsingLocalhost')) {
|
|
1884
|
-
if (requestedURI.slice(0, 7) === 'http://' && requestedURI.slice(7, 17) !== 'localhost/') {
|
|
1885
|
-
requestedURI = 'http://localhost/' + requestedURI.slice(7);
|
|
1886
|
-
_log.default.warn('Localhost kludge for offline use: actually getting <' + requestedURI + '>');
|
|
1887
|
-
} else {
|
|
1888
|
-
// log.warn("Localhost kludge NOT USED <" + requestedURI + ">")
|
|
1889
|
-
}
|
|
1890
|
-
} else {
|
|
1891
|
-
// log.warn("Localhost kludge OFF offline use: actually getting <" +
|
|
1892
|
-
// requestedURI + ">")
|
|
1893
|
-
}
|
|
1894
|
-
return requestedURI;
|
|
1895
|
-
}
|
|
1896
|
-
}, {
|
|
1897
|
-
key: "proxyIfNecessary",
|
|
1898
|
-
value: function proxyIfNecessary(uri) {
|
|
1899
|
-
var UI;
|
|
1900
|
-
if (typeof window !== 'undefined' && window.panes && (UI = window.panes.UI) && UI.isExtension) {
|
|
1901
|
-
return uri;
|
|
1902
|
-
} // Extension does not need proxy
|
|
1903
|
-
|
|
1904
|
-
if (typeof $SolidTestEnvironment !== 'undefined' && $SolidTestEnvironment.localSiteMap) {
|
|
1905
|
-
// nested dictionaries of URI parts from origin down
|
|
1906
|
-
var hostpath = uri.split('/').slice(2); // the bit after the //
|
|
1907
|
-
|
|
1908
|
-
var lookup = function lookup(parts, index) {
|
|
1909
|
-
var z = index[parts.shift()];
|
|
1910
|
-
if (!z) {
|
|
1911
|
-
return null;
|
|
1912
|
-
}
|
|
1913
|
-
if (typeof z === 'string') {
|
|
1914
|
-
return z + parts.join('/');
|
|
1915
|
-
}
|
|
1916
|
-
if (!parts) {
|
|
1917
|
-
return null;
|
|
1918
|
-
}
|
|
1919
|
-
return lookup(parts, z);
|
|
1920
|
-
};
|
|
1921
|
-
var y = lookup(hostpath, $SolidTestEnvironment.localSiteMap);
|
|
1922
|
-
if (y) {
|
|
1923
|
-
return y;
|
|
1924
|
-
}
|
|
1925
|
-
}
|
|
1611
|
+
let Handler = this.handlers.find(handler => {
|
|
1612
|
+
return contentType.match(handler.pattern);
|
|
1613
|
+
});
|
|
1926
1614
|
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1615
|
+
// @ts-ignore in practice all Handlers have constructors.
|
|
1616
|
+
return Handler ? new Handler(response) : null;
|
|
1617
|
+
}
|
|
1618
|
+
guessContentType(uri) {
|
|
1619
|
+
return CONTENT_TYPE_BY_EXT[uri.split('.').pop()];
|
|
1620
|
+
}
|
|
1621
|
+
normalizedContentType(options, headers) {
|
|
1622
|
+
if (options.forceContentType) {
|
|
1623
|
+
return options.forceContentType;
|
|
1624
|
+
}
|
|
1625
|
+
let contentType = headers.get('content-type');
|
|
1626
|
+
if (!contentType || contentType.includes('application/octet-stream')) {
|
|
1627
|
+
let guess = this.guessContentType(options.resource.value);
|
|
1628
|
+
if (guess) {
|
|
1629
|
+
return guess;
|
|
1935
1630
|
}
|
|
1936
|
-
return uri;
|
|
1937
1631
|
}
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
* @param uri
|
|
1942
|
-
*/
|
|
1943
|
-
}, {
|
|
1944
|
-
key: "unsupportedProtocol",
|
|
1945
|
-
value: function unsupportedProtocol(uri) {
|
|
1946
|
-
var pcol = Uri.protocol(uri);
|
|
1947
|
-
return pcol === 'tel' || pcol === 'mailto' || pcol === 'urn';
|
|
1632
|
+
let protocol = Uri.protocol(options.resource.value);
|
|
1633
|
+
if (!contentType && ['file', 'chrome'].includes(protocol)) {
|
|
1634
|
+
return 'text/xml';
|
|
1948
1635
|
}
|
|
1636
|
+
return contentType;
|
|
1637
|
+
}
|
|
1949
1638
|
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1639
|
+
/**
|
|
1640
|
+
* Sends a new request to the specified uri. (Extracted from `onerrorFactory()`)
|
|
1641
|
+
*/
|
|
1642
|
+
redirectToProxy(newURI, options) {
|
|
1643
|
+
this.addStatus(options.req, 'BLOCKED -> Cross-site Proxy to <' + newURI + '>');
|
|
1644
|
+
options.proxyUsed = true;
|
|
1645
|
+
const kb = this.store;
|
|
1646
|
+
const oldReq = options.req; // request metadata blank node
|
|
1647
|
+
|
|
1648
|
+
if (!options.noMeta) {
|
|
1649
|
+
kb.add(oldReq, this.ns.link('redirectedTo'), kb.rdfFactory.namedNode(newURI), oldReq);
|
|
1650
|
+
this.addStatus(oldReq, 'redirected to new request'); // why
|
|
1651
|
+
}
|
|
1652
|
+
this.requested[options.resource.value] = 'redirected';
|
|
1653
|
+
this.redirectedTo[options.resource.value] = newURI;
|
|
1654
|
+
let newOptions = Object.assign({}, options);
|
|
1655
|
+
newOptions.baseURI = options.resource.value;
|
|
1656
|
+
return this.fetchUri(newURI, newOptions).then(response => {
|
|
1657
|
+
if (!newOptions.noMeta) {
|
|
1658
|
+
kb.add(oldReq, this.ns.link('redirectedRequest'), newOptions.req, this.appNode);
|
|
1659
|
+
}
|
|
1660
|
+
return response;
|
|
1661
|
+
});
|
|
1662
|
+
}
|
|
1663
|
+
setRequestTimeout(uri, options) {
|
|
1664
|
+
return new Promise(resolve => {
|
|
1665
|
+
this.timeouts[uri] = (this.timeouts[uri] || []).concat(setTimeout(() => {
|
|
1666
|
+
if (this.isPending(uri) && !options.retriedWithNoCredentials && !options.proxyUsed) {
|
|
1667
|
+
resolve(this.failFetch(options, `Request to ${uri} timed out`, 'timeout'));
|
|
1971
1668
|
}
|
|
1669
|
+
}, this.timeout));
|
|
1670
|
+
});
|
|
1671
|
+
}
|
|
1672
|
+
addFetchCallback(uri, callback) {
|
|
1673
|
+
if (!this.fetchCallbacks[uri]) {
|
|
1674
|
+
this.fetchCallbacks[uri] = [callback];
|
|
1675
|
+
} else {
|
|
1676
|
+
this.fetchCallbacks[uri].push(callback);
|
|
1677
|
+
}
|
|
1678
|
+
}
|
|
1679
|
+
acceptString() {
|
|
1680
|
+
let acceptstring = '';
|
|
1681
|
+
for (let mediaType in this.mediatypes) {
|
|
1682
|
+
if (acceptstring !== '') {
|
|
1683
|
+
acceptstring += ', ';
|
|
1684
|
+
}
|
|
1685
|
+
acceptstring += mediaType;
|
|
1686
|
+
for (let property in this.mediatypes[mediaType]) {
|
|
1687
|
+
acceptstring += ';' + property + '=' + this.mediatypes[mediaType][property];
|
|
1972
1688
|
}
|
|
1973
1689
|
}
|
|
1974
|
-
|
|
1975
|
-
}
|
|
1690
|
+
return acceptstring;
|
|
1691
|
+
}
|
|
1692
|
+
// var updatesVia = new $rdf.UpdatesVia(this) // Subscribe to headers
|
|
1693
|
+
// @@@@@@@@ This is turned off because it causes a websocket to be set up for ANY fetch
|
|
1694
|
+
// whether we want to track it ot not. including ontologies loaed though the XSSproxy
|
|
1695
|
+
}
|
|
1696
|
+
exports.default = Fetcher;
|
|
1976
1697
|
(0, _defineProperty2.default)(Fetcher, "HANDLERS", void 0);
|
|
1977
1698
|
(0, _defineProperty2.default)(Fetcher, "CONTENT_TYPE_BY_EXT", void 0);
|
|
1978
1699
|
// TODO: Document this
|