!function(n){var e={};function t(r){if(e[r])return e[r].exports;var a=e[r]={i:r,l:!1,exports:{}};return n[r].call(a.exports,a,a.exports,t),a.l=!0,a.exports}t.m=n,t.c=e,t.d=function(n,e,r){t.o(n,e)||Object.defineProperty(n,e,{enumerable:!0,get:r})},t.r=function(n){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(n,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(n,"__esModule",{value:!0})},t.t=function(n,e){if(1&e&&(n=t(n)),8&e)return n;if(4&e&&"object"==typeof n&&n&&n.__esModule)return n;var r=Object.create(null);if(t.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:n}),2&e&&"string"!=typeof n)for(var a in n)t.d(r,a,function(e){return n[e]}.bind(null,a));return r},t.n=function(n){var e=n&&n.__esModule?function e(){return n.default}:function e(){return n};return t.d(e,"a",e),e},t.o=function(n,e){return Object.prototype.hasOwnProperty.call(n,e)},t.p="",t(t.s="./index.js")}({"./Errors.js": /*!*******************!*\ !*** ./Errors.js ***! \*******************/ /*! no static exports found */function(module,exports){eval('errors = {};\n\n// Use this when the code logic has been broken - e.g. something is called with an undefined parameter, its preferable to console.assert\n// Typically this is an error, that should have been caught higher up.\nclass CodingError extends Error {\n constructor(message) {\n super(message || "Coding Error");\n this.name = "CodingError"\n }\n}\nerrors.CodingError = CodingError;\n// These are equivalent of python exceptions, will log and raise alert in most cases - exceptions aren\'t caught\nclass ToBeImplementedError extends Error {\n constructor(message) {\n super("To be implemented: " + message);\n this.name = "ToBeImplementedError"\n }\n}\nerrors.ToBeImplementedError = ToBeImplementedError;\n\nclass TransportError extends Error {\n constructor(message) {\n super(message || "Transport failure");\n this.name = "TransportError"\n }\n}\nerrors.TransportError = TransportError;\n\nclass TimeoutError extends Error {\n constructor(message) {\n super(message || "Timed out");\n this.name = "TimeoutError"\n }\n}\nerrors.TimeoutError = TimeoutError;\n\nclass IntentionallyUnimplementedError extends Error {\n constructor(message) {\n super(message || "Intentionally Unimplemented Function");\n this.name = "IntentionallyUnimplementedError"\n }\n}\nerrors.IntentionallyUnimplementedError = IntentionallyUnimplementedError;\n\n\n/*---- Below here are errors copied from previous Dweb-Transport and not currently used */\n/*\nclass ObsoleteError extends Error {\n constructor(message) {\n super("Obsolete: " + message);\n this.name = "ObsoleteError"\n }\n}\nerrors.ObsoleteError = ObsoleteError;\n\n// Use this when the logic of encryption wont let you do something, typically something higher should have stopped you trying.\n// Examples include signing something when you only have a public key.\nclass EncryptionError extends Error {\n constructor(message) {\n super(message || "Encryption Error");\n this.name = "EncryptionError"\n }\n}\nerrors.EncryptionError = EncryptionError;\n\n// Use this something that should have been signed isn\'t - this is externally signed, i.e. a data rather than coding error\nclass SigningError extends Error {\n constructor(message) {\n super(message || "Signing Error");\n this.name = "SigningError"\n }\n}\nerrors.SigningError = SigningError;\n\nclass ForbiddenError extends Error {\n constructor(message) {\n super(message || "Forbidden failure");\n this.name = "ForbiddenError"\n }\n}\nerrors.ForbiddenError = ForbiddenError;\n\nclass AuthenticationError extends Error {\n constructor(message) {\n super(message || "Authentication failure");\n this.name = "AuthenticationError"\n }\n}\nerrors.AuthenticationError = AuthenticationError;\n\nclass DecryptionFailError extends Error {\n constructor(message) {\n super(message || "Decryption Failed");\n this.name = "DecryptionFailError"\n }\n}\nerrors.DecryptionFailError = DecryptionFailError;\n\nclass SecurityWarning extends Error {\n constructor(message) {\n super(message || "Security Warning");\n this.name = "SecurityWarning"\n }\n}\nerrors.SecurityWarning = SecurityWarning;\n\nclass ResolutionError extends Error {\n constructor(message) {\n super(message || "Resolution failure");\n this.name = "ResolutionError"\n }\n}\nerrors.ResolutionError = ResolutionError;\n*/\nexports = module.exports = errors;\n\n\n//# sourceURL=webpack:///./Errors.js?')},"./Transport.js": /*!**********************!*\ !*** ./Transport.js ***! \**********************/ /*! no static exports found */function(module,exports,__webpack_require__){eval('const Url = __webpack_require__(/*! url */ "./node_modules/url/url.js");\nconst stream = __webpack_require__(/*! readable-stream */ "./node_modules/readable-stream/readable-browser.js");\nconst errors = __webpack_require__(/*! ./Errors */ "./Errors.js"); // Standard Dweb Errors\n\nfunction delay(ms, val) { return new Promise(resolve => {setTimeout(() => { resolve(val); },ms)})}\n\n\nclass Transport {\n\n constructor(options, verbose) {\n /*\n Doesnt do anything, its all done by SuperClasses,\n Superclass should merge with default options, call super\n\n Fields:\n statuselement: If set is an HTML Element that should be adjusted to indicate status (this is managed by Transports, just stored on Transport)\n statuscb: Callback when status changes\n name: Short name of element e.g. HTTP IPFS WEBTORRENT GUN\n */\n }\n\n static setup0(options, verbose) {\n /*\n First part of setup, create obj, add to Transports but dont attempt to connect, typically called instead of p_setup if want to parallelize connections.\n */\n throw new errors.IntentionallyUnimplementedError("Intentionally undefined function Transport.setup0 should have been subclassed");\n }\n\n p_setup1(options, verbose) {\n /*\n Setup the resource and open any P2P connections etc required to be done just once. Asynchronous and should leave status=STATUS_STARTING until it resolves, or STATUS_FAILED if fails.\n\n cb (t)=>void If set, will be called back as status changes (so could be multiple times)\n Resolves to the Transport instance\n */\n return this;\n }\n p_setup2(options, verbose) {\n /*\n Works like p_setup1 but runs after p_setup1 has completed for all transports. This allows for example YJS to wait for IPFS to be connected in TransportIPFS.setup1() and then connect itself using the IPFS object.\n\n cb (t)=>void If set, will be called back as status changes (so could be multiple times)\n Resolves to the Transport instance\n */\n return this;\n }\n static async p_setup(options, verbose, cb) {\n /*\n A deprecated utility to simply setup0 then p_setup1 then p_setup2 to allow a transport to be started in one step, normally Transports.p_setup should be called instead.\n */\n let t = await this.setup0(options, verbose) // Sync version that doesnt connect\n .p_setup1(verbose, cb); // And connect\n\n return t.p_setup2(verbose, cb); // And connect\n }\n togglePaused(cb) {\n /*\n Switch the state of the transport between STATUS_CONNECTED and STATUS_PAUSED,\n in the paused state it will not be used for transport but, in some cases, will still do background tasks like serving files.\n\n cb(transport)=>void a callback called after this is run, may be used for example to change the UI\n */\n switch (this.status) {\n case Transport.STATUS_CONNECTED:\n this.status = Transport.STATUS_PAUSED;\n break;\n case Transport.STATUS_PAUSED:\n this.status = Transport.STATUS_CONNECTED; // Superclass might change to STATUS_STARTING if needs to stop/restart\n break;\n }\n if (cb) cb(this);\n }\n\n async p_status(verbose) {\n /*\n Check the status of the underlying transport. This may update the "status" field from the underlying transport.\n returns: a numeric code for the status of a transport.\n */\n return this.status;\n }\n\n supports(url, func) {\n /*\n Determine if this transport supports a certain set of URLs and a func\n\n :param url: String or parsed URL\n :return: true if this protocol supports these URLs and this func\n :throw: TransportError if invalid URL\n */\n if (typeof url === "string") {\n url = Url.parse(url); // For efficiency, only parse once.\n }\n if (url && !url.protocol) {\n throw new Error("URL failed to specific a scheme (before :) " + url.href)\n } //Should be TransportError but out of scope here\n // noinspection Annotator supportURLs is defined in subclasses\n return ( (!url || this.supportURLs.includes(url.protocol.slice(0, -1)))\n && (!func || this.supportFunctions.includes(func)))\n }\n\n p_rawstore(data, opts) {\n /*\n Store a blob of data onto the decentralised transport.\n Returns a promise that resolves to the url of the data\n\n :param string|Buffer data: Data to store - no assumptions made to size or content\n :param boolean verbose: true for debugging output\n :resolve string: url of data stored\n */\n throw new errors.ToBeImplementedError("Intentionally undefined function Transport.p_rawstore should have been subclassed");\n }\n\n async p_rawstoreCaught(data, {verbose}) {\n try {\n return await this.p_rawstore(data, {verbose});\n } catch (err) {\n\n }\n }\n p_store() {\n throw new errors.ToBeImplementedError("Undefined function Transport.p_store - may define higher level semantics here (see Python)");\n }\n\n //noinspection JSUnusedLocalSymbols\n\n p_rawfetch(url, {timeoutMS=undefined, start=undefined, end=undefined, relay=false, verbose=false}={}) {\n /*\n Fetch some bytes based on a url, no assumption is made about the data in terms of size or structure.\n Where required by the underlying transport it should retrieve a number if its "blocks" and concatenate them.\n Returns a new Promise that resolves currently to a string.\n There may also be need for a streaming version of this call, at this point undefined.\n\n :param string url: URL of object being retrieved\n :param verbose: true for debugging output\n :param timeoutMS Max time to wait on transports that support it (IPFS for fetch)\n :param start,end Inclusive byte range wanted (must be supported, uses a "slice" on output if transport ignores it.\n :param relay If first transport fails, try and retrieve on 2nd, then store on 1st, and so on.\n\n :resolve string: Return the object being fetched, (note currently returned as a string, may refactor to return Buffer)\n :throws: TransportError if url invalid - note this happens immediately, not as a catch in the promise\n */\n console.assert(false, "Intentionally undefined function Transport.p_rawfetch should have been subclassed");\n }\n\n p_fetch() {\n throw new errors.ToBeImplementedError("Undefined function Transport.p_fetch - may define higher level semantics here (see Python)");\n }\n\n p_rawadd(url, sig, {verbose=false}={}) {\n /*\n Store a new list item, ideally it should be stored so that it can be retrieved either by "signedby" (using p_rawlist) or\n by "url" (with p_rawreverse). The underlying transport does not need to guarantee the signature,\n an invalid item on a list should be rejected on higher layers.\n\n :param string url: String identifying an object being added to the list.\n :param Signature sig: A signature data structure.\n :param boolean verbose: true for debugging output\n :resolve undefined:\n */\n throw new errors.ToBeImplementedError("Undefined function Transport.p_rawadd");\n }\n\n p_rawlist(url, {verbose=false}={}) {\n /*\n Fetch all the objects in a list, these are identified by the url of the public key used for signing.\n (Note this is the \'signedby\' parameter of the p_rawadd call, not the \'url\' parameter\n Returns a promise that resolves to the list.\n Each item of the list is a dict: {"url": url, "date": date, "signature": signature, "signedby": signedby}\n List items may have other data (e.g. reference ids of underlying transport)\n\n :param string url: String with the url that identifies the list.\n :param boolean verbose: true for debugging output\n :resolve array: An array of objects as stored on the list.\n */\n throw new errors.ToBeImplementedError("Undefined function Transport.p_rawlist");\n }\n\n p_list() {\n throw new Error("Undefined function Transport.p_list");\n }\n p_newlisturls(cl, {verbose=false}={}) {\n /*\n Must be implemented by any list, return a pair of URLS that may be the same, private and public links to the list.\n returns: ( privateurl, publicurl) e.g. yjs:xyz/abc or orbitdb:a123\n */\n throw new Error("undefined function Transport.p_newlisturls");\n }\n\n //noinspection JSUnusedGlobalSymbols\n p_rawreverse(url, {verbose=false}={}) {\n /*\n Similar to p_rawlist, but return the list item of all the places where the object url has been listed.\n The url here corresponds to the "url" parameter of p_rawadd\n Returns a promise that resolves to the list.\n\n :param string url: String with the url that identifies the object put on a list.\n :param boolean verbose: true for debugging output\n :resolve array: An array of objects as stored on the list.\n */\n throw new errors.ToBeImplementedError("Undefined function Transport.p_rawreverse");\n }\n\n listmonitor(url, callback, {verbose=false, current=false}={}) {\n /*\n Setup a callback called whenever an item is added to a list, typically it would be called immediately after a p_rawlist to get any more items not returned by p_rawlist.\n\n :param url: string Identifier of list (as used by p_rawlist and "signedby" parameter of p_rawadd\n :param callback: function(obj) Callback for each new item added to the list\n \tobj is same format as p_rawlist or p_rawreverse\n :param verbose: boolean - true for debugging output\n */\n console.log("Undefined function Transport.listmonitor"); // Note intentionally a log, as legitamte to not implement it\n }\n\n\n // ==== TO SUPPORT KEY VALUE INTERFACES IMPLEMENT THESE =====\n // Support for Key-Value pairs as per\n // https://docs.google.com/document/d/1yfmLRqKPxKwB939wIy9sSaa7GKOzM5PrCZ4W1jRGW6M/edit#\n\n async p_newdatabase(pubkey, {verbose=false}={}) {\n /*\n Create a new database based on some existing object\n pubkey: Something that is, or has a pubkey, by default support Dweb.PublicPrivate, KeyPair or an array of strings as in the output of keypair.publicexport()\n returns: {publicurl, privateurl} which may be the same if there is no write authentication\n */\n throw new errors.ToBeImplementedError("Undefined function Transport.p_newdatabase");\n }\n //TODO maybe change the listmonitor / monitor code for to use "on" and the structure of PP.events\n //TODO but note https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy about Proxy which might be suitable, prob not as doesnt map well to lists\n async p_newtable(pubkey, table, {verbose=false}={}) {\n /*\n Create a new table,\n pubkey: Is or has a pubkey (see p_newdatabase)\n table: String representing the table - unique to the database\n returns: {privateurl, publicurl} which may be the same if there is no write authentication\n */\n throw new errors.ToBeImplementedError("Undefined function Transport.p_newtable");\n }\n\n async p_set(url, keyvalues, value, {verbose=false}={}) { // url = yjs:/yjs/database/table/key\n /*\n Set one or more keys in a table.\n url: URL of the table\n keyvalues: String representing a single key OR dictionary of keys\n value: String or other object to be stored (its not defined yet what objects should be supported, e.g. any object ?\n */\n throw new errors.ToBeImplementedError("Undefined function Transport.p_set");\n }\n async p_get(url, keys, {verbose=false}={}) {\n /* Get one or more keys from a table\n url: URL of the table\n keys: Array of keys\n returns: Dictionary of values found (undefined if not found)\n */\n throw new errors.ToBeImplementedError("Undefined function Transport.p_get");\n }\n\n async p_delete(url, keys, {verbose=false}={}) {\n /* Delete one or more keys from a table\n url: URL of the table\n keys: Array of keys\n */\n throw new errors.ToBeImplementedError("Undefined function Transport.p_delete");\n }\n\n async p_keys(url, {verbose=false}={}) {\n /* Return a list of keys in a table (suitable for iterating through)\n url: URL of the table\n returns: Array of strings\n */\n throw new errors.ToBeImplementedError("Undefined function Transport.p_keys");\n }\n async p_getall(url, {verbose=false}={}) {\n /* Return a dictionary representing the table\n url: URL of the table\n returns: Dictionary of Key:Value pairs, note take care if this could be large.\n */\n throw new errors.ToBeImplementedError("Undefined function Transport.p_keys");\n }\n static async p_f_createReadStream(url, {wanturl=false, verbose=false}) {\n /*\n Provide a function of the form needed by tag and renderMedia library etc\n\n url Urls of stream\n wanturl True if want the URL of the stream (for service workers)\n returns f(opts) => stream returning bytes from opts.start || start of file to opts.end-1 || end of file\n */\n }\n // ------ UTILITY FUNCTIONS, NOT REQD TO BE SUBCLASSED ----\n\n static mergeoptions(a) {\n /*\n Deep merge options dictionaries\n */\n let c = {};\n for (let i = 0; i < arguments.length; i++) {\n let b = arguments[i];\n for (let key in b) {\n let val = b[key];\n if ((typeof val === "object") && !Array.isArray(val) && c[key]) {\n c[key] = Transport.mergeoptions(a[key], b[key]);\n } else {\n c[key] = b[key];\n }\n }\n }\n return c;\n }\n\n async p_test_list({urlexpectedsubstring=undefined, verbose=false}={}) {\n //TODO - this test doesn\'t work since we dont have Signature nor want to create dependency on it - when works, add to GUN & YJS\n if (verbose) {console.log(this.name,"p_test_kvt")}\n try {\n let table = await this.p_newlisturls("NACL VERIFY:1234567LIST", {verbose});\n let mapurl = table.publicurl;\n if (verbose) console.log("newlisturls=",mapurl);\n console.assert((!urlexpectedsubstring) || mapurl.includes(urlexpectedsubstring));\n await this.p_rawadd(mapurl, "testvalue", {verbose});\n let res = await this.p_rawlist(mapurl, {verbose});\n console.assert(res.length===1 && res[0] === "testvalue");\n await this.p_rawadd(mapurl, {foo: "bar"}, {verbose}); // Try adding an object\n res = await this.p_rawlist(mapurl, {verbose});\n console.assert(res.length === 2 && res[1].foo === "bar");\n await this.p_rawadd(mapurl, [1,2,3], {verbose}); // Try setting to an array\n res = await this.p_rawlist(mapurl, {verbose});\n console.assert(res.length === 2 && res[2].length === 3 && res[2][1] === 2);\n await delay(200);\n if (verbose) console.log(this.name, "p_test_list complete")\n } catch(err) {\n console.log("Exception thrown in ", this.name, "p_test_list:", err.message);\n throw err;\n }\n\n }\n async p_test_kvt(urlexpectedsubstring, verbose=false) {\n /*\n Test the KeyValue functionality of any transport that supports it.\n urlexpectedsubstring: Some string expected in the publicurl of the table.\n */\n if (verbose) {console.log(this.name,"p_test_kvt")}\n try {\n let table = await this.p_newtable("NACL VERIFY:1234567KVT","mytable", {verbose});\n let mapurl = table.publicurl;\n if (verbose) console.log("newtable=",mapurl);\n console.assert(mapurl.includes(urlexpectedsubstring));\n await this.p_set(mapurl, "testkey", "testvalue", {verbose});\n let res = await this.p_get(mapurl, "testkey", {verbose});\n console.assert(res === "testvalue");\n await this.p_set(mapurl, "testkey2", {foo: "bar"}, {verbose}); // Try setting to an object\n res = await this.p_get(mapurl, "testkey2", {verbose});\n console.assert(res.foo === "bar");\n await this.p_set(mapurl, "testkey3", [1,2,3], {verbose}); // Try setting to an array\n res = await this.p_get(mapurl, "testkey3", {verbose});\n console.assert(res[1] === 2);\n res = await this.p_keys(mapurl, {verbose});\n console.assert(res.includes("testkey") && res.includes("testkey3") && res.length === 3);\n await this.p_delete(mapurl, ["testkey"], {verbose});\n res = await this.p_getall(mapurl, {verbose});\n if (verbose) console.log("getall=>",res);\n console.assert(res.testkey2.foo === "bar" && res.testkey3["1"] === 2 && !res.testkey);\n await delay(200);\n if (verbose) console.log(this.name, "p_test_kvt complete")\n } catch(err) {\n console.log("Exception thrown in ", this.name, "p_test_kvt:", err.message);\n throw err;\n }\n }\n\n\n}\nTransport.STATUS_CONNECTED = 0; // Connected - all other numbers are some version of not ok to use\nTransport.STATUS_FAILED = 1; // Failed to connect\nTransport.STATUS_STARTING = 2; // In the process of connecting\nTransport.STATUS_LOADED = 3; // Code loaded, but haven\'t tried to connect. (this is typically hard coded in subclasses constructor)\nTransport.STATUS_PAUSED = 4; // It was launched, probably connected, but now paused so will be ignored by validFor // Note this is copied to dweb-archive/Nav.js so check if change\nexports = module.exports = Transport;\n\n\n//# sourceURL=webpack:///./Transport.js?')},"./TransportGUN.js": /*!*************************!*\ !*** ./TransportGUN.js ***! \*************************/ /*! no static exports found */function(module,exports,__webpack_require__){eval('/* WEBPACK VAR INJECTION */(function(process) {/*\nThis Transport layers uses GUN.\n*/\nconst Url = __webpack_require__(/*! url */ "./node_modules/url/url.js");\nprocess.env.GUN_ENV = "false";\nconst Gun = __webpack_require__(/*! gun/gun.js */ "./node_modules/gun/gun.js"); // TODO-GUN switchback to gun/gun at some point to get minimized version\n__webpack_require__(/*! gun/lib/path.js */ "./node_modules/gun/lib/path.js");\n\n// Other Dweb modules\nconst errors = __webpack_require__(/*! ./Errors */ "./Errors.js"); // Standard Dweb Errors\nconst Transport = __webpack_require__(/*! ./Transport.js */ "./Transport.js"); // Base class for TransportXyz\nconst Transports = __webpack_require__(/*! ./Transports */ "./Transports.js"); // Manage all Transports that are loaded\nconst utils = __webpack_require__(/*! ./utils */ "./utils.js"); // Utility functions\n\n// Utility packages (ours) And one-liners\n//unused currently: function delay(ms, val) { return new Promise(resolve => {setTimeout(() => { resolve(val); },ms)})}\n\nlet defaultoptions = {\n peers: [ "https://dweb.me:4246/gun" ] // TODO-GUN get server setup and then replace this URL\n //localstore: true #True is default\n};\n//To run a superpeer - cd wherever; node install gun; cd node_modules/gun; npm start - starts server by default on port 8080, or set an "env" - see http.js\n//setenv GUN_ENV false; node examples/http.js 4246\n//Make sure to open of the port (typically in /etc/ferm)\n//TODO-GUN - figure out how to make server persistent - started by systemctl etc and incorporate in dweb-gateway/scripts/install.sh\n\n/*\n WORKING AROUND GUN WEIRNESS/SUBOPTIMAL\n\n .once() and possibly .on() send an extra GUN internal field "_" which needs filtering. Reported and hopefully will get fixed, for now see other TODO-GUN-UNDERSCORE for the workaround\n .once() and .on() deliver existing values as well as changes, reported & hopefully will get way to find just new ones. for now, see other TODO-GUN-CURRENT for the workaround\n There is no way to delete an item, setting it to null is recorded and is by convention a deletion. BUT the field will still show up in .once and .on, see other TODO-GUN-DELETE for the workaround\n GUN is not promisified, there is only one place we care, and that is .once (since .on is called multiple times). See TODO-GUN-PROMISES for workaround\n */\n\nclass TransportGUN extends Transport {\n /*\n GUN specific transport - over IPFS\n\n Fields:\n gun: object returned when starting GUN\n */\n\n constructor(options, verbose) {\n super(options, verbose);\n this.options = options; // Dictionary of options\n this.gun = undefined;\n this.name = "GUN"; // For console log etc\n this.supportURLs = [\'gun\'];\n this.supportFunctions = [\'connection\', \'get\', \'set\', \'getall\', \'keys\', \'newdatabase\', \'newtable\', \'monitor\',\n \'add\', \'list\', \'listmonitor\', \'newlisturls\']\n this.status = Transport.STATUS_LOADED;\n }\n\n connection(url, verbose) {\n /*\n Utility function to get Gun object for this URL (note this isn\'t async)\n url: URL string to find list of\n resolves: Gun a connection to use for get\'s etc, undefined if fails\n */\n if (typeof url === "string")\n url = Url.parse(url);\n let patharray = url.pathname.split(\'/\'); //[ \'gun\', database, table ]\n patharray.shift(); // Loose leading ""\n patharray.shift(); // Loose "gun"\n if (verbose) console.log("Path=", patharray);\n return this.gun.path(patharray); // Not sure how this could become undefined as it will return g before the path is walked, but if do a lookup on this "g" then should get undefined\n }\n\n static setup0(options, verbose) {\n /*\n First part of setup, create obj, add to Transports but dont attempt to connect, typically called instead of p_setup if want to parallelize connections.\n options: { gun: { }, } Set of options - "gun" is used for those to pass direct to Gun\n */\n let combinedoptions = Transport.mergeoptions(defaultoptions, options.gun);\n console.log("GUN options %o", combinedoptions); // Log even if !verbose\n let t = new TransportGUN(combinedoptions, verbose); // Note doesnt start IPFS or OrbitDB\n t.gun = new Gun(t.options); // This doesnt connect, just creates db structure\n Transports.addtransport(t);\n return t;\n }\n\n async p_setup1(verbose, cb) {\n /*\n This sets up for GUN.\n Throws: TODO-GUN-DOC document possible error behavior\n */\n try {\n this.status = Transport.STATUS_STARTING; // Should display, but probably not refreshed in most case\n if (cb) cb(this);\n //TODO-GUN-TEST - try connect and retrieve info then look at ._.opt.peers\n await this.p_status(verbose);\n } catch(err) {\n console.error(this.name,"failed to start",err);\n this.status = Transport.STATUS_FAILED;\n }\n if (cb) cb(this);\n return this;\n }\n\n async p_status(verbose) {\n /*\n Return an integer for the status of a transport see Transport\n */\n //TODO-GUN-TEST - try connect and retrieve info then look at ._.opt.peers\n this.status = Transport.STATUS_CONNECTED; //TODO-GUN how do I know if/when I\'m connected (see comment on p_setup1 as well)\n return this.status;\n }\n // ===== LISTS ========\n\n // noinspection JSCheckFunctionSignatures\n async p_rawlist(url, {verbose=false}={}) {\n /*\n Fetch all the objects in a list, these are identified by the url of the public key used for signing.\n (Note this is the \'signedby\' parameter of the p_rawadd call, not the \'url\' parameter\n Returns a promise that resolves to the list.\n Each item of the list is a dict: {"url": url, "date": date, "signature": signature, "signedby": signedby}\n List items may have other data (e.g. reference ids of underlying transport)\n\n :param string url: String with the url that identifies the list.\n :param boolean verbose: true for debugging output\n :resolve array: An array of objects as stored on the list.\n */\n try {\n let g = this.connection(url, verbose);\n let data = await this._p_once(g);\n let res = data ? Object.keys(data).filter(k => k !== \'_\').sort().map(k => data[k]) : []; //See TODO-GUN-UNDERSCORE\n // .filter((obj) => (obj.signedby.includes(url))); // upper layers verify, which filters\n if (verbose) console.log("GUN.p_rawlist found", ...utils.consolearr(res));\n return res;\n } catch(err) {\n console.log("TransportGUN.p_rawlist failed",err.message);\n throw(err);\n }\n }\n\n listmonitor(url, callback, {verbose=false, current=false}={}) {\n /*\n Setup a callback called whenever an item is added to a list, typically it would be called immediately after a p_rawlist to get any more items not returned by p_rawlist.\n\n url: string Identifier of list (as used by p_rawlist and "signedby" parameter of p_rawadd\n callback: function(obj) Callback for each new item added to the list\n obj is same format as p_rawlist or p_rawreverse\n current true if should send list of existing elements\n verbose: true for debugging output\n */\n let g = this.connection(url, verbose);\n if (!current) { // See TODO-GUN-CURRENT have to keep an extra copy to compare for which calls are new.\n g.once(data => {\n this.monitored = Object.keys(data) || []; // Keep a copy - could actually just keep high water mark unless getting partial knowledge of state of array.\n g.map().on((v, k) => {\n if (!(this.monitored.includes(k)) && (k !== \'_\')) { //See TODO-GUN-UNDERSCORE\n this.monitored.push(k)\n callback(JSON.parse(v));\n }\n });\n });\n } else {\n g.map().on((v, k) => callback("set", k, JSON.parse(v)));\n }\n }\n\n // noinspection JSCheckFunctionSignatures\n async p_rawadd(url, sig, {verbose=false}={}) {\n /*\n Store a new list item, it should be stored so that it can be retrieved either by "signedby" (using p_rawlist) or\n by "url" (with p_rawreverse). The underlying transport does not need to guarantee the signature,\n an invalid item on a list should be rejected on higher layers.\n\n :param string url: String identifying list to post to\n :param Signature sig: Signature object containing at least:\n date - date of signing in ISO format,\n urls - array of urls for the object being signed\n signature - verifiable signature of date+urls\n signedby - urls of public key used for the signature\n :param boolean verbose: true for debugging output\n :resolve undefined:\n */\n // noinspection JSUnresolvedVariable\n console.assert(url && sig.urls.length && sig.signature && sig.signedby.length, "TransportGUN.p_rawadd args", url, sig);\n if (verbose) console.log("TransportGUN.p_rawadd", typeof url === "string" ? url : url.href, sig);\n this.connection(url, verbose)\n .set( JSON.stringify( sig.preflight( Object.assign({}, sig))));\n }\n\n // noinspection JSCheckFunctionSignatures\n async p_newlisturls(cl, {verbose=false}={}) {\n let u = await this._p_newgun(cl, {verbose});\n return [ u, u];\n }\n\n //=======KEY VALUE TABLES ========\n\n // noinspection JSMethodCanBeStatic\n async _p_newgun(pubkey, {verbose=false}={}) {\n if (pubkey.hasOwnProperty("keypair"))\n pubkey = pubkey.keypair.signingexport();\n // By this point pubkey should be an export of a public key of form xyz:abc where xyz\n // specifies the type of public key (NACL VERIFY being the only kind we expect currently)\n return `gun:/gun/${encodeURIComponent(pubkey)}`;\n }\n async p_newdatabase(pubkey, {verbose=false}={}) {\n /*\n Request a new database\n For GUN it doesnt actually create anything, just generates the URLs\n TODO-GUN simple version first - userid based on my keypair first, then switch to Gun\'s userid and its keypair\n Include gun/sea.js; user.create(,); user.auth(,); # See gun.eco/docs/Auth\n\n returns: {publicurl: "gun:/gun/", privateurl: "gun:/gun/">\n */\n let u = await this._p_newgun(pubkey, {verbose});\n return {publicurl: u, privateurl: u};\n }\n\n async p_newtable(pubkey, table, {verbose=false}={}) {\n /*\n Request a new table\n For GUN it doesnt actually create anything, just generates the URLs\n\n returns: {publicurl: "gun:/gun//", privateurl: "gun:/gun//
">\n */\n if (!pubkey) throw new errors.CodingError("p_newtable currently requires a pubkey");\n let database = await this.p_newdatabase(pubkey, {verbose});\n // If have use cases without a database, then call p_newdatabase first\n return { privateurl: `${database.privateurl}/${table}`, publicurl: `${database.publicurl}/${table}`} // No action required to create it\n }\n\n async p_set(url, keyvalues, value, {verbose=false}={}) { // url = yjs:/yjs/database/table\n /*\n Set key values\n keyvalues: string (key) in which case value should be set there OR\n object in which case value is ignored\n */\n let table = this.connection(url, verbose);\n if (typeof keyvalues === "string") {\n table.path(keyvalues).put(JSON.stringify(value));\n } else {\n // Store all key-value pairs without destroying any other key/value pairs previously set\n console.assert(!Array.isArray(keyvalues), "TransportGUN - shouldnt be passsing an array as the keyvalues");\n table.put(\n Object.keys(keyvalues).reduce(\n function(previous, key) { previous[key] = JSON.stringify(keyvalues[key]); return previous; },\n {}\n ))\n }\n }\n\n async p_get(url, keys, {verbose=false}={}) {\n let table = this.connection(url, verbose);\n if (Array.isArray(keys)) {\n throw new errors.ToBeImplementedError("p_get(url, [keys]) isn\'t supported - because of ambiguity better to explicitly loop on set of keys or use getall and filter");\n /*\n return keys.reduce(function(previous, key) {\n let val = table.get(key);\n previous[key] = typeof val === "string" ? JSON.parse(val) : val; // Handle undefined\n return previous;\n }, {});\n */\n } else {\n let val = await this._p_once(table.get(keys)); // Resolves to value\n return typeof val === "string" ? JSON.parse(val) : val; // This looks like it is sync\n }\n }\n\n async p_delete(url, keys, {verbose=false}={}) {\n let table = this.connection(url, verbose);\n if (typeof keys === "string") {\n table.path(keys).put(null);\n } else {\n keys.map((key) => table.path(key).put(null)); // This looks like it is sync\n }\n }\n\n //TODO-GUN-PROMISE suggest p_once as a good single addition\n _p_once(gun) { // Npte in some cases (e.g. p_getall) this will resolve to a object, others a string/number (p_get)\n return new Promise((resolve, reject) => gun.once(resolve));\n }\n\n async p_keys(url, {verbose=false}={}) {\n let res = await this._p_once(this.connection(url, verbose));\n return Object.keys(res)\n .filter(k=> (k !== \'_\') && (res[k] !== null)); //See TODO-GUN-UNDERSCORE and TODO-GUN-DELETE\n }\n\n async p_getall(url, {verbose=false}={}) {\n let res = await this._p_once(this.connection(url, verbose));\n return Object.keys(res)\n .filter(k=> (k !== \'_\') && res[k] !== null) //See TODO-GUN-UNDERSCORE and TODO-GUN-DELETE\n .reduce( function(previous, key) { previous[key] = JSON.parse(res[key]); return previous; }, {});\n }\n\n async monitor(url, callback, {verbose=false, current=false}={}) {\n /*\n Setup a callback called whenever an item is added to a list, typically it would be called immediately after a p_getall to get any more items not returned by p_getall.\n Stack: KVT()|KVT.p_new => KVT.monitor => (a: Transports.monitor => GUN.monitor)(b: dispatchEvent)\n\n url: string Identifier of list (as used by p_rawlist and "signedby" parameter of p_rawadd\n callback: function({type, key, value}) Callback for each new item added to the list (type = "set"|"delete")\n\n verbose: boolean - true for debugging output\n current Send existing items to the callback as well\n */\n let g = this.connection(url, verbose);\n if (!current) { // See TODO-GUN-CURRENT have to keep an extra copy to compare for which calls are new.\n g.once(data => {\n this.monitored = Object.assign({},data); // Make a copy of data (this.monitored = data won\'t work as just points at same structure)\n g.map().on((v, k) => {\n if ((v !== this.monitored[k]) && (k !== \'_\')) { //See TODO-GUN-UNDERSCORE\n this.monitored[k] = v;\n callback("set", k, JSON.parse(v));\n }\n });\n });\n } else {\n g.map().on((v, k) => callback("set", k, JSON.parse(v)));\n }\n }\n\n static async p_test(verbose) {\n if (verbose) {console.log("TransportGUN.test")}\n try {\n let t = this.setup0({}, verbose); //TODO-GUN when works with peers commented out, try passing peers: []\n await t.p_setup1(verbose); // Not passing cb yet\n await t.p_setup2(verbose); // Not passing cb yet - this one does nothing on GUN\n // noinspection JSIgnoredPromiseFromCall\n t.p_test_kvt("gun:/gun/NACL", {verbose});\n //t.p_test_list("gun:/gun/NACL", {verbose}); //TODO test_list needs fixing to not create a dependency on Signature\n } catch(err) {\n console.log("Exception thrown in TransportGUN.test:", err.message);\n throw err;\n }\n }\n\n static async demo_bugs() {\n let gun = new Gun();\n gun.get(\'foo\').get(\'bar\').put(\'baz\');\n console.log("Expect {bar: \'baz\'} but get {_:..., bar: \'baz\'}");\n gun.get(\'foo\').once(data => console.log(data));\n gun.get(\'zip\').get(\'bar\').set(\'alice\');\n console.log("Expect {12345: \'alice\'} but get {_:..., 12345: \'alice\'}");\n gun.get(\'foo\').once(data => console.log(data));\n // Returns extra "_" field\n }\n}\nTransports._transportclasses["GUN"] = TransportGUN;\nexports = module.exports = TransportGUN;\n\n/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./node_modules/process/browser.js */ "./node_modules/process/browser.js")))\n\n//# sourceURL=webpack:///./TransportGUN.js?')},"./TransportHTTP.js": /*!**************************!*\ !*** ./TransportHTTP.js ***! \**************************/ /*! no static exports found */function(module,exports,__webpack_require__){eval('const Transport = __webpack_require__(/*! ./Transport */ "./Transport.js"); // Base class for TransportXyz\nconst Transports = __webpack_require__(/*! ./Transports */ "./Transports.js"); // Manage all Transports that are loaded\nconst httptools = __webpack_require__(/*! ./httptools */ "./httptools.js"); // Expose some of the httptools so that IPFS can use it as a backup\nconst Url = __webpack_require__(/*! url */ "./node_modules/url/url.js");\n\n\ndefaulthttpoptions = {\n urlbase: \'https://dweb.me:443\'\n};\n\nservercommands = { // What the server wants to see to return each of these\n rawfetch: "contenthash", // was content/rawfetch which should still work.\n rawstore: "contenturl/rawstore",\n rawadd: "void/rawadd",\n rawlist: "metadata/rawlist",\n get: "get/table",\n set: "set/table",\n delete: "delete/table",\n keys: "keys/table",\n getall: "getall/table"\n};\n\n\nclass TransportHTTP extends Transport {\n\n constructor(options, verbose) {\n super(options, verbose);\n this.options = options;\n this.urlbase = options.http.urlbase;\n this.supportURLs = [\'contenthash\', \'http\',\'https\'];\n this.supportFunctions = [\'fetch\', \'store\', \'add\', \'list\', \'reverse\', \'newlisturls\', "get", "set", "keys", "getall", "delete", "newtable", "newdatabase"]; //Does not support: listmonitor - reverse is disabled somewhere not sure if here or caller\n this.supportFeatures = [\'fetch.range\']\n this.name = "HTTP"; // For console log etc\n this.status = Transport.STATUS_LOADED;\n }\n\n static setup0(options, verbose) {\n let combinedoptions = Transport.mergeoptions({ http: defaulthttpoptions },options);\n try {\n let t = new TransportHTTP(combinedoptions, verbose);\n Transports.addtransport(t);\n return t;\n } catch (err) {\n console.log("Exception thrown in TransportHTTP.p_setup", err.message);\n throw err;\n }\n }\n async p_setup1(verbose, cb) {\n this.status = Transport.STATUS_STARTING;\n if (cb) cb(this);\n await this.p_status(verbose);\n if (cb) cb(this);\n return this;\n }\n\n async p_status(verbose) {\n /*\n Return a numeric code for the status of a transport.\n */\n try {\n this.info = await this.p_info(verbose);\n this.status = Transport.STATUS_CONNECTED;\n } catch(err) {\n console.log(this.name, ": Error in p_status.info",err.message);\n this.status = Transport.STATUS_FAILED;\n }\n return super.p_status(verbose);\n }\n\n _cmdurl(command) {\n return `${this.urlbase}/${command}`\n }\n _url(url, command, parmstr) {\n if (!url) throw new errors.CodingError(`${command}: requires url`);\n if (typeof url !== "string") { url = url.href }\n url = url.replace(\'contenthash:/contenthash\', this._cmdurl(command)) ; // Note leaves http: and https: urls unchanged\n url = url.replace(\'getall/table\', command);\n url = url + (parmstr ? "?"+parmstr : "");\n return url;\n }\n async p_rawfetch(url, opts={}) {\n /*\n Fetch from underlying transport,\n Fetch is used both for contenthash requests and table as when passed to SmartDict.p_fetch may not know what we have\n url: Of resource - which is turned into the HTTP url in p_httpfetch\n opts: {start, end, verbose} see p_GET for documentation\n throws: TransportError if fails\n */\n //if (!(url && url.includes(\':\') ))\n // throw new errors.CodingError("TransportHTTP.p_rawfetch bad url: "+url);\n if (url.href.includes(\'contenthash//\'))\n console.error("XXX@91",url)\n if (((typeof url === "string") ? url : url.href).includes(\'/getall/table\')) {\n throw new Error("Probably dont want to be calling p_rawfetch on a KeyValueTable, especially since dont know if its keyvaluetable or subclass"); //TODO-NAMING\n return { // I\'m not sure what this return would have done - looks half finished to me?\n table: "keyvaluetable",\n }\n } else {\n return await httptools.p_GET(this._url(url, servercommands.rawfetch), opts);\n }\n }\n\n p_rawlist(url, {verbose=false}={}) {\n // obj being loaded\n // Locate and return a block, based on its url\n if (!url) throw new errors.CodingError("TransportHTTP.p_rawlist: requires url");\n return httptools.p_GET(this._url(url, servercommands.rawlist), {verbose});\n }\n rawreverse() { throw new errors.ToBeImplementedError("Undefined function TransportHTTP.rawreverse"); }\n\n async p_rawstore(data, {verbose=false}={}) {\n /*\n Store data on http server,\n data: string\n resolves to: {string}: url\n throws: TransportError on failure in p_POST > p_httpfetch\n */\n //PY: res = self._sendGetPost(True, "rawstore", headers={"Content-Type": "application/octet-stream"}, urlargs=[], data=data, verbose=verbose)\n console.assert(data, "TransportHttp.p_rawstore: requires data");\n let res = await httptools.p_POST(this._cmdurl(servercommands.rawstore), "application/octet-stream", data, verbose); // resolves to URL\n let parsedurl = Url.parse(res);\n let pathparts = parsedurl.pathname.split(\'/\');\n return `contenthash:/contenthash/${pathparts.slice(-1)}`\n\n }\n\n p_rawadd(url, sig, {verbose=false}={}) {\n //verbose=true;\n if (!url || !sig) throw new errors.CodingError("TransportHTTP.p_rawadd: invalid parms",url, sig);\n if (verbose) console.log("rawadd", url, sig);\n let value = JSON.stringify(sig.preflight(Object.assign({},sig)))+"\\n";\n return httptools.p_POST(this._url(url, servercommands.rawadd), "application/json", value, verbose); // Returns immediately\n }\n\n p_newlisturls(cl, {verbose=false}={}) {\n let u = cl._publicurls.map(urlstr => Url.parse(urlstr))\n .find(parsedurl =>\n ((parsedurl.protocol === "https:" && ["gateway.dweb.me", "dweb.me"].includes(parsedurl.host)\n && (parsedurl.pathname.includes(\'/content/rawfetch\') || parsedurl.pathname.includes(\'/contenthash/\')))\n || (parsedurl.protocol === "contenthash:") && (parsedurl.pathname.split(\'/\')[1] === "contenthash")));\n if (!u) {\n u = `contenthash:/contenthash/${ cl.keypair.verifyexportmultihashsha256_58() }`; // Pretty random, but means same test will generate same list and server is expecting base58 of a hash\n }\n return [u,u];\n }\n\n // ============================== Key Value support\n\n\n // Support for Key-Value pairs as per\n // https://docs.google.com/document/d/1yfmLRqKPxKwB939wIy9sSaa7GKOzM5PrCZ4W1jRGW6M/edit#\n async p_newdatabase(pubkey, {verbose=false}={}) {\n //if (pubkey instanceof Dweb.PublicPrivate)\n if (pubkey.hasOwnProperty("keypair"))\n pubkey = pubkey.keypair.signingexport()\n // By this point pubkey should be an export of a public key of form xyz:abc where xyz\n // specifies the type of public key (NACL VERIFY being the only kind we expect currently)\n let u = `${this.urlbase}/getall/table/${encodeURIComponent(pubkey)}`;\n return {"publicurl": u, "privateurl": u};\n }\n\n\n async p_newtable(pubkey, table, {verbose=false}={}) {\n if (!pubkey) throw new errors.CodingError("p_newtable currently requires a pubkey");\n let database = await this.p_newdatabase(pubkey, {verbose});\n // If have use cases without a database, then call p_newdatabase first\n return { privateurl: `${database.privateurl}/${table}`, publicurl: `${database.publicurl}/${table}`} // No action required to create it\n }\n\n //TODO-KEYVALUE needs signing with private key of list\n async p_set(url, keyvalues, value, {verbose=false}={}) { // url = yjs:/yjs/database/table/key\n if (!url || !keyvalues) throw new errors.CodingError("TransportHTTP.p_set: invalid parms",url, keyvalyes);\n if (verbose) console.log("p_set", url, keyvalues, value);\n if (typeof keyvalues === "string") {\n let kv = JSON.stringify([{key: keyvalues, value: value}]);\n await httptools.p_POST(this._url(url, servercommands.set), "application/json", kv, verbose); // Returns immediately\n } else {\n let kv = JSON.stringify(Object.keys(keyvalues).map((k) => ({"key": k, "value": keyvalues[k]})));\n await httptools.p_POST(this._url(url, servercommands.set), "application/json", kv, verbose); // Returns immediately\n }\n }\n\n _keyparm(key) {\n return `key=${encodeURIComponent(key)}`\n }\n async p_get(url, keys, {verbose=false}={}) {\n if (!url && keys) throw new errors.CodingError("TransportHTTP.p_get: requires url and at least one key");\n let parmstr =Array.isArray(keys) ? keys.map(k => this._keyparm(k)).join(\'&\') : this._keyparm(keys)\n let res = await httptools.p_GET(this._url(url, servercommands.get, parmstr), {verbose});\n return Array.isArray(keys) ? res : res[keys]\n }\n\n async p_delete(url, keys, {verbose=false}={}) {\n if (!url && keys) throw new errors.CodingError("TransportHTTP.p_get: requires url and at least one key");\n let parmstr = keys.map(k => this._keyparm(k)).join(\'&\');\n await httptools.p_GET(this._url(url, servercommands.delete, parmstr), {verbose});\n }\n\n async p_keys(url, {verbose=false}={}) {\n if (!url && keys) throw new errors.CodingError("TransportHTTP.p_get: requires url and at least one key");\n return await httptools.p_GET(this._url(url, servercommands.keys), {verbose});\n }\n async p_getall(url, {verbose=false}={}) {\n if (!url && keys) throw new errors.CodingError("TransportHTTP.p_get: requires url and at least one key");\n return await httptools.p_GET(this._url(url, servercommands.getall), {verbose});\n }\n /* Make sure doesnt shadow regular p_rawfetch\n async p_rawfetch(url, verbose) {\n return {\n table: "keyvaluetable",\n _map: await this.p_getall(url, {verbose})\n }; // Data struc is ok as SmartDict.p_fetch will pass to KVT constructor\n }\n */\n\n p_info(verbose) { return httptools.p_GET(`${this.urlbase}/info`, {verbose}); }\n\n static async p_test(opts={}, verbose=false) {\n if (verbose) {console.log("TransportHTTP.test")}\n try {\n let transport = await this.p_setup(opts, verbose);\n if (verbose) console.log("HTTP connected");\n let res = await transport.p_info(verbose);\n if (verbose) console.log("TransportHTTP info=",res);\n res = await transport.p_status(verbose);\n console.assert(res === Transport.STATUS_CONNECTED);\n await transport.p_test_kvt("NACL%20VERIFY", verbose);\n } catch(err) {\n console.log("Exception thrown in TransportHTTP.test:", err.message);\n throw err;\n }\n }\n\n static async test() {\n return this;\n }\n\n}\nTransports._transportclasses["HTTP"] = TransportHTTP;\nexports = module.exports = TransportHTTP;\n\n\n\n//# sourceURL=webpack:///./TransportHTTP.js?')},"./TransportIPFS.js": /*!**************************!*\ !*** ./TransportIPFS.js ***! \**************************/ /*! no static exports found */function(module,exports,__webpack_require__){eval('/* WEBPACK VAR INJECTION */(function(Buffer) {/*\nThis is a shim to the IPFS library, (Lists are handled in YJS or OrbitDB)\nSee https://github.com/ipfs/js-ipfs but note its often out of date relative to the generic API doc.\n*/\n\nconst httptools = __webpack_require__(/*! ./httptools */ "./httptools.js"); // Expose some of the httptools so that IPFS can use it as a backup\n\n// IPFS components\nconst IPFS = __webpack_require__(/*! ipfs */ "./node_modules/ipfs/src/core/index.js");\nconst CID = __webpack_require__(/*! cids */ "./node_modules/cids/src/index.js");\n//Removed next two as not needed if use "Kludge" flagged below.\n//const dagPB = require(\'ipld-dag-pb\');\n//const DAGNode = dagPB.DAGNode; // So can check its type\nconst unixFs = __webpack_require__(/*! ipfs-unixfs */ "./node_modules/ipfs-unixfs/src/index.js");\n\n// Library packages other than IPFS\nconst Url = __webpack_require__(/*! url */ "./node_modules/url/url.js");\nconst stream = __webpack_require__(/*! readable-stream */ "./node_modules/readable-stream/readable-browser.js"); // Needed for the pullthrough - this is NOT Ipfs streams\n// Alternative to through - as used in WebTorrent\n\n// Utility packages (ours) And one-liners\n//No longer reqd: const promisify = require(\'promisify-es6\');\n//const makepromises = require(\'./utils/makepromises\'); // Replaced by direct call to promisify\n\n// Other Dweb modules\nconst errors = __webpack_require__(/*! ./Errors */ "./Errors.js"); // Standard Dweb Errors\nconst Transport = __webpack_require__(/*! ./Transport.js */ "./Transport.js"); // Base class for TransportXyz\nconst Transports = __webpack_require__(/*! ./Transports */ "./Transports.js"); // Manage all Transports that are loaded\nconst utils = __webpack_require__(/*! ./utils */ "./utils.js"); // Utility functions\n\nconst defaultoptions = {\n repo: \'/tmp/dweb_ipfsv2700\', //TODO-IPFS think through where, esp for browser\n //init: false,\n //start: false,\n //TODO-IPFS-Q how is this decentralized - can it run offline? Does it depend on star-signal.cloud.ipfs.team\n config: {\n // Addresses: { Swarm: [ \'/dns4/star-signal.cloud.ipfs.team/wss/p2p-webrtc-star\']}, // For Y - same as defaults\n // Addresses: { Swarm: [ ] }, // Disable WebRTC to test browser crash, note disables Y so doesnt work.\n //Addresses: {Swarm: [\'/dns4/ws-star.discovery.libp2p.io/tcp/443/wss/p2p-websocket-star\']}, // from https://github.com/ipfs/js-ipfs#faq 2017-12-05 as alternative to webrtc works sort-of\n Bootstrap: [\'/dns4/dweb.me/tcp/4245/wss/ipfs/QmPNgKEjC7wkpu3aHUzKKhZmbEfiGzL5TP1L8zZoHJyXZW\'], // Supposedly connects to Dweb IPFS instance, but doesnt work (nor does ".../wss/...")\n },\n //init: true, // Comment out for Y\n EXPERIMENTAL: {\n pubsub: true\n }\n};\n\nclass TransportIPFS extends Transport {\n /*\n IPFS specific transport\n\n Fields:\n ipfs: object returned when starting IPFS\n TODO - this is not complete\n */\n\n constructor(options, verbose) {\n super(options, verbose);\n this.ipfs = undefined; // Undefined till start IPFS\n this.options = options; // Dictionary of options\n this.name = "IPFS"; // For console log etc\n this.supportURLs = [\'ipfs\'];\n this.supportFunctions = [\'fetch\', \'store\']; // Does not support reverse, createReadStream fails on files uploaded with urlstore TODO reenable when Kyle fixes urlstore\n this.status = Transport.STATUS_LOADED;\n }\n\n/*\n _makepromises() {\n //Utility function to promisify Block\n //Replaced promisified utility since only two to promisify\n //this.promisified = {ipfs:{}};\n //makepromises(this.ipfs, this.promisified.ipfs, [ { block: ["put", "get"] }]); // Has to be after this.ipfs defined\n this.promisified = { ipfs: { block: {\n put: promisify(this.ipfs.block.put),\n get: promisify(this.ipfs.block.get)\n }}}\n }\n*/\n p_ipfsstart(verbose) {\n /*\n Just start IPFS - not Y (note used with "yarrays" and will be used for non-IPFS list management)\n Note - can\'t figure out how to use async with this, as we resolve the promise based on the event callback\n */\n const self = this;\n return new Promise((resolve, reject) => {\n this.ipfs = new IPFS(this.options);\n this.ipfs.on(\'ready\', () => {\n //this._makepromises();\n resolve();\n });\n this.ipfs.on(\'error\', (err) => reject(err));\n })\n .then(() => self.ipfs.version())\n .then((version) => console.log(\'IPFS READY\',version))\n .catch((err) => {\n console.log("Error caught in p_ipfsstart");\n throw(err);\n });\n }\n\n static setup0(options, verbose) {\n /*\n First part of setup, create obj, add to Transports but dont attempt to connect, typically called instead of p_setup if want to parallelize connections.\n */\n const combinedoptions = Transport.mergeoptions(defaultoptions, options.ipfs);\n if (verbose) console.log("IPFS loading options %o", combinedoptions);\n const t = new TransportIPFS(combinedoptions, verbose); // Note doesnt start IPFS\n Transports.addtransport(t);\n return t;\n }\n\n async p_setup1(verbose, cb) {\n try {\n if (verbose) console.log("IPFS starting and connecting");\n this.status = Transport.STATUS_STARTING; // Should display, but probably not refreshed in most case\n if (cb) cb(this);\n await this.p_ipfsstart(verbose); // Throws Error("websocket error") and possibly others.\n this.status = await this.p_status(verbose);\n } catch(err) {\n console.error(this.name, "failed to connect", err);\n this.status = Transport.STATUS_FAILED;\n }\n if (cb) cb(this);\n return this;\n }\n\n async p_status(verbose) {\n /*\n Return a numeric code for the status of a transport.\n */\n this.status = (await this.ipfs.isOnline()) ? Transport.STATUS_CONNECTED : Transport.STATUS_FAILED;\n return super.p_status(verbose);\n }\n\n // Everything else - unless documented here - should be opaque to the actual structure of a CID\n // or a url. This code may change as its not clear (from IPFS docs) if this is the right mapping.\n static urlFrom(unknown) {\n /*\n Convert a CID into a standardised URL e.g. ipfs:/ipfs/abc123\n */\n if (unknown instanceof CID)\n return "ipfs:/ipfs/"+unknown.toBaseEncodedString();\n if (typeof unknown === "object" && unknown.hash) // e.g. from files.add\n return "ipfs:/ipfs/"+unknown.hash;\n if (typeof unknown === "string") // Not used currently\n return "ipfs:/ipfs/"+unknown;\n throw new errors.CodingError("TransportIPFS.urlFrom: Cant convert to url from",unknown);\n }\n\n static cidFrom(url) {\n /*\n Convert a URL e.g. ipfs:/ipfs/abc123 into a CID structure suitable for retrieval\n url: String of form "ipfs://ipfs/" or parsed URL or CID\n returns: CID\n throws: TransportError if cant convert\n */\n if (url instanceof CID) return url;\n if (typeof(url) === "string") url = Url.parse(url);\n if (url && url["pathname"]) { // On browser "instanceof Url" isn\'t valid)\n const patharr = url.pathname.split(\'/\');\n if ((!["ipfs:","dweb:"].includes(url.protocol)) || (patharr[1] !== \'ipfs\') || (patharr.length < 3))\n throw new errors.TransportError("TransportIPFS.cidFrom bad format for url should be dweb: or ipfs:/ipfs/...: " + url.href);\n if (patharr.length > 3)\n throw new errors.TransportError("TransportIPFS.cidFrom not supporting paths in url yet, should be dweb: or ipfs:/ipfs/...: " + url.href);\n return new CID(patharr[2]);\n } else {\n throw new errors.CodingError("TransportIPFS.cidFrom: Cant convert url",url);\n }\n }\n\n static _stringFrom(url) {\n // Tool for ipfsFrom and ipfsGatewayFrom\n if (url instanceof CID)\n return "/ipfs/"+url.toBaseEncodedString();\n if (typeof url === \'object\' && url.path) { // It better be URL which unfortunately is hard to test\n return url.path;\n }\n }\n static ipfsFrom(url) {\n /*\n Convert to a ipfspath i.e. /ipfs/Qm....\n Required because of strange differences in APIs between files.cat and dag.get see https://github.com/ipfs/js-ipfs/issues/1229\n */\n url = this._stringFrom(url); // Convert CID or Url to a string hopefully containing /ipfs/\n if (url.indexOf(\'/ipfs/\') > -1) {\n return url.slice(url.indexOf(\'/ipfs/\'));\n }\n throw new errors.CodingError(`TransportIPFS.ipfsFrom: Cant convert url ${url} into a path starting /ipfs/`);\n }\n\n static ipfsGatewayFrom(url) {\n /*\n url: CID, Url, or a string\n returns: https://ipfs.io/ipfs/\n */\n url = this._stringFrom(url); // Convert CID or Url to a string hopefully containing /ipfs/\n if (url.indexOf(\'/ipfs/\') > -1) {\n return "https://ipfs.io" + url.slice(url.indexOf(\'/ipfs/\'));\n }\n throw new errors.CodingError(`TransportIPFS.ipfsGatewayFrom: Cant convert url ${url} into a path starting /ipfs/`);\n }\n\n static multihashFrom(url) {\n if (url instanceof CID)\n return cid.toBaseEncodedString();\n if (typeof url === \'object\' && url.path)\n url = url.path; // /ipfs/Q...\n if (typeof(url) === "string") {\n const idx = url.indexOf("/ipfs/");\n if (idx > -1) {\n return url.slice(idx+6);\n }\n }\n throw new errors.CodingError(`Cant turn ${url} into a multihash`);\n }\n\n async p_rawfetch(url, {verbose=false, timeoutMS=60000, relay=false}={}) {\n /*\n Fetch some bytes based on a url of the form ipfs:/ipfs/Qm..... or ipfs:/ipfs/z.... .\n No assumption is made about the data in terms of size or structure, nor can we know whether it was created with dag.put or ipfs add or http /api/v0/add/\n\n Where required by the underlying transport it should retrieve a number if its "blocks" and concatenate them.\n Returns a new Promise that resolves currently to a string.\n There may also be need for a streaming version of this call, at this point undefined since we havent (currently) got a use case..\n\n :param string url: URL of object being retrieved {ipfs|dweb}:/ipfs/ or /\n :param boolean verbose: true for debugging output\n :resolve buffer: Return the object being fetched. (may in the future return a stream and buffer externally)\n :throws: TransportError if url invalid - note this happens immediately, not as a catch in the promise\n */\n if (verbose) console.log("IPFS p_rawfetch", utils.stringfrom(url));\n if (!url) throw new errors.CodingError("TransportIPFS.p_rawfetch: requires url");\n const cid = TransportIPFS.cidFrom(url); // Throws TransportError if url bad\n const ipfspath = TransportIPFS.ipfsFrom(url) // Need because dag.get has different requirement than file.cat\n\n try {\n const res = await utils.p_timeout(this.ipfs.dag.get(cid), timeoutMS, "Timed out IPFS fetch of "+TransportIPFS._stringFrom(cid)); // Will reject and throw TimeoutError if times out\n // noinspection Annotator\n if (res.remainderPath.length)\n { // noinspection ExceptionCaughtLocallyJS\n throw new errors.TransportError("Not yet supporting paths in p_rawfetch");\n } //TODO-PATH\n let buff;\n //if (res.value instanceof DAGNode) { // Its file or something added with the HTTP API for example, TODO not yet handling multiple files\n if (res.value.constructor.name === "DAGNode") { // Kludge to replace above, as its not matching the type against the "require" above.\n if (verbose) console.log("IPFS p_rawfetch looks like its a file", url);\n //console.log("Case a or b" - we can tell the difference by looking at (res.value._links.length > 0) but dont need to\n // as since we dont know if we are on node or browser best way is to try the files.cat and if it fails try the block to get an approximate file);\n // Works on Node, but fails on Chrome, cant figure out how to get data from the DAGNode otherwise (its the wrong size)\n buff = await this.ipfs.files.cat(ipfspath); //See js-ipfs v0.27 version and https://github.com/ipfs/js-ipfs/issues/1229 and https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/FILES.md#cat\n\n /* Was needed on v0.26, not on v0.27\n if (buff.length === 0) { // Hit the Chrome bug\n // This will get a file padded with ~14 bytes - 4 at front, 4 at end and cant find the other 6 !\n // but it seems to work for PDFs which is what I\'m testing on.\n if (verbose) console.log("Kludge alert - files.cat fails in Chrome, trying block.get");\n let blk = await this.promisified.ipfs.block.get(cid);\n buff = blk.data;\n }\n END of v0.26 version */\n } else { //c: not a file\n buff = res.value;\n }\n if (verbose) console.log(`IPFS fetched ${buff.length} from ${ipfspath}`);\n return buff;\n } catch (err) { // TimeoutError or could be some other error from IPFS etc\n console.log("Caught misc error in TransportIPFS.p_rawfetch trying IPFS", err.message);\n try {\n let ipfsurl = TransportIPFS.ipfsGatewayFrom(url);\n return await utils.p_timeout(\n httptools.p_GET(ipfsurl), // Returns a buffer\n timeoutMS, "Timed out IPFS fetch of "+ipfsurl)\n } catch (err) {\n console.log("Caught misc error in TransportIPFS.p_rawfetch trying gateway", err.message);\n throw err;\n }\n }\n }\n\n async p_rawstore(data, {verbose}) {\n /*\n Store a blob of data onto the decentralised transport.\n Returns a promise that resolves to the url of the data\n\n :param string|Buffer data: Data to store - no assumptions made to size or content\n :param boolean verbose: true for debugging output\n :resolve string: url of data stored\n */\n console.assert(data, "TransportIPFS.p_rawstore: requires data");\n const buf = (data instanceof Buffer) ? data : new Buffer(data);\n //return this.promisified.ipfs.block.put(buf).then((block) => block.cid)\n //https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/DAG.md#dagput\n //let res = await this.ipfs.dag.put(buf,{ format: \'dag-cbor\', hashAlg: \'sha2-256\' });\n const res = (await this.ipfs.files.add(buf,{ "cid-version": 1, hashAlg: \'sha2-256\'}))[0];\n return TransportIPFS.urlFrom(res);\n }\n\n // Based on https://github.com/ipfs/js-ipfs/pull/1231/files\n\n async p_offsetStream(stream, links, startByte, endByte) {\n let streamPosition = 0\n try {\n for (let l in links) {\n const link = links[l];\n if (!stream.writable) { return } // The stream has been closed\n // DAGNode Links report unixfs object data sizes 14 bytes larger due to the protobuf wrapper\n const bytesInLinkedObjectData = link.size - 14\n if (startByte > (streamPosition + bytesInLinkedObjectData)) {\n // Start byte is after this block so skip it\n streamPosition += bytesInLinkedObjectData;\n } else if (endByte && endByte < streamPosition) { // TODO-STREAM this is copied from https://github.com/ipfs/js-ipfs/pull/1231/files but I think it should be endByte <= since endByte is first byte DONT want\n // End byte was before this block so skip it\n streamPosition += bytesInLinkedObjectData;\n } else {\n let lmh = link.multihash;\n let data;\n await this.ipfs.object.data(lmh)\n .then ((d) => unixFs.unmarshal(d).data)\n .then ((d) => data = d )\n .catch((err) => {console.log("XXX@289 err=",err);});\n if (!stream.writable) { return; } // The stream was closed while we were getting data\n const length = data.length;\n if (startByte > streamPosition && startByte < (streamPosition + length)) {\n // If the startByte is in the current block, skip to the startByte\n data = data.slice(startByte - streamPosition);\n }\n console.log(`Writing ${data.length} to stream`)\n stream.write(data);\n streamPosition += length;\n }\n }\n } catch(err) {\n console.log(err.message);\n }\n }\n async p_f_createReadStream(url, {verbose=false}={}) { // Asynchronously return a function that can be used in createReadStream\n verbose = true;\n if (verbose) console.log("p_f_createReadStream",url);\n const mh = TransportIPFS.multihashFrom(url);\n const links = await this.ipfs.object.links(mh);\n let throughstream; //Holds pointer to stream between calls.\n const self = this;\n function crs(opts) { // This is a synchronous function\n // Return a readable stream that provides the bytes between offsets "start" and "end" inclusive\n console.log("opts=",JSON.stringify(opts));\n /* Can replace rest of crs with this when https://github.com/ipfs/js-ipfs/pull/1231/files lands (hopefully v0.28.3)\n return self.ipfs.catReadableStream(mh, opts ? opts.start : 0, opts && opts.end) ? opts.end+1 : undefined)\n */\n if (!opts) return throughstream; //TODO-STREAM unclear why called without opts - take this out when figured out\n if (throughstream && throughstream.destroy) throughstream.destroy();\n throughstream = new stream.PassThrough();\n\n self.p_offsetStream( // Ignore promise returned, this will write to the stream asynchronously\n throughstream,\n links, // Uses the array of links created above in this function\n opts ? opts.start : 0,\n (opts && opts.end) ? opts.end : undefined);\n return throughstream;\n }\n return crs;\n }\n\n static async p_test(opts, verbose) {\n if (verbose) {console.log("TransportIPFS.test")}\n try {\n const transport = await this.p_setup(opts, verbose); // Assumes IPFS already setup\n if (verbose) console.log(transport.name,"setup");\n const res = await transport.p_status(verbose);\n console.assert(res === Transport.STATUS_CONNECTED)\n\n let urlqbf;\n const qbf = "The quick brown fox";\n const qbf_url = "ipfs:/ipfs/zdpuAscRnisRkYnEyJAp1LydQ3po25rCEDPPEDMymYRfN1yPK"; // Expected url\n const testurl = "1114"; // Just a predictable number can work with\n const url = await transport.p_rawstore(qbf, {verbose});\n if (verbose) console.log("rawstore returned", url);\n const newcid = TransportIPFS.cidFrom(url); // Its a CID which has a buffer in it\n console.assert(url === qbf_url, "url should match url from rawstore");\n const cidmultihash = url.split(\'/\')[2]; // Store cid from first block in form of multihash\n const newurl = TransportIPFS.urlFrom(newcid);\n console.assert(url === newurl, "Should round trip");\n urlqbf = url;\n const data = await transport.p_rawfetch(urlqbf, {verbose});\n console.assert(data.toString() === qbf, "Should fetch block stored above");\n //console.log("TransportIPFS test complete");\n return transport\n } catch(err) {\n console.log("Exception thrown in TransportIPFS.test:", err.message);\n throw err;\n }\n }\n\n}\nTransports._transportclasses["IPFS"] = TransportIPFS;\nexports = module.exports = TransportIPFS;\n\n/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./node_modules/node-libs-browser/node_modules/buffer/index.js */ "./node_modules/node-libs-browser/node_modules/buffer/index.js").Buffer))\n\n//# sourceURL=webpack:///./TransportIPFS.js?')},"./TransportWEBTORRENT.js": /*!********************************!*\ !*** ./TransportWEBTORRENT.js ***! \********************************/ /*! no static exports found */function(module,exports,__webpack_require__){eval('/* WEBPACK VAR INJECTION */(function(Buffer) {/*\nThis Transport layers builds on WebTorrent\n\nY Lists have listeners and generate events - see docs at ...\n*/\n\n// WebTorrent components\n\nconst WebTorrent = __webpack_require__(/*! webtorrent */ "./node_modules/webtorrent/index.js");\nconst stream = __webpack_require__(/*! readable-stream */ "./node_modules/readable-stream/readable-browser.js");\n\n// Other Dweb modules\nconst errors = __webpack_require__(/*! ./Errors */ "./Errors.js"); // Standard Dweb Errors\nconst Transport = __webpack_require__(/*! ./Transport.js */ "./Transport.js"); // Base class for TransportXyz\nconst Transports = __webpack_require__(/*! ./Transports */ "./Transports.js"); // Manage all Transports that are loaded\n\nlet defaultoptions = {\n};\n\nclass TransportWEBTORRENT extends Transport {\n /*\n WebTorrent specific transport\n\n Fields:\n webtorrent: object returned when starting webtorrent\n */\n\n constructor(options, verbose) {\n super(options, verbose);\n this.webtorrent = undefined; // Undefined till start WebTorrent\n this.options = options; // Dictionary of options\n this.name = "WEBTORRENT"; // For console log etc\n this.supportURLs = [\'magnet\'];\n this.supportFunctions = [\'fetch\', \'createReadStream\'];\n this.status = Transport.STATUS_LOADED;\n }\n\n p_webtorrentstart(verbose) {\n /*\n Start WebTorrent and wait until for ready.\n */\n let self = this;\n return new Promise((resolve, reject) => {\n this.webtorrent = new WebTorrent(this.options);\n this.webtorrent.once("ready", () => {\n console.log("WEBTORRENT READY");\n resolve();\n });\n this.webtorrent.once("error", (err) => reject(err));\n this.webtorrent.on("warning", (err) => {\n console.warn("WebTorrent Torrent WARNING: " + err.message);\n });\n })\n }\n\n static setup0(options, verbose) {\n /*\n First part of setup, create obj, add to Transports but dont attempt to connect, typically called instead of p_setup if want to parallelize connections.\n */\n let combinedoptions = Transport.mergeoptions(defaultoptions, options.webtorrent);\n console.log("WebTorrent options %o", combinedoptions);\n let t = new TransportWEBTORRENT(combinedoptions, verbose);\n Transports.addtransport(t);\n\n return t;\n }\n\n async p_setup1(verbose, cb) {\n try {\n this.status = Transport.STATUS_STARTING;\n if (cb) cb(this);\n await this.p_webtorrentstart(verbose);\n await this.p_status(verbose);\n } catch(err) {\n console.error(this.name, "failed to connect", err);\n this.status = Transport.STATUS_FAILED;\n }\n if (cb) cb(this);\n return this;\n }\n\n async p_status(verbose) {\n /*\n Return a string for the status of a transport. No particular format, but keep it short as it will probably be in a small area of the screen.\n */\n if (this.webtorrent && this.webtorrent.ready) {\n this.status = Transport.STATUS_CONNECTED;\n } else if (this.webtorrent) {\n this.status = Transport.STATUS_STARTING;\n } else {\n this.status = Transport.STATUS_FAILED;\n }\n return super.p_status(verbose);\n }\n\n webtorrentparseurl(url) {\n /* Parse a URL\n url: URL as string or already parsed into Url - should start magnet: or in future might support dweb:/magnet/; some other formats might be supported\n returns: torrentid, path\n */\n if (!url) {\n throw new errors.CodingError("TransportWEBTORRENT.p_rawfetch: requires url");\n }\n\n const urlstring = (typeof url === "string" ? url : url.href);\n const index = urlstring.indexOf(\'/\');\n\n if (index === -1) {\n throw new errors.CodingError("TransportWEBTORRENT.p_rawfetch: invalid url - missing path component. Should look like magnet:xyzabc/path/to/file");\n }\n\n const torrentId = urlstring.slice(0, index);\n const path = urlstring.slice(index + 1);\n\n return { torrentId, path }\n }\n\n async p_webtorrentadd(torrentId) {\n return new Promise((resolve, reject) => {\n // Check if this torrentId is already added to the webtorrent client\n let torrent = this.webtorrent.get(torrentId);\n\n // If not, then add the torrentId to the torrent client\n if (!torrent) {\n torrent = this.webtorrent.add(torrentId);\n\n torrent.once("error", (err) => {\n reject(new errors.TransportError("Torrent encountered a fatal error " + err.message));\n });\n\n torrent.on("warning", (err) => {\n console.warn("WebTorrent Torrent WARNING: " + err.message + " (" + torrent.name + ")");\n });\n }\n\n if (torrent.ready) {\n resolve(torrent);\n } else {\n torrent.once("ready", () => {\n resolve(torrent);\n });\n }\n });\n }\n\n webtorrentfindfile (torrent, path) {\n /*\n Given a torrent object and a path to a file within the torrent, find the given file.\n */\n const filePath = torrent.name + \'/\' + path;\n const file = torrent.files.find(file => {\n return file.path === filePath;\n });\n if (!file) {\n //debugger;\n throw new errors.TransportError("Requested file (" + path + ") not found within torrent ");\n }\n return file;\n }\n\n p_rawfetch(url, {verbose=false}={}) {\n /*\n Fetch some bytes based on a url of the form:\n\n magnet:xyzabc/path/to/file\n\n (Where xyzabc is the typical magnet uri contents)\n\n No assumption is made about the data in terms of size or structure. Returns a new Promise that resolves to a buffer.\n\n :param string url: URL of object being retrieved\n :param boolean verbose: true for debugging output\n :resolve buffer: Return the object being fetched.\n :throws: TransportError if url invalid - note this happens immediately, not as a catch in the promise\n */\n return new Promise((resolve, reject) => {\n if (verbose) console.log("WebTorrent p_rawfetch", url);\n\n const { torrentId, path } = this.webtorrentparseurl(url);\n this.p_webtorrentadd(torrentId)\n .then((torrent) => {\n torrent.deselect(0, torrent.pieces.length - 1, false); // Dont download entire torrent as will pull just the file we want (warning - may give problems if multiple reads from same webtorrent)\n const file = this.webtorrentfindfile(torrent, path);\n file.getBuffer((err, buffer) => {\n if (err) {\n return reject(new errors.TransportError("Torrent encountered a fatal error " + err.message + " (" + torrent.name + ")"));\n }\n resolve(buffer);\n });\n })\n .catch((err) => reject(err));\n });\n }\n\n async _p_fileTorrentFromUrl(url) {\n /*\n Then open a webtorrent for the file specified in the path part of the url\n url: of form magnet:... or magnet/:...\n return: Web Torrent file\n */\n try {\n const {torrentId, path} = this.webtorrentparseurl(url);\n const torrent = await this.p_webtorrentadd(torrentId);\n torrent.deselect(0, torrent.pieces.length - 1, false); // Dont download entire torrent as will pull just the file we want (warning - may give problems if multiple reads from same webtorrent)\n const file = this.webtorrentfindfile(torrent, path);\n if (typeof window !== "undefined") { // Check running in browser\n window.WEBTORRENT_TORRENT = torrent;\n window.WEBTORRENT_FILE = file;\n torrent.once(\'close\', () => {\n window.WEBTORRENT_TORRENT = null;\n window.WEBTORRENT_FILE = null;\n })\n }\n return file\n } catch(err) {\n console.log(`p_fileFrom failed on ${url} ${err.message}`);\n throw(err);\n }\n\n }\n\n async p_f_createReadStream(url, {verbose=false, wanturl=false}={}) {\n /*\n Fetch bytes progressively, using a node.js readable stream, based on a url of the form:\n No assumption is made about the data in terms of size or structure.\n\n This is the initialisation step, which returns a function suitable for