From 8fc8e8761f80cdb788d4660924409fabd76342db Mon Sep 17 00:00:00 2001 From: Mitra Ardron Date: Thu, 12 Sep 2019 09:43:26 +1000 Subject: [PATCH] * 0.2.0: Start moving transport dependencies to consumer, specifically IPFS, GUN, WebTorrent --- README.md | 2 + dist/dweb-transports-bundle.js | 12669 ++----------------------------- package.json | 4 +- 3 files changed, 479 insertions(+), 12196 deletions(-) diff --git a/README.md b/README.md index 32a6640..3dad1ff 100644 --- a/README.md +++ b/README.md @@ -127,6 +127,8 @@ See [Dweb document index](./DOCUMENTINDEX.md) for a list of the repos that make ### Release Notes +* 0.2.0: Start moving transport dependencies to consumer, specifically IPFS, GUN, WebTorrent +------ * 0.1.63: Move naming internal * 0.1.62: Upgrade dependencies * 0.1.61: loopguard to return correct error from queuedFetch; add info to statuses diff --git a/dist/dweb-transports-bundle.js b/dist/dweb-transports-bundle.js index 6053a52..77af7ef 100644 --- a/dist/dweb-transports-bundle.js +++ b/dist/dweb-transports-bundle.js @@ -1,12194 +1,475 @@ -/******/ (function(modules) { // webpackBootstrap -/******/ // The module cache -/******/ var installedModules = {}; -/******/ -/******/ // The require function -/******/ function __webpack_require__(moduleId) { -/******/ -/******/ // Check if module is in cache -/******/ if(installedModules[moduleId]) { -/******/ return installedModules[moduleId].exports; -/******/ } -/******/ // Create a new module (and put it into the cache) -/******/ var module = installedModules[moduleId] = { -/******/ i: moduleId, -/******/ l: false, -/******/ exports: {} -/******/ }; -/******/ -/******/ // Execute the module function -/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); -/******/ -/******/ // Flag the module as loaded -/******/ module.l = true; -/******/ -/******/ // Return the exports of the module -/******/ return module.exports; -/******/ } -/******/ -/******/ -/******/ // expose the modules object (__webpack_modules__) -/******/ __webpack_require__.m = modules; -/******/ -/******/ // expose the module cache -/******/ __webpack_require__.c = installedModules; -/******/ -/******/ // define getter function for harmony exports -/******/ __webpack_require__.d = function(exports, name, getter) { -/******/ if(!__webpack_require__.o(exports, name)) { -/******/ Object.defineProperty(exports, name, { enumerable: true, get: getter }); -/******/ } -/******/ }; -/******/ -/******/ // define __esModule on exports -/******/ __webpack_require__.r = function(exports) { -/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { -/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); -/******/ } -/******/ Object.defineProperty(exports, '__esModule', { value: true }); -/******/ }; -/******/ -/******/ // create a fake namespace object -/******/ // mode & 1: value is a module id, require it -/******/ // mode & 2: merge all properties of value into the ns -/******/ // mode & 4: return value when already ns object -/******/ // mode & 8|1: behave like require -/******/ __webpack_require__.t = function(value, mode) { -/******/ if(mode & 1) value = __webpack_require__(value); -/******/ if(mode & 8) return value; -/******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value; -/******/ var ns = Object.create(null); -/******/ __webpack_require__.r(ns); -/******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value }); -/******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key)); -/******/ return ns; -/******/ }; -/******/ -/******/ // getDefaultExport function for compatibility with non-harmony modules -/******/ __webpack_require__.n = function(module) { -/******/ var getter = module && module.__esModule ? -/******/ function getDefault() { return module['default']; } : -/******/ function getModuleExports() { return module; }; -/******/ __webpack_require__.d(getter, 'a', getter); -/******/ return getter; -/******/ }; -/******/ -/******/ // Object.prototype.hasOwnProperty.call -/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; -/******/ -/******/ // __webpack_public_path__ -/******/ __webpack_require__.p = ""; -/******/ -/******/ -/******/ // Load entry module and return exports -/******/ return __webpack_require__(__webpack_require__.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) {\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 //TODO-SPLIT define load()\n\n static setup0(options) {\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(cb) {\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(cb) {\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, 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) // Sync version that doesnt connect\n .p_setup1(cb); // And connect\n\n return t.p_setup2(cb); // And connect\n }\n /* Disconnect from the transport service - there is no guarrantee that a restart will be successfull so this is usually only for when exiting */\n stop(refreshstatus, cb) {\n // refreshstatus(Transport instance) => optional callback to the UI to update the status on the display\n this.status = Transport.STATUS_FAILED;\n if (refreshstatus) refreshstatus(this);\n cb(null, this);\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 case Transport.STATUS_LOADED:\n this.p_setup1(cb).then((t)=>t.p_setup2(cb)); // Allows for updating status progressively as attempts to connect\n }\n if (cb) cb(this);\n }\n\n async p_status() {\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 connected() {\n // True if connected (status==STATUS_CONNECTED==0) should not need subclassing\n return ! this.status;\n }\n supports(url, func, {noCache=undefined}={}) { //TODO-API\n /*\n Determine if this transport supports a certain set of URLs and a func\n\n :param url: String or parsed URL\n :param opts: { noCache } check against supportFeatures\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 && (!noCache || this.supportFeatures.includes(\"noCache\"))\n )\n }\n\n validFor(url, func, opts) {\n // By default a transport can handle a url and a func if its connected and supports that url/func\n // This shouldnt need subclassing, an exception is HTTP which only applies \"connected\" against urls heading for the gateway\n return this.connected() && this.supports(url, func, opts);\n }\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 :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) {\n try {\n return await this.p_rawstore(data);\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}={}) {\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 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 return \"UNIMPLEMENTED\";\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) {\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 :resolve undefined:\n */\n throw new errors.ToBeImplementedError(\"Undefined function Transport.p_rawadd\");\n }\n\n p_rawlist(url) {\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 :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) {\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) {\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 :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, {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 */\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) {\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) {\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) { // 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) {\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) {\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) {\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) {\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}) {\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, careful since searchparameters from URL passed in as null\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 (val !== null) {\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 }\n return c;\n }\n\n async p_test_list({urlexpectedsubstring=undefined}={}) {\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 {console.log(this.name,\"p_test_kvt\")}\n try {\n let table = await this.p_newlisturls(\"NACL VERIFY:1234567LIST\");\n let mapurl = table.publicurl;\n console.log(\"newlisturls=\",mapurl);\n console.assert((!urlexpectedsubstring) || mapurl.includes(urlexpectedsubstring));\n await this.p_rawadd(mapurl, \"testvalue\");\n let res = await this.p_rawlist(mapurl);\n console.assert(res.length===1 && res[0] === \"testvalue\");\n await this.p_rawadd(mapurl, {foo: \"bar\"}); // Try adding an object\n res = await this.p_rawlist(mapurl);\n console.assert(res.length === 2 && res[1].foo === \"bar\");\n await this.p_rawadd(mapurl, [1,2,3]); // Try setting to an array\n res = await this.p_rawlist(mapurl);\n console.assert(res.length === 2 && res[2].length === 3 && res[2][1] === 2);\n await delay(200);\n 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) {\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 {console.log(this.name,\"p_test_kvt\")}\n try {\n let table = await this.p_newtable(\"NACL VERIFY:1234567KVT\",\"mytable\");\n let mapurl = table.publicurl;\n console.log(\"newtable=\",mapurl);\n console.assert(mapurl.includes(urlexpectedsubstring));\n await this.p_set(mapurl, \"testkey\", \"testvalue\");\n let res = await this.p_get(mapurl, \"testkey\");\n console.assert(res === \"testvalue\");\n await this.p_set(mapurl, \"testkey2\", {foo: \"bar\"}); // Try setting to an object\n res = await this.p_get(mapurl, \"testkey2\");\n console.assert(res.foo === \"bar\");\n await this.p_set(mapurl, \"testkey3\", [1,2,3]); // Try setting to an array\n res = await this.p_get(mapurl, \"testkey3\");\n console.assert(res[1] === 2);\n res = await this.p_keys(mapurl);\n console.assert(res.includes(\"testkey\") && res.includes(\"testkey3\") && res.length === 3);\n await this.p_delete(mapurl, [\"testkey\"]);\n res = await this.p_getall(mapurl);\n console.log(\"getall=>\",res);\n console.assert(res.testkey2.foo === \"bar\" && res.testkey3[\"1\"] === 2 && !res.testkey);\n await delay(200);\n 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\nTransport.STATUSTEXT = [\"Connected\", \"Failed\", \"Starting\", \"Loaded\", \"Paused\"];\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\nSee https://github.com/internetarchive/dweb-mirror/issues/43 for meta issue\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\"); // gun/gun is the minimized version\n// Raw Gun has almost nothing in it, it needs at least the following to work properly.\n__webpack_require__(/*! gun/lib/path.js */ \"./node_modules/gun/lib/path.js\"); // So that .path works\n\n//WORKAROUND-GUN-STORAGE\n// The next step is to stop it failing as soon as its cached 5Mb in localstorage\n// see https://github.com/amark/gun/blob/master/test/tmp/indexedDB.html and https://github.com/amark/gun/issues/590\n// but the instructions on how to do this are obviously broken so waiting on @amark to get this working.\n\n// See https://github.com/internetarchive/dweb-archive/issues/106 unable to get this working (Gun doesnt work well with webpack)\n//require('gun/nts');\n//require('gun/lib/wire'); // NodeJS websocket\n//require('gun/lib/multicast'); // Local area broadcasting needs passing `multicast: true` in options (which is safe in node + browser)\n__webpack_require__(/*! gun/lib/radix.js */ \"./node_modules/gun/lib/radix.js\"); // loaded by store but required for webpack\n__webpack_require__(/*! gun/lib/radisk.js */ \"./node_modules/gun/lib/radisk.js\"); // loaded by store but required for webpack\n__webpack_require__(/*! gun/lib/store.js */ \"./node_modules/gun/lib/store.js\");\n__webpack_require__(/*! gun/lib/rindexed.js */ \"./node_modules/gun/lib/rindexed.js\");\n\nconst debuggun = __webpack_require__(/*! debug */ \"./node_modules/debug/src/browser.js\")('dweb-transports:gun');\nconst stringify = __webpack_require__(/*! canonical-json */ \"./node_modules/canonical-json/index.js\");\n\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\" ],\n localStorage: false // Need to be false to turn localStorage off on browser (do this if include radix/radisk)\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 - copy example from systemctl here\n\n/*\n WORKING AROUND GUN WEIRDNESS/SUBOPTIMAL (of course, whats weird/sub-optimal to me, might be ideal to someone else) - search the code to see where worked around\n\n WORKAROUND-GUN-UNDERSCORE .once() and possibly .on() send an extra GUN internal field \"_\" which needs filtering. Reported and hopefully will get fixed\n .once behaves differently on node or the browser - this is a bug https://github.com/amark/gun/issues/586 and for now this code doesnt work on Node\n WORKAROUND-GUN-CURRENT: .once() and .on() deliver existing values as well as changes, reported & hopefully will get way to find just new ones.\n WORKAROUND-GUN-DELETE: 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,\n WORKAROUND-GUN-PROMISES: GUN is not promisified, there is only one place we care, and that is .once (since .on is called multiple times).\n WORKAROUND-GUN-ERRORS: GUN does an unhelpful job with errors, for example returning undefined when it cant find something (e.g. if connection to superpeer is down),\n for now just throw an error on undefined\n WORKAROUND-GUN-STORAGE: GUN defaults to local storage, which then fails on 5Mb or more of data, need to use radix, which has to be included and has bizarre config requirement I can't figure out\n TODO-GUN, handle error callbacks which are available in put etc\n Errors and Promises: Note that GUN's use of promises is seriously unexpected (aka weird), see https://gun.eco/docs/SEA#errors\n instead of using .reject or throwing an error at async it puts the error in SEA.err, so how that works in async parallel context is anyone's guess\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) {\n super(options);\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 = [ 'fetch', //'store'\n 'connection', 'get', 'set', 'getall', 'keys', 'newdatabase', 'newtable', 'monitor',\n 'add', 'list', 'listmonitor', 'newlisturls'];\n this.supportFeatures = []; // Doesnt support noCache and is mutable\n this.status = Transport.STATUS_LOADED;\n }\n\n connection(url) {\n /*\n TODO-GUN need to determine what a \"rooted\" Url is in gun, is it specific to a superpeer for example\n Utility function to get Gun object for this URL (note this isn't async)\n url: URL string or structure, to find list of of form [gun|dweb]:/gun//[/debuggun(\"Got late result of: %o\", data)); // Checking for bug in GUN issue#586 - ignoring result\n if (!val)\n throw new errors.TransportError(\"GUN unable to retrieve: \"+url.href); // WORKAROUND-GUN-ERRORS - gun doesnt throw errors when it cant find something\n let o = typeof val === \"string\" ? JSON.parse(val) : val; // This looks like it is sync (see same code on p_get and p_rawfetch)\n //TODO-GUN this is a hack because the metadata such as metadata/audio is getting cached in GUN and in this case is wrong.\n if (o.metadata && o.metadata.thumbnaillinks && o.metadata.thumbnaillinks.find(t => t.includes('ipfs/zb2'))) {\n throw new errors.TransportError(\"GUN retrieving legacy data at: \"+url.href)\n }\n return o;\n }\n\n\n // ===== LISTS ========\n\n // noinspection JSCheckFunctionSignatures\n async p_rawlist(url) {\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 :resolve array: An array of objects as stored on the list.\n */\n try {\n let g = this.connection(url);\n let data = await this._p_once(g);\n let res = data ? Object.keys(data).filter(k => k !== '_').sort().map(k => data[k]) : []; //See WORKAROUND-GUN-UNDERSCORE\n // .filter((obj) => (obj.signedby.includes(url))); // upper layers verify, which filters\n debuggun(\"p_rawlist found\", ...utils.consolearr(res));\n return res;\n } catch(err) {\n // Will be logged by Transports\n throw(err);\n }\n }\n\n listmonitor(url, callback, {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 */\n let g = this.connection(url);\n if (!current) { // See WORKAROUND-GUN-CURRENT have to keep an extra copy to compare for which calls are new.\n g.once(data => {\n this.monitored = data ? 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 WORKAROUND-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) {\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 :resolve undefined:\n */\n // noinspection JSUnresolvedVariable\n // Logged by Transports\n console.assert(url && sig.urls.length && sig.signature && sig.signedby.length, \"TransportGUN.p_rawadd args\", url, sig);\n this.connection(url)\n .set( stringify( sig.preflight( Object.assign({}, sig))));\n }\n\n // noinspection JSCheckFunctionSignatures\n async p_newlisturls(cl) {\n let u = await this._p_newgun(cl);\n return [ u, u];\n }\n\n //=======KEY VALUE TABLES ========\n\n // noinspection JSMethodCanBeStatic\n async _p_newgun(pubkey) {\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) {\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);\n return {publicurl: u, privateurl: u};\n }\n\n async p_newtable(pubkey, table) {\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);\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) { // 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);\n if (typeof keyvalues === \"string\") {\n table.path(keyvalues).put(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] = stringify(keyvalues[key]); return previous; },\n {}\n ))\n }\n }\n\n async p_get(url, keys) {\n let table = this.connection(url);\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 (see same code on p_get and p_rawfetch)\n }\n }\n\n async p_delete(url, keys) {\n let table = this.connection(url);\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 //WORKAROUND-GUN-PROMISE suggest p_once as a good single addition\n //TODO-GUN expand this to workaround Gun weirdness with errors.\n _p_once(gun) { // Note in some cases (e.g. p_getall) this will resolve to a object, others a string/number (p_get)\n // TODO-GUN Temporarily added a 2000ms delay to workaround https://github.com/internetarchive/dweb-archive/issues/106 / https://github.com/amark/gun/issues/762\n return new Promise((resolve) => gun.once(resolve, {wait: 2000}));\n }\n\n async p_keys(url) {\n let res = await this._p_once(this.connection(url));\n return Object.keys(res)\n .filter(k=> (k !== '_') && (res[k] !== null)); //See WORKAROUND-GUN-UNDERSCORE and WORKAROUND-GUN-DELETE\n }\n\n async p_getall(url) {\n let res = await this._p_once(this.connection(url));\n return Object.keys(res)\n .filter(k=> (k !== '_') && res[k] !== null) //See WORKAROUND-GUN-UNDERSCORE and WORKAROUND-GUN-DELETE\n .reduce( function(previous, key) { previous[key] = JSON.parse(res[key]); return previous; }, {});\n }\n\n async monitor(url, callback, {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 current Send existing items to the callback as well\n */\n let g = this.connection(url);\n if (!current) { // See WORKAROUND-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 WORKAROUND-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() {\n debuggun(\"p_test\");\n try {\n let t = this.setup0({}); //TODO-GUN when works with peers commented out, try passing peers: []\n await t.p_setup1(); // Not passing cb yet\n await t.p_setup2(); // Not passing cb yet - this one does nothing on GUN\n // noinspection JSIgnoredPromiseFromCall\n t.p_test_kvt(\"gun:/gun/NACL\");\n //t.p_test_list(\"gun:/gun/NACL\"); //TODO test_list needs fixing to not create a dependency on Signature\n } catch(err) {\n console.warn(\"Exception thrown in TransportGUN.test:\", err.message);\n throw err;\n }\n }\n\n // noinspection JSUnusedGlobalSymbols\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\");\nconst stream = __webpack_require__(/*! readable-stream */ \"./node_modules/readable-stream/readable-browser.js\");\nconst debug = __webpack_require__(/*! debug */ \"./node_modules/debug/src/browser.js\")('dweb-transports:http');\nconst stringify = __webpack_require__(/*! canonical-json */ \"./node_modules/canonical-json/index.js\");\n\n\ndefaulthttpoptions = {\n urlbase: 'https://dweb.me',\n heartbeat: { delay: 30000 } // By default check twice a minute\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 /* Subclass of Transport for handling HTTP - see API.md for docs\n\n options {\n urlbase: e.g. https://dweb.me Where to go for URLS like /arc/...\n heartbeat: {\n delay // Time in milliseconds between checks - 30000 might be appropriate - if missing it wont do a heartbeat\n statusCB // Callback cb(transport) when status changes\n }\n }\n */\n\n constructor(options) {\n super(options); // These are now options.http\n this.options = options;\n this.urlbase = options.urlbase; // e.g. https://dweb.me\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 = ['noCache'];\n if (typeof window === \"undefined\") {\n // running in node, can support createReadStream, (browser can't - see createReadStream below)\n this.supportFunctions.push(\"createReadStream\");\n }\n // noinspection JSUnusedGlobalSymbols\n this.supportFeatures = ['fetch.range', 'noCache'];\n this.name = \"HTTP\"; // For console log etc\n this.status = Transport.STATUS_LOADED;\n }\n\n //TODO-SPLIT define load()\n\n static setup0(options) {\n let combinedoptions = Transport.mergeoptions(defaulthttpoptions, options.http);\n try {\n let t = new TransportHTTP(combinedoptions);\n Transports.addtransport(t);\n return t;\n } catch (err) {\n console.error(\"HTTP unable to setup0\", err.message);\n throw err;\n }\n }\n\n p_setup1(statusCB) {\n return new Promise((resolve, unusedReject) => {\n this.status = Transport.STATUS_STARTING;\n if (statusCB) statusCB(this);\n this.updateStatus((unusedErr, unusedRes) => {\n if (statusCB) statusCB(this);\n this.startHeartbeat(this.options.heartbeat);\n resolve(this); // Note always resolve even if error from p_status as have set status to failed\n });\n })\n }\n\n async p_status(cb) { //TODO-API\n /*\n Return (via cb or promise) a numeric code for the status of a transport.\n */\n if (cb) { try { this.updateStatus(cb) } catch(err) { cb(err)}} else { return new Promise((resolve, reject) => { try { this.updateStatus((err, res) => { if (err) {reject(err)} else {resolve(res)} })} catch(err) {reject(err)}})} // Promisify pattern v2f\n }\n updateStatus(cb) { //TODO-API\n this.updateInfo((err, res) => {\n if (err) {\n debug(\"Error status call to info failed %s\", err.message);\n this.status = Transport.STATUS_FAILED;\n cb(null, this.status); // DOnt pass error up, the status indicates the error\n } else {\n this.info = res; // Save result\n this.status = Transport.STATUS_CONNECTED;\n cb(null, this.status);\n }\n });\n }\n\n startHeartbeat({delay=undefined, statusCB=undefined}) {\n if (delay) {\n debug(\"HTTP Starting Heartbeat\")\n this.HTTPheartbeatTimer = setInterval(() => {\n this.updateStatus((err, res)=>{ // Pings server and sets status\n if (statusCB) statusCB(this); // repeatedly call callback if supplies\n }, (unusedErr, unusedRes)=>{}); // Dont wait for status to complete\n }, delay);\n }\n }\n stopHeartbeat() {\n if (this.HTTPheartbeatTimer) {\n debug(\"HTTP stopping heartbeat\");\n clearInterval(this.HTTPheartbeatTimer);}\n }\n stop(refreshstatus, cb) {\n this.stopHeartbeat();\n this.status = Transport.STATUS_FAILED;\n if (refreshstatus) { refreshstatus(this); }\n cb(null, this);\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\n validFor(url, func, opts) {\n // Overrides Transport.prototype.validFor because HTTP's connection test is only really for dweb.me\n // in particular this allows urls like https://be-api.us.archive.org\n return (this.connected() || (url.protocol.startsWith(\"http\") && ! url.href.startsWith(this.urlbase))) && this.supports(url, func, opts);\n }\n // noinspection JSCheckFunctionSignatures\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, retries, noCache} 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 } else {\n return await httptools.p_GET(this._url(url, servercommands.rawfetch), opts);\n }\n }\n\n p_rawlist(url) {\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));\n }\n rawreverse() { throw new errors.ToBeImplementedError(\"Undefined function TransportHTTP.rawreverse\"); }\n\n async p_rawstore(data) {\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)\n console.assert(data, \"TransportHttp.p_rawstore: requires data\");\n const res = await httptools.p_POST(this._cmdurl(servercommands.rawstore), {data, contenttype: \"application/octet-stream\"}); // 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) {\n // Logged by Transports\n if (!url || !sig) throw new errors.CodingError(\"TransportHTTP.p_rawadd: invalid parms\", url, sig);\n const data = stringify(sig.preflight(Object.assign({},sig)))+\"\\n\";\n return httptools.p_POST(this._url(url, servercommands.rawadd), {data, contenttype: \"application/json\"}); // Returns immediately\n }\n\n p_newlisturls(cl) {\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 // noinspection JSUnresolvedVariable\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 // ============================== Stream support\n\n /*\n Code disabled until have a chance to test it with