diff --git a/docker/solid.conf b/docker/solid.conf
index df551b3..3cd9305 100644
--- a/docker/solid.conf
+++ b/docker/solid.conf
@@ -17,6 +17,25 @@
RewriteRule ^(.+)$ index.php [QSA,L]
+
+ ServerName storage.solid.local
+ DocumentRoot /opt/solid/www/storage
+
+ SSLEngine on
+ SSLCertificateFile /etc/ssl/certs/ssl-cert-snakeoil.pem
+ SSLCertificateKeyFile /etc/ssl/private/ssl-cert-snakeoil.key
+
+ ErrorLog ${APACHE_LOG_DIR}/error.log
+ CustomLog ${APACHE_LOG_DIR}/access.log combined
+
+ Require all granted
+ RewriteEngine On
+ RewriteBase /
+ RewriteCond %{REQUEST_FILENAME} !-d
+ RewriteCond %{REQUEST_FILENAME} !-f
+ RewriteRule ^(.+)$ index.php [QSA,L]
+
+
ServerName identity.solid.local
ServerAlias *.solid.local
diff --git a/init.php b/init.php
index 52d3d89..af5668d 100644
--- a/init.php
+++ b/init.php
@@ -58,6 +58,26 @@ function initDatabase() {
echo $e->getMessage();
}
}
-
+
+ function initStorageDatabase() {
+ $statements = [
+ 'CREATE TABLE IF NOT EXISTS storage (
+ storage_id VARCHAR(255) NOT NULL PRIMARY KEY,
+ owner VARCHAR(255) NOT NULL
+ )'
+ ];
+
+ try {
+ $pdo = new \PDO("sqlite:" . DBPATH);
+
+ // create tables
+ foreach($statements as $statement){
+ $pdo->exec($statement);
+ }
+ } catch(\PDOException $e) {
+ echo $e->getMessage();
+ }
+ }
initKeys();
initDatabase();
+ initStorageDatabase();
\ No newline at end of file
diff --git a/lib/Routes/Account.php b/lib/Routes/Account.php
index 6528da3..1063902 100644
--- a/lib/Routes/Account.php
+++ b/lib/Routes/Account.php
@@ -7,6 +7,7 @@
use Pdsinterop\PhpSolid\Session;
use Pdsinterop\PhpSolid\Mailer;
use Pdsinterop\PhpSolid\IpAttempts;
+ use Pdsinterop\PhpSolid\StorageServer;
class Account {
public static function requireLoggedInUser() {
@@ -87,11 +88,15 @@ public static function respondToAccountNew() {
header("HTTP/1.1 400 Bad Request");
exit();
}
+ $createdStorage = StorageServer::createStorage($createdUser['webId']);
+
Mailer::sendAccountCreated($createdUser);
$responseData = array(
- "webId" => $createdUser['webId']
+ "webId" => $createdUser['webId'],
+ "storageUrl" => $createdStorage['storageUrl']
);
+
header("HTTP/1.1 201 Created");
header("Content-type: application/json");
Session::start($_POST['email']);
diff --git a/lib/Routes/SolidStorage.php b/lib/Routes/SolidStorage.php
index b2a4081..8fdfa6b 100644
--- a/lib/Routes/SolidStorage.php
+++ b/lib/Routes/SolidStorage.php
@@ -1,6 +1,7 @@
fromGlobals($_SERVER, $_GET, $_POST, $_COOKIE, $_FILES);
- StorageServer::initializeStorage();
- $filesystem = StorageServer::getFileSystem();
+ try {
+ StorageServer::initializeStorage();
+ $filesystem = StorageServer::getFileSystem();
+ } catch (\Exception $e) {
+ $response = new Response();
+ $response = $response->withStatus(404, "Not found");
+ StorageServer::respond($response);
+ exit();
+ }
$resourceServer = new ResourceServer($filesystem, new Response(), null);
$solidNotifications = new SolidNotifications();
@@ -40,9 +48,9 @@ public static function respondToStorage() {
$origin = $rawRequest->getHeaderLine("Origin");
// FIXME: Read allowed clients from the profile instead;
- $owner = StorageServer::getOwner();
-
- $allowedClients = $owner['allowedClients'] ?? [];
+ // $owner = StorageServer::getOwner();
+ $ownerWebId = StorageServer::getOwnerWebId();
+ $owner = User::getUserByWebId($ownerWebId);
$allowedOrigins = ($owner['allowedOrigins'] ?? []) + (TRUSTED_APPS ?? []);
if (!isset($origin) || ($origin === "")) {
diff --git a/lib/Routes/SolidStorageProvider.php b/lib/Routes/SolidStorageProvider.php
new file mode 100644
index 0000000..eb4b50c
--- /dev/null
+++ b/lib/Routes/SolidStorageProvider.php
@@ -0,0 +1,39 @@
+fromGlobals($_SERVER, $_GET, $_POST, $_COOKIE, $_FILES);
+ $webId = StorageServer::getWebId($rawRequest);
+
+ if (!isset($webId) || $webId === "public") {
+ header("HTTP/1.1 400 Bad Request");
+ exit();
+ }
+
+ // FIXME: Get the webID issuer and validate that we allow storage creation for that issuer
+
+ $createdStorage = StorageServer::createStorage($webId);
+ if (!$createdStorage) {
+ error_log("Failed to create storage");
+ header("HTTP/1.1 400 Bad Request");
+ exit();
+ }
+
+ //Mailer::sendStorageCreated($createdStoage);
+
+ $storageUrl = "https://storage-" . $createdStorage['storageId'] . "." . BASEDOMAIN . "/";
+
+ $responseData = array(
+ "storage" => $storageUrl
+ );
+ header("HTTP/1.1 201 Created");
+ header("Content-type: application/json");
+ echo json_encode($responseData, JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR);
+ }
+ }
+
\ No newline at end of file
diff --git a/lib/StorageServer.php b/lib/StorageServer.php
index 5c56eb0..5584bc4 100644
--- a/lib/StorageServer.php
+++ b/lib/StorageServer.php
@@ -4,11 +4,85 @@
use Pdsinterop\PhpSolid\Server;
use Pdsinterop\PhpSolid\User;
use Pdsinterop\PhpSolid\Util;
+ use Pdsinterop\PhpSolid\Db;
class StorageServer extends Server {
- public static function getFileSystem() {
+ public static function getStorage($storageId) {
+ Db::connect();
+ $query = Db::$pdo->prepare(
+ 'SELECT * FROM storage WHERE storage_id=:storageId'
+ );
+ $query->execute([
+ ':storageId' => $storageId
+ ]);
+ return $query->fetchAll();
+ }
+
+ public static function setStorageOwner($storageId, $owner) {
+ Db::connect();
+ $query = Db::$pdo->prepare(
+ 'UPDATE storage SET owner=:owner WHERE storage_id=:storageId'
+ );
+ $query->execute([
+ ':storageId' => $storageId,
+ ':owner' => $owner
+ ]);
+ }
+
+ public static function createStorage($ownerWebId) {
+ $generatedStorageId = bin2hex(random_bytes(16));
+ while (self::storageIdExists($generatedStorageId)) {
+ $generatedStorageId = bin2hex(random_bytes(16));
+ }
+ Db::connect();
+ $query = Db::$pdo->prepare(
+ 'INSERT OR REPLACE INTO storage VALUES(:storageId, :owner)'
+ );
+ $query->execute([
+ ':storageId' => $generatedStorageId,
+ ':owner' => $ownerWebId
+ ]);
+ return [
+ "storageId" => $generatedStorageId
+ ];
+ }
+
+ public static function storageIdExists($storageId) {
+ Db::connect();
+ $query = Db::$pdo->prepare(
+ 'SELECT storage_id FROM storage WHERE storage_id=:storageId'
+ );
+ $query->execute([
+ ':storageId' => $storageId
+ ]);
+ $result = $query->fetchAll();
+ if (sizeof($result) === 1) {
+ return true;
+ }
+ return false;
+ }
+
+ public static function getOwnerWebId() {
$storageId = self::getStorageId();
+ Db::connect();
+ $query = Db::$pdo->prepare(
+ 'SELECT owner FROM storage WHERE storage_id=:storageId'
+ );
+ $query->execute([
+ ':storageId' => $storageId
+ ]);
+ $result = $query->fetchAll();
+ if (sizeof($result) === 1) {
+ return $result[0]['owner'];
+ }
+ return false;
+ }
+ public static function getFileSystem() {
+ $storageId = self::getStorageId();
+ if (!self::storageIdExists($storageId)) {
+ throw new \Exception("Storage does not exist");
+ }
// The internal adapter
$adapter = new \League\Flysystem\Adapter\Local(
// Determine root directory
@@ -65,16 +139,6 @@ private static function getStorageId() {
return $storageId;
}
- public static function getOwner() {
- $storageId = self::getStorageId();
- return User::getUserById($storageId);
- }
-
- public static function getOwnerWebId() {
- $owner = self::getOwner();
- return $owner['webId'];
- }
-
public static function initializeStorage() {
$filesystem = self::getFilesystem();
if (!$filesystem->has("/.acl")) {
@@ -119,6 +183,9 @@ public static function initializeStorage() {
public static function generateDefaultAcl() {
$webId = self::getOwnerWebId();
+ if (!$webId) {
+ throw new \Exception("No owner found for storage");
+ }
$acl = <<< "EOF"
# Root ACL resource for the user account
@prefix acl: .
@@ -150,6 +217,9 @@ public static function generateDefaultAcl() {
public static function generatePublicAppendAcl() {
$webId = self::getOwnerWebId();
+ if (!$webId) {
+ throw new \Exception("No owner found for storage ID");
+ }
$acl = <<< "EOF"
# Inbox ACL resource for the user account
@prefix acl: .
@@ -179,6 +249,9 @@ public static function generatePublicAppendAcl() {
public static function generatePublicReadAcl() {
$webId = self::getOwnerWebId();
+ if (!$webId) {
+ throw new \Exception("No owner found for storage ID");
+ }
$acl = <<< "EOF"
# Inbox ACL resource for the user account
@prefix acl: .
diff --git a/lib/User.php b/lib/User.php
index 1cb9161..2c1a748 100644
--- a/lib/User.php
+++ b/lib/User.php
@@ -268,6 +268,15 @@ public static function getUserById($userId) {
return false;
}
+ public static function getUserByWebId($webId) {
+ $idParts = explode(".", $webId, 2);
+ if ($idParts[1] !== BASEDOMAIN . "/#me") {
+ return false;
+ }
+ $userId = preg_replace("/^id-/", "", $idParts[0]);
+ return self::getUserById($userId);
+ }
+
public static function checkPassword($email, $password) {
Db::connect();
$query = Db::$pdo->prepare(
diff --git a/tests/phpunit/StorageServerTest.php b/tests/phpunit/StorageServerTest.php
index 0de276f..aa35038 100644
--- a/tests/phpunit/StorageServerTest.php
+++ b/tests/phpunit/StorageServerTest.php
@@ -9,6 +9,7 @@ class StorageServerTest extends \PHPUnit\Framework\TestCase
{
public static $headers = [];
public static $createdUser;
+ public static $createdStorage;
protected function setUp(): void
{
@@ -16,6 +17,7 @@ protected function setUp(): void
'DROP TABLE IF EXISTS allowedClients',
'DROP TABLE IF EXISTS userStorage',
'DROP TABLE IF EXISTS users',
+ 'DROP TABLE IF EXISTS storage',
'CREATE TABLE IF NOT EXISTS allowedClients (
userId VARCHAR(255) NOT NULL PRIMARY KEY,
clientId VARCHAR(255) NOT NULL
@@ -30,6 +32,10 @@ protected function setUp(): void
password TEXT NOT NULL,
data TEXT
)',
+ 'CREATE TABLE IF NOT EXISTS storage (
+ storage_id VARCHAR(255) NOT NULL PRIMARY KEY,
+ owner VARCHAR(255) NOT NULL
+ )',
];
Db::connect();
@@ -48,9 +54,11 @@ protected function setUp(): void
"hello" => "world"
];
self::$createdUser = User::createUser($newUser);
+ self::$createdStorage = StorageServer::createStorage(self::$createdUser['webId']);
+
$_SERVER['REQUEST_URI'] = "/test/";
$_SERVER['REQUEST_SCHEME'] = "https";
- $_SERVER['SERVER_NAME'] = "storage-" . self::$createdUser['userId'] . ".example.com";
+ $_SERVER['SERVER_NAME'] = "storage-" . self::$createdStorage['storageId'] . ".example.com";
}
public function testGetFileSystem() {
@@ -72,12 +80,6 @@ public function testRespond() {
$this->assertEquals($sentBody, "{\"Hello\":\"world\"}");
}
- public function testGetOwner() {
- $owner = StorageServer::getOwner();
- $this->assertEquals(self::$createdUser['webId'], $owner['webId']);
- $this->assertEquals(self::$createdUser['email'], $owner['email']);
- }
-
public function testGetOwnerWebId() {
$webId = StorageServer::getOwnerWebId();
$this->assertEquals(self::$createdUser['webId'], $webId);
@@ -123,6 +125,13 @@ public function testGenerateDefaultPreferences() {
Currently untested:
public static function getWebId($rawRequest) {
public static function initializeStorage() {
+ public static function getStorage($storageId) {
+ public static function setStorageOwner($storageId, $owner) {
+ public static function createStorage($ownerWebId) {
+ public static function storageIdExists($storageId) {
*/
}
+
+
+
\ No newline at end of file
diff --git a/tests/testsuite/init-testsuite.php b/tests/testsuite/init-testsuite.php
index 39f97b5..cb8026a 100644
--- a/tests/testsuite/init-testsuite.php
+++ b/tests/testsuite/init-testsuite.php
@@ -24,6 +24,18 @@ public static function createUser($testUser) {
$queryParams[':data'] = json_encode($testUser);
$query->execute($queryParams);
}
+
+ public static function createStorage($testUser) {
+ self::connect();
+ $query = self::$pdo->prepare(
+ 'INSERT INTO storage VALUES (:storageId, :owner)'
+ );
+
+ $queryParams = [];
+ $queryParams[':storageId'] = $testUser['id'];
+ $queryParams[':owner'] = "https://id-" . $testUser['id'] . "." . BASEDOMAIN . "/#me";
+ $query->execute($queryParams);
+ }
}
TestUser::createUser([
@@ -31,10 +43,16 @@ public static function createUser($testUser) {
"password" => "alice123",
"email" => "alice"
]);
+ TestUser::createStorage([
+ "id" => "alice"
+ ]);
TestUser::createUser([
"id" => "bob",
"password" => "bob345",
"email" => "bob"
]);
+ TestUser::createStorage([
+ "id" => "bob"
+ ]);
\ No newline at end of file
diff --git a/www/storage/assets/fonts/CoconRegularFont.woff b/www/storage/assets/fonts/CoconRegularFont.woff
new file mode 100644
index 0000000..0f98fcf
Binary files /dev/null and b/www/storage/assets/fonts/CoconRegularFont.woff differ
diff --git a/www/storage/assets/img/feather-sprite.svg b/www/storage/assets/img/feather-sprite.svg
new file mode 100644
index 0000000..fcb2082
--- /dev/null
+++ b/www/storage/assets/img/feather-sprite.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/www/storage/index.php b/www/storage/index.php
new file mode 100644
index 0000000..023cef8
--- /dev/null
+++ b/www/storage/index.php
@@ -0,0 +1,42 @@
+
+
+
+
+
+ Swagger UI
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/www/storage/swagger/swagger-ui-bundle.js b/www/storage/swagger/swagger-ui-bundle.js
new file mode 100755
index 0000000..ec55a11
--- /dev/null
+++ b/www/storage/swagger/swagger-ui-bundle.js
@@ -0,0 +1,126 @@
+!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t(function(){try{return require("esprima")}catch(e){}}()):"function"==typeof define&&define.amd?define(["esprima"],t):"object"==typeof exports?exports.SwaggerUIBundle=t(function(){try{return require("esprima")}catch(e){}}()):e.SwaggerUIBundle=t(e.esprima)}(window,(function(e){return function(e){var t={};function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}return n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)n.d(r,o,function(t){return e[t]}.bind(null,o));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="/dist",n(n.s=487)}([function(e,t,n){"use strict";e.exports=n(105)},function(e,t,n){e.exports=n(799)},function(e,t,n){e.exports=function(){"use strict";var e=Array.prototype.slice;function t(e,t){t&&(e.prototype=Object.create(t.prototype)),e.prototype.constructor=e}function n(e){return a(e)?e:q(e)}function r(e){return u(e)?e:V(e)}function o(e){return s(e)?e:W(e)}function i(e){return a(e)&&!c(e)?e:H(e)}function a(e){return!(!e||!e[f])}function u(e){return!(!e||!e[p])}function s(e){return!(!e||!e[h])}function c(e){return u(e)||s(e)}function l(e){return!(!e||!e[d])}t(r,n),t(o,n),t(i,n),n.isIterable=a,n.isKeyed=u,n.isIndexed=s,n.isAssociative=c,n.isOrdered=l,n.Keyed=r,n.Indexed=o,n.Set=i;var f="@@__IMMUTABLE_ITERABLE__@@",p="@@__IMMUTABLE_KEYED__@@",h="@@__IMMUTABLE_INDEXED__@@",d="@@__IMMUTABLE_ORDERED__@@",v={},m={value:!1},y={value:!1};function g(e){return e.value=!1,e}function b(e){e&&(e.value=!0)}function _(){}function w(e,t){t=t||0;for(var n=Math.max(0,e.length-t),r=new Array(n),o=0;o>>0;if(""+n!==t||4294967295===n)return NaN;t=n}return t<0?x(e)+t:t}function S(){return!0}function C(e,t,n){return(0===e||void 0!==n&&e<=-n)&&(void 0===t||void 0!==n&&t>=n)}function A(e,t){return O(e,t,0)}function k(e,t){return O(e,t,t)}function O(e,t,n){return void 0===e?n:e<0?Math.max(0,t+e):void 0===t?e:Math.min(t,e)}var T,j,P,I="function"==typeof Symbol&&Symbol.iterator,M=I||"@@iterator";function D(e){this.next=e}function R(e,t,n,r){var o=0===e?t:1===e?n:[t,n];return r?r.value=o:r={value:o,done:!1},r}function N(){return{value:void 0,done:!0}}function L(e){return!!U(e)}function F(e){return e&&"function"==typeof e.next}function B(e){var t=U(e);return t&&t.call(e)}function U(e){var t=e&&(I&&e[I]||e["@@iterator"]);if("function"==typeof t)return t}function z(e){return e&&"number"==typeof e.length}function q(e){return null==e?Z():a(e)?e.toSeq():function(e){var t=ee(e)||"object"==typeof e&&new Y(e);if(!t)throw new TypeError("Expected Array or iterable object of values, or keyed object: "+e);return t}(e)}function V(e){return null==e?Z().toKeyedSeq():a(e)?u(e)?e.toSeq():e.fromEntrySeq():X(e)}function W(e){return null==e?Z():a(e)?u(e)?e.entrySeq():e.toIndexedSeq():Q(e)}function H(e){return(null==e?Z():a(e)?u(e)?e.entrySeq():e:Q(e)).toSetSeq()}function J(e){this._array=e,this.size=e.length}function Y(e){var t=Object.keys(e);this._object=e,this._keys=t,this.size=t.length}function K(e){this._iterable=e,this.size=e.length||e.size}function $(e){this._iterator=e,this._iteratorCache=[]}function G(e){return!(!e||!e["@@__IMMUTABLE_SEQ__@@"])}function Z(){return T||(T=new J([]))}function X(e){var t=Array.isArray(e)?new J(e).fromEntrySeq():F(e)?new $(e).fromEntrySeq():L(e)?new K(e).fromEntrySeq():"object"==typeof e?new Y(e):void 0;if(!t)throw new TypeError("Expected Array or iterable object of [k, v] entries, or keyed object: "+e);return t}function Q(e){var t=ee(e);if(!t)throw new TypeError("Expected Array or iterable object of values: "+e);return t}function ee(e){return z(e)?new J(e):F(e)?new $(e):L(e)?new K(e):void 0}function te(e,t,n,r){var o=e._cache;if(o){for(var i=o.length-1,a=0;a<=i;a++){var u=o[n?i-a:a];if(!1===t(u[1],r?u[0]:a,e))return a+1}return a}return e.__iterateUncached(t,n)}function ne(e,t,n,r){var o=e._cache;if(o){var i=o.length-1,a=0;return new D((function(){var e=o[n?i-a:a];return a++>i?{value:void 0,done:!0}:R(t,r?e[0]:a-1,e[1])}))}return e.__iteratorUncached(t,n)}function re(e,t){return t?function e(t,n,r,o){return Array.isArray(n)?t.call(o,r,W(n).map((function(r,o){return e(t,r,o,n)}))):ie(n)?t.call(o,r,V(n).map((function(r,o){return e(t,r,o,n)}))):n}(t,e,"",{"":e}):oe(e)}function oe(e){return Array.isArray(e)?W(e).map(oe).toList():ie(e)?V(e).map(oe).toMap():e}function ie(e){return e&&(e.constructor===Object||void 0===e.constructor)}function ae(e,t){if(e===t||e!=e&&t!=t)return!0;if(!e||!t)return!1;if("function"==typeof e.valueOf&&"function"==typeof t.valueOf){if((e=e.valueOf())===(t=t.valueOf())||e!=e&&t!=t)return!0;if(!e||!t)return!1}return!("function"!=typeof e.equals||"function"!=typeof t.equals||!e.equals(t))}function ue(e,t){if(e===t)return!0;if(!a(t)||void 0!==e.size&&void 0!==t.size&&e.size!==t.size||void 0!==e.__hash&&void 0!==t.__hash&&e.__hash!==t.__hash||u(e)!==u(t)||s(e)!==s(t)||l(e)!==l(t))return!1;if(0===e.size&&0===t.size)return!0;var n=!c(e);if(l(e)){var r=e.entries();return t.every((function(e,t){var o=r.next().value;return o&&ae(o[1],e)&&(n||ae(o[0],t))}))&&r.next().done}var o=!1;if(void 0===e.size)if(void 0===t.size)"function"==typeof e.cacheResult&&e.cacheResult();else{o=!0;var i=e;e=t,t=i}var f=!0,p=t.__iterate((function(t,r){if(n?!e.has(t):o?!ae(t,e.get(r,v)):!ae(e.get(r,v),t))return f=!1,!1}));return f&&e.size===p}function se(e,t){if(!(this instanceof se))return new se(e,t);if(this._value=e,this.size=void 0===t?1/0:Math.max(0,t),0===this.size){if(j)return j;j=this}}function ce(e,t){if(!e)throw new Error(t)}function le(e,t,n){if(!(this instanceof le))return new le(e,t,n);if(ce(0!==n,"Cannot step a Range by 0"),e=e||0,void 0===t&&(t=1/0),n=void 0===n?1:Math.abs(n),tr?{value:void 0,done:!0}:R(e,o,n[t?r-o++:o++])}))},t(Y,V),Y.prototype.get=function(e,t){return void 0===t||this.has(e)?this._object[e]:t},Y.prototype.has=function(e){return this._object.hasOwnProperty(e)},Y.prototype.__iterate=function(e,t){for(var n=this._object,r=this._keys,o=r.length-1,i=0;i<=o;i++){var a=r[t?o-i:i];if(!1===e(n[a],a,this))return i+1}return i},Y.prototype.__iterator=function(e,t){var n=this._object,r=this._keys,o=r.length-1,i=0;return new D((function(){var a=r[t?o-i:i];return i++>o?{value:void 0,done:!0}:R(e,a,n[a])}))},Y.prototype[d]=!0,t(K,W),K.prototype.__iterateUncached=function(e,t){if(t)return this.cacheResult().__iterate(e,t);var n=B(this._iterable),r=0;if(F(n))for(var o;!(o=n.next()).done&&!1!==e(o.value,r++,this););return r},K.prototype.__iteratorUncached=function(e,t){if(t)return this.cacheResult().__iterator(e,t);var n=B(this._iterable);if(!F(n))return new D(N);var r=0;return new D((function(){var t=n.next();return t.done?t:R(e,r++,t.value)}))},t($,W),$.prototype.__iterateUncached=function(e,t){if(t)return this.cacheResult().__iterate(e,t);for(var n,r=this._iterator,o=this._iteratorCache,i=0;i=r.length){var t=n.next();if(t.done)return t;r[o]=t.value}return R(e,o,r[o++])}))},t(se,W),se.prototype.toString=function(){return 0===this.size?"Repeat []":"Repeat [ "+this._value+" "+this.size+" times ]"},se.prototype.get=function(e,t){return this.has(e)?this._value:t},se.prototype.includes=function(e){return ae(this._value,e)},se.prototype.slice=function(e,t){var n=this.size;return C(e,t,n)?this:new se(this._value,k(t,n)-A(e,n))},se.prototype.reverse=function(){return this},se.prototype.indexOf=function(e){return ae(this._value,e)?0:-1},se.prototype.lastIndexOf=function(e){return ae(this._value,e)?this.size:-1},se.prototype.__iterate=function(e,t){for(var n=0;n=0&&t=0&&nn?{value:void 0,done:!0}:R(e,i++,a)}))},le.prototype.equals=function(e){return e instanceof le?this._start===e._start&&this._end===e._end&&this._step===e._step:ue(this,e)},t(fe,n),t(pe,fe),t(he,fe),t(de,fe),fe.Keyed=pe,fe.Indexed=he,fe.Set=de;var ve="function"==typeof Math.imul&&-2===Math.imul(4294967295,2)?Math.imul:function(e,t){var n=65535&(e|=0),r=65535&(t|=0);return n*r+((e>>>16)*r+n*(t>>>16)<<16>>>0)|0};function me(e){return e>>>1&1073741824|3221225471&e}function ye(e){if(!1===e||null==e)return 0;if("function"==typeof e.valueOf&&(!1===(e=e.valueOf())||null==e))return 0;if(!0===e)return 1;var t=typeof e;if("number"===t){if(e!=e||e===1/0)return 0;var n=0|e;for(n!==e&&(n^=4294967295*e);e>4294967295;)n^=e/=4294967295;return me(n)}if("string"===t)return e.length>Ce?function(e){var t=Oe[e];return void 0===t&&(t=ge(e),ke===Ae&&(ke=0,Oe={}),ke++,Oe[e]=t),t}(e):ge(e);if("function"==typeof e.hashCode)return e.hashCode();if("object"===t)return function(e){var t;if(xe&&void 0!==(t=be.get(e)))return t;if(void 0!==(t=e[Se]))return t;if(!we){if(void 0!==(t=e.propertyIsEnumerable&&e.propertyIsEnumerable[Se]))return t;if(void 0!==(t=function(e){if(e&&e.nodeType>0)switch(e.nodeType){case 1:return e.uniqueID;case 9:return e.documentElement&&e.documentElement.uniqueID}}(e)))return t}if(t=++Ee,1073741824&Ee&&(Ee=0),xe)be.set(e,t);else{if(void 0!==_e&&!1===_e(e))throw new Error("Non-extensible objects are not allowed as keys.");if(we)Object.defineProperty(e,Se,{enumerable:!1,configurable:!1,writable:!1,value:t});else if(void 0!==e.propertyIsEnumerable&&e.propertyIsEnumerable===e.constructor.prototype.propertyIsEnumerable)e.propertyIsEnumerable=function(){return this.constructor.prototype.propertyIsEnumerable.apply(this,arguments)},e.propertyIsEnumerable[Se]=t;else{if(void 0===e.nodeType)throw new Error("Unable to set a non-enumerable property on object.");e[Se]=t}}return t}(e);if("function"==typeof e.toString)return ge(e.toString());throw new Error("Value type "+t+" cannot be hashed.")}function ge(e){for(var t=0,n=0;n=t.length)throw new Error("Missing value for key: "+t[n]);e.set(t[n],t[n+1])}}))},je.prototype.toString=function(){return this.__toString("Map {","}")},je.prototype.get=function(e,t){return this._root?this._root.get(0,void 0,e,t):t},je.prototype.set=function(e,t){return He(this,e,t)},je.prototype.setIn=function(e,t){return this.updateIn(e,v,(function(){return t}))},je.prototype.remove=function(e){return He(this,e,v)},je.prototype.deleteIn=function(e){return this.updateIn(e,(function(){return v}))},je.prototype.update=function(e,t,n){return 1===arguments.length?e(this):this.updateIn([e],t,n)},je.prototype.updateIn=function(e,t,n){n||(n=t,t=void 0);var r=function e(t,n,r,o){var i=t===v,a=n.next();if(a.done){var u=i?r:t,s=o(u);return s===u?t:s}ce(i||t&&t.set,"invalid keyPath");var c=a.value,l=i?v:t.get(c,v),f=e(l,n,r,o);return f===l?t:f===v?t.remove(c):(i?We():t).set(c,f)}(this,$t(e),t,n);return r===v?void 0:r},je.prototype.clear=function(){return 0===this.size?this:this.__ownerID?(this.size=0,this._root=null,this.__hash=void 0,this.__altered=!0,this):We()},je.prototype.merge=function(){return $e(this,void 0,arguments)},je.prototype.mergeWith=function(t){var n=e.call(arguments,1);return $e(this,t,n)},je.prototype.mergeIn=function(t){var n=e.call(arguments,1);return this.updateIn(t,We(),(function(e){return"function"==typeof e.merge?e.merge.apply(e,n):n[n.length-1]}))},je.prototype.mergeDeep=function(){return $e(this,Ge,arguments)},je.prototype.mergeDeepWith=function(t){var n=e.call(arguments,1);return $e(this,Ze(t),n)},je.prototype.mergeDeepIn=function(t){var n=e.call(arguments,1);return this.updateIn(t,We(),(function(e){return"function"==typeof e.mergeDeep?e.mergeDeep.apply(e,n):n[n.length-1]}))},je.prototype.sort=function(e){return wt(Ft(this,e))},je.prototype.sortBy=function(e,t){return wt(Ft(this,t,e))},je.prototype.withMutations=function(e){var t=this.asMutable();return e(t),t.wasAltered()?t.__ensureOwner(this.__ownerID):this},je.prototype.asMutable=function(){return this.__ownerID?this:this.__ensureOwner(new _)},je.prototype.asImmutable=function(){return this.__ensureOwner()},je.prototype.wasAltered=function(){return this.__altered},je.prototype.__iterator=function(e,t){return new Ue(this,e,t)},je.prototype.__iterate=function(e,t){var n=this,r=0;return this._root&&this._root.iterate((function(t){return r++,e(t[1],t[0],n)}),t),r},je.prototype.__ensureOwner=function(e){return e===this.__ownerID?this:e?Ve(this.size,this._root,e,this.__hash):(this.__ownerID=e,this.__altered=!1,this)},je.isMap=Pe;var Ie,Me="@@__IMMUTABLE_MAP__@@",De=je.prototype;function Re(e,t){this.ownerID=e,this.entries=t}function Ne(e,t,n){this.ownerID=e,this.bitmap=t,this.nodes=n}function Le(e,t,n){this.ownerID=e,this.count=t,this.nodes=n}function Fe(e,t,n){this.ownerID=e,this.keyHash=t,this.entries=n}function Be(e,t,n){this.ownerID=e,this.keyHash=t,this.entry=n}function Ue(e,t,n){this._type=t,this._reverse=n,this._stack=e._root&&qe(e._root)}function ze(e,t){return R(e,t[0],t[1])}function qe(e,t){return{node:e,index:0,__prev:t}}function Ve(e,t,n,r){var o=Object.create(De);return o.size=e,o._root=t,o.__ownerID=n,o.__hash=r,o.__altered=!1,o}function We(){return Ie||(Ie=Ve(0))}function He(e,t,n){var r,o;if(e._root){var i=g(m),a=g(y);if(r=Je(e._root,e.__ownerID,0,void 0,t,n,i,a),!a.value)return e;o=e.size+(i.value?n===v?-1:1:0)}else{if(n===v)return e;o=1,r=new Re(e.__ownerID,[[t,n]])}return e.__ownerID?(e.size=o,e._root=r,e.__hash=void 0,e.__altered=!0,e):r?Ve(o,r):We()}function Je(e,t,n,r,o,i,a,u){return e?e.update(t,n,r,o,i,a,u):i===v?e:(b(u),b(a),new Be(t,r,[o,i]))}function Ye(e){return e.constructor===Be||e.constructor===Fe}function Ke(e,t,n,r,o){if(e.keyHash===r)return new Fe(t,r,[e.entry,o]);var i,a=31&(0===n?e.keyHash:e.keyHash>>>n),u=31&(0===n?r:r>>>n);return new Ne(t,1<>1&1431655765))+(e>>2&858993459))+(e>>4)&252645135,e+=e>>8,127&(e+=e>>16)}function et(e,t,n,r){var o=r?e:w(e);return o[t]=n,o}De[Me]=!0,De.delete=De.remove,De.removeIn=De.deleteIn,Re.prototype.get=function(e,t,n,r){for(var o=this.entries,i=0,a=o.length;i=tt)return function(e,t,n,r){e||(e=new _);for(var o=new Be(e,ye(n),[n,r]),i=0;i>>e)),i=this.bitmap;return 0==(i&o)?r:this.nodes[Qe(i&o-1)].get(e+5,t,n,r)},Ne.prototype.update=function(e,t,n,r,o,i,a){void 0===n&&(n=ye(r));var u=31&(0===t?n:n>>>t),s=1<=nt)return function(e,t,n,r,o){for(var i=0,a=new Array(32),u=0;0!==n;u++,n>>>=1)a[u]=1&n?t[i++]:void 0;return a[r]=o,new Le(e,i+1,a)}(e,p,c,u,d);if(l&&!d&&2===p.length&&Ye(p[1^f]))return p[1^f];if(l&&d&&1===p.length&&Ye(d))return d;var m=e&&e===this.ownerID,y=l?d?c:c^s:c|s,g=l?d?et(p,f,d,m):function(e,t,n){var r=e.length-1;if(n&&t===r)return e.pop(),e;for(var o=new Array(r),i=0,a=0;a>>e),i=this.nodes[o];return i?i.get(e+5,t,n,r):r},Le.prototype.update=function(e,t,n,r,o,i,a){void 0===n&&(n=ye(r));var u=31&(0===t?n:n>>>t),s=o===v,c=this.nodes,l=c[u];if(s&&!l)return this;var f=Je(l,e,t+5,n,r,o,i,a);if(f===l)return this;var p=this.count;if(l){if(!f&&--p